svg icons for Linux

This commit is contained in:
Felix Kauselmann
2017-11-21 11:01:31 +01:00
commit 28a6939a01
1047 changed files with 104156 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,325 @@
TEMPLATE = app
TARGET = YACReaderLibrary
QMAKE_TARGET_BUNDLE_PREFIX = "com.yacreader"
DEPENDPATH += .
INCLUDEPATH += . \
../common \
./server \
./db \
../custom_widgets \
./comic_vine \
./comic_vine/model
DEFINES += SERVER_RELEASE NOMINMAX YACREADER_LIBRARY
QMAKE_MAC_SDK = macosx10.12
# load default build flags
include (../config.pri)
include (../dependencies/pdf_backend.pri)
unix:haiku {
DEFINES += _BSD_SOURCE
LIBS += -lnetwork -lbsd
}
CONFIG(legacy_gl_widget) {
INCLUDEPATH += ../common/gl_legacy \
} else {
INCLUDEPATH += ../common/gl \
}
# there are two builds for Windows, Desktop OpenGL based and ANGLE OpenGL ES based
win32 {
CONFIG(force_angle) {
message("using ANGLE")
LIBS += -loleaut32 -lole32 -lshell32 -lopengl32 -lglu32 -luser32
# linking extra libs are necesary for a successful compilation, a better approach should be
# to remove any OpenGL (desktop) dependencies
# the OpenGL stuff should be migrated to OpenGL ES
DEFINES += FORCE_ANGLE
} else {
LIBS += -loleaut32 -lole32 -lshell32 -lopengl32 -lglu32 -luser32
}
QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /GL
QMAKE_LFLAGS_RELEASE += /LTCG
CONFIG -= embed_manifest_exe
}
CONFIG(force_angle) {
contains(QMAKE_TARGET.arch, x86_64) {
Release:DESTDIR = ../release64_angle
Debug:DESTDIR = ../debug64_angle
} else {
Release:DESTDIR = ../release_angle
Debug:DESTDIR = ../debug_angle
}
} else {
contains(QMAKE_TARGET.arch, x86_64) {
Release:DESTDIR = ../release64
Debug:DESTDIR = ../debug64
} else {
Release:DESTDIR = ../release
Debug:DESTDIR = ../debug
}
}
unix:!macx:!CONFIG(no_opengl) {
LIBS += -lGLU
}
macx {
LIBS += -framework Foundation -framework ApplicationServices -framework AppKit
CONFIG += objective_c
QT += macextras gui-private
}
unix:!macx {
CONFIG += c++11
}
#CONFIG += release
CONFIG -= flat
QT += sql network widgets script
!CONFIG(no_opengl) {
QT += opengl
}
# Input
HEADERS += comic_flow.h \
create_library_dialog.h \
library_creator.h \
library_window.h \
add_library_dialog.h \
rename_library_dialog.h \
properties_dialog.h \
options_dialog.h \
export_library_dialog.h \
import_library_dialog.h \
package_manager.h \
bundle_creator.h \
export_comics_info_dialog.h \
import_comics_info_dialog.h \
server_config_dialog.h \
comic_flow_widget.h \
db_helper.h \
./db/data_base_management.h \
./db/folder_item.h \
./db/folder_model.h \
./db/comic_model.h \
./db/comic_item.h \
../common/comic_db.h \
../common/folder.h \
../common/library_item.h \
../common/comic.h \
../common/bookmarks.h \
../common/pictureflow.h \
../common/custom_widgets.h \
../common/qnaturalsorting.h \
../common/yacreader_global.h \
../common/yacreader_global_gui.h \
../common/onstart_flow_selection_dialog.h \
../common/pdf_comic.h \
no_libraries_widget.h \
import_widget.h \
yacreader_local_server.h \
yacreader_main_toolbar.h \
comics_remover.h \
../common/http_worker.h \
yacreader_libraries.h \
../common/exit_check.h \
comics_view.h \
classic_comics_view.h \
empty_folder_widget.h \
no_search_results_widget.h \
comic_files_manager.h \
db/reading_list_model.h \
db/reading_list_item.h \
yacreader_folders_view.h \
yacreader_reading_lists_view.h \
add_label_dialog.h \
yacreader_history_controller.h \
yacreader_navigation_controller.h \
empty_label_widget.h \
empty_container_info.h \
empty_special_list.h \
empty_reading_list_widget.h \
../common/scroll_management.h \
../common/opengl_checker.h \
yacreader_comics_views_manager.h \
info_comics_view.h \
yacreader_comics_selection_helper.h \
yacreader_comic_info_helper.h
!CONFIG(no_opengl) {
CONFIG(legacy_gl_widget) {
message("using legacy YACReaderFlowGL (QGLWidget) header")
HEADERS += ../common/gl_legacy/yacreader_flow_gl.h
} else {
HEADERS += ../common/gl/yacreader_flow_gl.h
}
}
SOURCES += comic_flow.cpp \
create_library_dialog.cpp \
library_creator.cpp \
library_window.cpp \
main.cpp \
add_library_dialog.cpp \
rename_library_dialog.cpp \
properties_dialog.cpp \
options_dialog.cpp \
export_library_dialog.cpp \
import_library_dialog.cpp \
package_manager.cpp \
bundle_creator.cpp \
export_comics_info_dialog.cpp \
import_comics_info_dialog.cpp \
server_config_dialog.cpp \
comic_flow_widget.cpp \
db_helper.cpp \
./db/data_base_management.cpp \
./db/folder_item.cpp \
./db/folder_model.cpp \
./db/comic_model.cpp \
./db/comic_item.cpp \
../common/comic_db.cpp \
../common/folder.cpp \
../common/library_item.cpp \
../common/comic.cpp \
../common/bookmarks.cpp \
../common/pictureflow.cpp \
../common/custom_widgets.cpp \
../common/qnaturalsorting.cpp \
../common/onstart_flow_selection_dialog.cpp \
no_libraries_widget.cpp \
import_widget.cpp \
yacreader_local_server.cpp \
yacreader_main_toolbar.cpp \
comics_remover.cpp \
../common/http_worker.cpp \
../common/yacreader_global.cpp \
../common/yacreader_global_gui.cpp \
yacreader_libraries.cpp \
../common/exit_check.cpp \
comics_view.cpp \
classic_comics_view.cpp \
empty_folder_widget.cpp \
no_search_results_widget.cpp \
comic_files_manager.cpp \
db/reading_list_model.cpp \
db/reading_list_item.cpp \
yacreader_folders_view.cpp \
yacreader_reading_lists_view.cpp \
add_label_dialog.cpp \
yacreader_history_controller.cpp \
yacreader_navigation_controller.cpp \
empty_label_widget.cpp \
empty_container_info.cpp \
empty_special_list.cpp \
empty_reading_list_widget.cpp \
../common/scroll_management.cpp \
../common/opengl_checker.cpp \
yacreader_comics_views_manager.cpp \
info_comics_view.cpp \
yacreader_comics_selection_helper.cpp \
yacreader_comic_info_helper.cpp
!CONFIG(no_opengl) {
CONFIG(legacy_gl_widget) {
message("using legacy YACReaderFlowGL (QGLWidget) source code")
SOURCES += ../common/gl_legacy/yacreader_flow_gl.cpp
} else {
SOURCES += ../common/gl/yacreader_flow_gl.cpp
}
}
include(./server/server.pri)
include(../custom_widgets/custom_widgets_yacreaderlibrary.pri)
CONFIG(7zip){
include(../compressed_archive/wrapper.pri)
} else:CONFIG(unarr) {
include(../compressed_archive/unarr/unarr-wrapper.pri)
} else {
error(No compression backend specified. Did you mess with the build system?)
}
include(./comic_vine/comic_vine.pri)
include(../QsLog/QsLog.pri)
include(../shortcuts_management/shortcuts_management.pri)
RESOURCES += images.qrc files.qrc
win32:RESOURCES += images_win.qrc
unix:!macx:RESOURCES += images_win.qrc
macx:RESOURCES += images_osx.qrc
RC_FILE = icon.rc
macx {
ICON = YACReaderLibrary.icns
}
TRANSLATIONS = yacreaderlibrary_es.ts \
yacreaderlibrary_ru.ts \
yacreaderlibrary_pt.ts \
yacreaderlibrary_fr.ts \
yacreaderlibrary_nl.ts \
yacreaderlibrary_tr.ts \
yacreaderlibrary_de.ts \
yacreaderlibrary_source.ts
#QML/GridView
QT += quick qml
HEADERS += grid_comics_view.h \
comics_view_transition.h
SOURCES += grid_comics_view.cpp \
comics_view_transition.cpp
RESOURCES += qml.qrc
win32:RESOURCES += qml_win.qrc
unix:!macx:RESOURCES += qml_win.qrc
macx:RESOURCES += qml_osx.qrc
unix:!macx {
#set install prefix if it's empty
isEmpty(PREFIX) {
PREFIX = /usr
}
BINDIR = $$PREFIX/bin
LIBDIR = $$PREFIX/lib
DATADIR = $$PREFIX/share
DEFINES += "LIBDIR=\\\"$$LIBDIR\\\"" "DATADIR=\\\"$$DATADIR\\\"" "BINDIR=\\\"$$BINDIR\\\""
#MAKE INSTALL
INSTALLS += bin icon desktop server translation manpage
bin.path = $$BINDIR
isEmpty(DESTDIR) {
bin.files = YACReaderLibrary
} else {
bin.files = $$DESTDIR/YACReaderLibrary
}
server.path = $$DATADIR/yacreader
server.files = ../release/server
icon.path = $$DATADIR/icons/hicolor/scaleable/apps
icon.files = ../YACReaderLibrary.svg
desktop.path = $$DATADIR/applications
desktop.files = ../YACReaderLibrary.desktop
translation.path = $$DATADIR/yacreader/languages
translation.files = ../release/languages/yacreaderlibrary_*
manpage.path = $$DATADIR/man/man1
manpage.files = ../YACReaderLibrary.1
}

View File

@ -0,0 +1,84 @@
#include "add_label_dialog.h"
AddLabelDialog::AddLabelDialog(QWidget *parent) :
QDialog(parent)
{
QVBoxLayout * layout = new QVBoxLayout;
layout->addWidget(new QLabel(tr("Label name:")));
layout->addWidget(edit = new QLineEdit());
layout->addWidget(new QLabel(tr("Choose a color:")));
layout->addWidget(list = new QListWidget() );
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_red.png"), tr("red")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_orange.png"), tr("orange")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_yellow.png"), tr("yellow")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_green.png"), tr("green")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_cyan.png"), tr("cyan")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_blue.png"), tr("blue")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_violet.png"), tr("violet")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_purple.png"), tr("purple")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_pink.png"), tr("pink")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_white.png"), tr("white")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_light.png"), tr("light")));
list->addItem(new QListWidgetItem(QIcon(":/images/lists/label_dark.png"), tr("dark")));
QColor backgroundColor = this->palette().background().color();
list->setStyleSheet(QString("QListWidget {border : none; background-color: rgb(%1,%2,%3);}").arg(backgroundColor.red()).arg(backgroundColor.green()).arg(backgroundColor.blue()));
list->setMinimumHeight(225);
setModal(true);
setMinimumHeight(340);
//buttons
acceptButton = new QPushButton(tr("accept"),this);
cancelButton = new QPushButton(tr("cancel"),this);
QHBoxLayout * buttons = new QHBoxLayout;
buttons->addStretch();
buttons->addWidget(acceptButton);
buttons->addWidget(cancelButton);
layout->addStretch();
layout->addLayout(buttons);
setLayout(layout);
//connections
connect(edit,SIGNAL(textChanged(QString)),this,SLOT(validateName(QString)));
connect(cancelButton,SIGNAL(clicked()),this,SLOT(close()));
connect(acceptButton,SIGNAL(clicked()),this,SLOT(accept()));
}
YACReader::LabelColors AddLabelDialog::selectedColor()
{
return YACReader::LabelColors(list->currentRow()+1);
}
QString AddLabelDialog::name()
{
return edit->text();
}
int AddLabelDialog::exec()
{
edit->clear();
list->clearSelection();
acceptButton->setDisabled(true);
list->setCurrentRow(0);
return QDialog::exec();
}
void AddLabelDialog::validateName(const QString &name)
{
if(name.isEmpty())
acceptButton->setDisabled(true);
else
acceptButton->setEnabled(true);
}

View File

@ -0,0 +1,31 @@
#ifndef ADD_LABEL_DIALOG_H
#define ADD_LABEL_DIALOG_H
#include <QtWidgets>
#include "yacreader_global.h"
class AddLabelDialog : public QDialog
{
Q_OBJECT
public:
explicit AddLabelDialog(QWidget *parent = 0);
YACReader::LabelColors selectedColor();
QString name();
signals:
public slots:
int exec();
protected slots:
void validateName(const QString & name);
protected:
QLineEdit * edit;
QListWidget * list;
QPushButton * acceptButton;
QPushButton * cancelButton;
};
#endif // ADD_LABEL_DIALOG_H

View File

@ -0,0 +1,124 @@
#include "add_library_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QGridLayout>
AddLibraryDialog::AddLibraryDialog(QWidget * parent)
:QDialog(parent)
{
setupUI();
}
void AddLibraryDialog::setupUI()
{
textLabel = new QLabel(tr("Comics folder : "));
path = new QLineEdit;
textLabel->setBuddy(path);
connect(path,SIGNAL(textChanged(QString)),this,SLOT(pathSetted(QString)));
nameLabel = new QLabel(tr("Library Name : "));
nameEdit = new QLineEdit;
nameLabel->setBuddy(nameEdit);
connect(nameEdit,SIGNAL(textChanged(QString)),this,SLOT(nameSetted(QString)));
accept = new QPushButton(tr("Add"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(add()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
find = new QPushButton(QIcon(":/images/find_folder.png"),"");
connect(find,SIGNAL(clicked()),this,SLOT(findPath()));
QGridLayout * content = new QGridLayout;
content->addWidget(nameLabel,0,0);
content->addWidget(nameEdit,0,1);
content->addWidget(textLabel,1,0);
content->addWidget(path,1,1);
content->addWidget(find,1,2);
content->setColumnStretch(2,0);
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addStretch();
bottomLayout->addWidget(accept);
bottomLayout->addWidget(cancel);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(content);
mainLayout->addStretch();
mainLayout->addLayout(bottomLayout);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/openLibrary.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);//,0,Qt::AlignTop);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Add an existing library"));
}
void AddLibraryDialog::add()
{
//accept->setEnabled(false);
emit(addLibrary(QDir::cleanPath(path->text()),nameEdit->text()));
}
void AddLibraryDialog::nameSetted(const QString & text)
{
if(!text.isEmpty())
{
if(!path->text().isEmpty())
{
QFileInfo fi(path->text());
if(fi.isDir())
accept->setEnabled(true);
else
accept->setEnabled(false);
}
}
else
accept->setEnabled(false);
}
void AddLibraryDialog::pathSetted(const QString & text)
{
QFileInfo fi(text);
if(fi.isDir())
{
if(!nameEdit->text().isEmpty())
accept->setEnabled(true);
}
else
accept->setEnabled(false);
}
void AddLibraryDialog::findPath()
{
QString s = QFileDialog::getExistingDirectory(0,"Comics directory",".");
if(!s.isEmpty())
{
path->setText(s);
if(!nameEdit->text().isEmpty())
accept->setEnabled(true);
}
else
accept->setEnabled(false);
}
void AddLibraryDialog::close()
{
path->clear();
nameEdit->clear();
accept->setEnabled(false);
QDialog::close();
}

View File

@ -0,0 +1,35 @@
#ifndef __ADD_LIBRARY_DIALOG_H
#define __ADD_LIBRARY_DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QThread>
class AddLibraryDialog : public QDialog
{
Q_OBJECT
public:
AddLibraryDialog(QWidget * parent = 0);
private:
QLabel * nameLabel;
QLabel * textLabel;
QLineEdit * path;
QLineEdit * nameEdit;
QPushButton * find;
QPushButton * accept;
QPushButton * cancel;
void setupUI();
public slots:
void add();
void findPath();
void close();
void nameSetted(const QString & text);
void pathSetted(const QString & text);
signals:
void addLibrary(QString target, QString name);
};
#endif

View File

@ -0,0 +1,13 @@
#include "bundle_creator.h"
BundleCreator::BundleCreator(void)
:QObject()
{
}
BundleCreator::~BundleCreator(void)
{
}

View File

@ -0,0 +1,14 @@
#ifndef __BUNDLE_CREATOR_H
#define __BUNDLE_CREATOR_H
#include <QtCore>
class BundleCreator : public QObject
{
Q_OBJECT
public:
BundleCreator(void);
~BundleCreator(void);
};
#endif

View File

@ -0,0 +1,377 @@
#include "classic_comics_view.h"
#include "QStackedWidget"
#include "comic_flow_widget.h"
#include "QsLog.h"
#include "shortcuts_manager.h"
#include "yacreader_table_view.h"
#include "yacreader_tool_bar_stretch.h"
ClassicComicsView::ClassicComicsView(QWidget *parent)
:ComicsView(parent),searching(false)
{
QHBoxLayout * layout = new QHBoxLayout;
settings = new QSettings(YACReader::getSettingsPath()+"/YACReaderLibrary.ini",QSettings::IniFormat); //TODO unificar la creación del fichero de config con el servidor
settings->beginGroup("libraryConfig");
//FLOW-----------------------------------------------------------------------
//---------------------------------------------------------------------------
//FORCE_ANGLE is not used here, because ComicFlowWidgetGL will use OpenGL ES in the future
#ifndef NO_OPENGL
if((settings->value(USE_OPEN_GL).toBool() == true))
comicFlow = new ComicFlowWidgetGL(0);
else
comicFlow = new ComicFlowWidgetSW(0);
#else
comicFlow = new ComicFlowWidgetSW(0);
#endif
comicFlow->updateConfig(settings);
comicFlow->setFocusPolicy(Qt::StrongFocus);
comicFlow->setShowMarks(true);
setFocusProxy(comicFlow);
comicFlow->setFocus(Qt::OtherFocusReason);
comicFlow->setContextMenuPolicy(Qt::CustomContextMenu);
//layout-----------------------------------------------
sVertical = new QSplitter(Qt::Vertical); //spliter derecha
stack = new QStackedWidget;
stack->addWidget(comicFlow);
setupSearchingIcon();
stack->addWidget(searchingIcon);
sVertical->addWidget(stack);
comics = new QWidget;
QVBoxLayout * comicsLayout = new QVBoxLayout;
comicsLayout->setSpacing(0);
comicsLayout->setContentsMargins(0,0,0,0);
//TODO ComicsView:(set toolbar) comicsLayout->addWidget(editInfoToolBar);
tableView = new YACReaderTableView;
tableView->verticalHeader()->hide();
tableView->setFocusPolicy(Qt::StrongFocus);
comicsLayout->addWidget(tableView);
comics->setLayout(comicsLayout);
sVertical->addWidget(comics);
tableView->setContextMenuPolicy(Qt::CustomContextMenu);
//config--------------------------------------------------
if(settings->contains(COMICS_VIEW_HEADERS))
tableView->horizontalHeader()->restoreState(settings->value(COMICS_VIEW_HEADERS).toByteArray());
//connections---------------------------------------------
connect(tableView, SIGNAL(clicked(QModelIndex)), this, SLOT(centerComicFlow(QModelIndex)));
connect(tableView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(selectedComicForOpening(QModelIndex)));
connect(comicFlow, SIGNAL(centerIndexChanged(int)), this, SLOT(updateTableView(int)));
connect(tableView, SIGNAL(comicRated(int,QModelIndex)), this, SIGNAL(comicRated(int,QModelIndex)));
connect(comicFlow, SIGNAL(selected(uint)), this, SIGNAL(selected(uint)));
connect(tableView->horizontalHeader(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(saveTableHeadersStatus()));
connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(saveTableHeadersStatus()));
connect(comicFlow, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(requestedViewContextMenu(QPoint)));
connect(tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(requestedItemContextMenu(QPoint)));
layout->addWidget(sVertical);
setLayout(layout);
layout->setMargin(0);
#ifdef Q_OS_MAC
sVertical->setCollapsible(1,false);
#endif
if(settings->contains(COMICS_VIEW_FLOW_SPLITTER_STATUS))
sVertical->restoreState(settings->value(COMICS_VIEW_FLOW_SPLITTER_STATUS).toByteArray());
//hide flow widgets
hideFlowViewAction = new QAction(this);
hideFlowViewAction->setText(tr("Hide comic flow"));
hideFlowViewAction->setData(HIDE_COMIC_VIEW_ACTION_YL);
hideFlowViewAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(HIDE_COMIC_VIEW_ACTION_YL));
hideFlowViewAction->setIcon(QIcon(":/images/comics_view_toolbar/hideComicFlow.png"));
hideFlowViewAction->setCheckable(true);
hideFlowViewAction->setChecked(false);
connect(hideFlowViewAction, SIGNAL(toggled(bool)),this, SLOT(hideComicFlow(bool)));
}
void ClassicComicsView::hideComicFlow(bool hide)
{
if(hide)
{
QList<int> sizes;
sizes.append(0);
int total = sVertical->sizes().at(0) + sVertical->sizes().at(1);
sizes.append(total);
sVertical->setSizes(sizes);
}
else
{
QList<int> sizes;
int total = sVertical->sizes().at(0) + sVertical->sizes().at(1);
sizes.append(2*total/3);
sizes.append(total/3);
sVertical->setSizes(sizes);
}
}
//the toolbar has to be populated
void ClassicComicsView::setToolBar(QToolBar *toolBar)
{
static_cast<QVBoxLayout *>(comics->layout())->insertWidget(0,toolBar);
this->toolbar = toolBar;
toolBarStretch = new YACReaderToolBarStretch(this);
toolBarStretchAction = toolBar->addWidget(toolBarStretch);
toolBar->addAction(hideFlowViewAction);
}
void ClassicComicsView::setModel(ComicModel *model)
{
ComicsView::setModel(model);
if(model == NULL)
{
comicFlow->clear();
}
else
{
connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), this, SLOT(applyModelChanges(QModelIndex,QModelIndex,QVector<int>)),Qt::UniqueConnection);
connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(removeItemsFromFlow(QModelIndex,int,int)),Qt::UniqueConnection);
connect(model, SIGNAL(resortedIndexes(QList<int>)),comicFlow,SLOT(resortCovers(QList<int>)),Qt::UniqueConnection);
connect(model, SIGNAL(newSelectedIndex(QModelIndex)),this,SLOT(setCurrentIndex(QModelIndex)),Qt::UniqueConnection);
tableView->setModel(model);
if(model->rowCount()>0)
tableView->setCurrentIndex(model->index(0,0));
tableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
#if QT_VERSION >= 0x050000
tableView->horizontalHeader()->setSectionsMovable(true);
#else
tableView->horizontalHeader()->setMovable(true);
#endif
//TODO parametrizar la configuración de las columnas
/*if(!settings->contains(COMICS_VIEW_HEADERS))
{*/
for(int i = 0;i<tableView->horizontalHeader()->count();i++)
tableView->horizontalHeader()->hideSection(i);
tableView->horizontalHeader()->showSection(ComicModel::Number);
tableView->horizontalHeader()->showSection(ComicModel::Title);
tableView->horizontalHeader()->showSection(ComicModel::FileName);
tableView->horizontalHeader()->showSection(ComicModel::NumPages);
tableView->horizontalHeader()->showSection(ComicModel::Hash); //Size is part of the Hash...TODO add Columns::Size to Columns
tableView->horizontalHeader()->showSection(ComicModel::ReadColumn);
tableView->horizontalHeader()->showSection(ComicModel::CurrentPage);
tableView->horizontalHeader()->showSection(ComicModel::Rating);
//}
//debido a un bug, qt4 no es capaz de ajustar el ancho teniendo en cuenta todas la filas (no sólo las visibles)
//así que se ecala la primera vez y después se deja el control al usuario.
//if(!settings->contains(COMICS_VIEW_HEADERS))
QStringList paths = model->getPaths(model->getCurrentPath());//TODO ComicsView: get currentpath from somewhere currentPath());
comicFlow->setImagePaths(paths);
comicFlow->setMarks(model->getReadList());
//comicFlow->setFocus(Qt::OtherFocusReason);
if(settings->contains(COMICS_VIEW_HEADERS))
tableView->horizontalHeader()->restoreState(settings->value(COMICS_VIEW_HEADERS).toByteArray());
tableView->resizeColumnsToContents();
tableView->horizontalHeader()->setStretchLastSection(true);
}
}
void ClassicComicsView::setCurrentIndex(const QModelIndex &index)
{
tableView->setCurrentIndex(index);
centerComicFlow(index);
}
QModelIndex ClassicComicsView::currentIndex()
{
return tableView->currentIndex();
}
QItemSelectionModel *ClassicComicsView::selectionModel()
{
return tableView->selectionModel();
}
void ClassicComicsView::scrollTo(const QModelIndex & mi, QAbstractItemView::ScrollHint hint)
{
Q_UNUSED(hint);
comicFlow->setCenterIndex(mi.row());
}
void ClassicComicsView::toFullScreen()
{
comicFlow->hide();
comicFlow->setCenterIndex(comicFlow->centerIndex());
comics->hide();
//showFullScreen() //parent windows
comicFlow->show();
comicFlow->setFocus(Qt::OtherFocusReason);
}
void ClassicComicsView::toNormal()
{
comicFlow->hide();
comicFlow->setCenterIndex(comicFlow->centerIndex());
comicFlow->render();
comics->show();
comicFlow->show();
}
void ClassicComicsView::updateConfig(QSettings *settings)
{
comicFlow->updateConfig(settings);
}
void ClassicComicsView::enableFilterMode(bool enabled)
{
if(enabled)
{
comicFlow->clear();
if(previousSplitterStatus.isEmpty())
previousSplitterStatus = sVertical->saveState();
sVertical->setSizes(QList<int> () << 100 << 10000000);
showSearchingIcon();
}else
{
hideSearchingIcon();
sVertical->restoreState(previousSplitterStatus);
previousSplitterStatus.clear();
}
//sVertical->setCollapsible(0,!enabled);
searching = enabled;
}
void ClassicComicsView::selectIndex(int index)
{
tableView->selectRow(index);
}
void ClassicComicsView::selectAll()
{
tableView->selectAll();
}
void ClassicComicsView::selectedComicForOpening(const QModelIndex &mi)
{
emit selected(mi.row());
}
void ClassicComicsView::requestedViewContextMenu(const QPoint &point)
{
emit customContextMenuViewRequested(comicFlow->mapTo(this, point));
}
void ClassicComicsView::requestedItemContextMenu(const QPoint &point)
{
emit customContextMenuItemRequested(tableView->mapTo(this, point));
}
void ClassicComicsView::setShowMarks(bool show)
{
comicFlow->setShowMarks(show);
}
void ClassicComicsView::centerComicFlow(const QModelIndex & mi)
{
comicFlow->showSlide(mi.row());
comicFlow->setFocus(Qt::OtherFocusReason);
}
void ClassicComicsView::updateTableView(int i)
{
QModelIndex mi = model->index(i,2);
tableView->setCurrentIndex(mi);
tableView->scrollTo(mi,QAbstractItemView::EnsureVisible);
}
void ClassicComicsView::saveTableHeadersStatus()
{
settings->setValue(COMICS_VIEW_HEADERS,tableView->horizontalHeader()->saveState());
}
void ClassicComicsView::saveSplitterStatus()
{
settingsMutex.lock();
if(!searching)
settings->setValue(COMICS_VIEW_FLOW_SPLITTER_STATUS, sVertical->saveState());
settingsMutex.unlock();
}
void ClassicComicsView::applyModelChanges(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles)
{
Q_UNUSED(topLeft);
Q_UNUSED(bottomRight);
if(roles.contains(ComicModel::ReadColumnRole))
{
comicFlow->setMarks(model->getReadList());
comicFlow->updateMarks();
}
}
void ClassicComicsView::removeItemsFromFlow(const QModelIndex &parent, int from, int to)
{
Q_UNUSED(parent);
for(int i = from; i<=to; i++)
comicFlow->remove(i);
}
void ClassicComicsView::closeEvent(QCloseEvent *event)
{
toolbar->removeAction(toolBarStretchAction);
toolbar->removeAction(hideFlowViewAction);
saveTableHeadersStatus();
saveSplitterStatus();
ComicsView::closeEvent(event);
}
void ClassicComicsView::setupSearchingIcon()
{
searchingIcon = new QWidget(comicFlow);
QHBoxLayout * h = new QHBoxLayout;
QPixmap p(":/images/searching_icon.png");
QLabel * l = new QLabel(searchingIcon);
l->setPixmap(p);
l->setFixedSize(p.size());
h->addWidget(l,0,Qt::AlignCenter);
searchingIcon->setLayout(h);
QPalette pal(searchingIcon->palette());
pal.setColor(QPalette::Background, Qt::black);
searchingIcon->setAutoFillBackground(true);
searchingIcon->setPalette(pal);
hideSearchingIcon();
}
void ClassicComicsView::showSearchingIcon()
{
stack->setCurrentWidget(searchingIcon);
}
void ClassicComicsView::hideSearchingIcon()
{
stack->setCurrentWidget(comicFlow);
}

View File

@ -0,0 +1,79 @@
#ifndef CLASSIC_COMICS_VIEW_H
#define CLASSIC_COMICS_VIEW_H
#include "comics_view.h"
#include <QModelIndex>
#include <QModelIndexList>
class QSplitter;
class QStackedWidget;
class QToolBar;
class ComicFlowWidget;
class ComicModel;
class YACReaderTableView;
class YACReaderToolBarStretch;
class ClassicComicsView : public ComicsView
{
Q_OBJECT
public:
ClassicComicsView(QWidget *parent = 0);
void setToolBar(QToolBar * toolBar);
void setModel(ComicModel *model);
QModelIndex currentIndex();
QItemSelectionModel * selectionModel();
void scrollTo(const QModelIndex & mi, QAbstractItemView::ScrollHint hint );
void toFullScreen();
void toNormal();
void updateConfig(QSettings * settings);
void enableFilterMode(bool enabled);
void selectIndex(int index);
public slots:
void setCurrentIndex(const QModelIndex &index);
void centerComicFlow(const QModelIndex & mi);
void updateTableView(int i);
void saveTableHeadersStatus();
void saveSplitterStatus();
void applyModelChanges(const QModelIndex & topLeft,const QModelIndex & bottomRight,const QVector<int> & roles);
void removeItemsFromFlow(const QModelIndex & parent, int from, int to);
//ComicsView
void setShowMarks(bool show);
void selectAll();
void selectedComicForOpening(const QModelIndex & mi);
protected slots:
void hideComicFlow(bool hide);
void requestedViewContextMenu(const QPoint & point);
void requestedItemContextMenu(const QPoint & point);
private:
YACReaderTableView * tableView;
YACReaderToolBarStretch * toolBarStretch;
QAction * toolBarStretchAction;
QToolBar * toolbar;
QWidget *comics;
QSplitter * sVertical;
ComicFlowWidget * comicFlow;
QSettings * settings;
void closeEvent ( QCloseEvent * event );
QAction * hideFlowViewAction;
QStackedWidget * stack;
QByteArray previousSplitterStatus;
QWidget * searchingIcon;
bool searching;
void setupSearchingIcon();
void showSearchingIcon();
void hideSearchingIcon();
void updateSearchingIconPosition();
QMutex settingsMutex;
};
#endif // CLASSIC_COMICS_VIEW_H

View File

@ -0,0 +1,108 @@
#include "comic_files_manager.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QsLog.h>
#include "comic.h"
ComicFilesManager::ComicFilesManager(QObject *parent) :
QObject(parent), canceled(false)
{
}
void ComicFilesManager::copyComicsTo(const QList<QPair<QString,QString> > &sourceComics, const QString &folderDest, const QModelIndex & dest)
{
comics = sourceComics;
folder = folderDest;
folderDestinationModelIndex = dest;
move = false;
}
void ComicFilesManager::moveComicsTo(const QList<QPair<QString, QString> > &sourceComics, const QString &folderDest, const QModelIndex &dest)
{
comics = sourceComics;
folder = folderDest;
folderDestinationModelIndex = dest;
move = true;
}
QList<QPair<QString, QString> > ComicFilesManager::getDroppedFiles(const QList<QUrl> &urls)
{
QList<QPair<QString,QString> > dropedFiles;
QString currentPath;
foreach(QUrl url, urls)
{
currentPath = url.toLocalFile();
if(currentPath.endsWith('/'))
currentPath = currentPath.remove(currentPath.length()-1,1); //QTBUG-35896 QUrl.toLocalFile inconsistency.
if(Comic::fileIsComic(currentPath))
dropedFiles << QPair<QString, QString>(currentPath,"/");
else
{
QLOG_DEBUG() << "XXXXXXXXXXXX :" << currentPath;
QFileInfo info(currentPath);
if(info.isDir())
{
QLOG_DEBUG() << "origin path prior to absoluteFilePath : " << info.absolutePath();
foreach(QString comicPath, Comic::findValidComicFilesInFolder(info.absoluteFilePath()))
{
QFileInfo comicInfo(comicPath);
QString path = comicInfo.absolutePath();
QLOG_DEBUG() << "comic path : " << comicPath;
QLOG_DEBUG() << "full comic path : " << path;
QLOG_DEBUG() << "origin path : " << info.absolutePath();
dropedFiles << QPair<QString, QString>(comicPath, path.remove(info.absolutePath()));
}
}
}
}
return dropedFiles;
}
void ComicFilesManager::process()
{
int i=0;
bool successProcesingFiles = false;
QPair<QString, QString> source;
foreach (source, comics) {
if(canceled)
{
if(successProcesingFiles)
emit success(folderDestinationModelIndex);
emit finished();
return; //TODO rollback?
}
QFileInfo info(source.first);
QString destPath = QDir::cleanPath(folder+'/'+source.second);
QLOG_DEBUG() << "crear : " << destPath;
QDir().mkpath(destPath);
if(QFile::copy(source.first, QDir::cleanPath(destPath+'/'+info.fileName())))
{
successProcesingFiles = true;
if(move)
{
QFile::remove(source.first); //TODO: remove the whole path....
}
}
i++;
emit progress(i);
}
if(successProcesingFiles)
emit success(folderDestinationModelIndex);
emit finished();
}
void ComicFilesManager::cancel()
{
QLOG_DEBUG() << "Operation canceled";
canceled = true;
}

View File

@ -0,0 +1,37 @@
#ifndef COMIC_FILES_MANAGER_H
#define COMIC_FILES_MANAGER_H
#include <QObject>
#include <QList>
#include <QPair>
#include <QModelIndex>
//this class is intended to work in background, just use moveToThread and process to start working
class ComicFilesManager : public QObject
{
Q_OBJECT
public:
explicit ComicFilesManager(QObject *parent = 0);
void copyComicsTo(const QList<QPair<QString,QString> > & sourceComics, const QString & folderDest, const QModelIndex &dest);
void moveComicsTo(const QList<QPair<QString,QString> > & comics, const QString & folderDest, const QModelIndex &dest);
static QList<QPair<QString, QString> > getDroppedFiles(const QList<QUrl> & urls);
signals:
void currentComic(QString);
void progress(int);
void finished();
void success(QModelIndex); //at least one comics has been copied or moved
public slots:
void process();
void cancel();
protected:
bool move;
bool canceled;
QList<QPair<QString,QString> > comics;
QString folder;
QModelIndex folderDestinationModelIndex;
};
#endif // COMIC_FILES_MANAGER_H

