corregida toolbar en macosx

This commit is contained in:
Luis Ángel San Martín
2013-06-17 12:57:00 +02:00
commit ca6347e689
546 changed files with 45296 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,141 @@
######################################################################
# Automatically generated by qmake (2.01a) dom 12. oct 20:47:48 2008
######################################################################
TEMPLATE = app
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
INCLUDEPATH += ../common \
./server \
./db \
../YACReader \
../custom_widgets
DEFINES += SERVER_RELEASE
win32 {
INCLUDEPATH += ../dependencies/poppler/include
LIBS += -L../dependencies/poppler/lib -lpoppler-qt4
QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /GL
QMAKE_LFLAGS_RELEASE += /LTCG
CONFIG -= embed_manifest_exe
}
unix:!macx{
INCLUDEPATH += /usr/include/poppler/qt4
LIBS += -L/usr/lib -lpoppler-qt4
LIBS += -lGLU
}
macx{
#INCLUDEPATH += "/Volumes/Mac OS X Lion/usr/X11/include"
INCLUDEPATH += /usr/local/include/poppler/qt4
LIBS += -L/usr/local/lib -lpoppler-qt4
}
CONFIG += release
CONFIG -= flat
QT += sql network 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/treeitem.h \
./db/treemodel.h \
./db/tablemodel.h \
./db/tableitem.h \
../common/comic_db.h \
../common/folder.h \
../common/library_item.h \
../YACReader/comic.h \
../YACReader/bookmarks.h \
../common/pictureflow.h \
../common/custom_widgets.h \
../common/qnaturalsorting.h \
../common/yacreader_flow_gl.h \
../common/yacreader_global.h \
../common/onstart_flow_selection_dialog.h \
no_libraries_widget.h \
import_widget.h \
yacreader_local_server.h \
yacreader_main_toolbar.h \
comics_remover.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/treeitem.cpp \
./db/treemodel.cpp \
./db/tablemodel.cpp \
./db/tableitem.cpp \
../common/comic_db.cpp \
../common/folder.cpp \
../common/library_item.cpp \
../YACReader/comic.cpp \
../YACReader/bookmarks.cpp \
../common/pictureflow.cpp \
../common/custom_widgets.cpp \
../common/qnaturalsorting.cpp \
../common/yacreader_flow_gl.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
include(./server/server.pri)
include(../custom_widgets/custom_widgets.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
Release:DESTDIR = ../release
Debug:DESTDIR = ../debug

View File

@ -0,0 +1,125 @@
#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/comicFolder.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()));
close();
}
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,221 @@
#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()
{
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)
{
imageFiles.removeAt(cover);
if(imagesLoaded[cover])
numImagesLoaded--;
imagesLoaded.remove(cover);
imagesSetted.remove(cover);
YACReaderFlow::removeSlide(cover);
}
//-----------------------------------------------------------------------------
//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::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,75 @@
#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);
private slots:
void preload();
void updateImageData();
private:
//QString imagePath;
QStringList imageFiles;
QVector<bool> imagesLoaded;
QVector<bool> imagesSetted;
uint 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; };
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,344 @@
#include "comic_flow_widget.h"
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<bool> marks)
{
flow->setMarks(marks);
}
void ComicFlowWidgetSW::setMarkImage(QImage & image)
{
flow->setMarkImage(image);
}
void ComicFlowWidgetSW::markSlide(int index)
{
flow->markSlide(index);
}
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 0:
flow->setFlowType(CoverFlowLike);
return;
case 1:
flow->setFlowType(Strip);
return;
case 2:
flow->setFlowType(StripOverlapped);
return;
}
}
void ComicFlowWidgetSW::remove(int cover)
{
flow->removeSlide(cover);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
///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<bool> marks)
{
flow->setMarks(marks);
}
void ComicFlowWidgetGL::setMarkImage(QImage & image)
{
flow->setMarkImage(image);
}
void ComicFlowWidgetGL::markSlide(int index)
{
flow->markSlide(index);
}
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);
}
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::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,128 @@
#ifndef __COMIC_FLOW_WIDGET_H
#define __COMIC_FLOW_WIDGET_H
#include <QWidget>
#include "pictureflow.h"
#include "comic_flow.h"
#include "yacreader_flow_gl.h"
class ComicFlowWidget : public QWidget
{
Q_OBJECT
public:
ComicFlowWidget(QWidget * paret = 0);
public slots:
virtual void setShowMarks(bool value) = 0;
virtual void setMarks(QVector<bool> marks) = 0;
virtual void setMarkImage(QImage & image) = 0;
virtual void markSlide(int index) = 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;
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<bool> marks);
void setMarkImage(QImage & image);
void markSlide(int index);
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);
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;
};
class ComicFlowWidgetGL : public ComicFlowWidget
{
Q_OBJECT
private:
YACReaderComicFlowGL * flow;
public:
ComicFlowWidgetGL(QWidget * parent = 0);
void setShowMarks(bool value);
void setMarks(QVector<bool> marks);
void setMarkImage(QImage & image);
void markSlide(int index);
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);
//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

View File

@ -0,0 +1,29 @@
#include "comics_remover.h"
#include <QFile>
ComicsRemover::ComicsRemover(QModelIndexList & il, QList<QString> & ps, QObject *parent) :
QThread(parent),indexList(il), paths(ps)
{
}
void ComicsRemover::run()
{
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());
}
}
emit finished();
}

View File

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

View File

@ -0,0 +1,235 @@
#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/comicFolder.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 *middleLayout = new QHBoxLayout;
progressBar = new QProgressBar(this);
progressBar->setMinimum(0);
progressBar->setMaximum(0);
progressBar->setTextVisible(false);
progressBar->hide();
currentFileLabel = new QLabel("");
currentFileLabel->setWordWrap(true);
middleLayout->addWidget(currentFileLabel);
middleLayout->addStretch();
middleLayout->setSizeConstraint(QLayout::SetMaximumSize);
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(middleLayout);
mainLayout->addStretch();
mainLayout->addWidget(progressBar);
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::create()
{
QFileInfo f(path->text());
if(f.exists() && f.isDir() && f.isWritable())
{
progressBar->show();
message->show();
currentFileLabel->setText("Importing : \n\n\n\n\n");
this->adjustSize();
accept->setEnabled(false);
emit(createLibrary(QDir::cleanPath(path->text()),QDir::cleanPath(path->text())+"/.yacreaderlibrary",nameEdit->text()));
close();
}
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::showCurrentFile(QString file)
{
currentFileLabel->setText(tr("Importing : \n") + file);
currentFileLabel->update();
//this->adjustSize();
//is->update();
}
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()
{
progressBar->hide();
path->clear();
nameEdit->clear();
currentFileLabel->setText("");
this->adjustSize();
accept->setEnabled(false);
QDialog::close();
}
void CreateLibraryDialog::setDataAndStart(QString name, QString path)
{
this->path->setText(path);
this->nameEdit->setText(name);
show();
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();
progressBar = new QProgressBar(this);
progressBar->setMinimum(0);
progressBar->setMaximum(0);
progressBar->setTextVisible(false);
mainLayout->addWidget(progressBar);
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,58 @@
#ifndef __CREATE_LIBRARY_DIALOG_H
#define __CREATE_LIBRARY_DIALOG_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;
QLabel * currentFileLabel;
QLineEdit * path;
QLineEdit * nameEdit;
QPushButton * find;
QPushButton * accept;
QPushButton * cancel;
void setupUI();
public slots:
void create();
void findPath();
void showCurrentFile(QString file);
void close();
void setDataAndStart(QString name, QString paht);
void nameSetted(const QString & text);
void pathSetted(const QString & text);
signals:
void createLibrary(QString source, QString target, QString name);
void cancelCreate();
};
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,591 @@
#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,"
"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<61>a e inv<6E>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<61>a e inv<6E>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, FOREIGN KEY(parentId) REFERENCES folder(id) ON DELETE CASCADE)");
success = success && queryFolder.exec();
//queryFolder.finish();
//COMIC INFO (representa la informaci<63>n de un c<>mic, cada c<>mic tendr<64> un id<69>ntificador <20>nico formado por un hash sha1'de los primeros 512kb' + su tama<6D>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<61> 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)");
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();
}
return success;
}
#include <qmessagebox.h>
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<65> 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"
" 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,"
"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,"
":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;
}
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("hash",record,query);
}
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)
{
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;
}
db.close();
QSqlDatabase::removeDatabase(fullPath);
return returnValue;
}
//COMICS_INFO_EXPORTER
ComicsInfoExporter::ComicsInfoExporter()
:QThread()
{
}
void ComicsInfoExporter::exportComicsInfo(QSqlDatabase & source, QSqlDatabase & dest)
{
}
void ComicsInfoExporter::run()
{
}
//COMICS_INFO_IMPORTER
ComicsInfoImporter::ComicsInfoImporter()
:QThread()
{
}
void ComicsInfoImporter::importComicsInfo(QSqlDatabase & source, QSqlDatabase & dest)
{
}
void ComicsInfoImporter::run()
{
}

View File

@ -0,0 +1,59 @@
#ifndef __DATA_BASE_MANAGEMENT_H
#define __DATA_BASE_MANAGEMENT_H
#include <QtCore>
#include <QtSql>
#include <QSqlDatabase>
#include "treemodel.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);
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 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<6E>lida <20> la versi<73>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,47 @@
#include <QStringList>
#include "tableitem.h"
//! [0]
TableItem::TableItem(const QList<QVariant> &data)
{
itemData = data;
}
//! [0]
//! [1]
TableItem::~TableItem()
{
}
//! [1]
//! [5]
int TableItem::columnCount() const
{
return itemData.count();
}
//! [5]
//! [6]
QVariant TableItem::data(int column) const
{
return itemData.value(column);
}
//! [6]
void TableItem::setData(int column,const QVariant & value)
{
itemData[column] = value;
}
//! [8]
int TableItem::row() const
{
return 0;
}
//! [8]

View File

@ -0,0 +1,27 @@
#ifndef TABLEITEM_H
#define TABLEITEM_H
#include <QList>
#include <QVariant>
//! [0]
class TableItem : public QObject
{
Q_OBJECT
public:
TableItem(const QList<QVariant> &data);
~TableItem();
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

View File

@ -0,0 +1,491 @@
#include <QtGui>
#include <QtDebug>
#include "tableitem.h"
#include "tablemodel.h"
#include "data_base_management.h"
#include "qnaturalsorting.h"
#include "comic_db.h"
#include "db_helper.h"
//ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read
#define NUMBER 0
#define TITLE 1
#define FILE_NAME 2
#define NUM_PAGES 3
#define ID 4
#define PARENT_ID 5
#define PATH 6
#define HASH 7
#define READ 8
#define IS_BIS 9
TableModel::TableModel(QObject *parent)
: QAbstractItemModel(parent)
{
connect(this,SIGNAL(beforeReset()),this,SIGNAL(modelAboutToBeReset()));
connect(this,SIGNAL(reset()),this,SIGNAL(modelReset()));
}
//! [0]
TableModel::TableModel( QSqlQuery &sqlquery, QObject *parent)
: QAbstractItemModel(parent)
{
setupModelData(sqlquery);
}
//! [0]
//! [1]
TableModel::~TableModel()
{
qDeleteAll(_data);
}
//! [1]
//! [2]
int TableModel::columnCount(const QModelIndex &parent) const
{
if(_data.isEmpty())
return 0;
return _data.first()->columnCount();
}
//! [2]
//! [3]
QVariant TableModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DecorationRole)
{
return QVariant();
}
if (role != Qt::DisplayRole)
return QVariant();
TableItem *item = static_cast<TableItem*>(index.internalPointer());
if(index.column() == HASH)
return QString::number(item->data(index.column()).toString().right(item->data(index.column()).toString().length()-40).toInt()/1024.0/1024.0,'f',2)+"Mb";
if(index.column() == READ)
return item->data(index.column()).toBool()?QVariant(tr("yes")):QVariant(tr("no"));
return item->data(index.column());
}
//! [3]
//! [4]
Qt::ItemFlags TableModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
//! [4]
//! [5]
QVariant TableModel::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 0:
return QVariant(QString("#"));
case 1:
return QVariant(QString(tr("Title")));
case 2:
return QVariant(QString(tr("File Name")));
case 3:
return QVariant(QString(tr("Pages")));
case 7:
return QVariant(QString(tr("Size")));
case 8:
return QVariant(QString(tr("Read")));
}
}
if(orientation == Qt::Vertical && role == Qt::DecorationRole)
{
QString fileName = _data.value(section)->data(FILE_NAME).toString();
QFileInfo fi(fileName);
QString ext = fi.suffix();
if (ext.compare("cbr",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/comicRar.png"));
else if (ext.compare("cbz",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/comicZip.png"));
else if(ext.compare("pdf",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/pdf.png"));
else if (ext.compare("tar",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/tar.png"));
else if(ext.compare("zip",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/zip.png"));
else if(ext.compare("rar",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/rar.png"));
else if (ext.compare("7z",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/7z.png"));
else if (ext.compare("cb7",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/comic7z.png"));
else if (ext.compare("cb7",Qt::CaseInsensitive) == 0)
return QVariant(QIcon(":/images/comicTar.png"));
}
return QVariant();
}
//! [5]
//! [6]
QModelIndex TableModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
return createIndex(row, column, _data.at(row));
}
//! [6]
//! [7]
QModelIndex TableModel::parent(const QModelIndex &index) const
{
return QModelIndex();
}
//! [7]
//! [8]
int TableModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
return _data.count();
return 0;
}
//! [8]
QStringList TableModel::getPaths(const QString & _source)
{
QStringList paths;
QString source = _source + "/.yacreaderlibrary/covers/";
QList<TableItem *>::ConstIterator itr;
for(itr = _data.constBegin();itr != _data.constEnd();itr++)
{
QString hash = (*itr)->data(HASH).toString();
paths << source+ hash +".jpg";
}
return paths;
}
void TableModel::setupModelData(unsigned long long int folderId,const QString & databasePath)
{
//QFile f(QCoreApplication::applicationDirPath()+"/performance.txt");
//f.open(QIODevice::Append);
beginResetModel();
//QElapsedTimer timer;
//timer.start();
qDeleteAll(_data);
_data.clear();
//QTextStream txtS(&f);
//txtS << "TABLEMODEL: Tiempo de borrado: " << timer.elapsed() << "ms\r\n";
_databasePath = databasePath;
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
{
//crear la consulta
//timer.restart();
QSqlQuery selectQuery(db); //TODO check
selectQuery.prepare("select ci.number,ci.title,c.fileName,ci.numPages,c.id,c.parentId,c.path,ci.hash,ci.read,ci.isBis from comic c inner join comic_info ci on (c.comicInfoId = ci.id) where c.parentId = :parentId");
selectQuery.bindValue(":parentId", folderId);
selectQuery.exec();
//txtS << "TABLEMODEL: Tiempo de consulta: " << timer.elapsed() << "ms\r\n";
//timer.restart();
setupModelData(selectQuery);
//txtS << "TABLEMODEL: Tiempo de creaci<63>n del modelo: " << timer.elapsed() << "ms\r\n";
//selectQuery.finish();
}
db.close();
QSqlDatabase::removeDatabase(_databasePath);
endResetModel();
//f.close();
}
QString TableModel::getComicPath(QModelIndex mi)
{
if(mi.isValid())
return _data.at(mi.row())->data(PATH).toString();
return "";
}
#define NUMBER_MAX 99999999
void TableModel::setupModelData(QSqlQuery &sqlquery)
{
TableItem * currentItem;
while (sqlquery.next())
{
QList<QVariant> data;
QSqlRecord record = sqlquery.record();
for(int i=0;i<record.count();i++)
data << record.value(i);
currentItem = new TableItem(data);
bool lessThan = false;
if(_data.isEmpty())
_data.append(currentItem);
else
{
TableItem * last = _data.back();
QString nameLast = last->data(FILE_NAME).toString();
QString nameCurrent = currentItem->data(FILE_NAME).toString();
int numberLast,numberCurrent;
numberLast = numberCurrent = NUMBER_MAX; //TODO change by std limit
if(!last->data(NUMBER).isNull())
numberLast = last->data(NUMBER).toInt();
if(!currentItem->data(NUMBER).isNull())
numberCurrent = currentItem->data(NUMBER).toInt();
QList<TableItem *>::iterator i;
i = _data.end();
i--;
if(numberCurrent != NUMBER_MAX)
{
while ((lessThan =numberCurrent < numberLast) && i != _data.begin())
{
i--;
numberLast = NUMBER_MAX; //TODO change by std limit
if(!(*i)->data(NUMBER).isNull())
numberLast = (*i)->data(NUMBER).toInt();
}
}
else
{
while ((lessThan = naturalSortLessThanCI(nameCurrent,nameLast)) && i != _data.begin() && numberLast == 99999999)
{
i--;
nameLast = (*i)->data(FILE_NAME).toString();
numberLast = NUMBER_MAX; //TODO change by std limit
if(!(*i)->data(NUMBER).isNull())
numberLast = (*i)->data(NUMBER).toInt();
}
}
if(!lessThan) //si se ha encontrado un elemento menor que current, se inserta justo despu<70>s
{
if(numberCurrent != NUMBER_MAX)
{
if(numberCurrent == numberLast)
if(currentItem->data(IS_BIS).toBool())
{
_data.insert(++i,currentItem);
}
else
_data.insert(i,currentItem);
else
_data.insert(++i,currentItem);
}
else
_data.insert(++i,currentItem);
}
else
{
_data.insert(i,currentItem);
}
}
}
}
ComicDB TableModel::getComic(const QModelIndex & mi)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
ComicDB c = DBHelper::loadComic(_data.at(mi.row())->data(ID).toULongLong(),db);
db.close();
QSqlDatabase::removeDatabase(_databasePath);
return c;
}
ComicDB TableModel::_getComic(const QModelIndex & mi)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
ComicDB c = DBHelper::loadComic(_data.at(mi.row())->data(ID).toULongLong(),db);
db.close();
QSqlDatabase::removeDatabase(_databasePath);
return c;
}
QVector<bool> TableModel::getReadList()
{
int numComics = _data.count();
QVector<bool> readList(numComics);
for(int i=0;i<numComics;i++)
{
readList[i] = _data.value(i)->data(READ).toBool();
}
return readList;
}
QVector<bool> TableModel::setAllComicsRead(bool read)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
int numComics = _data.count();
QVector<bool> readList(numComics);
for(int i=0;i<numComics;i++)
{
readList[i] = read;
_data.value(i)->setData(READ,QVariant(read));
ComicDB c = DBHelper::loadComic(_data.value(i)->data(ID).toULongLong(),db);
c.info.read = read;
DBHelper::update(&(c.info),db);
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
emit dataChanged(index(0,READ),index(numComics-1,READ));
return readList;
}
QList<ComicDB> TableModel::getComics(QList<QModelIndex> list)
{
QList<ComicDB> comics;
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
QList<QModelIndex>::const_iterator itr;
for(itr = list.constBegin(); itr!= list.constEnd();itr++)
{
comics.append(_getComic(*itr));
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
return comics;
}
QVector<bool> TableModel::setComicsRead(QList<QModelIndex> list,bool read)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
foreach (QModelIndex mi, list)
{
_data.value(mi.row())->setData(READ, QVariant(read));
ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ID).toULongLong(),db);
c.info.read = read;
DBHelper::update(&(c.info),db);
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
emit dataChanged(index(list.first().row(),READ),index(list.last().row(),READ));
return getReadList();
}
qint64 TableModel::asignNumbers(QList<QModelIndex> list,int startingNumber)
{
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
db.transaction();
qint64 idFirst = _data.value(list[0].row())->data(ID).toULongLong();
int i = 0;
foreach (QModelIndex mi, list)
{
ComicDB c = DBHelper::loadComic(_data.value(mi.row())->data(ID).toULongLong(),db);
c.info.setNumber(startingNumber+i);
c.info.edited = true;
DBHelper::update(&(c.info),db);
i++;
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
//emit dataChanged(index(list.first().row(),READ),index(list.last().row(),READ));
return idFirst;
}
QModelIndex TableModel::getIndexFromId(quint64 id)
{
QList<TableItem *>::ConstIterator itr;
int i=0;
for(itr = _data.constBegin();itr != _data.constEnd();itr++)
{
if((*itr)->data(ID).toULongLong() == id)
break;
i++;
}
return index(i,0);
}
void TableModel::startTransaction()
{
dbTransaction = DataBaseManagement::loadDatabase(_databasePath);
dbTransaction.transaction();
}
void TableModel::finishTransaction()
{
dbTransaction.commit();
dbTransaction.close();
QSqlDatabase::removeDatabase(_databasePath);
}
void TableModel::removeInTransaction(int row)
{
ComicDB c = DBHelper::loadComic(_data.at(row)->data(ID).toULongLong(),dbTransaction);
DBHelper::removeFromDB(&c,dbTransaction);
beginRemoveRows(QModelIndex(),row,row);
removeRow(row);
delete _data.at(row);
_data.removeAt(row);
endRemoveRows();
}
void TableModel::remove(ComicDB * comic, int row)
{
beginRemoveRows(QModelIndex(),row,row);
QSqlDatabase db = DataBaseManagement::loadDatabase(_databasePath);
DBHelper::removeFromDB(comic,db);
removeRow(row);
delete _data.at(row);
_data.removeAt(row);
db.close();
QSqlDatabase::removeDatabase(_databasePath);
endRemoveRows();
}
ComicDB TableModel::getComic(int row)
{
return getComic(index(row,0));
}
void TableModel::remove(int row)
{
removeInTransaction(row);
}

View File

@ -0,0 +1,73 @@
#ifndef TABLEMODEL_H
#define TABLEMODEL_H
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QSqlQuery>
#include <QSqlDatabase>
class ComicDB;
class TableItem;
//! [0]
class TableModel : public QAbstractItemModel
{
Q_OBJECT
public:
TableModel(QObject *parent = 0);
TableModel( QSqlQuery &sqlquery, QObject *parent = 0);
~TableModel();
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;
void setupModelData(unsigned long long int parentFolder,const QString & databasePath);
//M<>todos de conveniencia
QStringList getPaths(const QString & _source);
QString getComicPath(QModelIndex mi);
ComicDB getComic(const QModelIndex & mi); //--> para la edici<63>n
ComicDB getComic(int row);
QVector<bool> getReadList();
QVector<bool> setAllComicsRead(bool read);
QList<ComicDB> getComics(QList<QModelIndex> list); //--> recupera la informaci<63>n com<6F>n a los comics seleccionados
QModelIndex getIndexFromId(quint64 id);
//setcomicInfo(QModelIndex & mi); --> inserta en la base datos
//setComicInfoForAllComics(); --> inserta la informaci<63>n com<6F>n a todos los c<>mics de una sola vez.
//setComicInfoForSelectedComis(QList<QModelIndex> list); -->inserta la informaci<63>n com<6F>n para los comics seleccionados
QVector<bool> setComicsRead(QList<QModelIndex> list,bool read);
qint64 asignNumbers(QList<QModelIndex> list,int startingNumber);
void remove(ComicDB * comic, int row);
void removeInTransaction(int row);
public slots:
void remove(int row);
void startTransaction();
void finishTransaction();
private:
void setupModelData( QSqlQuery &sqlquery);
ComicDB _getComic(const QModelIndex & mi);
QList<TableItem *> _data;
QString _databasePath;
QSqlDatabase dbTransaction;
signals:
void beforeReset();
void reset();
};
//! [0]
#endif

View File

@ -0,0 +1,147 @@
/****************************************************************************
**
** 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$
**
****************************************************************************/
/*
treeitem.cpp
A container for items of data supplied by the simple tree model.
*/
#include <QStringList>
#include "treeitem.h"
#include "qnaturalsorting.h"
//! [0]
TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
{
parentItem = parent;
itemData = data;
}
//! [0]
//! [1]
TreeItem::~TreeItem()
{
qDeleteAll(childItems);
}
//! [1]
//! [2]
void TreeItem::appendChild(TreeItem *item)
{
item->parentItem = this;
//TODO insertar odernado
if(childItems.isEmpty())
childItems.append(item);
else
{
TreeItem * 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<TreeItem *>::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);
}
//! [2]
//! [3]
TreeItem *TreeItem::child(int row)
{
return childItems.value(row);
}
//! [3]
//! [4]
int TreeItem::childCount() const
{
return childItems.count();
}
//! [4]
//! [5]
int TreeItem::columnCount() const
{
return itemData.count();
}
//! [5]
//! [6]
QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}
//! [6]
//! [7]
TreeItem *TreeItem::parent()
{
return parentItem;
}
//! [7]
//! [8]
int TreeItem::row() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
//! [8]
QList<QVariant> TreeItem::getData() const
{
return itemData;
}

View File

@ -0,0 +1,78 @@
/****************************************************************************
**
** 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>
//! [0]
class TreeItem
{
public:
TreeItem(const QList<QVariant> &data, TreeItem *parent = 0);
~TreeItem();
void appendChild(TreeItem *child);
TreeItem *child(int row);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
QList<QVariant> getData() const;
int row() const;
TreeItem *parent();
TreeItem *parentItem;
unsigned long long int id;
QList<QString> comicNames;
TreeItem * originalItem;
private:
QList<TreeItem*> childItems;
QList<QVariant> itemData;
};
//! [0]
#endif

View File

@ -0,0 +1,417 @@
/****************************************************************************
**
** 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 "treeitem.h"
#include "treemodel.h"
#include "data_base_management.h"
#define ROOT 1
TreeModel::TreeModel(QObject *parent)
: QAbstractItemModel(parent),rootItem(0),rootBeforeFilter(0),filterEnabled(false),includeComics(false)
{
connect(this,SIGNAL(beforeReset()),this,SIGNAL(modelAboutToBeReset()));
connect(this,SIGNAL(reset()),this,SIGNAL(modelReset()));
}
//! [0]
TreeModel::TreeModel( QSqlQuery &sqlquery, QObject *parent)
: QAbstractItemModel(parent),rootItem(0),rootBeforeFilter(0),filterEnabled(false),includeComics(false)
{
//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 TreeItem(rootData);
rootItem->id = ROOT;
rootItem->parentItem = 0;
setupModelData(sqlquery, rootItem);
//sqlquery.finish();
}
//! [0]
//! [1]
TreeModel::~TreeModel()
{
if(rootItem != 0)
delete rootItem;
}
//! [1]
//! [2]
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}
//! [2]
//! [3]
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (role == Qt::DecorationRole)
#ifdef Q_OS_MAC
return QVariant(QIcon(":/images/folder_macosx.png"));
#else
return QVariant(QIcon(":/images/folder.png"));
#endif
if (role != Qt::DisplayRole)
return QVariant();
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item->data(index.column());
}
//! [3]
//! [4]
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
//! [4]
//! [5]
QVariant TreeModel::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 TreeModel::index(int row, int column, const QModelIndex &parent)
const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
//! [6]
//! [7]
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
TreeItem *parentItem = childItem->parent();
if (parentItem == rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
//! [7]
QModelIndex TreeModel::indexFromItem(TreeItem * 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 TreeModel::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeItem*>(parent.internalPointer());
return parentItem->childCount();
}
//! [8]
void TreeModel::setupModelData(QString path)
{
beginResetModel();
if(rootItem != 0)
delete rootItem; //TODO comprobar que se libera bien la memoria
filterEnabled = false;
rootItem = 0;
rootBeforeFilter = 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 TreeItem(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 TreeModel::setupModelData(QSqlQuery &sqlquery, TreeItem *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);
while (sqlquery.next()) {
QList<QVariant> data;
QSqlRecord record = sqlquery.record();
data << record.value("name").toString();
data << record.value("path").toString();
TreeItem * item = new TreeItem(data);
item->id = record.value("id").toULongLong();
//la inserci<63>n de hijos se hace de forma ordenada
items.value(record.value("parentId").toULongLong())->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 TreeModel::setupFilteredModelData()
{
beginResetModel();
//TODO hay que liberar memoria de anteriores filtrados
//inicializar el nodo ra<72>z
if(rootBeforeFilter == 0)
rootBeforeFilter = rootItem;
else
delete rootItem;//los resultados de la b<>squeda anterior deben ser borrados
QList<QVariant> rootData;
rootData << "root"; //id 1, padre 1, title "root" (el id, y el id del padre van a ir en la clase TreeItem)
rootItem = new TreeItem(rootData);
rootItem->id = ROOT;
rootItem->parentItem = 0;
//cargar la base de datos
QSqlDatabase db = DataBaseManagement::loadDatabase(_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
{
selectQuery.prepare("SELECT DISTINCT f.id, f.parentId, f.name, f.path FROM folder f INNER JOIN comic c ON (f.id = c.parentId) WHERE f.id <> 1 AND ((UPPER(c.fileName) like UPPER(:filter)) OR (UPPER(f.name) like UPPER(:filter2))) ORDER BY f.parentId,f.name");
selectQuery.bindValue(":filter", "%%"+filter+"%%");
selectQuery.bindValue(":filter2", "%%"+filter+"%%");
}
selectQuery.exec();
setupFilteredModelData(selectQuery,rootItem);
}
//selectQuery.finish();
db.close();
QSqlDatabase::removeDatabase(_databasePath);
endResetModel();
}
void TreeModel::setupFilteredModelData(QSqlQuery &sqlquery, TreeItem *parent)
{
//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);
while (sqlquery.next()) { //se procesan todos los folders que cumplen con el filtro
//datos de la base de datos
QList<QVariant> data;
QSqlRecord record = sqlquery.record();
data << record.value("name").toString();
data << record.value("path").toString();
TreeItem * item = new TreeItem(data);
item->id = sqlquery.value(0).toULongLong();
//id del padre
quint64 parentId = record.value("parentId").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 = 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
TreeItem * parentItem = items.value(parentId);
//se debe crear un nuevo nodo (para no compartir los hijos con el nodo original)
TreeItem * newparentItem = new TreeItem(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);
}
}
}
QString TreeModel::getDatabase()
{
return _databasePath;
}
QString TreeModel::getFolderPath(const QModelIndex &folder)
{
return static_cast<TreeItem*>(folder.internalPointer())->data(1).toString();
}
void TreeModel::setFilter(QString filter, bool includeComics)
{
this->filter = filter;
this->includeComics = includeComics;
filterEnabled = true;
setupFilteredModelData();
}
void TreeModel::resetFilter()
{
beginResetModel();
filter = "";
includeComics = false;
//TODO hay que liberar la memoria reservada para el filtrado
//items.clear();
filteredItems.clear();
TreeItem * root = rootItem;
rootItem = rootBeforeFilter; //TODO si no se aplica el filtro previamente, esto invalidar<61>a en modelo
if(root !=0)
delete root;
rootBeforeFilter = 0;
filterEnabled = false;
endResetModel();
}

View File

@ -0,0 +1,106 @@
/****************************************************************************
**
** 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 <QModelIndex>
#include <QVariant>
#include <QSqlQuery>
#include <QSqlDatabase>
class TreeItem;
//! [0]
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
TreeModel(QObject *parent = 0);
TreeModel( QSqlQuery &sqlquery, QObject *parent = 0);
~TreeModel();
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;
QModelIndex indexFromItem(TreeItem * item, int column);
/*QModelIndex _indexFromItem(TreeItem * item, int column);
int column;*/
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
void setupModelData(QString path);
QString getDatabase();
//M<>todos de conveniencia
QString getFolderPath(const QModelIndex &folder);
void setFilter(QString filter, bool includeComics);
void resetFilter();
bool isFilterEnabled(){return filterEnabled;};
private:
void setupModelData( QSqlQuery &sqlquery, TreeItem *parent);
void setupFilteredModelData( QSqlQuery &sqlquery, TreeItem *parent);
void setupFilteredModelData();
TreeItem *rootItem; //el <20>rbol
QMap<unsigned long long int, TreeItem *> items; //relaci<63>n entre folders
TreeItem *rootBeforeFilter;
QMap<unsigned long long int, TreeItem *> filteredItems; //relaci<63>n entre folders
QString _databasePath;
bool includeComics;
QString filter;
bool filterEnabled;
signals:
void beforeReset();
void reset();
};
//! [0]
#endif

View File

@ -0,0 +1,564 @@
#include "db_helper.h"
#include <QMap>
#include <QString>
#include <QList>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
#include <QCoreApplication>
#include <QTextStream>
#include <QSqlDatabase>
#include <QSqlRecord>
#include <QSqlQuery>
#include "library_item.h"
#include "comic_db.h"
#include "data_base_management.h"
#include "folder.h"
//server
//TODO optimizar, evitar que se tenga que leer en cada petición el archivo
//conservar un QDateTime stático que compruebe si libraries.yacr ha sido modificado
//libraries debe ser una variable estática
static QDateTime lastModified;
static QMap<QString,QString> libraries;
QMap<QString,QString> DBHelper::getLibraries()
{
QFileInfo fi(QCoreApplication::applicationDirPath()+"/libraries.yacr");
if(fi.lastModified() == lastModified)
return libraries;
lastModified = fi.lastModified();
libraries.clear();
QFile f(QCoreApplication::applicationDirPath()+"/libraries.yacr");
f.open(QIODevice::ReadOnly);
QTextStream txtS(&f);
QString content = txtS.readAll();
QStringList lines = content.split('\n');
QString line,name;
int i=0;
foreach(line,lines)
{
if((i%2)==0)
{
name = line;
}
else
{
//sólo se agregan las bibliotecas realmente disponibles
QSqlDatabase db = DataBaseManagement::loadDatabase(line.trimmed()+"/.yacreaderlibrary");
if(db.isValid())
libraries.insert(name.trimmed(),line.trimmed());
db.close();
}
i++;
}
f.close();
return libraries;
}
QList<LibraryItem *> DBHelper::getFolderContentFromLibrary(const QString & libraryName, qulonglong folderId)
{
QString libraryPath = DBHelper::getLibraries().value(libraryName);
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary");
QList<LibraryItem *> list = DBHelper::getFoldersFromParent(folderId,db,false);
db.close();
QSqlDatabase::removeDatabase(libraryPath);
return list;
}
QList<LibraryItem *> DBHelper::getFolderComicsFromLibrary(const QString & libraryName, qulonglong folderId)
{
QString libraryPath = DBHelper::getLibraries().value(libraryName);
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary");
QList<LibraryItem *> list = DBHelper::getComicsFromParent(folderId,db,false);
db.close();
QSqlDatabase::removeDatabase(libraryPath);
return list;
}
qulonglong DBHelper::getParentFromComicFolderId(const QString & libraryName, qulonglong id)
{
QString libraryPath = DBHelper::getLibraries().value(libraryName);
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary");
Folder f = DBHelper::loadFolder(id,db);
db.close();
QSqlDatabase::removeDatabase(libraryPath);
return f.parentId;
}
ComicDB DBHelper::getComicInfo(const QString & libraryName, qulonglong id)
{
QString libraryPath = DBHelper::getLibraries().value(libraryName);
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary");
ComicDB comic = DBHelper::loadComic(id,db);
db.close();
QSqlDatabase::removeDatabase(libraryPath);
return comic;
}
QString DBHelper::getFolderName(const QString & libraryName, qulonglong id)
{
QString libraryPath = DBHelper::getLibraries().value(libraryName);
QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary");
QString name="";
{
QSqlQuery selectQuery(db); //TODO check
selectQuery.prepare("SELECT name FROM folder WHERE id = :id");
selectQuery.bindValue(":id", id);
selectQuery.exec();
if(selectQuery.next())
{
QSqlRecord record = selectQuery.record();
name = record.value(0).toString();
}
}
db.close();
QSqlDatabase::removeDatabase(libraryPath);
return name;
}
//objects management
//deletes
void DBHelper::removeFromDB(LibraryItem * item, QSqlDatabase & db)
{
if(item->isDir())
DBHelper::removeFromDB(dynamic_cast<Folder *>(item),db);
else
DBHelper::removeFromDB(dynamic_cast<ComicDB *>(item),db);
}
void DBHelper::removeFromDB(Folder * folder, QSqlDatabase & db)
{
QSqlQuery query(db);
query.prepare("DELETE FROM folder WHERE id = :id");
query.bindValue(":id", folder->id);
query.exec();
}
void DBHelper::removeFromDB(ComicDB * comic, QSqlDatabase & db)
{
QSqlQuery query(db);
query.prepare("DELETE FROM comic WHERE id = :id");
query.bindValue(":id", comic->id);
query.exec();
}
//updates
void DBHelper::update(ComicDB * comic, QSqlDatabase & db)
{
//do nothing
}
void DBHelper::update(ComicInfo * comicInfo, QSqlDatabase & db)
{
QSqlQuery updateComicInfo(db);
updateComicInfo.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,"
"read = :read,"
"edited = :edited"
" WHERE id = :id ");
bindField(":title",comicInfo->title,updateComicInfo);
bindField(":coverPage",comicInfo->coverPage,updateComicInfo);
bindField(":numPages",comicInfo->numPages,updateComicInfo);
bindField(":number",comicInfo->number,updateComicInfo);
bindField(":isBis",comicInfo->isBis,updateComicInfo);
bindField(":count",comicInfo->count,updateComicInfo);
bindField(":volume",comicInfo->volume,updateComicInfo);
bindField(":storyArc",comicInfo->storyArc,updateComicInfo);
bindField(":arcNumber",comicInfo->arcNumber,updateComicInfo);
bindField(":arcCount",comicInfo->arcCount,updateComicInfo);
bindField(":genere",comicInfo->genere,updateComicInfo);
bindField(":writer",comicInfo->writer,updateComicInfo);
bindField(":penciller",comicInfo->penciller,updateComicInfo);
bindField(":inker",comicInfo->inker,updateComicInfo);
bindField(":colorist",comicInfo->colorist,updateComicInfo);
bindField(":letterer",comicInfo->letterer,updateComicInfo);
bindField(":coverArtist",comicInfo->coverArtist,updateComicInfo);
bindField(":date",comicInfo->date,updateComicInfo);
bindField(":publisher",comicInfo->publisher,updateComicInfo);
bindField(":format",comicInfo->format,updateComicInfo);
bindField(":color",comicInfo->color,updateComicInfo);
bindField(":ageRating",comicInfo->ageRating,updateComicInfo);
bindField(":synopsis",comicInfo->synopsis,updateComicInfo);
bindField(":characters",comicInfo->characters,updateComicInfo);
bindField(":notes",comicInfo->notes,updateComicInfo);
updateComicInfo.bindValue(":read", comicInfo->read?1:0);
updateComicInfo.bindValue(":id", comicInfo->id);
updateComicInfo.bindValue(":edited", comicInfo->edited?1:0);
updateComicInfo.exec();
}
void DBHelper::updateRead(ComicInfo * comicInfo, QSqlDatabase & db)
{
QSqlQuery findComicInfo(db);
findComicInfo.prepare("UPDATE comic_info SET "
"read = :read"
" WHERE id = :id ");
findComicInfo.bindValue(":read", comicInfo->read?1:0);
findComicInfo.bindValue(":id", comicInfo->id);
findComicInfo.exec();
}
//inserts
qulonglong DBHelper::insert(Folder * folder, QSqlDatabase & db)
{
QSqlQuery query(db);
query.prepare("INSERT INTO folder (parentId, name, path) "
"VALUES (:parentId, :name, :path)");
query.bindValue(":parentId", folder->parentId);
query.bindValue(":name", folder->name);
query.bindValue(":path", folder->path);
query.exec();
return query.lastInsertId().toULongLong();
}
qulonglong DBHelper::insert(ComicDB * comic, QSqlDatabase & db)
{
if(!comic->info.existOnDb)
{
QSqlQuery comicInfoInsert(db);
comicInfoInsert.prepare("INSERT INTO comic_info (hash,numPages) "
"VALUES (:hash,:numPages)");
comicInfoInsert.bindValue(":hash", comic->info.hash);
comicInfoInsert.bindValue(":numPages", *(comic->info.numPages));
comicInfoInsert.exec();
comic->info.id =comicInfoInsert.lastInsertId().toULongLong();
comic->_hasCover = false;
}
else
comic->_hasCover = true;
QSqlQuery query(db);
query.prepare("INSERT INTO comic (parentId, comicInfoId, fileName, path) "
"VALUES (:parentId,:comicInfoId,:name, :path)");
query.bindValue(":parentId", comic->parentId);
query.bindValue(":comicInfoId", comic->info.id);
query.bindValue(":name", comic->name);
query.bindValue(":path", comic->path);
query.exec();
return query.lastInsertId().toULongLong();
}
//queries
QList<LibraryItem *> DBHelper::getFoldersFromParent(qulonglong parentId, QSqlDatabase & db, bool sort)
{
QList<LibraryItem *> list;
QSqlQuery selectQuery(db); //TODO check
selectQuery.prepare("SELECT * FROM folder WHERE parentId = :parentId and id <> 1");
selectQuery.bindValue(":parentId", parentId);
selectQuery.exec();
Folder * currentItem;
while (selectQuery.next())
{
QList<QVariant> data;
QSqlRecord record = selectQuery.record();
for(int i=0;i<record.count();i++)
data << record.value(i);
//TODO sort by sort indicator and name
currentItem = new Folder(record.value("id").toULongLong(),record.value("parentId").toULongLong(),record.value("name").toString(),record.value("path").toString());
int lessThan = 0;
if(list.isEmpty() || !sort)
list.append(currentItem);
else
{
Folder * last = static_cast<Folder *>(list.back());
QString nameLast = last->name;
QString nameCurrent = currentItem->name;
QList<LibraryItem *>::iterator i;
i = list.end();
i--;
while ((0 > (lessThan = nameCurrent.localeAwareCompare(nameLast))) && i != list.begin())
{
i--;
nameLast = (*i)->name;
}
if(lessThan>0) //si se ha encontrado un elemento menor que current, se inserta justo después
list.insert(++i,currentItem);
else
list.insert(i,currentItem);
}
}
return list;
}
QList<LibraryItem *> DBHelper::getComicsFromParent(qulonglong parentId, QSqlDatabase & db, bool sort)
{
QList<LibraryItem *> list;
QSqlQuery selectQuery(db);
selectQuery.prepare("select c.id,c.parentId,c.fileName,c.path,ci.hash from comic c inner join comic_info ci on (c.comicInfoId = ci.id) where c.parentId = :parentId");
selectQuery.bindValue(":parentId", parentId);
selectQuery.exec();
ComicDB * currentItem;
while (selectQuery.next())
{
QList<QVariant> data;
QSqlRecord record = selectQuery.record();
for(int i=0;i<record.count();i++)
data << record.value(i);
currentItem = new ComicDB();
currentItem->id = record.value("id").toULongLong();
currentItem->parentId = record.value(1).toULongLong();
currentItem->name = record.value(2).toString();
currentItem->path = record.value(3).toString();
currentItem->info = DBHelper::loadComicInfo(record.value(4).toString(),db);
int lessThan = 0;
if(list.isEmpty() || !sort)
list.append(currentItem);
else
{
ComicDB * last = static_cast<ComicDB *>(list.back());
QString nameLast = last->name;
QString nameCurrent = currentItem->name;
QList<LibraryItem *>::iterator i;
i = list.end();
i--;
while ((0 > (lessThan = nameCurrent.localeAwareCompare(nameLast))) && i != list.begin()) //se usa la misma ordenación que en QDir
{
i--;
nameLast = (*i)->name;
}
if(lessThan>0) //si se ha encontrado un elemento menor que current, se inserta justo después
list.insert(++i,currentItem);
else
list.insert(i,currentItem);
}
}
//selectQuery.finish();
return list;
}
//loads
Folder DBHelper::loadFolder(qulonglong id, QSqlDatabase & db)
{
Folder folder;
QSqlQuery query(db);
query.prepare("SELECT * FROM folder WHERE id = :id");
query.bindValue(":id",id);
query.exec();
folder.id = id;
folder.parentId = 0;
if(query.next())
{
QSqlRecord record = query.record();
folder.parentId = record.value("parentId").toULongLong();
folder.name = record.value("name").toString();
folder.path = record.value("path").toString();
}
return folder;
}
ComicDB DBHelper::loadComic(qulonglong id, QSqlDatabase & db)
{
ComicDB comic;
QSqlQuery selectQuery(db);
selectQuery.prepare("select c.id,c.parentId,c.fileName,c.path,ci.hash from comic c inner join comic_info ci on (c.comicInfoId = ci.id) where c.id = :id");
selectQuery.bindValue(":id", id);
selectQuery.exec();
comic.id = id;
if(selectQuery.next())
{
QSqlRecord record = selectQuery.record();
//id = record.value("id").toULongLong();
comic.parentId = record.value("parentId").toULongLong();
comic.name = record.value("name").toString();
comic.path = record.value("path").toString();
comic.info = DBHelper::loadComicInfo(record.value("hash").toString(),db);
}
return comic;
}
ComicDB DBHelper::loadComic(qulonglong cparentId, QString cname, QString cpath, QString chash, QSqlDatabase & database)
{
ComicDB comic;
comic.parentId = cparentId;
comic.name = cname;
comic.path = cpath;
comic.info = DBHelper::loadComicInfo(chash,database);
if(!comic.info.existOnDb)
{
comic.info.hash = chash;
comic.info.coverPage = new int(1);
comic._hasCover = false;
}
else
comic._hasCover = true;
return comic;
}
ComicInfo DBHelper::loadComicInfo(QString hash, QSqlDatabase & db)
{
ComicInfo comicInfo;
QSqlQuery findComicInfo(db);
findComicInfo.prepare("SELECT * FROM comic_info WHERE hash = :hash");
findComicInfo.bindValue(":hash", hash);
findComicInfo.exec();
if(findComicInfo.next())
{
comicInfo.hash = hash;
QSqlRecord record = findComicInfo.record();
comicInfo.hash = hash;
comicInfo.id = record.value("id").toULongLong();
comicInfo.read = record.value("read").toBool();
comicInfo.edited = record.value("edited").toBool();
setField("title",comicInfo.title,record);
setField("numPages",comicInfo.numPages,record);
setField("coverPage",comicInfo.coverPage,record);
setField("number",comicInfo.number,record);
setField("isBis",comicInfo.isBis,record);
setField("count",comicInfo.count,record);
setField("volume",comicInfo.volume,record);
setField("storyArc",comicInfo.storyArc,record);
setField("arcNumber",comicInfo.arcNumber,record);
setField("arcCount",comicInfo.arcCount,record);
setField("genere",comicInfo.genere,record);
setField("writer",comicInfo.writer,record);
setField("penciller",comicInfo.penciller,record);
setField("inker",comicInfo.inker,record);
setField("colorist",comicInfo.colorist,record);
setField("letterer",comicInfo.letterer,record);
setField("coverArtist",comicInfo.coverArtist,record);
setField("date",comicInfo.date,record);
setField("publisher",comicInfo.publisher,record);
setField("format",comicInfo.format,record);
setField("color",comicInfo.color,record);
setField("ageRating",comicInfo.ageRating,record);
setField("synopsis",comicInfo.synopsis,record);
setField("characters",comicInfo.characters,record);
setField("notes",comicInfo.notes,record);
comicInfo.existOnDb = true;
}
else
comicInfo.existOnDb = false;
return comicInfo;
}
void DBHelper::setField(const QString & name, QString * & field, QSqlRecord & record)
{
if(!record.value(name).isNull())
{
field = new QString();
*field = record.value(name).toString();
}
}
void DBHelper::setField(const QString & name, int * & field, QSqlRecord & record)
{
if(!record.value(name).isNull())
{
field = new int;
*field = record.value(name).toInt();
}
}
void DBHelper::setField(const QString & name, bool * & field, QSqlRecord & record)
{
if(!record.value(name).isNull())
{
field = new bool;
*field = record.value(name).toBool();
}
}
void DBHelper::bindField(const QString & name, QString * field, QSqlQuery & query)
{
if(field != NULL)
{
query.bindValue(name,*field);
}
}
void DBHelper::bindField(const QString & name, int * field, QSqlQuery & query)
{
if(field != NULL)
{
query.bindValue(name,*field);
}
}
void DBHelper::bindField(const QString & name, bool * field, QSqlQuery & query)
{
if(field != NULL)
{
query.bindValue(name,*field);
}
}

View File

@ -0,0 +1,57 @@
#ifndef DB_HELPER_H
#define DB_HELPER_H
class QString;
#include <QMap>
#include <QList>
class ComicDB;
class Folder;
class LibraryItem;
class QSqlDatabase;
class ComicInfo;
class QSqlRecord;
class QSqlQuery;
class DBHelper
{
public:
//server
static QMap<QString,QString> getLibraries();
static QList<LibraryItem *> getFolderContentFromLibrary(const QString & libraryName, qulonglong folderId);
static QList<LibraryItem *> getFolderComicsFromLibrary(const QString & libraryName, qulonglong folderId);
static qulonglong getParentFromComicFolderId(const QString & libraryName, qulonglong id);
static ComicDB getComicInfo(const QString & libraryName, qulonglong id);
static QString getFolderName(const QString & libraryName, qulonglong 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);
//inserts
static qulonglong insert(Folder * folder, QSqlDatabase & db);
static qulonglong insert(ComicDB * comic, QSqlDatabase & db);
//updates
static void update(ComicDB * comics, QSqlDatabase & db);
static void update(ComicInfo * comicInfo, QSqlDatabase & db);
static void updateRead(ComicInfo * comicInfo, QSqlDatabase & db);
//queries
static QList<LibraryItem *> getFoldersFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true);
static QList<LibraryItem *> getComicsFromParent(qulonglong parentId, QSqlDatabase & db, bool sort = true);
//load
static Folder loadFolder(qulonglong id, QSqlDatabase & db);
static ComicDB loadComic(qulonglong id, QSqlDatabase & db);
static ComicDB loadComic(qulonglong cparentId, QString cname, QString cpath, QString chash, QSqlDatabase & database);
static ComicInfo loadComicInfo(QString hash, QSqlDatabase & db);
private:
static void setField(const QString & name, QString * & field, QSqlRecord & record);
static void setField(const QString & name, int * & field, QSqlRecord & record);
static void setField(const QString & name, bool * & field, QSqlRecord & record);
static void bindField(const QString & name, QString * field, QSqlQuery & query);
static void bindField(const QString & name, int * field, QSqlQuery & query);
static void bindField(const QString & name, bool * field, QSqlQuery & query);
};
#endif

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/db.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,104 @@
#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/comicFolder.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"));
t.setInterval(500);
connect(&t,SIGNAL(timeout()),this,SLOT(updateProgress()));
}
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()));
t.start();
}
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);
t.stop();
progressCount=0;
QDialog::close();
}
void ExportLibraryDialog::run()
{
}

View File

@ -0,0 +1,36 @@
#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();
QTimer t;
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>

BIN
YACReaderLibrary/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

3
YACReaderLibrary/icon.rc Normal file
View File

@ -0,0 +1,3 @@
IDI_ICON1 ICON DISCARDABLE "icon.ico"
IDI_ICON2 ICON DISCARDABLE "icon2.ico"
IDI_ICON3 ICON DISCARDABLE "icon3.ico"

BIN
YACReaderLibrary/icon2.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
YACReaderLibrary/icon3.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

109
YACReaderLibrary/images.qrc Normal file
View File

@ -0,0 +1,109 @@
<RCC>
<qresource prefix="/" >
<file>../images/deleteLibrary.png</file>
<file>../images/folder.png</file>
<file>../images/help.png</file>
<file>../images/icon.png</file>
<file>../images/iconLibrary.png</file>
<file>../images/new.png</file>
<file>../images/openLibrary.png</file>
<file>../images/removeLibrary.png</file>
<file>../images/updateLibrary.png</file>
<file>../images/comicFolder.png</file>
<file>../images/notCover.png</file>
<file>../images/edit.png</file>
<file>../images/fit.png</file>
<file>../images/properties.png</file>
<file>../images/options.png</file>
<file>../images/flow1.png</file>
<file>../images/flow2.png</file>
<file>../images/flow3.png</file>
<file>../images/flow4.png</file>
<file>../images/flow5.png</file>
<file>../images/importLibrary.png</file>
<file>../images/exportLibrary.png</file>
<file>../images/open.png</file>
<file>../images/coversPackage.png</file>
<file>../images/setRead.png</file>
<file>../images/setAllRead.png</file>
<file>../images/setUnread.png</file>
<file>../images/setAllUnread.png</file>
<file>../images/showMarks.png</file>
<file>../images/importCover.png</file>
<file>../images/editComic.png</file>
<file>../images/selectAll.png</file>
<file>../images/hideComicFlow.png</file>
<file>../images/exportComicsInfo.png</file>
<file>../images/importComicsInfo.png</file>
<file>../images/db.png</file>
<file>../images/asignNumber.png</file>
<file>../images/defaultCover.png</file>
<file>../images/server.png</file>
<file>../images/iphoneConfig.png</file>
<file>../images/qrMessage.png</file>
<file>../images/onStartFlowSelection.png</file>
<file>../images/onStartFlowSelection_es.png</file>
<file>../images/useNewFlowButton.png</file>
<file>../images/useOldFlowButton.png</file>
<file>../images/zip.png</file>
<file>../images/rar.png</file>
<file>../images/tar.png</file>
<file>../images/7z.png</file>
<file>../images/pdf.png</file>
<file>../images/comicZip.png</file>
<file>../images/comicRar.png</file>
<file>../images/comicTar.png</file>
<file>../images/comic7z.png</file>
<file>../images/serverConfigBackground.png</file>
<file>../images/noLibrariesIcon.png</file>
<file>../images/noLibrariesLine.png</file>
<file>../images/importingIcon.png</file>
<file>../images/updatingIcon.png</file>
<file>../images/importTopCoversDecoration.png</file>
<file>../images/importBottomCoversDecoration.png</file>
<file>../images/glowLine.png</file>
<file>../images/clearSearch.png</file>
<file>../images/iconSearch.png</file>
<file>../images/readRibbon.png</file>
<file>../images/shownCovers.png</file>
<file>../images/hiddenCovers.png</file>
<file>../images/trash.png</file>
<file>../images/setReadButton.png</file>
<file>../images/openInYACReader.png</file>
<file>../images/deleting_progress/imgTopLeft.png</file>
<file>../images/deleting_progress/imgTopMiddle.png</file>
<file>../images/deleting_progress/imgTopRight.png</file>
<file>../images/deleting_progress/imgLeftMiddle.png</file>
<file>../images/deleting_progress/imgRightMiddle.png</file>
<file>../images/deleting_progress/imgBottomLeft.png</file>
<file>../images/deleting_progress/imgBottomMiddle.png</file>
<file>../images/deleting_progress/imgBottomRight.png</file>
<file>../images/deleting_progress/icon.png</file>
<file>../images/social_dialog/close.png</file>
<file>../images/social_dialog/facebook.png</file>
<file>../images/social_dialog/google+.png</file>
<file>../images/social_dialog/icon.png</file>
<file>../images/social_dialog/shadow.png</file>
<file>../images/social_dialog/twitter.png</file>
<file>../images/social_dialog/separator.png</file>
<file>../images/main_toolbar/back.png</file>
<file>../images/main_toolbar/back_disabled.png</file>
<file>../images/main_toolbar/forward.png</file>
<file>../images/main_toolbar/forward_disabled.png</file>
<file>../images/main_toolbar/divider.png</file>
<file>../images/main_toolbar/settings.png</file>
<file>../images/main_toolbar/server.png</file>
<file>../images/main_toolbar/help.png</file>
<file>../images/main_toolbar/fullscreen.png</file>
<file>../images/collapsed_branch_osx.png</file>
<file>../images/expanded_branch_osx.png</file>
<file>../images/folder_macosx.png</file>
<file>../images/libraryIcon.png</file>
<file>../images/libraryIconSelected.png</file>
<file>../images/libraryOptions.png</file>
<file>../images/branch-open.png</file>
<file>../images/branch-closed.png</file>
<file>../images/expanded_branch_selected.png</file>
<file>../images/collapsed_branch_selected.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/" >
<file alias="images/setRoot.png">../images/setRoot_osx.png</file>
<file alias="images/expand.png">../images/expand_osx.png</file>
<file alias="images/colapse.png">../images/colapse_osx.png</file>
<file alias="images/newLibraryIcon.png">../images/newLibraryIcon_osx.png</file>
<file alias="images/openLibraryIcon.png">../images/openLibraryIcon_osx.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/" >
<file>../images/setRoot.png</file>
<file>../images/expand.png</file>
<file>../images/colapse.png</file>
<file>../images/newLibraryIcon.png</file>
<file>../images/openLibraryIcon.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,111 @@
#include "import_comics_info_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QProgressBar>
#include "data_base_management.h"
ImportComicsInfoDialog::ImportComicsInfoDialog(QWidget *parent)
: QDialog(parent)
{
setModal(true);
setWindowTitle(tr("Import comics info"));
textLabel = new QLabel(tr("Info database location : "));
path = new QLineEdit;
textLabel->setBuddy(path);
accept = new QPushButton(tr("Import"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(import()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
//connect(cancel,SIGNAL(clicked()),this,SIGNAL(rejected()));
find = new QPushButton(QIcon(":/images/db.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
progressBar = new QProgressBar(this);
progressBar->setMinimum(0);
progressBar->setMaximum(0);
progressBar->setTextVisible(false);
progressBar->hide();
connect(accept,SIGNAL(progressBar()),this,SLOT(show()));
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addStretch();
bottomLayout->addWidget(accept);
bottomLayout->addWidget(cancel);
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/importComicsInfo.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
}
ImportComicsInfoDialog::~ImportComicsInfoDialog()
{
}
void ImportComicsInfoDialog::findPath()
{
QString s = QFileDialog::getOpenFileName(0,"Comics Info",".",tr("Comics info file (*.ydb)"));
if(!s.isEmpty())
{
path->setText(s);
accept->setEnabled(true);
}
}
void ImportComicsInfoDialog::import()
{
progressBar->show();
Importer * importer = new Importer();
importer->source = path->text();
importer->dest = dest;
connect(importer,SIGNAL(finished()),this,SLOT(close()));
connect(importer,SIGNAL(finished()),this,SLOT(hide()));
importer->start();
}
void ImportComicsInfoDialog::close()
{
path->clear();
progressBar->hide();
accept->setDisabled(true);
QDialog::close();
emit(finished(0));
}
void Importer::run()
{
DataBaseManagement::importComicsInfo(source,dest);
}

View File

@ -0,0 +1,52 @@
#ifndef IMPORT_COMICS_INFO_DIALOG_H
#define IMPORT_COMICS_INFO_DIALOG_H
#include <QDialog>
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QProgressBar>
#include <QThread>
class Importer : public QThread
{
public:
QString source;
QString dest;
private:
void run();
};
class ImportComicsInfoDialog : public QDialog
{
Q_OBJECT
public:
ImportComicsInfoDialog(QWidget *parent = 0);
~ImportComicsInfoDialog();
QString dest;
private:
QLabel * nameLabel;
QLabel * textLabel;
QLabel * destLabel;
QLineEdit * path;
QLineEdit * destPath;
QLineEdit * nameEdit;
QPushButton * find;
QPushButton * findDest;
QPushButton * accept;
QPushButton * cancel;
QLabel * progress;
void setupUI();
int progressCount;
QProgressBar *progressBar;
public slots:
void findPath();
void import();
void close();
};
#endif // IMPORT_COMICS_INFO_DIALOG_H

View File

@ -0,0 +1,144 @@
#include "import_library_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
#include <QGridLayout>
ImportLibraryDialog::ImportLibraryDialog(QWidget * parent)
:QDialog(parent),progressCount(0)
{
setupUI();
}
void ImportLibraryDialog::setupUI()
{
nameLabel = new QLabel(tr("Library Name : "));
nameEdit = new QLineEdit;
nameLabel->setBuddy(nameEdit);
connect(nameEdit,SIGNAL(textChanged(QString)),this,SLOT(nameEntered()));
textLabel = new QLabel(tr("Package location : "));
path = new QLineEdit;
textLabel->setBuddy(path);
destLabel = new QLabel(tr("Destination folder : "));
destPath = new QLineEdit;
textLabel->setBuddy(destPath);
accept = new QPushButton(tr("Unpack"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(add()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
//connect(cancel,SIGNAL(clicked()),this,SIGNAL(rejected()));
find = new QPushButton(QIcon(":/images/coversPackage.png"),"");
connect(find,SIGNAL(clicked()),this,SLOT(findPath()));
findDest = new QPushButton(QIcon(":/images/open.png"),"");
connect(findDest,SIGNAL(clicked()),this,SLOT(findDestination()));
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); //TODO
content->addWidget(destLabel,2,0);
content->addWidget(destPath,2,1);
content->addWidget(findDest,2,2);
//destLayout->setStretchFactor(findDest,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(content);
//mainLayout->addWidget(progress = new QLabel());
mainLayout->addStretch();
mainLayout->addWidget(progressBar);
mainLayout->addLayout(bottomLayout);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/importLibrary.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Extract a catalog"));
}
void ImportLibraryDialog::add()
{
accept->setEnabled(false);
progressBar->show();
emit(unpackCLC(QDir::cleanPath(path->text()),QDir::cleanPath(destPath->text()),nameEdit->text()));
}
void ImportLibraryDialog::findPath()
{
QString s = QFileDialog::getOpenFileName(0,"Covers Package",".",tr("Compresed library covers (*.clc)"));
if(!s.isEmpty())
{
path->setText(s);
if(!destPath->text().isEmpty() && !nameEdit->text().isEmpty())
accept->setEnabled(true);
}
}
void ImportLibraryDialog::findDestination()
{
QString s = QFileDialog::getExistingDirectory(0,"Folder",".",QFileDialog::ShowDirsOnly);
if(!s.isEmpty())
{
destPath->setText(s);
if(!path->text().isEmpty() && !nameEdit->text().isEmpty())
accept->setEnabled(true);
}
}
void ImportLibraryDialog::nameEntered()
{
if(!nameEdit->text().isEmpty())
{
if(!path->text().isEmpty() && !destPath->text().isEmpty())
accept->setEnabled(true);
}
else
accept->setEnabled(false);
}
void ImportLibraryDialog::close()
{
path->clear();
destPath->clear();
nameEdit->clear();
accept->setEnabled(false);
progressBar->hide();
QDialog::hide();
}
void ImportLibraryDialog::closeEvent ( QCloseEvent * e )
{
close();
}

View File

@ -0,0 +1,43 @@
#ifndef IMPORT_LIBRARY_DIALOG_H
#define IMPORT_LIBRARY_DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QThread>
#include <QProgressBar>
class ImportLibraryDialog : public QDialog
{
Q_OBJECT
public:
ImportLibraryDialog(QWidget * parent = 0);
private:
QLabel * nameLabel;
QLabel * textLabel;
QLabel * destLabel;
QLineEdit * path;
QLineEdit * destPath;
QLineEdit * nameEdit;
QPushButton * find;
QPushButton * findDest;
QPushButton * accept;
QPushButton * cancel;
QProgressBar *progressBar;
void setupUI();
int progressCount;
void closeEvent ( QCloseEvent * e );
public slots:
void add();
void findPath();
void findDestination();
void close();
void nameEntered();
signals:
void unpackCLC(QString clc,QString targetFolder, QString name);
};
#endif

View File

@ -0,0 +1,385 @@
#include "import_widget.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsPixmapItem>
#include <QScrollBar>
#include <QGraphicsItemAnimation>
#include <QTimeLine>
#include <QGLWidget>
#include <QTimer>
#include <QElapsedTimer>
#include <QToolButton>
#include <QResizeEvent>
#include <QPropertyAnimation>
#include <QGraphicsOpacityEffect>
class YACReaderActivityIndicatorWidget : public QWidget
{
public:
YACReaderActivityIndicatorWidget(QWidget * parent = 0);
public slots:
private:
QLabel * normal;
QLabel * glow;
};
YACReaderActivityIndicatorWidget::YACReaderActivityIndicatorWidget(QWidget * parent)
:QWidget(parent)
{
QPixmap line(":/images/noLibrariesLine.png");
QPixmap glowLine(":/images/glowLine.png");
normal = new QLabel(this);
glow = new QLabel(this);
normal->setPixmap(line);
glow->setPixmap(glowLine);
QHBoxLayout * layout = new QHBoxLayout();
layout->addWidget(normal,0,Qt::AlignVCenter);
setLayout(layout);
layout->setMargin(4);
layout->setSpacing(0);
//setFixedHeight(3);
//resize(579,3);
glow->setGeometry(4,4,glowLine.width(),glowLine.height());
//normal->setGeometry(0,1,579,1);
QGraphicsOpacityEffect * effect = new QGraphicsOpacityEffect();
//effect->setOpacity(1.0);
QPropertyAnimation * animation = new QPropertyAnimation(effect,"opacity");
animation->setDuration(1000);
animation->setStartValue(1);
animation->setEndValue(0);
//animation->setEasingCurve(QEasingCurve::InQuint);
QPropertyAnimation * animation2 = new QPropertyAnimation(effect,"opacity");
animation2->setDuration(1000);
animation2->setStartValue(0);
animation2->setEndValue(1);
//animation2->setEasingCurve(QEasingCurve::InQuint);
#ifndef Q_OS_MAC
glow->setGraphicsEffect(effect);
#endif
connect(animation,SIGNAL(finished()),animation2,SLOT(start()));
connect(animation2,SIGNAL(finished()),animation,SLOT(start()));
animation->start();
}
ImportWidget::ImportWidget(QWidget *parent) :
QWidget(parent)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QPalette p(palette());
p.setColor(QPalette::Background, QColor(250,250,250));
setAutoFillBackground(true);
setPalette(p);
QPixmap icon(":/images/importingIcon.png");
iconLabel = new QLabel();
iconLabel->setPixmap(icon);
/*QPixmap line(":/images/noLibrariesLine.png");
QLabel * lineLabel = new QLabel();
lineLabel->setPixmap(line);*/
YACReaderActivityIndicatorWidget * activityIndicator = new YACReaderActivityIndicatorWidget();
text = new QLabel();//"<font color=\"#495252\">"+tr("Importing comics")+"</font>");
text->setStyleSheet("QLabel {font-size:25px;font-weight:bold;}");
textDescription = new QLabel();//"<font color=\"#565959\">"+tr("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")+"</font>");
textDescription->setWordWrap(true);
textDescription->setMaximumWidth(330);
currentComicLabel = new QLabel("<font color=\"#565959\">...</font>");
coversViewContainer = new QWidget(this);
QVBoxLayout * coversViewLayout = new QVBoxLayout;
coversViewContainer->setLayout(coversViewLayout);
coversView = new QGraphicsView();
//coversView->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
coversView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
coversView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
coversView->setMaximumHeight(300);
coversView->setStyleSheet("QGraphicsView {background-color: #E6E6E6;border:none;}");
coversScene = new QGraphicsScene();
coversScene->setSceneRect(0,0,coversView->width(),coversView->height());
coversView->setAlignment(Qt::AlignLeft);
coversView->setScene(coversScene);
QLabel * topDecorator = new QLabel();
QLabel * bottomDecorator = new QLabel();
QPixmap top(":/images/importTopCoversDecoration.png");
QPixmap bottom(":/images/importBottomCoversDecoration.png");
topDecorator->setPixmap(top);
bottomDecorator->setPixmap(bottom);
topDecorator->setScaledContents(true);
bottomDecorator->setScaledContents(true);
topDecorator->setFixedHeight(top.height());
bottomDecorator->setFixedHeight(bottom.height());
coversViewLayout->addWidget(topDecorator,0);
coversViewLayout->addWidget(coversView,1);
coversViewLayout->addWidget(bottomDecorator,0);
coversViewLayout->setMargin(0);
coversViewLayout->setSpacing(0);
QPushButton * stop = new QPushButton(tr("stop"));
stop->setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum);
QVBoxLayout * layout = new QVBoxLayout(this);
QHBoxLayout * buttonLayout = new QHBoxLayout();
QHBoxLayout * topLayout = new QHBoxLayout();
QVBoxLayout * textLayout = new QVBoxLayout();
QWidget * topWidget = new QWidget();
topWidget->setFixedWidth(650);
textLayout->addStretch();
textLayout->addWidget(text);
textLayout->addSpacing(12);
textLayout->addWidget(textDescription);
textLayout->addStretch();
topLayout->addStretch();
topLayout->addWidget(iconLabel,0,Qt::AlignVCenter);
topLayout->addSpacing(30);
topLayout->addLayout(textLayout,1);
topLayout->addStretch();
topLayout->setMargin(0);
topWidget->setLayout(topLayout);
layout->setAlignment(Qt::AlignHCenter);
buttonLayout->addSpacing(250);
buttonLayout->addWidget(stop);
buttonLayout->addSpacing(250);
layout->addSpacing(50);
layout->addWidget(topWidget,0,Qt::AlignHCenter);
layout->addSpacing(20);
layout->addWidget(activityIndicator,0,Qt::AlignHCenter);
layout->addSpacing(10);
layout->addLayout(buttonLayout,0);
layout->addSpacing(10);
layout->addStretch();
portadasLabel = new QLabel("<font color=\"#565959\">"+tr("Some of the comics being added...")+"</font>");
hideButton = new QToolButton(this);
hideButton->setFixedSize(25,18);
hideButton->setStyleSheet("QToolButton {background: url(\":/images/shownCovers.png\"); border:none;}"
" QToolButton:checked {background:url(\":/images/hiddenCovers.png\"); border:none;}");
hideButton->setCheckable(true);
connect(hideButton,SIGNAL(toggled(bool)),this,SLOT(showCovers(bool)));
layout->addWidget(portadasLabel,0,Qt::AlignHCenter);
layout->addWidget(coversViewContainer);
//layout->addStretch();
layout->addWidget(currentComicLabel,0,Qt::AlignHCenter);
layout->setContentsMargins(0,layout->contentsMargins().top(),0,layout->contentsMargins().bottom());
connect(stop,SIGNAL(clicked()),this,SIGNAL(stop()));
//connect(stop,SIGNAL(clicked()),this,SLOT(addCoverTest()));
previousWidth = 10;
updatingCovers = false;
elapsedTimer = new QElapsedTimer();
}
void ImportWidget::newComic(const QString & path, const QString & coverPath)
{
currentComicLabel->setText("<font color=\"#565959\">"+path+"</font>");
if(((elapsedTimer->elapsed()>=1000) || ((previousWidth < coversView->width()) && (elapsedTimer->elapsed()>=500))) && !updatingCovers)//todo elapsed time
{
QPixmap p(coverPath);
p = p.scaledToHeight(300,Qt::SmoothTransformation);
QGraphicsPixmapItem * item = new QGraphicsPixmapItem(p);
item->setPos(previousWidth,0);
item->setZValue(i/10000.0);
previousWidth += 10 + p.width();
coversScene->addItem(item);
elapsedTimer->start();
if(previousWidth >= coversView->width()+200 && !updatingCovers)
{
updatingCovers = true;
foreach(QGraphicsItem * itemToRemove, coversScene->items())
{
QGraphicsPixmapItem * last = dynamic_cast<QGraphicsPixmapItem *>(itemToRemove);
if((last->pos().x()+last->pixmap().width())<=0)
{
coversScene->removeItem(last);
delete last;
}
else
break;
}
int width = p.width();
foreach(QGraphicsItem * itemToMove, coversScene->items())
{
QTimeLine *timer = new QTimeLine(400);
timer->setFrameRange(0, 24);
timer->setUpdateInterval(17);
QGraphicsItemAnimation *animation = new QGraphicsItemAnimation;
animation->setItem(itemToMove);
animation->setTimeLine(timer);
QPointF point = itemToMove->scenePos();
float step = (width+10)/24.0;
for (int i = 0; i < 24; ++i)
animation->setPosAt(i / 24.0, QPointF(point.x()-((i+1)*step), point.y()));
timer->start();
connect(timer,SIGNAL(finished()),timer,SLOT(deleteLater()));
connect(timer,SIGNAL(finished()),animation,SLOT(deleteLater()));
}
QTimer::singleShot(400,this,SLOT(finishedUpdatingCover()));
previousWidth -= 10+width;
}
}
}
void ImportWidget::finishedUpdatingCover()
{
updatingCovers = false;
}
void ImportWidget::newCover(const QPixmap & image)
{
}
static int i = 1;
static int previousWidth = 10;
static int j = 0;
void ImportWidget::addCoverTest()
{
QPixmap p(QString("c:/temp/%1.jpg").arg(i));
p = p.scaledToHeight(300,Qt::SmoothTransformation);
QGraphicsPixmapItem * item = new QGraphicsPixmapItem(p);
item->setPos(previousWidth,0);
item->setZValue(i/10000.0);
previousWidth += 10 + p.width();
coversScene->addItem(item);
if(previousWidth >= coversView->width())
{
QGraphicsItem * last = coversScene->items().last();
int width = p.width();
if(j>=1)
{
coversScene->removeItem(last);
delete last;
}
else
j++;
foreach(QGraphicsItem * itemToMove, coversScene->items())
{
QTimeLine *timer = new QTimeLine(/*350*/1000);
timer->setFrameRange(0, 60);
QGraphicsItemAnimation *animation = new QGraphicsItemAnimation;
animation->setItem(itemToMove);
animation->setTimeLine(timer);
QPointF point = itemToMove->scenePos();
float step = (width+10)/60.0;
for (int i = 0; i < 60; ++i)
animation->setPosAt(i / 60.0, QPointF(point.x()-((i+1)*step), point.y()));
timer->start();
}
previousWidth -= 10+width;
}
i++;
}
void ImportWidget::clear()
{
previousWidth = 10;
//nos aseguramos de que las animaciones han finalizado antes de borrar
QList<QGraphicsItem*> all = coversScene->items();
for (int i = 0; i < all.size(); i++)
{
QGraphicsItem *gi = all[i];
if(gi->parentItem()==NULL)
delete gi;
}
coversScene->clear();
updatingCovers = false;
currentComicLabel->setText("<font color=\"#565959\">...</font>");
i = 0;
}
void ImportWidget::setImportLook()
{
iconLabel->setPixmap(QPixmap(":/images/importingIcon.png"));
text->setText("<font color=\"#495252\">"+tr("Importing comics")+"</font>");
textDescription->setText("<font color=\"#565959\">"+tr("<p>YACReaderLibrary is now creating a new library.</p><p>Create a library could take several minutes. You can stop the process and update the library later for completing the task.</p>")+"</font>");
}
void ImportWidget::setUpdateLook()
{
iconLabel->setPixmap(QPixmap(":/images/updatingIcon.png"));
text->setText("<font color=\"#495252\">"+tr("Updating the library")+"</font>");
textDescription->setText("<font color=\"#565959\">"+tr("<p>The current library is being updated. For faster updates, please, update your libraries frequently.</p><p>You can stop the process and continue updating this library later.</p>")+"</font>");
}
void ImportWidget::clearScene()
{
}
void ImportWidget::showCovers(bool hide)
{
portadasLabel->setHidden(hide);
coversViewContainer->setHidden(hide);
}
void ImportWidget::resizeEvent(QResizeEvent * event)
{
hideButton->move(event->size().width()-hideButton->width()- (currentComicLabel->height()/2),event->size().height()-hideButton->height()- (currentComicLabel->height()/2));
QWidget::resizeEvent(event);
}

View File

@ -0,0 +1,52 @@
#ifndef IMPORT_WIDGET_H
#define IMPORT_WIDGET_H
#include <QWidget>
class QLabel;
class QGraphicsView;
class QGraphicsScene;
class QElapsedTimer;
class QVBoxLayout;
class QToolButton;
class QResizeEvent;
class ImportWidget : public QWidget
{
Q_OBJECT
public:
explicit ImportWidget(QWidget *parent = 0);
signals:
void stop();
public slots:
void newComic(const QString & path, const QString & coverPath);
void newCover(const QPixmap & image);
void clear();
void addCoverTest();
void finishedUpdatingCover();
void clearScene();
void setImportLook();
void setUpdateLook();
void showCovers(bool hide);
private:
QLabel * currentComicLabel;
QLabel * portadasLabel;
QLabel * iconLabel;
QLabel * text;
QLabel * textDescription;
QWidget * coversViewContainer;
QGraphicsView * coversView;
QGraphicsScene * coversScene;
int previousWidth;
bool updatingCovers;
QElapsedTimer * elapsedTimer;
quint64 i;
QToolButton * hideButton;
void resizeEvent(QResizeEvent * event);
};
#endif // IMPORT_WIDGET_H

View File

@ -0,0 +1,548 @@
#include "library_creator.h"
#include "custom_widgets.h"
#include <QMutex>
#include <QDebug>
#include <QSqlQuery>
#include <QSqlRecord>
#include "data_base_management.h"
#include "qnaturalsorting.h"
#include "db_helper.h"
#include <algorithm>
using namespace std;
#include "poppler-qt4.h"
//--------------------------------------------------------------------------------
LibraryCreator::LibraryCreator()
:creation(false)
{
_nameFilter << "*.cbr" << "*.cbz" << "*.rar" << "*.zip" << "*.tar" << "*.pdf" << "*.7z" << "*.cb7" << "*.arj" << "*.cbt";
}
void LibraryCreator::createLibrary(const QString &source, const QString &target)
{
creation = true;
processLibrary(source,target);
}
void LibraryCreator::updateLibrary(const QString &source, const QString &target)
{
processLibrary(source,target);
}
void LibraryCreator::processLibrary(const QString & source, const QString & target)
{
_source = source;
_target = target;
if(DataBaseManagement::checkValidDB(target+"/library.ydb")=="")
{
//se limpia el directorio ./yacreaderlibrary
delTree(target);
_mode = CREATOR;
}
else //
_mode = UPDATER;
}
//
void LibraryCreator::run()
{
stopRunning = false;
if(_mode == CREATOR)
{
_currentPathFolders.clear();
_currentPathFolders.append(Folder(1,1,"root","/"));
//se crean los directorios .yacreaderlibrary y .yacreaderlibrary/covers
QDir dir;
dir.mkpath(_target+"/covers");
//se crea la base de datos .yacreaderlibrary/library.ydb
_database = DataBaseManagement::createDatabase("library",_target);//
if(!_database.isOpen())
{
emit failedCreatingDB(_database.lastError().databaseText() + "-" + _database.lastError().driverText());
emit finished();
creation = false;
return;
}
/*QSqlQuery pragma("PRAGMA foreign_keys = ON",_database);*/
_database.transaction();
//se crea la librer<65>a
create(QDir(_source));
_database.commit();
_database.close();
QSqlDatabase::removeDatabase(_database.connectionName());
emit(created());
}
else
{
_currentPathFolders.clear();
_currentPathFolders.append(Folder(1,1,"root","/"));
_database = DataBaseManagement::loadDatabase(_target);
//_database.setDatabaseName(_target+"/library.ydb");
if(!_database.open())
{
emit failedOpeningDB(_database.lastError().databaseText() + "-" + _database.lastError().driverText());
emit finished();
creation = false;
return;
}
//QSqlQuery pragma("PRAGMA foreign_keys = ON",_database);
_database.transaction();
update(QDir(_source));
_database.commit();
_database.close();
QSqlDatabase::removeDatabase(_target);
//si estabamos en modo creaci<63>n, se est<73> a<>adiendo una librer<65>a que ya exist<73>a y se ha actualizado antes de a<>adirse.
if(!creation)
emit(updated());
else
emit(created());
}
msleep(100);//TODO try to solve the problem with the udpate dialog (ya no se usa m<>s...)
emit(finished());
creation = false;
}
void LibraryCreator::stop()
{
_database.commit();
stopRunning = true;
}
//retorna el id del ultimo de los folders
qulonglong LibraryCreator::insertFolders()
{
QList<Folder>::iterator i;
int currentId = 0;
for (i = _currentPathFolders.begin(); i != _currentPathFolders.end(); ++i)
{
if(!(i->knownId))
{
i->setFather(currentId);
currentId = DBHelper::insert(&(*i),_database);//insertFolder(currentId,*i);
i->setId(currentId);
}
else
{
currentId = i->id;
}
}
return 0;
}
void LibraryCreator::create(QDir dir)
{
dir.setNameFilters(_nameFilter);
dir.setFilter(QDir::AllDirs|QDir::Files|QDir::NoDotAndDotDot);
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i)
{
if(stopRunning)
return;
QFileInfo fileInfo = list.at(i);
QString fileName = fileInfo.fileName();
#ifdef Q_OS_MAC
QStringList src = _source.split("/");
QString filePath = fileInfo.absoluteFilePath();
QStringList fp = filePath.split("/");
for(int i = 0; i< src.count();i++)
{
fp.removeFirst();
}
QString relativePath = "/" + fp.join("/");
#else
QString relativePath = QDir::cleanPath(fileInfo.absoluteFilePath()).remove(_source);
#endif
if(fileInfo.isDir())
{
//se a<>ade al path actual el folder, a<>n no se sabe si habr<62> que a<>adirlo a la base de datos
_currentPathFolders.append(Folder(fileInfo.fileName(),relativePath));
create(QDir(fileInfo.absoluteFilePath()));
//una vez importada la informaci<63>n del folder, se retira del path actual ya que no volver<65> a ser visitado
_currentPathFolders.pop_back();
}
else
{
insertComic(relativePath,fileInfo);
}
}
}
bool LibraryCreator::checkCover(const QString & hash)
{
return QFile::exists(_target+"/covers/"+hash+".jpg");
}
void LibraryCreator::insertComic(const QString & relativePath,const QFileInfo & fileInfo)
{
//en este punto sabemos que todos los folders que hay en _currentPath, deber<65>an estar a<>adidos a la base de datos
insertFolders();
emit(coverExtracted(relativePath));
//Se calcula el hash del c<>mic
QCryptographicHash crypto(QCryptographicHash::Sha1);
QFile file(fileInfo.absoluteFilePath());
file.open(QFile::ReadOnly);
crypto.addData(file.read(524288));
file.close();
//hash Sha1 del primer 0.5MB + filesize
QString hash = QString(crypto.result().toHex().constData()) + QString::number(fileInfo.size());
ComicDB comic = DBHelper::loadComic(_currentPathFolders.last().id,fileInfo.fileName(),relativePath,hash,_database);
int numPages;
if(! ( comic.hasCover() && checkCover(hash)))
{
ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()),_target+"/covers/"+hash+".jpg",*comic.info.coverPage);
//ThumbnailCreator tc(QDir::cleanPath(fileInfo.absoluteFilePath()),_target+"/covers/"+fileInfo.fileName()+".jpg");
tc.create();
numPages = tc.getNumPages();
emit(comicAdded(relativePath,_target+"/covers/"+hash+".jpg"));
}
comic.info.setNumPages(numPages);
DBHelper::insert(&comic,_database);
}
void LibraryCreator::update(QDir dirS)
{
dirS.setNameFilters(_nameFilter);
dirS.setFilter(QDir::AllDirs|QDir::Files|QDir::NoDotAndDotDot);
dirS.setSorting(QDir::Name|QDir::IgnoreCase|QDir::LocaleAware|QDir::DirsFirst);
QFileInfoList listS = dirS.entryInfoList();
QList<LibraryItem *> folders = DBHelper::getFoldersFromParent(_currentPathFolders.last().id,_database);
QList<LibraryItem *> comics = DBHelper::getComicsFromParent(_currentPathFolders.last().id,_database);
QList <LibraryItem *> listD;
listD.append(folders);
listD.append(comics);
int lenghtS = listS.size();
int lenghtD = listD.size();
int maxLenght = qMax(lenghtS,lenghtD);
bool updated;
int i,j;
for (i=0,j=0; (i < lenghtS)||(j < lenghtD);)
{
if(stopRunning)
return;
updated = false;
if(i>=lenghtS) //finished source files/dirs
{
//delete listD //from j
for(;j<lenghtD;j++)
{
if(stopRunning)
return;
DBHelper::removeFromDB(listD.at(j),(_database));
}
updated = true;
}
if(j>=lenghtD) //finished library files/dirs
{
//create listS //from i
for(;i<lenghtS;i++)
{
if(stopRunning)
return;
QFileInfo fileInfoS = listS.at(i);
if(fileInfoS.isDir()) //create folder
{
#ifdef Q_OS_MAC
QStringList src = _source.split("/");
QString filePath = fileInfoS.absoluteFilePath();
QStringList fp = filePath.split("/");
for(int i = 0; i< src.count();i++)
{
fp.removeFirst();
}
QString path = "/" + fp.join("/");
#else
QString path = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source);
#endif
_currentPathFolders.append(Folder(fileInfoS.fileName(),path)); //folder actual no est<73> en la BD
create(QDir(fileInfoS.absoluteFilePath()));
_currentPathFolders.pop_back();
}
else //create comic
{
#ifdef Q_OS_MAC
QStringList src = _source.split("/");
QString filePath = fileInfoS.absoluteFilePath();
QStringList fp = filePath.split("/");
for(int i = 0; i< src.count();i++)
{
fp.removeFirst();
}
QString path = "/" + fp.join("/");
#else
QString path = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source);
#endif
insertComic(path,fileInfoS);
}
}
updated = true;
}
if(!updated)
{
QFileInfo fileInfoS = listS.at(i);
LibraryItem * fileInfoD = listD.at(j);
QString nameS = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(QDir::cleanPath(fileInfoS.absolutePath())); //remove source
QString nameD = "/"+fileInfoD->name;
int comparation = QString::localeAwareCompare(nameS,nameD);
if(fileInfoS.isDir()&&fileInfoD->isDir())
if(comparation == 0)//same folder, update
{
_currentPathFolders.append(*static_cast<Folder *>(fileInfoD));//fileInfoD conoce su padre y su id
update(QDir(fileInfoS.absoluteFilePath()));
_currentPathFolders.pop_back();
i++;
j++;
}
else
if(comparation < 0) //nameS doesn't exist on DB
{
if(nameS!="/.yacreaderlibrary")
{
#ifdef Q_OS_MAC
QStringList src = _source.split("/");
QString filePath = fileInfoS.absoluteFilePath();
QStringList fp = filePath.split("/");
for(int i = 0; i< src.count();i++)
{
fp.removeFirst();
}
QString path = "/" + fp.join("/");
#else
QString path = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source);
#endif
_currentPathFolders.append(Folder(fileInfoS.fileName(),path));
create(QDir(fileInfoS.absoluteFilePath()));
_currentPathFolders.pop_back();
}
i++;
}
else //nameD no longer avaliable on Source folder...
{
if(nameS!="/.yacreaderlibrary")
{
DBHelper::removeFromDB(fileInfoD,_database);
j++;
}
else
i++; //skip library directory
}
else // one of them(or both) is a file
if(fileInfoS.isDir()) //this folder doesn't exist on library
{
if(nameS!="/.yacreaderlibrary") //skip .yacreaderlibrary folder
{
#ifdef Q_OS_MAC
QStringList src = _source.split("/");
QString filePath = fileInfoS.absoluteFilePath();
QStringList fp = filePath.split("/");
for(int i = 0; i< src.count();i++)
{
fp.removeFirst();
}
QString path = "/" + fp.join("/");
#else
QString path = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source);
#endif
_currentPathFolders.append(Folder(fileInfoS.fileName(),path));
create(QDir(fileInfoS.absoluteFilePath()));
_currentPathFolders.pop_back();
}
i++;
}
else
if(fileInfoD->isDir()) //delete this folder from library
{
DBHelper::removeFromDB(fileInfoD,_database);
j++;
}
else //both are files //BUG on windows (no case sensitive)
{
//nameD.remove(nameD.size()-4,4);
int comparation = QString::localeAwareCompare(nameS,nameD);
if(comparation < 0) //create new thumbnail
{
#ifdef Q_OS_MAC
QStringList src = _source.split("/");
QString filePath = fileInfoS.absoluteFilePath();
QStringList fp = filePath.split("/");
for(int i = 0; i< src.count();i++)
{
fp.removeFirst();
}
QString path = "/" + fp.join("/");
#else
QString path = QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source);
#endif
insertComic(path,fileInfoS);
i++;
}
else
{
if(comparation > 0) //delete thumbnail
{
DBHelper::removeFromDB(fileInfoD,_database);
j++;
}
else //same file
{
if(fileInfoS.isFile() && !fileInfoD->isDir())
{
//TODO comprobar fechas + tama<6D>o
//if(fileInfoS.lastModified()>fileInfoD.lastModified())
//{
// dirD.mkpath(_target+(QDir::cleanPath(fileInfoS.absolutePath()).remove(_source)));
// emit(coverExtracted(QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source)));
// ThumbnailCreator tc(QDir::cleanPath(fileInfoS.absoluteFilePath()),_target+(QDir::cleanPath(fileInfoS.absoluteFilePath()).remove(_source))+".jpg");
// tc.create();
//}
}
i++;j++;
}
}
}
}
}
}
ThumbnailCreator::ThumbnailCreator(QString fileSource, QString target="", int coverPage)
:_fileSource(fileSource),_target(target),_numPages(0),_coverPage(coverPage)
{
}
void ThumbnailCreator::create()
{
QFileInfo fi(_fileSource);
if(fi.suffix().compare("pdf",Qt::CaseInsensitive) == 0)
{
Poppler::Document * pdfComic = Poppler::Document::load(_fileSource);
if (!pdfComic)
{
delete pdfComic;
pdfComic = 0;
QImage p;
p.load(":/images/notCover.png");
p.save(_target);
return;
}
_numPages = pdfComic->numPages();
if(_numPages >= _coverPage)
{
QImage p = pdfComic->page(_coverPage-1)->renderToImage(72,72); //TODO check if the the page is valid
_cover = QPixmap::fromImage(p);
QImage scaled;
if(p.width()>p.height()) //landscape??
scaled = p.scaledToWidth(640,Qt::SmoothTransformation);
else
scaled = p.scaledToWidth(480,Qt::SmoothTransformation);
scaled.save(_target,0,75);
}
else
{
QImage p;
p.load(":/images/notCover.png");
p.save(_target);
}
}
else
{
_7z = new QProcess();
QStringList attributes;
attributes << "l" << "-ssc-" << "-r" << _fileSource << "*.jpg" << "*.jpeg" << "*.png" << "*.gif" << "*.tiff" << "*.tif" << "*.bmp";
//connect(_7z,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(loadFirstImage(void)));
connect(_7z,SIGNAL(error(QProcess::ProcessError)),this,SIGNAL(openingError(QProcess::ProcessError)));
_7z->start(QCoreApplication::applicationDirPath()+"/utils/7z",attributes);
_7z->waitForFinished(60000);
QRegExp rx("[0-9]{4}-[0-9]{2}-[0-9]{2}[ ]+[0-9]{2}:[0-9]{2}:[0-9]{2}[ ]+.{5}[ ]+([0-9]+)[ ]+([0-9]+)[ ]+(.+)");
QString ba = QString::fromUtf8(_7z->readAllStandardOutput());
QList<QString> lines = ba.split('\n');
QString line;
_currentName = "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"; //TODO
QString name;
if(_coverPage == 1)
{
foreach(line,lines)
{
if(rx.indexIn(line)!=-1)
{
name = rx.cap(3).trimmed();
if(naturalSortLessThanCI(name,_currentName))
_currentName = name;
_numPages++;
}
}
}
else
{
QList<QString> names;
foreach(line,lines)
{
if(rx.indexIn(line)!=-1)
{
name = rx. cap(3).trimmed();
names.append(name);
}
}
std::sort(names.begin(),names.end(),naturalSortLessThanCI);
_currentName = names[_coverPage-1];
}
delete _7z;
attributes.clear();
_currentName = QDir::fromNativeSeparators(_currentName).split('/').last(); //separator fixed.
#ifdef Q_OS_WIN32
attributes << "e" << "-so" << "-r" << _fileSource << QString(_currentName.toLocal8Bit().constData()); //TODO platform dependency?? OEM 437
#else
attributes << "e" << "-so" << "-r" << _fileSource << _currentName; //TODO platform dependency?? OEM 437
#endif
_7z = new QProcess();
connect(_7z,SIGNAL(error(QProcess::ProcessError)),this,SIGNAL(openingError(QProcess::ProcessError)));
//connect(_7z,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(writeThumbnail(void)));
_7z->start(QCoreApplication::applicationDirPath()+"/utils/7z",attributes);
_7z->waitForFinished(60000);
QByteArray image = _7z->readAllStandardOutput();
QString error = _7z->readAllStandardError();
QImage p;
if(_target=="")
{
if(!_cover.loadFromData(image))
_cover.load(":/images/notCover.png");
}
else
{
if(p.loadFromData(image))
{
QImage scaled;
if(p.width()>p.height()) //landscape??
scaled = p.scaledToWidth(640,Qt::SmoothTransformation);
else
scaled = p.scaledToWidth(480,Qt::SmoothTransformation);
scaled.save(_target,0,75);
}
else
{
p.load(":/images/notCover.png");
p.save(_target);
}
}
delete _7z;
}
}