View File

@ -0,0 +1,265 @@
#include "comic_flow.h"
#include "qnaturalsorting.h"
#include "yacreader_global.h"
#include <algorithm>
#include <QMutex>
#include <QImageReader>
#include <QTimer>
ComicFlow::ComicFlow(QWidget* parent,FlowType flowType)
:YACReaderFlow(parent,flowType)
{
updateTimer = new QTimer;
connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateImageData()));
worker = new ImageLoader;
connect(this, SIGNAL(centerIndexChanged(int)), this, SLOT(preload()));
connect(this, SIGNAL(centerIndexChangedSilent(int)), this, SLOT(preload()));
setReflectionEffect(PlainReflection);
}
ComicFlow::~ComicFlow()
{
worker->terminate();
delete worker;
delete updateTimer;
}
void ComicFlow::setImagePaths(const QStringList& paths)
{
clear();
//imagePath = path;
imageFiles = paths;
imagesLoaded.clear();
imagesLoaded.fill(false,imageFiles.size());
numImagesLoaded = 0;
imagesSetted.clear();
imagesSetted.fill(false,imageFiles.size());
// populate with empty images
QImage img; //TODO remove
QString s;
for(int i = 0; i < (int)imageFiles.size(); i++)
{
addSlide(img);
s = imageFiles.at(i);
s.remove(s.size()-4,4);
if(QFileInfo(s+".r").exists())
markSlide(i);
}
setCenterIndex(0);
worker->reset();
preload();
}
void ComicFlow::preload()
{
if(numImagesLoaded < imagesLoaded.size())
updateTimer->start(30); //TODO comprobar rendimiento, originalmente era 70
}
void ComicFlow::updateImageData()
{
// can't do anything, wait for the next possibility
if(worker->busy())
return;
// set image of last one
int idx = worker->index();
if( idx >= 0 && !worker->result().isNull())
{
if(!imagesSetted[idx])
{
setSlide(idx, worker->result());
imagesSetted[idx] = true;
numImagesLoaded++;
imagesLoaded[idx]=true;
}
}
// try to load only few images on the left and right side
// i.e. all visible ones plus some extra
#define COUNT 8
int indexes[2*COUNT+1];
int center = centerIndex();
indexes[0] = center;
for(int j = 0; j < COUNT; j++)
{
indexes[j*2+1] = center+j+1;
indexes[j*2+2] = center-j-1;
}
for(int c = 0; c < 2*COUNT+1; c++)
{
int i = indexes[c];
if((i >= 0) && (i < slideCount()))
if(!imagesLoaded[i])//slide(i).isNull())
{
// schedule thumbnail generation
QString fname = imageFiles[i];
worker->generate(i, fname, slideSize());
return;
}
}
// no need to generate anything? stop polling...
updateTimer->stop();
}
void ComicFlow::keyPressEvent(QKeyEvent* event)
{
PictureFlow::keyPressEvent(event);
}
void ComicFlow::wheelEvent(QWheelEvent * event)
{
if(event->delta()<0)
showNext();
else
showPrevious();
event->accept();
}
void ComicFlow::removeSlide(int cover)
{
worker->lock();
worker->reset();
imageFiles.removeAt(cover);
if(imagesLoaded[cover])
numImagesLoaded--;
imagesLoaded.remove(cover);
imagesSetted.remove(cover);
YACReaderFlow::removeSlide(cover);
worker->unlock();
preload();
}
void ComicFlow::resortCovers(QList<int> newOrder)
{
worker->lock();
worker->reset();
YACReaderFlow::resortCovers(newOrder);
QStringList imageFilesNew;
QVector<bool> imagesLoadedNew;
QVector<bool> imagesSettedNew;
foreach(int index, newOrder)
{
imageFilesNew << imageFiles.at(index);
imagesLoadedNew << imagesLoaded.at(index);
imagesSettedNew << imagesSetted.at(index);
}
imageFiles = imageFilesNew;
imagesLoaded = imagesLoadedNew;
imagesSetted = imagesSettedNew;
worker->unlock();
}
//-----------------------------------------------------------------------------
//ImageLoader
//-----------------------------------------------------------------------------
static QImage loadImage(const QString& fileName)
{
QImage image;
bool result = image.load(fileName);
if(!result)
return QImage();
return image;
}
ImageLoader::ImageLoader():
QThread(), restart(false), working(false), idx(-1)
{
}
ImageLoader::~ImageLoader()
{
mutex.lock();
condition.wakeOne();
mutex.unlock();
wait();
}
bool ImageLoader::busy() const
{
return isRunning() ? working : false;
}
void ImageLoader::generate(int index, const QString& fileName, QSize size)
{
mutex.lock();
this->idx = index;
this->fileName = fileName;
this->size = size;
this->img = QImage();
mutex.unlock();
if (!isRunning())
start();
else
{
// already running, wake up whenever ready
restart = true;
condition.wakeOne();
}
}
void ImageLoader::lock()
{
mutex.lock();
}
void ImageLoader::unlock()
{
mutex.unlock();
}
void ImageLoader::run()
{
for(;;)
{
// copy necessary data
mutex.lock();
this->working = true;
QString fileName = this->fileName;
mutex.unlock();
QImage image = loadImage(fileName);
// let everyone knows it is ready
mutex.lock();
this->working = false;
this->img = image;
mutex.unlock();
// put to sleep
mutex.lock();
if (!this->restart)
condition.wait(&mutex);
restart = false;
mutex.unlock();
}
}
QImage ImageLoader::result()
{
return img;
}

View File

@ -0,0 +1,78 @@
#ifndef __COMICFLOW_H
#define __COMICFLOW_H
#include "yacreader_flow.h"
#include <QtCore>
#include <QObject>
#include <QThread>
#include <QImage>
#include <QMutex>
#include <QWaitCondition>
#include <QString>
#include <QWheelEvent>
class ImageLoader;
class ComicFlow : public YACReaderFlow
{
Q_OBJECT
public:
ComicFlow(QWidget* parent = 0,FlowType flowType = CoverFlowLike);
virtual ~ComicFlow();
void setImagePaths(const QStringList& paths);
//bool eventFilter(QObject *target, QEvent *event);
void keyPressEvent(QKeyEvent* event);
void removeSlide(int cover);
void resortCovers(QList<int> newOrder);
private slots:
void preload();
void updateImageData();
private:
//QString imagePath;
QStringList imageFiles;
QVector<bool> imagesLoaded;
QVector<bool> imagesSetted;
int numImagesLoaded;
QTimer* updateTimer;
ImageLoader* worker;
virtual void wheelEvent(QWheelEvent * event);
};
//-----------------------------------------------------------------------------
// Source code of ImageLoader class was modified from http://code.google.com/p/photoflow/
//------------------------------------------------------------------------------
class ImageLoader : public QThread
{
public:
ImageLoader();
~ImageLoader();
// returns FALSE if worker is still busy and can't take the task
bool busy() const;
void generate(int index, const QString& fileName, QSize size);
void reset(){idx = -1;};
int index() const { return idx; };
void lock();
void unlock();
QImage result();
protected:
void run();
private:
QMutex mutex;
QWaitCondition condition;
bool restart;
bool working;
int idx;
QString fileName;
QSize size;
QImage img;
};
#endif

View File

@ -0,0 +1,355 @@
#include "comic_flow_widget.h"
#include <QVBoxLayout>
ComicFlowWidget::ComicFlowWidget(QWidget * parent)
:QWidget(parent)
{
}
ComicFlowWidgetSW::ComicFlowWidgetSW(QWidget * parent)
:ComicFlowWidget(parent)
{
flow = new ComicFlow(parent);
connect(flow,SIGNAL(centerIndexChanged(int)),this,SIGNAL(centerIndexChanged(int)));
connect(flow,SIGNAL(selected(unsigned int)),this,SIGNAL(selected(unsigned int)));
QVBoxLayout * l = new QVBoxLayout;
l->addWidget(flow);
setLayout(l);
//TODO eleminar "padding"
QPalette Pal(palette());
// set black background
Pal.setColor(QPalette::Background, Qt::black);
setAutoFillBackground(true);
setPalette(Pal);
//config
QMatrix m;
m.rotate(-90);
m.scale(-1,1);
QImage image(":/images/setRead.png");
QImage imageTransformed = image.transformed(m,Qt::SmoothTransformation);
setMarkImage(imageTransformed);
}
QSize ComicFlowWidgetSW::minimumSizeHint() const
{
return flow->minimumSizeHint();
}
QSize ComicFlowWidgetSW::sizeHint() const
{
return flow->sizeHint();
}
void ComicFlowWidgetSW::setShowMarks(bool value)
{
flow->setShowMarks(value);
}
void ComicFlowWidgetSW::setMarks(QVector<YACReaderComicReadStatus> marks)
{
flow->setMarks(marks);
}
void ComicFlowWidgetSW::setMarkImage(QImage & image)
{
flow->setMarkImage(image);
}
void ComicFlowWidgetSW::markSlide(int index, YACReaderComicReadStatus status)
{
flow->markSlide(index,status);
}
void ComicFlowWidgetSW::unmarkSlide(int index)
{
flow->unmarkSlide(index);
}
void ComicFlowWidgetSW::setSlideSize(QSize size)
{
flow->setSlideSize(size);
}
void ComicFlowWidgetSW::clear()
{
flow->clear();
}
void ComicFlowWidgetSW::setImagePaths(QStringList paths)
{
flow->setImagePaths(paths);
}
void ComicFlowWidgetSW::setCenterIndex(int index)
{
flow->setCenterIndex(index);
}
void ComicFlowWidgetSW::showSlide(int index)
{
flow->showSlide(index);
}
int ComicFlowWidgetSW::centerIndex()
{
return flow->centerIndex();
}
void ComicFlowWidgetSW::updateMarks()
{
flow->updateMarks();
}
void ComicFlowWidgetSW::setFlowType(FlowType flowType)
{
flow->setFlowType(flowType);
}
void ComicFlowWidgetSW::render()
{
flow->render();
}
void ComicFlowWidgetSW::keyPressEvent(QKeyEvent* event)
{
flow->keyPressEvent(event);
}
void ComicFlowWidgetSW::paintEvent(QPaintEvent *event)
{
flow->paintEvent(event);
}
void ComicFlowWidgetSW::mousePressEvent(QMouseEvent* event)
{
flow->mousePressEvent(event);
}
void ComicFlowWidgetSW::resizeEvent(QResizeEvent* event)
{
flow->resizeEvent(event);
}
void ComicFlowWidgetSW::mouseDoubleClickEvent(QMouseEvent* event)
{
flow->mouseDoubleClickEvent(event);
}
void ComicFlowWidgetSW::updateConfig(QSettings * settings)
{
switch (settings->value(FLOW_TYPE_SW).toInt())
{
case CoverFlowLike:
flow->setFlowType(CoverFlowLike);
return;
case Strip:
flow->setFlowType(Strip);
return;
case StripOverlapped:
flow->setFlowType(StripOverlapped);
return;
}
}
void ComicFlowWidgetSW::remove(int cover)
{
flow->removeSlide(cover);
}
void ComicFlowWidgetSW::resortCovers(QList<int> newOrder)
{
flow->resortCovers(newOrder);
}
#ifndef NO_OPENGL
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
///OpenGL ComicFlow
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
ComicFlowWidgetGL::ComicFlowWidgetGL(QWidget * parent)
:ComicFlowWidget(parent)
{
flow = new YACReaderComicFlowGL(parent);
connect(flow,SIGNAL(centerIndexChanged(int)),this,SIGNAL(centerIndexChanged(int)));
connect(flow,SIGNAL(selected(unsigned int)),this,SIGNAL(selected(unsigned int)));
QVBoxLayout * l = new QVBoxLayout;
l->addWidget(flow);
l->setContentsMargins(0,0,0,0);
setLayout(l);
//TODO eleminar "padding"
QPalette Pal(palette());
// set black background
Pal.setColor(QPalette::Background, Qt::black);
setAutoFillBackground(true);
setPalette(Pal);
}
QSize ComicFlowWidgetGL::minimumSizeHint() const
{
return flow->minimumSizeHint();
}
QSize ComicFlowWidgetGL::sizeHint() const
{
return flow->sizeHint();
}
void ComicFlowWidgetGL::setShowMarks(bool value)
{
flow->setShowMarks(value);
}
void ComicFlowWidgetGL::setMarks(QVector<YACReaderComicReadStatus> marks)
{
flow->setMarks(marks);
}
void ComicFlowWidgetGL::setMarkImage(QImage & image)
{
flow->setMarkImage(image);
}
void ComicFlowWidgetGL::markSlide(int index, YACReaderComicReadStatus status)
{
flow->markSlide(index,status);
}
void ComicFlowWidgetGL::unmarkSlide(int index)
{
flow->unmarkSlide(index);
}
void ComicFlowWidgetGL::setSlideSize(QSize size)
{
flow->setSlideSize(size);
}
void ComicFlowWidgetGL::clear()
{
flow->clear();
}
void ComicFlowWidgetGL::setImagePaths(QStringList paths)
{
flow->setImagePaths(paths);
}
void ComicFlowWidgetGL::setCenterIndex(int index)
{
flow->setCenterIndex(index);
}
void ComicFlowWidgetGL::showSlide(int index)
{
flow->showSlide(index);
}
int ComicFlowWidgetGL::centerIndex()
{
return flow->centerIndex();
}
void ComicFlowWidgetGL::updateMarks()
{
flow->updateMarks();
}
void ComicFlowWidgetGL::setFlowType(FlowType flowType)
{
if(flowType == CoverFlowLike)
flow->setPreset(presetYACReaderFlowClassicConfig);
else if(flowType == Strip)
flow->setPreset(presetYACReaderFlowStripeConfig);
else if(flowType == StripOverlapped)
flow->setPreset(presetYACReaderFlowOverlappedStripeConfig);
else
flow->setPreset(defaultYACReaderFlowConfig);
}
void ComicFlowWidgetGL::render()
{
flow->render();
}
void ComicFlowWidgetGL::keyPressEvent(QKeyEvent* event)
{
flow->keyPressEvent(event);
}
void ComicFlowWidgetGL::paintEvent(QPaintEvent *event)
{
//flow->paintEvent(event);
ComicFlowWidget::paintEvent(event);
}
void ComicFlowWidgetGL::mousePressEvent(QMouseEvent* event)
{
flow->mousePressEvent(event);
}
void ComicFlowWidgetGL::resizeEvent(QResizeEvent* event)
{
flow->resizeGL(event->size().width(),event->size().height());
}
void ComicFlowWidgetGL::mouseDoubleClickEvent(QMouseEvent* event)
{
flow->mouseDoubleClickEvent(event);
}
void ComicFlowWidgetGL::updateConfig(QSettings * settings)
{
Performance performance = medium;
switch (settings->value(PERFORMANCE).toInt())
{
case 0:
performance = low;
break;
case 1:
performance = medium;
break;
case 2:
performance = high;
break;
case 3:
performance = ultraHigh;
break;
}
flow->setPerformance(performance);
if(!settings->contains(V_SYNC))
flow->useVSync(false);
else
flow->useVSync(settings->value(V_SYNC).toBool());
switch (settings->value(FLOW_TYPE_GL).toInt())
{
case 0:
flow->setPreset(presetYACReaderFlowClassicConfig);
return;
case 1:
flow->setPreset(presetYACReaderFlowStripeConfig);
return;
case 2:
flow->setPreset(presetYACReaderFlowOverlappedStripeConfig);
return;
case 3:
flow->setPreset(defaultYACReaderFlowConfig);
return;
case 4:
flow->setPreset(pressetYACReaderFlowDownConfig);
return;
}
//custom config
flow->setCF_RX(settings->value(X_ROTATION).toInt());
flow->setCF_Y(settings->value(Y_POSITION).toInt());
flow->setX_Distance(settings->value(COVER_DISTANCE).toInt());
flow->setCenter_Distance(settings->value(CENTRAL_DISTANCE).toInt());
flow->setCF_Z(settings->value(ZOOM_LEVEL).toInt());
flow->setY_Distance(settings->value(Y_COVER_OFFSET).toInt());
flow->setZ_Distance(settings->value(Z_COVER_OFFSET).toInt());
flow->setRotation(settings->value(COVER_ROTATION).toInt());
flow->setFadeOutDist(settings->value(FADE_OUT_DIST).toInt());
flow->setLightStrenght(settings->value(LIGHT_STRENGTH).toInt());
flow->setMaxAngle(settings->value(MAX_ANGLE).toInt());
/* flow->setVisibility(settings->value("visibilityDistance").toInt());
flow->setLightStrenght(settings->value("lightStrength").toInt())*/;
}
void ComicFlowWidgetGL::remove(int cover)
{
flow->remove(cover);
}
void ComicFlowWidgetGL::resortCovers(QList<int> newOrder)
{
flow->resortCovers(newOrder);
}
#endif
//void ComicFlowWidgetGL::setCF_RX(int value){ flow->setCF_RX(value);}
//void ComicFlowWidgetGL::setCF_RY(int value){ flow->setCF_RY(value);}
//void ComicFlowWidgetGL::setCF_RZ(int value){ flow->setCF_RZ(value);}
//void ComicFlowWidgetGL::setZoom(int zoom){ flow->setZoom(zoom);}
//void ComicFlowWidgetGL::setRotation(int angle){ flow->setRotation(angle);}
//void ComicFlowWidgetGL::setX_Distance(int distance){ flow->setX_Distance(distance);}
//void ComicFlowWidgetGL::setCenter_Distance(int distance){ flow->setCenter_Distance(distance);}
//void ComicFlowWidgetGL::setZ_Distance(int distance){ flow->setZ_Distance(distance);}
//void ComicFlowWidgetGL::setCF_Y(int value){ flow->setCF_Y(value);}
//void ComicFlowWidgetGL::setY_Distance(int value){ flow->setY_Distance(value);}
//void ComicFlowWidgetGL::setPreset(const Preset & p){ flow->setPreset(p);}

View File

@ -0,0 +1,133 @@
#ifndef __COMIC_FLOW_WIDGET_H
#define __COMIC_FLOW_WIDGET_H
#include <QWidget>
#include "pictureflow.h"
#include "comic_flow.h"
#ifndef NO_OPENGL
#include "yacreader_flow_gl.h"
#endif
class ComicFlowWidget : public QWidget
{
Q_OBJECT
public:
ComicFlowWidget(QWidget * paret = 0);
public slots:
virtual void setShowMarks(bool value) = 0;
virtual void setMarks(QVector<YACReaderComicReadStatus> marks) = 0;
virtual void setMarkImage(QImage & image) = 0;
virtual void markSlide(int index, YACReaderComicReadStatus status) = 0;
virtual void unmarkSlide(int index) = 0;
virtual void setSlideSize(QSize size) = 0;
virtual void clear() = 0;
virtual void setImagePaths(QStringList paths) = 0;
virtual void setCenterIndex(int index) = 0;
virtual void showSlide(int index) = 0;
virtual int centerIndex() = 0;
virtual void updateMarks() = 0;
virtual void setFlowType(FlowType flowType) = 0;
virtual void render() = 0;
virtual void updateConfig(QSettings * settings) = 0;
virtual void remove(int cover) = 0;
virtual void resortCovers(QList<int> newOrder) = 0;
signals:
void centerIndexChanged(int);
void selected(unsigned int);
};
class ComicFlowWidgetSW : public ComicFlowWidget
{
Q_OBJECT
private:
ComicFlow * flow;
public:
ComicFlowWidgetSW(QWidget * parent = 0);
void setShowMarks(bool value);
void setMarks(QVector<YACReaderComicReadStatus> marks);
void setMarkImage(QImage & image);
void markSlide(int index, YACReaderComicReadStatus status);
void unmarkSlide(int index);
void setSlideSize(QSize size);
void clear();
void setImagePaths(QStringList paths);
void setCenterIndex(int index);
void showSlide(int index);
int centerIndex();
void updateMarks();
void setFlowType(FlowType flowType);
void render();
void updateConfig(QSettings * settings);
void remove(int cover);
void resortCovers(QList<int> newOrder);
protected:
void keyPressEvent(QKeyEvent* event);
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent* event);
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
QSize minimumSizeHint() const;
QSize sizeHint() const;
QSize slideSizeW;
QSize slideSizeF;
};
#ifndef NO_OPENGL
class ComicFlowWidgetGL : public ComicFlowWidget
{
Q_OBJECT
private:
YACReaderComicFlowGL * flow;
public:
ComicFlowWidgetGL(QWidget * parent = 0);
void setShowMarks(bool value);
void setMarks(QVector<YACReaderComicReadStatus> marks);
void setMarkImage(QImage & image);
void markSlide(int index, YACReaderComicReadStatus status);
void unmarkSlide(int index);
void setSlideSize(QSize size);
void clear();
void setImagePaths(QStringList paths);
void setCenterIndex(int index);
void showSlide(int index);
int centerIndex();
void updateMarks();
void setFlowType(FlowType flowType);
void render();
void updateConfig(QSettings * settings);
void remove(int cover);
void resortCovers(QList<int> newOrder);
//public slots:
// void setCF_RX(int value);
// //the Y Rotation of the Coverflow
// void setCF_RY(int value);
// //the Z Rotation of the Coverflow
// void setCF_RZ(int value);
// //perspective
// void setZoom(int zoom);
// void setRotation(int angle);
// //sets the distance between the covers
// void setX_Distance(int distance);
// //sets the distance between the centered and the non centered covers
// void setCenter_Distance(int distance);
// //sets the pushback amount
// void setZ_Distance(int distance);
// void setCF_Y(int value);
// void setY_Distance(int value);
// void setPreset(const Preset & p);
protected:
void keyPressEvent(QKeyEvent* event);
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent* event);
void resizeEvent(QResizeEvent* event);
void mouseDoubleClickEvent(QMouseEvent* event);
QSize minimumSizeHint() const;
QSize sizeHint() const;
};
#endif
#endif

View File

@ -0,0 +1,68 @@
#include "api_key_dialog.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QSettings>
#include "yacreader_global_gui.h"
ApiKeyDialog::ApiKeyDialog(QWidget *parent) :
QDialog(parent)
{
QVBoxLayout * layout = new QVBoxLayout;
QHBoxLayout * buttonsLayout = new QHBoxLayout;
settings = new QSettings(YACReader::getSettingsPath()+"/YACReaderLibrary.ini",QSettings::IniFormat); //TODO unificar la creación del fichero de config con el servidor
settings->beginGroup("ComicVine");
QLabel * info = new QLabel(tr("Before you can connect to Comic Vine, you need your own API key. Please, get one free <a href=\"http://www.comicvine.com/api/\">here</a>"));
info->setWordWrap(true);
info->setOpenExternalLinks(true);
edit = new QLineEdit();
edit->setPlaceholderText(tr("Paste here your Comic Vine API key"));
connect(edit,SIGNAL(textChanged(QString)),this,SLOT(enableAccept(QString)));
acceptButton = new QPushButton(tr("Accept"));
acceptButton->setDisabled(true);
connect(acceptButton,SIGNAL(clicked()),this,SLOT(saveApiKey()));
cancelButton = new QPushButton(tr("Cancel"));
connect(cancelButton,SIGNAL(clicked()),this,SLOT(reject()));
layout->addWidget(info);
layout->addWidget(edit);
layout->addStretch();
buttonsLayout->addStretch();
buttonsLayout->addWidget(acceptButton);
buttonsLayout->addWidget(cancelButton);
layout->addLayout(buttonsLayout);
setLayout(layout);
resize(400,150);
if(settings->contains(COMIC_VINE_API_KEY))
edit->setText(settings->value(COMIC_VINE_API_KEY).toString());
}
ApiKeyDialog::~ApiKeyDialog()
{
delete settings;
}
void ApiKeyDialog::enableAccept(const QString &text)
{
//TODO key validation
acceptButton->setEnabled(!text.isEmpty());
}
void ApiKeyDialog::saveApiKey()
{
settings->setValue(COMIC_VINE_API_KEY,edit->text());
accept();
}

View File

@ -0,0 +1,31 @@
#ifndef API_KEY_DIALOG_H
#define API_KEY_DIALOG_H
#include <QDialog>
class QPushButton;
class QLineEdit;
class QSettings;
class ApiKeyDialog : public QDialog
{
Q_OBJECT
public:
explicit ApiKeyDialog(QWidget *parent = 0);
~ApiKeyDialog();
signals:
public slots:
protected slots:
void enableAccept(const QString & text);
void saveApiKey();
protected:
QPushButton * acceptButton;
QPushButton * cancelButton;
QLineEdit * edit;
QSettings * settings;
};
#endif // API_KEY_DIALOG_H

View File

@ -0,0 +1,48 @@
HEADERS += \
comic_vine/comic_vine_dialog.h \
comic_vine/comic_vine_client.h \
comic_vine/scraper_lineedit.h \
comic_vine/title_header.h \
comic_vine/series_question.h \
comic_vine/search_single_comic.h \
comic_vine/search_volume.h \
comic_vine/select_comic.h \
comic_vine/select_volume.h \
comic_vine/model/volumes_model.h \
comic_vine/model/comics_model.h \
comic_vine/model/json_model.h \
comic_vine/model/response_parser.h \
comic_vine/scraper_tableview.h \
comic_vine/sort_volume_comics.h \
comic_vine/model/local_comic_list_model.h \
comic_vine/model/volume_comics_model.h \
comic_vine/scraper_scroll_label.h \
comic_vine/scraper_results_paginator.h \
comic_vine/scraper_selector.h \
comic_vine/api_key_dialog.h \
$$PWD/comic_vine_all_volume_comics_retriever.h
SOURCES += \
comic_vine/comic_vine_dialog.cpp \
comic_vine/comic_vine_client.cpp \
comic_vine/scraper_lineedit.cpp \
comic_vine/title_header.cpp \
comic_vine/series_question.cpp \
comic_vine/search_single_comic.cpp \
comic_vine/search_volume.cpp \
comic_vine/select_comic.cpp \
comic_vine/select_volume.cpp \
comic_vine/model/volumes_model.cpp \
comic_vine/model/comics_model.cpp \
comic_vine/model/json_model.cpp \
comic_vine/model/response_parser.cpp \
comic_vine/scraper_tableview.cpp \
comic_vine/sort_volume_comics.cpp \
comic_vine/model/local_comic_list_model.cpp \
comic_vine/model/volume_comics_model.cpp \
comic_vine/scraper_scroll_label.cpp \
comic_vine/scraper_results_paginator.cpp \
comic_vine/scraper_selector.cpp \
comic_vine/api_key_dialog.cpp \
$$PWD/comic_vine_all_volume_comics_retriever.cpp

View File

@ -0,0 +1,97 @@
#include "comic_vine_all_volume_comics_retriever.h"
#include "http_worker.h"
#include "response_parser.h"
#include <QtScript>
ComicVineAllVolumeComicsRetriever::ComicVineAllVolumeComicsRetriever(const QString &volumeURLString, QObject *parent)
: QObject(parent), volumeURLString(volumeURLString)
{
}
void ComicVineAllVolumeComicsRetriever::getAllVolumeComics()
{
getAllVolumeComics(0);
}
void ComicVineAllVolumeComicsRetriever::getAllVolumeComics(int range)
{
HttpWorker * search = new HttpWorker(volumeURLString.arg(range));
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SLOT(appendVolumeComicsInfo(const QByteArray &)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut()));
connect(search,SIGNAL(timeout()),this,SIGNAL(finished()));
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
void ComicVineAllVolumeComicsRetriever::appendVolumeComicsInfo(const QByteArray &data)
{
QString json(data);
jsonResponses.append(data);
ResponseParser rp;
rp.loadJSONResponse(json);
qint32 currentPage = rp.getCurrentPage();
qint32 totalPages = rp.getTotalPages();
bool isLastResponse = currentPage == totalPages;
if (!isLastResponse) {
getAllVolumeComics(currentPage * 100);
}
else
{
emit allVolumeComicsInfo(consolidateJSON());
emit finished();
}
}
QString ComicVineAllVolumeComicsRetriever::consolidateJSON()
{
QJsonObject consolidatedJSON;
QJsonArray comicsInfo;
foreach (QByteArray json, jsonResponses) {
QJsonDocument doc = QJsonDocument::fromJson(json);
if(doc.isNull() || !doc.isObject() || doc.isEmpty())
{
continue;
}
QJsonObject main = doc.object();
QJsonValue error = main["error"];
if (error.isUndefined() || error.toString() != "OK")
{
continue;
}
else
{
QJsonValue results = main["results"];
if (results.isUndefined() || !results.isArray())
{
continue;
}
QJsonArray resultsArray = results.toArray();
foreach (const QJsonValue & v, resultsArray)
comicsInfo.append(v);
}
}
consolidatedJSON["error"] = "OK";
consolidatedJSON["status_code"] = 1;
consolidatedJSON["number_of_total_results"] = comicsInfo.size();
consolidatedJSON["offset"] = 0;
consolidatedJSON["results"] = comicsInfo;
QJsonDocument doc(consolidatedJSON);
return doc.toJson(QJsonDocument::Compact);
}

View File

@ -0,0 +1,28 @@
#ifndef COMIC_VINE_ALL_VOLUME_COMICS_RETRIEVER_H
#define COMIC_VINE_ALL_VOLUME_COMICS_RETRIEVER_H
#include <QObject>
class ComicVineAllVolumeComicsRetriever : public QObject
{
Q_OBJECT
public:
explicit ComicVineAllVolumeComicsRetriever(const QString &volumeURLString, QObject *parent = 0);
void getAllVolumeComics();
protected:
void getAllVolumeComics(const int range);
signals:
void allVolumeComicsInfo(QString json);
void finished();
void timeOut();
protected slots:
void appendVolumeComicsInfo(const QByteArray &data);
protected:
QString volumeURLString;
QList<QByteArray> jsonResponses;
QString consolidateJSON();
};
#endif // COMIC_VINE_ALL_VOLUME_COMICS_RETRIEVER_H

View File

@ -0,0 +1,189 @@
#include "comic_vine_client.h"
#include "yacreader_global_gui.h"
#include "comic_vine_all_volume_comics_retriever.h"
//this is the API key used by YACReader to access Comic Vine
//please, do not use it in your own software, get one for free at Comic Vine
static const QString CV_API_KEY = "%CV_API_KEY%"; //get from settings
static const QString CV_API_KEY_DEFAULT = "46680bebb358f1de690a5a365e15d325f9649f91";
static const QString CV_WEB_ADDRESS = "%CV_WEB_ADDRESS%"; //get from settings
//gets any volumen containing any comic matching 'query'
static const QString CV_SEARCH = CV_WEB_ADDRESS + "/search/?api_key=" + CV_API_KEY +
"&format=json&limit=100&resources=volume"
"&field_list=name,start_year,publisher,id,image,count_of_issues,deck"
"&sort=name:asc"
"&query=%1&page=%2";
//http://www.comicvine.com/api/search/?api_key=46680bebb358f1de690a5a365e15d325f9649f91&format=json&limit=100&resources=volume&field_list=name,start_year,publisher,id,image,count_of_issues,deck&query=superman
//gets the detail for a volume %1
static const QString CV_SERIES_DETAIL = CV_WEB_ADDRESS + "/volume/4050-%1/?api_key=" + CV_API_KEY +
"&format=json&field_list=name,start_year,publisher,image,count_of_issues,id,description";
//gets info for comics in a volume id %1
static const QString CV_COMICS_INFO = CV_WEB_ADDRESS + "/issues/?api_key=" + CV_API_KEY +
"&limit=1000&format=json&field_list=name,issue_number,id,image&filter=volume:%1"
"&sort=cover_date:asc" //sorting by cover_date, because comic vine doesn't use natural sorting (issue_number -> 1 10 11 ... 100 2 20 21....)
"&offset=%2";
//"http://www.comicvine.com/api/issues/?api_key=46680bebb358f1de690a5a365e15d325f9649f91&format=json&field_list=name,issue_number,id,image&filter=volume:%1&page=%2
//gets id for comic number %2 in a volume id %1
static const QString CV_COMIC_ID = CV_WEB_ADDRESS + "/issues/?api_key=" + CV_API_KEY +
"&format=json&field_list=name,issue_number,id,image"
"&filter=volume:%1,issue_number:%2";
//gets comic detail
static const QString CV_COMIC_DETAIL = CV_WEB_ADDRESS + "/issue/4000-%1/?api_key=" + CV_API_KEY + "&format=json";
//http://www.comicvine.com/api/issue/4000-%1/?api_key=46680bebb358f1de690a5a365e15d325f9649f91&format=json
//gets comic cover URL
static const QString CV_COVER_URL = CV_WEB_ADDRESS + "/issue/4000-%1/?api_key=" + CV_API_KEY + "&format=json&field_list=image";
//gets comics matching name %1 and number %2
//http://comicvine.com/api/issues/?api_key=46680bebb358f1de690a5a365e15d325f9649f91&limit=20&filter=name:super,issue_number:15
ComicVineClient::ComicVineClient(QObject *parent) :
QObject(parent)
{
settings = new QSettings(YACReader::getSettingsPath()+"/YACReaderLibrary.ini",QSettings::IniFormat); //TODO unificar la creación del fichero de config con el servidor
settings->beginGroup("ComicVine");
baseURL = settings->value(COMIC_VINE_BASE_URL, "https://comicvine.gamespot.com/api").toString();
}
ComicVineClient::~ComicVineClient()
{
delete settings;
}
//CV_SEARCH
void ComicVineClient::search(const QString & query, int page)
{
HttpWorker * search = new HttpWorker(QString(CV_SEARCH).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY,settings->value(COMIC_VINE_API_KEY,CV_API_KEY_DEFAULT).toString()).arg(query).arg(page));
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SLOT(proccessVolumesSearchData(const QByteArray &)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut()));
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
//CV_SEARCH result
void ComicVineClient::proccessVolumesSearchData(const QByteArray & data)
{
QString json(data);
emit searchResult(json);
emit finished();
}
void ComicVineClient::proccessSeriesDetailData(const QByteArray &data)
{
QString json(data);
emit seriesDetail(json);
emit finished();
}
void ComicVineClient::processVolumeComicsInfo(const QByteArray &data)
{
QString json(data);
emit volumeComicsInfo(json);
emit finished();
}
void ComicVineClient::proccessComicDetailData(const QByteArray &data)
{
QString json(data);
emit comicDetail(json);
emit finished();
}
//CV_SERIES_DETAIL
void ComicVineClient::getSeriesDetail(const QString & id)
{
HttpWorker * search = new HttpWorker(QString(CV_SERIES_DETAIL).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY,settings->value(COMIC_VINE_API_KEY,CV_API_KEY_DEFAULT).toString()).arg(id));
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SLOT(proccessSeriesDetailData(const QByteArray &)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut()));
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
void ComicVineClient::getSeriesCover(const QString & url)
{
HttpWorker * search = new HttpWorker(url);
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SIGNAL(seriesCover(const QByteArray &)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut())); //TODO
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
//CV_COMIC_IDS
void ComicVineClient::getVolumeComicsInfo(const QString & idVolume, int page)
{
HttpWorker * search = new HttpWorker(QString(CV_COMICS_INFO).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY,settings->value(COMIC_VINE_API_KEY,CV_API_KEY_DEFAULT).toString()).arg(idVolume).arg((page-1)*100)); //page doesn't work for search, using offset instead
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SLOT(processVolumeComicsInfo(const QByteArray &)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut())); //TODO
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
void ComicVineClient::getAllVolumeComicsInfo(const QString &idVolume)
{
QString url = QString(CV_COMICS_INFO).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY,settings->value(COMIC_VINE_API_KEY,CV_API_KEY_DEFAULT).toString()).arg(idVolume);
ComicVineAllVolumeComicsRetriever * comicsRetriever = new ComicVineAllVolumeComicsRetriever(url);
connect(comicsRetriever, &ComicVineAllVolumeComicsRetriever::allVolumeComicsInfo, this, &ComicVineClient::volumeComicsInfo);
connect(comicsRetriever, &ComicVineAllVolumeComicsRetriever::finished, this, &ComicVineClient::finished);
connect(comicsRetriever, &ComicVineAllVolumeComicsRetriever::finished, this, &ComicVineAllVolumeComicsRetriever::deleteLater);
connect(comicsRetriever, &ComicVineAllVolumeComicsRetriever::timeOut, this, &ComicVineClient::timeOut);
comicsRetriever->getAllVolumeComics();
}
//CV_COMIC_ID
void ComicVineClient::getComicId(const QString & id, int comicNumber)
{
Q_UNUSED(id);
Q_UNUSED(comicNumber);
}
//CV_COMIC_DETAIL
QByteArray ComicVineClient::getComicDetail(const QString & id, bool & outError, bool & outTimeout)
{
HttpWorker * search = new HttpWorker(QString(CV_COMIC_DETAIL).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY,settings->value(COMIC_VINE_API_KEY,CV_API_KEY_DEFAULT).toString()).arg(id));
//connect(search,SIGNAL(dataReady(const QByteArray &)),this,SLOT(proccessComicDetailData(const QByteArray &)));
//connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut()));
//connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
search->wait();
outError = !(search->wasValid());
outTimeout = search->wasTimeout();
QByteArray result = search->getResult();
delete search;
return result;
}
//CV_COMIC_DETAIL
void ComicVineClient::getComicDetailAsync(const QString & id)
{
HttpWorker * search = new HttpWorker(QString(CV_COMIC_DETAIL).replace(CV_WEB_ADDRESS, baseURL).replace(CV_API_KEY,settings->value(COMIC_VINE_API_KEY,CV_API_KEY_DEFAULT).toString()).arg(id));
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SLOT(proccessComicDetailData(const QByteArray &)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut()));
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
void ComicVineClient::getComicCover(const QString &url)
{
HttpWorker * search = new HttpWorker(url);
connect(search,SIGNAL(dataReady(const QByteArray &)),this,SIGNAL(comicCover(QByteArray)));
connect(search,SIGNAL(timeout()),this,SIGNAL(timeOut())); //TODO
connect(search,SIGNAL(finished()),search,SLOT(deleteLater()));
search->get();
}
//CV_COVER_DETAIL
void ComicVineClient::getCoverURL(const QString & id)
{
Q_UNUSED(id);
}