View File

@ -0,0 +1,86 @@
#ifndef __LIBRARY_CREATOR_H
#define __LIBRARY_CREATOR_H
#include <QObject>
#include <QString>
#include <QDir>
#include <QFile>
#include <QByteArray>
#include <QRegExp>
#include <QProcess>
#include <QtCore>
#include <QtGui>
#include <QMutex>
#include <QThread>
#include <QSqlDatabase>
#include "folder.h"
#include "comic_db.h"
class LibraryCreator : public QThread
{
Q_OBJECT
public:
LibraryCreator();
void createLibrary(const QString & source, const QString & target);
void updateLibrary(const QString & source, const QString & target);
void stop();
private:
void processLibrary(const QString & source, const QString & target);
enum Mode {CREATOR,UPDATER};
//atributos "globales" durante el proceso de creaci<63>n y actualizaci<63>n
enum Mode _mode;
QString _source;
QString _target;
QStringList _nameFilter;
QSqlDatabase _database;
QList<Folder> _currentPathFolders; //lista de folders en el orden en el que est<73>n siendo explorados, el <20>ltimo es el folder actual
//recursive method
void create(QDir currentDirectory);
void update(QDir currentDirectory);
void run();
qulonglong insertFolders();//devuelve el id del <20>ltimo folder a<>adido (<28>ltimo en la ruta)
bool checkCover(const QString & hash);
void insertComic(const QString & relativePath,const QFileInfo & fileInfo);
//qulonglong insertFolder(qulonglong parentId,const Folder & folder);
//qulonglong insertComic(const Comic & comic);
bool stopRunning;
//LibraryCreator est<73> en modo creaci<63>n si creation == true;
bool creation;
signals:
void finished();
void coverExtracted(QString);
void folderUpdated(QString);
void comicAdded(QString,QString);
void updated();
void created();
void failedCreatingDB(QString);
void failedOpeningDB(QString);
};
class ThumbnailCreator : public QObject
{
Q_OBJECT
public:
ThumbnailCreator(QString fileSource, QString target, int coverPage = 1);
private:
QProcess * _7z;
QString _fileSource;
QString _target;
QString _currentName;
int _numPages;
QPixmap _cover;
int _coverPage;
public slots:
void create();
int getNumPages(){return _numPages;};
QPixmap getCover(){return _cover;};
signals:
void openingError(QProcess::ProcessError error);
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,256 @@
#ifndef __LIBRARYWINDOW_H
#define __LIBRARYWINDOW_H
#include <QMainWindow>
#include <QMap>
#include <QModelIndex>
#include <QFileInfo>
class QTreeView;
class QDirModel;
class QAction;
class QToolBar;
class QComboBox;
class QThread;
class QStackedWidget;
class YACReaderSearchLineEdit;
class CreateLibraryDialog;
class UpdateLibraryDialog;
class ExportLibraryDialog;
class ImportLibraryDialog;
class ExportComicsInfoDialog;
class ImportComicsInfoDialog;
class AddLibraryDialog;
class LibraryCreator;
class HelpAboutDialog;
class RenameLibraryDialog;
class PropertiesDialog;
class PackageManager;
class ComicFlowWidget;
class QCheckBox;
class QPushButton;
class TableModel;
class QSplitter;
class TreeItem;
class TreeModel;
class QItemSelectionModel;
class QString;
class QLabel;
class NoLibrariesWidget;
class OptionsDialog;
class ServerConfigDialog;
class QCloseEvent;
class ImportWidget;
class QSettings;
class LibraryItem;
class YACReaderTableView;
class YACReaderSideBar;
class YACReaderLibraryListWidget;
class YACReaderTreeView;
class YACReaderMainToolBar;
#include "comic_db.h"
class LibraryWindow : public QMainWindow
{
Q_OBJECT
private:
YACReaderSideBar * sideBar;
QSplitter * sVertical;
CreateLibraryDialog * createLibraryDialog;
UpdateLibraryDialog * updateLibraryDialog;
ExportLibraryDialog * exportLibraryDialog;
ImportLibraryDialog * importLibraryDialog;
ExportComicsInfoDialog * exportComicsInfoDialog;
ImportComicsInfoDialog * importComicsInfoDialog;
AddLibraryDialog * addLibraryDialog;
LibraryCreator * libraryCreator;
HelpAboutDialog * had;
RenameLibraryDialog * renameLibraryDialog;
PropertiesDialog * propertiesDialog;
//YACReaderSocialDialog * socialDialog;
bool fullscreen;
bool importedCovers; //if true, the library is read only (not updates,open comic or properties)
bool fromMaximized;
//Ya no se usan proxies, el rendimiento de la BD es suficiente
//YACReaderTreeSearch * proxyFilter;
//YACReaderSortComics * proxySort;
PackageManager * packageManager;
ComicFlowWidget * comicFlow;
QSize slideSizeW;
QSize slideSizeF;
//search filter
YACReaderSearchLineEdit * foldersFilter;
TreeItem * index; //index al que hay que hacer scroll despu<70>s de pulsar sobre un folder filtrado
int column;
QString previousFilter;
QPushButton * clearFoldersFilter;
QCheckBox * includeComicsCheckBox;
//-------------
QWidget *comics;
YACReaderTableView * comicView;
YACReaderTreeView * foldersView;
YACReaderLibraryListWidget * selectedLibrary;
TreeModel * dm;
QItemSelectionModel * sm;
TableModel * dmCV;
//QStringList paths;
QMap<QString,QString> libraries;
QLabel * fullScreenToolTip;
QStackedWidget * mainWidget;
NoLibrariesWidget * noLibrariesWidget;
ImportWidget * importWidget;
bool fetching;
int i;
QAction * backAction;
QAction * fordwardAction;
QAction * openComicAction;
QAction * showPropertiesAction;
QAction * createLibraryAction;
QAction * openLibraryAction;
QAction * exportComicsInfo;
QAction * importComicsInfo;
QAction * exportLibraryAction;
QAction * importLibraryAction;
QAction * updateLibraryAction;
QAction * removeLibraryAction;
QAction * helpAboutAction;
QAction * renameLibraryAction;
QAction * toggleFullScreenAction;
QAction * optionsAction;
QAction * serverConfigAction;
//QAction * socialAction;
//tree actions
QAction * setRootIndexAction;
QAction * expandAllNodesAction;
QAction * colapseAllNodesAction;
QAction * openContainingFolderAction;
QAction * openContainingFolderComicAction;
QAction * setAsReadAction;
QAction * setAsNonReadAction;
QAction * setAllAsReadAction;
QAction * setAllAsNonReadAction;
QAction * showHideMarksAction;
//edit info actions
QAction * selectAllComicsAction;
QAction * editSelectedComicsAction;
QAction * asignOrderActions;
QAction * forceConverExtractedAction;
QAction * deleteComicsAction;
QAction * hideComicViewAction;
#ifdef Q_OS_MAC
QToolBar * libraryToolBar;
#else
YACReaderMainToolBar * libraryToolBar;
#endif
QToolBar * treeActions;
QToolBar * comicsToolBar;
QToolBar * editInfoToolBar;
OptionsDialog * optionsDialog;
ServerConfigDialog * serverConfigDialog;
QString libraryPath;
QString comicsPath;
QString _lastAdded;
QString _sourceLastAdded;
QModelIndex _rootIndex;
QModelIndex _rootIndexCV;
quint64 _comicIdEdited;
void setupUI();
void createActions();
void createToolBars();
void createMenus();
void createConnections();
void doLayout();
void doDialogs();
void doModels();
void disableAllActions();
void disableActions();
void enableActions();
void enableLibraryActions();
QString currentPath();
//settings
QSettings * settings;
protected:
virtual void closeEvent ( QCloseEvent * event );
public:
LibraryWindow();
public slots:
void loadLibrary(const QString & path);
void loadCovers(const QModelIndex & mi);
void checkEmptyFolder(QStringList * paths = 0);
void reloadCovers();
void centerComicFlow(const QModelIndex & mi);
void updateComicView(int i);
void openComic();
void createLibrary();
void create(QString source,QString dest, QString name);
void showAddLibrary();
void openLibrary(QString path, QString name);
void loadLibraries();
void saveLibraries();
void reloadCurrentLibrary();
void openLastCreated();
void updateLibrary();
//void deleteLibrary();
void openContainingFolder();
void openContainingFolderComic();
void deleteCurrentLibrary();
void removeLibrary();
void renameLibrary();
void rename(QString newName);
void cancelCreating();
void stopLibraryCreator();
void setRootIndex();
void toggleFullScreen();
void toNormal();
void toFullScreen();
void setFoldersFilter(QString filter);
void showProperties();
void exportLibrary(QString destPath);
void importLibrary(QString clc,QString destPath,QString name);
void reloadOptions();
void setCurrentComicsStatusReaded(bool readed);
void setCurrentComicReaded();
void setCurrentComicUnreaded();
void setComicsReaded();
void setComicsUnreaded();
void hideComicFlow(bool hide);
void showExportComicsInfo();
void showImportComicsInfo();
void asignNumbers();
void showNoLibrariesWidget();
void showRootWidget();
void showImportingWidget();
void manageCreatingError(const QString & error);
void manageUpdatingError(const QString & error);
void manageOpeningLibraryError(const QString & error);
QModelIndexList getSelectedComics();
void deleteComics();
//void showSocial();
};
#endif

59
YACReaderLibrary/main.cpp Normal file
View File

@ -0,0 +1,59 @@
#include "library_window.h"
#include <QApplication>
#include <QTranslator>
#include <QSettings>
#include <QLocale>
#include "yacreader_global.h"
#include "startup.h"
#include "yacreader_local_server.h"
#define PICTUREFLOW_QT4 1
//interfaz al servidor
Startup * s;
int main( int argc, char ** argv )
{
QApplication app( argc, argv );
QTranslator translator;
QString sufix = QLocale::system().name();
translator.load(QCoreApplication::applicationDirPath()+"/languages/yacreaderlibrary_"+sufix);
app.installTranslator(&translator);
app.setApplicationName("YACReaderLibrary");
#ifdef SERVER_RELEASE
QSettings * settings = new QSettings(QCoreApplication::applicationDirPath()+"/YACReaderLibrary.ini",QSettings::IniFormat); //TODO unificar la creaci<63>n del fichero de config con el servidor
settings->beginGroup("libraryConfig");
s = new Startup();
if(settings->value(SERVER_ON,true).toBool())
{
s->start();
}
#endif
if(YACReaderLocalServer::isRunning()) //s<>lo se permite una instancia de YACReaderLibrary
return 0;
YACReaderLocalServer * localServer = new YACReaderLocalServer();
LibraryWindow * mw = new LibraryWindow();
//connections to localServer
mw->show();
/*mw->resize(800,480);
mw->showMaximized();*/
int ret = app.exec();
//server shutdown
s->stop();
delete s;
return ret;
}

View File

@ -0,0 +1,80 @@
#include "no_libraries_widget.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
NoLibrariesWidget::NoLibrariesWidget(QWidget *parent) :
QWidget(parent)
{
setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
QPalette p(palette());
p.setColor(QPalette::Background, QColor(250,250,250));
setAutoFillBackground(true);
setPalette(p);
QPixmap icon(":/images/noLibrariesIcon.png");
QLabel * iconLabel = new QLabel();
iconLabel->setPixmap(icon);
QPixmap line(":/images/noLibrariesLine.png");
QLabel * lineLabel = new QLabel();
lineLabel->setPixmap(line);
QLabel * text = new QLabel("<font color=\"#495252\">"+tr("You don't have any librarires yet")+"</font>");
text->setStyleSheet("QLabel {font-size:25px;font-weight:bold;}");
QLabel * textDescription = new QLabel("<font color=\"#565959\">"+tr("<p>You can create a library in any folder, YACReaderLibrary will import all comics and folders from this folder. If you have created any library in the past you can open them.</p><p>Don't forget that you can use YACReader as a stand alone application for reading the comics on your computer.</p>")+"</font>");
textDescription->setWordWrap(true);
textDescription->setMaximumWidth(330);
QPushButton * createButton = new QPushButton(tr("create your first library"));
createButton->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
QPushButton * addButton = new QPushButton(tr("add an existing one"));
addButton->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
QVBoxLayout * layout = new QVBoxLayout(this);
QHBoxLayout * buttonLayout = new QHBoxLayout();
QHBoxLayout * topLayout = new QHBoxLayout();
QVBoxLayout * textLayout = new QVBoxLayout();
QWidget * topWidget = new QWidget();
topWidget->setFixedWidth(650);
textLayout->addStretch();
textLayout->addWidget(text);
textLayout->addSpacing(12);
textLayout->addWidget(textDescription);
textLayout->addStretch();
topLayout->addStretch();
topLayout->addWidget(iconLabel,0,Qt::AlignVCenter);
topLayout->addSpacing(30);
topLayout->addLayout(textLayout,1);
topLayout->addStretch();
topLayout->setMargin(0);
topWidget->setLayout(topLayout);
layout->setAlignment(Qt::AlignHCenter);
buttonLayout->addSpacing(125);
buttonLayout->addWidget(createButton);
layout->addSpacing(25);
buttonLayout->addWidget(addButton);
buttonLayout->addSpacing(125);
layout->addStretch();
layout->addWidget(topWidget);
layout->addSpacing(20);
layout->addWidget(lineLabel,0,Qt::AlignHCenter);
layout->addSpacing(10);
layout->addLayout(buttonLayout,0);
layout->addSpacing(150);
layout->addStretch();
connect(createButton,SIGNAL(clicked()),this,SIGNAL(createNewLibrary()));
connect(addButton,SIGNAL(clicked()),this,SIGNAL(addExistingLibrary()));
}

View File

@ -0,0 +1,19 @@
#ifndef NO_LIBRARIES_WIDGET_H
#define NO_LIBRARIES_WIDGET_H
#include <QWidget>
class NoLibrariesWidget : public QWidget
{
Q_OBJECT
public:
explicit NoLibrariesWidget(QWidget *parent = 0);
signals:
void createNewLibrary();
void addExistingLibrary();
public slots:
};
#endif // NO_LIBRARIES_WIDGET_H

View File

@ -0,0 +1,52 @@
#include "options_dialog.h"
#include "yacreader_flow_gl.h"
#include "yacreader_flow_config_widget.h"
#include "yacreader_gl_flow_config_widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFileDialog>
#include <QGroupBox>
#include <QRadioButton>
#include <QTextStream>
#include <QCoreApplication>
#include <QFile>
#include <QMessageBox>
#include <QCheckBox>
FlowType flowType = Strip;
OptionsDialog::OptionsDialog(QWidget * parent)
:YACReaderOptionsDialog(parent)
{
QVBoxLayout * layout = new QVBoxLayout;
QHBoxLayout * switchFlowType = new QHBoxLayout;
switchFlowType->addStretch();
switchFlowType->addWidget(useGL);
QHBoxLayout * buttons = new QHBoxLayout();
buttons->addStretch();
buttons->addWidget(accept);
buttons->addWidget(cancel);
layout->addWidget(sw);
layout->addWidget(gl);
layout->addLayout(switchFlowType);
layout->addLayout(buttons);
sw->hide();
setLayout(layout);
//restoreOptions(settings); //load options
//resize(200,0);
setModal (true);
setWindowTitle(tr("Options"));
this->layout()->setSizeConstraint(QLayout::SetFixedSize);
}

View File

@ -0,0 +1,18 @@
#ifndef __OPTIONS_DIALOG_H
#define __OPTIONS_DIALOG_H
#include "yacreader_options_dialog.h"
#include "yacreader_global.h"
extern FlowType flowType;
class OptionsDialog : public YACReaderOptionsDialog
{
Q_OBJECT
public:
OptionsDialog(QWidget * parent = 0);
};
#endif

View File

@ -0,0 +1,47 @@
#include "package_manager.h"
#include <QCoreApplication>
PackageManager::PackageManager()
:_7z(0)
{
}
void PackageManager::createPackage(const QString & libraryPath,const QString & dest)
{
QStringList attributes;
attributes << "a" << "-y" << "-ttar" << dest+".clc" << libraryPath ;
_7z = new QProcess();
connect(_7z,SIGNAL(error(QProcess::ProcessError)),this,SLOT(openingError(QProcess::ProcessError)));
connect(_7z,SIGNAL(finished(int,QProcess::ExitStatus)),this,SIGNAL(exported()));
_7z->start(QCoreApplication::applicationDirPath()+"/utils/7z",attributes);
}
void PackageManager::extractPackage(const QString & packagePath,const QString & destDir)
{
QStringList attributes;
QString output = "-o";
output += destDir;
attributes << "x" << "-y" << output << packagePath;
_7z = new QProcess();
connect(_7z,SIGNAL(error(QProcess::ProcessError)),this,SLOT(openingError(QProcess::ProcessError)));
connect(_7z,SIGNAL(finished(int,QProcess::ExitStatus)),this,SIGNAL(imported()));
_7z->start(QCoreApplication::applicationDirPath()+"/utils/7z",attributes);
}
void PackageManager::cancel()
{
if(_7z!=0)
{
_7z->disconnect();
_7z->kill();
if(creating)
{
//TODO remove dest+".clc"
}
else
{
//TODO fixed: is done by libraryWindow
}
}
}

View File

@ -0,0 +1,24 @@
#ifndef PACKAGE_MANAGER_H
#define PACKAGE_MANAGER_H
#include <QProcess>
class PackageManager : public QObject
{
Q_OBJECT
public:
PackageManager();
void createPackage(const QString & libraryPath,const QString & dest);
void extractPackage(const QString & packagePath,const QString & destDir);
public slots:
void cancel();
private:
bool creating;
QProcess * _7z;
signals:
void exported();
void imported();
};
#endif

View File

@ -0,0 +1,697 @@
#include "properties_dialog.h"
#include "data_base_management.h"
#include "library_creator.h"
#include "yacreader_field_edit.h"
#include "yacreader_field_plain_text_edit.h"
#include "db_helper.h"
#include <QHBoxLayout>
#include <QApplication>
#include <QDesktopWidget>
#include <QSizePolicy>
#include <QFormLayout>
#include <QCheckBox>
#include <QTabWidget>
#include <QIntValidator>
PropertiesDialog::PropertiesDialog(QWidget * parent)
:QDialog(parent)
{
createCoverBox();
createGeneralInfoBox();
createAuthorsBox();
createPublishingBox();
createButtonBox();
createPlotBox();
createTabBar();
mainLayout = new QGridLayout;
mainLayout->addWidget(coverBox,0,0);
mainLayout->addWidget(tabBar,0,1);
mainLayout->setColumnStretch(1,1);
/*mainLayout->addWidget(authorsBox,1,1);
mainLayout->addWidget(publishingBox,2,1);*/
mainLayout->addWidget(buttonBox,1,1,Qt::AlignBottom);
this->setLayout(mainLayout);
mainLayout->setSizeConstraint(QLayout::SetMinimumSize);
int heightDesktopResolution = QApplication::desktop()->screenGeometry().height();
int widthDesktopResolution = QApplication::desktop()->screenGeometry().width();
int sHeight,sWidth;
sHeight = static_cast<int>(heightDesktopResolution*0.65);
sWidth = static_cast<int>(sHeight*1.4);
setCover(QPixmap(":/images/notCover.png"));
this->resize(sWidth,this->height());
this->move(QPoint((widthDesktopResolution-sWidth)/2,((heightDesktopResolution-sHeight)-40)/2));
setModal(true);
repaint();
}
void PropertiesDialog::createTabBar()
{
tabBar = new QTabWidget;
tabBar->addTab(generalInfoBox,tr("General info"));
tabBar->addTab(authorsBox,tr("Authors"));
tabBar->addTab(publishingBox,tr("Publishing"));
tabBar->addTab(plotBox,tr("Plot"));
}
void PropertiesDialog::createCoverBox()
{
coverBox = new QGroupBox(tr("Cover"));
sa = new QScrollArea();
cover = new QLabel();
cover->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
cover->setScaledContents(false);
cover->setAlignment(Qt::AlignTop|Qt::AlignHCenter);
/*sa->setWidget(cover);
sa->setBackgroundRole(QPalette::Dark);
sa->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
sa->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
sa->setFrameStyle(QFrame::NoFrame);
sa->setAlignment(Qt::AlignCenter);*/
QVBoxLayout * coverLayout = new QVBoxLayout();
coverLayout->addWidget(cover);
QHBoxLayout * coverPageLayout = new QHBoxLayout;
coverPageLayout->addWidget(new QLabel(tr("Cover page : ")));
coverPageLayout->addWidget(coverPageEdit = new YACReaderFieldEdit());
coverPageLayout->setStretch(1,0);
coverLayout->addLayout(coverPageLayout);
coverBox->setLayout(coverLayout);
}
QFrame * createLine()
{
QFrame * line = new QFrame();
line->setObjectName(QString::fromUtf8("line"));
//line->setGeometry(QRect(320, 150, 118, 3));
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
return line;
}
void PropertiesDialog::createGeneralInfoBox()
{
generalInfoBox = new QWidget;
QFormLayout *generalInfoLayout = new QFormLayout;
//generalInfoLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
generalInfoLayout->addRow(tr("Title:"), title = new YACReaderFieldEdit());
QHBoxLayout * number = new QHBoxLayout;
number->addWidget(numberEdit = new YACReaderFieldEdit());
numberValidator.setBottom(0);
numberEdit->setValidator(&numberValidator);
number->addWidget(new QLabel("Bis:"));
number->addWidget(isBisCheck = new QCheckBox());
number->addWidget(new QLabel("of:"));
number->addWidget(countEdit = new YACReaderFieldEdit());
countValidator.setBottom(0);
countEdit->setValidator(&countValidator);
number->addStretch(1);
/*generalInfoLayout->addRow(tr("&Issue number:"), );
generalInfoLayout->addRow(tr("&Bis:"), );*/
generalInfoLayout->addRow(tr("Issue number:"), number);
generalInfoLayout->addRow(tr("Volume:"), volumeEdit = new YACReaderFieldEdit());
QHBoxLayout * arc = new QHBoxLayout;
arc->addWidget(storyArcEdit = new YACReaderFieldEdit());
arc->addWidget(new QLabel("Arc number:"));
arc->addWidget(arcNumberEdit = new YACReaderFieldEdit());
arcNumberValidator.setBottom(0);
arcNumberEdit->setValidator(&arcNumberValidator);
arc->addWidget(new QLabel("of:"));
arc->addWidget(arcCountEdit = new YACReaderFieldEdit());
arcCountValidator.setBottom(0);
arcCountEdit->setValidator(&arcCountValidator);
arc->addStretch(1);
generalInfoLayout->addRow(tr("Story arc:"), arc);
generalInfoLayout->addRow(tr("Genere:"), genereEdit = new YACReaderFieldEdit());
generalInfoLayout->addRow(tr("Size:"), size = new QLabel("size"));
generalInfoBox->setLayout(generalInfoLayout);
}
void PropertiesDialog::createAuthorsBox()
{
authorsBox = new QWidget;
QVBoxLayout *authorsLayout = new QVBoxLayout;
//authorsLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
QHBoxLayout * h1 = new QHBoxLayout;
QVBoxLayout * vl1 = new QVBoxLayout;
QVBoxLayout * vr1 = new QVBoxLayout;
vl1->addWidget(new QLabel(tr("Writer(s):")));
vl1->addWidget(writer = new YACReaderFieldPlainTextEdit());
h1->addLayout(vl1);
vr1->addWidget(new QLabel(tr("Penciller(s):")));
vr1->addWidget(penciller = new YACReaderFieldPlainTextEdit());
h1->addLayout(vr1);
//authorsLayout->addRow(tr("Writer(s):"), new YACReaderFieldPlainTextEdit());
//authorsLayout->addRow(tr("Penciller(s):"), new YACReaderFieldPlainTextEdit());
QHBoxLayout * h2 = new QHBoxLayout;
QVBoxLayout * vl2 = new QVBoxLayout;
QVBoxLayout * vr2 = new QVBoxLayout;
vl2->addWidget(new QLabel(tr("Inker(s):")));
vl2->addWidget(inker = new YACReaderFieldPlainTextEdit());
h2->addLayout(vl2);
vr2->addWidget(new QLabel(tr("Colorist(s):")));
vr2->addWidget(colorist = new YACReaderFieldPlainTextEdit());
h2->addLayout(vr2);
//authorsLayout->addRow(tr("Inker(s):"), new YACReaderFieldPlainTextEdit());
//authorsLayout->addRow(tr("Colorist(s):"), new YACReaderFieldPlainTextEdit());
QHBoxLayout * h3 = new QHBoxLayout;
QVBoxLayout * vl3 = new QVBoxLayout;
QVBoxLayout * vr3 = new QVBoxLayout;
vl3->addWidget(new QLabel(tr("Letterer(s):")));
vl3->addWidget(letterer = new YACReaderFieldPlainTextEdit());
h3->addLayout(vl3);
vr3->addWidget(new QLabel(tr("Cover Artist(s):")));
vr3->addWidget(coverArtist = new YACReaderFieldPlainTextEdit());
h3->addLayout(vr3);
//authorsLayout->addRow(tr("Letterer(es):"), new YACReaderFieldPlainTextEdit());
//authorsLayout->addRow(tr("Cover Artist(s):"), new YACReaderFieldPlainTextEdit());
authorsLayout->addLayout(h1);
authorsLayout->addLayout(h2);
authorsLayout->addLayout(h3);
authorsLayout->addStretch(1);
authorsBox->setLayout(authorsLayout);
}
void PropertiesDialog::createPublishingBox()
{
publishingBox = new QWidget;
QFormLayout *publishingLayout = new QFormLayout;
QHBoxLayout * date = new QHBoxLayout;
date->addWidget(new QLabel(tr("Day:")));
date->addWidget(dayEdit = new YACReaderFieldEdit());
dayValidator.setRange(1,31);
dayEdit->setValidator(&dayValidator);
date->addWidget(new QLabel(tr("Month:")));
date->addWidget(monthEdit = new YACReaderFieldEdit());
monthValidator.setRange(1,12);
monthEdit->setValidator(&monthValidator);
date->addWidget(new QLabel(tr("Year:")));
date->addWidget(yearEdit = new YACReaderFieldEdit());
yearValidator.setRange(1,9999);
yearEdit->setValidator(&yearValidator);
date->addStretch(1);
publishingLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
publishingLayout->addRow(date);
publishingLayout->addRow(tr("Publisher:"), publisherEdit = new YACReaderFieldEdit());
publishingLayout->addRow(tr("Format:"), formatEdit = new YACReaderFieldEdit());
publishingLayout->addRow(tr("Color/BW:"), colorCheck = new QCheckBox());
publishingLayout->addRow(tr("Age rating:"), ageRatingEdit = new YACReaderFieldEdit());
publishingBox->setLayout(publishingLayout);
}
void PropertiesDialog::createPlotBox()
{
plotBox = new QWidget;
QFormLayout *plotLayout = new QFormLayout;
plotLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
plotLayout->addRow(tr("Synopsis:"), synopsis = new YACReaderFieldPlainTextEdit());
plotLayout->addRow(tr("Characters:"), characters = new YACReaderFieldPlainTextEdit());
plotLayout->addRow(tr("Notes:"), notes = new YACReaderFieldPlainTextEdit());
plotBox->setLayout(plotLayout);
}
void PropertiesDialog::createButtonBox()
{
buttonBox = new QDialogButtonBox;
closeButton = buttonBox->addButton(QDialogButtonBox::Close);
saveButton = buttonBox->addButton(QDialogButtonBox::Save);
//rotateWidgetsButton = buttonBox->addButton(tr("Rotate &Widgets"),QDialogButtonBox::ActionRole);
//connect(rotateWidgetsButton, SIGNAL(clicked()), this, SLOT(rotateWidgets()));
connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
connect(saveButton, SIGNAL(clicked()), this, SLOT(save()));
}
void PropertiesDialog::setComics(QList<ComicDB> comics)
{
this->comics = comics;
ComicDB comic = comics.at(0);
if(comic.info.title != NULL)
title->setText(*comic.info.title);
if(comic.info.coverPage != NULL)
{
coverPageEdit->setText(QString::number(*comic.info.coverPage));
coverPageValidator.setRange(1,*comic.info.numPages);
coverPageEdit->setValidator(&coverPageValidator);
}
/*if(comic.info.numPages != NULL)
numPagesEdit->setText(QString::number(*comic.info.numPages));*/
if(comic.info.number != NULL)
numberEdit->setText(QString::number(*comic.info.number));
if(comic.info.isBis != NULL)
isBisCheck->setChecked(*comic.info.isBis);
if(comic.info.count != NULL)
countEdit->setText(QString::number(*comic.info.count));
if(comic.info.volume != NULL)
volumeEdit->setText(*comic.info.volume);
if(comic.info.storyArc != NULL)
storyArcEdit->setText(*comic.info.storyArc);
if(comic.info.arcNumber != NULL)
arcNumberEdit->setText(QString::number(*comic.info.arcNumber));
if(comic.info.arcCount != NULL)
arcCountEdit->setText(QString::number(*comic.info.arcCount));
if(comic.info.genere != NULL)
genereEdit->setText(*comic.info.genere);
if(comic.info.writer != NULL)
writer->setPlainText(*comic.info.writer);
if(comic.info.penciller != NULL)
penciller->setPlainText(*comic.info.penciller);
if(comic.info.inker != NULL)
inker->setPlainText(*comic.info.inker);
if(comic.info.colorist != NULL)
colorist->setPlainText(*comic.info.colorist);
if(comic.info.letterer != NULL)
letterer->setPlainText(*comic.info.letterer);
if(comic.info.coverArtist != NULL)
coverArtist->setPlainText(*comic.info.coverArtist);
size->setText(QString::number(comic.info.hash.right(comic.info.hash.length()-40).toInt()/1024.0/1024.0,'f',2)+"Mb");
if(comic.info.date != NULL)
{
QStringList date = (*comic.info.date).split("/");
dayEdit->setText(date[0]);
monthEdit->setText(date[1]);
yearEdit->setText(date[2]);
}
if(comic.info.publisher != NULL)
publisherEdit->setText(*comic.info.publisher);
if(comic.info.format != NULL)
formatEdit->setText(*comic.info.format);
if(comic.info.color != NULL)
colorCheck->setChecked(*comic.info.color);
else
colorCheck->setCheckState(Qt::PartiallyChecked);
if(comic.info.ageRating != NULL)
ageRatingEdit->setText(*comic.info.ageRating);
if(comic.info.synopsis != NULL)
synopsis->setPlainText(*comic.info.synopsis);
if(comic.info.characters != NULL)
characters->setPlainText(*comic.info.characters);
if(comic.info.notes != NULL)
notes->setPlainText(*comic.info.notes);
if(comics.length() > 1)
{
setDisableUniqueValues(true);
this->setWindowTitle(tr("Edit selected comics information"));
setCover(QPixmap(":/images/editComic.png"));
QList<ComicDB>::iterator itr;
for(itr = ++comics.begin();itr!=comics.end();itr++)
{
if(itr->info.title == NULL || *(itr->info.title) != title->text())
title->clear();
if(itr->info.count == NULL || *(itr->info.count) != countEdit->text().toInt())
countEdit->clear();
if(itr->info.volume == NULL || *(itr->info.volume) != volumeEdit->text())
volumeEdit->clear();
if(itr->info.storyArc == NULL || *(itr->info.storyArc) != storyArcEdit->text())
storyArcEdit->clear();
if(itr->info.arcCount == NULL || *(itr->info.arcCount) != storyArcEdit->text().toInt())
arcCountEdit->clear();
if(itr->info.genere == NULL || *(itr->info.genere) != genereEdit->text())
genereEdit->clear();
if(itr->info.writer == NULL || *(itr->info.writer) != writer->toPlainText())
writer->clear();
if(itr->info.penciller == NULL || *(itr->info.penciller) != penciller->toPlainText())
penciller->clear();
if(itr->info.inker == NULL || *(itr->info.inker) != inker->toPlainText())
inker->clear();
if(itr->info.colorist == NULL || *(itr->info.colorist) != colorist->toPlainText())
colorist->clear();
if(itr->info.letterer == NULL || *(itr->info.letterer) != letterer->toPlainText())
letterer->clear();
if(itr->info.coverArtist == NULL || *(itr->info.coverArtist) != coverArtist->toPlainText())
coverArtist->clear();
if(itr->info.date == NULL)
{
dayEdit->clear();
monthEdit->clear();
yearEdit->clear();
}
else
{
QStringList date = itr->info.date->split("/");
if(dayEdit->text() != date[0])
dayEdit->clear();
if(monthEdit->text() != date[1])
monthEdit->clear();
if(yearEdit->text() != date[2])
yearEdit->clear();
}
if(itr->info.publisher == NULL || *(itr->info.publisher) != publisherEdit->text())
publisherEdit->clear();
if(itr->info.format == NULL || *(itr->info.format) != formatEdit->text())
formatEdit->clear();
if(itr->info.color == NULL || *(itr->info.color) != colorCheck->isChecked())
colorCheck->setCheckState(Qt::PartiallyChecked);
if(itr->info.ageRating == NULL || *(itr->info.ageRating) != ageRatingEdit->text())
ageRatingEdit->clear();
if(itr->info.synopsis == NULL || *(itr->info.synopsis) != synopsis->toPlainText())
synopsis->clear();
if(itr->info.characters == NULL || *(itr->info.characters) != characters->toPlainText())
characters->clear();
if(itr->info.notes == NULL || *(itr->info.notes) != notes->toPlainText())
notes->clear();
}
}
else
{
this->setWindowTitle(tr("Edit comic information"));
setCover(comic.info.getCover(basePath));
}
}
void PropertiesDialog::updateComics()
{
QSqlDatabase db = DataBaseManagement::loadDatabase(databasePath);
db.open();
db.transaction();
QList<ComicDB>::iterator itr;
for(itr = comics.begin();itr!=comics.end();itr++)
{
if(itr->info.edited)
DBHelper::update(&(itr->info),db);
}
db.commit();
db.close();
QSqlDatabase::removeDatabase(databasePath);
}
//Deprecated
void PropertiesDialog::setCover(const QPixmap & coverImage)
{
cover->setPixmap(coverImage.scaledToWidth(125,Qt::SmoothTransformation));
//cover->repaint();
//float aspectRatio = (float)coverImage.width()/coverImage.height();
//int heightDesktopResolution = QApplication::desktop()->screenGeometry().height();
//int widthDesktopResolution = QApplication::desktop()->screenGeometry().width();
//int sHeight,sWidth;
//sHeight = static_cast<int>(heightDesktopResolution*0.65);
//if(aspectRatio<1)
//{
// sWidth = static_cast<int>(sHeight*1.4);
// //this->resize(sWidth,sHeight);
// this->move(QPoint((widthDesktopResolution-sWidth)/2,((heightDesktopResolution-sHeight)-40)/2));
// //sa->resize(sa->width(),sa->width()*1.333);
// /*cover->resize(static_cast<int>((sa->height())*aspectRatio),
// (sa->height()));*/
//}
//else
//{
// sWidth = static_cast<int>(sHeight/1.16);
// //this->resize(sWidth,sHeight);
// this->move(QPoint((widthDesktopResolution-sWidth)/2,((heightDesktopResolution-sHeight)-40)/2));
// cover->resize((width()-25),
// static_cast<int>((width()-25)/aspectRatio));
//}
}
void PropertiesDialog::setFilename(const QString & nameString)
{
title->setText(nameString);
}
void PropertiesDialog::setNumpages(int pagesNum)
{
numPagesEdit->setText(QString::number(pagesNum));
}
void PropertiesDialog::setSize(float sizeFloat)
{
size->setText(QString::number(sizeFloat,'f',2) + " MB");
}
void PropertiesDialog::save()
{
QList<ComicDB>::iterator itr;
for(itr = comics.begin();itr!=comics.end();itr++)
{
//Comic & comic = comics[0];
bool edited = false;
if(title->isModified())
{
itr->info.setTitle(title->text());
edited = true;
}
if(comics.size()==1)
if(coverPageEdit->isModified() && !coverPageEdit->text().isEmpty() && coverPageEdit->text().toInt() != 0)
{
itr->info.setCoverPage(coverPageEdit->text().toInt());
edited = true;
}
/*if(comic.info.numPages != NULL)
numPagesEdit->setText(QString::number(*comic.info.numPages));*/
if(comics.size()==1)
if(numberEdit->isModified() && !numberEdit->text().isEmpty())
{
itr->info.setNumber(numberEdit->text().toInt());
edited = true;
}
if(comics.size()==1)
if(itr->info.isBis != NULL || isBisCheck->isChecked())
{
itr->info.setIsBis(isBisCheck->isChecked());
edited = true;
}
if(countEdit->isModified())
{
itr->info.setCount(countEdit->text().toInt());
edited = true;
}
if(volumeEdit->isModified())
{
itr->info.setVolume(volumeEdit->text());
edited = true;
}
if(storyArcEdit->isModified())
{
itr->info.setStoryArc(storyArcEdit->text());
edited = true;
}
if(comics.size()==1)
if(arcNumberEdit->isModified() && !arcNumberEdit->text().isEmpty())
{
itr->info.setArcNumber(arcNumberEdit->text().toInt());
edited = true;
}
if(arcCountEdit->isModified())
{
itr->info.setArcCount(arcCountEdit->text().toInt());
edited = true;
}
if(genereEdit->isModified())
{
itr->info.setGenere(genereEdit->text());
edited = true;
}
if(writer->document()->isModified())
{
itr->info.setWriter(writer->toPlainText());
edited = true;
}
if(penciller->document()->isModified())
{
itr->info.setPenciller(penciller->toPlainText());
edited = true;
}
if(inker->document()->isModified())
{
itr->info.setInker(inker->toPlainText());
edited = true;
}
if(colorist->document()->isModified())
{
itr->info.setColorist(colorist->toPlainText());
edited = true;
}
if(letterer->document()->isModified())
{
itr->info.setLetterer(letterer->toPlainText());
edited = true;
}
if(coverArtist->document()->isModified())
{
itr->info.setCoverArtist(coverArtist->toPlainText());
edited = true;
}
if(dayEdit->isModified() || monthEdit->isModified() || yearEdit->isModified() )
{
itr->info.setDate(dayEdit->text()+"/"+monthEdit->text()+"/"+yearEdit->text());
edited = true;
}
if(publisherEdit->isModified())
{
itr->info.setPublisher(publisherEdit->text());
edited = true;
}
if(formatEdit->isModified())
{
itr->info.setFormat(formatEdit->text());
edited = true;
}
if(colorCheck->checkState() != Qt::PartiallyChecked)
{
itr->info.setColor(colorCheck->isChecked());
edited = true;
}
if(ageRatingEdit->isModified())
{
itr->info.setAgeRating(ageRatingEdit->text());
edited = true;
}
if(synopsis->document()->isModified())
{
itr->info.setSynopsis(synopsis->toPlainText());
edited = true;
}
if(characters->document()->isModified())
{
itr->info.setCharacters(characters->toPlainText());
edited = true;
}
if(notes->document()->isModified())
{
itr->info.setNotes(notes->toPlainText());
edited = true;
}
itr->info.edited = edited;
}
updateComics();
if(comics.count() == 1)
{
if(coverPageEdit->isModified())// && coverPageEdit->text().toInt() != *comics[0].info.coverPage)
{
ThumbnailCreator tc(basePath+comics[0].path,basePath+"/.yacreaderlibrary/covers/"+comics[0].info.hash+".jpg",*comics[0].info.coverPage);
tc.create();
}
}
close();
emit(accepted());
}
void PropertiesDialog::setDisableUniqueValues(bool disabled)
{
coverPageEdit->setDisabled(disabled);
coverPageEdit->clear();
numberEdit->setDisabled(disabled);
numberEdit->clear();
isBisCheck->setDisabled(disabled);
isBisCheck->setChecked(false);
arcNumberEdit->setDisabled(disabled);
arcNumberEdit->clear();
}
void PropertiesDialog::closeEvent ( QCloseEvent * e )
{
title->clear();
title->setModified(false);
coverPageEdit->clear();
// numPagesEdit->setText(QString::number(*comic.info.numPages));
numberEdit->clear();
isBisCheck->setChecked(false);
countEdit->clear();
volumeEdit->clear();
storyArcEdit->clear();
arcNumberEdit->clear();
arcCountEdit->clear();
genereEdit->clear();
writer->clear();
penciller->clear();
inker->clear();
colorist->clear();
letterer->clear();
coverArtist->clear();
dayEdit->clear();
monthEdit->clear();
yearEdit->clear();
publisherEdit->clear();
formatEdit->clear();
colorCheck->setCheckState(Qt::PartiallyChecked);
ageRatingEdit->clear();
synopsis->clear();
characters->clear();
notes->clear();
setDisableUniqueValues(false);
tabBar->setCurrentIndex(0);
coverPageEdit->setFocus();
QDialog::closeEvent(e);
}

View File

@ -0,0 +1,118 @@
#ifndef __PROPERTIES_DIALOG_H
#define __PROPERTIES_DIALOG_H
#include <QDialog>
#include <QIntValidator>
class QGridLayout;
class QTabWidget;
class QGroupBox;
class QLabel;
class QScrollArea;
class QWidget;
class YACReaderFieldEdit;
class YACReaderFieldPlainTextEdit;
class QDialogButtonBox;
class QCheckBox;
#include "comic_db.h"
class PropertiesDialog : public QDialog
{
Q_OBJECT
private:
QGridLayout * mainLayout;
QTabWidget * tabBar;
QGroupBox * coverBox;
QLabel * cover;
QScrollArea * sa;
QWidget * generalInfoBox;
YACReaderFieldEdit * title;
YACReaderFieldEdit * numPagesEdit;
QLabel * size;
YACReaderFieldEdit * coverPageEdit;
QIntValidator coverPageValidator;
YACReaderFieldEdit * numberEdit;
QIntValidator numberValidator;
QCheckBox * isBisCheck;
YACReaderFieldEdit * countEdit;
QIntValidator countValidator;
YACReaderFieldEdit * volumeEdit;
YACReaderFieldEdit * storyArcEdit;
YACReaderFieldEdit * arcNumberEdit;
QIntValidator arcNumberValidator;
YACReaderFieldEdit * arcCountEdit;
QIntValidator arcCountValidator;
YACReaderFieldEdit * genereEdit;
YACReaderFieldPlainTextEdit * writer;
YACReaderFieldPlainTextEdit * penciller;
YACReaderFieldPlainTextEdit * inker;
YACReaderFieldPlainTextEdit * colorist;
YACReaderFieldPlainTextEdit * letterer;
YACReaderFieldPlainTextEdit * coverArtist;
YACReaderFieldEdit * dayEdit;
QIntValidator dayValidator;
YACReaderFieldEdit * monthEdit;
QIntValidator monthValidator;
YACReaderFieldEdit * yearEdit;
QIntValidator yearValidator;
YACReaderFieldEdit * publisherEdit;
YACReaderFieldEdit * formatEdit;
QCheckBox * colorCheck;
YACReaderFieldEdit * ageRatingEdit;
YACReaderFieldPlainTextEdit * synopsis;
YACReaderFieldPlainTextEdit * characters;
YACReaderFieldPlainTextEdit * notes;
QWidget * authorsBox;
QWidget * publishingBox;
QWidget * plotBox;
QDialogButtonBox *buttonBox;
QPushButton *closeButton;
QPushButton *saveButton;
QPushButton *restoreButton; //??
void createTabBar();
void createCoverBox();
void createGeneralInfoBox();
void createAuthorsBox();
void createPublishingBox();
void createPlotBox();
void createButtonBox();
void setDisableUniqueValues(bool disabled);
QList<ComicDB> comics;
void closeEvent ( QCloseEvent * e );
public:
PropertiesDialog(QWidget * parent = 0);
QString databasePath;
QString basePath;
public slots:
void setComics(QList<ComicDB> comics);
void updateComics();
void save();
//Deprecated
void setCover(const QPixmap & cover);
void setFilename(const QString & name);
void setNumpages(int pages);
void setSize(float size);
};
#endif

View File

@ -0,0 +1,77 @@
#include "rename_library_dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileDialog>
RenameLibraryDialog::RenameLibraryDialog(QWidget * parent)
:QDialog(parent)
{
setupUI();
}
void RenameLibraryDialog::setupUI()
{
newNameLabel = new QLabel(tr("New Library Name : "));
newNameEdit = new QLineEdit;
newNameLabel->setBuddy(newNameEdit);
connect(newNameEdit,SIGNAL(textChanged(QString)),this,SLOT(nameSetted(QString)));
accept = new QPushButton(tr("Rename"));
accept->setDisabled(true);
connect(accept,SIGNAL(clicked()),this,SLOT(rename()));
cancel = new QPushButton(tr("Cancel"));
connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
QHBoxLayout *nameLayout = new QHBoxLayout;
nameLayout->addWidget(newNameLabel);
nameLayout->addWidget(newNameEdit);
QHBoxLayout *bottomLayout = new QHBoxLayout;
bottomLayout->addStretch();
bottomLayout->addWidget(accept);
bottomLayout->addWidget(cancel);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(nameLayout);
mainLayout->addStretch();
mainLayout->addLayout(bottomLayout);
QHBoxLayout * imgMainLayout = new QHBoxLayout;
QLabel * imgLabel = new QLabel(this);
QPixmap p(":/images/edit.png");
imgLabel->setPixmap(p);
imgMainLayout->addWidget(imgLabel);
imgMainLayout->addLayout(mainLayout);
setLayout(imgMainLayout);
setModal(true);
setWindowTitle(tr("Rename current library"));
}
void RenameLibraryDialog::rename()
{
//accept->setEnabled(false);
emit(renameLibrary(newNameEdit->text()));
close();
}
void RenameLibraryDialog::nameSetted(const QString & text)
{
if(!text.isEmpty())
accept->setEnabled(true);
else
accept->setEnabled(false);
}
void RenameLibraryDialog::close()
{
newNameEdit->clear();
//accept->setEnabled(false);
QDialog::close();
}

View File

@ -0,0 +1,31 @@
#ifndef __RENAME_LIBRARY_DIALOG_H
#define __RENAME_LIBRARY_DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
class RenameLibraryDialog : public QDialog
{
Q_OBJECT
public:
RenameLibraryDialog(QWidget * parent = 0);
private:
QLabel * newNameLabel;
QLineEdit * newNameEdit;
QPushButton * accept;
QPushButton * cancel;
void setupUI();
public slots:
void rename();
void close();
void nameSetted(const QString & name);
signals:
void renameLibrary(QString newName);
};
#endif

View File

@ -0,0 +1,84 @@
#include "comiccontroller.h"
#include "db_helper.h"
#include "template.h"
#include "../static.h"
#include "comic_db.h"
#include "comic.h"
#include <typeinfo>
ComicController::ComicController() {}
void ComicController::service(HttpRequest& request, HttpResponse& response)
{
HttpSession session=Static::sessionStore->getSession(request,response,false);
QString path = QUrl::fromPercentEncoding(request.getPath()).toLatin1();
QStringList pathElements = path.split('/');
QString libraryName = pathElements.at(2);
qulonglong comicId = pathElements.at(4).toULongLong();
//TODO
//if(pathElements.size() == 6)
//{
// QString action = pathElements.at(5);
// if(!action.isEmpty() && (action == "close"))
// {
// session.dismissCurrentComic();
// response.write("",true);
// return;
// }
//}
//Aplicar a todos los controladores
//TODO usar LibraryWindow para acceder a informaci<63>n de las bases de datos est<73> mal, hay
//que crear una clase que se encargue de estas cosas
//<2F>Se est<73> accediendo a la UI desde un hilo?
QMap<QString,QString> libraries = DBHelper::getLibraries();
ComicDB comic = DBHelper::getComicInfo(libraryName, comicId);
session.setDownloadedComic(comic.info.hash);
Comic * comicFile = FactoryComic::newComic(libraries.value(libraryName)+comic.path);
if(comicFile != NULL)
{
QThread * thread = NULL;
if (typeid(*comicFile) != typeid(FileComic))
{
thread = new QThread();
comicFile->moveToThread(thread);
connect(thread, SIGNAL(started()), comicFile, SLOT(process()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
}
comicFile->load(libraries.value(libraryName)+comic.path);
if(thread != NULL)
thread->start();
session.setCurrentComic(comic.id, comicFile);
response.setHeader("Content-Type", "plain/text; charset=ISO-8859-1");
response.writeText(QString("library:%1\r\n").arg(libraryName));
response.writeText(comic.toTXT(),true);
}
else
{
//delete comicFile;
response.setStatus(404,"not found");
response.write("404 not found",true);
}
//response.write(t.toLatin1(),true);
}

View File

@ -0,0 +1,23 @@
#ifndef COMICCONTROLLER_H
#define COMICCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
#include <QThread>
class Comic;
class QString;
class ComicController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(ComicController);
public:
/** Constructor */
ComicController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // COMICCONTROLLER_H

View File

@ -0,0 +1,63 @@
#include "covercontroller.h"
#include "db_helper.h" //get libraries
#include "template.h"
#include "../static.h"
CoverController::CoverController() {}
void CoverController::service(HttpRequest& request, HttpResponse& response)
{
HttpSession session=Static::sessionStore->getSession(request,response,false);
response.setHeader("Content-Type", "image/jpeg");
response.setHeader("Connection","close");
//response.setHeader("Content-Type", "plain/text; charset=ISO-8859-1");
QMap<QString,QString> libraries = DBHelper::getLibraries();
QString path = QUrl::fromPercentEncoding(request.getPath()).toLatin1();
QStringList pathElements = path.split('/');
QString libraryName = pathElements.at(2);
QString fileName = pathElements.at(4);
//response.writeText(path+"<br/>");
//response.writeText(libraryName+"<br/>");
//response.writeText(libraries.value(libraryName)+"/.yacreaderlibrary/covers/"+fileName+"<br/>");
//QFile file(libraries.value(libraryName)+"/.yacreaderlibrary/covers/"+fileName);
//if (file.exists()) {
// if (file.open(QIODevice::ReadOnly))
// {
// qDebug("StaticFileController: Open file %s",qPrintable(file.fileName()));
// // Return the file content, do not store in cache
// while (!file.atEnd() && !file.error()) {
// response.write(file.read(131072));
// }
// }
// file.close();
//}
QImage img(libraries.value(libraryName)+"/.yacreaderlibrary/covers/"+fileName);
if (!img.isNull()) {
int width = 80;
if(session.getDisplayType()=="retina")
width = 160;
img = img.scaledToWidth(width,Qt::SmoothTransformation);
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
img.save(&buffer, "JPG");
response.write(ba,true);
}
//DONE else, hay que devolver un 404
else
{
response.setStatus(404,"not found");
response.write("404 not found",true);
}
}

View File

@ -0,0 +1,20 @@
#ifndef COVERCONTROLLER_H
#define COVERCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
class CoverController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(CoverController);
public:
/** Constructor */
CoverController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // COVERCONTROLLER_H

View File

@ -0,0 +1,62 @@
/**
@file
@author Stefan Frings
*/
#include "dumpcontroller.h"
#include <QVariant>
#include <QDateTime>
DumpController::DumpController(){}
void DumpController::service(HttpRequest& request, HttpResponse& response) {
response.setHeader("Content-Type", "text/html; charset=ISO-8859-1");
response.setCookie(HttpCookie("firstCookie","hello",600));
response.setCookie(HttpCookie("secondCookie","world",600));
QByteArray body("<html><body>");
body.append("<b>Request:</b>");
body.append("<br>Method: ");
body.append(request.getMethod());
body.append("<br>Path: ");
body.append(request.getPath());
body.append("<br>Version: ");
body.append(request.getVersion());
body.append("<p><b>Headers:</b>");
QMapIterator<QByteArray,QByteArray> i(request.getHeaderMap());
while (i.hasNext()) {
i.next();
body.append("<br>");
body.append(i.key());
body.append("=");
body.append(i.value());
}
body.append("<p><b>Parameters:</b>");
i=QMapIterator<QByteArray,QByteArray>(request.getParameterMap());
while (i.hasNext()) {
i.next();
body.append("<br>");
body.append(i.key());
body.append("=");
body.append(i.value());
}
body.append("<p><b>Cookies:</b>");
i=QMapIterator<QByteArray,QByteArray>(request.getCookieMap());
while (i.hasNext()) {
i.next();
body.append("<br>");
body.append(i.key());
body.append("=");
body.append(i.value());
}
body.append("<p><b>Body:</b><br>");
body.append(request.getBody());
body.append("</body></html>");
response.write(body,true);
}

View File

@ -0,0 +1,29 @@
/**
@file
@author Stefan Frings
*/
#ifndef DUMPCONTROLLER_H
#define DUMPCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
/**
This controller dumps the received HTTP request in the response.
*/
class DumpController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(DumpController);
public:
/** Constructor */
DumpController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // DUMPCONTROLLER_H

View File

@ -0,0 +1,25 @@
#include "errorcontroller.h"
#include "template.h"
#include "../static.h"
ErrorController::ErrorController(int errorCode)
:error(errorCode)
{}
void ErrorController::service(HttpRequest& request, HttpResponse& response)
{
switch(error)
{
case 300:
response.setStatus(300,"redirect");
response.write("<html> <head> <meta http-equiv=\"refresh\" content=\"0; URL=/\"> </head> <body> </body> </html>", true);
break;
case 404:
response.setStatus(404,"not found");
response.write("404 not found",true);
break;
}
}

View File

@ -0,0 +1,22 @@
#ifndef ERRORCONTROLLER_H
#define ERRORCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
class ErrorController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(ErrorController);
public:
/** Constructor */
ErrorController(int errorCode);
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
private:
int error;
};
#endif // ERRORCONTROLLER_H

View File

@ -0,0 +1,38 @@
/**
@file
@author Stefan Frings
*/
#include "fileuploadcontroller.h"
FileUploadController::FileUploadController() {}
void FileUploadController::service(HttpRequest& request, HttpResponse& response) {
if (request.getParameter("action")=="show") {
response.setHeader("Content-Type", "image/jpeg");
QTemporaryFile* file=request.getUploadedFile("file1");
if (file) {
while (!file->atEnd() && !file->error()) {
QByteArray buffer=file->read(65536);
response.write(buffer);
}
}
else {
response.write("upload failed");
}
}
else {
response.setHeader("Content-Type", "text/html; charset=ISO-8859-1");
response.write("<html><body>");
response.write("Upload a JPEG image file<p>");
response.write("<form method=\"post\" enctype=\"multipart/form-data\">");
response.write(" <input type=\"hidden\" name=\"action\" value=\"show\">");
response.write(" File: <input type=\"file\" name=\"file1\"><br>");
response.write(" <input type=\"submit\">");
response.write("</form>");
response.write("</body></html>",true);
}
}

View File

@ -0,0 +1,30 @@
/**
@file
@author Stefan Frings
*/
#ifndef FILEUPLOADCONTROLLER_H
#define FILEUPLOADCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
/**
This controller displays a HTML form for file upload and recieved the file.
*/
class FileUploadController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(FileUploadController);
public:
/** Constructor */
FileUploadController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // FILEUPLOADCONTROLLER_H

View File

@ -0,0 +1,312 @@
#include "foldercontroller.h"
#include "db_helper.h" //get libraries
#include "comic_db.h"
#include "folder.h"
#include "template.h"
#include "../static.h"
#include "qnaturalsorting.h"
struct LibraryItemSorter
{
bool operator()(const LibraryItem * a,const LibraryItem * b) const
{
return naturalSortLessThanCI(a->name,b->name);
}
};
FolderController::FolderController() {}
void FolderController::service(HttpRequest& request, HttpResponse& response)
{
HttpSession session=Static::sessionStore->getSession(request,response,false);
response.setHeader("Content-Type", "text/html; charset=ISO-8859-1");
response.setHeader("Connection","close");
//QString y = session.get("xxx").toString();
//response.writeText(QString("session xxx : %1 <br/>").arg(y));
Template t=Static::templateLoader->getTemplate("folder_"+session.getDeviceType(),request.getHeader("Accept-Language"));
t.enableWarnings();
QString path = QUrl::fromPercentEncoding(request.getPath()).toLatin1();
QStringList pathElements = path.split('/');
QString libraryName = pathElements.at(2);
qulonglong parentId = pathElements.at(4).toULongLong();
QString folderName = DBHelper::getFolderName(libraryName,parentId);
if(parentId!=1)
t.setVariable("folder.name",folderName);
else
t.setVariable("folder.name",libraryName);
QList<LibraryItem *> folderContent = DBHelper::getFolderContentFromLibrary(libraryName,parentId);
QList<LibraryItem *> folderComics = DBHelper::getFolderComicsFromLibrary(libraryName,parentId);
//response.writeText(libraryName);
folderContent.append(folderComics);
qSort(folderContent.begin(),folderContent.end(),LibraryItemSorter());
folderComics.clear();
qulonglong backId = DBHelper::getParentFromComicFolderId(libraryName,parentId);
int page = 0;
QByteArray p = request.getParameter("page");
if(p.length() != 0)
page = p.toInt();
// /comicIdi/pagei/comicIdj/pagej/....../comicIdn/pagen
//QString currentPath = session.get("currentPath").toString();
//QStringList pathSize = currentPath.split("/").last().toInt;
bool fromUp = false;
QMultiMap<QByteArray,QByteArray> map = request.getParameterMap();
if(map.contains("up"))
fromUp = true;
int upPage = 0;
if(backId == 1 && parentId == 1)
{
session.popPage();
session.pushPage(page);
t.setVariable(QString("upurl"),"/?page=0");
}
else
{
if(fromUp)
{
session.popPage();
upPage = session.topPage();
page = upPage;
}
else //este nivel puede haberse cargado por primera vez <20> puede que estemos navegando horizontalmente
if(p.length() == 0) // acabamos de entrar
{
upPage = session.topPage();
session.pushPage(page);
}
else //navegaci<63>n horizontal
{
session.popPage();
upPage = session.topPage();
session.pushPage(page);
}
t.setVariable(QString("upurl"),"/library/" + QUrl::toPercentEncoding(libraryName) + "/folder/" +QString("%1?page=%2&up=true").arg(backId).arg(upPage));
}
/*if(currentPath.length()>0)
{
if(currentPath.contains(QString("%1").arg(parentId))
{
}
else
{
session.set("currentPath",currentPath+QString("/%1/%2").arg(parentId).arg(page);
}
}*/
//t.loop("element",folderContent.length());
int elementsPerPage = 18;
int numFolders = folderContent.length();
int numComics = folderComics.length();
int totalLength = folderContent.length() + folderComics.length();
int numFolderPages = numFolders / elementsPerPage + ((numFolders%elementsPerPage)>0?1:0);
int numPages = totalLength / elementsPerPage + ((totalLength%elementsPerPage)>0?1:0);
//response.writeText(QString("Number of pages : %1 <br/>").arg(numPages));
if(page < 0)
page = 0;
else if(page >= numPages)
page = numPages-1;
int indexCurrentPage = page*elementsPerPage;
int numFoldersAtCurrentPage = qMax(0,qMin(numFolders - indexCurrentPage, elementsPerPage));
//response.writeText(QString("indexCurrentPage : %1 <br/>").arg(indexCurrentPage));
//response.writeText(QString("numFoldersAtCurrentPage : %1 <br/>").arg(numFoldersAtCurrentPage));
//response.writeText(QString("foldersLength : %1 <br/>").arg(folderContent.length()));
t.loop("element",numFoldersAtCurrentPage);
int i = 0;
while(i<numFoldersAtCurrentPage)
{
LibraryItem * item = folderContent.at(i + (page*elementsPerPage));
t.setVariable(QString("element%1.name").arg(i),folderContent.at(i + (page*elementsPerPage))->name);
if(item->isDir())
{
t.setVariable(QString("element%1.class").arg(i),"folder");
t.setVariable(QString("element%1.image.width").arg(i),"89px");
t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png");
t.setVariable(QString("element%1.browse").arg(i),QString("<a class =\"browseButton\" href=\"%1\">browse</a>").arg(QString("/library/%1/folder/%2").arg(libraryName).arg(item->id)));
//t.setVariable(QString("element%1.url").arg(i),"/library/"+libraryName+"/folder/"+QString("%1").arg(folderContent.at(i + (page*10))->id));
//t.setVariable(QString("element%1.downloadurl").arg(i),"/library/"+libraryName+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id));
t.setVariable(QString("element%1.download").arg(i),QString("<a onclick=\"this.innerHTML='importing';this.className='importedButton';\" class =\"importButton\" href=\"%1\">import</a>").arg("/library/"+QUrl::toPercentEncoding(libraryName)+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id)));
}
else
{
t.setVariable(QString("element%1.class").arg(i),"cover");
const ComicDB * comic = (ComicDB *)item;
t.setVariable(QString("element%1.browse").arg(i),"");
t.setVariable(QString("element%1.image.width").arg(i),"80px");
//t.setVariable(QString("element%1.downloadurl").arg(i),"/library/"+libraryName+"/comic/"+QString("%1").arg(comic->id));
if(!session.isComicOnDevice(comic->info.hash) && !session.isComicDownloaded(comic->info.hash))
t.setVariable(QString("element%1.download").arg(i),QString("<a onclick=\"this.innerHTML='importing';this.className='importedButton';\" class =\"importButton\" href=\"%1\">import</a>").arg("/library/"+QUrl::toPercentEncoding(libraryName)+"/comic/"+QString("%1").arg(comic->id)));
else if (!session.isComicDownloaded(comic->info.hash))
t.setVariable(QString("element%1.download").arg(i),QString("<div class=\"importedButton\">imported</div>"));
else
t.setVariable(QString("element%1.download").arg(i),QString("<div class=\"importedButton\">importing</div>"));
//t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png");
t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(comic->info.hash));
}
i++;
}
//int comicsOffset;// = qMax(0,((page - (numFolderPages - 1)) * 10) - (numFolders%10));
//int comicPage = numFolderPages!=0?page-(numFolderPages - 1):page;
//if(comicPage > 0)
//{
// comicsOffset = elementsPerPage - (numFolders%elementsPerPage);
// comicsOffset += (comicPage-1) *elementsPerPage;
//}
//else
// comicsOffset = 0;
//
//int globalComicsOffset = elementsPerPage - (numFolders%elementsPerPage);
//int numComicsAtCurrentPage = 0;
//if(comicPage == 0) //primera p<>gina de los c<>mics
// numComicsAtCurrentPage = qMin(globalComicsOffset,numComics);
// else if (page == (numPages-1)) //<2F>ltima p<>gina de los c<>mics
// numComicsAtCurrentPage = elementsPerPage-globalComicsOffset + (numComics%elementsPerPage);
// else
// numComicsAtCurrentPage = elementsPerPage - numFoldersAtCurrentPage;
//if(numComics == 0)
// numComicsAtCurrentPage = 0;
////response.writeText(QString("numComicsAtCurrentPage : %1 <br/>").arg(numComicsAtCurrentPage));
////response.writeText(QString("comicsOffset : %1 <br/>").arg(comicsOffset));
//t.loop("elementcomic",numComicsAtCurrentPage);
////
//int j = 0;
//while(j<numComicsAtCurrentPage)
//{
// const ComicDB * comic = (ComicDB *)folderComics.at(j+comicsOffset);
// //if(comic->info.title == 0 || comic->info.title->isEmpty())
// t.setVariable(QString("elementcomic%1.name").arg(j),comic->name);
// //else
// // t.setVariable(QString("elementcomic%1.name").arg(i),*comic->info.title);
// t.setVariable(QString("elementcomic%1.url").arg(j),"/library/"+QUrl::toPercentEncoding(libraryName)+"/comic/"+QString("%1").arg(comic->id));
// t.setVariable(QString("elementcomic%1.coverulr").arg(j),"/library/"+QUrl::toPercentEncoding(libraryName)+"/cover/"+QString("%1").arg(comic->info.hash + ".jpg"));
// j++;
//}
if(numPages > 1)
{
t.setCondition("pageIndex",true);
QMap<QString,int> indexCount;
QString firstChar;
int xyz = 1;
for(QList<LibraryItem *>::const_iterator itr=folderContent.constBegin();itr!=folderContent.constEnd();itr++)
{
firstChar = QString((*itr)->name[0]).toUpper();
firstChar = firstChar.normalized(QString::NormalizationForm_D).at(0);//TODO _D or _KD??
bool ok;
int dec = firstChar.toInt(&ok, 10);
if(ok)
firstChar = "#";
//response.writeText(QString("%1 - %2 <br />").arg((*itr)->name).arg(xyz));
if(indexCount.contains(firstChar))
indexCount.insert(firstChar, indexCount.value(firstChar)+1);
else
indexCount.insert(firstChar, 1);
xyz++;
}
QList<QString> index = indexCount.keys();
if(index.length()>1)
{
t.setCondition("alphaIndex",true);
qSort(index.begin(),index.end(),naturalSortLessThanCI);
t.loop("index",index.length());
int i=0;
int count=0;
int indexPage=0;
for(QList<QString>::const_iterator itr=index.constBegin();itr!=index.constEnd();itr++)
{
//response.writeText(QString("%1 - %2 <br />").arg(*itr).arg(count));
t.setVariable(QString("index%1.indexname").arg(i), *itr);
t.setVariable(QString("index%1.url").arg(i),QString("/library/%1/folder/%2?page=%3").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(parentId).arg(indexPage));
i++;
count += indexCount.value(*itr);
indexPage = count/elementsPerPage;
}
}
else
{
t.loop("index",0);
t.setCondition("alphaIndex",false);
}
t.loop("page",numPages);
int z = 0;
while(z < numPages)
{
t.setVariable(QString("page%1.url").arg(z),QString("/library/%1/folder/%2?page=%3").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(parentId).arg(z));
t.setVariable(QString("page%1.number").arg(z),QString("%1").arg(z));
if(page == z)
t.setVariable(QString("page%1.current").arg(z),"current");
else
t.setVariable(QString("page%1.current").arg(z),"");
z++;
}
t.setVariable("page.first",QString("/library/%1/folder/%2?page=%3").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(parentId).arg(0));
t.setVariable("page.previous",QString("/library/%1/folder/%2?page=%3").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(parentId).arg((page==0)?page:page-1));
t.setVariable("page.next",QString("/library/%1/folder/%2?page=%3").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(parentId).arg((page==numPages-1)?page:page+1));
t.setVariable("page.last",QString("/library/%1/folder/%2?page=%3").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(parentId).arg(numPages-1));
}
else
{
t.loop("page",0);
t.loop("index",0);
t.setCondition("pageIndex",false);
t.setCondition("alphaIndex",false);
}
t.setVariable("page",QString("%1").arg(page+1));
t.setVariable("pages",QString("%1").arg(numPages));
response.write(t.toLatin1(),true);
}