View File

@ -0,0 +1,48 @@
#ifndef COMIC_VINE_CLIENT_H
#define COMIC_VINE_CLIENT_H
#include "http_worker.h"
#include <QObject>
#include <QSettings>
class ComicVineClient : public QObject
{
Q_OBJECT
public:
explicit ComicVineClient(QObject *parent = 0);
~ComicVineClient();
signals:
void searchResult(QString);
void seriesDetail(QString);//JSON
void comicDetail(QString);//JSON
void seriesCover(const QByteArray &);
void comicCover(const QByteArray &);
void volumeComicsInfo(QString);
void timeOut();
void finished();
public slots:
void search(const QString & query, int page = 1);
void getSeriesDetail(const QString & id);
void getSeriesCover(const QString & url);
void getVolumeComicsInfo(const QString & idVolume, int page=1);
void getAllVolumeComicsInfo(const QString & idVolume);
QByteArray getComicDetail(const QString & id, bool &outError, bool &outTimeout);
void getComicCover(const QString & url);
void getComicId(const QString & id, int comicNumber);
void getCoverURL(const QString & id);
void getComicDetailAsync(const QString &id);
protected slots:
void proccessVolumesSearchData(const QByteArray & data);
void proccessSeriesDetailData(const QByteArray & data);
void processVolumeComicsInfo(const QByteArray & data);
void proccessComicDetailData(const QByteArray & data);
protected:
QSettings * settings;
QString baseURL;
};
#endif // COMIC_VINE_CLIENT_H

View File

@ -0,0 +1,742 @@
#include "comic_vine_dialog.h"
#include <QtWidgets>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QStackedWidget>
#include <QRadioButton>
#include <QMessageBox>
#include <QTableView>
#if QT_VERSION >= 0x050000
#include <QtConcurrent/QtConcurrentRun>
#else
#include <QtConcurrentRun>
#endif
#include <QSqlDatabase>
#include <QtScript>
#include "data_base_management.h"
#include "yacreader_busy_widget.h"
#include "comic_vine_client.h"
#include "scraper_lineedit.h"
#include "title_header.h"
#include "series_question.h"
#include "search_single_comic.h"
#include "search_volume.h"
#include "select_comic.h"
#include "select_volume.h"
#include "sort_volume_comics.h"
#include "db_helper.h"
#include "response_parser.h"
#include "QsLog.h"
ComicVineDialog::ComicVineDialog(QWidget *parent) :
QDialog(parent)
{
setWindowFlags(Qt::Window);
doLayout();
doStackedWidgets();
doConnections();
}
void ComicVineDialog::doLayout()
{
setStyleSheet(""
"QDialog {background-color: #404040; }"
"");
QString dialogButtonsStyleSheet = "QPushButton {border: 1px solid #242424; background: #2e2e2e; color:white; padding: 5px 26px 5px 26px; font-size:12px;font-family:Arial; font-weight:bold;}";
skipButton = new QPushButton(tr("skip"));
backButton = new QPushButton(tr("back"));
nextButton = new QPushButton(tr("next"));
searchButton = new QPushButton(tr("search"));
closeButton = new QPushButton(tr("close"));
skipButton->setStyleSheet(dialogButtonsStyleSheet);
backButton->setStyleSheet(dialogButtonsStyleSheet);
nextButton->setStyleSheet(dialogButtonsStyleSheet);
searchButton->setStyleSheet(dialogButtonsStyleSheet);
closeButton->setStyleSheet(dialogButtonsStyleSheet);
content = new QStackedWidget(this);
QVBoxLayout * mainLayout = new QVBoxLayout;
QHBoxLayout * buttonLayout = new QHBoxLayout;
buttonLayout->addStretch();
buttonLayout->addWidget(skipButton);
buttonLayout->addWidget(backButton);
buttonLayout->addWidget(nextButton);
buttonLayout->addWidget(searchButton);
buttonLayout->addWidget(closeButton);
buttonLayout->setContentsMargins(0,0,0,0);
mainLayout->addWidget(titleHeader = new TitleHeader, 0);
mainLayout->addWidget(content, 1);
mainLayout->addLayout(buttonLayout, 0);
mainLayout->setContentsMargins(26,16,26,11);
setLayout(mainLayout);
setWindowTitle("Comic Vine Scraper (beta)");
}
void ComicVineDialog::doStackedWidgets()
{
doLoading();
content->addWidget(seriesQuestionWidget = new SeriesQuestion);
content->addWidget(searchSingleComicWidget = new SearchSingleComic);
content->addWidget(searchVolumeWidget = new SearchVolume);
content->addWidget(selectVolumeWidget = new SelectVolume);
content->addWidget(selectComicWidget = new SelectComic);
content->addWidget(sortVolumeComicsWidget = new SortVolumeComics);
}
void ComicVineDialog::doConnections()
{
connect(closeButton,SIGNAL(clicked()),this,SLOT(close()));
connect(nextButton,SIGNAL(clicked()),this,SLOT(goNext()));
connect(backButton,SIGNAL(clicked()),this,SLOT(goBack()));
connect(searchButton,SIGNAL(clicked()),this,SLOT(search()));
connect(skipButton,SIGNAL(clicked()),this,SLOT(goToNextComic()));
connect(selectVolumeWidget,SIGNAL(loadPage(QString,int)),this,SLOT(searchVolume(QString,int)));
connect(selectComicWidget,SIGNAL(loadPage(QString,int)),this,SLOT(getVolumeComicsInfo(QString,int)));
connect(sortVolumeComicsWidget,SIGNAL(loadPage(QString,int)),this,SLOT(getVolumeComicsInfo(QString,int)));
}
void ComicVineDialog::goNext()
{
//
if(content->currentWidget() == seriesQuestionWidget)
{
if(seriesQuestionWidget->getYes())
{
QString volumeSearchString = comics[0].getParentFolderName();
mode = Volume;
if(volumeSearchString.isEmpty())
showSearchVolume();
else
{
status = AutoSearching;
showLoading(tr("Looking for volume..."));
searchVolume(volumeSearchString);
}
}
else
{
status = AutoSearching;
mode = SingleComicInList;
ComicDB comic = comics[currentIndex];
QString title = comic.getTitleOrFileName();
titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex+1).arg(comics.length()).arg(title));
showLoading(tr("Looking for volume..."));
searchVolume(title);
}
}
else if (content->currentWidget() == selectVolumeWidget) {
currentVolumeId = selectVolumeWidget->getSelectedVolumeId();
getVolumeComicsInfo(currentVolumeId);
} else if (content->currentWidget() == sortVolumeComicsWidget) {
showLoading();
//ComicDB-ComicVineID
QList<QPair<ComicDB,QString> > matchingInfo = sortVolumeComicsWidget->getMatchingInfo();
int count = selectVolumeWidget->getSelectedVolumeNumIssues();
QString publisher = selectVolumeWidget->getSelectedVolumePublisher();
QtConcurrent::run(this, &ComicVineDialog::getComicsInfo,matchingInfo,count,publisher);
} else if (content->currentWidget() == selectComicWidget)
{
showLoading();
QString comicId = selectComicWidget->getSelectedComicId();
int count = selectVolumeWidget->getSelectedVolumeNumIssues();
QString publisher = selectVolumeWidget->getSelectedVolumePublisher();
QtConcurrent::run(this, &ComicVineDialog::getComicInfo,comicId,count,publisher);
}
}
void ComicVineDialog::goBack()
{
switch (status) {
case SelectingSeries:
if(mode == Volume)
showSearchVolume();
else
showSearchSingleComic();
break;
case SortingComics:
showSelectVolume();
break;
case SelectingComic:
if(mode == SingleComic)
showSelectVolume();
break;
case AutoSearching:
if(mode == Volume)
showSearchVolume();
else
showSearchSingleComic();
default:
if(mode == Volume)
showSearchVolume();
else
showSearchSingleComic();
break;
}
}
void ComicVineDialog::setComics(const QList<ComicDB> & comics)
{
this->comics = comics;
}
QSize ComicVineDialog::sizeHint() const
{
int heightDesktopResolution = QApplication::desktop()->screenGeometry().height();
int widthDesktopResolution = QApplication::desktop()->screenGeometry().width();
int height,width;
height = qMax(529, static_cast<int>(heightDesktopResolution*0.5));
width = height * 1.65;
if (width > widthDesktopResolution)
return minimumSizeHint();
return QSize(width, height);
}
QSize ComicVineDialog::minimumSizeHint() const
{
return QSize(872, 529);
}
void ComicVineDialog::show()
{
QDialog::show();
currentIndex = 0;
seriesQuestionWidget->setYes(true);
searchSingleComicWidget->clean();
searchVolumeWidget->clean();
if(comics.length() == 1)
{
status = AutoSearching;
mode = SingleComic;
ComicDB singleComic = comics[0];
QString title = singleComic.getTitleOrFileName();
titleHeader->setSubTitle(title);
showLoading(tr("Looking for volume..."));
searchVolume(singleComic.getParentFolderName());
QLOG_TRACE() << singleComic.getParentFolderName();
}else if(comics.length()>1)
{
titleHeader->setSubTitle(tr("%1 comics selected").arg(comics.length()));
showSeriesQuestion();
}
}
void ComicVineDialog::doLoading()
{
QWidget * w = new QWidget;
QVBoxLayout * l = new QVBoxLayout;
YACReaderBusyWidget * bw = new YACReaderBusyWidget;
loadingMessage = new QLabel;
loadingMessage->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
l->addStretch();
l->addWidget(bw,0,Qt::AlignHCenter);
l->addStretch();
l->addWidget(loadingMessage);
l->setContentsMargins(0,0,0,0);
w->setLayout(l);
w->setContentsMargins(0,0,0,0);
content->addWidget(w);
}
void ComicVineDialog::debugClientResults(const QString & string)
{
ResponseParser p;
p.loadJSONResponse(string);
//QMessageBox::information(0,"Result", QString("Number of results : %1").arg(p.getNumResults()));
if(p.responseError())
{
QMessageBox::critical(0,tr("Error connecting to ComicVine"), p.errorDescription());
goBack();
}
else
{
switch(mode)
{
case SingleComic: case SingleComicInList:
if(p.getNumResults() == 0)
showSearchSingleComic();
else
if(status == SearchingVolume)
showSelectVolume(string);
else
showSelectComic(string);
break;
case Volume:
if(p.getNumResults() == 0)
showSearchVolume();
else
showSelectVolume(string);
break;
}
}
}
void ComicVineDialog::showSeriesQuestion()
{
status = AskingForInfo;
content->setCurrentWidget(seriesQuestionWidget);
backButton->setHidden(true);
skipButton->setHidden(true);
nextButton->setVisible(true);
searchButton->setHidden(true);
closeButton->setVisible(true);
if(mode == SingleComicInList)
skipButton->setVisible(true);
else
skipButton->setHidden(true);
}
void ComicVineDialog::showSearchSingleComic()
{
status = AskingForInfo;
content->setCurrentWidget(searchSingleComicWidget);
backButton->setHidden(true);
skipButton->setHidden(true);
nextButton->setHidden(true);
searchButton->setVisible(true);
closeButton->setVisible(true);
if(mode == SingleComicInList)
skipButton->setVisible(true);
else
skipButton->setHidden(true);
}
void ComicVineDialog::showSearchVolume()
{
status = AskingForInfo;
content->setCurrentWidget(searchVolumeWidget);
backButton->setHidden(true);
nextButton->setHidden(true);
searchButton->setVisible(true);
closeButton->setVisible(true);
toggleSkipButton();
}
void ComicVineDialog::showSelectVolume(const QString & json)
{
showSelectVolume();
selectVolumeWidget->load(json,currentVolumeSearchString);
}
void ComicVineDialog::showSelectVolume()
{
status = SelectingSeries;
content->setCurrentWidget(selectVolumeWidget);
backButton->setVisible(true);
nextButton->setVisible(true);
searchButton->setHidden(true);
closeButton->setVisible(true);
toggleSkipButton();
}
void ComicVineDialog::showSelectComic(const QString &json)
{
status = SelectingComic;
content->setCurrentWidget(selectComicWidget);
selectComicWidget->load(json,currentVolumeId);
backButton->setVisible(true);
nextButton->setVisible(true);
searchButton->setHidden(true);
closeButton->setVisible(true);
toggleSkipButton();
}
void ComicVineDialog::showSortVolumeComics(const QString &json)
{
status = SortingComics;
content->setCurrentWidget(sortVolumeComicsWidget);
sortVolumeComicsWidget->setData(comics, json, currentVolumeId);
backButton->setVisible(true);
nextButton->setVisible(true);
searchButton->setHidden(true);
closeButton->setVisible(true);
toggleSkipButton();
}
void ComicVineDialog::queryTimeOut()
{
QMessageBox::warning(this,"Comic Vine error", "Time out connecting to Comic Vine");
switch (status) {
case AutoSearching:
if(mode == Volume)
showSearchVolume();
else
showSearchSingleComic();
break;
case SearchingVolume:
if(mode == Volume)
showSearchVolume();
else
showSearchSingleComic();
break;
case SearchingSingleComic:
showSearchSingleComic();
break;
case GettingVolumeComics:
showSelectVolume();
break;
default:
break;
}
}
void ComicVineDialog::getComicsInfo(QList<QPair<ComicDB, QString> > & matchingInfo, int count,const QString & publisher)
{
QPair<ComicDB, QString> p;
QList<ComicDB> comics;
foreach (p, matchingInfo) {
ComicVineClient * comicVineClient = new ComicVineClient;
//connect(comicVineClient,SIGNAL(searchResult(QString)),this,SLOT(debugClientResults(QString)));
//connect(comicVineClient,SIGNAL(timeOut()),this,SLOT(queryTimeOut()));
//connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater()));
bool error;
bool timeout;
QByteArray result = comicVineClient->getComicDetail(p.second,error,timeout); //TODO check timeOut or Connection error
if(error || timeout)
continue; //TODO
ComicDB comic = parseComicInfo(p.first,result,count,publisher);//TODO check result error
comic.info.comicVineID = p.second;
comics.push_back(comic);
setLoadingMessage(tr("Retrieving tags for : %1").arg(p.first.getFileName()));
}
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
db.open();
db.transaction();
foreach(ComicDB comic, comics)
{
DBHelper::update(&(comic.info),db);
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(databasePath);
close();
emit accepted();
}
void ComicVineDialog::getComicInfo(const QString &comicId, int count, const QString &publisher)
{
ComicVineClient * comicVineClient = new ComicVineClient;
bool error;
bool timeout;
QByteArray result = comicVineClient->getComicDetail(comicId,error,timeout); //TODO check timeOut or Connection error
if(error || timeout)
{
//TODO
if(mode == SingleComic || currentIndex == (comics.count()-1))
{
close();
emit accepted();
} else
{
goToNextComic();
}
}
ComicDB comic = parseComicInfo(comics[currentIndex],result,count,publisher); //TODO check result error
comic.info.comicVineID = comicId;
setLoadingMessage(tr("Retrieving tags for : %1").arg(comics[currentIndex].getFileName()));
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
db.open();
db.transaction();
DBHelper::update(&(comic.info),db);
db.commit();
db.close();
QSqlDatabase::removeDatabase(databasePath);
if(mode == SingleComic || currentIndex == (comics.count()-1))
{
close();
emit accepted();
} else
{
goToNextComic();
}
}
ComicDB ComicVineDialog::parseComicInfo(ComicDB & comic, const QString & json, int count, const QString & publisher)
{
QScriptEngine engine;
QScriptValue sc;
sc = engine.evaluate("(" + json + ")");
if (!sc.property("error").isValid() && sc.property("error").toString() != "OK")
{
qDebug("Error detected");
}
else
{
int numResults = sc.property("number_of_total_results").toString().toInt(); //fix to weird behaviour using hasNext
if(numResults > 0)
{
QScriptValue result = sc.property("results");
QString title = result.property("name").toString();
QString number = result.property("issue_number").toString();
//QString count; //get from select volume
QString volume = result.property("volume").property("name").toString();
// QString storyArc; //story_arc
// QString arcNumber; //??
// QString arcCount; //count_of_issue_appearances -> NO
// QString genere; //no
QMap<QString,QString> authors = getAuthors(result.property("person_credits"));
QString writer = QStringList(authors.values("writer")).join("\n");
QString penciller = QStringList(authors.values("penciller")).join("\n");
QString inker = QStringList(authors.values("inker")).join("\n");
QString colorist = QStringList(authors.values("colorist")).join("\n");
QString letterer = QStringList(authors.values("letterer")).join("\n");
QString coverArtist = QStringList(authors.values("cover")).join("\n");
QString date = result.property("cover_date").toString();
//QString publisher; //get from select volume
// QString format; //no
// bool color; //no
// QString ageRating; //no
QString synopsis = result.property("description").toString().remove(QRegExp("<[^>]*>")); //description
QString characters = getCharacters(result.property("character_credits"));
comic.info.title = title;
comic.info.number = number;
comic.info.count = count;
comic.info.writer = writer;
comic.info.penciller = penciller;
comic.info.inker = inker;
comic.info.colorist = colorist;
comic.info.letterer = letterer;
comic.info.coverArtist = coverArtist;
QStringList tempList = date.split("-");
std::reverse(tempList.begin(),tempList.end());
comic.info.date = tempList.join("/");
comic.info.volume = volume;
comic.info.publisher = publisher;
comic.info.synopsis = synopsis;
comic.info.characters = characters;
}
}
return comic;
}
QString ComicVineDialog::getCharacters(const QScriptValue &json_characters)
{
QString characters;
QScriptValueIterator it(json_characters);
QScriptValue resultsValue;
while (it.hasNext()) {
it.next();
if(it.flags() & QScriptValue::SkipInEnumeration)
continue;
resultsValue = it.value();
characters += resultsValue.property("name").toString() + "\n";
}
return characters;
}
QMap<QString, QString> ComicVineDialog::getAuthors(const QScriptValue &json_authors)
{
QMap<QString, QString> authors;
QScriptValueIterator it(json_authors);
QScriptValue resultsValue;
while (it.hasNext()) {
it.next();
if(it.flags() & QScriptValue::SkipInEnumeration)
continue;
resultsValue = it.value();
QString authorName = resultsValue.property("name").toString();
QStringList roles = resultsValue.property("role").toString().split(",");
foreach(QString role, roles)
{
if(role.trimmed() == "writer")
authors.insertMulti("writer",authorName);
else if(role.trimmed() == "inker")
authors.insertMulti("inker",authorName);
else if(role.trimmed() == "penciler" || role.trimmed() == "penciller")
authors.insertMulti("penciller",authorName);
else if(role.trimmed() == "colorist")
authors.insertMulti("colorist",authorName);
else if(role.trimmed() == "letterer")
authors.insertMulti("letterer",authorName);
else if(role.trimmed() == "cover")
authors.insertMulti("cover",authorName);
}
}
return authors;
}
void ComicVineDialog::toggleSkipButton()
{
if (mode == SingleComicInList)
skipButton->setVisible(true);
else
skipButton->setHidden(true);
}
void ComicVineDialog::goToNextComic()
{
if(mode == SingleComic || currentIndex == (comics.count()-1))
{
close();
emit accepted();
return;
}
currentIndex++;
showSearchSingleComic();
ComicDB comic = comics[currentIndex];
QString title = comic.getTitleOrFileName();
titleHeader->setSubTitle(tr("comic %1 of %2 - %3").arg(currentIndex+1).arg(comics.length()).arg(title));
}
void ComicVineDialog::showLoading(const QString &message)
{
content->setCurrentIndex(0);
loadingMessage->setText(message);
backButton->setHidden(true);
skipButton->setHidden(true);
nextButton->setHidden(true);
searchButton->setHidden(true);
closeButton->setVisible(true);
}
void ComicVineDialog::setLoadingMessage(const QString &message)
{
loadingMessage->setText(message);
}
void ComicVineDialog::search()
{
switch (mode) {
case Volume:
launchSearchVolume();
break;
default:
launchSearchComic();
break;
}
}
void ComicVineDialog::searchVolume(const QString &v, int page)
{
showLoading(tr("Looking for volume..."));
currentVolumeSearchString = v;
ComicVineClient * comicVineClient = new ComicVineClient;
connect(comicVineClient,SIGNAL(searchResult(QString)),this,SLOT(debugClientResults(QString)));
connect(comicVineClient,SIGNAL(timeOut()),this,SLOT(queryTimeOut()));
connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater()));
comicVineClient->search(v,page);
status = SearchingVolume;
}
void ComicVineDialog::getVolumeComicsInfo(const QString &vID, int page)
{
showLoading(tr("Retrieving volume info..."));
status = GettingVolumeComics;
ComicVineClient * comicVineClient = new ComicVineClient;
if(mode == Volume)
connect(comicVineClient,SIGNAL(volumeComicsInfo(QString)),this,SLOT(showSortVolumeComics(QString)));
else
connect(comicVineClient,SIGNAL(volumeComicsInfo(QString)),this,SLOT(showSelectComic(QString)));
connect(comicVineClient,SIGNAL(timeOut()),this,SLOT(queryTimeOut()));
connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater()));
QLOG_TRACE() << vID;
comicVineClient->getAllVolumeComicsInfo(vID);
}
void ComicVineDialog::launchSearchVolume()
{
showLoading(tr("Looking for volume..."));
//TODO: check if volume info is empty.
searchVolume(searchVolumeWidget->getVolumeInfo());
}
void ComicVineDialog::launchSearchComic()
{
showLoading(tr("Looking for comic..."));
QString volumeInfo = searchSingleComicWidget->getVolumeInfo();
//QString comicInfo = searchSingleComicWidget->getComicInfo();
//int comicNumber = searchSingleComicWidget->getComicNumber();
//if(comicInfo.isEmpty() && comicNumber == -1)
searchVolume(volumeInfo);
}

View File

@ -0,0 +1,131 @@
#ifndef COMIC_VINE_DIALOG_H
#define COMIC_VINE_DIALOG_H
#include <QDialog>
#include "comic_db.h"
class QPushButton;
class QStackedWidget;
class QLabel;
class QRadioButton;
class ComicVineClient;
class QTableView;
class TitleHeader;
class SeriesQuestion;
class SearchSingleComic;
class SearchVolume;
class SelectComic;
class SelectVolume;
class SortVolumeComics;
class QScriptValue;
//TODO this should use a QStateMachine
//----------------------------------------
class ComicVineDialog : public QDialog
{
Q_OBJECT
public:
explicit ComicVineDialog(QWidget *parent = 0);
QString databasePath;
QString basePath;
void setComics(const QList<ComicDB> & comics);
QSize sizeHint() const;
QSize minimumSizeHint() const;
signals:
public slots:
void show();
protected slots:
void goNext();
void goBack();
void debugClientResults(const QString & string);
//show widget methods
void showSeriesQuestion();
void showSearchSingleComic();
void showSearchVolume();
void showLoading(const QString & message = "");
void search();
void searchVolume(const QString & v, int page = 1);
void getVolumeComicsInfo(const QString &vID, int page = 1);
void launchSearchVolume();
void launchSearchComic();
void showSelectVolume(const QString & json);
void showSelectVolume();
void showSelectComic(const QString & json);
void showSortVolumeComics(const QString & json);
void queryTimeOut();
void getComicsInfo(QList<QPair<ComicDB,QString> > & matchingInfo, int count, const QString & publisher);
void getComicInfo(const QString & comicId, int count, const QString & publisher);
ComicDB parseComicInfo(ComicDB &comic, const QString & json, int count, const QString &publisher);
void setLoadingMessage(const QString &message);
void goToNextComic();
private:
QString getCharacters(const QScriptValue & json_characters);
QMap<QString,QString> getAuthors(const QScriptValue & json_authors);
void toggleSkipButton();
enum ScraperMode
{
SingleComic, //the scraper has been opened for a single comic
Volume, //the scraper is trying to get comics info for a whole volume
SingleComicInList //the scraper has been opened for a list of unrelated comics
};
enum ScraperStatus
{
AutoSearching,
AskingForInfo,
SelectingComic,
SelectingSeries,
SearchingSingleComic,
SearchingVolume,
SortingComics,
GettingVolumeComics
};
ScraperMode mode;
ScraperStatus status;
int currentIndex;
TitleHeader * titleHeader;
QPushButton * skipButton;
QPushButton * backButton;
QPushButton * nextButton;
QPushButton * searchButton;
QPushButton * closeButton;
//stacked widgets
QStackedWidget * content;
QWidget * infoNotFound;
QWidget * singleComicBrowser;
QLabel * loadingMessage;
void doLayout();
void doStackedWidgets();
void doLoading();
void doConnections();
QList<ComicDB> comics;
SeriesQuestion * seriesQuestionWidget;
SearchSingleComic * searchSingleComicWidget;
SearchVolume * searchVolumeWidget;
SelectVolume * selectVolumeWidget;
SelectComic * selectComicWidget;
SortVolumeComics * sortVolumeComicsWidget;
QString currentVolumeSearchString;
QString currentVolumeId;
};
#endif // COMIC_VINE_DIALOG_H

View File

@ -0,0 +1,6 @@
#include "comics_model.h"
ComicsModel::ComicsModel(QObject *parent) :
JSONModel(parent)
{
}

View File

@ -0,0 +1,18 @@
#ifndef COMICS_MODEL_H
#define COMICS_MODEL_H
#include "json_model.h"
class ComicsModel : public JSONModel
{
Q_OBJECT
public:
explicit ComicsModel(QObject *parent = 0);
signals:
public slots:
};
#endif // COMICS_MODEL_H

View File

@ -0,0 +1,6 @@
#include "json_model.h"
JSONModel::JSONModel(QObject *parent) :
QAbstractItemModel(parent)
{
}

View File

@ -0,0 +1,19 @@
#ifndef JSON_MODEL_H
#define JSON_MODEL_H
#include <QAbstractItemModel>
class JSONModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit JSONModel(QObject *parent = 0);
virtual void load(const QString & json) = 0 ;
signals:
public slots:
};
#endif // JSON_MODEL_H

View File

@ -0,0 +1,184 @@
#include "local_comic_list_model.h"
LocalComicListModel::LocalComicListModel(QObject *parent) :
QAbstractItemModel(parent),numExtraRows(0)
{
}
void LocalComicListModel::load(QList<ComicDB> &comics)
{
_data = comics;
}
QModelIndex LocalComicListModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index)
return QModelIndex(); //no parent
}
int LocalComicListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return _data.count();
}
int LocalComicListModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if(_data.isEmpty())
return 0;
else
return 1;//_data.at(0)->count();
}
QVariant LocalComicListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DecorationRole)
{
return QVariant();
}
if (role == Qt::TextAlignmentRole)
{
//TODO
}
if(role != Qt::DisplayRole)
return QVariant();
int row = index.row();
//if(row < _data.count())
return _data[row].getFileName();
//else
//return QVariant();
}
Qt::ItemFlags LocalComicListModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant LocalComicListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(section);
if ( role == Qt::TextAlignmentRole)
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
return QVariant(QString(tr("file name")));
}
return QVariant();
}
QModelIndex LocalComicListModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
return createIndex(row, column);
}
QList<ComicDB> LocalComicListModel::getData()
{
return _data;
}
void LocalComicListModel::removeComics(const QList<QModelIndex> &selectedIndexes)
{
QModelIndex mi = selectedIndexes.first();
QModelIndex lastMi = selectedIndexes.last();
int sourceRow = mi.row();
int sourceLastRow = lastMi.row();
beginRemoveRows(QModelIndex(),selectedIndexes.first().row(),selectedIndexes.last().row());
for(int i = sourceLastRow;i>=sourceRow;i--)
{
_removed.push_front(_data.at(i));
_data.removeAt(i);
}
endRemoveRows();
beginInsertRows(QModelIndex(),_data.count()-_removed.count(),_data.count()-1);
for(int i = 0; i<_removed.count(); i++)
_data.append(ComicDB());
endInsertRows();
}
void LocalComicListModel::restoreAll()
{
int numItemsToRemove = 0;
for(int i = 0;numItemsToRemove<_removed.count();i++)
{
if(_data.at(i).getFileName().isEmpty())
{
beginRemoveRows(QModelIndex(),i,i);
_data.removeAt(i);
endRemoveRows();
beginInsertRows(QModelIndex(),i,i);
_data.insert(i,_removed.at(numItemsToRemove));
endInsertRows();
numItemsToRemove++;
}
}
_removed.clear();
}
void LocalComicListModel::moveSelectionUp(const QList<QModelIndex> &selectedIndexes)
{
QModelIndex mi = selectedIndexes.first();
QModelIndex lastMi = selectedIndexes.last();
int sourceRow = mi.row();
int sourceLastRow = lastMi.row();
int destRow = sourceRow - 1;
if(destRow < 0)
return;
beginMoveRows(mi.parent(),sourceRow,sourceLastRow,mi.parent(),destRow);
for(int i = sourceRow; i <= sourceLastRow; i++)
_data.swap(i, i-1);
endMoveRows();
}
void LocalComicListModel::moveSelectionDown(const QList<QModelIndex> &selectedIndexes)
{
QModelIndex mi = selectedIndexes.first();
QModelIndex lastMi = selectedIndexes.last();
int sourceRow = mi.row();
int sourceLastRow = lastMi.row();
int destRow = sourceLastRow + 1;
if(destRow >= _data.count())
return;
beginMoveRows(mi.parent(),sourceRow,sourceLastRow,mi.parent(),destRow+1);
for(int i = sourceLastRow; i >= sourceRow; i--)
_data.swap(i, i+1);
endMoveRows();
}
void LocalComicListModel::addExtraRows(int numRows)
{
numExtraRows = numRows;
for(int i = 0; i<numExtraRows; i++)
_data.append(ComicDB());
}

View File

@ -0,0 +1,42 @@
#ifndef LOCAL_COMIC_LIST_MODEL_H
#define LOCAL_COMIC_LIST_MODEL_H
#include <QAbstractItemModel>
#include "comic_db.h"
class LocalComicListModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit LocalComicListModel(QObject *parent = 0);
void load(QList<ComicDB> & comics);
//QAbstractItemModel methods
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QList<ComicDB> getData();
void removeComics(const QList<QModelIndex> & selectedIndexes);
void restoreAll();
signals:
public slots:
void moveSelectionUp(const QList<QModelIndex> & selectedIndexes);
void moveSelectionDown(const QList<QModelIndex> & selectedIndexes);
void addExtraRows(int numRows);
private:
int numExtraRows;
QList<ComicDB> _data;
QList<ComicDB> _removed;
};
#endif // LOCAL_COMIC_LIST_MODEL_H

View File

@ -0,0 +1,83 @@
#include "response_parser.h"
#include <QtScript>
#include <QDebug>
ResponseParser::ResponseParser(QObject *parent) :
QObject(parent),error(false),numResults(-1),currentPage(-1),totalPages(-1),errorTxt("None")
{
}
bool ResponseParser::responseError()
{
return error;
}
QString ResponseParser::errorDescription()
{
return errorTxt;
}
qint32 ResponseParser::getNumResults()
{
return numResults;
}
qint32 ResponseParser::getCurrentPage()
{
return currentPage;
}
qint32 ResponseParser::getTotalPages()
{
return totalPages;
}
bool ResponseParser::isError(qint32 error)
{
switch(error)
{
case 100:
return true;
default:
return false;
}
}
void ResponseParser::loadJSONResponse(const QString &response)
{
QScriptEngine engine;
QScriptValue sc;
sc = engine.evaluate("(" + response + ")");
errorTxt = "None";
if (!sc.property("status_code").isValid() || isError(sc.property("status_code").toInt32()))
{
error = true;
if(sc.property("error").isValid())
errorTxt = sc.property("error").toString();
else
errorTxt = "Unknown error";
}
else
{
error = false;
if(sc.property("number_of_total_results").isValid())
numResults = sc.property("number_of_total_results").toString().toInt();// sc.property("number_of_total_results").toInt32();
else
qDebug() << sc.property("oops").toString();
int limit = sc.property("limit").toInt32();
int offset = sc.property("offset").toInt32();
int total = sc.property("number_of_total_results").toInt32();
if(limit > 0)
{
totalPages = (total / limit) + (total%limit>0?1:0);
currentPage = (offset / limit) + 1;
}
else
totalPages = currentPage = 1;
}
}

View File

@ -0,0 +1,30 @@
#ifndef RESPONSE_PARSER_H
#define RESPONSE_PARSER_H
#include <QObject>
class ResponseParser : public QObject
{
Q_OBJECT
public:
explicit ResponseParser(QObject *parent = 0);
bool responseError();
QString errorDescription();
qint32 getNumResults();
qint32 getCurrentPage();
qint32 getTotalPages();
bool isError(qint32 error);
signals:
public slots:
void loadJSONResponse(const QString & response);
protected:
bool error;
QString errorTxt;
qint32 numResults;
qint32 currentPage;
qint32 totalPages;
};
#endif // RESPONSE_PARSER_H

View File

@ -0,0 +1,179 @@
#include "volume_comics_model.h"
#include "qnaturalsorting.h"
#include <QtScript>
bool lessThan(const QList<QString> & left, const QList<QString> & right)
{
if ((left.count() > 0) && (right.count() > 0))
return naturalSortLessThanCI(left.at(0),right.at(0));
else
return true;
}
VolumeComicsModel::VolumeComicsModel(QObject * parent) :
JSONModel(parent),numExtraRows(0)
{
}
void VolumeComicsModel::load(const QString & json)
{
QScriptEngine engine;
QScriptValue sc;
sc = engine.evaluate("(" + json + ")");
if (!sc.property("error").isValid() && sc.property("error").toString() != "OK")
{
qDebug("Error detected");
}
else
{
QScriptValueIterator it(sc.property("results"));
//bool test;
QScriptValue resultsValue;
while (it.hasNext()) {
it.next();
if(it.flags() & QScriptValue::SkipInEnumeration)
continue;
resultsValue = it.value();
QString issueNumber = resultsValue.property("issue_number").toString();
QString name = resultsValue.property("name").toString();
QString coverURL = resultsValue.property("image").property("medium_url").toString();
QString id = resultsValue.property("id").toString();
QStringList l;
l << issueNumber << name << coverURL << id;
_data.push_back(l);
}
qSort(_data.begin(),_data.end(),lessThan);
}
}
/*void VolumeComicsModel::load(const QStringList &jsonList)
{
foreach (QString json, jsonList) {
load(json);
}
}*/
QModelIndex VolumeComicsModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index)
return QModelIndex(); //no parent
}
int VolumeComicsModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return _data.count() + numExtraRows;
}
int VolumeComicsModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if(_data.isEmpty())
return 0;
else
return 2;
}
QVariant VolumeComicsModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
int row = index.row();
int column = index.column();
if (role == Qt::DecorationRole)
{
return QVariant();
}
if (role == Qt::TextAlignmentRole)
{
switch(column)//TODO obtener esto de la query
{
case ISSUE:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case TITLE:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
}
}
if(role != Qt::DisplayRole)
return QVariant();
if(row<_data.count())
return _data[row][column];
else
return QVariant();
}
Qt::ItemFlags VolumeComicsModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant VolumeComicsModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch(section)//TODO obtener esto de la query
{
case ISSUE:
return QVariant(QString("issue"));
case TITLE:
return QVariant(QString(tr("title")));
}
}
if (orientation == Qt::Horizontal && role == Qt::TextAlignmentRole)
{
switch(section)//TODO obtener esto de la query
{
case ISSUE:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case TITLE:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
}
}
return QVariant();
}
QModelIndex VolumeComicsModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
return createIndex(row, column);
}
QString VolumeComicsModel::getComicId(const QModelIndex &index) const
{
int row = index.row();
if(row >= _data.count())
return "";
return _data[row][ID];
}
QString VolumeComicsModel::getComicId(int row) const
{
if(row >= _data.count())
return "";
return _data[row][ID];
}
QString VolumeComicsModel::getCoverURL(const QModelIndex &index) const
{
return _data[index.row()][COVER_URL];
}
void VolumeComicsModel::addExtraRows(int numRows)
{
numExtraRows = numRows;
}

View File

@ -0,0 +1,42 @@
#ifndef VOLUME_COMICS_MODEL_H
#define VOLUME_COMICS_MODEL_H
#include "json_model.h"
class VolumeComicsModel : public JSONModel
{
Q_OBJECT
public:
explicit VolumeComicsModel(QObject *parent = 0);
void load(const QString & json);
//void load(const QStringList & jsonList);
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
signals:
public slots:
QString getComicId(const QModelIndex &index) const;
QString getComicId(int row) const;
QString getCoverURL(const QModelIndex &index) const;
void addExtraRows(int numRows);
private:
int numExtraRows;
QList <QList <QString> > _data;
enum Column {
ISSUE = 0,
TITLE,
COVER_URL,
ID
};
};
#endif // VOLUME_COMICS_MODEL_H

View File

@ -0,0 +1,180 @@
#include "volumes_model.h"
#include <QtScript>
VolumesModel::VolumesModel(QObject *parent) :
JSONModel(parent)
{
}
VolumesModel::~VolumesModel()
{
//std::for_each(_data.begin(), _data.end(), [](QList<QString> * ptr) { delete ptr; });
}
void VolumesModel::load(const QString &json)
{
QScriptEngine engine;
QScriptValue sc;
sc = engine.evaluate("(" + json + ")");
if (!sc.property("error").isValid() && sc.property("error").toString() != "OK")
{
qDebug("Error detected");
}
else
{
int numResults = sc.property("number_of_total_results").toString().toInt(); //fix to weird behaviour using hasNext
QScriptValueIterator it(sc.property("results"));
bool test;
QScriptValue resultsValue;
while (it.hasNext()) {
it.next();
resultsValue = it.value();
QString numIssues = resultsValue.property("count_of_issues").toString();
QString year = resultsValue.property("start_year").toString();
QString name = resultsValue.property("name").toString();
QString publisher = resultsValue.property("publisher").property("name").toString();
QString url = resultsValue.property("image").property("medium_url").toString();
QString deck = resultsValue.property("deck").toString();
QString id = resultsValue.property("id").toString();
QStringList l;
l << name << year << numIssues << publisher << url << deck << id;
test = name.isEmpty() && year.isEmpty() && numIssues.isEmpty() && url.isEmpty();
if(numResults>0 && !test)
_data.push_back(l);
numResults--;
}
}
}
QModelIndex VolumesModel::parent(const QModelIndex &index) const
{
Q_UNUSED(index)
return QModelIndex(); //no parent
}
int VolumesModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return _data.count();
}
int VolumesModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if(_data.isEmpty())
return 0;
else
return 4;//_data.at(0)->count();
}
QVariant VolumesModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DecorationRole)
{
return QVariant();
}
int row = index.row();
int column = index.column();
if (role == Qt::TextAlignmentRole)
{
switch(column)
{
case YEAR:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case ISSUES:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
default:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
}
}
if(role != Qt::DisplayRole)
return QVariant();
if (column == YEAR || column == ISSUES)
{
return _data[row][column].toInt();
}
else
{
return _data[row][column];
}
}
Qt::ItemFlags VolumesModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant VolumesModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch(section)//TODO obtener esto de la query
{
case SERIES:
return QVariant(QString("series"));
case YEAR:
return QVariant(QString(tr("year")));
case ISSUES:
return QVariant(QString(tr("issues")));
case PUBLISHER:
return QVariant(QString(tr("publisher")));
}
}
if (orientation == Qt::Horizontal && role == Qt::TextAlignmentRole)
{
switch(section)//TODO obtener esto de la query
{
case YEAR:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
case ISSUES:
return QVariant(Qt::AlignRight | Qt::AlignVCenter);
default:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
}
}
return QVariant();
}
QModelIndex VolumesModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
return createIndex(row, column);
}
QString VolumesModel::getVolumeId(const QModelIndex &index) const
{
return _data[index.row()][ID];
}
int VolumesModel::getNumIssues(const QModelIndex &index) const
{
return _data[index.row()][ISSUES].toInt();
}
QString VolumesModel::getPublisher(const QModelIndex &index) const
{
return _data[index.row()][PUBLISHER];
}
QString VolumesModel::getCoverURL(const QModelIndex &index) const
{
return _data[index.row()][COVER_URL];
}

View File

@ -0,0 +1,53 @@
#ifndef VOLUMES_MODEL_H
#define VOLUMES_MODEL_H
#include "json_model.h"
class VolumesModel : public JSONModel
{
Q_OBJECT
public:
explicit VolumesModel(QObject *parent = 0);
virtual ~VolumesModel();
//receive a valid json with a list of volumes
void load(const QString & json);
//QAbstractItemModel methods
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QString getVolumeId(const QModelIndex & index) const;
int getNumIssues(const QModelIndex & index) const;
QString getPublisher(const QModelIndex & index) const;
QString getCoverURL(const QModelIndex & index) const;
signals:
public slots:
private:
QList <QList <QString> > _data;
public:
enum Column {
SERIES = 0,
YEAR,
ISSUES,
PUBLISHER,
COVER_URL,
DECK,
ID
};
enum Role {
SORT_ROLE = Qt::UserRole
};
};
#endif // VOLUMES_MODEL_H

View File

@ -0,0 +1,21 @@
#include "scraper_lineedit.h"
#include <QLabel>
ScraperLineEdit::ScraperLineEdit(const QString & title, QWidget * widget)
:QLineEdit(widget)
{
titleLabel = new QLabel(title,this);
titleLabel->setStyleSheet("QLabel {color:white;}");
setStyleSheet(QString("QLineEdit {"
"border:none; background-color: #2E2E2E; color : white; padding-left: %1; padding-bottom: 1px; margin-bottom: 0px;"
"}").arg(titleLabel->sizeHint().width()+6));
setFixedHeight(22);
}
void ScraperLineEdit::resizeEvent(QResizeEvent *)
{
QSize szl = titleLabel->sizeHint();
titleLabel->move(6,(rect().bottom() + 1 - szl.height())/2);
}

View File

@ -0,0 +1,19 @@
#ifndef SCRAPPER_LINEEDIT_H
#define SCRAPPER_LINEEDIT_H
#include <QLineEdit>
class QLabel;
class ScraperLineEdit : public QLineEdit
{
Q_OBJECT
public:
ScraperLineEdit(const QString & title, QWidget * widget = 0);
protected:
void resizeEvent(QResizeEvent *);
private:
QLabel * titleLabel;
};
#endif // SCRAPPER_LINEEDIT_H

View File

@ -0,0 +1,75 @@
#include "scraper_results_paginator.h"
#include "response_parser.h"
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QtScript>
ScraperResultsPaginator::ScraperResultsPaginator(QWidget *parent) :
QWidget(parent),customLabel("items")
{
QHBoxLayout * pagesButtonsLayout = new QHBoxLayout;
QString labelStylesheet = "QLabel {color:white; font-size:12px;font-family:Arial;}";
nextPage = new QToolButton;
nextPage->setStyleSheet("QToolButton {border:none;}");
QPixmap np(":/images/comic_vine/nextPage.png");
nextPage->setIconSize(np.size());
nextPage->setIcon(np);
previousPage = new QToolButton;
previousPage->setStyleSheet("QToolButton {border:none;}");
QPixmap pp(":/images/comic_vine/previousPage.png");
previousPage->setIconSize(pp.size());
previousPage->setIcon(pp);
connect(nextPage,SIGNAL(clicked()),this,SIGNAL(loadNextPage()));
connect(previousPage,SIGNAL(clicked()),this,SIGNAL(loadPreviousPage()));
numElements = new QLabel(tr("Number of volumes found : %1"));
numElements->setStyleSheet(labelStylesheet);
numPages = new QLabel(tr("page %1 of %2"));
numPages->setStyleSheet(labelStylesheet);
pagesButtonsLayout->addSpacing(15);
pagesButtonsLayout->addWidget(numElements);
pagesButtonsLayout->addStretch();
pagesButtonsLayout->addWidget(numPages);
pagesButtonsLayout->addWidget(previousPage);
pagesButtonsLayout->addWidget(nextPage);
setContentsMargins(0,0,0,0);
pagesButtonsLayout->setContentsMargins(0,0,0,0);
setLayout(pagesButtonsLayout);
}
void ScraperResultsPaginator::update(const QString &json)
{
ResponseParser rp;
rp.loadJSONResponse(json);
currentPage = rp.getCurrentPage();
numElements->setText(tr("Number of %1 found : %2").arg(customLabel).arg(rp.getNumResults()));
numPages->setText(tr("page %1 of %2").arg(currentPage).arg(rp.getTotalPages()));
previousPage->setDisabled(currentPage == 1);
nextPage->setDisabled(currentPage == rp.getTotalPages());
numPages->setHidden(rp.getTotalPages()==1);
previousPage->setHidden(rp.getTotalPages()==1);
nextPage->setHidden(rp.getTotalPages()==1);
}
int ScraperResultsPaginator::getCurrentPage()
{
return currentPage;
}
void ScraperResultsPaginator::setCustomLabel(const QString &label)
{
customLabel = label;
}

View File

@ -0,0 +1,34 @@
#ifndef SCRAPER_RESULTS_PAGINATOR_H
#define SCRAPER_RESULTS_PAGINATOR_H
#include <QWidget>
class QToolButton;
class QLabel;
class ScraperResultsPaginator : public QWidget
{
Q_OBJECT
public:
explicit ScraperResultsPaginator(QWidget *parent = 0);
void update(const QString & json);
int getCurrentPage();
void setCustomLabel(const QString & label);
signals:
void loadNextPage();
void loadPreviousPage();
public slots:
private:
QToolButton * nextPage;
QToolButton * previousPage;
QLabel * numElements;
QLabel * numPages;
int currentPage;
QString customLabel;
};
#endif // SCRAPER_RESULTS_PAGINATOR_H

View File

@ -0,0 +1,53 @@
#include "scraper_scroll_label.h"
#include <QLabel>
#include <QDesktopServices>
#include <QUrl>
ScraperScrollLabel::ScraperScrollLabel(QWidget *parent) :
QScrollArea(parent)
{
textLabel = new QLabel(this);
textLabel->setStyleSheet("QLabel {background-color: #2B2B2B; color:white; font-size:12px; font-family:Arial; }");
textLabel->setWordWrap(true);
textLabel->setMinimumSize(168,12);
setWidget(textLabel);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setStyleSheet(
"QScrollArea {background-color:#2B2B2B; border:none;}"
"QScrollBar:vertical { border: none; background: #2B2B2B; width: 3px; margin: 0; }"
"QScrollBar:horizontal { border: none; background: #2B2B2B; height: 3px; margin: 0; }"
"QScrollBar::handle:vertical { background: #DDDDDD; width: 7px; min-height: 20px; }"
"QScrollBar::handle:horizontal { background: #DDDDDD; width: 7px; min-height: 20px; }"
"QScrollBar::add-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::sub-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::add-line:horizontal { border: none; background: #404040; width: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 0 3px 0;}"
"QScrollBar::sub-line:horizontal { border: none; background: #404040; width: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 0 3px 0;}"
"QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}"
"QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}"
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {background: none; }"
);
connect(textLabel,SIGNAL(linkActivated(QString)),this,SLOT(openLink(QString)));
}
void ScraperScrollLabel::setAltText(const QString &text)
{
textLabel->setAlignment(Qt::AlignTop|Qt::AlignHCenter);
textLabel->setText(text);
textLabel->adjustSize();
}
void ScraperScrollLabel::setText(const QString &text)
{
textLabel->setAlignment(Qt::AlignTop|Qt::AlignLeft);
textLabel->setText(text);
textLabel->adjustSize();
}
void ScraperScrollLabel::openLink(const QString & link)
{
QDesktopServices::openUrl(QUrl("http://www.comicvine.com"+link));
}

View File

@ -0,0 +1,25 @@
#ifndef SCRAPER_SCROLL_LABEL_H
#define SCRAPER_SCROLL_LABEL_H
#include <QScrollArea>
class QLabel;
class ScraperScrollLabel : public QScrollArea
{
Q_OBJECT
public:
explicit ScraperScrollLabel(QWidget *parent = 0);
signals:
public slots:
void setText(const QString & text);
void setAltText(const QString &text);
void openLink(const QString &link);
private:
QLabel * textLabel;
};
#endif // SCRAPER_SCROLL_LABEL_H

View File

@ -0,0 +1,25 @@
#include "scraper_selector.h"
ScraperSelector::ScraperSelector(QWidget *parent) :
QWidget(parent)
{
paginator = new ScraperResultsPaginator;
connect(paginator,SIGNAL(loadNextPage()),this,SLOT(loadNextPage()));
connect(paginator,SIGNAL(loadPreviousPage()),this,SLOT(loadPreviousPage()));
}
void ScraperSelector::load(const QString &json, const QString &searchString)
{
currentSearchString = searchString;
paginator->update(json);
}
void ScraperSelector::loadNextPage()
{
emit loadPage(currentSearchString,paginator->getCurrentPage()+1);
}
void ScraperSelector::loadPreviousPage()
{
emit loadPage(currentSearchString,paginator->getCurrentPage()-1);
}

View File

@ -0,0 +1,28 @@
#ifndef SCRAPER_SELECTOR_H
#define SCRAPER_SELECTOR_H
#include <QWidget>
#include "scraper_results_paginator.h"
class ScraperSelector : public QWidget
{
Q_OBJECT
public:
explicit ScraperSelector(QWidget *parent = 0);
virtual void load(const QString & json, const QString & searchString);
public slots:
signals:
void loadPage(QString,int);
private slots:
void loadNextPage();
void loadPreviousPage();
protected:
QString currentSearchString;
ScraperResultsPaginator * paginator;
};
#endif // SCRAPER_SELECTOR_H

View File

@ -0,0 +1,60 @@
#include "scraper_tableview.h"
#include <QHeaderView>
ScraperTableView::ScraperTableView(QWidget *parent) :
QTableView(parent)
{
QString tableStylesheet = "QTableView {color:white; border:0px;alternate-background-color: #2E2E2E;background-color: #2B2B2B; outline: 0px;}"
"QTableView::item {outline: 0px; border: 0px; color:#FFFFFF;}"
"QTableView::item:selected {outline: 0px; background-color: #555555; }"
"QHeaderView::section:horizontal {background-color:#292929; border-bottom:1px solid #1F1F1F; border-right:1px solid qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #292929, stop: 1 #1F1F1F); border-left:none; border-top:none; padding:4px; color:#ebebeb;}"
"QHeaderView::section:vertical {border-bottom: 1px solid #DFDFDF;border-top: 1px solid #FEFEFE;}"
"QHeaderView::down-arrow {image: url(':/images/comic_vine/downArrow.png');}"
"QHeaderView::up-arrow {image: url(':/images/comic_vine/upArrow.png');}"
"QScrollBar:vertical { border: none; background: #2B2B2B; width: 3px; margin: 0; }"
"QScrollBar:horizontal { border: none; background: #2B2B2B; height: 3px; margin: 0; }"
"QScrollBar::handle:vertical { background: #DDDDDD; width: 7px; min-height: 20px; }"
"QScrollBar::handle:horizontal { background: #DDDDDD; width: 7px; min-height: 20px; }"
"QScrollBar::add-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::sub-line:vertical { border: none; background: #404040; height: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::add-line:horizontal { border: none; background: #404040; width: 10px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 0 3px 0;}"
"QScrollBar::sub-line:horizontal { border: none; background: #404040; width: 10px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 0 3px 0;}"
"QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}"
"QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}"
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical, QScrollBar::add-page:horizontal, QScrollBar::sub-page:horizontal {background: none; }";
setStyleSheet(tableStylesheet);
setShowGrid(false);
#if QT_VERSION >= 0x050000
verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
#else
verticalHeader()->setResizeMode(QHeaderView::Fixed);
#endif
horizontalHeader()->setStretchLastSection(true);
#if QT_VERSION >= 0x050000
horizontalHeader()->setSectionsClickable(false);
#else
horizontalHeader()->setClickable(false);
#endif
//comicView->verticalHeader()->setResizeMode(QHeaderView::ResizeToContents);
verticalHeader()->setDefaultSectionSize(24);
#if QT_VERSION >= 0x050000
verticalHeader()->setSectionsClickable(false); //TODO comportamiento anómalo
#else
verticalHeader()->setClickable(false); //TODO comportamiento anómalo
#endif
setCornerButtonEnabled(false);
setSelectionBehavior(QAbstractItemView::SelectRows);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setAlternatingRowColors(true);
verticalHeader()->hide();
setSelectionMode(QAbstractItemView::SingleSelection);
}

View File

@ -0,0 +1,18 @@
#ifndef SCRAPPER_TABLEVIEW_H
#define SCRAPPER_TABLEVIEW_H
#include <QTableView>
class ScraperTableView : public QTableView
{
Q_OBJECT
public:
explicit ScraperTableView(QWidget *parent = 0);
signals:
public slots:
};
#endif // SCRAPPER_TABLEVIEW_H

View File

@ -0,0 +1,62 @@
#include "search_single_comic.h"
#include "scraper_lineedit.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
SearchSingleComic::SearchSingleComic(QWidget * parent)
:QWidget(parent)
{
//QLabel * label = new QLabel(tr("Please provide some additional information. At least one field is needed."));
QLabel * label = new QLabel(tr("Please provide some additional information."));
label->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
//titleEdit = new ScraperLineEdit(tr("Title:"));
//numberEdit = new ScraperLineEdit(tr("Number:"));
volumeEdit = new ScraperLineEdit(tr("Series:"));
//numberEdit->setMaximumWidth(126);
QVBoxLayout * l = new QVBoxLayout;
//QHBoxLayout * hl = new QHBoxLayout;
//hl->addWidget(titleEdit);
//hl->addWidget(numberEdit);
l->addSpacing(35);
l->addWidget(label);
//l->addLayout(hl);
l->addWidget(volumeEdit);
l->addStretch();
l->setContentsMargins(0,0,0,0);
setLayout(l);
setContentsMargins(0,0,0,0);
}
QString SearchSingleComic::getVolumeInfo()
{
return volumeEdit->text();
}
QString SearchSingleComic::getComicInfo()
{
//return titleEdit->text();
return "";
}
int SearchSingleComic::getComicNumber()
{
//QString numberText = numberEdit->text();
//if(numberText.isEmpty())
// return -1;
//return numberText.toInt();
return 0;
}
void SearchSingleComic::clean()
{
volumeEdit->clear();
}

View File

@ -0,0 +1,22 @@
#ifndef SEARCH_SINGLE_COMIC_H
#define SEARCH_SINGLE_COMIC_H
#include <QWidget>
class ScraperLineEdit;
class SearchSingleComic : public QWidget
{
Q_OBJECT
public:
SearchSingleComic(QWidget * parent = 0);
QString getVolumeInfo();
QString getComicInfo();
int getComicNumber();
void clean();
private:
ScraperLineEdit * titleEdit;
ScraperLineEdit * numberEdit;
ScraperLineEdit * volumeEdit;
};
#endif // SEARCH_SINGLE_COMIC_H

View File

@ -0,0 +1,36 @@
#include "search_volume.h"
#include "scraper_lineedit.h"
#include <QLabel>
#include <QVBoxLayout>
SearchVolume::SearchVolume(QWidget * parent)
:QWidget(parent)
{
QLabel * label = new QLabel(tr("Please provide some additional information."));
label->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
volumeEdit = new ScraperLineEdit(tr("Series:"));
QVBoxLayout * l = new QVBoxLayout;
l->addSpacing(35);
l->addWidget(label);
l->addWidget(volumeEdit);
l->addStretch();
l->setContentsMargins(0,0,0,0);
setLayout(l);
setContentsMargins(0,0,0,0);
}
void SearchVolume::clean()
{
volumeEdit->clear();
}
QString SearchVolume::getVolumeInfo()
{
return volumeEdit->text();
}

View File

@ -0,0 +1,21 @@
#ifndef SEARCH_VOLUME_H
#define SEARCH_VOLUME_H
#include <QWidget>
class ScraperLineEdit;
class SearchVolume : public QWidget
{
Q_OBJECT
public:
SearchVolume(QWidget * parent = 0);
void clean();
public slots:
QString getVolumeInfo();
private:
ScraperLineEdit * volumeEdit;
};
#endif // SEARCH_VOLUME_H

View File

@ -0,0 +1,144 @@
#include "select_comic.h"
#include "comic_vine_client.h"
#include "scraper_scroll_label.h"
#include "scraper_tableview.h"
#include "volume_comics_model.h"
#include <QLabel>
#include <QLayout>
#include <QtScript>
SelectComic::SelectComic(QWidget *parent)
:ScraperSelector(parent),model(0)
{
QString labelStylesheet = "QLabel {color:white; font-size:12px;font-family:Arial;}";
QLabel * label = new QLabel(tr("Please, select the right comic info."));
label->setStyleSheet(labelStylesheet);
QVBoxLayout * l = new QVBoxLayout;
QWidget * leftWidget = new QWidget;
QVBoxLayout * left = new QVBoxLayout;
QGridLayout * content = new QGridLayout;
//widgets
cover = new QLabel();
cover->setScaledContents(true);
cover->setAlignment(Qt::AlignTop|Qt::AlignHCenter);
cover->setMinimumSize(168,168*5.0/3);
cover->setStyleSheet("QLabel {background-color: #2B2B2B; color:white; font-size:12px; font-family:Arial; }");
detailLabel = new ScraperScrollLabel(this);
tableComics = new ScraperTableView(this);
//connections
connect(tableComics,SIGNAL(clicked(QModelIndex)),this,SLOT(loadComicInfo(QModelIndex)));
paginator->setCustomLabel(tr("comics"));
left->addWidget(cover);
left->addWidget(detailLabel,1);
leftWidget->setMaximumWidth(180);
leftWidget->setLayout(left);
left->setContentsMargins(0,0,0,0);
leftWidget->setContentsMargins(0,0,0,0);
content->addWidget(leftWidget, 0, 0);
content->addWidget(tableComics, 0, 1);
content->addWidget(paginator, 1, 1);
content->setColumnStretch(1, 1);
content->setRowStretch(0, 1);;
l->addSpacing(15);
l->addWidget(label);
l->addSpacing(5);
l->addLayout(content);
l->setContentsMargins(0,0,0,0);
setLayout(l);
setContentsMargins(0,0,0,0);
}
void SelectComic::load(const QString &json, const QString & searchString)
{
VolumeComicsModel * tempM = new VolumeComicsModel();
tempM->load(json);
tableComics->setModel(tempM);
if(model != 0)
delete model;
model = tempM;
if(model->rowCount()>0)
{
tableComics->selectRow(0);
loadComicInfo(model->index(0,0));
}
tableComics->resizeColumnToContents(0);
ScraperSelector::load(json,searchString);
}
SelectComic::~SelectComic() {}
void SelectComic::loadComicInfo(const QModelIndex &mi)
{
QString coverURL = model->getCoverURL(mi);
QString id = model->getComicId(mi);
QString loadingStyle = "<font color='#AAAAAA'>%1</font>";
cover->setText(loadingStyle.arg(tr("loading cover")));
detailLabel->setAltText(loadingStyle.arg(tr("loading description")));
ComicVineClient * comicVineClient = new ComicVineClient;
connect(comicVineClient,SIGNAL(comicCover(const QByteArray &)),this,SLOT(setCover(const QByteArray &)));
connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater()));
comicVineClient->getComicCover(coverURL);
ComicVineClient * comicVineClient2 = new ComicVineClient;
connect(comicVineClient2,SIGNAL(comicDetail(QString)),this,SLOT(setDescription(QString)));
connect(comicVineClient2,SIGNAL(finished()),comicVineClient2,SLOT(deleteLater()));
comicVineClient2->getComicDetailAsync(id);
}
void SelectComic::setCover(const QByteArray & data)
{
QPixmap p;
p.loadFromData(data);
int w = p.width();
int h = p.height();
cover->setPixmap(p);
float aspectRatio = static_cast<float>(w)/h;
cover->setFixedSize(180,static_cast<int>(180/aspectRatio));
cover->update();
}
void SelectComic::setDescription(const QString &jsonDetail)
{
QScriptEngine engine;
QScriptValue sc;
sc = engine.evaluate("(" + jsonDetail + ")");
if (!sc.property("error").isValid() && sc.property("error").toString() != "OK")
{
qDebug("Error detected");
}
else
{
QScriptValue descriptionValues = sc.property("results").property("description");
bool valid = !descriptionValues.isNull() && descriptionValues.isValid();
detailLabel->setText(valid?descriptionValues.toString().replace("<a","<a style = 'color:#827A68; text-decoration:none;'"):tr("description unavailable"));
}
}
QString SelectComic::getSelectedComicId()
{
return model->getComicId(tableComics->currentIndex());
}

View File

@ -0,0 +1,34 @@
#ifndef SELECT_COMIC_H
#define SELECT_COMIC_H
#include "scraper_selector.h"
class QLabel;
class VolumeComicsModel;
class QModelIndex;
class ScraperScrollLabel;
class ScraperTableView;
class SelectComic : public ScraperSelector
{
Q_OBJECT
public:
SelectComic(QWidget * parent = 0);
void load(const QString & json, const QString & searchString);
virtual ~SelectComic();
public slots:
void loadComicInfo(const QModelIndex & mi);
void setCover(const QByteArray &);
void setDescription(const QString & jsonDetail);
QString getSelectedComicId();
private:
QLabel * cover;
ScraperScrollLabel * detailLabel;
ScraperTableView * tableComics;
VolumeComicsModel * model;
};
#endif // SELECT_COMIC_H

View File

@ -0,0 +1,185 @@
#include "select_volume.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QScrollBar>
#include <QModelIndex>
#include <QScrollArea>
#include <QDesktopServices>
#include <QHeaderView>
#include <QToolButton>
#include <QSortFilterProxyModel>
#include "scraper_tableview.h"
#include <QtScript>
#include "volumes_model.h"
#include "comic_vine_client.h"
#include "scraper_scroll_label.h"
#include "response_parser.h"
#include "scraper_results_paginator.h"
SelectVolume::SelectVolume(QWidget *parent)
:ScraperSelector(parent),model(0)
{
proxyModel = new QSortFilterProxyModel;
QString labelStylesheet = "QLabel {color:white; font-size:12px;font-family:Arial;}";
QLabel * label = new QLabel(tr("Please, select the right series for your comic."));
label->setStyleSheet(labelStylesheet);
QVBoxLayout * l = new QVBoxLayout;
QWidget * leftWidget = new QWidget;
QVBoxLayout * left = new QVBoxLayout;
QGridLayout * content = new QGridLayout;
//widgets
cover = new QLabel();
cover->setScaledContents(true);
cover->setAlignment(Qt::AlignTop|Qt::AlignHCenter);
cover->setMinimumSize(168,168*5.0/3);
cover->setStyleSheet("QLabel {background-color: #2B2B2B; color:white; font-size:12px; font-family:Arial; }");
detailLabel = new ScraperScrollLabel();
tableVolumes = new ScraperTableView();
tableVolumes->setSortingEnabled(true);
#if QT_VERSION >= 0x050000
tableVolumes->horizontalHeader()->setSectionsClickable(true);
#else
tableVolumes->horizontalHeader()->setClickable(true);
#endif
//tableVolumes->horizontalHeader()->setSortIndicatorShown(false);
connect(tableVolumes->horizontalHeader(),SIGNAL(sectionClicked(int)), tableVolumes, SLOT(sortByColumn(int)));
//connections
connect(tableVolumes,SIGNAL(clicked(QModelIndex)),this,SLOT(loadVolumeInfo(QModelIndex)));
paginator->setCustomLabel(tr("volumes"));
left->addWidget(cover);
left->addWidget(detailLabel,1);
leftWidget->setMaximumWidth(180);
leftWidget->setLayout(left);
left->setContentsMargins(0,0,0,0);
leftWidget->setContentsMargins(0,0,0,0);
content->addWidget(leftWidget, 0, 0);
content->addWidget(tableVolumes, 0, 1);
content->addWidget(paginator, 1, 1);
content->setColumnStretch(1, 1);
content->setRowStretch(0, 1);
l->addSpacing(15);
l->addWidget(label);
l->addSpacing(5);
l->addLayout(content);
l->setContentsMargins(0,0,0,0);
setLayout(l);
setContentsMargins(0,0,0,0);
}
void SelectVolume::load(const QString & json, const QString & searchString)
{
VolumesModel * tempM = new VolumesModel();
tempM->load(json);
//tableVolumes->setModel(tempM);
proxyModel->setSourceModel( tempM );
tableVolumes->setModel(proxyModel);
tableVolumes->sortByColumn(0,Qt::AscendingOrder);
tableVolumes->resizeColumnsToContents();
if(model != 0)
delete model;
model = tempM;
if(model->rowCount()>0)
{
tableVolumes->selectRow(0);
loadVolumeInfo(proxyModel->index(0,0));
}
tableVolumes->setColumnWidth(0,350);
ScraperSelector::load(json,searchString);
}
SelectVolume::~SelectVolume() {}
void SelectVolume::loadVolumeInfo(const QModelIndex & omi)
{
QModelIndex mi = proxyModel->mapToSource(omi);
QString coverURL = model->getCoverURL(mi);
QString id = model->getVolumeId(mi);
QString loadingStyle = "<font color='#AAAAAA'>%1</font>";
cover->setText(loadingStyle.arg(tr("loading cover")));
detailLabel->setAltText(loadingStyle.arg(tr("loading description")));
ComicVineClient * comicVineClient = new ComicVineClient;
connect(comicVineClient,SIGNAL(seriesCover(const QByteArray &)),this,SLOT(setCover(const QByteArray &)));
connect(comicVineClient,SIGNAL(finished()),comicVineClient,SLOT(deleteLater()));
comicVineClient->getSeriesCover(coverURL);
ComicVineClient * comicVineClient2 = new ComicVineClient;
connect(comicVineClient2,SIGNAL(seriesDetail(QString)),this,SLOT(setDescription(QString)));
connect(comicVineClient2,SIGNAL(finished()),comicVineClient2,SLOT(deleteLater()));
comicVineClient2->getSeriesDetail(id);
}
void SelectVolume::setCover(const QByteArray & data)
{
QPixmap p;
p.loadFromData(data);
int w = p.width();
int h = p.height();
cover->setPixmap(p);
float aspectRatio = static_cast<float>(w)/h;
cover->setFixedSize(180,static_cast<int>(180/aspectRatio));
cover->update();
}
void SelectVolume::setDescription(const QString & jsonDetail)
{
QScriptEngine engine;
QScriptValue sc;
sc = engine.evaluate("(" + jsonDetail + ")");
if (!sc.property("error").isValid() && sc.property("error").toString() != "OK")
{
qDebug("Error detected");
}
else
{
QScriptValue descriptionValues = sc.property("results").property("description");
bool valid = !descriptionValues.isNull() && descriptionValues.isValid();
detailLabel->setText(valid?descriptionValues.toString().replace("<a","<a style = 'color:#827A68; text-decoration:none;'"):tr("description unavailable"));
}
}
QString SelectVolume::getSelectedVolumeId()
{
return model->getVolumeId(proxyModel->mapToSource(tableVolumes->currentIndex()));
}
int SelectVolume::getSelectedVolumeNumIssues()
{
return model->getNumIssues(proxyModel->mapToSource(tableVolumes->currentIndex()));
}
QString SelectVolume::getSelectedVolumePublisher()
{
return model->getPublisher(proxyModel->mapToSource(tableVolumes->currentIndex()));
}