View File

@ -0,0 +1,20 @@
#ifndef FOLDERCONTROLLER_H
#define FOLDERCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
class FolderController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(FolderController);
public:
/** Constructor */
FolderController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // FOLDERCONTROLLER_H

View File

@ -0,0 +1,38 @@
#include "folderinfocontroller.h"
#include "db_helper.h" //get libraries
#include "folder.h"
#include "comic_db.h"
#include "template.h"
#include "../static.h"
FolderInfoController::FolderInfoController() {}
void FolderInfoController::service(HttpRequest& request, HttpResponse& response)
{
response.setHeader("Content-Type", "plain/text; charset=ISO-8859-1");
QString path = QUrl::fromPercentEncoding(request.getPath()).toLatin1();
QStringList pathElements = path.split('/');
QString libraryName = pathElements.at(2);
qulonglong parentId = pathElements.at(4).toULongLong();
QList<LibraryItem *> folderContent = DBHelper::getFolderContentFromLibrary(libraryName,parentId);
QList<LibraryItem *> folderComics = DBHelper::getFolderComicsFromLibrary(libraryName,parentId);
Folder * currentFolder;
for(QList<LibraryItem *>::const_iterator itr = folderContent.constBegin();itr!=folderContent.constEnd();itr++)
{
currentFolder = (Folder *)(*itr);
response.writeText(QString("/library/%1/folder/%2/info\n").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(currentFolder->id));
}
ComicDB * currentComic;
for(QList<LibraryItem *>::const_iterator itr = folderComics.constBegin();itr!=folderComics.constEnd();itr++)
{
currentComic = (ComicDB *)(*itr);
response.writeText(QString("/library/%1/comic/%2\n").arg(QString(QUrl::toPercentEncoding(libraryName))).arg(currentComic->id));
}
}

View File

@ -0,0 +1,20 @@
#ifndef FOLDERINFOCONTROLLER_H
#define FOLDERINFOCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
class FolderInfoController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(FolderInfoController);
public:
/** Constructor */
FolderInfoController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // FOLDERINFOCONTROLLER_H

View File

@ -0,0 +1,64 @@
/**
@file
@author Stefan Frings
*/
#include "formcontroller.h"
#include <QStringList>
FormController::FormController() {}
void FormController::service(HttpRequest& request, HttpResponse& response) {
response.setHeader("Content-Type", "text/html; charset=utf-8");
QString data(request.getBody());
QStringList list = data.split("\n");
response.write("<html><body>");
response.writeText("á é í ó ú ñ -> \\ /Device type: "+list.first());
//test background proccesing
/*int i=0;
int j=0;
while(i<1000000000)
{
if(request.getBody().length()>1)
j++;
else
i++;
if(i%1000000 == 0)
response.write("<p> lista </p>");
}*/
response.write("<p> lista </p>");
response.write("<ul>");
for(int i=1;i<list.length();i++)
{
response.writeText("<li>"+list.at(i)+"</li>");
}
response.write("</ul></body></html>",true);
/*if (request.getParameter("action")=="show") {
response.write("<html><body>");
response.write("Name = ");
response.write(request.getParameter("name"));
response.write("<br>City = ");
response.write(request.getParameter("city"));
response.write("</body></html>",true);
}
else {
response.write("<html><body>");
response.write("<form method=\"post\">");
response.write(" <input type=\"hidden\" name=\"action\" value=\"show\">");
response.write(" Name: <input type=\"text\" name=\"name\"><br>");
response.write(" City: <input type=\"text\" name=\"city\"><br>");
response.write(" <input type=\"submit\">");
response.write("</form>");
response.write("</body></html>",true);
}*/
}