View File

@ -0,0 +1,39 @@
#ifndef SELECT_VOLUME_H
#define SELECT_VOLUME_H
#include "scraper_selector.h"
class QLabel;
class VolumesModel;
class QModelIndex;
class QToolButton;
class QSortFilterProxyModel;
class ScraperScrollLabel;
class ScraperTableView;
class SelectVolume : public ScraperSelector
{
Q_OBJECT
public:
SelectVolume(QWidget * parent = 0);
void load(const QString & json, const QString & searchString);
virtual ~SelectVolume();
public slots:
void loadVolumeInfo(const QModelIndex & mi);
void setCover(const QByteArray &);
void setDescription(const QString & jsonDetail);
QString getSelectedVolumeId();
int getSelectedVolumeNumIssues();
QString getSelectedVolumePublisher();
private:
QLabel * cover;
ScraperScrollLabel * detailLabel;
ScraperTableView * tableVolumes;
VolumesModel * model;
QSortFilterProxyModel * proxyModel;
};
#endif // SELECT_VOLUME_H

View File

@ -0,0 +1,46 @@
#include "series_question.h"
#include <QRadioButton>
#include <QVBoxLayout>
#include <QLabel>
SeriesQuestion::SeriesQuestion(QWidget * parent)
:QWidget(parent)
{
QVBoxLayout * l = new QVBoxLayout;
QLabel * questionLabel = new QLabel(tr("You are trying to get information for various comics at once, are they part of the same series?"));
questionLabel->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
yes = new QRadioButton(tr("yes"));
no = new QRadioButton(tr("no"));
QString rbStyle = "QRadioButton {margin-left:27px; margin-top:5px; color:white;font-size:12px;font-family:Arial;}"
"QRadioButton::indicator {width:11px;height:11px;}"
"QRadioButton::indicator::unchecked {image : url(:/images/comic_vine/radioUnchecked.png);}"
"QRadioButton::indicator::checked {image : url(:/images/comic_vine/radioChecked.png);}";
yes->setStyleSheet(rbStyle);
no->setStyleSheet(rbStyle);
yes->setChecked(true);
l->addSpacing(35);
l->addWidget(questionLabel);
l->addWidget(yes);
l->addWidget(no);
l->addStretch();
l->setContentsMargins(0,0,0,0);
setLayout(l);
setContentsMargins(0,0,0,0);
}
bool SeriesQuestion::getYes()
{
return yes->isChecked();
}
void SeriesQuestion::setYes(bool y)
{
yes->setChecked(y);
}

View File

@ -0,0 +1,23 @@
#ifndef SERIES_QUESTION_H
#define SERIES_QUESTION_H
#include <QWidget>
class QRadioButton;
class SeriesQuestion : public QWidget
{
Q_OBJECT
public:
SeriesQuestion(QWidget * parent = 0);
bool getYes();
void setYes(bool yes = true);
private:
QRadioButton * yes;
QRadioButton * no;
};
#endif // SERIES_QUESTION_H

View File

@ -0,0 +1,222 @@
#include "sort_volume_comics.h"
#include <QLabel>
#include <QBoxLayout>
#include <QPushButton>
#include <QScrollBar>
#include <QAction>
#include "scraper_tableview.h"
#include "local_comic_list_model.h"
#include "volume_comics_model.h"
SortVolumeComics::SortVolumeComics(QWidget *parent) :
ScraperSelector(parent)
{
QString labelStylesheet = "QLabel {color:white; font-size:12px;font-family:Arial;}";
QLabel * label = new QLabel(tr("Please, sort the list of comics on the left until it matches the comics' information."));
label->setStyleSheet(labelStylesheet);
QLabel * sortLabel = new QLabel(tr("sort comics to match comic information"));
sortLabel->setStyleSheet(labelStylesheet);
moveUpButtonCL = new ScrapperToolButton(ScrapperToolButton::LEFT);
moveUpButtonCL->setIcon(QIcon(":/images/comic_vine/rowUp.png"));
moveUpButtonCL->setAutoRepeat(true);
moveDownButtonCL = new ScrapperToolButton(ScrapperToolButton::RIGHT);
moveDownButtonCL->setIcon(QIcon(":/images/comic_vine/rowDown.png"));
moveDownButtonCL->setAutoRepeat(true);
//moveUpButtonIL = new ScrapperToolButton(ScrapperToolButton::LEFT);
//moveUpButtonIL->setIcon(QIcon(":/images/comic_vine/rowUp.png"));
//moveDownButtonIL = new ScrapperToolButton(ScrapperToolButton::RIGHT);
//moveDownButtonIL->setIcon(QIcon(":/images/comic_vine/rowDown.png"));
connect(moveUpButtonCL,SIGNAL(clicked()),this,SLOT(moveUpCL()));
connect(moveDownButtonCL,SIGNAL(clicked()),this,SLOT(moveDownCL()));
//connect(moveUpButtonIL,SIGNAL(clicked()),this,SLOT(moveUpIL()));
//connect(moveUpButtonIL,SIGNAL(clicked()),this,SLOT(moveDownIL()));
QVBoxLayout * l = new QVBoxLayout;
QGridLayout * content = new QGridLayout;
QHBoxLayout * sortButtonsLayout = new QHBoxLayout;
tableFiles = new ScraperTableView();
tableVolumeComics = new ScraperTableView();
tableFiles->setSelectionBehavior(QAbstractItemView::SelectRows);
tableFiles->setSelectionMode(QAbstractItemView::ContiguousSelection);
//content->addWidget(tableVolumes,0,Qt::AlignRight|Qt::AlignTop);
connect(tableVolumeComics->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(synchronizeScroll(int)));
connect(tableFiles->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(synchronizeScroll(int)));
//connect(tableVolumeComics, SIGNAL(pressed(QModelIndex)), tableFiles, SLOT(setCurrentIndex(QModelIndex)));
//connect(tableFiles, SIGNAL(pressed(QModelIndex)), tableVolumeComics, SLOT(setCurrentIndex(QModelIndex)));
paginator->setCustomLabel(tr("issues"));
paginator->setMinimumWidth(422);
sortButtonsLayout->addWidget(moveUpButtonCL);
sortButtonsLayout->addWidget(ScrapperToolButton::getSeparator());
sortButtonsLayout->addWidget(moveDownButtonCL);
sortButtonsLayout->addSpacing(10);
sortButtonsLayout->addWidget(sortLabel);
sortButtonsLayout->addStretch();
sortButtonsLayout->setSpacing(0);
content->addWidget(tableFiles, 0, 0);
content->addWidget(tableVolumeComics, 0, 1);
content->addLayout(sortButtonsLayout, 1, 0);
content->addWidget(paginator, 1, 1);
content->setRowStretch(0, 1);
l->addSpacing(15);
l->addWidget(label, 0);
l->addSpacing(5);
l->addLayout(content, 1);
l->addLayout(sortButtonsLayout, 0);
l->setContentsMargins(0,0,0,0);
setLayout(l);
setContentsMargins(0,0,0,0);
//rows actions
QAction * removeItemFromList = new QAction(tr("remove selected comics"),this);
QAction * restoreAllItems = new QAction(tr("restore all removed comics"),this);
QAction * restoreItems = new QAction(tr("restore removed comics"),this);
tableFiles->setContextMenuPolicy(Qt::ActionsContextMenu);
tableFiles->addAction(removeItemFromList);
tableFiles->addAction(restoreAllItems);
//tableFiles->addAction(restoreItems);
connect(removeItemFromList,SIGNAL(triggered()),this,SLOT(removeSelectedComics()));
connect(restoreAllItems,SIGNAL(triggered()),this,SLOT(restoreAllComics()));
connect(restoreItems,SIGNAL(triggered()),this,SLOT(showRemovedComicsSelector()));
}
void SortVolumeComics::setData(QList<ComicDB> & comics, const QString &json, const QString &vID)
{
//set up models
localComicsModel = new LocalComicListModel;
localComicsModel->load(comics);
volumeComicsModel = new VolumeComicsModel;
volumeComicsModel->load(json);
int numLocalComics = localComicsModel->rowCount();
int numVolumeComics = volumeComicsModel->rowCount();
if(numLocalComics > numVolumeComics)
volumeComicsModel->addExtraRows(numLocalComics - numVolumeComics);
if(numLocalComics < numVolumeComics)
localComicsModel->addExtraRows(numVolumeComics - numLocalComics);
tableFiles->setModel(localComicsModel);
tableVolumeComics->setModel(volumeComicsModel);
tableVolumeComics->resizeColumnToContents(0);
ScraperSelector::load(json,vID);
}
void SortVolumeComics::synchronizeScroll(int pos)
{
void * senderObject = sender();
if(senderObject == 0) //invalid call
return;
QScrollBar * tableVolumeComicsScrollBar = tableVolumeComics->verticalScrollBar();
QScrollBar * tableFilesScrollBar = tableFiles->verticalScrollBar();
if(senderObject == tableVolumeComicsScrollBar)
{
disconnect(tableFilesScrollBar,SIGNAL(valueChanged(int)),this,0);
tableFilesScrollBar->setValue(pos);
connect(tableFilesScrollBar, SIGNAL(valueChanged(int)), this, SLOT(synchronizeScroll(int)));
}
else
{
disconnect(tableVolumeComicsScrollBar,SIGNAL(valueChanged(int)),this,0);
tableVolumeComicsScrollBar->setValue(pos);
connect(tableVolumeComicsScrollBar, SIGNAL(valueChanged(int)), this, SLOT(synchronizeScroll(int)));
}
}
void SortVolumeComics::moveUpCL()
{
QList<QModelIndex> selection = tableFiles->selectionModel()->selectedIndexes();
if(selection.count() == 0)
return;
localComicsModel->moveSelectionUp(selection);
selection = tableFiles->selectionModel()->selectedIndexes();
tableFiles->scrollTo(selection.first());
}
void SortVolumeComics::moveDownCL()
{
QList<QModelIndex> selection = tableFiles->selectionModel()->selectedIndexes();
if(selection.count() > 0)
{
localComicsModel->moveSelectionDown(selection);
selection = tableFiles->selectionModel()->selectedIndexes();
tableFiles->scrollTo(selection.last());
}
}
void SortVolumeComics::moveUpIL()
{
}
void SortVolumeComics::moveDownIL()
{
}
void SortVolumeComics::removeSelectedComics()
{
QList<QModelIndex> selection = tableFiles->selectionModel()->selectedIndexes();
localComicsModel->removeComics(selection);
}
void SortVolumeComics::restoreAllComics()
{
localComicsModel->restoreAll();
}
void SortVolumeComics::showRemovedComicsSelector()
{
}
QList<QPair<ComicDB, QString> > SortVolumeComics::getMatchingInfo()
{
QList<ComicDB> comicList = localComicsModel->getData();
QList<QPair<ComicDB, QString> > l;
int index = 0;
QString id;
foreach(ComicDB c, comicList)
{
id = volumeComicsModel->getComicId(index);
if(!c.getFileName().isEmpty() && !id.isEmpty()) //there is a valid comic, and valid comic ID
{
l.push_back(QPair<ComicDB, QString>(c,id));
}
index++;
}
return l;
}

View File

@ -0,0 +1,99 @@
#ifndef SORT_VOLUME_COMICS_H
#define SORT_VOLUME_COMICS_H
#include "scraper_selector.h"
#include <QModelIndex>
#include <QPushButton>
#include <QPainter>
#include "comic_db.h"
class ScraperTableView;
class LocalComicListModel;
class VolumeComicsModel;
class ScrapperToolButton : public QPushButton
{
Q_OBJECT
public:
enum Appearance {
DEFAULT,
LEFT,
RIGHT
};
ScrapperToolButton(ScrapperToolButton::Appearance appearance = DEFAULT, QWidget * parent=0):QPushButton(parent),appearance(appearance) {
setStyleSheet("QPushButton {border: none; background: #2e2e2e; color:white; border-radius:2px;}"
"QPushButton::pressed {border: none; background: #282828; color:white; border-radius:2px;}");
setFixedSize(18,17);
}
static QWidget * getSeparator(){QWidget * w = new QWidget; w->setFixedWidth(1); w->setStyleSheet("QWidget {background:#282828;}"); return w;}
void setAppearance(ScrapperToolButton::Appearance appearance){this->appearance = appearance;}
virtual ~ScrapperToolButton() {}
protected:
void paintEvent(QPaintEvent * e)
{
QPainter p(this);
switch (appearance) {
case LEFT:
p.fillRect(16,0,2,18,QColor("#2E2E2E"));
break;
case RIGHT:
p.fillRect(0,0,2,18,QColor("#2E2E2E"));
break;
default:
break;
}
QPushButton::paintEvent(e);
}
private:
Appearance appearance;
};
class SortVolumeComics : public ScraperSelector
{
Q_OBJECT
public:
explicit SortVolumeComics(QWidget *parent = 0);
signals:
public slots:
void setData(QList<ComicDB> & comics, const QString &json, const QString & vID);
QList<QPair<ComicDB,QString> > getMatchingInfo();
protected slots:
void synchronizeScroll(int pos);
void moveUpCL();
void moveDownCL();
void moveUpIL();
void moveDownIL();
void removeSelectedComics();
void restoreAllComics();
void showRemovedComicsSelector();
private:
ScraperTableView * tableFiles;
ScraperTableView * tableVolumeComics;
LocalComicListModel * localComicsModel;
VolumeComicsModel * volumeComicsModel;
ScrapperToolButton * moveUpButtonCL;
ScrapperToolButton * moveDownButtonCL;
ScrapperToolButton * moveUpButtonIL;
ScrapperToolButton * moveDownButtonIL;
};
#endif // SORT_VOLUME_COMICS_H

View File

@ -0,0 +1,53 @@
#include "title_header.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
TitleHeader::TitleHeader(QWidget * parent )
:QWidget(parent)
{
mainTitleLabel = new QLabel();
subTitleLabel = new QLabel();
mainTitleLabel->setStyleSheet("QLabel {color:white; font-size:18px;font-family:Arial;}");
subTitleLabel->setStyleSheet("QLabel {color:white; font-size:12px;font-family:Arial;}");
QHBoxLayout * titleLayout = new QHBoxLayout;
QVBoxLayout * titleLabelsLayout = new QVBoxLayout;
titleLabelsLayout->addWidget(mainTitleLabel);
titleLabelsLayout->addWidget(subTitleLabel);
titleLabelsLayout->setSpacing(0);
titleLayout->addLayout(titleLabelsLayout);
titleLayout->setContentsMargins(0,0,0,0);
setLayout(titleLayout);
setContentsMargins(0,0,0,0);
setTitle(tr("SEARCH"));
}
void TitleHeader::setTitle(const QString & title)
{
mainTitleLabel->setText(title);
}
void TitleHeader::setSubTitle(const QString & title)
{
subTitleLabel->setText(title);
}
void TitleHeader::showButtons(bool show)
{
if(show)
{
}
else
{
}
}

View File

@ -0,0 +1,22 @@
#ifndef TITLE_HEADER_H
#define TITLE_HEADER_H
#include <QWidget>
class QLabel;
class TitleHeader : public QWidget
{
Q_OBJECT
public:
TitleHeader(QWidget * parent = 0);
public slots:
void setTitle(const QString & title);
void setSubTitle(const QString & title);
void showButtons(bool show);
private:
QLabel * mainTitleLabel;
QLabel * subTitleLabel;
};
#endif // TITLE_HEADER_H

View File

@ -0,0 +1,63 @@
#include "comics_remover.h"
#include <QFile>
#include <QDir>
#include "QsLog.h"
ComicsRemover::ComicsRemover(QModelIndexList & il, QList<QString> & ps, QObject *parent)
:QObject(parent),indexList(il), paths(ps)
{
}
void ComicsRemover::process()
{
QString currentComicPath;
QListIterator<QModelIndex> i(indexList);
QListIterator<QString> i2(paths);
i.toBack();
i2.toBack();
while (i.hasPrevious() && i2.hasPrevious())
{
QModelIndex mi = i.previous();
currentComicPath = i2.previous();
if(QFile::remove(currentComicPath))
emit remove(mi.row());
else
emit removeError();
}
emit finished();
}
FoldersRemover::FoldersRemover(QModelIndexList &il, QList<QString> &ps, QObject *parent)
:QObject(parent),indexList(il), paths(ps)
{
}
void FoldersRemover::process()
{
QString currentFolderPath;
QListIterator<QModelIndex> i(indexList);
QListIterator<QString> i2(paths);
i.toBack();
i2.toBack();
QLOG_DEBUG() << "Deleting folders" << paths.at(0);
while (i.hasPrevious() && i2.hasPrevious())
{
QModelIndex mi = i.previous();
currentFolderPath = i2.previous();
QDir d(currentFolderPath);
if(d.removeRecursively() || !d.exists()) //the folder is in the DB but no in the drive...
emit remove(mi);
else
emit removeError();
}
emit finished();
}

View File

@ -0,0 +1,47 @@
#ifndef COMICS_REMOVER_H
#define COMICS_REMOVER_H
#include <QThread>
#include <QModelIndex>
#include <comic_db.h>
class ComicsRemover : public QObject
{
Q_OBJECT
public:
explicit ComicsRemover(QModelIndexList & indexList, QList<QString> & paths, QObject *parent = 0);
signals:
void remove(int);
void removeError();
void finished();
public slots:
void process();
private:
QModelIndexList indexList;
QList<QString> paths;
};
class FoldersRemover : public QObject
{
Q_OBJECT
public:
explicit FoldersRemover(QModelIndexList & indexList, QList<QString> & paths, QObject *parent = 0);
signals:
void remove(QModelIndex);
void removeError();
void finished();
public slots:
void process();
private:
QModelIndexList indexList;
QList<QString> paths;
};
#endif // COMICS_REMOVER_H

View File

@ -0,0 +1,88 @@
#include "comics_view.h"
#include "comic.h"
#include "comic_files_manager.h"
#include "comic_db.h"
#include "QsLog.h"
#include <QtQuick>
ComicsView::ComicsView(QWidget *parent) :
QWidget(parent),model(NULL),comicDB(nullptr)
{
setAcceptDrops(true);
}
void ComicsView::setModel(ComicModel *m)
{
model = m;
}
void ComicsView::updateInfoForIndex(int index)
{
QQmlContext *ctxt = view->rootContext();
if(comicDB != nullptr) delete comicDB;
comicDB = new ComicDB(model->getComic(this->model->index(index, 0)));
ComicInfo *comicInfo = &(comicDB->info);
comicInfo->isFavorite = model->isFavorite(model->index(index,0));
ctxt->setContextProperty("comic", comicDB);
ctxt->setContextProperty("comicInfo", comicInfo);
ctxt->setContextProperty("comic_info_index", index);
}
void ComicsView::dragEnterEvent(QDragEnterEvent *event)
{
if(model->canDropMimeData(event->mimeData(),event->proposedAction(),0,0,QModelIndex()))
event->acceptProposedAction();
else
{
QLOG_TRACE() << "dragEnterEvent";
QList<QUrl> urlList;
if (event->mimeData()->hasUrls() && event->dropAction() == Qt::CopyAction)
{
urlList = event->mimeData()->urls();
QString currentPath;
foreach (QUrl url, urlList)
{
//comics or folders are accepted, folders' content is validate in dropEvent (avoid any lag before droping)
currentPath = url.toLocalFile();
if(Comic::fileIsComic(currentPath) || QFileInfo(currentPath).isDir())
{
event->acceptProposedAction();
return;
}
}
}
}
}
void ComicsView::dropEvent(QDropEvent *event)
{
QLOG_DEBUG() << "drop" << event->dropAction();
bool validAction = event->dropAction() == Qt::CopyAction;// || event->dropAction() & Qt::MoveAction; TODO move
if(event->mimeData()->hasUrls() && validAction)
{
QList<QPair<QString, QString> > droppedFiles = ComicFilesManager::getDroppedFiles(event->mimeData()->urls());
if(event->dropAction() == Qt::CopyAction)
{
QLOG_DEBUG() << "copy :" << droppedFiles;
emit copyComicsToCurrentFolder(droppedFiles);
}
else if(event->dropAction() & Qt::MoveAction)
{
QLOG_DEBUG() << "move :" << droppedFiles;
emit moveComicsToCurrentFolder(droppedFiles);
}
event->acceptProposedAction();
}
}

View File

@ -0,0 +1,65 @@
#ifndef COMICS_VIEW_H
#define COMICS_VIEW_H
#include <QtWidgets>
#include "comic_model.h"
class YACReaderTableView;
class QSplitter;
class ComicFlowWidget;
class QToolBar;
class ComicModel;
class QQuickView;
class ComicsView : public QWidget
{
Q_OBJECT
public:
explicit ComicsView(QWidget *parent = 0);
virtual void setToolBar(QToolBar * toolBar) = 0;
virtual void setModel(ComicModel *model);
virtual void setCurrentIndex(const QModelIndex &index) = 0;
virtual QModelIndex currentIndex() = 0;
virtual QItemSelectionModel * selectionModel() = 0;
virtual void scrollTo(const QModelIndex & mi, QAbstractItemView::ScrollHint hint ) = 0;
virtual void toFullScreen() = 0;
virtual void toNormal() = 0;
virtual void updateConfig(QSettings * settings) = 0;
virtual void enableFilterMode(bool enabled) = 0;
virtual void selectIndex(int index) = 0;
public slots:
virtual void updateInfoForIndex(int index);
virtual void setShowMarks(bool show) = 0;
virtual void selectAll() = 0;
signals:
void selected(unsigned int);
void comicRated(int,QModelIndex);
//Context menus
void customContextMenuViewRequested(QPoint);
void customContextMenuItemRequested(QPoint);
//Drops
void copyComicsToCurrentFolder(QList<QPair<QString, QString> >);
void moveComicsToCurrentFolder(QList<QPair<QString, QString> >);
protected:
ComicModel * model;
//Drop to import
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
QQuickView *view;
QWidget *container;
ComicDB *comicDB;
private:
};
#endif // COMICS_VIEW_H

View File

@ -0,0 +1,38 @@
#include "comics_view_transition.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QMovie>
#include <QSettings>
#include <QTimer>
#include <QSizePolicy>
#include <QPainter>
#include "yacreader_global_gui.h"
ComicsViewTransition::ComicsViewTransition(QWidget *parent) :
QWidget(parent)
{
#ifdef Q_OS_MAC
setStyleSheet("QWidget {background:#FFFFFF}");
#else
setStyleSheet("QWidget {background:#2A2A2A}");
#endif
}
QSize ComicsViewTransition::sizeHint()
{
return QSize(450,350);
}
void ComicsViewTransition::paintEvent(QPaintEvent *)
{
QPainter painter (this);
#ifdef Q_OS_MAC
painter.fillRect(0,0,width(),height(),QColor("#FFFFFF"));
#else
painter.fillRect(0,0,width(),height(),QColor("#2A2A2A"));
#endif
}

View File

@ -0,0 +1,17 @@
#ifndef COMICS_VIEW_TRANSITION_H
#define COMICS_VIEW_TRANSITION_H
#include <QWidget>
class ComicsViewTransition : public QWidget
{
Q_OBJECT
public:
explicit ComicsViewTransition(QWidget *parent = 0);
QSize sizeHint();
protected:
void paintEvent(QPaintEvent *);
};
#endif // COMICS_VIEW_TRANSITION_H

View File