View File

@ -0,0 +1,30 @@
/**
@file
@author Stefan Frings
*/
#ifndef FORMCONTROLLER_H
#define FORMCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
/**
This controller displays a HTML form and dumps the submitted input.
*/
class FormController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(FormController);
public:
/** Constructor */
FormController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // FORMCONTROLLER_H

View File

@ -0,0 +1,54 @@
#include "librariescontroller.h"
#include "db_helper.h" //get libraries
#include "template.h"
#include "../static.h"
LibrariesController::LibrariesController() {}
void LibrariesController::service(HttpRequest& request, HttpResponse& response)
{
HttpSession session=Static::sessionStore->getSession(request,response);
session.set("ySession","ok");
session.clearNavigationPath();
response.setHeader("Content-Type", "text/html; charset=ISO-8859-1");
response.setHeader("Connection","close");
QString postData = QString::fromUtf8(request.getBody());
//response.writeText(postData);
QList<QString> data = postData.split("\n");
if(data.length() > 2)
{
session.setDeviceType(data.at(0).split(":").at(1));
session.setDisplayType(data.at(1).split(":").at(1));
QList<QString> comics = data.at(2).split(":").at(1).split("\t");
foreach(QString hash,comics)
{
session.setComicOnDevice(hash);
}
}
else //valores por defecto, con propositos de depuraci<63>n
{
session.setDeviceType("ipad");
session.setDisplayType("retina");
}
Template t=Static::templateLoader->getTemplate("libraries_"+session.getDeviceType(),request.getHeader("Accept-Language"));
t.enableWarnings();
QMap<QString,QString> libraries = DBHelper::getLibraries();
QList<QString> names = libraries.keys();
t.loop("library",names.length());
int i=0;
while (i<names.length()) {
t.setVariable(QString("library%1.name").arg(i),QUrl::toPercentEncoding(names.at(i)));
t.setVariable(QString("library%1.label").arg(i),names.at(i));
i++;
}
response.write(t.toLatin1(),true);
}