@ -0,0 +1,206 @@
#include "create_library_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QSizePolicy>
#include <QMessageBox>
CreateLibraryDialog::CreateLibraryDialog(QWidget * parent)
:QDialog(parent)
{
setupUI();
}
void CreateLibraryDialog::setupUI()
{
textLabel = new QLabel(tr("Comics folder : "));
path = new QLineEdit;
textLabel->setBuddy(path);
connect(path,SIGNAL(textChanged(QString)),this,SLOT(pathSetted(QString)));
nameLabel = new QLabel(tr("Library Name : "));
nameEdit = new QLineEdit;
nameLabel->setBuddy(nameEdit);
connect(nameEdit,SIGNAL(textChanged(QString)),this,SLOT(nameSetted(QString)));
accept = new QPushButton(tr("Create"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(create()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SIGNAL(cancelCreate()));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
find = new QPushButton(QIcon(":/images/find_folder.png"),"");
connect(find,SIGNAL(clicked()),this,SLOT(findPath()));
QGridLayout * content = new QGridLayout;
//QHBoxLayout *nameLayout = new QHBoxLayout;
content->addWidget(nameLabel,0,0);
content->addWidget(nameEdit,0,1);
//QHBoxLayout *libraryLayout = new QHBoxLayout;
content->addWidget(textLabel,1,0);
content->addWidget(path,1,1);
content->addWidget(find,1,2);
content->setColumnMinimumWidth(2,0); //TODO
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addWidget(message = new QLabel(tr("Create a library could take several minutes. You can stop the process and update the library later for completing the task.")));
message->setWordWrap(true);
//message->hide();
bottomLayout->addStretch();
bottomLayout->addWidget(accept);
bottomLayout->addWidget(cancel);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(content);
mainLayout->addLayout(bottomLayout);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/new.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Create new library"));
}
void CreateLibraryDialog::open(const YACReaderLibraries & libs)
{
libraries = libs;
QDialog::open();
}
void CreateLibraryDialog::create()
{
QFileInfo f(path->text());
if(f.exists() && f.isDir() && f.isWritable())
{
if(!libraries.contains(nameEdit->text()))
{
emit(createLibrary(QDir::cleanPath(path->text()),QDir::cleanPath(path->text())+"/.yacreaderlibrary",nameEdit->text()));
close();
}
else
emit(libraryExists(nameEdit->text()));
}
else
QMessageBox::critical(NULL,tr("Path not found"),tr("The selected path does not exist or is not a valid path. Be sure that you have write access to this folder"));
}
void CreateLibraryDialog::nameSetted(const QString & text)
{
if(!text.isEmpty())
{
if(!path->text().isEmpty())
{
QFileInfo fi(path->text());
if(fi.isDir())
accept->setEnabled(true);
else
accept->setEnabled(false);
}
}
else
accept->setEnabled(false);
}
void CreateLibraryDialog::pathSetted(const QString & text)
{
QFileInfo fi(text);
if(fi.isDir())
{
if(!nameEdit->text().isEmpty())
accept->setEnabled(true);
}
else
accept->setEnabled(false);
}
void CreateLibraryDialog::findPath()
{
QString s = QFileDialog::getExistingDirectory(0,"Comics directory",".");
if(!s.isEmpty())
{
path->setText(s);
if(!nameEdit->text().isEmpty())
accept->setEnabled(true);
}
else
accept->setEnabled(false);
}
void CreateLibraryDialog::close()
{
path->clear();
nameEdit->clear();
accept->setEnabled(false);
QDialog::close();
}
void CreateLibraryDialog::setDataAndStart(QString name, QString path)
{
this->path->setText(path);
this->nameEdit->setText(name);
QDialog::open();
create();
}
//-----------------------------------------------------------------------------
// UpdateLibraryDialog
//-----------------------------------------------------------------------------
UpdateLibraryDialog::UpdateLibraryDialog(QWidget * parent)
:QDialog(parent)
{
QVBoxLayout * mainLayout = new QVBoxLayout;
mainLayout->addWidget(message = new QLabel(tr("Updating....")));
mainLayout->addWidget(currentFileLabel = new QLabel("\n\n\n\n"));
currentFileLabel->setWordWrap(true);
QHBoxLayout * bottom = new QHBoxLayout;
bottom->addStretch();
bottom->addWidget(cancel = new QPushButton(tr("Cancel")));
connect(cancel,SIGNAL(clicked()),this,SIGNAL(cancelUpdate()));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
mainLayout->addStretch();
mainLayout->addLayout(bottom);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/updateLibrary.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Update library"));
}
void UpdateLibraryDialog::showCurrentFile(QString file)
{
currentFileLabel->setText(file);
currentFileLabel->update();
this->update();
}
void UpdateLibraryDialog::close()
{
currentFileLabel->setText("");
this->adjustSize();
QDialog::close();
}

View File

@ -0,0 +1,61 @@
#ifndef __CREATE_LIBRARY_DIALOG_H
#define __CREATE_LIBRARY_DIALOG_H
#include "yacreader_libraries.h"
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QThread>
#include <QProgressBar>
class CreateLibraryDialog : public QDialog
{
Q_OBJECT
public:
CreateLibraryDialog(QWidget * parent = 0);
private:
QLabel * nameLabel;
QLabel * textLabel;
QLabel * message;
QProgressBar *progressBar;
QLineEdit * path;
QLineEdit * nameEdit;
QPushButton * find;
QPushButton * accept;
QPushButton * cancel;
YACReaderLibraries libraries;
void setupUI();
public slots:
void create();
void findPath();
void close();
void setDataAndStart(QString name, QString paht);
void nameSetted(const QString & text);
void pathSetted(const QString & text);
void open(const YACReaderLibraries &libraries);
signals:
void createLibrary(QString source, QString target, QString name);
void cancelCreate();
void libraryExists(const QString & name);
};
class UpdateLibraryDialog : public QDialog
{
Q_OBJECT
public:
UpdateLibraryDialog(QWidget * parent = 0);
private:
QLabel * message;
QLabel * currentFileLabel;
QProgressBar *progressBar;
QPushButton * cancel;
public slots:
void showCurrentFile(QString file);
void close();
signals:
void cancelUpdate();
};
#endif

View File

@ -0,0 +1,47 @@
#include <QStringList>
#include "comic_item.h"
//! [0]
ComicItem::ComicItem(const QList<QVariant> &data)
{
itemData = data;
}
//! [0]
//! [1]
ComicItem::~ComicItem()
{
}
//! [1]
//! [5]
int ComicItem::columnCount() const
{
return itemData.count();
}
//! [5]
//! [6]
QVariant ComicItem::data(int column) const
{
return itemData.value(column);
}
//! [6]
void ComicItem::setData(int column,const QVariant & value)
{
itemData[column] = value;
}
//! [8]
int ComicItem::row() const
{
return 0;
}
//! [8]

View File

@ -0,0 +1,27 @@
#ifndef TABLEITEM_H
#define TABLEITEM_H
#include <QList>
#include <QVariant>
//! [0]
class ComicItem : public QObject
{
Q_OBJECT
public:
ComicItem(const QList<QVariant> &data);
~ComicItem();
int columnCount() const;
QVariant data(int column) const;
void setData(int column,const QVariant & value);
int row() const;
//unsigned long long int id; //TODO sustituir por una clase adecuada
//Comic comic;
private:
QList<QVariant> itemData;
};
//! [0]
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,168 @@
#ifndef TABLEMODEL_H
#define TABLEMODEL_H
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QSqlQuery>
#include <QSqlDatabase>
#include "yacreader_global_gui.h"
class ComicDB;
class ComicItem;
using namespace YACReader;
//! [0]
class ComicModel : public QAbstractItemModel
{
Q_OBJECT
public:
ComicModel(QObject *parent = 0);
ComicModel( QSqlQuery &sqlquery, QObject *parent = 0);
~ComicModel();
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
bool canBeResorted();
QMimeData * mimeData(const QModelIndexList &indexes) const;
QStringList mimeTypes() const;
void setupFolderModelData(unsigned long long int parentFolder,const QString & databasePath);
void setupLabelModelData(unsigned long long int parentLabel, const QString & databasePath);
void setupReadingListModelData(unsigned long long int parentReadingList, const QString & databasePath);
void setupFavoritesModelData(const QString & databasePath);
void setupReadingModelData(const QString & databasePath);
//configures the model for showing the comics matching the filter criteria.
void setupModelData(const SearchModifiers modifier, const QString & filter, const QString & databasePath);
//Métodos de conveniencia
QStringList getPaths(const QString & _source);
QString getComicPath(QModelIndex mi);
QString getCurrentPath(){return QString(_databasePath).remove("/.yacreaderlibrary");}
ComicDB getComic(const QModelIndex & mi); //--> para la edición
//ComicDB getComic(int row);
QVector<YACReaderComicReadStatus> getReadList();
QVector<YACReaderComicReadStatus> setAllComicsRead(YACReaderComicReadStatus readStatus);
QList<ComicDB> getComics(QList<QModelIndex> list); //--> recupera la información común a los comics seleccionados
QList<ComicDB> getAllComics();
QModelIndex getIndexFromId(quint64 id);
QList<QModelIndex> getIndexesFromIds(const QList<qulonglong> &comicIds);
//setcomicInfo(QModelIndex & mi); --> inserta en la base datos
//setComicInfoForAllComics(); --> inserta la información común a todos los cómics de una sola vez.
//setComicInfoForSelectedComis(QList<QModelIndex> list); -->inserta la información común para los comics seleccionados
QVector<YACReaderComicReadStatus> setComicsRead(QList<QModelIndex> list,YACReaderComicReadStatus read);
qint64 asignNumbers(QList<QModelIndex> list,int startingNumber);
void remove(ComicDB * comic, int row);
void removeInTransaction(int row);
void reload(const ComicDB & comic);
void resetComicRating(const QModelIndex & mi);
void addComicsToFavorites(const QList<QModelIndex> &comicsList);
void addComicsToLabel(const QList<QModelIndex> &comicsList, qulonglong labelId);
void addComicsToReadingList(const QList<QModelIndex> &comicsList, qulonglong readingListId);
void deleteComicsFromFavorites(const QList<QModelIndex> &comicsList);
void deleteComicsFromLabel(const QList<QModelIndex> &comicsList, qulonglong labelId);
void deleteComicsFromReadingList(const QList<QModelIndex> &comicsList, qulonglong readingListId);
void deleteComicsFromModel(const QList<QModelIndex> &comicsList);
bool isFavorite(const QModelIndex &index);
QHash<int, QByteArray> roleNames() const;
enum Columns {
Number = 0,
Title = 1,
FileName = 2,
NumPages = 3,
Id = 4,
Parent_Id = 5,
Path = 6,
Hash = 7,
ReadColumn = 8,
IsBis = 9,
CurrentPage = 10,
Rating = 11,
HasBeenOpened = 12
};
enum Roles {
NumberRole = Qt::UserRole + 1,
TitleRole,
FileNameRole,
NumPagesRole,
IdRole,
Parent_IdRole,
PathRole,
HashRole,
ReadColumnRole,
IsBisRole,
CurrentPageRole,
RatingRole,
HasBeenOpenedRole,
CoverPathRole
};
enum Mode {
Folder,
Favorites,
Reading,
Label,
ReadingList
};
public slots:
void remove(int row);
void startTransaction();
void finishTransaction();
void updateRating(int rating, QModelIndex mi);
void addComicsToFavorites(const QList<qulonglong> &comicIds);
void addComicsToLabel(const QList<qulonglong> &comicIds, qulonglong labelId);
void addComicsToReadingList(const QList<qulonglong> &comicIds, qulonglong readingListId);
protected:
private:
void setupModelData( QSqlQuery &sqlquery);
void setupModelDataForList(QSqlQuery &sqlquery);
ComicDB _getComic(const QModelIndex & mi);
QList<ComicItem *> _data;
QString _databasePath;
QSqlDatabase dbTransaction;
bool enableResorting;
Mode mode;
qulonglong sourceId;
signals:
void beforeReset();
void reset();
void isEmpty();
void searchNumResults(int);
void resortedIndexes(QList<int>);
void newSelectedIndex(const QModelIndex &);
};
//! [0]
#endif

View File

@ -0,0 +1,790 @@
#include "data_base_management.h"
#include <QtCore>
#include "library_creator.h"
#include "check_new_version.h"
static QString fields = "title ,"
"coverPage,"
"numPages,"
"number,"
"isBis,"
"count,"
"volume,"
"storyArc,"
"arcNumber,"
"arcCount,"
"genere,"
"writer,"
"penciller,"
"inker,"
"colorist,"
"letterer,"
"coverArtist,"
"date,"
"publisher,"
"format,"
"color,"
"ageRating,"
"synopsis,"
"characters,"
"notes,"
"comicVineID,"
"hash"
;
DataBaseManagement::DataBaseManagement()
:QObject(),dataBasesList()
{
}
/*TreeModel * DataBaseManagement::newTreeModel(QString path)
{
//la consulta se ejecuta...
QSqlQuery selectQuery(loadDatabase(path));
selectQuery.setForwardOnly(true);
selectQuery.exec("select * from folder order by parentId,name");
//selectQuery.finish();
return new TreeModel(selectQuery);
}*/
QSqlDatabase DataBaseManagement::createDatabase(QString name, QString path)
{
return createDatabase(QDir::cleanPath(path) + "/" + name + ".ydb");
}
QSqlDatabase DataBaseManagement::createDatabase(QString dest)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",dest);
db.setDatabaseName(dest);
if (!db.open())
qDebug() << db.lastError();
else {
qDebug() << db.tables();
}
{
QSqlQuery pragma("PRAGMA foreign_keys = ON",db);
//pragma.finish();
DataBaseManagement::createTables(db);
QSqlQuery query("INSERT INTO folder (parentId, name, path) "
"VALUES (1,'root', '/')",db);
}
//query.finish();
//db.close();
return db;
}
QSqlDatabase DataBaseManagement::loadDatabase(QString path)
{
//TODO check path
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",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);
//pragma.finish();
//devuelve la base de datos
return db;
}
QSqlDatabase DataBaseManagement::loadDatabaseFromFile(QString filePath)
{
//TODO check path
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE",filePath);
db.setDatabaseName(filePath);
if (!db.open()) {
//se devuelve una base de datos vacía e inválida
return QSqlDatabase();
}
{
QSqlQuery pragma("PRAGMA foreign_keys = ON",db);
}
//pragma.finish();
//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,"
//new 7.1 fields
"finished BOOLEAN DEFAULT 0," //reading
"completed BOOLEAN DEFAULT 1," //collecting
//--
"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,"
"title TEXT,"
"coverPage INTEGER DEFAULT 1,"
"numPages INTEGER,"
"number INTEGER,"
"isBis BOOLEAN,"
"count INTEGER,"
"volume TEXT,"
"storyArc TEXT,"
"arcNumber INTEGER,"
"arcCount INTEGER,"
"genere TEXT,"
"writer TEXT,"
"penciller TEXT,"
"inker TEXT,"
"colorist TEXT,"
"letterer TEXT,"
"coverArtist TEXT,"
"date TEXT," //dd/mm/yyyy --> se mostrará en 3 campos diferentes
"publisher TEXT,"
"format TEXT,"
"color BOOLEAN,"
"ageRating BOOLEAN,"
"synopsis TEXT,"
"characters TEXT,"
"notes TEXT,"
"hash TEXT UNIQUE NOT NULL,"
"edited BOOLEAN DEFAULT 0,"
"read BOOLEAN DEFAULT 0,"
//new 7.0 fields
"hasBeenOpened BOOLEAN DEFAULT 0,"
"rating INTEGER DEFAULT 0,"
"currentPage INTEGER DEFAULT 1, "
"bookmark1 INTEGER DEFAULT -1, "
"bookmark2 INTEGER DEFAULT -1, "
"bookmark3 INTEGER DEFAULT -1, "
"brightness INTEGER DEFAULT -1, "
"contrast INTEGER DEFAULT -1, "
"gamma INTEGER DEFAULT -1, "
//new 7.1 fields
"comicVineID TEXT"
")");
success = success && queryComicInfo.exec();
//queryComicInfo.finish();
//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();
//queryComic.finish();
//DB INFO
QSqlQuery queryDBInfo(database);
queryDBInfo.prepare("CREATE TABLE db_info (version TEXT NOT NULL)");
success = success && queryDBInfo.exec();
//queryDBInfo.finish();
QSqlQuery query("INSERT INTO db_info (version) "
"VALUES ('" VERSION "')",database);
//query.finish();
//8.0> tables
success = success && DataBaseManagement::createV8Tables(database);
}
return success;
}
bool DataBaseManagement::createV8Tables(QSqlDatabase &database)
{
bool success = true;
{
//8.0> tables
//LABEL
QSqlQuery queryLabel(database);
success = success && queryLabel.exec("CREATE TABLE label (id INTEGER PRIMARY KEY, "
"name TEXT NOT NULL, "
"color TEXT NOT NULL, "
"ordering INTEGER NOT NULL); "); //order depends on the color
QSqlQuery queryIndexLabel(database);
success = success && queryIndexLabel.exec("CREATE INDEX label_ordering_index ON label (ordering)");
//COMIC LABEL
QSqlQuery queryComicLabel(database);
success = success && queryComicLabel.exec("CREATE TABLE comic_label ("
"comic_id INTEGER, "
"label_id INTEGER, "
"ordering INTEGER, " //TODO order????
"FOREIGN KEY(label_id) REFERENCES label(id) ON DELETE CASCADE, "
"FOREIGN KEY(comic_id) REFERENCES comic(id) ON DELETE CASCADE, "
"PRIMARY KEY(label_id, comic_id))");
QSqlQuery queryIndexComicLabel(database);
success = success && queryIndexComicLabel.exec("CREATE INDEX comic_label_ordering_index ON label (ordering)");
//READING LIST
QSqlQuery queryReadingList(database);
success = success && queryReadingList.exec("CREATE TABLE reading_list ("
"id INTEGER PRIMARY KEY, "
"parentId INTEGER, "
"ordering INTEGER DEFAULT 0, " //only use it if the parentId is NULL
"name TEXT NOT NULL, "
"finished BOOLEAN DEFAULT 0, "
"completed BOOLEAN DEFAULT 1, "
"FOREIGN KEY(parentId) REFERENCES reading_list(id) ON DELETE CASCADE)");
QSqlQuery queryIndexReadingList(database);
success = success && queryIndexReadingList.exec("CREATE INDEX reading_list_ordering_index ON label (ordering)");
//COMIC READING LIST
QSqlQuery queryComicReadingList(database);
success = success && queryComicReadingList.exec("CREATE TABLE comic_reading_list ("
"reading_list_id INTEGER, "
"comic_id INTEGER, "
"ordering INTEGER, "
"FOREIGN KEY(reading_list_id) REFERENCES reading_list(id) ON DELETE CASCADE, "
"FOREIGN KEY(comic_id) REFERENCES comic(id) ON DELETE CASCADE, "
"PRIMARY KEY(reading_list_id, comic_id))");
QSqlQuery queryIndexComicReadingList(database);
success = success && queryIndexComicReadingList.exec("CREATE INDEX comic_reading_list_ordering_index ON label (ordering)");
//DEFAULT READING LISTS
QSqlQuery queryDefaultReadingList(database);
success = success && queryDefaultReadingList.exec("CREATE TABLE default_reading_list ("
"id INTEGER PRIMARY KEY, "
"name TEXT NOT NULL"
//TODO icon????
")");
//COMIC DEFAULT READING LISTS
QSqlQuery queryComicDefaultReadingList(database);
success = success && queryComicDefaultReadingList.exec("CREATE TABLE comic_default_reading_list ("
"comic_id INTEGER, "
"default_reading_list_id INTEGER, "
"ordering INTEGER, " //order????
"FOREIGN KEY(default_reading_list_id) REFERENCES default_reading_list(id) ON DELETE CASCADE, "
"FOREIGN KEY(comic_id) REFERENCES comic(id) ON DELETE CASCADE,"
"PRIMARY KEY(default_reading_list_id, comic_id))");
QSqlQuery queryIndexComicDefaultReadingList(database);
success = success && queryIndexComicDefaultReadingList.exec("CREATE INDEX comic_default_reading_list_ordering_index ON label (ordering)");
//INSERT DEFAULT READING LISTS
QSqlQuery queryInsertDefaultReadingList(database);
//if(!queryInsertDefaultReadingList.prepare())
//1 Favorites
//queryInsertDefaultReadingList.bindValue(":name", "Favorites");
success = success && queryInsertDefaultReadingList.exec("INSERT INTO default_reading_list (name) VALUES (\"Favorites\")");
//Reading doesn't need its onw list
}
return success;
}
void DataBaseManagement::exportComicsInfo(QString source, QString dest)
{
//QSqlDatabase sourceDB = loadDatabase(source);
QSqlDatabase destDB = loadDatabaseFromFile(dest);
//sourceDB.open();
{
QSqlQuery attach(destDB);
attach.prepare("ATTACH DATABASE '"+QDir().toNativeSeparators(dest) +"' AS dest;");
//attach.bindValue(":dest",QDir().toNativeSeparators(dest));
attach.exec();
//attach.finish();
QSqlQuery attach2(destDB);
attach2.prepare("ATTACH DATABASE '"+QDir().toNativeSeparators(source) +"' AS source;");
attach2.exec();
//attach2.finish();
//sourceDB.close();
QSqlQuery queryDBInfo(destDB);
queryDBInfo.prepare("CREATE TABLE dest.db_info (version TEXT NOT NULL)");
queryDBInfo.exec();
//queryDBInfo.finish();
/*QSqlQuery queryComicsInfo(sourceDB);
queryComicsInfo.prepare("CREATE TABLE dest.comic_info (id INTEGER PRIMARY KEY, hash TEXT NOT NULL, edited BOOLEAN DEFAULT FALSE, title TEXT, read BOOLEAN)");
queryComicsInfo.exec();*/
QSqlQuery query("INSERT INTO dest.db_info (version) "
"VALUES ('" VERSION "')",destDB);
//query.finish();
QSqlQuery exportData(destDB);
exportData.prepare("create table dest.comic_info as select " + fields +
" from source.comic_info where source.comic_info.edited = 1");
exportData.exec();
//exportData.finish();
}
//sourceDB.close();
destDB.close();
QSqlDatabase::removeDatabase(dest);
}
bool DataBaseManagement::importComicsInfo(QString source, QString dest)
{
QString error;
QString driver;
QStringList hashes;
bool b = false;
QSqlDatabase sourceDB = loadDatabaseFromFile(source);
QSqlDatabase destDB = loadDatabaseFromFile(dest);
{
QSqlQuery pragma("PRAGMA synchronous=OFF",destDB);
QSqlQuery newInfo(sourceDB);
newInfo.prepare("SELECT * FROM comic_info");
newInfo.exec();
destDB.transaction();
int cp;
while (newInfo.next()) //cada tupla deberá ser insertada o actualizada
{
QSqlQuery update(destDB);
update.prepare("UPDATE comic_info SET "
"title = :title,"
"coverPage = :coverPage,"
"numPages = :numPages,"
"number = :number,"
"isBis = :isBis,"
"count = :count,"
"volume = :volume,"
"storyArc = :storyArc,"
"arcNumber = :arcNumber,"
"arcCount = :arcCount,"
"genere = :genere,"
"writer = :writer,"
"penciller = :penciller,"
"inker = :inker,"
"colorist = :colorist,"
"letterer = :letterer,"
"coverArtist = :coverArtist,"
"date = :date,"
"publisher = :publisher,"
"format = :format,"
"color = :color,"
"ageRating = :ageRating,"
"synopsis = :synopsis,"
"characters = :characters,"
"notes = :notes,"
"edited = :edited,"
"comicVineID = :comicVineID"
" WHERE hash = :hash ");
QSqlQuery insert(destDB);
insert.prepare("INSERT INTO comic_info "
"(title,"
"coverPage,"
"numPages,"
"number,"
"isBis,"
"count,"
"volume,"
"storyArc,"
"arcNumber,"
"arcCount,"
"genere,"
"writer,"
"penciller,"
"inker,"
"colorist,"
"letterer,"
"coverArtist,"
"date,"
"publisher,"
"format,"
"color,"
"ageRating,"
"synopsis,"
"characters,"
"notes,"
"read,"
"edited,"
"comicVineID,"
"hash)"
"VALUES (:title,"
":coverPage,"
":numPages,"
":number,"
":isBis,"
":count,"
":volume,"
":storyArc,"
":arcNumber,"
":arcCount,"
":genere,"
":writer,"
":penciller,"
":inker,"
":colorist,"
":letterer,"
":coverArtist,"
":date,"
":publisher,"
":format,"
":color,"
":ageRating,"
":synopsis,"
":characters,"
":notes,"
":read,"
":edited,"
":comicVineID,"
":hash )");
QSqlRecord record = newInfo.record();
cp = record.value("coverPage").toInt();
if(cp>1)
{
QSqlQuery checkCoverPage(destDB);
checkCoverPage.prepare("SELECT coverPage FROM comic_info where hash = :hash");
checkCoverPage.bindValue(":hash",record.value("hash").toString());
checkCoverPage.exec();
bool extract = false;
if(checkCoverPage.next())
{
extract = checkCoverPage.record().value("coverPage").toInt() != cp;
}
if(extract)
hashes.append(record.value("hash").toString());
}
bindValuesFromRecord(record,update);
update.bindValue(":edited",1);
update.exec();
if(update.numRowsAffected() == 0)
{
bindValuesFromRecord(record,insert);
insert.bindValue(":edited",1);
insert.bindValue(":read",0);
insert.exec();
QString error1 = insert.lastError().databaseText();
QString error2 = insert.lastError().driverText();
//QMessageBox::critical(NULL,"db",error1);
//QMessageBox::critical(NULL,"driver",error2);
}
//update.finish();
//insert.finish();
}
}
destDB.commit();
QString hash;
foreach(hash, hashes)
{
QSqlQuery getComic(destDB);
getComic.prepare("SELECT c.path,ci.coverPage FROM comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id) where ci.hash = :hash");
getComic.bindValue(":hash",hash);
getComic.exec();
if(getComic.next())
{
QString basePath = QString(dest).remove("/.yacreaderlibrary/library.ydb");
QString path = basePath + getComic.record().value("path").toString();
int coverPage = getComic.record().value("coverPage").toInt();
ThumbnailCreator tc(path,basePath+"/.yacreaderlibrary/covers/"+hash+".jpg",coverPage);
tc.create();
}
}
destDB.close();
sourceDB.close();
QSqlDatabase::removeDatabase(source);
QSqlDatabase::removeDatabase(dest);
return b;
}
//TODO fix these bindings
void DataBaseManagement::bindValuesFromRecord(const QSqlRecord & record, QSqlQuery & query)
{
bindString("title",record,query);
bindInt("coverPage",record,query);
bindInt("numPages",record,query);
bindInt("number",record,query);
bindInt("isBis",record,query);
bindInt("count",record,query);
bindString("volume",record,query);
bindString("storyArc",record,query);
bindInt("arcNumber",record,query);
bindInt("arcCount",record,query);
bindString("genere",record,query);
bindString("writer",record,query);
bindString("penciller",record,query);
bindString("inker",record,query);
bindString("colorist",record,query);
bindString("letterer",record,query);
bindString("coverArtist",record,query);
bindString("date",record,query);
bindString("publisher",record,query);
bindString("format",record,query);
bindInt("color",record,query);
bindString("ageRating",record,query);
bindString("synopsis",record,query);
bindString("characters",record,query);
bindString("notes",record,query);
bindString("comicVineID",record,query);
bindString("hash",record,query);
}
bool DataBaseManagement::addColumns(const QString &tableName, const QStringList &columnDefs, const QSqlDatabase &db)
{
QString sql = "ALTER TABLE %1 ADD COLUMN %2";
bool returnValue = true;
foreach(QString columnDef, columnDefs)
{
QSqlQuery alterTable(db);
alterTable.prepare(sql.arg(tableName).arg(columnDef));
//alterTableComicInfo.bindValue(":column_def",columnDef);
alterTable.exec();
returnValue = returnValue && (alterTable.numRowsAffected() > 0);
}
return returnValue;
}
void DataBaseManagement::bindString(const QString & name, const QSqlRecord & record, QSqlQuery & query)
{
if(!record.value(name).isNull())
{
query.bindValue(":"+name,record.value(name).toString());
}
}
void DataBaseManagement::bindInt(const QString & name, const QSqlRecord & record, QSqlQuery & query)
{
if(!record.value(name).isNull())
{
query.bindValue(":"+name,record.value(name).toInt());
}
}
QString DataBaseManagement::checkValidDB(const QString & fullPath)
{
QSqlDatabase db = loadDatabaseFromFile(fullPath);
QString versionString = "";
if(db.isValid() && db.isOpen())
{
QSqlQuery version(db);
version.prepare("SELECT * FROM db_info");
version.exec();
if(version.next())
versionString = version.record().value("version").toString();
}
db.close();
QSqlDatabase::removeDatabase(fullPath);
return versionString;
}
int DataBaseManagement::compareVersions(const QString & v1, const QString v2)
{
QStringList v1l = v1.split('.');
QStringList v2l = v2.split('.');
QList<int> v1il;
QList<int> v2il;
foreach(QString s, v1l)
v1il.append(s.toInt());
foreach(QString s,v2l)
v2il.append(s.toInt());
for(int i=0;i<qMin(v1il.length(),v2il.length());i++)
{
if(v1il[i]<v2il[i])
return -1;
if(v1il[i]>v2il[i])
return 1;
}
if(v1il.length() < v2il.length())
return -1;
if(v1il.length() == v2il.length())
return 0;
if(v1il.length() > v2il.length())
return 1;
return 0;
}
bool DataBaseManagement::updateToCurrentVersion(const QString & fullPath)
{
bool pre7 = false;
bool pre7_1 = false;
bool pre8 = false;
if(compareVersions(DataBaseManagement::checkValidDB(fullPath),"7.0.0")<0)
pre7 = true;
if(compareVersions(DataBaseManagement::checkValidDB(fullPath),"7.0.3")<0)
pre7_1 = true;
if(compareVersions(DataBaseManagement::checkValidDB(fullPath),"8.0.0")<0)
pre8 = true;
QSqlDatabase db = loadDatabaseFromFile(fullPath);
bool returnValue = false;
if(db.isValid() && db.isOpen())
{
QSqlQuery updateVersion(db);
updateVersion.prepare("UPDATE db_info SET "
"version = :version");
updateVersion.bindValue(":version",VERSION);
updateVersion.exec();
if(updateVersion.numRowsAffected() > 0)
returnValue = true;
if(pre7) //TODO: execute only if previous version was < 7.0
{
//new 7.0 fields
QStringList columnDefs;
columnDefs << "hasBeenOpened BOOLEAN DEFAULT 0"
<< "rating INTEGER DEFAULT 0"
<< "currentPage INTEGER DEFAULT 1"
<< "bookmark1 INTEGER DEFAULT -1"
<< "bookmark2 INTEGER DEFAULT -1"
<< "bookmark3 INTEGER DEFAULT -1"
<< "brightness INTEGER DEFAULT -1"
<< "contrast INTEGER DEFAULT -1"
<< "gamma INTEGER DEFAULT -1";
returnValue = returnValue && addColumns("comic_info", columnDefs, db);
}
//TODO update hasBeenOpened value
if(pre7_1)
{
{
QStringList columnDefs;
columnDefs << "finished BOOLEAN DEFAULT 0"
<< "completed BOOLEAN DEFAULT 1";
returnValue = returnValue && addColumns("folder", columnDefs, db);
}
{//comic_info
QStringList columnDefs;
columnDefs << "comicVineID TEXT DEFAULT NULL";
returnValue = returnValue && addColumns("comic_info", columnDefs, db);
}
}
if(pre8)
{
returnValue = returnValue && createV8Tables(db);
}
}
db.close();
QSqlDatabase::removeDatabase(fullPath);
return returnValue;
}
//COMICS_INFO_EXPORTER
ComicsInfoExporter::ComicsInfoExporter()
:QThread()
{
}
void ComicsInfoExporter::exportComicsInfo(QSqlDatabase & source, QSqlDatabase & dest)
{
Q_UNUSED(source)
Q_UNUSED(dest)
//TODO check this method
}
void ComicsInfoExporter::run()
{
}
//COMICS_INFO_IMPORTER
ComicsInfoImporter::ComicsInfoImporter()
:QThread()
{
}
void ComicsInfoImporter::importComicsInfo(QSqlDatabase & source, QSqlDatabase & dest)
{
Q_UNUSED(source)
Q_UNUSED(dest)
//TODO check this method
}
void ComicsInfoImporter::run()
{
}

View File

@ -0,0 +1,62 @@
#ifndef __DATA_BASE_MANAGEMENT_H
#define __DATA_BASE_MANAGEMENT_H
#include <QtCore>
#include <QtSql>
#include <QSqlDatabase>
#include "folder_model.h"
class ComicsInfoExporter : public QThread
{
Q_OBJECT
public:
ComicsInfoExporter();
void exportComicsInfo(QSqlDatabase & source, QSqlDatabase & dest);
private:
void run();
};
class ComicsInfoImporter : public QThread
{
Q_OBJECT
public:
ComicsInfoImporter();
void importComicsInfo(QSqlDatabase & source, QSqlDatabase & dest);
private:
void run();
};
class DataBaseManagement : public QObject
{
Q_OBJECT
private:
QList<QString> dataBasesList;
static void bindString(const QString & name, const QSqlRecord & record, QSqlQuery & query);
static void bindInt(const QString & name, const QSqlRecord & record, QSqlQuery & query);
static void bindValuesFromRecord(const QSqlRecord & record, QSqlQuery & query);
static bool addColumns(const QString & tableName, const QStringList & columnDefs, const QSqlDatabase & db);
public:
DataBaseManagement();
//TreeModel * newTreeModel(QString path);
//crea una base de datos y todas sus tablas
static QSqlDatabase createDatabase(QString name, QString path);
static QSqlDatabase createDatabase(QString dest);
//carga una base de datos desde la ruta path
static QSqlDatabase loadDatabase(QString path);
static QSqlDatabase loadDatabaseFromFile(QString path);
static bool createTables(QSqlDatabase & database);
static bool createV8Tables(QSqlDatabase & database);
static void exportComicsInfo(QString source, QString dest);
static bool importComicsInfo(QString source, QString dest);
static QString checkValidDB(const QString & fullPath); //retorna "" si la DB es inválida ó la versión si es válida.
static int compareVersions(const QString & v1, const QString v2); //retorna <0 si v1 < v2, 0 si v1 = v2 y >0 si v1 > v2
static bool updateToCurrentVersion(const QString & path);
};
#endif

View File

@ -0,0 +1,103 @@
#include <QStringList>
#include "folder_item.h"
#include "qnaturalsorting.h"
FolderItem::FolderItem(const QList<QVariant> &data, FolderItem *parent)
{
parentItem = parent;
itemData = data;
}
FolderItem::~FolderItem()
{
qDeleteAll(childItems);
}
void FolderItem::appendChild(FolderItem *item)
{
item->parentItem = this;
if(childItems.isEmpty())
childItems.append(item);
else
{
FolderItem * last = childItems.back();
QString nameLast = last->data(1).toString(); //TODO usar info name si est<73> disponible, sino el nombre del fichero.....
QString nameCurrent = item->data(1).toString();
QList<FolderItem *>::iterator i;
i = childItems.end();
i--;
while (naturalSortLessThanCI(nameCurrent,nameLast) && i != childItems.begin())
{
i--;
nameLast = (*i)->data(1).toString();
}
if(!naturalSortLessThanCI(nameCurrent,nameLast)) //si se ha encontrado un elemento menor que current, se inserta justo despu<70>s
childItems.insert(++i,item);
else
childItems.insert(i,item);
}
//childItems.append(item);
}
FolderItem *FolderItem::child(int row)
{
return childItems.value(row);
}
int FolderItem::childCount() const
{
return childItems.count();
}
int FolderItem::columnCount() const
{
return itemData.count();
}
QVariant FolderItem::data(int column) const
{
return itemData.value(column);
}
void FolderItem::setData(int column, const QVariant & value)
{
itemData[column] = value;
}
void FolderItem::removeChild(int childIndex)
{
childItems.removeAt(childIndex);
}
void FolderItem::clearChildren()
{
qDeleteAll(childItems);
childItems.clear();
}
QList<FolderItem *> FolderItem::children()
{
return childItems;
}
FolderItem *FolderItem::parent()
{
return parentItem;
}
int FolderItem::row() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<FolderItem*>(this));
return 0;
}
QList<QVariant> FolderItem::getData() const
{
return itemData;
}

View File

@ -0,0 +1,77 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QList>
#include <QVariant>
#include <QModelIndex>
class FolderItem
{
public:
FolderItem(const QList<QVariant> &data, FolderItem *parent = 0);
~FolderItem();
void appendChild(FolderItem *child);
FolderItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
QList<QVariant> getData() const;
int row() const;
FolderItem *parent();
FolderItem *parentItem;
unsigned long long int id;
QList<QString> comicNames;
FolderItem * originalItem;
void setData(int column, const QVariant &value);
void removeChild(int childIndex);
void clearChildren();
QList<FolderItem*> children();
private:
QList<FolderItem*> childItems;
QList<QVariant> itemData;
};
//! [0]
#endif

View File

@ -0,0 +1,818 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/
/*
treemodel.cpp
Provides a simple tree model to show how to create and use hierarchical
models.
*/
#include <QtGui>
#include "folder_item.h"
#include "folder_model.h"
#include "data_base_management.h"
#include "folder.h"
#include "db_helper.h"
#include "qnaturalsorting.h"
#include "yacreader_global_gui.h"
#include "QsLog.h"
#ifdef Q_OS_MAC
#include <QFileIconProvider>
QIcon finishedFolderIcon;
void drawMacOSXFinishedFolderIcon()
{
QIcon ico = QFileIconProvider().icon(QFileIconProvider::Folder);
QPixmap pixNormalOff = ico.pixmap(16,16, QIcon::Normal, QIcon::Off);
QPixmap pixNormalOn = ico.pixmap(16,16, QIcon::Normal, QIcon::On);
QPixmap pixSelectedOff = ico.pixmap(16,16, QIcon::Selected, QIcon::Off);
QPixmap pixSelectedOn = ico.pixmap(16,16, QIcon::Selected, QIcon::On);
QPixmap tick(":/images/folder_finished_macosx.png");
{
QPainter p(&pixNormalOff);
p.drawPixmap(4,7,tick);
}
finishedFolderIcon.addPixmap(pixNormalOff, QIcon::Normal, QIcon::Off);
{
QPainter p(&pixNormalOn);
p.drawPixmap(4,7,tick);
}
finishedFolderIcon.addPixmap(pixNormalOn, QIcon::Normal, QIcon::On);
{
QPainter p(&pixSelectedOff);
p.drawPixmap(4,7,tick);
}
finishedFolderIcon.addPixmap(pixSelectedOff, QIcon::Selected, QIcon::Off);
{
QPainter p(&pixSelectedOn);
p.drawPixmap(4,7,tick);
}
finishedFolderIcon.addPixmap(pixSelectedOn, QIcon::Selected, QIcon::On);
}
#endif
#define ROOT 1
FolderModel::FolderModel(QObject *parent)
: QAbstractItemModel(parent),rootItem(0)
{
connect(this,SIGNAL(beforeReset()),this,SIGNAL(modelAboutToBeReset()));
connect(this,SIGNAL(reset()),this,SIGNAL(modelReset()));
}
//! [0]
FolderModel::FolderModel( QSqlQuery &sqlquery, QObject *parent)
: QAbstractItemModel(parent),rootItem(0)
{
//lo m<>s probable es que el nodo ra<72>z no necesite tener informaci<63>n
QList<QVariant> 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 FolderItem(rootData);
rootItem->id = ROOT;
rootItem->parentItem = 0;
setupModelData(sqlquery, rootItem);
//sqlquery.finish();
}
//! [0]
//! [1]
FolderModel::~FolderModel()
{
if(rootItem != 0)
delete rootItem;
}
//! [1]
//! [2]
int FolderModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<FolderItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}
//! [2]
//! [3]
QVariant FolderModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
FolderItem *item = static_cast<FolderItem*>(index.internalPointer());
if (role == Qt::ToolTipRole)
{
QString toolTip = item->data(FolderModel::Name).toString();
int totalNumOfChildren = item->childCount() + item->comicNames.size();
if(totalNumOfChildren > 0)
{
toolTip = toolTip + " - " + QString::number(totalNumOfChildren);
}
return toolTip;
}
if (role == Qt::DecorationRole)
#ifdef Q_OS_MAC
if(item->data(FolderModel::Finished).toBool()){
if(finishedFolderIcon.isNull()){
drawMacOSXFinishedFolderIcon();
}
return QVariant(finishedFolderIcon);
}
else {
return QVariant(QFileIconProvider().icon(QFileIconProvider::Folder));
}
#else
if(item->data(FolderModel::Finished).toBool())
return QVariant(YACReader::noHighlightedIcon(":/images/sidebar/folder_finished.png"));
else
return QVariant(YACReader::noHighlightedIcon(":/images/sidebar/folder.png"));
#endif
if(role == FolderModel::CompletedRole)
return item->data(FolderModel::Completed);
if(role == FolderModel::FinishedRole)
return item->data(FolderModel::Finished);
if (role != Qt::DisplayRole)
return QVariant();
return item->data(index.column());
}
//! [3]
//! [4]
Qt::ItemFlags FolderModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled;
}
//! [4]
//! [5]
QVariant FolderModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);
return QVariant();
}
//! [5]
//! [6]
QModelIndex FolderModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
FolderItem *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<FolderItem*>(parent.internalPointer());
FolderItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
//! [6]
//! [7]
QModelIndex FolderModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
FolderItem *childItem = static_cast<FolderItem*>(index.internalPointer());
FolderItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
//! [7]
/*
QModelIndex FolderModel::indexFromItem(FolderItem * item,int column)
{
//if(item->parent() != 0)
// return index(item->row(),column,parent(indexFromItem(item->parent(),column-1)));
//else
// return index(item->row(),0,QModelIndex());
return createIndex(item->row(), column, item);
}*/
//! [8]
int FolderModel::rowCount(const QModelIndex &parent) const
{
FolderItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<FolderItem*>(parent.internalPointer());
return parentItem->childCount();
}
//! [8]
void FolderModel::setupModelData(QString path)
{
beginResetModel();
if(rootItem != 0)
delete rootItem; //TODO comprobar que se libera bien la memoria
rootItem = 0;
//inicializar el nodo ra<72>z
QList<QVariant> 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 FolderItem(rootData);
rootItem->id = ROOT;
rootItem->parentItem = 0;
//cargar la base de datos
_databasePath = path;
QSqlDatabase db = DataBaseManagement::loadDatabase(path);
//crear la consulta
{
QSqlQuery selectQuery("select * from folder where id <> 1 order by parentId,name",db);
setupModelData(selectQuery,rootItem);
}
//selectQuery.finish();
db.close();
QSqlDatabase::removeDatabase(path);
endResetModel();
}
void FolderModel::setupModelData(QSqlQuery &sqlquery, FolderItem *parent)
{
//64 bits para la primary key, es decir la misma precisi<73>n que soporta sqlit 2^64
//el diccionario permitir<69> encontrar cualquier nodo del <20>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);
QSqlRecord record = sqlquery.record();
int name = record.indexOf("name");
int path = record.indexOf("path");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int id = record.indexOf("id");
int parentId = record.indexOf("parentId");
while (sqlquery.next()) {
QList<QVariant> data;
data << sqlquery.value(name).toString();
data << sqlquery.value(path).toString();
data << sqlquery.value(finished).toBool();
data << sqlquery.value(completed).toBool();
FolderItem * item = new FolderItem(data);
item->id = sqlquery.value(id).toULongLong();
//la inserci<63>n de hijos se hace de forma ordenada
FolderItem * parent = items.value(sqlquery.value(parentId).toULongLong());
//if(parent !=0) //TODO if parent==0 the parent of item was removed from the DB and delete on cascade didn't work, ERROR.
parent->appendChild(item);
//se a<>ade el item al map, de forma que se pueda encontrar como padre en siguientes iteraciones
items.insert(item->id,item);
}
}
void FolderModel::updateFolderModelData(QSqlQuery &sqlquery, FolderItem *parent)
{
Q_UNUSED(parent);
QSqlRecord record = sqlquery.record();
int name = record.indexOf("name");
int path = record.indexOf("path");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int id = record.indexOf("id");
int parentId = record.indexOf("parentId");
while (sqlquery.next()) {
QList<QVariant> data;
data << sqlquery.value(name).toString();
data << sqlquery.value(path).toString();
data << sqlquery.value(finished).toBool();
data << sqlquery.value(completed).toBool();
FolderItem * item = new FolderItem(data);
item->id = sqlquery.value(id).toULongLong();
//la inserci<63>n de hijos se hace de forma ordenada
FolderItem * parent = items.value(sqlquery.value(parentId).toULongLong());
if(parent !=0) //TODO if parent==0 the parent of item was removed from the DB and delete on cascade didn't work, ERROR.
parent->appendChild(item);
//se a<>ade el item al map, de forma que se pueda encontrar como padre en siguientes iteraciones
items.insert(item->id,item);
}
}
QString FolderModel::getDatabase()
{
return _databasePath;
}
QString FolderModel::getFolderPath(const QModelIndex &folder)
{
if(!folder.isValid()) //root folder
return "/";
return static_cast<FolderItem*>(folder.internalPointer())->data(FolderModel::Path).toString();
}
/*
void FolderModel::resetFilter()
{
beginResetModel();
filter = "";
includeComics = false;
//TODO hay que liberar la memoria reservada para el filtrado
//items.clear();
filteredItems.clear();
FolderItem * root = rootItem;
rootItem = rootBeforeFilter; //TODO si no se aplica el filtro previamente, esto invalidar<61>a en modelo
if(root !=0)
delete root;
rootBeforeFilter = 0;
filterEnabled = false;
endResetModel();
}*/
void FolderModel::updateFolderCompletedStatus(const QModelIndexList &list, bool status)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
foreach (QModelIndex mi, list)
{
FolderItem * item = static_cast<FolderItem*>(mi.internalPointer());
item->setData(FolderModel::Completed,status);
Folder f = DBHelper::loadFolder(item->id,db);
f.setCompleted(status);
DBHelper::update(f,db);
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
emit dataChanged(index(list.first().row(),FolderModel::Name),index(list.last().row(),FolderModel::Completed));
}
void FolderModel::updateFolderFinishedStatus(const QModelIndexList &list, bool status)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
foreach (QModelIndex mi, list)
{
FolderItem * item = static_cast<FolderItem*>(mi.internalPointer());
item->setData(FolderModel::Finished,status);
Folder f = DBHelper::loadFolder(item->id,db);
f.setFinished(status);
DBHelper::update(f,db);
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
emit dataChanged(index(list.first().row(),FolderModel::Name),index(list.last().row(),FolderModel::Completed));
}
QStringList FolderModel::getSubfoldersNames(const QModelIndex &mi)
{
QStringList result;
qulonglong id = 1;
if(mi.isValid()){
FolderItem * item = static_cast<FolderItem*>(mi.internalPointer());
id = item->id;
}
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
result = DBHelper::loadSubfoldersNames(id,db);
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
//TODO sort result))
qSort(result.begin(),result.end(),naturalSortLessThanCI);
return result;
}
void FolderModel::fetchMoreFromDB(const QModelIndex &parent)
{
FolderItem * item;
if(parent.isValid())
item = static_cast<FolderItem*>(parent.internalPointer());
else
item = rootItem;
//Remove all children
if(item->childCount() > 0)
{
beginRemoveRows(parent, 0, item->childCount()-1);
item->clearChildren();
endRemoveRows();
}
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
QList<FolderItem *> items;
QList<FolderItem *> nextLevelItems;
QSqlQuery selectQuery(db);
selectQuery.prepare("select * from folder where id <> 1 and parentId = :parentId order by parentId,name");
items << item;
bool firstLevelUpdated = false;
while(items.size() > 0)
{
nextLevelItems.clear();
foreach(FolderItem * item, items)
{
QLOG_DEBUG() << "ID " << item->id;
selectQuery.bindValue(":parentId", item->id);
selectQuery.exec();
if(!firstLevelUpdated)
{
//NO size support
int numResults = 0;
while(selectQuery.next())
numResults++;
if(!selectQuery.seek(-1))
selectQuery.exec();
//END no size support
beginInsertRows(parent, 0, numResults-1);
}
updateFolderModelData(selectQuery,item);
if(!firstLevelUpdated)
{
endInsertRows();
firstLevelUpdated = true;
}
nextLevelItems << item->children();
}
items.clear();
items = nextLevelItems;
}
QLOG_DEBUG() << "item->childCount()-1" << item->childCount()-1;
db.close();
QSqlDatabase::removeDatabase(_databasePath);
}
QModelIndex FolderModel::addFolderAtParent(const QString &folderName, const QModelIndex &parent)
{
FolderItem * parentItem;
if(parent.isValid())
parentItem = static_cast<FolderItem*>(parent.internalPointer());
else
parentItem = rootItem;
Folder newFolder;
newFolder.name = folderName;
newFolder.parentId = parentItem->id;
newFolder.path = parentItem->data(1).toString() + "/" + folderName;
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
newFolder.id = DBHelper::insert(&newFolder, db);
QSqlDatabase::removeDatabase(_databasePath);
int destRow = 0;
QList<QVariant> data;
data << newFolder.name;
data << newFolder.path;
data << false; //finished
data << true; //completed
FolderItem * item = new FolderItem(data);
item->id = newFolder.id;
beginInsertRows(parent,0,0); //TODO calculate the destRow before inserting the new child
parentItem->appendChild(item);
destRow = parentItem->children().indexOf(item); //TODO optimize this, appendChild should return the index of the new item
items.insert(item->id,item);
endInsertRows();
return index(destRow,0,parent);
}
void FolderModel::deleteFolder(const QModelIndex &mi)
{
beginRemoveRows(mi.parent(),mi.row(),mi.row());
FolderItem * item = static_cast<FolderItem*>(mi.internalPointer());
FolderItem * parent = item->parent();
parent->removeChild(mi.row());
Folder f;
f.setId(item->id);
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
DBHelper::removeFromDB(&f,db);
QSqlDatabase::removeDatabase(_databasePath);
endRemoveRows();
}
//PROXY
FolderModelProxy::FolderModelProxy(QObject *parent)
:QSortFilterProxyModel(parent),rootItem(0),filterEnabled(false),filter(""),includeComics(true)
{
}
FolderModelProxy::~FolderModelProxy()
{
}
bool FolderModelProxy::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
if(!filterEnabled)
return true;
FolderItem * parent = static_cast<FolderItem *>(source_parent.internalPointer());
if(parent == 0)
parent = static_cast<FolderModel *>(sourceModel())->rootItem;
FolderItem * item = parent->children().at(source_row);
return filteredItems.contains(item->id);
}
void FolderModelProxy::setFilter(const YACReader::SearchModifiers modifier, QString filter, bool includeComics)
{
clear();
this->filter = filter;
this->includeComics = includeComics;
this->modifier = modifier;
filterEnabled = true;
setupFilteredModelData();
}
void FolderModelProxy::setupFilteredModelData()
{
beginResetModel();
//TODO hay que liberar memoria de anteriores filtrados
//inicializar el nodo ra<72>z
if(rootItem != 0)
delete rootItem; //TODO comprobar que se libera bien la memoria
rootItem = 0;
//inicializar el nodo ra<72>z
QList<QVariant> rootData;
rootData << "root";
rootItem = new FolderItem(rootData);
rootItem->id = ROOT;
rootItem->parentItem = 0;
FolderModel * model = static_cast<FolderModel *>(sourceModel());
//cargar la base de datos
QSqlDatabase db = DataBaseManagement::loadDatabase(model->_databasePath);
//crear la consulta
{
QSqlQuery selectQuery(db); //TODO check
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
{
switch(modifier)
{
case YACReader::NoModifiers:
selectQuery.prepare("SELECT DISTINCT f.id, f.parentId, f.name, f.path, f.finished, f.completed "
"FROM folder f LEFT JOIN comic c ON (f.id = c.parentId) "
"WHERE f.id <> 1 AND ((UPPER(c.fileName) like UPPER(:filter)) OR (UPPER(f.name) like UPPER(:filter2))) ORDER BY f.parentId,f.name");
selectQuery.bindValue(":filter", "%%"+filter+"%%");
selectQuery.bindValue(":filter2", "%%"+filter+"%%");
break;
case YACReader::OnlyRead:
selectQuery.prepare("SELECT DISTINCT f.id, f.parentId, f.name, f.path, f.finished, f.completed "
"FROM folder f LEFT JOIN (comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id)) ON (f.id = c.parentId) "
"WHERE f.id <> 1 AND ((UPPER(c.fileName) like UPPER(:filter)) OR (UPPER(f.name) like UPPER(:filter2))) AND ci.read = 1 ORDER BY f.parentId,f.name;");
selectQuery.bindValue(":filter", "%%"+filter+"%%");
selectQuery.bindValue(":filter2", "%%"+filter+"%%");
break;
case YACReader::OnlyUnread:
selectQuery.prepare("SELECT DISTINCT f.id, f.parentId, f.name, f.path, f.finished, f.completed "
"FROM folder f LEFT JOIN (comic c INNER JOIN comic_info ci ON (c.comicInfoId = ci.id)) ON (f.id = c.parentId) "
"WHERE f.id <> 1 AND ((UPPER(c.fileName) like UPPER(:filter)) OR (UPPER(f.name) like UPPER(:filter2))) AND ci.read = 0 ORDER BY f.parentId,f.name;");
selectQuery.bindValue(":filter", "%%"+filter+"%%");
selectQuery.bindValue(":filter2", "%%"+filter+"%%");
break;
default:
QLOG_ERROR() << "not implemented";
break;
}
}
selectQuery.exec();
setupFilteredModelData(selectQuery,rootItem);
}
//selectQuery.finish();
db.close();
QSqlDatabase::removeDatabase(model->_databasePath);
endResetModel();
}
void FolderModelProxy::clear()
{
filterEnabled = false;
filteredItems.clear();
QSortFilterProxyModel::clear();
}
void FolderModelProxy::setupFilteredModelData(QSqlQuery &sqlquery, FolderItem *parent)
{
FolderModel * model = static_cast<FolderModel *>(sourceModel());
//64 bits para la primary key, es decir la misma precisi<73>n que soporta sqlit 2^64
filteredItems.clear();
//se a<>ade el nodo 0 al modelo que representa el arbol de elementos que cumplen con el filtro
filteredItems.insert(parent->id,parent);
QSqlRecord record = sqlquery.record();
int name = record.indexOf("name");
int path = record.indexOf("path");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int parentIdIndex = record.indexOf("parentId");
while (sqlquery.next()) { //se procesan todos los folders que cumplen con el filtro
//datos de la base de datos
QList<QVariant> data;
data << sqlquery.value(name).toString();
data << sqlquery.value(path).toString();
data << sqlquery.value(finished).toBool();
data << sqlquery.value(completed).toBool();
FolderItem * item = new FolderItem(data);
item->id = sqlquery.value(0).toULongLong();
//id del padre
quint64 parentId = sqlquery.value(parentIdIndex).toULongLong();
//se a<>ade el item al map, de forma que se pueda encontrar como padre en siguientes iteraciones
if(!filteredItems.contains(item->id))
filteredItems.insert(item->id,item);
//es necesario conocer las coordenadas de origen para poder realizar scroll autom<6F>tico en la vista
item->originalItem = model->items.value(item->id);
//si el padre ya existe en el modelo, el item se a<>ade como hijo
if(filteredItems.contains(parentId))
filteredItems.value(parentId)->appendChild(item);
else//si el padre a<>n no se ha a<>adido, hay que a<>adirlo a <20>l y todos los padres hasta el nodo ra<72>z
{
//comprobamos con esta variable si el <20>ltimo de los padres (antes del nodo ra<72>z) ya exist<73>a en el modelo
bool parentPreviousInserted = false;
//mientras no se alcance el nodo ra<72>z se procesan todos los padres (de abajo a arriba)
while(parentId != ROOT )
{
//el padre no estaba en el modelo filtrado, as<61> que se rescata del modelo original
FolderItem * parentItem = model->items.value(parentId);
//se debe crear un nuevo nodo (para no compartir los hijos con el nodo original)
FolderItem * newparentItem = new FolderItem(parentItem->getData()); //padre que se a<>adir<69> a la estructura de directorios filtrados
newparentItem->id = parentId;
newparentItem->originalItem = parentItem;
//si el modelo contiene al padre, se a<>ade el item actual como hijo
if(filteredItems.contains(parentId))
{
filteredItems.value(parentId)->appendChild(item);
parentPreviousInserted = true;
}
//sino se registra el nodo para poder encontrarlo con posterioridad y se a<>ade el item actual como hijo
else
{
newparentItem->appendChild(item);
filteredItems.insert(newparentItem->id,newparentItem);
parentPreviousInserted = false;
}
//variables de control del bucle, se avanza hacia el nodo padre
item = newparentItem;
parentId = parentItem->parentItem->id;
}
//si el nodo es hijo de 1 y no hab<61>a sido previamente insertado como hijo, se a<>ade como tal
if(!parentPreviousInserted)
filteredItems.value(ROOT)->appendChild(item);
}
}
}

View File

@ -0,0 +1,151 @@
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
** the names of its contributors may be used to endorse or promote
** products derived from this software without specific prior written
** permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>
#include <QVariant>
#include <QSqlQuery>
#include <QSqlDatabase>
#include "yacreader_global.h"
class FolderItem;
class FolderModelProxy : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit FolderModelProxy(QObject *parent = 0);
~FolderModelProxy();
void setFilter(const YACReader::SearchModifiers modifier, QString filter, bool includeComics);
void setupFilteredModelData( QSqlQuery &sqlquery, FolderItem *parent);
void setupFilteredModelData();
void clear();
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const;
protected:
FolderItem *rootItem;
QMap<unsigned long long int, FolderItem *> filteredItems; //relación entre folders
bool includeComics;
QString filter;
bool filterEnabled;
YACReader::SearchModifiers modifier;
};
class FolderModel : public QAbstractItemModel
{
Q_OBJECT
friend class FolderModelProxy;
public:
FolderModel(QObject *parent = 0);
FolderModel( QSqlQuery &sqlquery, QObject *parent = 0);
~FolderModel();
//QAbstractItemModel methods
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
//Convenience methods
void setupModelData(QString path);
QString getDatabase();
QString getFolderPath(const QModelIndex &folder);
//QModelIndex indexFromItem(FolderItem * item, int column);
//bool isFilterEnabled(){return filterEnabled;};
void updateFolderCompletedStatus(const QModelIndexList & list, bool status);
void updateFolderFinishedStatus(const QModelIndexList & list, bool status);
QStringList getSubfoldersNames(const QModelIndex & mi);
void fetchMoreFromDB(const QModelIndex & parent);
QModelIndex addFolderAtParent(const QString & folderName, const QModelIndex & parent);
enum Columns {
Name = 0,
Path = 1,
Finished = 2,
Completed = 3
};//id INTEGER PRIMARY KEY, parentId INTEGER NOT NULL, name TEXT NOT NULL, path TEXT NOT NULL
enum Roles {
FinishedRole = Qt::UserRole + 1,
CompletedRole
};
public slots:
void deleteFolder(const QModelIndex & mi);
private:
void setupModelData( QSqlQuery &sqlquery, FolderItem *parent);
void updateFolderModelData( QSqlQuery &sqlquery, FolderItem *parent);
FolderItem *rootItem; //el árbol
QMap<unsigned long long int, FolderItem *> items; //relación entre folders
QString _databasePath;
signals:
void beforeReset();
void reset();
};
//! [0]
#endif

View File

@ -0,0 +1,276 @@
#include "reading_list_item.h"
#include "qnaturalsorting.h"
#include <QFileIconProvider>
#include "QsLog.h"
ListItem::ListItem(const QList<QVariant> &data)
:itemData(data)
{
}
int ListItem::columnCount()
{
return itemData.count();
}
QVariant ListItem::data(int column) const
{
return itemData.at(column);
}
qulonglong ListItem::getId() const
{
return 0;
}
//------------------------------------------------------
SpecialListItem::SpecialListItem(const QList<QVariant> &data)
:ListItem(data)
{
}
QIcon SpecialListItem::getIcon() const
{
if(itemData.count()>Id)
{
QString id = itemData.at(Id).toString();
return YACReader::noHighlightedIcon(QString(":/images/lists/default_%1.png").arg(id));
}
QLOG_WARN() << "Icon for SpecialListItem not available";
return QIcon();
}
ReadingListModel::TypeSpecialList SpecialListItem::getType() const
{
if(itemData.count()>Id)
{
int id = itemData.at(Id).toInt();
return (ReadingListModel::TypeSpecialList)id;
}
QLOG_WARN() << "TypeSpecialList not available";
return (ReadingListModel::TypeSpecialList)0;
}
//------------------------------------------------------
LabelItem::LabelItem(const QList<QVariant> &data)
:ListItem(data)
{
}
QIcon LabelItem::getIcon() const
{
if(itemData.count()>Color)
{
QString color = itemData.at(Color).toString();
return YACReader::noHighlightedIcon(QString(":/images/lists/label_%1.png").arg(color).toLower());
}
QLOG_WARN() << "Icon for label item not available";
return QIcon();
}
YACReader::LabelColors LabelItem::colorid() const
{
if(itemData.count()>Ordering)
{
return YACReader::LabelColors(itemData.at(Ordering).toInt());
}
QLOG_WARN() << "Label color for label item not available";
return (YACReader::LabelColors)0;
}
QString LabelItem::name() const
{
if(itemData.count()>Name)
{
return itemData.at(Name).toString();
}
QLOG_WARN() << "Name for label item not available";
return "";
}
void LabelItem::setName(const QString &name)
{
if(itemData.count()>Name)
{
itemData[Name] = name;
}
}
qulonglong LabelItem::getId() const
{
if(itemData.count()>Id)
{
return YACReader::LabelColors(itemData.at(Id).toULongLong());
}
QLOG_WARN() << "Id for Label item not available";
return 0;
}
//------------------------------------------------------
ReadingListItem::ReadingListItem(const QList<QVariant> &data, ReadingListItem *p)
:ListItem(data), parent(p)
{
}
QIcon ReadingListItem::getIcon() const
{
if(parent->getId() == 0)
return YACReader::noHighlightedIcon(":/images/lists/list.png"); //top level list
else
#ifdef Q_OS_MAC
return QFileIconProvider().icon(QFileIconProvider::Folder);
#else
return YACReader::noHighlightedIcon(":/images/sidebar/folder.png"); //sublist
#endif
}
int ReadingListItem::childCount() const
{
return childItems.count();
}
ReadingListItem *ReadingListItem::child(int row)
{
return childItems.at(row);
}
//items are sorted by order
void ReadingListItem::appendChild(ReadingListItem *item)
{
item->parent = this;
if(childItems.isEmpty())
childItems.append(item);
else
{
if(item->parent->getId()==0) //sort by name, top level child
{
int i= 0;
while(i<childItems.length() && naturalSortLessThanCI(childItems.at(i)->name(),item->name()))
i++;
childItems.insert(i,item);
}
else
{
int i= 0;
while(i<childItems.length() && (childItems.at(i)->getOrdering()<item->getOrdering()))
i++;
childItems.insert(i,item);
}
/*ReadingListItem * last = childItems.back();
QString nameLast = last->data(1).toString(); //TODO usar info name si est<73> disponible, sino el nombre del fichero.....
QString nameCurrent = item->data(1).toString();
QList<FolderItem *>::iterator i;
i = childItems.end();
i--;
while (naturalSortLessThanCI(nameCurrent,nameLast) && i != childItems.begin())
{
i--;
nameLast = (*i)->data(1).toString();
}
if(!naturalSortLessThanCI(nameCurrent,nameLast)) //si se ha encontrado un elemento menor que current, se inserta justo despu<70>s
childItems.insert(++i,item);
else
childItems.insert(i,item);*/
}
}
void ReadingListItem::appendChild(ReadingListItem *item, int pos)
{
childItems.insert(pos, item);
}
void ReadingListItem::removeChild(ReadingListItem *item)
{
childItems.removeOne(item);
}
qulonglong ReadingListItem::getId() const
{
if(itemData.count()>Id)
return itemData.at(Id).toULongLong();
QLOG_WARN() << "Name for reading list item not available";
return 0;
}
QString ReadingListItem::name() const
{
if(itemData.count()>Name)
return itemData.at(Name).toString();
QLOG_WARN() << "Name for reading list item not available";
return "";
}
void ReadingListItem::setName(const QString &name)
{
if(itemData.count()>Name)
itemData[Name] = name;
}
int ReadingListItem::getOrdering() const
{
if(itemData.count()>Ordering)
return itemData[Ordering].toInt();
QLOG_WARN() << "Ordering for Item not available";
return 0;
}
void ReadingListItem::setOrdering(const int ordering)
{
if(itemData.count()>Ordering)
itemData[Ordering] = ordering;
}
QList<ReadingListItem *> ReadingListItem::children()
{
return childItems;
}
int ReadingListItem::row() const
{
if (parent)
return parent->childItems.indexOf(const_cast<ReadingListItem*>(this));
return 0;
}
ReadingListSeparatorItem::ReadingListSeparatorItem()
:ListItem(QList<QVariant>())
{
}
QIcon ReadingListSeparatorItem::getIcon() const
{
return QIcon();
}

View File

@ -0,0 +1,104 @@
#ifndef READING_LIST_ITEM_H
#define READING_LIST_ITEM_H
#include <QIcon>
#include <QVariant>
#include "yacreader_global_gui.h"
#include "reading_list_model.h"
//TODO add propper constructors, using QList<QVariant> is not safe
class ListItem
{
public:
ListItem(const QList<QVariant> &data);
int columnCount();
virtual QIcon getIcon() const = 0;
QVariant data(int column) const;
virtual qulonglong getId() const;
QList<QVariant> itemData;
virtual ~ListItem() {}
};
//------------------------------------------------------
class SpecialListItem : public ListItem
{
public:
SpecialListItem(const QList<QVariant> &data);
QIcon getIcon() const;
ReadingListModel::TypeSpecialList getType() const;
private:
enum DataIndexes {
Name,
Id
};
};
//------------------------------------------------------
class LabelItem : public ListItem
{
public:
LabelItem(const QList<QVariant> &data);
QIcon getIcon() const;
YACReader::LabelColors colorid() const;
QString name() const;
void setName(const QString & name);
qulonglong getId() const;
private:
enum DataIndexes {
Name,
Color,
Id,
Ordering
};
};
//------------------------------------------------------
class ReadingListItem : public ListItem
{
public:
ReadingListItem(const QList<QVariant> &data, ReadingListItem * parent = 0);
QIcon getIcon() const;
ReadingListItem * parent;
int childCount() const;
int row() const;
ReadingListItem * child(int row);
void appendChild(ReadingListItem *item);
void appendChild(ReadingListItem *item, int pos);
void removeChild(ReadingListItem *item);
qulonglong getId() const;
QString name() const;
void setName(const QString & name);
int getOrdering() const;
void setOrdering(const int ordering);
QList<ReadingListItem*> children();
private:
QList<ReadingListItem*> childItems;
enum DataIndexes {
Name,
Id,
Finished,
Completed,
Ordering
};
};
//------------------------------------------------------
class ReadingListSeparatorItem : public ListItem
{
public:
ReadingListSeparatorItem();
QIcon getIcon() const;
};
#endif // READING_LIST_ITEM_H

View File

@ -0,0 +1,806 @@
#include "reading_list_model.h"
#include "reading_list_item.h"
#include "data_base_management.h"
#include "qnaturalsorting.h"
#include "db_helper.h"
#include "QsLog.h"
#include <typeinfo>
ReadingListModel::ReadingListModel(QObject *parent) :
QAbstractItemModel(parent),rootItem(0)
{
separator1 = new ReadingListSeparatorItem;
separator2 = new ReadingListSeparatorItem;
}
int ReadingListModel::rowCount(const QModelIndex &parent) const
{
if(!parent.isValid()) //TOP
{
int separatorsCount = 2;//labels.isEmpty()?1:2;
return specialLists.count() + labels.count() + rootItem->childCount() + separatorsCount;
}
else
{
ListItem * item = static_cast<ListItem*>(parent.internalPointer());
if(typeid(*item) == typeid(ReadingListItem))
{
ReadingListItem * item = static_cast<ReadingListItem*>(parent.internalPointer());
return item->childCount();
}
}
return 0;
}
int ReadingListModel::columnCount(const QModelIndex &parent) const
{
if(parent.isValid())
{
ListItem * item = static_cast<ListItem*>(parent.internalPointer());
if(typeid(*item) == typeid(ReadingListSeparatorItem))
return 0;
}
return 1;
/*if (parent.isValid())
return static_cast<ListItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();*/
}
QVariant ReadingListModel::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
ListItem * item = static_cast<ListItem*>(index.internalPointer());
if (role == ReadingListModel::TypeListsRole)
{
if(typeid(*item) == typeid(SpecialListItem))
return QVariant(ReadingListModel::SpecialList);
if(typeid(*item) == typeid(LabelItem))
return QVariant(ReadingListModel::Label);
if(typeid(*item) == typeid(ReadingListItem))
return QVariant(ReadingListModel::ReadingList);
if(typeid(*item) == typeid(ReadingListSeparatorItem))
return QVariant(ReadingListModel::Separator);
}
if (role == ReadingListModel::LabelColorRole && typeid(*item) == typeid(LabelItem) )
{
LabelItem * labelItem = static_cast<LabelItem*>(item);
return QVariant(labelItem->colorid());
}
if (role == ReadingListModel::IDRole)
{
QLOG_DEBUG() << "getting role";
return item->getId();
}
if (role == ReadingListModel::SpecialListTypeRole && typeid(*item) == typeid(SpecialListItem))
{
SpecialListItem * specialListItem = static_cast<SpecialListItem*>(item);
return QVariant(specialListItem->getType());
}
if(typeid(*item) == typeid(ReadingListSeparatorItem))
return QVariant();
if (role == Qt::DecorationRole)
{
return QVariant(item->getIcon());
}
if (role != Qt::DisplayRole)
return QVariant();
return item->data(index.column());
}
Qt::ItemFlags ReadingListModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
ListItem * item = static_cast<ListItem*>(index.internalPointer());
if(typeid(*item) == typeid(ReadingListSeparatorItem))
return 0;
if(typeid(*item) == typeid(ReadingListItem) && static_cast<ReadingListItem *>(item)->parent->getId()!=0)
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled; //only sublists are dragable
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
}
QVariant ReadingListModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);
return QVariant();
}
QModelIndex ReadingListModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
if(!parent.isValid())
{
int separatorsCount = 2;//labels.isEmpty()?1:2;
if(rowIsSpecialList(row,parent))
return createIndex(row, column, specialLists.at(row));
if(row == specialLists.count())
return createIndex(row,column,separator1);
if(rowIsLabel(row,parent))
return createIndex(row,column,labels.at(row-specialLists.count()-1));
if(separatorsCount == 2)
if(row == specialLists.count() + labels.count() + 1)
return createIndex(row,column,separator2);
if(rowIsReadingList(row,parent))
return createIndex(row,column,rootItem->child(row - (specialLists.count() + labels.count() + separatorsCount)));
} else //sublist
{
ReadingListItem *parentItem;
if (!parent.isValid())
parentItem = rootItem; //this should be impossible
else
parentItem = static_cast<ReadingListItem*>(parent.internalPointer());
ReadingListItem *childItem = parentItem->child(row);
return createIndex(row,column,childItem);
}
/*FolderItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else*/
return QModelIndex();
}
QModelIndex ReadingListModel::parent(const QModelIndex &index) const
{
if(!index.isValid())
return QModelIndex();
ListItem * item = static_cast<ListItem*>(index.internalPointer());
if(typeid(*item) == typeid(ReadingListItem))
{
ReadingListItem * childItem = static_cast<ReadingListItem*>(index.internalPointer());
ReadingListItem * parent = childItem->parent;
if(parent->getId() != 0)
return createIndex(parent->row()+specialLists.count()+labels.count()+2, 0, parent);
}
return QModelIndex();
}
bool ReadingListModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(action);
QLOG_DEBUG() << "trying to drop into row = " << row << "column column = " << column << "parent" << parent;
if(row == -1)
return false;
if(!parent.isValid()) //top level items
{
if(row == -1) //no list
return false;
if(row == 1) //reading is just an smart list
return false;
if(rowIsSeparator(row,parent))
return false;
}
if(data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat))
return true;
if(rowIsReadingList(row,parent))// TODO avoid droping in a different parent
if(!parent.isValid())
return false;
else
{
QList<QPair<int,int> > sublistsRows;
QByteArray rawData = data->data(YACReader::YACReaderLibrarSubReadingListMimeDataFormat);
QDataStream in(&rawData,QIODevice::ReadOnly);
in >> sublistsRows; //deserialize the list of indentifiers
if(parent.row()!= sublistsRows.at(0).second)
return false;
return data->formats().contains(YACReader::YACReaderLibrarSubReadingListMimeDataFormat);
}
return false;
}
bool ReadingListModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
QLOG_DEBUG() << "drop mimedata into row = " << row << " column = " << column << "parent" << parent;
if(data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat))
return dropComics(data, action, row, column, parent);
if(data->formats().contains(YACReader::YACReaderLibrarSubReadingListMimeDataFormat))
return dropSublist(data, action, row, column, parent);
return false;
}
bool ReadingListModel::dropComics(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
Q_UNUSED(action);
QList<qulonglong> comicIds = YACReader::mimeDataToComicsIds(data);
QLOG_DEBUG() << "dropped : " << comicIds;
QModelIndex dest;
QModelIndex parentDest;
if(row == -1)
{
dest = parent;
}
else
dest = index(row,column,parent);
parentDest = dest.parent();
if(rowIsSpecialList(dest.row(),parentDest)) {
if(dest.row() == 0) //add to favorites
{
QLOG_DEBUG() << "-------addComicsToFavorites : " << comicIds << " to " << dest.data(IDRole).toULongLong();
emit addComicsToFavorites(comicIds);
return true;
}
}
if(rowIsLabel(dest.row(),parentDest)) {
QLOG_DEBUG() << "+++++++++++addComicsToLabel : " << comicIds << " to " << dest.data(IDRole).toULongLong();
emit addComicsToLabel(comicIds, dest.data(IDRole).toULongLong());
return true;
}
if(rowIsReadingList(dest.row(),parentDest)) {
QLOG_DEBUG() << "///////////addComicsToReadingList : " << comicIds << " to " << dest.data(IDRole).toULongLong();
emit addComicsToReadingList(comicIds, dest.data(IDRole).toULongLong());
return true;
}
return false;
}
bool ReadingListModel::dropSublist(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
Q_UNUSED(action);
Q_UNUSED(column);
QList<QPair<int,int> > sublistsRows;
QByteArray rawData = data->data(YACReader::YACReaderLibrarSubReadingListMimeDataFormat);
QDataStream in(&rawData,QIODevice::ReadOnly);
in >> sublistsRows; //deserialize the list of indentifiers
QLOG_DEBUG() << "dropped : " << sublistsRows;
int sourceRow = sublistsRows.at(0).first;
int destRow = row;
QModelIndex destParent = parent;
if(row == -1)
{
QLOG_DEBUG() << "droping inside parent";
destRow = parent.row();
destParent = parent.parent();
}
QLOG_DEBUG() << "move " << sourceRow << "-" << destRow;
if(sourceRow == destRow)
return false;
//beginMoveRows(destParent,sourceRow,sourceRow,destParent,destRow);
ReadingListItem * parentItem = static_cast<ReadingListItem *>(destParent.internalPointer());
ReadingListItem * child = parentItem->child(sourceRow);
parentItem->removeChild(child);
parentItem->appendChild(child,destRow);
reorderingChildren(parentItem->children());
//endMoveRows();
return true;
}
QMimeData *ReadingListModel::mimeData(const QModelIndexList &indexes) const
{
QLOG_DEBUG() << "mimeData requested" << indexes;
if(indexes.length() == 0)
{
QLOG_ERROR() << "mimeData requested: indexes is empty";
return new QMimeData();//TODO what happens if 0 is returned?
}
if(indexes.length() > 1)
QLOG_DEBUG() << "mimeData requested for more than one index, this shouldn't be possible";
QModelIndex modelIndex = indexes.at(0);
QList<QPair<int,int> > rows;
rows << QPair<int,int>(modelIndex.row(),modelIndex.parent().row());
QLOG_DEBUG() << "mimeData requested for row : " << modelIndex.row();
QByteArray data;
QDataStream out(&data,QIODevice::WriteOnly);
out << rows; //serialize the list of identifiers
QMimeData * mimeData = new QMimeData();
mimeData->setData(YACReader::YACReaderLibrarSubReadingListMimeDataFormat, data);
return mimeData;
}
void ReadingListModel::setupReadingListsData(QString path)
{
beginResetModel();
cleanAll();
_databasePath = path;
QSqlDatabase db = DataBaseManagement::loadDatabase(path);
//setup special lists
specialLists = setupSpecialLists(db);
//separator--------------------------------------------
//setup labels
setupLabels(db);
//separator--------------------------------------------
//setup reading list
setupReadingLists(db);
endResetModel();
}
void ReadingListModel::addNewLabel(const QString &name, YACReader::LabelColors color)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
qulonglong id = DBHelper::insertLabel(name, color, db);
int newPos = addLabelIntoList(new LabelItem(QList<QVariant>() << name << YACReader::colorToName(color) << id << color));
beginInsertRows(QModelIndex(),specialLists.count()+1+newPos+1, specialLists.count()+1+newPos+1);
endInsertRows();
QSqlDatabase::removeDatabase(_databasePath);
}
void ReadingListModel::addReadingList(const QString &name)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
beginInsertRows(QModelIndex(), 0, 0); //TODO calculate the right coordinates before inserting
qulonglong id = DBHelper::insertReadingList(name,db);
ReadingListItem * newItem;
rootItem->appendChild(newItem = new ReadingListItem(QList<QVariant>()
<< name
<< id
<< false
<< true
<< 0));
items.insert(id, newItem);
/*int pos = rootItem->children().indexOf(newItem);
pos += specialLists.count()+1+labels.count()+labels.count()>0?1:0;*/
endInsertRows();
QSqlDatabase::removeDatabase(_databasePath);
}
void ReadingListModel::addReadingListAt(const QString &name, const QModelIndex &mi)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
beginInsertRows(mi, 0, 0); //TODO calculate the right coordinates before inserting
ReadingListItem * readingListParent = static_cast<ReadingListItem*>(mi.internalPointer());
qulonglong id = DBHelper::insertReadingSubList(name,mi.data(IDRole).toULongLong(),readingListParent->childCount(),db);
ReadingListItem * newItem;
readingListParent->appendChild(newItem = new ReadingListItem(QList<QVariant>()
<< name
<< id
<< false
<< true
<< readingListParent->childCount()));
items.insert(id, newItem);
/*int pos = readingListParent->children().indexOf(newItem);
pos += specialLists.count()+1+labels.count()+labels.count()>0?1:0;*/
endInsertRows();
QSqlDatabase::removeDatabase(_databasePath);
}
bool ReadingListModel::isEditable(const QModelIndex &mi)
{
if(!mi.isValid())
return false;
ListItem * item = static_cast<ListItem*>(mi.internalPointer());
return typeid(*item) != typeid(SpecialListItem);
}
bool ReadingListModel::isReadingList(const QModelIndex &mi)
{
if(!mi.isValid())
return false;
ListItem * item = static_cast<ListItem*>(mi.internalPointer());
return typeid(*item) == typeid(ReadingListItem);
}
bool ReadingListModel::isReadingSubList(const QModelIndex &mi)
{
if(!mi.isValid())
return false;
ListItem * item = static_cast<ListItem*>(mi.internalPointer());
if(typeid(*item) == typeid(ReadingListItem))
{
ReadingListItem * readingListItem = static_cast<ReadingListItem *>(item);
if(readingListItem->parent == rootItem)
return false;
else
return true;
}
else
return false;
}
QString ReadingListModel::name(const QModelIndex &mi)
{
return data(mi,Qt::DisplayRole).toString();
}
void ReadingListModel::rename(const QModelIndex &mi, const QString &name)
{
if(!isEditable(mi))
return;
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
ListItem * item = static_cast<ListItem*>(mi.internalPointer());
if(typeid(*item) == typeid(ReadingListItem))
{
ReadingListItem * rli = static_cast<ReadingListItem*>(item);
rli->setName(name);
DBHelper::renameList(item->getId(), name, db);
if(rli->parent->getId()!=0)
{
//TODO
//move row depending on the name
}else
emit dataChanged(index(mi.row(), 0), index(mi.row(), 0));
}
else if(typeid(*item) == typeid(LabelItem))
{
LabelItem * li = static_cast<LabelItem*>(item);
li->setName(name);
DBHelper::renameLabel(item->getId(), name, db);
emit dataChanged(index(mi.row(), 0), index(mi.row(), 0));
}
QSqlDatabase::removeDatabase(_databasePath);
}
void ReadingListModel::deleteItem(const QModelIndex &mi)
{
if(isEditable(mi))
{
QLOG_DEBUG() << "parent row :" << mi.parent().data() << "-" << mi.row();
beginRemoveRows(mi.parent(),mi.row(),mi.row());
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
ListItem * item = static_cast<ListItem*>(mi.internalPointer());
if(typeid(*item) == typeid(ReadingListItem))
{
ReadingListItem * rli = static_cast<ReadingListItem*>(item);
QLOG_DEBUG() << "num children : " << rli->parent->childCount();
rli->parent->removeChild(rli);
QLOG_DEBUG() << "num children : " << rli->parent->childCount();
DBHelper::removeListFromDB(item->getId(), db);
if(rli->parent->getId()!=0)
{
reorderingChildren(rli->parent->children());
}
QLOG_DEBUG() << "num children : " << rli->parent->childCount();
}
else if(typeid(*item) == typeid(LabelItem))
{
LabelItem * li = static_cast<LabelItem*>(item);
labels.removeOne(li);
DBHelper::removeLabelFromDB(item->getId(), db);
}
QSqlDatabase::removeDatabase(_databasePath);
endRemoveRows();
}
}
const QList<LabelItem *> ReadingListModel::getLabels()
{
return labels;
}
void ReadingListModel::cleanAll()
{
if(rootItem != 0)
{
delete rootItem;
qDeleteAll(specialLists);
qDeleteAll(labels);
specialLists.clear();
labels.clear();
items.clear();
}
rootItem = 0;
}
void ReadingListModel::setupReadingListsData(QSqlQuery &sqlquery, ReadingListItem *parent)
{
items.insert(parent->getId(),parent);
QSqlRecord record = sqlquery.record();
int name = record.indexOf("name");
int id = record.indexOf("id");
int finished = record.indexOf("finished");
int completed = record.indexOf("completed");
int ordering = record.indexOf("ordering");
int parentId = record.indexOf("parentId");
while (sqlquery.next())
{
ReadingListItem * rli = new ReadingListItem(QList<QVariant>()
<< sqlquery.value(name)
<< sqlquery.value(id)
<< sqlquery.value(finished)
<< sqlquery.value(completed)
<< sqlquery.value(ordering));
ReadingListItem * currentParent;
if(sqlquery.value(parentId).isNull())
currentParent = rootItem;
else
currentParent = items.value(sqlquery.value(parentId).toULongLong());
currentParent->appendChild(rli);
items.insert(rli->getId(),rli);
}
}
QList<SpecialListItem *> ReadingListModel::setupSpecialLists(QSqlDatabase & db)
{
QList<SpecialListItem *> list;
QSqlQuery selectQuery("SELECT * FROM default_reading_list ORDER BY id,name",db);
QSqlRecord record = selectQuery.record();
int name = record.indexOf("name");
int id = record.indexOf("id");
while(selectQuery.next()) {
list << new SpecialListItem(QList<QVariant>()
<< selectQuery.value(name)
<< selectQuery.value(id));
}
//Reading after Favorites, Why? Because I want to :P
list.insert(1,new SpecialListItem(QList<QVariant>() << "Reading" << 0));
return list;
}
void ReadingListModel::setupLabels(QSqlDatabase & db)
{
QSqlQuery selectQuery("SELECT * FROM label ORDER BY ordering,name",db);
QSqlRecord record = selectQuery.record();
int name = record.indexOf("name");
int color = record.indexOf("color");
int id = record.indexOf("id");
int ordering = record.indexOf("ordering");
while(selectQuery.next()) {
addLabelIntoList(new LabelItem(QList<QVariant>()
<< selectQuery.value(name)
<< selectQuery.value(color)
<< selectQuery.value(id)
<< selectQuery.value(ordering)));
}
//TEST
// INSERT INTO label (name, color, ordering) VALUES ("Oh Oh", "red", 1);
// INSERT INTO label (name, color, ordering) VALUES ("lalala", "orange", 2);
// INSERT INTO label (name, color, ordering) VALUES ("we are not sorry", "yellow", 3);
// INSERT INTO label (name, color, ordering) VALUES ("there we go", "green", 4);
// INSERT INTO label (name, color, ordering) VALUES ("oklabunga", "cyan", 5);
// INSERT INTO label (name, color, ordering) VALUES ("hailer mailer", "blue", 6);
// INSERT INTO label (name, color, ordering) VALUES ("lol", "violet", 7);
// INSERT INTO label (name, color, ordering) VALUES ("problems", "purple", 8);
// INSERT INTO label (name, color, ordering) VALUES ("me gussssta", "pink", 9);
// INSERT INTO label (name, color, ordering) VALUES (":D", "white", 10);
// INSERT INTO label (name, color, ordering) VALUES ("ainsss", "light", 11);
// INSERT INTO label (name, color, ordering) VALUES ("put a smile on my face", "dark", 12);
}
void ReadingListModel::setupReadingLists(QSqlDatabase & db)
{
//setup root item
rootItem = new ReadingListItem(QList<QVariant>() << "ROOT" << 0 << true << false);
QSqlQuery selectQuery("select * from reading_list order by parentId IS NULL DESC",db);
//setup reading lists
setupReadingListsData(selectQuery,rootItem);
//TEST
// ReadingListItem * node1;
// rootItem->appendChild(node1 = new ReadingListItem(QList<QVariant>() /*<< 0*/ << "My reading list" << "atr"));
// rootItem->appendChild(new ReadingListItem(QList<QVariant>() /*<< 0*/ << "X timeline" << "atr"));
// node1->appendChild(new ReadingListItem(QList<QVariant>() /*<< 0*/ << "sublist" << "atr",node1));
}
int ReadingListModel::addLabelIntoList(LabelItem *item)
{
if(labels.isEmpty())
labels << item;
else
{
int i = 0;
while (i < labels.count() && (labels.at(i)->colorid() < item->colorid()) )
i++;
if(i < labels.count())
{
if(labels.at(i)->colorid() == item->colorid()) //sort by name
{
while( i < labels.count() && labels.at(i)->colorid() == item->colorid() && naturalSortLessThanCI(labels.at(i)->name(),item->name()))
i++;
}
}
if(i >= labels.count())
{
QLOG_DEBUG() << "insertando label al final " << item->name();
labels << item;
}
else
{
QLOG_DEBUG() << "insertando label en " << i << "-" << item->name();
labels.insert(i,item);
}
return i;
}
return 0;
}
void ReadingListModel::reorderingChildren(QList<ReadingListItem *> children)
{
QList<qulonglong> childrenIds;
int i = 0;
foreach (ReadingListItem * item, children) {
item->setOrdering(i++);
childrenIds << item->getId();
}
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
DBHelper::reasignOrderToSublists(childrenIds, db);
QSqlDatabase::removeDatabase(_databasePath);
}
bool ReadingListModel::rowIsSpecialList(int row, const QModelIndex &parent) const
{
if(parent.isValid())
return false; //by now no sublists in special list
if(row >=0 && row < specialLists.count())
return true;
return false;
}
bool ReadingListModel::rowIsLabel(int row, const QModelIndex &parent) const
{
if(parent.isValid())
return false; //by now no sublists in labels
if(row > specialLists.count() && row <= specialLists.count() + labels.count())
return true;
return false;
}
bool ReadingListModel::rowIsReadingList(int row, const QModelIndex &parent) const
{
if(parent.isValid())
return true; //only lists with sublists
int separatorsCount = labels.isEmpty()?1:2;
if(row >= specialLists.count() + labels.count() + separatorsCount)
return true;
return false;
}
bool ReadingListModel::rowIsSeparator(int row, const QModelIndex &parent) const
{
if(parent.isValid())
return false; //only separators at top level
if(row == specialLists.count())
return true;
int separatorsCount = labels.isEmpty()?1:2;
if(separatorsCount == 2 && row == specialLists.count() + labels.count() + 1)
return true;
return false;
}
ReadingListModelProxy::ReadingListModelProxy(QObject *parent)
:QSortFilterProxyModel(parent)
{
}