View File

@ -0,0 +1,25 @@
#ifndef LIBRARIESCONTROLLER_H
#define LIBRARIESCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
/**
This controller displays a HTML form and dumps the submitted input.
*/
class LibrariesController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(LibrariesController);
public:
/** Constructor */
LibrariesController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // LIBRARIESCONTROLLER_H

View File

@ -0,0 +1,74 @@
#include "pagecontroller.h"
#include "../static.h"
#include "comic.h"
#include "comiccontroller.h"
#include <QDataStream>
#include <QPointer>
PageController::PageController() {}
void PageController::service(HttpRequest& request, HttpResponse& response)
{
HttpSession session=Static::sessionStore->getSession(request,response,false);
QString path = QUrl::fromPercentEncoding(request.getPath()).toLatin1();
//QByteArray path2=request.getPath();
//qDebug("PageController: request to -> %s ",path2.data());
QStringList pathElements = path.split('/');
QString libraryName = pathElements.at(2);
qulonglong comicId = pathElements.at(4).toULongLong();
unsigned int page = pathElements.at(6).toUInt();
//qDebug("lib name : %s",pathElements.at(2).data());
Comic * comicFile = session.getCurrentComic();
if(session.getCurrentComicId() != 0 && !QPointer<Comic>(comicFile).isNull())
{
if(comicId == session.getCurrentComicId() && page < comicFile->numPages())
{
if(comicFile->pageIsLoaded(page))
{
//qDebug("PageController: La p<>gina estaba cargada -> %s ",path.data());
response.setHeader("Content-Type", "image/jpeg");
response.setHeader("Transfer-Encoding","chunked");
QByteArray pageData = comicFile->getRawPage(page);
QDataStream data(pageData);
char buffer[4096];
while (!data.atEnd()) {
int len = data.readRawData(buffer,4096);
response.write(QByteArray(buffer,len));
}
//response.write(pageData,true);
response.write(QByteArray(),true);
}
else
{
//qDebug("PageController: La p<>gina NO estaba cargada 404 -> %s ",path.data());
response.setStatus(404,"not found"); //TODO qu<71> mensaje enviar
response.write("404 not found",true);
}
}
else
{
if(comicId != session.getCurrentComicId())
{
//delete comicFile;
session.dismissCurrentComic();
}
response.setStatus(404,"not found"); //TODO qu<71> mensaje enviar
response.write("404 not found",true);
}
}
else
{
response.setStatus(404,"not found");
response.write("404 not found",true);
}
//response.write(t.toLatin1(),true);
}

View File

@ -0,0 +1,20 @@
#ifndef PAGECONTROLLER_H
#define PAGECONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
class PageController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(PageController);
public:
/** Constructor */
PageController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // PAGECONTROLLER_H

View File

@ -0,0 +1,31 @@
/**
@file
@author Stefan Frings
*/
#include "sessioncontroller.h"
#include "../static.h"
#include <QVariant>
#include <QDateTime>
SessionController::SessionController(){}
void SessionController::service(HttpRequest& request, HttpResponse& response) {
response.setHeader("Content-Type", "text/html; charset=ISO-8859-1");
// Get current session, or create a new one
HttpSession session=Static::sessionStore->getSession(request,response);
if (!session.contains("startTime")) {
response.write("<html><body>New session started. Reload this page now.</body></html>");
session.set("startTime",QDateTime::currentDateTime());
}
else {
QDateTime startTime=session.get("startTime").toDateTime();
response.write("<html><body>Your session started ");
response.write(startTime.toString().toLatin1());
response.write("</body></html>");
}
}

View File

@ -0,0 +1,29 @@
/**
@file
@author Stefan Frings
*/
#ifndef SESSIONCONTROLLER_H
#define SESSIONCONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
/**
This controller demonstrates how to use sessions.
*/
class SessionController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(SessionController);
public:
/** Constructor */
SessionController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // SESSIONCONTROLLER_H

View File

@ -0,0 +1,31 @@
/**
@file
@author Stefan Frings
*/
#include "templatecontroller.h"
#include "template.h"
#include "../static.h"
TemplateController::TemplateController(){}
void TemplateController::service(HttpRequest& request, HttpResponse& response) {
response.setHeader("Content-Type", "text/html; charset=ISO-8859-1");
Template t=Static::templateLoader->getTemplate("demo",request.getHeader("Accept-Language"));
t.enableWarnings();
t.setVariable("path",request.getPath());
QMap<QByteArray,QByteArray> headers=request.getHeaderMap();
QMapIterator<QByteArray,QByteArray> iterator(headers);
t.loop("header",headers.size());
int i=0;
while (iterator.hasNext()) {
iterator.next();
t.setVariable(QString("header%1.name").arg(i),QString(iterator.key()));
t.setVariable(QString("header%1.value").arg(i),QString(iterator.value()));
++i;
}
response.write(t.toLatin1(),true);
}

View File

@ -0,0 +1,30 @@
/**
@file
@author Stefan Frings
*/
#ifndef TEMPLATECONTROLLER_H
#define TEMPLATECONTROLLER_H
#include "httprequest.h"
#include "httpresponse.h"
#include "httprequesthandler.h"
/**
This controller generates a website using the template engine.
It generates a Latin1 (ISO-8859-1) encoded website from a UTF-8 encoded template file.
*/
class TemplateController : public HttpRequestHandler {
Q_OBJECT
Q_DISABLE_COPY(TemplateController);
public:
/** Constructor */
TemplateController();
/** Generates the response */
void service(HttpRequest& request, HttpResponse& response);
};
#endif // TEMPLATECONTROLLER_H

View File

@ -0,0 +1,4 @@
#ifndef DOCUMENTCACHE_H
#define DOCUMENTCACHE_H
#endif // DOCUMENTCACHE_H

View File

@ -0,0 +1,12 @@
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
HEADERS += $$PWD/httplistener.h $$PWD/httpconnectionhandler.h $$PWD/httpconnectionhandlerpool.h $$PWD/httprequest.h $$PWD/httpresponse.h $$PWD/httpcookie.h $$PWD/httprequesthandler.h
HEADERS += $$PWD/httpsession.h $$PWD/httpsessionstore.h
HEADERS += $$PWD/staticfilecontroller.h
SOURCES += $$PWD/httplistener.cpp $$PWD/httpconnectionhandler.cpp $$PWD/httpconnectionhandlerpool.cpp $$PWD/httprequest.cpp $$PWD/httpresponse.cpp $$PWD/httpcookie.cpp $$PWD/httprequesthandler.cpp
SOURCES += $$PWD/httpsession.cpp $$PWD/httpsessionstore.cpp
SOURCES += $$PWD/staticfilecontroller.cpp
OTHER_FILES += $$PWD/../doc/readme.txt

View File

@ -0,0 +1,164 @@
/**
@file
@author Stefan Frings
*/
#include "httpconnectionhandler.h"
#include "httpresponse.h"
#include <QTimer>
#include <QCoreApplication>
HttpConnectionHandler::HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler)
: QThread()
{
Q_ASSERT(settings!=0);
Q_ASSERT(requestHandler!=0);
this->settings=settings;
this->requestHandler=requestHandler;
currentRequest=0;
busy = false;
// execute signals in my own thread
moveToThread(this);
socket.moveToThread(this);
readTimer.moveToThread(this);
connect(&socket, SIGNAL(readyRead()), SLOT(read()));
connect(&socket, SIGNAL(disconnected()), SLOT(disconnected()));
connect(&readTimer, SIGNAL(timeout()), SLOT(readTimeout()));
readTimer.setSingleShot(true);
qDebug("HttpConnectionHandler (%p): constructed", this);
this->start();
}
HttpConnectionHandler::~HttpConnectionHandler() {
socket.close();
quit();
wait();
qDebug("HttpConnectionHandler (%p): destroyed", this);
}
void HttpConnectionHandler::run() {
qDebug("HttpConnectionHandler (%p): thread started", this);
try {
exec();
}
catch (...) {
qCritical("HttpConnectionHandler (%p): an uncatched exception occured in the thread",this);
}
qDebug("HttpConnectionHandler (%p): thread stopped", this);
// Change to the main thread, otherwise deleteLater() would not work
moveToThread(QCoreApplication::instance()->thread());
}
void HttpConnectionHandler::handleConnection(int socketDescriptor) {
qDebug("HttpConnectionHandler (%p): handle new connection", this);
busy = true;
Q_ASSERT(socket.isOpen()==false); // if not, then the handler is already busy
if (!socket.setSocketDescriptor(socketDescriptor)) {
qCritical("HttpConnectionHandler (%p): cannot initialize socket: %s", this,qPrintable(socket.errorString()));
return;
}
// Start timer for read timeout
int readTimeout=settings->value("readTimeout",10000).toInt();
readTimer.start(readTimeout);
// delete previous request
delete currentRequest;
currentRequest=0;
}
bool HttpConnectionHandler::isBusy() {
return busy;
}
void HttpConnectionHandler::setBusy() {
this->busy = true;
}
void HttpConnectionHandler::readTimeout() {
qDebug("HttpConnectionHandler (%p): read timeout occured",this);
//Commented out because QWebView cannot handle this.
//socket.write("HTTP/1.1 408 request timeout\r\nConnection: close\r\n\r\n408 request timeout\r\n");
socket.disconnectFromHost();
delete currentRequest;
currentRequest=0;
}
void HttpConnectionHandler::disconnected() {
qDebug("HttpConnectionHandler (%p): disconnected", this);
socket.close();
readTimer.stop();
busy = false;
}
void HttpConnectionHandler::read() {
#ifdef SUPERVERBOSE
qDebug("HttpConnectionHandler (%p): read input",this);
#endif
// Create new HttpRequest object if necessary
if (!currentRequest) {
currentRequest=new HttpRequest(settings);
}
// Collect data for the request object
while (socket.bytesAvailable() && currentRequest->getStatus()!=HttpRequest::complete && currentRequest->getStatus()!=HttpRequest::abort) {
currentRequest->readFromSocket(socket);
if (currentRequest->getStatus()==HttpRequest::waitForBody) {
// Restart timer for read timeout, otherwise it would
// expire during large file uploads.
int readTimeout=settings->value("readTimeout",10000).toInt();
readTimer.start(readTimeout);
}
}
// If the request is aborted, return error message and close the connection
if (currentRequest->getStatus()==HttpRequest::abort) {
socket.write("HTTP/1.1 413 entity too large\r\nConnection: close\r\n\r\n413 Entity too large\r\n");
socket.disconnectFromHost();
delete currentRequest;
currentRequest=0;
return;
}
// If the request is complete, let the request mapper dispatch it
if (currentRequest->getStatus()==HttpRequest::complete) {
readTimer.stop();
qDebug("HttpConnectionHandler (%p): received request",this);
HttpResponse response(&socket);
//response.setHeader("Connection","close"); No funciona bien con NSURLConnection
try {
requestHandler->service(*currentRequest, response);
}
catch (...) {
qCritical("HttpConnectionHandler (%p): An uncatched exception occured in the request handler",this);
}
// Finalize sending the response if not already done
if (!response.hasSentLastPart()) {
response.write(QByteArray(),true);
}
socket.disconnectFromHost(); //CAMBIADO s<>lo se van a soportar conexiones NO persistentes
// Close the connection after delivering the response, if requested
//if (QString::compare(currentRequest->getHeader("Connection"),"close",Qt::CaseInsensitive)==0) {
// socket.disconnectFromHost();
//}
//else {
// // Start timer for next request
// int readTimeout=settings->value("readTimeout",10000).toInt();
// readTimer.start(readTimeout);
//}
// Prepare for next request
delete currentRequest;
currentRequest=0;
}
}

View File

@ -0,0 +1,97 @@
/**
@file
@author Stefan Frings
*/
#ifndef HTTPCONNECTIONHANDLER_H
#define HTTPCONNECTIONHANDLER_H
#include <QTcpSocket>
#include <QSettings>
#include <QTimer>
#include <QThread>
#include "httprequest.h"
#include "httprequesthandler.h"
/**
The connection handler accepts incoming connections and dispatches incoming requests to to a
request mapper. Since HTTP clients can send multiple requests before waiting for the response,
the incoming requests are queued and processed one after the other.
<p>
Example for the required configuration settings:
<code><pre>
readTimeout=60000
maxRequestSize=16000
maxMultiPartSize=1000000
</pre></code>
<p>
The readTimeout value defines the maximum time to wait for a complete HTTP request.
@see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize
*/
class HttpConnectionHandler : public QThread {
Q_OBJECT
Q_DISABLE_COPY(HttpConnectionHandler)
public:
/**
Constructor.
@param settings Configuration settings of the HTTP webserver
@param requestHandler handler that will process each incomin HTTP request
*/
HttpConnectionHandler(QSettings* settings, HttpRequestHandler* requestHandler);
/** Destructor */
virtual ~HttpConnectionHandler();
/** Returns true, if this handler is in use. */
bool isBusy();
/** Mark this handler as busy */
void setBusy();
private:
/** Configuration settings */
QSettings* settings;
/** TCP socket of the current connection */
QTcpSocket socket;
/** Time for read timeout detection */
QTimer readTimer;
/** Storage for the current incoming HTTP request */
HttpRequest* currentRequest;
/** Dispatches received requests to services */
HttpRequestHandler* requestHandler;
/** This shows the busy-state from a very early time */
bool busy;
/** Executes the htreads own event loop */
void run();
public slots:
/**
Received from from the listener, when the handler shall start processing a new connection.
@param socketDescriptor references the accepted connection.
*/
void handleConnection(int socketDescriptor);
private slots:
/** Received from the socket when a read-timeout occured */
void readTimeout();
/** Received from the socket when incoming data can be read */
void read();
/** Received from the socket when a connection has been closed */
void disconnected();
};
#endif // HTTPCONNECTIONHANDLER_H

View File

@ -0,0 +1,64 @@
#include "httpconnectionhandlerpool.h"
HttpConnectionHandlerPool::HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler)
: QObject()
{
Q_ASSERT(settings!=0);
this->settings=settings;
this->requestHandler=requestHandler;
cleanupTimer.start(settings->value("cleanupInterval",10000).toInt());
connect(&cleanupTimer, SIGNAL(timeout()), SLOT(cleanup()));
}
HttpConnectionHandlerPool::~HttpConnectionHandlerPool() {
foreach(HttpConnectionHandler* handler, pool) {
connect(handler,SIGNAL(finished()),handler,SLOT(deleteLater()));
handler->quit();
}
}
HttpConnectionHandler* HttpConnectionHandlerPool::getConnectionHandler() {
HttpConnectionHandler* freeHandler=0;
mutex.lock();
// find a free handler in pool
foreach(HttpConnectionHandler* handler, pool) {
if (!handler->isBusy()) {
freeHandler=handler;
freeHandler->setBusy();
break;
}
}
// create a new handler, if necessary
if (!freeHandler) {
int maxConnectionHandlers=settings->value("maxThreads",1000).toInt();
if (pool.count()<maxConnectionHandlers) {
freeHandler=new HttpConnectionHandler(settings,requestHandler);
freeHandler->setBusy();
pool.append(freeHandler);
}
}
mutex.unlock();
return freeHandler;
}
void HttpConnectionHandlerPool::cleanup() {
int maxIdleHandlers=settings->value("minThreads",50).toInt();
int idleCounter=0;
mutex.lock();
foreach(HttpConnectionHandler* handler, pool) {
if (!handler->isBusy()) {
if (++idleCounter > maxIdleHandlers) {
pool.removeOne(handler);
qDebug("HttpConnectionHandlerPool: Removed connection handler (%p), pool size is now %i",handler,pool.size());
connect(handler,SIGNAL(finished()),handler,SLOT(deleteLater()));
handler->quit();
break; // remove only one handler in each interval
}
}
}
mutex.unlock();
}

View File

@ -0,0 +1,73 @@
#ifndef HTTPCONNECTIONHANDLERPOOL_H
#define HTTPCONNECTIONHANDLERPOOL_H
#include <QList>
#include <QTimer>
#include <QObject>
#include <QMutex>
#include "httpconnectionhandler.h"
/**
Pool of http connection handlers. Connection handlers are created on demand and idle handlers are
cleaned up in regular time intervals.
<p>
Example for the required configuration settings:
<code><pre>
minThreads=1
maxThreads=100
cleanupInterval=1000
maxRequestSize=16000
maxMultiPartSize=1000000
</pre></code>
The pool is empty initially and grows with the number of concurrent
connections. A timer removes one idle connection handler at each
interval, but it leaves some spare handlers in memory to improve
performance.
@see HttpConnectionHandler for description of config settings readTimeout
@see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize
*/
class HttpConnectionHandlerPool : public QObject {
Q_OBJECT
Q_DISABLE_COPY(HttpConnectionHandlerPool)
public:
/**
Constructor.
@param settings Configuration settings for the HTTP server. Must not be 0.
@param requestHandler The handler that will process each received HTTP request.
@warning The requestMapper gets deleted by the destructor of this pool
*/
HttpConnectionHandlerPool(QSettings* settings, HttpRequestHandler* requestHandler);
/** Destructor */
virtual ~HttpConnectionHandlerPool();
/** Get a free connection handler, or 0 if not available. */
HttpConnectionHandler* getConnectionHandler();
private:
/** Settings for this pool */
QSettings* settings;
/** Will be assigned to each Connectionhandler during their creation */
HttpRequestHandler* requestHandler;
/** Pool of connection handlers */
QList<HttpConnectionHandler*> pool;
/** Timer to clean-up unused connection handler */
QTimer cleanupTimer;
/** Used to synchronize threads */
QMutex mutex;
private slots:
/** Received from the clean-up timer. */
void cleanup();
};
#endif // HTTPCONNECTIONHANDLERPOOL_H

View File

@ -0,0 +1,199 @@
/**
@file
@author Stefan Frings
*/
#include "httpcookie.h"
HttpCookie::HttpCookie() {
version=1;
maxAge=0;
secure=false;
}
HttpCookie::HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path, const QByteArray comment, const QByteArray domain, const bool secure) {
this->name=name;
this->value=value;
this->maxAge=maxAge;
this->path=path;
this->comment=comment;
this->domain=domain;
this->secure=secure;
this->version=1;
}
HttpCookie::HttpCookie(const QByteArray source) {
version=1;
maxAge=0;
secure=false;
QList<QByteArray> list=splitCSV(source);
foreach(QByteArray part, list) {
// Split the part into name and value
QByteArray name;
QByteArray value;
int posi=part.indexOf('=');
if (posi) {
name=part.left(posi).trimmed();
value=part.mid(posi+1).trimmed();
}
else {
name=part.trimmed();
value="";
}
// Set fields
if (name=="Comment") {
comment=value;
}
else if (name=="Domain") {
domain=value;
}
else if (name=="Max-Age") {
maxAge=value.toInt();
}
else if (name=="Path") {
path=value;
}
else if (name=="Secure") {
secure=true;
}
else if (name=="Version") {
version=value.toInt();
}
else {
if (this->name.isEmpty()) {
this->name=name;
this->value=value;
}
else {
qWarning("HttpCookie: Ignoring unknown %s=%s",name.data(),value.data());
}
}
}
}
QByteArray HttpCookie::toByteArray() const {
QByteArray buffer(name);
buffer.append('=');
buffer.append(value);
if (!comment.isEmpty()) {
buffer.append("; Comment=");
buffer.append(comment);
}
if (!domain.isEmpty()) {
buffer.append("; Domain=");
buffer.append(domain);
}
if (maxAge!=0) {
buffer.append("; Max-Age=");
buffer.append(QByteArray::number(maxAge));
}
if (!path.isEmpty()) {
buffer.append("; Path=");
buffer.append(path);
}
if (secure) {
buffer.append("; Secure");
}
buffer.append("; Version=");
buffer.append(QByteArray::number(version));
return buffer;
}
void HttpCookie::setName(const QByteArray name){
this->name=name;
}
void HttpCookie::setValue(const QByteArray value){
this->value=value;
}
void HttpCookie::setComment(const QByteArray comment){
this->comment=comment;
}
void HttpCookie::setDomain(const QByteArray domain){
this->domain=domain;
}
void HttpCookie::setMaxAge(const int maxAge){
this->maxAge=maxAge;
}
void HttpCookie::setPath(const QByteArray path){
this->path=path;
}
void HttpCookie::setSecure(const bool secure){
this->secure=secure;
}
QByteArray HttpCookie::getName() const {
return name;
}
QByteArray HttpCookie::getValue() const {
return value;
}
QByteArray HttpCookie::getComment() const {
return comment;
}
QByteArray HttpCookie::getDomain() const {
return domain;
}
int HttpCookie::getMaxAge() const {
return maxAge;
}
QByteArray HttpCookie::getPath() const {
return path;
}
bool HttpCookie::getSecure() const {
return secure;
}
int HttpCookie::getVersion() const {
return version;
}
QList<QByteArray> HttpCookie::splitCSV(const QByteArray source) {
bool inString=false;
QList<QByteArray> list;
QByteArray buffer;
for (int i=0; i<source.size(); ++i) {
char c=source.at(i);
if (inString==false) {
if (c=='\"') {
inString=true;
}
else if (c==';') {
QByteArray trimmed=buffer.trimmed();
if (!trimmed.isEmpty()) {
list.append(trimmed);
}
buffer.clear();
}
else {
buffer.append(c);
}
}
else {
if (c=='\"') {
inString=false;
}
else {
buffer.append(c);
}
}
}
QByteArray trimmed=buffer.trimmed();
if (!trimmed.isEmpty()) {
list.append(trimmed);
}
return list;
}

View File

@ -0,0 +1,110 @@
/**
@file
@author Stefan Frings
*/
#ifndef HTTPCOOKIE_H
#define HTTPCOOKIE_H
#include <QList>
#include <QByteArray>
/**
HTTP cookie as defined in RFC 2109. This class can also parse
RFC 2965 cookies, but skips fields that are not defined in RFC
2109.
*/
class HttpCookie
{
public:
/** Creates an empty cookie */
HttpCookie();
/**
Create a cookie and set name/value pair.
@param name name of the cookie
@param value value of the cookie
@param maxAge maximum age of the cookie in seconds. 0=discard immediately
@param path Path for that the cookie will be sent, default="/" which means the whole domain
@param comment Optional comment, may be displayed by the web browser somewhere
@param domain Optional domain for that the cookie will be sent. Defaults to the current domain
@param secure If true, the cookie will only be sent on secure connections
*/
HttpCookie(const QByteArray name, const QByteArray value, const int maxAge, const QByteArray path="/", const QByteArray comment=QByteArray(), const QByteArray domain=QByteArray(), const bool secure=false);
/**
Create a cookie from a string.
@param source String as received in a HTTP Cookie2 header.
*/
HttpCookie(const QByteArray source);
/** Convert this cookie to a string that may be used in a Set-Cookie2 header. */
QByteArray toByteArray() const ;
/**
Split a string list into parts, where each part is delimited by semicolon.
Semicolons within double quotes are skipped. Double quotes are removed.
*/
static QList<QByteArray> splitCSV(const QByteArray source);
/** Set the name of this cookie */
void setName(const QByteArray name);
/** Set the value of this cookie */
void setValue(const QByteArray value);
/** Set the comment of this cookie */
void setComment(const QByteArray comment);
/** Set the domain of this cookie */
void setDomain(const QByteArray domain);
/** Set the maximum age of this cookie in seconds. 0=discard immediately */
void setMaxAge(const int maxAge);
/** Set the path for that the cookie will be sent, default="/" which means the whole domain */
void setPath(const QByteArray path);
/** Set secure mode, so that the cokkie will only be sent on secure connections */
void setSecure(const bool secure);
/** Get the name of this cookie */
QByteArray getName() const;
/** Get the value of this cookie */
QByteArray getValue() const;
/** Get the comment of this cookie */
QByteArray getComment() const;
/** Get the domain of this cookie */
QByteArray getDomain() const;
/** Set the maximum age of this cookie in seconds. */
int getMaxAge() const;
/** Set the path of this cookie */
QByteArray getPath() const;
/** Get the secure flag of this cookie */
bool getSecure() const;
/** Returns always 1 */
int getVersion() const;
private:
QByteArray name;
QByteArray value;
QByteArray comment;
QByteArray domain;
int maxAge;
QByteArray path;
bool secure;
int version;
};
#endif // HTTPCOOKIE_H