View File

@ -0,0 +1,117 @@
#ifndef READING_LIST_MODEL_H
#define READING_LIST_MODEL_H
#include <QAbstractItemModel>
#include <QSortFilterProxyModel>
#include <QModelIndex>
#include <QVariant>
#include <QSqlQuery>
#include <QSqlDatabase>
#include "yacreader_global.h"
class LabelItem;
class SpecialListItem;
class ReadingListItem;
class ReadingListSeparatorItem;
class ReadingListModelProxy : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit ReadingListModelProxy(QObject *parent = 0);
};
class ReadingListModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit ReadingListModel(QObject *parent = 0);
//QAbstractItemModel methods
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
QMimeData *mimeData(const QModelIndexList &indexes) const;
//Convenience methods
void setupReadingListsData(QString path);
void addNewLabel(const QString & name, YACReader::LabelColors color);
void addReadingList(const QString & name);//top level reading list
void addReadingListAt(const QString & name, const QModelIndex & mi);
bool isEditable(const QModelIndex & mi);
bool isReadingList(const QModelIndex & mi);
bool isReadingSubList(const QModelIndex & mi);
QString name(const QModelIndex & mi);
void rename(const QModelIndex & mi, const QString & name);
void deleteItem(const QModelIndex & mi);
const QList<LabelItem *> getLabels();
enum Roles {
TypeListsRole = Qt::UserRole + 1,
IDRole,
LabelColorRole,
SpecialListTypeRole
};
enum TypeList {
SpecialList,
Label,
ReadingList,
Separator
};
enum TypeSpecialList {
Reading,
Favorites
};
signals:
void addComicsToFavorites(const QList<qulonglong> & comicIds);
void addComicsToLabel(const QList<qulonglong> & comicIds, qulonglong labelId);
void addComicsToReadingList(const QList<qulonglong> & comicIds, qulonglong readingListId);
private:
void cleanAll();
void setupReadingListsData(QSqlQuery &sqlquery, ReadingListItem *parent);
QList<SpecialListItem *> setupSpecialLists(QSqlDatabase &db);
void setupLabels(QSqlDatabase &db);
void setupReadingLists(QSqlDatabase &db);
int addLabelIntoList(LabelItem *item);
void reorderingChildren(QList<ReadingListItem *> children);
bool rowIsSpecialList(int row, const QModelIndex & parent = QModelIndex()) const;
bool rowIsLabel(int row, const QModelIndex & parent = QModelIndex()) const;
bool rowIsReadingList(int row, const QModelIndex & parent = QModelIndex()) const;
bool rowIsSeparator(int row, const QModelIndex & parent = QModelIndex()) const;
bool dropComics(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
bool dropSublist(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
//Special lists
QList<SpecialListItem *> specialLists;
//Label
QList<LabelItem *> labels;
//Reading lists
ReadingListItem * rootItem; //
QMap<unsigned long long int, ReadingListItem *> items; //lists relationship
//separators
ReadingListSeparatorItem * separator1;
ReadingListSeparatorItem * separator2;
QString _databasePath;
};
#endif // READING_LIST_MODEL_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,83 @@
#ifndef DB_HELPER_H
#define DB_HELPER_H
class QString;
#include <QMap>
#include <QList>
#include "yacreader_global.h"
class ComicDB;
class Folder;
class LibraryItem;
class QSqlDatabase;
class ComicInfo;
class QSqlRecord;
class QSqlQuery;
class YACReaderLibraries;
class DBHelper
{
public:
//server
static YACReaderLibraries getLibraries();
static QList<LibraryItem *> getFolderSubfoldersFromLibrary(qulonglong libraryId, qulonglong folderId);
static QList<LibraryItem *> getFolderComicsFromLibrary(qulonglong libraryId, qulonglong folderId);
static QList<LibraryItem *> getFolderComicsFromLibrary(qulonglong libraryId, qulonglong folderId, bool sort);
static qulonglong getParentFromComicFolderId(qulonglong libraryId, qulonglong id);
static ComicDB getComicInfo(qulonglong libraryId, qulonglong id);
static QList<ComicDB> getSiblings(qulonglong libraryId, qulonglong parentId);
static QString getFolderName(qulonglong libraryId, qulonglong id);
static QList<QString> getLibrariesNames();
static QString getLibraryName(int id);
//objects management
//deletes
static void removeFromDB(LibraryItem * item, QSqlDatabase & db);
static void removeFromDB(Folder * folder, QSqlDatabase & db);
static void removeFromDB(ComicDB * comic, QSqlDatabase & db);
static void removeLabelFromDB(qulonglong id, QSqlDatabase & db);
static void removeListFromDB(qulonglong id, QSqlDatabase & db);
//logic deletes
static void deleteComicsFromFavorites(const QList<ComicDB> & comicsList, QSqlDatabase & db);
static void deleteComicsFromLabel(const QList<ComicDB> & comicsList, qulonglong labelId, QSqlDatabase & db);
static void deleteComicsFromReadingList(const QList<ComicDB> & comicsList, qulonglong readingListId, QSqlDatabase & db);
//inserts
static qulonglong insert(Folder * folder, QSqlDatabase & db);
static qulonglong insert(ComicDB * comic, QSqlDatabase & db);
static qulonglong insertLabel(const QString & name, YACReader::LabelColors color , QSqlDatabase & db);
static qulonglong insertReadingList(const QString & name, QSqlDatabase & db);
static qulonglong insertReadingSubList(const QString & name, qulonglong parentId, int ordering, QSqlDatabase & db);
static void insertComicsInFavorites(const QList<ComicDB> & comicsList, QSqlDatabase & db);
static void insertComicsInLabel(const QList<ComicDB> & comicsList, qulonglong labelId, QSqlDatabase & db);
static void insertComicsInReadingList(const QList<ComicDB> & comicsList, qulonglong readingListId, QSqlDatabase & db);
//updates
static void update(qulonglong libraryId, ComicInfo & comicInfo);
static void update(ComicDB * comics, QSqlDatabase & db);
static void update(ComicInfo * comicInfo, QSqlDatabase & db);
static void updateRead(ComicInfo * comicInfo, QSqlDatabase & db);
static void update(const Folder & folder, QSqlDatabase & db);
static void updateProgress(qulonglong libraryId,const ComicInfo & comicInfo);
static void updateReadingRemoteProgress(const ComicInfo & comicInfo, QSqlDatabase & db);
static void updateFromRemoteClient(qulonglong libraryId,const ComicInfo & comicInfo);
static void renameLabel(qulonglong id, const QString & name, QSqlDatabase & db);
static void renameList(qulonglong id, const QString & name, QSqlDatabase & db);
static void reasignOrderToSublists(QList<qulonglong> ids, QSqlDatabase & db);
static void reasignOrderToComicsInFavorites(QList<qulonglong> comicIds, QSqlDatabase & db);
static void reasignOrderToComicsInLabel(qulonglong labelId, QList<qulonglong> comicIds, QSqlDatabase & db);
static void reasignOrderToComicsInReadingList(qulonglong readingListId, QList<qulonglong> comicIds, QSqlDatabase & db);
static QList<LibraryItem *> getFoldersFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true);
static QList<ComicDB> getSortedComicsFromParent(qulonglong parentId, QSqlDatabase & db);
static QList<LibraryItem *> getComicsFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true);
//load
static Folder loadFolder(qulonglong id, QSqlDatabase & db);
static Folder loadFolder(const QString & folderName, qulonglong parentId, QSqlDatabase & db);
static ComicDB loadComic(qulonglong id, QSqlDatabase & db);
static ComicDB loadComic(QString cname, QString cpath, QString chash, QSqlDatabase & database);
static ComicInfo loadComicInfo(QString hash, QSqlDatabase & db);
static QList<QString> loadSubfoldersNames(qulonglong folderId, QSqlDatabase & db);
//queries
static bool isFavoriteComic(qulonglong id, QSqlDatabase & db);
};
#endif

View File

@ -0,0 +1,47 @@
#include "empty_container_info.h"
EmptyContainerInfo::EmptyContainerInfo(QWidget *parent) :
QWidget(parent), iconLabel(new QLabel()), titleLabel(new QLabel())
{
#ifdef Q_OS_MAC
backgroundColor = "#FFFFFF";
titleLabel->setStyleSheet("QLabel {color:#888888; font-size:24px;font-family:Arial;font-weight:bold;}");
#else
backgroundColor = "#2A2A2A";
titleLabel->setStyleSheet("QLabel {color:#CCCCCC; font-size:24px;font-family:Arial;font-weight:bold;}");
#endif
iconLabel->setAlignment(Qt::AlignCenter);
titleLabel->setAlignment(Qt::AlignCenter);
}
void EmptyContainerInfo::setPixmap(const QPixmap &pixmap)
{
iconLabel->setPixmap(pixmap);
}
void EmptyContainerInfo::setText(const QString &text)
{
titleLabel->setText(text);
}
QVBoxLayout * EmptyContainerInfo::setUpDefaultLayout(bool addStretch)
{
QVBoxLayout * layout = new QVBoxLayout;
layout->addSpacing(100);
layout->addWidget(iconLabel);
layout->addSpacing(30);
layout->addWidget(titleLabel);
if(addStretch)
layout->addStretch();
setLayout(layout);
return layout;
}
void EmptyContainerInfo::paintEvent(QPaintEvent *)
{
QPainter painter (this);
painter.fillRect(0,0,width(),height(),QColor(backgroundColor));
}

View File

@ -0,0 +1,26 @@
#ifndef EMPTY_CONTAINER_INFO_H
#define EMPTY_CONTAINER_INFO_H
#include <QtWidgets>
class EmptyContainerInfo : public QWidget
{
Q_OBJECT
public:
explicit EmptyContainerInfo(QWidget *parent = 0);
void setPixmap(const QPixmap & pixmap);
void setText(const QString & text);
QVBoxLayout *setUpDefaultLayout(bool addStretch);
signals:
public slots:
protected:
void paintEvent(QPaintEvent *);
QLabel * iconLabel;
QLabel * titleLabel;
QString backgroundColor;
};
#endif // EMPTY_CONTAINER_INFO_H

View File

@ -0,0 +1,194 @@
#include "empty_folder_widget.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QListView>
#include <QPainter>
#include <QStringListModel>
#include "comic.h"
#include "comic_files_manager.h"
#include "QsLog.h"
void testListView(QListView * l)
{
QStringListModel * slm = new QStringListModel(QStringList() << "Lorem ipsum" << "Hailer skualer"<< "Mumbaluba X" << "Finger layden" << "Pacum tactus filer" << "Aposum" << "En" << "Lorem ipsum" << "Hailer skualer" << "Mumbaluba X" << "Finger layden" << "Pacum tactus filer" << "Aposum" << "En" );
l->setModel(slm);
}
class ListviewDelegate : public QStyledItemDelegate
{
public:
ListviewDelegate() : QStyledItemDelegate() {}
virtual ~ListviewDelegate() {}
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
painter->save();
QFontMetrics fm(option.font);
QString text = qvariant_cast<QString>(index.data(Qt::DisplayRole));
QRect textRect = option.rect;
textRect.setLeft(std::max(0, (option.rect.size().width() - fm.width(text)) / 2));
painter->drawText(textRect,text);
painter->restore();
//TODO add mouse hover style ??
}
QSize sizeHint(const QStyleOptionViewItem &option,
const QModelIndex &index ) const
{
QFontMetrics fm(option.font);
QString text = qvariant_cast<QString>(index.data(Qt::DisplayRole));
return QSize(fm.width(text),fm.height());
}
};
EmptyFolderWidget::EmptyFolderWidget(QWidget *parent) :
EmptyContainerInfo(parent),subfoldersModel(new QStringListModel())
{
QVBoxLayout * layout = setUpDefaultLayout(false);
iconLabel->setPixmap(QPixmap(":/images/empty_folder.png"));
titleLabel->setText(tr("Subfolders in this folder"));
foldersView = new QListView();
foldersView->setAttribute(Qt::WA_MacShowFocusRect,false);
foldersView->setItemDelegate(new ListviewDelegate);
#ifdef Q_OS_MAC
foldersView->setStyleSheet("QListView {background-color:transparent; border: none; color:#959595; outline:0; font-size: 18px; show-decoration-selected: 0; margin:0}"
"QListView::item:selected {background-color: #EFEFEF; color:#CCCCCC;}"
"QListView::item:hover {background-color:#F4F4F8; color:#757575; }"
"QScrollBar:vertical { border-radius:3px; background: #FFFFFF; width: 14px; margin: 0 10px 0 0; }"
"QScrollBar::handle:vertical { border: 1px solid #999999; background: #999999; width: 14px; min-height: 20px; border-radius: 2px; }"
"QScrollBar::add-line:vertical { border: none; background: #999999; height: 0px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::sub-line:vertical { border: none; background: #999999; height: 0px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}"
"QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}"
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {background: none; }"
"QScrollBar:horizontal{height:0px;}"
);
#else
foldersView->setStyleSheet("QListView {background-color:transparent; border: none; color:#858585; outline:0; font-size: 18px; font:bold; show-decoration-selected: 0; margin:0}"
"QListView::item:selected {background-color: #212121; color:#CCCCCC;}"
"QListView::item:hover {background-color:#212121; color:#CCCCCC; }"
"QScrollBar:vertical { border: none; background: #212121; width: 14px; margin: 0 10px 0 0; }"
"QScrollBar::handle:vertical { background: #858585; width: 14px; min-height: 20px; }"
"QScrollBar::add-line:vertical { border: none; background: #212121; height: 0px; subcontrol-position: bottom; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::sub-line:vertical { border: none; background: #212121; height: 0px; subcontrol-position: top; subcontrol-origin: margin; margin: 0 3px 0 0;}"
"QScrollBar::up-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-up.png') center top no-repeat;}"
"QScrollBar::down-arrow:vertical {border:none;width: 9px;height: 6px;background: url(':/images/folders_view/line-down.png') center top no-repeat;}"
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {background: none; }"
"QScrollBar:horizontal{height:0px;}"
);
#endif
foldersView->setSizePolicy(QSizePolicy ::Expanding , QSizePolicy ::Expanding );
layout->addSpacing(12);
layout->addWidget(foldersView,1);
layout->addStretch();
layout->setMargin(0);
layout->setSpacing(0);
setContentsMargins(0,0,0,0);
setStyleSheet(QString("QWidget {background:%1}").arg(backgroundColor));
setSizePolicy(QSizePolicy ::Expanding , QSizePolicy ::Expanding );
setAcceptDrops(true);
connect(foldersView,SIGNAL(clicked(QModelIndex)),this,SLOT(onItemClicked(QModelIndex)));
}
void EmptyFolderWidget::setSubfolders(const QModelIndex &mi, const QStringList &foldersNames)
{
parent = mi;
subfoldersModel->setStringList(foldersNames);
foldersView->setModel(subfoldersModel);
if(foldersNames.isEmpty())
{
titleLabel->setText(tr("Empty folder") + QString("<p style='color:rgb(150,150,150);font-size:14px;font-weight:normal;'>%1</p>").arg(tr("Drag and drop folders and comics here")));
}
else
{
titleLabel->setText(tr("Subfolders in this folder"));
}
}
void EmptyFolderWidget::onItemClicked(const QModelIndex &mi)
{
emit subfolderSelected(parent,mi.row());
}
//TODO remove repeated code in drag & drop support....
void EmptyFolderWidget::dragEnterEvent(QDragEnterEvent *event)
{
QList<QUrl> urlList;
if (event->mimeData()->hasUrls() && event->dropAction() == Qt::CopyAction)
{
urlList = event->mimeData()->urls();
QString currentPath;
foreach (QUrl url, urlList)
{
//comics or folders are accepted, folders' content is validate in dropEvent (avoid any lag before droping)
currentPath = url.toLocalFile();
if(Comic::fileIsComic(currentPath) || QFileInfo(currentPath).isDir())
{
event->acceptProposedAction();
return;
}
}
}
}
void EmptyFolderWidget::dropEvent(QDropEvent *event)
{
QLOG_DEBUG() << "drop in emptyfolder" << event->dropAction();
bool validAction = event->dropAction() == Qt::CopyAction; // || event->dropAction() & Qt::MoveAction; TODO move
if(validAction)
{
QList<QPair<QString, QString> > droppedFiles = ComicFilesManager::getDroppedFiles(event->mimeData()->urls());
if(event->dropAction() == Qt::CopyAction)
{
QLOG_DEBUG() << "copy in emptyfolder:" << droppedFiles;
emit copyComicsToCurrentFolder(droppedFiles);
}
else if(event->dropAction() & Qt::MoveAction)
{
QLOG_DEBUG() << "move in emptyfolder:" << droppedFiles;
emit moveComicsToCurrentFolder(droppedFiles);
}
event->acceptProposedAction();
}
}

View File

@ -0,0 +1,36 @@
#ifndef EMPTY_FOLDER_WIDGET_H
#define EMPTY_FOLDER_WIDGET_H
#include "empty_container_info.h"
#include <QtWidgets>
class EmptyFolderWidget : public EmptyContainerInfo
{
Q_OBJECT
public:
explicit EmptyFolderWidget(QWidget *parent = 0);
void setSubfolders(const QModelIndex & mi, const QStringList & foldersNames);
signals:
void subfolderSelected(QModelIndex, int);
//Drops
void copyComicsToCurrentFolder(QList<QPair<QString, QString> >);
void moveComicsToCurrentFolder(QList<QPair<QString, QString> >);
public slots:
void onItemClicked(const QModelIndex & mi);
protected:
QListView * foldersView;
QModelIndex parent;
QStringListModel * subfoldersModel;
QString backgroundColor;
//Drop to import
void dragEnterEvent(QDragEnterEvent *event);
void dropEvent(QDropEvent *event);
};
#endif // EMPTY_FOLDER_WIDGET_H

View File

@ -0,0 +1,21 @@
#include "empty_label_widget.h"
EmptyLabelWidget::EmptyLabelWidget(QWidget *parent) :
EmptyContainerInfo(parent)
{
setUpDefaultLayout(true);
iconLabel->setPixmap(QPixmap(":/images/empty_label.png"));
//titleLabel->setText(tr("This label doesn't contain comics yet") + QString("<p style='color:rgb(150,150,150);font-size:14px;font-weight:normal;'>%1</p>").arg(tr("Drag and drop folders and comics here")));
titleLabel->setText(tr("This label doesn't contain comics yet"));
}
void EmptyLabelWidget::setColor(YACReader::LabelColors color)
{
QPixmap p(":/images/empty_label.png");
QImage img = p.toImage().convertToFormat(QImage::Format_ARGB32);
QColor destColor(YACReader::labelColorToRGBString(color));
YACReader::colorize(img,destColor);
iconLabel->setPixmap(QPixmap::fromImage(img));
}

View File

@ -0,0 +1,22 @@
#ifndef EMPTY_LABEL_WIDGET_H
#define EMPTY_LABEL_WIDGET_H
#include <QtWidgets>
#include "empty_container_info.h"
#include "yacreader_global_gui.h"
class EmptyLabelWidget : public EmptyContainerInfo
{
Q_OBJECT
public:
explicit EmptyLabelWidget(QWidget *parent = 0);
void setColor(YACReader::LabelColors color);
signals:
public slots:
protected:
};
#endif // EMPTY_LABEL_WIDGET_H

View File

@ -0,0 +1,9 @@
#include "empty_reading_list_widget.h"
EmptyReadingListWidget::EmptyReadingListWidget(QWidget *parent)
:EmptyContainerInfo(parent)
{
setUpDefaultLayout(true);
setPixmap(QPixmap(":/images/empty_reading_list"));
setText(tr("This reading list does not contain any comics yet"));
}

View File

@ -0,0 +1,13 @@
#ifndef EMPTY_READING_LIST_WIDGET_H
#define EMPTY_READING_LIST_WIDGET_H
#include <QtWidgets>
#include "empty_container_info.h"
class EmptyReadingListWidget : public EmptyContainerInfo
{
public:
EmptyReadingListWidget(QWidget * parent = 0);
};
#endif // EMPTY_READING_LIST_WIDGET_H

View File

@ -0,0 +1,7 @@
#include "empty_special_list.h"
EmptySpecialListWidget::EmptySpecialListWidget(QWidget *parent)
:EmptyContainerInfo(parent)
{
setUpDefaultLayout(true);
}

View File

@ -0,0 +1,13 @@
#ifndef EMPTY_SPECIAL_LIST_H
#define EMPTY_SPECIAL_LIST_H
#include <QtWidgets>
#include "empty_container_info.h"
class EmptySpecialListWidget : public EmptyContainerInfo
{
public:
EmptySpecialListWidget(QWidget * parent = 0);
};
#endif // EMPTY_SPECIAL_LIST_H

View File

@ -0,0 +1,92 @@
#include "export_comics_info_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QDir>
#include <QMessageBox>
#include "data_base_management.h"
ExportComicsInfoDialog::ExportComicsInfoDialog(QWidget *parent)
: QDialog(parent)
{
textLabel = new QLabel(tr("Output file : "));
path = new QLineEdit;
textLabel->setBuddy(path);
accept = new QPushButton(tr("Create"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(exportComicsInfo()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
connect(cancel,SIGNAL(clicked()),this,SIGNAL(rejected()));
find = new QPushButton(QIcon(":/images/find_folder.png"),"");
connect(find,SIGNAL(clicked()),this,SLOT(findPath()));
QHBoxLayout *libraryLayout = new QHBoxLayout;
libraryLayout->addWidget(textLabel);
libraryLayout->addWidget(path);
libraryLayout->addWidget(find);
libraryLayout->setStretchFactor(find,0); //TODO
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addStretch();
bottomLayout->addWidget(accept);
bottomLayout->addWidget(cancel);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(libraryLayout);
mainLayout->addWidget(progress=new QLabel());
mainLayout->addStretch();
mainLayout->addLayout(bottomLayout);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/exportComicsInfo.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Export comics info"));
}
ExportComicsInfoDialog::~ExportComicsInfoDialog()
{
}
void ExportComicsInfoDialog::findPath()
{
QString s = QFileDialog::getSaveFileName(this,tr("Destination database name"),".","*.ydb");
if(!s.isEmpty())
{
path->setText(s);
accept->setEnabled(true);
}
}
void ExportComicsInfoDialog::exportComicsInfo()
{
QFileInfo f(path->text());
QFileInfo fPath(f.absoluteDir().path());
if(fPath.exists() && fPath.isDir() && fPath.isWritable())
{
DataBaseManagement::exportComicsInfo(source,path->text());
close();
}
else
QMessageBox::critical(NULL,tr("Problem found while writing"),tr("The selected path for the output file does not exist or is not a valid path. Be sure that you have write access to this folder"));
}
void ExportComicsInfoDialog::close()
{
path->clear();
QDialog::close();
}

View File

@ -0,0 +1,35 @@
#ifndef EXPORT_COMICS_INFO_DIALOG_H
#define EXPORT_COMICS_INFO_DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
class ExportComicsInfoDialog : public QDialog
{
Q_OBJECT
public:
ExportComicsInfoDialog(QWidget *parent = 0);
~ExportComicsInfoDialog();
QString source;
public slots:
void findPath();
void exportComicsInfo();
void close();
private:
QLabel * progress;
QLabel * textLabel;
QLineEdit * path;
QPushButton * find;
QPushButton * accept;
QPushButton * cancel;
};
#endif // EXPORT_COMICS_INFO_DIALOG_H

View File

@ -0,0 +1,100 @@
#include "export_library_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QMessageBox>
#include <QDir>
ExportLibraryDialog::ExportLibraryDialog(QWidget * parent)
:QDialog(parent),progressCount(0)
{
textLabel = new QLabel(tr("Output folder : "));
path = new QLineEdit;
textLabel->setBuddy(path);
accept = new QPushButton(tr("Create"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(exportLibrary()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
connect(cancel,SIGNAL(clicked()),this,SIGNAL(rejected()));
find = new QPushButton(QIcon(":/images/find_folder.png"),"");
connect(find,SIGNAL(clicked()),this,SLOT(findPath()));
QHBoxLayout *libraryLayout = new QHBoxLayout;
libraryLayout->addWidget(textLabel);
libraryLayout->addWidget(path);
libraryLayout->addWidget(find);
libraryLayout->setStretchFactor(find,0); //TODO
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addStretch();
bottomLayout->addWidget(accept);
bottomLayout->addWidget(cancel);
progressBar = new QProgressBar(this);
progressBar->setMinimum(0);
progressBar->setMaximum(0);
progressBar->setTextVisible(false);
progressBar->hide();
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(libraryLayout);
mainLayout->addStretch();
mainLayout->addWidget(progressBar);
mainLayout->addLayout(bottomLayout);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/exportLibrary.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Create covers package"));
}
void ExportLibraryDialog::exportLibrary()
{
QFileInfo f(path->text());
if(f.exists() && f.isDir() && f.isWritable())
{
progressBar->show();
accept->setEnabled(false);
emit exportPath(QDir::cleanPath(path->text()));
}
else
QMessageBox::critical(NULL,tr("Problem found while writing"),tr("The selected path for the output file does not exist or is not a valid path. Be sure that you have write access to this folder"));
}
void ExportLibraryDialog::findPath()
{
QString s = QFileDialog::getExistingDirectory(0,tr("Destination directory"),".");
if(!s.isEmpty())
{
path->setText(s);
accept->setEnabled(true);
}
}
void ExportLibraryDialog::close()
{
path->clear();
progressBar->hide();
accept->setEnabled(false);
progressCount=0;
QDialog::close();
}
void ExportLibraryDialog::run()
{
}

View File

@ -0,0 +1,35 @@
#ifndef EXPORT_LIBRARY_DIALOG_H
#define EXPORT_LIBRARY_DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QString>
#include <QThread>
#include <QTimer>
#include <QProgressBar>
class ExportLibraryDialog : public QDialog
{
Q_OBJECT
public:
ExportLibraryDialog(QWidget * parent = 0);
public slots:
void exportLibrary();
void findPath();
void close();
private:
int progressCount;
QProgressBar *progressBar;
QLabel * textLabel;
QLineEdit * path;
QPushButton * find;
QPushButton * accept;
QPushButton * cancel;
void run();
signals:
void exportPath(QString);
};
#endif

View File

@ -0,0 +1,12 @@
<RCC>
<qresource>
<file>../files/about.html</file>
<file>../files/helpYACReaderLibrary.html</file>
</qresource>
<qresource lang="es_ES">
<file alias="/files/about.html">../files/about_es_ES.html</file>
<file alias="/files/helpYACReaderLibrary.html">../files/helpYACReaderLibrary_es_ES.html</file>
</qresource>
</RCC>

Some files were not shown because too many files have changed in this diff Show More