View File

@ -0,0 +1,66 @@
/**
@file
@author Stefan Frings
*/
#include "httplistener.h"
#include "httpconnectionhandler.h"
#include "httpconnectionhandlerpool.h"
#include <QCoreApplication>
HttpListener::HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject *parent)
: QTcpServer(parent)
{
Q_ASSERT(settings!=0);
this->settings=settings;
pool=new HttpConnectionHandlerPool(settings,requestHandler);
// Start listening
int port=settings->value("port",8080).toInt();
listen(QHostAddress::Any, port);
//Cambiado
int i = 0;
while (!isListening() && i < 1000) {
listen(QHostAddress::Any, (rand() % 45535)+20000);
i++;
}
if(!isListening())
{
qCritical("HttpListener: Cannot bind on port %i: %s",port,qPrintable(errorString()));
}
else {
qDebug("HttpListener: Listening on port %i",port);
}
}
HttpListener::~HttpListener() {
close();
qDebug("HttpListener: closed");
delete pool;
qDebug("HttpListener: destroyed");
}
void HttpListener::incomingConnection(int socketDescriptor) {
#ifdef SUPERVERBOSE
qDebug("HttpListener: New connection");
#endif
HttpConnectionHandler* freeHandler=pool->getConnectionHandler();
// Let the handler process the new connection.
if (freeHandler) {
// The descriptor is passed via signal/slot because the handler lives in another
// thread and cannot open the socket when called by another thread.
connect(this,SIGNAL(handleConnection(int)),freeHandler,SLOT(handleConnection(int)));
emit handleConnection(socketDescriptor);
disconnect(this,SIGNAL(handleConnection(int)),freeHandler,SLOT(handleConnection(int)));
}
else {
// Reject the connection
qDebug("HttpListener: Too many connections");
QTcpSocket* socket=new QTcpSocket(this);
socket->setSocketDescriptor(socketDescriptor);
connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
socket->write("HTTP/1.1 503 too many connections\r\nConnection: close\r\n\r\nToo many connections\r\n");
socket->disconnectFromHost();
}
}

View File

@ -0,0 +1,76 @@
/**
@file
@author Stefan Frings
*/
#ifndef LISTENER_H
#define LISTENER_H
#include <QTcpServer>
#include <QSettings>
#include <QBasicTimer>
#include "httpconnectionhandler.h"
#include "httpconnectionhandlerpool.h"
#include "httprequesthandler.h"
/**
Listens for incoming TCP connections and passes control to
one of the pooled connection handlers. This class is also
responsible for managing the pool.
<p>
Example for the required settings in the config file:
<code><pre>
port=8080
minThreads=1
maxThreads=10
cleanupInterval=1000
readTimeout=60000
maxRequestSize=16000
maxMultiPartSize=1000000
</pre></code>
The port number is the incoming TCP port that this listener listens to.
@see HttpConnectionHandlerPool for description of config settings minThreads, maxThreads and cleanupInterval
@see HttpConnectionHandler for description of config settings readTimeout
@see HttpRequest for description of config settings maxRequestSize and maxMultiPartSize
*/
class HttpListener : public QTcpServer {
Q_OBJECT
Q_DISABLE_COPY(HttpListener)
public:
/**
Constructor.
@param settings Configuration settings for the HTTP server. Must not be 0.
@param requestHandler Processes each received HTTP request, usually by dispatching to controller classes.
@param parent Parent object
*/
HttpListener(QSettings* settings, HttpRequestHandler* requestHandler, QObject* parent = 0);
/** Destructor */
virtual ~HttpListener();
protected:
/** Serves new incoming connection requests */
void incomingConnection(int socketDescriptor);
private:
/** Configuration settings for the HTTP server */
QSettings* settings;
/** Pool of connection handlers */
HttpConnectionHandlerPool* pool;
signals:
/**
Emitted when the connection handler shall process a new incoming onnection.
@param socketDescriptor references the accepted connection.
*/
void handleConnection(int socketDescriptor);
};
#endif // LISTENER_H

View File

@ -0,0 +1,431 @@
/**
@file
@author Stefan Frings
*/
#include "httprequest.h"
#include <QList>
#include <QDir>
#include "httpcookie.h"
HttpRequest::HttpRequest(QSettings* settings) {
status=waitForRequest;
currentSize=0;
expectedBodySize=0;
maxSize=settings->value("maxRequestSize","16000000").toInt();
maxMultiPartSize=settings->value("maxMultiPartSize","1000000").toInt();
}
void HttpRequest::readRequest(QTcpSocket& socket) {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: read request");
#endif
int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
QByteArray newData=socket.readLine(toRead).trimmed();
currentSize+=newData.size();
if (!newData.isEmpty()) {
QList<QByteArray> list=newData.split(' ');
if (list.count()!=3 || !list.at(2).contains("HTTP")) {
qWarning("HttpRequest: received broken HTTP request, invalid first line");
status=abort;
}
else {
method=list.at(0);
path=list.at(1);
version=list.at(2);
status=waitForHeader;
}
}
}
void HttpRequest::readHeader(QTcpSocket& socket) {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: read header");
#endif
int toRead=maxSize-currentSize+1; // allow one byte more to be able to detect overflow
QByteArray newData=socket.readLine(toRead).trimmed();
currentSize+=newData.size();
int colon=newData.indexOf(':');
if (colon>0) {
// Received a line with a colon - a header
currentHeader=newData.left(colon);
QByteArray value=newData.mid(colon+1).trimmed();
headers.insert(currentHeader,value);
#ifdef SUPERVERBOSE
qDebug("HttpRequest: received header %s: %s",currentHeader.data(),value.data());
#endif
}
else if (!newData.isEmpty()) {
// received another line - belongs to the previous header
#ifdef SUPERVERBOSE
qDebug("HttpRequest: read additional line of header");
#endif
// Received additional line of previous header
if (headers.contains(currentHeader)) {
headers.insert(currentHeader,headers.value(currentHeader)+" "+newData);
}
}
else {
// received an empty line - end of headers reached
#ifdef SUPERVERBOSE
qDebug("HttpRequest: headers completed");
#endif
// Empty line received, that means all headers have been received
// Check for multipart/form-data
QByteArray contentType=headers.value("Content-Type");
if (contentType.startsWith("multipart/form-data")) {
int posi=contentType.indexOf("boundary=");
if (posi>=0) {
boundary=contentType.mid(posi+9);
}
}
QByteArray contentLength=getHeader("Content-Length");
if (!contentLength.isEmpty()) {
expectedBodySize=contentLength.toInt();
}
if (expectedBodySize==0) {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: expect no body");
#endif
status=complete;
}
else if (boundary.isEmpty() && expectedBodySize+currentSize>maxSize) {
qWarning("HttpRequest: expected body is too large");
status=abort;
}
else if (!boundary.isEmpty() && expectedBodySize>maxMultiPartSize) {
qWarning("HttpRequest: expected multipart body is too large");
status=abort;
}
else {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: expect %i bytes body",expectedBodySize);
#endif
status=waitForBody;
}
}
}
void HttpRequest::readBody(QTcpSocket& socket) {
Q_ASSERT(expectedBodySize!=0);
if (boundary.isEmpty()) {
// normal body, no multipart
#ifdef SUPERVERBOSE
qDebug("HttpRequest: receive body");
#endif
int toRead=expectedBodySize-bodyData.size();
QByteArray newData=socket.read(toRead);
currentSize+=newData.size();
bodyData.append(newData);
if (bodyData.size()>=expectedBodySize) {
status=complete;
}
}
else {
// multipart body, store into temp file
#ifdef SUPERVERBOSE
qDebug("HttpRequest: receiving multipart body");
#endif
if (!tempFile.isOpen()) {
tempFile.open();
}
// Transfer data in 64kb blocks
int fileSize=tempFile.size();
int toRead=expectedBodySize-fileSize;
if (toRead>65536) {
toRead=65536;
}
fileSize+=tempFile.write(socket.read(toRead));
if (fileSize>=maxMultiPartSize) {
qWarning("HttpRequest: received too many multipart bytes");
status=abort;
}
else if (fileSize>=expectedBodySize) {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: received whole multipart body");
#endif
tempFile.flush();
if (tempFile.error()) {
qCritical("HttpRequest: Error writing temp file for multipart body");
}
parseMultiPartFile();
tempFile.close();
status=complete;
}
}
}
void HttpRequest::decodeRequestParams() {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: extract and decode request parameters");
#endif
// Get URL parameters
QByteArray rawParameters;
int questionMark=path.indexOf('?');
if (questionMark>=0) {
rawParameters=path.mid(questionMark+1);
path=path.left(questionMark);
}
// Get request body parameters
QByteArray contentType=headers.value("Content-Type");
if (!bodyData.isEmpty() && (contentType.isEmpty() || contentType.startsWith("application/x-www-form-urlencoded"))) {
if (rawParameters.isEmpty()) {
rawParameters.append('&');
rawParameters.append(bodyData);
}
else {
rawParameters=bodyData;
}
}
// Split the parameters into pairs of value and name
QList<QByteArray> list=rawParameters.split('&');
foreach (QByteArray part, list) {
int equalsChar=part.indexOf('=');
if (equalsChar>=0) {
QByteArray name=part.left(equalsChar).trimmed();
QByteArray value=part.mid(equalsChar+1).trimmed();
parameters.insert(urlDecode(name),urlDecode(value));
}
else if (!part.isEmpty()){
// Name without value
parameters.insert(urlDecode(part),"");
}
}
}
void HttpRequest::extractCookies() {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: extract cookies");
#endif
foreach(QByteArray cookieStr, headers.values("Cookie")) {
QList<QByteArray> list=HttpCookie::splitCSV(cookieStr);
foreach(QByteArray part, list) {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: found cookie %s",part.data());
#endif // Split the part into name and value
QByteArray name;
QByteArray value;
int posi=part.indexOf('=');
if (posi) {
name=part.left(posi).trimmed();
value=part.mid(posi+1).trimmed();
}
else {
name=part.trimmed();
value="";
}
cookies.insert(name,value);
}
}
headers.remove("Cookie");
}
void HttpRequest::readFromSocket(QTcpSocket& socket) {
Q_ASSERT(status!=complete);
if (status==waitForRequest) {
readRequest(socket);
}
else if (status==waitForHeader) {
readHeader(socket);
}
else if (status==waitForBody) {
readBody(socket);
}
if (currentSize>maxSize) {
qWarning("HttpRequest: received too many bytes");
status=abort;
}
if (status==complete) {
// Extract and decode request parameters from url and body
decodeRequestParams();
// Extract cookies from headers
extractCookies();
}
}
HttpRequest::RequestStatus HttpRequest::getStatus() const {
return status;
}
QByteArray HttpRequest::getMethod() const {
return method;
}
QByteArray HttpRequest::getPath() const {
return urlDecode(path);
}
QByteArray HttpRequest::getVersion() const {
return version;
}
QByteArray HttpRequest::getHeader(const QByteArray& name) const {
return headers.value(name);
}
QList<QByteArray> HttpRequest::getHeaders(const QByteArray& name) const {
return headers.values(name);
}
QMultiMap<QByteArray,QByteArray> HttpRequest::getHeaderMap() const {
return headers;
}
QByteArray HttpRequest::getParameter(const QByteArray& name) const {
return parameters.value(name);
}
QList<QByteArray> HttpRequest::getParameters(const QByteArray& name) const {
return parameters.values(name);
}
QMultiMap<QByteArray,QByteArray> HttpRequest::getParameterMap() const {
return parameters;
}
QByteArray HttpRequest::getBody() const {
return bodyData;
}
QByteArray HttpRequest::urlDecode(const QByteArray source) {
QByteArray buffer(source);
buffer.replace('+',' ');
int percentChar=buffer.indexOf('%');
while (percentChar>=0) {
bool ok;
char byte=buffer.mid(percentChar+1,2).toInt(&ok,16);
if (ok) {
buffer.replace(percentChar,3,(char*)&byte,1);
}
percentChar=buffer.indexOf('%',percentChar+1);
}
return buffer;
}
void HttpRequest::parseMultiPartFile() {
qDebug("HttpRequest: parsing multipart temp file");
tempFile.seek(0);
bool finished=false;
while (!tempFile.atEnd() && !finished && !tempFile.error()) {
#ifdef SUPERVERBOSE
qDebug("HttpRequest: reading multpart headers");
#endif
QByteArray fieldName;
QByteArray fileName;
while (!tempFile.atEnd() && !finished && !tempFile.error()) {
QByteArray line=tempFile.readLine(65536).trimmed();
if (line.startsWith("Content-Disposition:")) {
if (line.contains("form-data")) {
int start=line.indexOf(" name=\"");
int end=line.indexOf("\"",start+7);
if (start>=0 && end>=start) {
fieldName=line.mid(start+7,end-start-7);
}
start=line.indexOf(" filename=\"");
end=line.indexOf("\"",start+11);
if (start>=0 && end>=start) {
fileName=line.mid(start+11,end-start-11);
}
#ifdef SUPERVERBOSE
qDebug("HttpRequest: multipart field=%s, filename=%s",fieldName.data(),fileName.data());
#endif
}
else {
qDebug("HttpRequest: ignoring unsupported content part %s",line.data());
}
}
else if (line.isEmpty()) {
break;
}
}
#ifdef SUPERVERBOSE
qDebug("HttpRequest: reading multpart data");
#endif
QTemporaryFile* uploadedFile=0;
QByteArray fieldValue;
while (!tempFile.atEnd() && !finished && !tempFile.error()) {
QByteArray line=tempFile.readLine(65536);
if (line.startsWith("--"+boundary)) {
// Boundary found. Until now we have collected 2 bytes too much,
// so remove them from the last result
if (fileName.isEmpty() && !fieldName.isEmpty()) {
// last field was a form field
fieldValue.remove(fieldValue.size()-2,2);
parameters.insert(fieldName,fieldValue);
qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fieldValue.data());
}
else if (!fileName.isEmpty() && !fieldName.isEmpty()) {
// last field was a file
#ifdef SUPERVERBOSE
qDebug("HttpRequest: finishing writing to uploaded file");
#endif
uploadedFile->resize(uploadedFile->size()-2);
uploadedFile->flush();
uploadedFile->seek(0);
parameters.insert(fieldName,fileName);
qDebug("HttpRequest: set parameter %s=%s",fieldName.data(),fileName.data());
uploadedFiles.insert(fieldName,uploadedFile);
qDebug("HttpRequest: uploaded file size is %i",(int) uploadedFile->size());
}
if (line.contains(boundary+"--")) {
finished=true;
}
break;
}
else {
if (fileName.isEmpty() && !fieldName.isEmpty()) {
// this is a form field.
currentSize+=line.size();
fieldValue.append(line);
}
else if (!fileName.isEmpty() && !fieldName.isEmpty()) {
// this is a file
if (!uploadedFile) {
uploadedFile=new QTemporaryFile();
uploadedFile->open();
}
uploadedFile->write(line);
if (uploadedFile->error()) {
qCritical("HttpRequest: error writing temp file, %s",qPrintable(uploadedFile->errorString()));
}
}
}
}
}
if (tempFile.error()) {
qCritical("HttpRequest: cannot read temp file, %s",qPrintable(tempFile.errorString()));
}
#ifdef SUPERVERBOSE
qDebug("HttpRequest: finished parsing multipart temp file");
#endif
}
HttpRequest::~HttpRequest() {
foreach(QByteArray key, uploadedFiles.keys()) {
QTemporaryFile* file=uploadedFiles.value(key);
file->close();
delete file;
}
}
QTemporaryFile* HttpRequest::getUploadedFile(const QByteArray fieldName) {
return uploadedFiles.value(fieldName);
}
QByteArray HttpRequest::getCookie(const QByteArray& name) const {
return cookies.value(name);
}
/** Get the map of cookies */
QMap<QByteArray,QByteArray>& HttpRequest::getCookieMap() {
return cookies;
}

View File

@ -0,0 +1,212 @@
/**
@file
@author Stefan Frings
*/
#ifndef HTTPREQUEST_H
#define HTTPREQUEST_H
#include <QByteArray>
#include <QTcpSocket>
#include <QMap>
#include <QMultiMap>
#include <QSettings>
#include <QTemporaryFile>
#include <QUuid>
/**
This object represents a single HTTP request. It reads the request
from a TCP socket and provides getters for the individual parts
of the request.
<p>
The follwing config settings are required:
<code><pre>
maxRequestSize=16000
maxMultiPartSize=1000000
</pre></code>
<p>
MaxRequestSize is the maximum size of a HTTP request. In case of
multipart/form-data requests (also known as file-upload), the maximum
size of the body must not exceed maxMultiPartSize.
The body is always a little larger than the file itself.
*/
class HttpRequest {
Q_DISABLE_COPY(HttpRequest)
friend class HttpSessionStore;
public:
/** Values for getStatus() */
enum RequestStatus {waitForRequest, waitForHeader, waitForBody, complete, abort};
/**
Constructor.
@param settings Configuration settings
*/
HttpRequest(QSettings* settings);
/**
Destructor.
*/
virtual ~HttpRequest();
/**
Read the request from a socket. This method must be called repeatedly
until the status is RequestStatus::complete or RequestStatus::abort.
@param socket Source of the data
*/
void readFromSocket(QTcpSocket& socket);
/**
Get the status of this reqeust.
@see RequestStatus
*/
RequestStatus getStatus() const;
/** Get the method of the HTTP request (e.g. "GET") */
QByteArray getMethod() const;
/** Get the decoded path of the HTPP request (e.g. "/index.html") */
QByteArray getPath() const;
/** Get the version of the HTPP request (e.g. "HTTP/1.1") */
QByteArray getVersion() const;
/**
Get the value of a HTTP request header.
@param name Name of the header
@return If the header occurs multiple times, only the last
one is returned.
*/
QByteArray getHeader(const QByteArray& name) const;
/**
Get the values of a HTTP request header.
@param name Name of the header
*/
QList<QByteArray> getHeaders(const QByteArray& name) const;
/** Get all HTTP request headers */
QMultiMap<QByteArray,QByteArray> getHeaderMap() const;
/**
Get the value of a HTTP request parameter.
@param name Name of the parameter
@return If the parameter occurs multiple times, only the last
one is returned.
*/
QByteArray getParameter(const QByteArray& name) const;
/**
Get the values of a HTTP request parameter.
@param name Name of the parameter
*/
QList<QByteArray> getParameters(const QByteArray& name) const;
/** Get all HTTP request parameters */
QMultiMap<QByteArray,QByteArray> getParameterMap() const;
/** Get the HTTP request body */
QByteArray getBody() const;
/**
Decode an URL parameter.
E.g. replace "%23" by '#' and replace '+' by ' '.
@param source The url encoded strings
@see QUrl::toPercentEncoding for the reverse direction
*/
static QByteArray urlDecode(const QByteArray source);
/**
Get an uploaded file. The file is already open. It will
be closed and deleted by the destructor of this HttpRequest
object (after processing the request).
<p>
For uploaded files, the method getParameters() returns
the original fileName as provided by the calling web browser.
*/
QTemporaryFile* getUploadedFile(const QByteArray fieldName);
/**
Get the value of a cookie
@param name Name of the cookie
*/
QByteArray getCookie(const QByteArray& name) const;
/** Get the map of cookies */
QMap<QByteArray,QByteArray>& getCookieMap();
private:
/** Request headers */
QMultiMap<QByteArray,QByteArray> headers;
/** Parameters of the request */
QMultiMap<QByteArray,QByteArray> parameters;
/** Uploaded files of the request, key is the field name. */
QMap<QByteArray,QTemporaryFile*> uploadedFiles;
/** Received cookies */
QMap<QByteArray,QByteArray> cookies;
/** Storage for raw body data */
QByteArray bodyData;
/** Request method */
QByteArray method;
/** Request path (in raw encoded format) */
QByteArray path;
/** Request protocol version */
QByteArray version;
/**
Status of this request.
@see RequestStatus
*/
RequestStatus status;
/** Maximum size of requests in bytes. */
int maxSize;
/** Maximum allowed size of multipart forms in bytes. */
int maxMultiPartSize;
/** Current size */
int currentSize;
/** Expected size of body */
int expectedBodySize;
/** Name of the current header, or empty if no header is being processed */
QByteArray currentHeader;
/** Boundary of multipart/form-data body. Empty if there is no such header */
QByteArray boundary;
/** Temp file, that is used to store the multipart/form-data body */
QTemporaryFile tempFile;
/** Parset he multipart body, that has been stored in the temp file. */
void parseMultiPartFile();
/** Sub-procedure of readFromSocket(), read the first line of a request. */
void readRequest(QTcpSocket& socket);
/** Sub-procedure of readFromSocket(), read header lines. */
void readHeader(QTcpSocket& socket);
/** Sub-procedure of readFromSocket(), read the request body. */
void readBody(QTcpSocket& socket);
/** Sub-procedure of readFromSocket(), extract and decode request parameters. */
void decodeRequestParams();
/** Sub-procedure of readFromSocket(), extract cookies from headers */
void extractCookies();
};
#endif // HTTPREQUEST_H

View File

@ -0,0 +1,19 @@
/**
@file
@author Stefan Frings
*/
#include "httprequesthandler.h"
HttpRequestHandler::HttpRequestHandler(QObject* parent)
: QObject(parent)
{}
HttpRequestHandler::~HttpRequestHandler() {}
void HttpRequestHandler::service(HttpRequest& request, HttpResponse& response) {
qCritical("HttpRequestHandler: you need to override the dispatch() function");
qDebug("HttpRequestHandler: request=%s %s %s",request.getMethod().data(),request.getPath().data(),request.getVersion().data());
response.setStatus(501,"not implemented");
response.write("501 not implemented",true);
}

View File

@ -0,0 +1,45 @@
/**
@file
@author Stefan Frings
*/
#ifndef HTTPREQUESTHANDLER_H
#define HTTPREQUESTHANDLER_H
#include "httprequest.h"
#include "httpresponse.h"
/**
The request handler generates a response for each HTTP request. Web Applications
usually have one central request handler that maps incoming requests to several
controllers (servlets) based on the requested path.
<p>
You need to override the service() method or you will always get an HTTP error 501.
<p>
@warning Be aware that the main request handler instance must be created on the heap and
that it is used by multiple threads simultaneously.
@see StaticFileController which delivers static local files.
*/
class HttpRequestHandler : public QObject {
Q_OBJECT
Q_DISABLE_COPY(HttpRequestHandler)
public:
/** Constructor */
HttpRequestHandler(QObject* parent=0);
/** Destructor */
virtual ~HttpRequestHandler();
/**
Generate a response for an incoming HTTP request.
@param request The received HTTP request
@param response Must be used to return the response
@warning This method must be thread safe
*/
virtual void service(HttpRequest& request, HttpResponse& response);
};
#endif // HTTPREQUESTHANDLER_H

View File

@ -0,0 +1,132 @@
/**
@file
@author Stefan Frings
*/
#include "httpresponse.h"
HttpResponse::HttpResponse(QTcpSocket* socket) {
this->socket=socket;
statusCode=200;
statusText="OK";
sentHeaders=false;
sentLastPart=false;
}
void HttpResponse::setHeader(QByteArray name, QByteArray value) {
//Q_ASSERT(sentHeaders==false);
headers.insert(name,value);
}
void HttpResponse::setHeader(QByteArray name, int value) {
//Q_ASSERT(sentHeaders==false);
headers.insert(name,QByteArray::number(value));
}
QMap<QByteArray,QByteArray>& HttpResponse::getHeaders() {
return headers;
}
void HttpResponse::setStatus(int statusCode, QByteArray description) {
this->statusCode=statusCode;
statusText=description;
}
void HttpResponse::writeHeaders() {
//Q_ASSERT(sentHeaders==false);
QByteArray buffer;
buffer.append("HTTP/1.1 ");
buffer.append(QByteArray::number(statusCode));
buffer.append(' ');
buffer.append(statusText);
buffer.append("\r\n");
foreach(QByteArray name, headers.keys()) {
buffer.append(name);
buffer.append(": ");
buffer.append(headers.value(name));
buffer.append("\r\n");
}
foreach(HttpCookie cookie,cookies.values()) {
buffer.append("Set-Cookie: ");
buffer.append(cookie.toByteArray());
buffer.append("\r\n");
}
buffer.append("\r\n");
writeToSocket(buffer);
sentHeaders=true;
}
bool HttpResponse::writeToSocket(QByteArray data) {
int remaining=data.size();
char* ptr=data.data();
while (socket->isOpen() && remaining>0) {
// Wait until the previous buffer content is written out, otherwise it could become very large
socket->waitForBytesWritten(-1);
int written=socket->write(ptr,remaining);
if (written==-1) {
return false;
}
ptr+=written;
remaining-=written;
}
return true;
}
void HttpResponse::write(QByteArray data, bool lastPart) {
//Q_ASSERT(sentLastPart==false);
if (sentHeaders==false) {
QByteArray connectionMode=headers.value("Connection");
if (!headers.contains("Content-Length") && !headers.contains("Transfer-Encoding") && connectionMode!="close" && connectionMode!="Close") {
if (!lastPart) {
headers.insert("Transfer-Encoding","chunked");
}
else {
headers.insert("Content-Length",QByteArray::number(data.size()));
}
}
writeHeaders();
}
bool chunked=headers.value("Transfer-Encoding")=="chunked" || headers.value("Transfer-Encoding")=="Chunked";
if (chunked) {
if (data.size()>0) {
QByteArray buffer=QByteArray::number(data.size(),16);
buffer.append("\r\n");
writeToSocket(buffer);
writeToSocket(data);
writeToSocket("\r\n");
}
}
else {
writeToSocket(data);
}
if (lastPart) {
if (chunked) {
writeToSocket("0\r\n\r\n");
}
else if (!headers.contains("Content-Length")) {
socket->disconnectFromHost();
}
sentLastPart=true;
}
}
void HttpResponse::writeText(QString text, bool lastPart)
{
write(text.toAscii(),lastPart);
}
bool HttpResponse::hasSentLastPart() const {
return sentLastPart;
}
void HttpResponse::setCookie(const HttpCookie& cookie) {
//Q_ASSERT(sentHeaders==false);
if (!cookie.getName().isEmpty()) {
cookies.insert(cookie.getName(),cookie);
}
}
QMap<QByteArray,HttpCookie>& HttpResponse::getCookies() {
return cookies;
}

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