diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 3308f936..51e0b35c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,34 @@ +8.0.0 +Reading lists +Tags +'Favorites' and 'being read' lists +New search engine, now you can filter folders and comics +New grid view +Add and delete folders +Update a single folder (no need for updating the whole library to rescan a single folder) +Drag and drop for adding new comics and folders +Customizable shorcuts +Manga mode (thank you Felix) +Spread page detection for double page mode (including manga mode)(thank you again Felix :) ) +New view for folders not containing comics (only subfolders) +Save selected covers to disk +Comics in Reading Lists and Tags can be sorted by drag&drop +Sublist in Reading Lists can by sorted by drag&drop +Added WebP image format support +The user has to provide its own Comic Vine API key to prevent usage limit errors from Comic Vine +New unarr decompression backend for Linux and Unix systems +Fixed memory and filedescriptor leaks in 7zip wrapper +Dropped support for Qt4 +Lots of smaller bugfixes + +7.2.0 +Added support for the new "remote read" feature in iOS devices. +Improved stability +Fixed broken compatibility with Windows XP +Improved Linux "packageability" (thanks to Felix, Alexander and Yoann)** +German translation (thanks to Gerhard Joeken) +Bug fixes. + 7.1.0 Añadida opción para resetear el rating de un comics Corregidos bugs que afectaban a la información de página. diff --git a/INSTALL.txt b/INSTALL.txt index 55eef0b0..3863450e 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -2,14 +2,57 @@ COMPILATION GUIDE FOR LINUX/UNIX USERS ********************************** YACReader and YACReaderLibrary are build using qmake. To build and install the program, run: -qmake +qmake-qt5 CONFIG+=[Options] make make install from the source dir. For seperate builds of YACReader or YACReaderLibrary, enter their respective subfolders and run the commands from there. -Build options: +Dependencies: +----------------------- + +- Qt >= 5.3 with the following modules: + - declarative + - sql + - script + - multimedia + - imageformats + - opengl + - sql-sqlite + +- poppler-qt5 +- qrencode +- sqllite +- glu +- a decompression backend, either 7zip or unarr (see below) + + +Decompression backend: +--------------------------------------- + +YACReader supports two decompression backends: + +- unarr +- p7zip=9.20.1 + +These can be selected at build time by adding either CONFIG+=unarr or CONFIG+=7zip as an option when running qmake. +If none of these is provided, the build system will default to unarr on Linux/Unix and [p]7zip on Windows and OS X. + +The decompression backends have their own dependencies and require additional steps like downloading additional source code +for setup. Please consult the README files provided in the compressed_archive and compressed_archive/unarr folders for details. + +If you chose to build YACReader with p7zip as a backend on Linux/Unix, please take notice that this backend has some problems: + +- p7zip > 9.20.1 is not supported +- p7zip 9.20.1 has known bugs that are fixed only in the later versions +- a system update that replaces p7zip 9.20.1 with a later version will make YACReader unusable + +If your system already ships with p7zip > 9.20.1 you can place 7z.so and the Codecs folder with the Rar29.so from p7zip 9.20.1 in /usr/lib/yacreader +YACReader will check this folder first and can thus continue using 7zip as a backend with p7zip > 9.20.1 installed on your system. + +Other build options: --------------------- + You can adjust the installation prefix as well als the path make install uses to install the files. Use "qmake PREFIX=DIR" to configure YACReader for your systems default prefix (for example "/", "/usr", "/usr/local"). @@ -24,7 +67,7 @@ On embedded devices that don't support desktop OpenGL, it is recommended to use qmake CONFIG+=no_opengl -This will remove any dependency on desktop OpenGL. +This will remove any dependency on desktop OpenGL and hardlock rendering to software. DO YOU WANT TO HELP YACREADER? diff --git a/YACReader.pro b/YACReader.pro index bfc8dc47..528d3c41 100644 --- a/YACReader.pro +++ b/YACReader.pro @@ -1,3 +1,3 @@ TEMPLATE = subdirs SUBDIRS = YACReader YACReaderLibrary -YACReaderLibrary.depends = YACReader +YACReaderLibrary.depends = YACReader \ No newline at end of file diff --git a/YACReader/YACReader.pri b/YACReader/YACReader.pri deleted file mode 100644 index 446932e8..00000000 --- a/YACReader/YACReader.pri +++ /dev/null @@ -1,164 +0,0 @@ -INCLUDEPATH += $$PWD -DEPENDPATH += $$PWD -INCLUDEPATH += . -INCLUDEPATH += $$PWD/../common \ - $$PWD/../custom_widgets - -CONFIG(legacy_gl_widget) { - INCLUDEPATH += ../common/gl_legacy \ -} else { - INCLUDEPATH += ../common/gl \ -} - -win32 { -LIBS += -L$$PWD/../dependencies/poppler/lib -loleaut32 -lole32 - -LIBS += -lpoppler-qt5 -INCLUDEPATH += ../dependencies/poppler/include/qt5 - -QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT -!CONFIG(no_opengl) { - QMAKE_CXXFLAGS_RELEASE += /GL -} -QMAKE_LFLAGS_RELEASE += /LTCG -CONFIG -= embed_manifest_exe -} - -unix:!macx{ - -INCLUDEPATH += /usr/include/poppler/qt5 -LIBS += -L/usr/lib -lpoppler-qt5 - -!CONFIG(no_opengl) { - LIBS += -lGLU -} - -} - -macx{ -#INCLUDEPATH += "/Volumes/Mac OS X Lion/usr/X11/include" -#isEqual(QT_MAJOR_VERSION, 5) { -#INCLUDEPATH += /usr/local/include/poppler/qt5 -#LIBS += -L/usr/local/lib -lpoppler-qt5 -#} -#else { -#INCLUDEPATH += /usr/local/include/poppler/qt4 -#LIBS += -L/usr/local/lib -lpoppler-qt4 -#} -CONFIG += objective_c -QT += macextras gui-private - - -LIBS += -framework Foundation -framework ApplicationServices -framework AppKit - -OBJECTIVE_SOURCES += $$PWD/../common/pdf_comic.mm -HEADERS += $$PWD/../common/pdf_comic.h -} - -QT += network widgets core -!CONFIG(no_opengl) { - QT += opengl -} - -#CONFIG += release -CONFIG -= flat - -QT += multimedia - -# Input -HEADERS += $$PWD/../common/comic.h \ - $$PWD/configuration.h \ - $$PWD/goto_dialog.h \ - $$PWD/magnifying_glass.h \ - $$PWD/main_window_viewer.h \ - $$PWD/viewer.h \ - $$PWD/goto_flow.h \ - $$PWD/options_dialog.h \ - $$PWD/../common/bookmarks.h \ - $$PWD/bookmarks_dialog.h \ - $$PWD/render.h \ - $$PWD/shortcuts_dialog.h \ - $$PWD/translator.h \ - $$PWD/goto_flow_widget.h \ - $$PWD/page_label_widget.h \ - $$PWD/goto_flow_toolbar.h \ - $$PWD/goto_flow_decorationbar.h \ - $$PWD/width_slider.h \ - $$PWD/notifications_label_widget.h \ - $$PWD/../common/pictureflow.h \ - $$PWD/../common/custom_widgets.h \ - $$PWD/../common/check_new_version.h \ - $$PWD/../common/qnaturalsorting.h \ - $$PWD/../common/yacreader_global.h \ - $$PWD/../common/onstart_flow_selection_dialog.h \ - $$PWD/../common/comic_db.h \ - $$PWD/../common/folder.h \ - $$PWD/../common/library_item.h \ - $$PWD/yacreader_local_client.h \ - $$PWD/../common/http_worker.h \ - $$PWD/../common/exit_check.h \ - $$PWD/../common/scroll_management.h - -!CONFIG(no_opengl) { - CONFIG(legacy_gl_widget) { - message("using legacy YACReaderFlowGL (QGLWidget) header") - HEADERS += ../common/gl_legacy/yacreader_flow_gl.h - } else { - HEADERS += ../common/gl/yacreader_flow_gl.h - } - HEADERS += $$PWD/goto_flow_gl.h -} - -SOURCES += $$PWD/../common/comic.cpp \ - $$PWD/configuration.cpp \ - $$PWD/goto_dialog.cpp \ - $$PWD/magnifying_glass.cpp \ - $$PWD/main_window_viewer.cpp \ - $$PWD/viewer.cpp \ - $$PWD/goto_flow.cpp \ - $$PWD/options_dialog.cpp \ - $$PWD/../common/bookmarks.cpp \ - $$PWD/bookmarks_dialog.cpp \ - $$PWD/render.cpp \ - $$PWD/shortcuts_dialog.cpp \ - $$PWD/translator.cpp \ - $$PWD/goto_flow_widget.cpp \ - $$PWD/page_label_widget.cpp \ - $$PWD/goto_flow_toolbar.cpp \ - $$PWD/goto_flow_decorationbar.cpp \ - $$PWD/width_slider.cpp \ - $$PWD/notifications_label_widget.cpp \ - $$PWD/../common/pictureflow.cpp \ - $$PWD/../common/custom_widgets.cpp \ - $$PWD/../common/check_new_version.cpp \ - $$PWD/../common/qnaturalsorting.cpp \ - $$PWD/../common/onstart_flow_selection_dialog.cpp \ - $$PWD/../common/comic_db.cpp \ - $$PWD/../common/folder.cpp \ - $$PWD/../common/library_item.cpp \ - $$PWD/yacreader_local_client.cpp \ - $$PWD/../common/http_worker.cpp \ - $$PWD/../common/yacreader_global.cpp \ - $$PWD/../common/exit_check.cpp \ - $$PWD/../common/scroll_management.cpp - -!CONFIG(no_opengl) { - CONFIG(legacy_gl_widget) { - message("using legacy YACReaderFlowGL (QGLWidget) source code") - SOURCES += ../common/gl_legacy/yacreader_flow_gl.cpp - } else { - SOURCES += ../common/gl/yacreader_flow_gl.cpp - } - SOURCES += $$PWD/goto_flow_gl.cpp -} - -include($$PWD/../custom_widgets/custom_widgets_yacreader.pri) -include($$PWD/../compressed_archive/wrapper.pri) -include($$PWD/../shortcuts_management/shortcuts_management.pri) - -RESOURCES += $$PWD/yacreader_images.qrc \ - $$PWD/yacreader_files.qrc - -win32:RESOURCES += $$PWD/yacreader_images_win.qrc -unix:!macx:RESOURCES += $$PWD/yacreader_images_win.qrc -macx:RESOURCES += $$PWD/yacreader_images_osx.qrc diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro index 31378ae9..6c0b418e 100644 --- a/YACReader/YACReader.pro +++ b/YACReader/YACReader.pro @@ -8,41 +8,51 @@ DEPENDPATH += . \ DEFINES += NOMINMAX YACREADER -CONFIG(no_opengl) { - DEFINES += NO_OPENGL +#load default build flags +include (../config.pri) + +unix:!macx{ + QMAKE_CXXFLAGS += -std=c++11 } - - unix:!macx{ -QMAKE_CXXFLAGS += -std=c++11 +CONFIG(force_angle) { + Release:DESTDIR = ../release_angle + Debug:DESTDIR = ../debug_angle +} else { + Release:DESTDIR = ../release + Debug:DESTDIR = ../debug } -Release:DESTDIR = ../release -Debug:DESTDIR = ../debug - SOURCES += main.cpp INCLUDEPATH += ../common \ ../custom_widgets -CONFIG(legacy_gl_widget) { +!CONFIG(no_opengl):CONFIG(legacy_gl_widget) { INCLUDEPATH += ../common/gl_legacy \ } else { INCLUDEPATH += ../common/gl \ } +#there are going to be two builds for windows, OpenGL based and ANGLE based win32 { -LIBS += -L../dependencies/poppler/lib -loleaut32 -lole32 + CONFIG(force_angle) { + message("using ANGLE") + LIBS += -L../dependencies/poppler/lib -loleaut32 -lole32 -lshell32 -lopengl32 -lglu32 -luser32 + #linking extra libs are necesary for a successful compilation, a better approach should be + #to remove any OpenGL (desktop) dependencies + #the OpenGL stuff should be migrated to OpenGL ES + DEFINES += FORCE_ANGLE + } else { + LIBS += -L../dependencies/poppler/lib -loleaut32 -lole32 -lshell32 -lopengl32 -lglu32 -luser32 + } -LIBS += -lpoppler-qt5 -INCLUDEPATH += ../dependencies/poppler/include/qt5 + LIBS += -lpoppler-qt5 + INCLUDEPATH += ../dependencies/poppler/include/qt5 -QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT -!CONFIG(no_opengl) { - QMAKE_CXXFLAGS_RELEASE += /GL -} -QMAKE_LFLAGS_RELEASE += /LTCG -CONFIG -= embed_manifest_exe + QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /GL + QMAKE_LFLAGS_RELEASE += /LTCG + CONFIG -= embed_manifest_exe } unix:!macx{ @@ -118,7 +128,8 @@ HEADERS += ../common/comic.h \ yacreader_local_client.h \ ../common/http_worker.h \ ../common/exit_check.h \ - ../common/scroll_management.h + ../common/scroll_management.h \ + ../common/opengl_checker.h !CONFIG(no_opengl) { CONFIG(legacy_gl_widget) { @@ -161,7 +172,8 @@ SOURCES += ../common/comic.cpp \ ../common/http_worker.cpp \ ../common/yacreader_global.cpp \ ../common/exit_check.cpp \ - ../common/scroll_management.cpp + ../common/scroll_management.cpp \ + ../common/opengl_checker.cpp !CONFIG(no_opengl) { CONFIG(legacy_gl_widget) { @@ -174,7 +186,13 @@ SOURCES += ../common/comic.cpp \ } include(../custom_widgets/custom_widgets_yacreader.pri) +CONFIG(7zip){ include(../compressed_archive/wrapper.pri) +} else:CONFIG(unarr){ +include(../compressed_archive/unarr/unarr-wrapper.pri) +} else { + error(No compression backend specified. Did you mess with the build system?) + } include(../shortcuts_management/shortcuts_management.pri) RESOURCES += yacreader_images.qrc \ @@ -202,24 +220,6 @@ TRANSLATIONS = yacreader_es.ts \ yacreader_de.ts \ yacreader_source.ts - -win32 { -!exists (../compressed_archive/lib7zip) { - error(You\'ll need 7zip source code to compile YACReader. \ - Please check the compressed_archive folder for further instructions.) -} -} - -unix { -exists (../compressed_archive/libp7zip) { - message(Found p7zip source code...) - system(patch -d ../compressed_archive -N -p0 -i libp7zip.patch) -} else { - error(You\'ll need 7zip source code to compile YACReader. \ - Please check the compressed_archive folder for further instructions.) -} -} - unix:!macx { #set install prefix if it's empty isEmpty(PREFIX) { diff --git a/YACReader/main.cpp b/YACReader/main.cpp index 1a85e808..fd01d04e 100644 --- a/YACReader/main.cpp +++ b/YACReader/main.cpp @@ -59,6 +59,7 @@ class YACReaderApplication: public QApplication int main(int argc, char * argv[]) { + #if defined(_MSC_VER) && defined(_DEBUG) _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); #endif @@ -78,9 +79,13 @@ int main(int argc, char * argv[]) QApplication app(argc, argv); #endif +#ifdef FORCE_ANGLE + app.setAttribute(Qt::AA_UseOpenGLES); +#endif + app.setApplicationName("YACReader"); app.setOrganizationName("YACReader"); - qApp->setAttribute(Qt::AA_UseHighDpiPixmaps); + app.setAttribute(Qt::AA_UseHighDpiPixmaps); //simple command line parser //will be replaced by QCommandLineParser in the future QStringList optlist; diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp index ec604098..3809482b 100644 --- a/YACReader/main_window_viewer.cpp +++ b/YACReader/main_window_viewer.cpp @@ -354,7 +354,7 @@ void MainWindowViewer::createActions() setBookmarkAction->setCheckable(true); setBookmarkAction->setData(SET_BOOKMARK_ACTION_Y); setBookmarkAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SET_BOOKMARK_ACTION_Y)); - connect(setBookmarkAction,SIGNAL(triggered (bool)),viewer,SLOT(setBookmarkAction(bool))); + connect(setBookmarkAction,SIGNAL(triggered (bool)),viewer,SLOT(setBookmark(bool))); connect(viewer,SIGNAL(pageAvailable(bool)),setBookmarkAction,SLOT(setEnabled(bool))); connect(viewer,SIGNAL(pageIsBookmark(bool)),setBookmarkAction,SLOT(setChecked(bool))); @@ -578,7 +578,7 @@ void MainWindowViewer::createToolBars() #ifdef Q_OS_MAC comicToolBar->addStretch(); #else - comicToolBar->addWidget(new QToolBarStretch()); + comicToolBar->addWidget(new YACReaderToolBarStretch()); #endif @@ -671,7 +671,11 @@ void MainWindowViewer::reloadOptions() void MainWindowViewer::open() { QFileDialog openDialog; +#ifndef use_unarr QString pathFile = openDialog.getOpenFileName(this,tr("Open Comic"),currentDirectory,tr("Comic files") + "(*.cbr *.cbz *.rar *.zip *.tar *.pdf *.7z *.cb7 *.arj *.cbt)"); +#else + QString pathFile = openDialog.getOpenFileName(this,tr("Open Comic"),currentDirectory,tr("Comic files") + "(*.cbr *.cbz *.rar *.zip *.tar *.pdf *.cbt)"); +#endif if (!pathFile.isEmpty()) { openComicFromPath(pathFile); @@ -1315,7 +1319,11 @@ void MainWindowViewer::getSiblingComics(QString path,QString currentComic) { QDir d(path); d.setFilter(QDir::Files|QDir::NoDotAndDotDot); +#ifndef use_unarr d.setNameFilters(QStringList() << "*.cbr" << "*.cbz" << "*.rar" << "*.zip" << "*.tar" << "*.pdf" << "*.7z" << "*.cb7" << "*.arj" << "*.cbt"); +#else + d.setNameFilters(QStringList() << "*.cbr" << "*.cbz" << "*.rar" << "*.zip" << "*.tar" << "*.pdf" << "*.cbt"); +#endif d.setSorting(QDir::Name|QDir::IgnoreCase|QDir::LocaleAware); QStringList list = d.entryList(); qSort(list.begin(),list.end(),naturalSortLessThanCI); diff --git a/YACReader/render.cpp b/YACReader/render.cpp index 26549d34..87cab589 100644 --- a/YACReader/render.cpp +++ b/YACReader/render.cpp @@ -431,9 +431,9 @@ Render::~Render() delete pr; } } -//Este método se encarga de forzar el renderizado de las páginas. -//Actualiza el buffer según es necesario. -//si la pagina actual no está renderizada, se lanza un hilo que la renderize (double or single page mode) y se emite una señal que indica que se está renderizando. +//Este método se encarga de forzar el renderizado de las páginas. +//Actualiza el buffer según es necesario. +//si la pagina actual no está renderizada, se lanza un hilo que la renderize (double or single page mode) y se emite una señal que indica que se está renderizando. void Render::render() { updateBuffer(); @@ -446,30 +446,30 @@ void Render::render() pageRenders[currentPageBufferedIndex] = new PageRender(this,currentIndex,comic->getRawData()->at(currentIndex),buffer[currentPageBufferedIndex],imageRotation,filters); } else - //las páginas no están listas, y se están cargando en el cómic - emit processingPage(); //para evitar confusiones esta señal debería llamarse de otra forma + //las páginas no están listas, y se están cargando en el cómic + emit processingPage(); //para evitar confusiones esta señal debería llamarse de otra forma - //si se ha creado un hilo para renderizar la página actual, se arranca + //si se ha creado un hilo para renderizar la página actual, se arranca if(pageRenders[currentPageBufferedIndex]!=0) { - //se conecta la señal pageReady del hilo, con el SLOT prepareAvailablePage + //se conecta la señal pageReady del hilo, con el SLOT prepareAvailablePage connect(pageRenders[currentPageBufferedIndex],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int))); - //se emite la señal de procesando, debido a que los hilos se arrancan aquí + //se emite la señal de procesando, debido a que los hilos se arrancan aquí if(filters.size()>0) emit processingPage(); pageRenders[currentPageBufferedIndex]->start(); pageRenders[currentPageBufferedIndex]->setPriority(QThread::TimeCriticalPriority); } else - //en qué caso sería necesario hacer esto??? //TODO: IMPORTANTE, puede que no sea necesario. + //en qué caso sería necesario hacer esto??? //TODO: IMPORTANTE, puede que no sea necesario. emit processingPage(); } else - //no hay ninguna página lista para ser renderizada, es necesario esperar. + //no hay ninguna página lista para ser renderizada, es necesario esperar. emit processingPage(); } else - // la página actual está lista + // la página actual está lista { //emit currentPageReady(); //make prepareAvailablePage the only function that emits currentPageReady() @@ -768,7 +768,7 @@ void Render::createComic(const QString & path) comic = FactoryComic::newComic(path); - if(comic == NULL)//archivo no encontrado o no válido + if(comic == NULL)//archivo no encontrado o no válido { emit errorOpening(); reset(); @@ -788,7 +788,6 @@ void Render::createComic(const QString & path) connect(comic,SIGNAL(numPages(unsigned int)),this,SLOT(setNumPages(unsigned int))); connect(comic,SIGNAL(imageLoaded(int,QByteArray)),this,SIGNAL(imageLoaded(int,QByteArray))); connect(comic,SIGNAL(isBookmark(bool)),this,SIGNAL(currentPageIsBookmark(bool))); - connect(comic,SIGNAL(isBookmark(bool)),this,SLOT(pageIsBookmark(bool))); connect(comic,SIGNAL(bookmarksUpdated()),this,SIGNAL(bookmarksUpdated())); @@ -814,6 +813,9 @@ void Render::startLoad() comic->moveToThread(thread); + connect(comic, SIGNAL(errorOpening()), thread, SLOT(quit())); + connect(comic, SIGNAL(errorOpening(QString)), thread, SLOT(quit())); + connect(comic, SIGNAL(imagesLoaded()), thread, SLOT(quit())); connect(thread, SIGNAL(started()), comic, SLOT(process())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); @@ -836,13 +838,13 @@ void Render::reset() loadedComic = false; invalidate(); } -//si se solicita la siguiente página, se calcula cuál debe ser en función de si se lee en modo a doble página o no. -//la página sólo se renderiza, si realmente ha cambiado. +//si se solicita la siguiente página, se calcula cuál debe ser en función de si se lee en modo a doble página o no. +//la página sólo se renderiza, si realmente ha cambiado. void Render::nextPage() { - int nextPage; //indica cuál será la próxima página + int nextPage; //indica cuál será la próxima página nextPage = comic->nextPage(); - //se fuerza renderizado si la página ha cambiado + //se fuerza renderizado si la página ha cambiado if(currentIndex != nextPage) { previousIndex = currentIndex; @@ -880,14 +882,14 @@ void Render::nextDoublePage() } } -//si se solicita la página anterior, se calcula cuál debe ser en función de si se lee en modo a doble página o no. -//la página sólo se renderiza, si realmente ha cambiado. +//si se solicita la página anterior, se calcula cuál debe ser en función de si se lee en modo a doble página o no. +//la página sólo se renderiza, si realmente ha cambiado. void Render::previousPage() { - int previousPage; //indica cuál será la próxima página + int previousPage; //indica cuál será la próxima página previousPage = comic->previousPage(); - //se fuerza renderizado si la página ha cambiado + //se fuerza renderizado si la página ha cambiado if(currentIndex != previousPage) { previousIndex = currentIndex; @@ -903,7 +905,7 @@ void Render::previousPage() void Render::previousDoublePage() { - int previousPage; //indica cuál será la próxima página + int previousPage; //indica cuál será la próxima página previousPage = qMax(currentIndex-2,0); if(currentIndex != previousPage) { @@ -965,7 +967,7 @@ void Render::pageRawDataReady(int page) } } -//sólo se renderiza la página, si ha habido un cambio de página +//sólo se renderiza la página, si ha habido un cambio de página void Render::goTo(int index) { @@ -993,9 +995,9 @@ void Render::rotateLeft() reload(); } -//Actualiza el buffer, añadiendo las imágenes (vacías) necesarias para su posterior renderizado y -//eliminado aquellas que ya no sean necesarias. También libera los hilos (no estoy seguro de que sea responsabilidad suya) -//Calcula el número de nuevas páginas que hay que buferear y si debe hacerlo por la izquierda o la derecha (según sea el sentido de la lectura) +//Actualiza el buffer, añadiendo las imágenes (vacías) necesarias para su posterior renderizado y +//eliminado aquellas que ya no sean necesarias. También libera los hilos (no estoy seguro de que sea responsabilidad suya) +//Calcula el número de nuevas páginas que hay que buferear y si debe hacerlo por la izquierda o la derecha (según sea el sentido de la lectura) void Render::updateBuffer() { QMutexLocker locker(&mutex); @@ -1081,8 +1083,8 @@ void Render::fillBuffer() } -//Método que debe ser llamado cada vez que la estructura del buffer se vuelve inconsistente con el modo de lectura actual. -//se terminan todos los hilos en ejecución y se libera la memoria (de hilos e imágenes) +//Método que debe ser llamado cada vez que la estructura del buffer se vuelve inconsistente con el modo de lectura actual. +//se terminan todos los hilos en ejecución y se libera la memoria (de hilos e imágenes) void Render::invalidate() { for(int i=0;i @@ -72,10 +74,15 @@ shouldOpenPrevious(false) //CONFIG GOTO_FLOW-------------------------------------------------------- #ifndef NO_OPENGL - if(!settings->contains(USE_OPEN_GL)) - { + + OpenGLChecker openGLChecker; + bool openGLAvailable = openGLChecker.hasCompatibleOpenGLVersion(); + + if(openGLAvailable && !settings->contains(USE_OPEN_GL)) settings->setValue(USE_OPEN_GL,2); - } + else + if(!openGLAvailable) + settings->setValue(USE_OPEN_GL,0); if((settings->value(USE_OPEN_GL).toBool() == true)) goToFlow = new GoToFlowGL(this,Configuration::getConfiguration().getFlowType()); diff --git a/YACReaderLibrary/YACReaderLibrary.pro b/YACReaderLibrary/YACReaderLibrary.pro index bd0659ba..2b707b90 100644 --- a/YACReaderLibrary/YACReaderLibrary.pro +++ b/YACReaderLibrary/YACReaderLibrary.pro @@ -15,9 +15,8 @@ INCLUDEPATH += ../common \ DEFINES += SERVER_RELEASE NOMINMAX YACREADER_LIBRARY -CONFIG(no_opengl) { - DEFINES += NO_OPENGL -} +#load default build flags +include (../config.pri) CONFIG(legacy_gl_widget) { INCLUDEPATH += ../common/gl_legacy \ @@ -25,19 +24,25 @@ CONFIG(legacy_gl_widget) { INCLUDEPATH += ../common/gl \ } +#there are going to be two builds for windows, OpenGL based and ANGLE based win32 { + CONFIG(force_angle) { + message("using ANGLE") + LIBS += -L../dependencies/poppler/lib -loleaut32 -lole32 -lshell32 -lopengl32 -lglu32 -luser32 + #linking extra libs are necesary for a successful compilation, a better approach should be + #to remove any OpenGL (desktop) dependencies + #the OpenGL stuff should be migrated to OpenGL ES + DEFINES += FORCE_ANGLE + } else { + LIBS += -L../dependencies/poppler/lib -loleaut32 -lole32 -lshell32 -lopengl32 -lglu32 -luser32 + } -LIBS += -L../dependencies/poppler/lib -loleaut32 -lole32 -lshell32 + LIBS += -lpoppler-qt5 + INCLUDEPATH += ../dependencies/poppler/include/qt5 -LIBS += -lpoppler-qt5 -INCLUDEPATH += ../dependencies/poppler/include/qt5 - -QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT -!CONFIG(no_opengl) { - QMAKE_CXXFLAGS_RELEASE += /GL -} -QMAKE_LFLAGS_RELEASE += /LTCG -CONFIG -= embed_manifest_exe + QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /GL + QMAKE_LFLAGS_RELEASE += /LTCG + CONFIG -= embed_manifest_exe } unix:!macx{ @@ -71,7 +76,7 @@ QT += macextras gui-private } unix{ -QMAKE_CXXFLAGS += -std=c++11 +CONFIG += c++11 } #CONFIG += release @@ -138,7 +143,8 @@ HEADERS += comic_flow.h \ empty_container_info.h \ empty_special_list.h \ empty_reading_list_widget.h \ - ../common/scroll_management.h + ../common/scroll_management.h \ + ../common/opengl_checker.h !CONFIG(no_opengl) { CONFIG(legacy_gl_widget) { @@ -206,7 +212,8 @@ SOURCES += comic_flow.cpp \ empty_container_info.cpp \ empty_special_list.cpp \ empty_reading_list_widget.cpp \ - ../common/scroll_management.cpp + ../common/scroll_management.cpp \ + ../common/opengl_checker.cpp !CONFIG(no_opengl) { CONFIG(legacy_gl_widget) { @@ -220,7 +227,14 @@ SOURCES += comic_flow.cpp \ include(./server/server.pri) include(../custom_widgets/custom_widgets_yacreaderlibrary.pri) +CONFIG(7zip){ include(../compressed_archive/wrapper.pri) +} else:CONFIG(unarr) { +include(../compressed_archive/unarr/unarr-wrapper.pri) +} else { + error(No compression backend specified. Did you mess with the build system?) +} + include(./comic_vine/comic_vine.pri) include(../QsLog/QsLog.pri) include(../shortcuts_management/shortcuts_management.pri) @@ -245,9 +259,13 @@ TRANSLATIONS = yacreaderlibrary_es.ts \ yacreaderlibrary_de.ts \ yacreaderlibrary_source.ts - -Release:DESTDIR = ../release -Debug:DESTDIR = ../debug +CONFIG(force_angle) { + Release:DESTDIR = ../release_angle + Debug:DESTDIR = ../debug_angle +} else { + Release:DESTDIR = ../release + Debug:DESTDIR = ../debug +} #QML/GridView QT += quick qml @@ -263,23 +281,6 @@ win32:RESOURCES += qml_win.qrc unix:!macx:RESOURCES += qml_win.qrc macx:RESOURCES += qml_osx.qrc -win32 { -!exists(../compressed_archive/lib7zip){ - error(You\'ll need 7zip source code to compile YACReader. \ - Please check the compressed_archive folder for further instructions.) -} -} - -unix { -exists (../compressed_archive/libp7zip) { - message(Found p7zip source code...) - system(patch -d ../compressed_archive -N -p0 -i libp7zip.patch) -} else { - error(You\'ll need 7zip source code to compile YACReader. \ - Please check the compressed_archive folder for further instructions.) -} -} - unix:!macx { #set install prefix if it's empty isEmpty(PREFIX) { diff --git a/YACReaderLibrary/classic_comics_view.cpp b/YACReaderLibrary/classic_comics_view.cpp index a94f67f9..4a72fd54 100644 --- a/YACReaderLibrary/classic_comics_view.cpp +++ b/YACReaderLibrary/classic_comics_view.cpp @@ -1,11 +1,12 @@ #include "classic_comics_view.h" -#include "yacreader_table_view.h" +#include "QStackedWidget" #include "comic_flow_widget.h" #include "QsLog.h" - -#include "QStackedWidget" +#include "shortcuts_manager.h" +#include "yacreader_table_view.h" +#include "yacreader_tool_bar_stretch.h" ClassicComicsView::ClassicComicsView(QWidget *parent) :ComicsView(parent),searching(false) @@ -16,6 +17,7 @@ ClassicComicsView::ClassicComicsView(QWidget *parent) settings->beginGroup("libraryConfig"); //FLOW----------------------------------------------------------------------- //--------------------------------------------------------------------------- +//FORCE_ANGLE is not used here, because ComicFlowWidgetGL will use OpenGL ES in the future #ifndef NO_OPENGL if((settings->value(USE_OPEN_GL).toBool() == true)) comicFlow = new ComicFlowWidgetGL(0); @@ -70,6 +72,7 @@ ClassicComicsView::ClassicComicsView(QWidget *parent) connect(tableView, SIGNAL(comicRated(int,QModelIndex)), this, SIGNAL(comicRated(int,QModelIndex))); connect(comicFlow, SIGNAL(selected(uint)), this, SIGNAL(selected(uint))); connect(tableView->horizontalHeader(), SIGNAL(sectionMoved(int,int,int)), this, SLOT(saveTableHeadersStatus())); + connect(tableView->horizontalHeader(), SIGNAL(sectionResized(int,int,int)), this, SLOT(saveTableHeadersStatus())); connect(comicFlow, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(requestedViewContextMenu(QPoint))); connect(tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(requestedItemContextMenu(QPoint))); layout->addWidget(sVertical); @@ -83,11 +86,49 @@ ClassicComicsView::ClassicComicsView(QWidget *parent) if(settings->contains(COMICS_VIEW_FLOW_SPLITTER_STATUS)) sVertical->restoreState(settings->value(COMICS_VIEW_FLOW_SPLITTER_STATUS).toByteArray()); + + //hide flow widgets + toolBarStretch = new YACReaderToolBarStretch(this); + + hideFlowViewAction = new QAction(this); + hideFlowViewAction->setText(tr("Hide comic flow")); + hideFlowViewAction->setData(HIDE_COMIC_VIEW_ACTION_YL); + hideFlowViewAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(HIDE_COMIC_VIEW_ACTION_YL)); + hideFlowViewAction->setIcon(QIcon(":/images/hideComicFlow.png")); + hideFlowViewAction->setCheckable(true); + hideFlowViewAction->setChecked(false); + + connect(hideFlowViewAction, SIGNAL(toggled(bool)),this, SLOT(hideComicFlow(bool))); } +void ClassicComicsView::hideComicFlow(bool hide) +{ + if(hide) + { + QList sizes; + sizes.append(0); + int total = sVertical->sizes().at(0) + sVertical->sizes().at(1); + sizes.append(total); + sVertical->setSizes(sizes); + } + else + { + QList sizes; + int total = sVertical->sizes().at(0) + sVertical->sizes().at(1); + sizes.append(2*total/3); + sizes.append(total/3); + sVertical->setSizes(sizes); + } +} + +//the toolbar has to be populated void ClassicComicsView::setToolBar(QToolBar *toolBar) { static_cast(comics->layout())->insertWidget(0,toolBar); + this->toolbar = toolBar; + + toolBarStretchAction = toolBar->addWidget(toolBarStretch); + toolBar->addAction(hideFlowViewAction); } void ClassicComicsView::setModel(ComicModel *model) @@ -118,23 +159,25 @@ void ClassicComicsView::setModel(ComicModel *model) tableView->horizontalHeader()->setMovable(true); #endif //TODO parametrizar la configuración de las columnas - for(int i = 0;ihorizontalHeader()->count();i++) - tableView->horizontalHeader()->hideSection(i); + /*if(!settings->contains(COMICS_VIEW_HEADERS)) + {*/ + for(int i = 0;ihorizontalHeader()->count();i++) + tableView->horizontalHeader()->hideSection(i); - tableView->horizontalHeader()->showSection(ComicModel::Number); - tableView->horizontalHeader()->showSection(ComicModel::Title); - tableView->horizontalHeader()->showSection(ComicModel::FileName); - tableView->horizontalHeader()->showSection(ComicModel::NumPages); - tableView->horizontalHeader()->showSection(ComicModel::Hash); //Size is part of the Hash...TODO add Columns::Size to Columns - tableView->horizontalHeader()->showSection(ComicModel::ReadColumn); - tableView->horizontalHeader()->showSection(ComicModel::CurrentPage); - tableView->horizontalHeader()->showSection(ComicModel::Rating); + tableView->horizontalHeader()->showSection(ComicModel::Number); + tableView->horizontalHeader()->showSection(ComicModel::Title); + tableView->horizontalHeader()->showSection(ComicModel::FileName); + tableView->horizontalHeader()->showSection(ComicModel::NumPages); + tableView->horizontalHeader()->showSection(ComicModel::Hash); //Size is part of the Hash...TODO add Columns::Size to Columns + tableView->horizontalHeader()->showSection(ComicModel::ReadColumn); + tableView->horizontalHeader()->showSection(ComicModel::CurrentPage); + tableView->horizontalHeader()->showSection(ComicModel::Rating); + //} //debido a un bug, qt4 no es capaz de ajustar el ancho teniendo en cuenta todas la filas (no sólo las visibles) //así que se ecala la primera vez y después se deja el control al usuario. //if(!settings->contains(COMICS_VIEW_HEADERS)) - tableView->resizeColumnsToContents(); - tableView->horizontalHeader()->setStretchLastSection(true); + QStringList paths = model->getPaths(model->getCurrentPath());//TODO ComicsView: get currentpath from somewhere currentPath()); comicFlow->setImagePaths(paths); @@ -143,6 +186,10 @@ void ClassicComicsView::setModel(ComicModel *model) if(settings->contains(COMICS_VIEW_HEADERS)) tableView->horizontalHeader()->restoreState(settings->value(COMICS_VIEW_HEADERS).toByteArray()); + + tableView->resizeColumnsToContents(); + + tableView->horizontalHeader()->setStretchLastSection(true); } } @@ -264,8 +311,10 @@ void ClassicComicsView::saveTableHeadersStatus() void ClassicComicsView::saveSplitterStatus() { + settingsMutex.lock(); if(!searching) settings->setValue(COMICS_VIEW_FLOW_SPLITTER_STATUS, sVertical->saveState()); + settingsMutex.unlock(); } void ClassicComicsView::applyModelChanges(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) @@ -291,6 +340,9 @@ void ClassicComicsView::closeEvent(QCloseEvent *event) saveTableHeadersStatus(); saveSplitterStatus(); ComicsView::closeEvent(event); + + toolbar->removeAction(toolBarStretchAction); + toolbar->removeAction(hideFlowViewAction); } void ClassicComicsView::setupSearchingIcon() diff --git a/YACReaderLibrary/classic_comics_view.h b/YACReaderLibrary/classic_comics_view.h index d98b3910..27bb878f 100644 --- a/YACReaderLibrary/classic_comics_view.h +++ b/YACReaderLibrary/classic_comics_view.h @@ -6,12 +6,14 @@ #include #include -class YACReaderTableView; class QSplitter; -class ComicFlowWidget; -class QToolBar; -class ComicModel; class QStackedWidget; +class QToolBar; + +class ComicFlowWidget; +class ComicModel; +class YACReaderTableView; +class YACReaderToolBarStretch; class ClassicComicsView : public ComicsView { @@ -44,16 +46,22 @@ public slots: void selectedComicForOpening(const QModelIndex & mi); protected slots: + void hideComicFlow(bool hide); void requestedViewContextMenu(const QPoint & point); void requestedItemContextMenu(const QPoint & point); + private: YACReaderTableView * tableView; + YACReaderToolBarStretch * toolBarStretch; + QAction * toolBarStretchAction; + QToolBar * toolbar; QWidget *comics; QSplitter * sVertical; ComicFlowWidget * comicFlow; QSettings * settings; void closeEvent ( QCloseEvent * event ); + QAction * hideFlowViewAction; QStackedWidget * stack; @@ -64,6 +72,8 @@ private: void showSearchingIcon(); void hideSearchingIcon(); void updateSearchingIconPosition(); + + QMutex settingsMutex; }; #endif // CLASSIC_COMICS_VIEW_H diff --git a/YACReaderLibrary/comic_vine/sort_volume_comics.cpp b/YACReaderLibrary/comic_vine/sort_volume_comics.cpp index 6d7441f2..16e7d782 100644 --- a/YACReaderLibrary/comic_vine/sort_volume_comics.cpp +++ b/YACReaderLibrary/comic_vine/sort_volume_comics.cpp @@ -169,10 +169,12 @@ void SortVolumeComics::moveDownCL() QList selection = tableFiles->selectionModel()->selectedIndexes(); if(selection.count() > 0) + { localComicsModel->moveSelectionDown(selection); - selection = tableFiles->selectionModel()->selectedIndexes(); - tableFiles->scrollTo(selection.last()); + selection = tableFiles->selectionModel()->selectedIndexes(); + tableFiles->scrollTo(selection.last()); + } } void SortVolumeComics::moveUpIL() diff --git a/YACReaderLibrary/db/comic_model.cpp b/YACReaderLibrary/db/comic_model.cpp index 922360b6..33ddf459 100644 --- a/YACReaderLibrary/db/comic_model.cpp +++ b/YACReaderLibrary/db/comic_model.cpp @@ -47,7 +47,7 @@ bool ComicModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, i return data->formats().contains(YACReader::YACReaderLibrarComiscSelectionMimeDataFormat); } -//TODO: optimize this method +//TODO: optimize this method (seriously) bool ComicModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { @@ -118,9 +118,26 @@ bool ComicModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int QLOG_INFO() << newSorting; - if(!beginMoveRows(parent,currentIndexes.first(),currentIndexes.last(),parent,row)) - return false; - _data = resortedData; + int tempRow = row; + foreach(qulonglong id, comicIds) + { + int i = 0; + foreach (ComicItem *item, _data) { + if(item->data(Id) == id) + { + beginMoveRows(parent,i,i,parent,tempRow); + _data.removeAll(item); + _data.insert(tempRow++, item); + endMoveRows(); + break; + } + i++; + } + } + + /*if(!beginMoveRows(parent,currentIndexes.first(),currentIndexes.last(),parent,row)) + return false;*/ + _data = resortedData; //TODO No longer needed //TODO emit signals @@ -145,7 +162,7 @@ bool ComicModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int QSqlDatabase::removeDatabase(_databasePath); - endMoveRows(); + //endMoveRows(); emit resortedIndexes(newSorting); int destSelectedIndex = row<0?_data.length():row; @@ -262,7 +279,7 @@ QVariant ComicModel::data(const QModelIndex &index, int role) const else if (role == RatingRole) return item->data(Rating); else if (role == CoverPathRole) - return "file:///"+_databasePath+"/covers/"+item->data(Hash).toString()+".jpg"; + return QUrl("file:"+_databasePath+"/covers/"+item->data(Hash).toString()+".jpg"); else if (role == NumPagesRole) return item->data(NumPages); else if (role == CurrentPageRole) @@ -361,11 +378,13 @@ QVariant ComicModel::headerData(int section, Qt::Orientation orientation, return QVariant(QIcon(":/images/zip.png")); else if(ext.compare("rar",Qt::CaseInsensitive) == 0) return QVariant(QIcon(":/images/rar.png")); +#ifndef use_unarr 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) +#endif + else if (ext.compare("cbt",Qt::CaseInsensitive) == 0) return QVariant(QIcon(":/images/comicTar.png")); } diff --git a/YACReaderLibrary/db_helper.cpp b/YACReaderLibrary/db_helper.cpp index c442af91..d7f9911a 100644 --- a/YACReaderLibrary/db_helper.cpp +++ b/YACReaderLibrary/db_helper.cpp @@ -348,7 +348,7 @@ void DBHelper::update(ComicInfo * comicInfo, QSqlDatabase & db) updateComicInfo.bindValue(":comicVineID", comicInfo->comicVineID); - updateComicInfo.exec(); + updateComicInfo.exec(); } void DBHelper::updateRead(ComicInfo * comicInfo, QSqlDatabase & db) @@ -391,6 +391,44 @@ void DBHelper::updateProgress(qulonglong libraryId, const ComicInfo &comicInfo) QSqlDatabase::removeDatabase(libraryPath); } +void DBHelper::updateReadingRemoteProgress(const ComicInfo &comicInfo, QSqlDatabase &db) +{ + QSqlQuery updateComicInfo(db); + updateComicInfo.prepare("UPDATE comic_info SET " + "read = :read, " + "currentPage = :currentPage, " + "hasBeenOpened = :hasBeenOpened" + " WHERE id = :id "); + + updateComicInfo.bindValue(":read", comicInfo.read?1:0); + updateComicInfo.bindValue(":currentPage", comicInfo.currentPage); + updateComicInfo.bindValue(":hasBeenOpened", comicInfo.hasBeenOpened?1:0); + updateComicInfo.bindValue(":id", comicInfo.id); + updateComicInfo.exec(); +} + + +void DBHelper::updateFromRemoteClient(qulonglong libraryId,const ComicInfo & comicInfo) +{ + QString libraryPath = DBHelper::getLibraries().getPath(libraryId); + QSqlDatabase db = DataBaseManagement::loadDatabase(libraryPath+"/.yacreaderlibrary"); + + ComicDB comic = DBHelper::loadComic(comicInfo.id,db); + + if(comic.info.hash == comicInfo.hash) + { + if(comic.info.currentPage == comic.info.numPages) + comic.info.read = true; + comic.info.currentPage = comicInfo.currentPage; + comic.info.hasBeenOpened = true; + + DBHelper::updateReadingRemoteProgress(comic.info,db); + } + + db.close(); + QSqlDatabase::removeDatabase(libraryPath); +} + void DBHelper::renameLabel(qulonglong id, const QString &name, QSqlDatabase &db) { QSqlQuery renameLabelQuery(db); @@ -566,16 +604,21 @@ qulonglong DBHelper::insertReadingSubList(const QString &name, qulonglong parent void DBHelper::insertComicsInFavorites(const QList &comicsList, QSqlDatabase &db) { + QSqlQuery getNumComicsInFavoritesQuery("SELECT count(*) FROM comic_default_reading_list WHERE default_reading_list_id = 1;",db); + getNumComicsInFavoritesQuery.next(); + QSqlRecord record = getNumComicsInFavoritesQuery.record(); + int numComics = record.value(0).toInt(); + db.transaction(); QSqlQuery query(db); - query.prepare("INSERT INTO comic_default_reading_list (default_reading_list_id, comic_id) " - "VALUES (1, :comic_id)"); + query.prepare("INSERT INTO comic_default_reading_list (default_reading_list_id, comic_id, ordering) " + "VALUES (1, :comic_id, :ordering)"); foreach(ComicDB comic, comicsList) { query.bindValue(":comic_id", comic.id); - //query.bindValue(":order", numComics++); + query.bindValue(":ordering", numComics++); query.exec(); } @@ -584,16 +627,22 @@ void DBHelper::insertComicsInFavorites(const QList &comicsList, QSqlDat void DBHelper::insertComicsInLabel(const QList &comicsList, qulonglong labelId, QSqlDatabase &db) { + QSqlQuery getNumComicsInFavoritesQuery(QString("SELECT count(*) FROM comic_label WHERE label_id = %1;").arg(labelId) ,db); + getNumComicsInFavoritesQuery.next(); + QSqlRecord record = getNumComicsInFavoritesQuery.record(); + int numComics = record.value(0).toInt(); + db.transaction(); QSqlQuery query(db); - query.prepare("INSERT INTO comic_label (label_id, comic_id) " - "VALUES (:label_id, :comic_id)"); + query.prepare("INSERT INTO comic_label (label_id, comic_id, ordering) " + "VALUES (:label_id, :comic_id, :ordering)"); foreach(ComicDB comic, comicsList) { query.bindValue(":label_id", labelId); query.bindValue(":comic_id", comic.id); + query.bindValue(":ordering", numComics++); query.exec(); } @@ -602,7 +651,7 @@ void DBHelper::insertComicsInLabel(const QList &comicsList, qulonglong void DBHelper::insertComicsInReadingList(const QList &comicsList, qulonglong readingListId, QSqlDatabase &db) { - QSqlQuery getNumComicsInFavoritesQuery("SELECT count(*) from comic_reading_list;",db); + QSqlQuery getNumComicsInFavoritesQuery("SELECT count(*) FROM comic_reading_list;",db); getNumComicsInFavoritesQuery.next(); QSqlRecord record = getNumComicsInFavoritesQuery.record(); int numComics = record.value(0).toInt(); diff --git a/YACReaderLibrary/db_helper.h b/YACReaderLibrary/db_helper.h index b104b48f..5eb2eae3 100644 --- a/YACReaderLibrary/db_helper.h +++ b/YACReaderLibrary/db_helper.h @@ -57,6 +57,8 @@ public: static void updateRead(ComicInfo * comicInfo, QSqlDatabase & db); static void update(const Folder & folder, QSqlDatabase & db); static void updateProgress(qulonglong libraryId,const ComicInfo & comicInfo); + static void updateReadingRemoteProgress(const ComicInfo & comicInfo, QSqlDatabase & db); + static void updateFromRemoteClient(qulonglong libraryId,const ComicInfo & comicInfo); static void renameLabel(qulonglong id, const QString & name, QSqlDatabase & db); static void renameList(qulonglong id, const QString & name, QSqlDatabase & db); static void reasignOrderToSublists(QList ids, QSqlDatabase & db); diff --git a/YACReaderLibrary/grid_comics_view.cpp b/YACReaderLibrary/grid_comics_view.cpp index f4f5f637..8f4602a5 100644 --- a/YACReaderLibrary/grid_comics_view.cpp +++ b/YACReaderLibrary/grid_comics_view.cpp @@ -84,7 +84,7 @@ void GridComicsView::setModel(ComicModel *model) ctxt->setContextProperty("textColor", "#636363"); //fonts settings ctxt->setContextProperty("fontSize", 11); - ctxt->setContextProperty("fontFamily", "none"); + ctxt->setContextProperty("fontFamily", QApplication::font().family()); ctxt->setContextProperty("fontSpacing", 0.5); #else @@ -93,12 +93,15 @@ void GridComicsView::setModel(ComicModel *model) ctxt->setContextProperty("selectedColor", "#121212"); ctxt->setContextProperty("selectedBorderColor", "#121212"); ctxt->setContextProperty("borderColor", "#121212"); - ctxt->setContextProperty("titleColor", "#E6E6E6"); - ctxt->setContextProperty("textColor", "#E6E6E6"); + ctxt->setContextProperty("titleColor", "#FFFFFF"); + ctxt->setContextProperty("textColor", "#A8A8A8"); ctxt->setContextProperty("dropShadow",false); //fonts settings - ctxt->setContextProperty("fontSize", "none"); - ctxt->setContextProperty("fontFamily", "none"); + int fontSize = QApplication::font().pointSize(); + if(fontSize == -1) + fontSize = QApplication::font().pixelSize(); + ctxt->setContextProperty("fontSize", fontSize); + ctxt->setContextProperty("fontFamily", QApplication::font().family()); ctxt->setContextProperty("fontSpacing", 0.5); #endif diff --git a/YACReaderLibrary/import_widget.cpp b/YACReaderLibrary/import_widget.cpp index 0e325e26..35fca0f4 100644 --- a/YACReaderLibrary/import_widget.cpp +++ b/YACReaderLibrary/import_widget.cpp @@ -127,11 +127,14 @@ ImportWidget::ImportWidget(QWidget *parent) : coversView->setMaximumHeight(300); coversView->setStyleSheet("QGraphicsView {background-color: #E6E6E6;border:none;}"); - coversScene = new QGraphicsScene(); - coversScene->setSceneRect(0,0,coversView->width(),coversView->height()); + coversScene = new QGraphicsScene(); coversView->setAlignment(Qt::AlignLeft); - coversView->setScene(coversScene); + coversView->setScene(coversScene); + coversView->setFixedHeight(300); + coversView->setInteractive(false); + + scrollAnimation = new QPropertyAnimation(coversView->horizontalScrollBar(), "value"); QLabel * topDecorator = new QLabel(); QLabel * bottomDecorator = new QLabel(); @@ -208,7 +211,7 @@ ImportWidget::ImportWidget(QWidget *parent) : connect(stop,SIGNAL(clicked()),this,SIGNAL(stop())); //connect(stop,SIGNAL(clicked()),this,SLOT(addCoverTest())); - previousWidth = 10; + previousWidth = 0; updatingCovers = false; elapsedTimer = new QElapsedTimer(); elapsedTimer->start(); @@ -216,70 +219,49 @@ ImportWidget::ImportWidget(QWidget *parent) : void ImportWidget::newComic(const QString & path, const QString & coverPath) { - currentComicLabel->setText(""+path+""); + if(!this->isVisible()) + return; - 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); + currentComicLabel->setText(""+path+""); - elapsedTimer->start(); - if(previousWidth >= coversView->width()+200 && !updatingCovers) - { - updatingCovers = true; - - foreach(QGraphicsItem * itemToRemove, coversScene->items()) - { - QGraphicsPixmapItem * last = dynamic_cast(itemToRemove); + if( ((elapsedTimer->elapsed()>=1100) || ((previousWidth < coversView->width()) && (elapsedTimer->elapsed()>=500))) && scrollAnimation->state() != QAbstractAnimation::Running)//todo elapsed time + { + updatingCovers = true; + elapsedTimer->start(); - if((last->pos().x()+last->pixmap().width())<=0) - { - coversScene->removeItem(last); - delete last; - } - //else - // break; - } + QPixmap p(coverPath); + p = p.scaledToHeight(300,Qt::SmoothTransformation); - int width = p.width(); + QGraphicsPixmapItem * item = new QGraphicsPixmapItem(p); + item->setPos(previousWidth, 0); + coversScene->addItem(item); - foreach(QGraphicsItem * itemToMove, coversScene->items()) - { - QTimeLine *timer = new QTimeLine(400); - timer->setFrameRange(0, 24); - timer->setUpdateInterval(17); + previousWidth += 10 + p.width(); - QGraphicsItemAnimation *animation = new QGraphicsItemAnimation; - animation->setItem(itemToMove); - animation->setTimeLine(timer); + foreach(QGraphicsItem * itemToRemove, coversScene->items()) + { + QGraphicsPixmapItem * last = dynamic_cast(itemToRemove); - 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())); + if((last->pos().x()+last->pixmap().width()) < coversView->horizontalScrollBar()->value()) //TODO check this + { + coversScene->removeItem(last); + delete last; + } + } - timer->start(); - connect(timer,SIGNAL(finished()),timer,SLOT(deleteLater())); - connect(timer,SIGNAL(finished()),animation,SLOT(deleteLater())); - } + QScrollBar * scrollBar = coversView->horizontalScrollBar(); - QTimer::singleShot(400,this,SLOT(finishedUpdatingCover())); - - previousWidth -= 10+width; - } + float speedFactor = 2.5; + int origin = scrollBar->value(); + int dest = origin + 10 + p.width(); - } -} - -void ImportWidget::finishedUpdatingCover() -{ - updatingCovers = false; + scrollAnimation->setDuration((dest-origin)*speedFactor); + scrollAnimation->setStartValue(origin); + scrollAnimation->setEndValue(dest); + QEasingCurve easing(QEasingCurve::OutQuad); + scrollAnimation->setEasingCurve(easing); + scrollAnimation->start(); + } } void ImportWidget::newCover(const QPixmap & image) @@ -335,7 +317,7 @@ void ImportWidget::addCoverTest() void ImportWidget::clear() { - previousWidth = 10; + previousWidth = 0; //nos aseguramos de que las animaciones han finalizado antes de borrar QList all = coversScene->items(); @@ -347,7 +329,12 @@ void ImportWidget::clear() } coversScene->clear(); - updatingCovers = false; + delete coversScene; + coversScene = new QGraphicsScene; + + coversView->setScene(coversScene); + + updatingCovers = false; currentComicLabel->setText("..."); @@ -377,7 +364,7 @@ void ImportWidget::clearScene() void ImportWidget::showCovers(bool hide) { portadasLabel->setHidden(hide); - coversViewContainer->setHidden(hide); + coversViewContainer->setHidden(hide); } void ImportWidget::resizeEvent(QResizeEvent * event) diff --git a/YACReaderLibrary/import_widget.h b/YACReaderLibrary/import_widget.h index 5ff57814..e38f4d9c 100644 --- a/YACReaderLibrary/import_widget.h +++ b/YACReaderLibrary/import_widget.h @@ -10,43 +10,46 @@ class QElapsedTimer; class QVBoxLayout; class QToolButton; class QResizeEvent; +class QPropertyAnimation; class ImportWidget : public QWidget { - Q_OBJECT + Q_OBJECT public: - explicit ImportWidget(QWidget *parent = 0); - + explicit ImportWidget(QWidget *parent = 0); + signals: - void stop(); + 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); + void newComic(const QString & path, const QString & coverPath); + void newCover(const QPixmap & image); + void clear(); + void addCoverTest(); + 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; + QLabel * currentComicLabel; + QLabel * portadasLabel; + QLabel * iconLabel; + QLabel * text; + QLabel * textDescription; + QWidget * coversViewContainer; + QGraphicsView * coversView; + QGraphicsScene * coversScene; + QPropertyAnimation * scrollAnimation; - QToolButton * hideButton; + int previousWidth; + bool updatingCovers; + QElapsedTimer * elapsedTimer; + quint64 i; + + QToolButton * hideButton; + + void resizeEvent(QResizeEvent * event); - void resizeEvent(QResizeEvent * event); - }; #endif // IMPORT_WIDGET_H diff --git a/YACReaderLibrary/library_creator.cpp b/YACReaderLibrary/library_creator.cpp index 42e43271..8b5662f4 100644 --- a/YACReaderLibrary/library_creator.cpp +++ b/YACReaderLibrary/library_creator.cpp @@ -122,8 +122,8 @@ void LibraryCreator::processLibrary(const QString & source, const QString & targ void LibraryCreator::run() { stopRunning = false; - - //check for 7z lib +#ifndef use_unarr +//check for 7z lib #if defined Q_OS_UNIX && !defined Q_OS_MAC QLibrary *sevenzLib = new QLibrary(QString(LIBDIR)+"/p7zip/7z.so"); #else @@ -137,7 +137,7 @@ void LibraryCreator::run() exit(); } sevenzLib->deleteLater(); - +#endif if(_mode == CREATOR) { QLOG_INFO() << "Starting to create new library ( " << _source << "," << _target << ")"; diff --git a/YACReaderLibrary/library_window.cpp b/YACReaderLibrary/library_window.cpp index 2b620a09..f88d03c4 100644 --- a/YACReaderLibrary/library_window.cpp +++ b/YACReaderLibrary/library_window.cpp @@ -86,6 +86,7 @@ #include "db_helper.h" #include "reading_list_item.h" +#include "opengl_checker.h" #include "QsLog.h" @@ -187,10 +188,15 @@ void LibraryWindow::doLayout() #ifndef NO_OPENGL //FLOW----------------------------------------------------------------------- //--------------------------------------------------------------------------- - if(QGLFormat::hasOpenGL() && !settings->contains(USE_OPEN_GL)) - { + + OpenGLChecker openGLChecker; + bool openGLAvailable = openGLChecker.hasCompatibleOpenGLVersion(); + + if(openGLAvailable && !settings->contains(USE_OPEN_GL)) settings->setValue(USE_OPEN_GL,2); - } + else + if(!openGLAvailable) + settings->setValue(USE_OPEN_GL,0); #endif //FOLDERS FILTER------------------------------------------------------------- //--------------------------------------------------------------------------- @@ -245,7 +251,6 @@ void LibraryWindow::doLayout() doComicsViewConnections(); - comicsView->setToolBar(editInfoToolBar); comicsViewStack->addWidget(comicsViewTransition = new ComicsViewTransition()); comicsViewStack->addWidget(emptyFolderWidget = new EmptyFolderWidget()); comicsViewStack->addWidget(emptyLabelWidget = new EmptyLabelWidget()); @@ -413,8 +418,7 @@ void LibraryWindow::setUpShortcutsManagement() #ifndef Q_OS_MAC << toggleFullScreenAction #endif - << toggleComicsViewAction - << hideComicViewAction); + << toggleComicsViewAction); allActions << tmpList; @@ -570,7 +574,7 @@ void LibraryWindow::createActions() setAllAsNonReadAction->setIcon(QIcon(":/images/setAllUnread.png"));*/ showHideMarksAction = new QAction(tr("Show/Hide marks"),this); - showHideMarksAction->setToolTip(tr("Show or hide readed marks")); + showHideMarksAction->setToolTip(tr("Show or hide read marks")); showHideMarksAction->setData(SHOW_HIDE_MARKS_ACTION_YL); showHideMarksAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_HIDE_MARKS_ACTION_YL)); showHideMarksAction->setCheckable(true); @@ -719,14 +723,6 @@ void LibraryWindow::createActions() deleteComicsAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(DELETE_COMICS_ACTION_YL)); deleteComicsAction->setIcon(QIcon(":/images/trash.png")); - hideComicViewAction = new QAction(this); - hideComicViewAction->setText(tr("Hide comic flow")); - hideComicViewAction->setData(HIDE_COMIC_VIEW_ACTION_YL); - hideComicViewAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(HIDE_COMIC_VIEW_ACTION_YL)); - hideComicViewAction->setIcon(QIcon(":/images/hideComicFlow.png")); - hideComicViewAction->setCheckable(true); - hideComicViewAction->setChecked(false); - getInfoAction = new QAction(this); getInfoAction->setData(GET_INFO_ACTION_YL); getInfoAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(GET_INFO_ACTION_YL)); @@ -932,8 +928,8 @@ void LibraryWindow::createToolBars() editInfoToolBar->addAction(deleteComicsAction); - /*editInfoToolBar->addWidget(new QToolBarStretch()); - editInfoToolBar->addAction(hideComicViewAction);*/ + + comicsView->setToolBar(editInfoToolBar); } void LibraryWindow::createMenus() @@ -1142,8 +1138,6 @@ void LibraryWindow::createConnections() connect(deleteComicsAction,SIGNAL(triggered()),this,SLOT(deleteComics())); - connect(hideComicViewAction, SIGNAL(toggled(bool)),this, SLOT(hideComicFlow(bool))); - connect(getInfoAction,SIGNAL(triggered()),this,SLOT(showComicVineScraper())); //connect(socialAction,SIGNAL(triggered()),this,SLOT(showSocial())); @@ -2458,30 +2452,6 @@ QString LibraryWindow::currentFolderPath() return QDir::cleanPath(currentPath()+path); } -//TODO ComicsView: some actions in the comics toolbar can be relative to a certain view -//show/hide actions on show/hide widget -void LibraryWindow::hideComicFlow(bool hide) -{ - /* - if(hide) - { - QList sizes; - sizes.append(0); - int total = sVertical->sizes().at(0) + sVertical->sizes().at(1); - sizes.append(total); - sVertical->setSizes(sizes); - } - else - { - QList sizes; - int total = sVertical->sizes().at(0) + sVertical->sizes().at(1); - sizes.append(2*total/3); - sizes.append(total/3); - sVertical->setSizes(sizes); - } -*/ -} - void LibraryWindow::showExportComicsInfo() { exportComicsInfoDialog->source = currentPath() + "/.yacreaderlibrary/library.ydb"; diff --git a/YACReaderLibrary/library_window.h b/YACReaderLibrary/library_window.h index 69ae896c..26b218ac 100644 --- a/YACReaderLibrary/library_window.h +++ b/YACReaderLibrary/library_window.h @@ -205,7 +205,6 @@ private: QAction * asignOrderAction; QAction * forceCoverExtractedAction; QAction * deleteComicsAction; - QAction * hideComicViewAction; QAction *showEditShortcutsAction; @@ -339,7 +338,6 @@ public slots: void setCurrentComicsStatusReaded(YACReaderComicReadStatus readStatus); void setCurrentComicReaded(); void setCurrentComicUnreaded(); - void hideComicFlow(bool hide); void showExportComicsInfo(); void showImportComicsInfo(); void asignNumbers(); diff --git a/YACReaderLibrary/main.cpp b/YACReaderLibrary/main.cpp index a883151f..581fcfc3 100644 --- a/YACReaderLibrary/main.cpp +++ b/YACReaderLibrary/main.cpp @@ -19,6 +19,7 @@ #include "db_helper.h" #include "yacreader_libraries.h" #include "exit_check.h" +#include "opengl_checker.h" #include "QsLog.h" #include "QsLogDest.h" @@ -92,6 +93,7 @@ void logSystemAndConfig() QLOG_INFO() << "SO : Unknown"; #endif +#ifndef use_unarr #ifdef Q_OS_WIN if(QLibrary::isLibrary(QApplication::applicationDirPath()+"/utils/7z.dll")) #elif defined Q_OS_UNIX && !defined Q_OS_MAC @@ -102,6 +104,9 @@ void logSystemAndConfig() QLOG_INFO() << "7z : found"; else QLOG_ERROR() << "7z : not found"; +#else + QLOG_INFO() << "using unarr decompression backend"; +#endif #if defined Q_OS_UNIX && !defined Q_OS_MAC if(QFileInfo(QString(BINDIR)+"/qrencode").exists()) #else @@ -123,12 +128,16 @@ void logSystemAndConfig() else QLOG_INFO() << "OpenGL : disabled"; + OpenGLChecker checker; + QLOG_INFO() << "OpenGL version : " << checker.textVersionDescription(); + QLOG_INFO() << "Libraries: " << DBHelper::getLibraries().getLibraries(); QLOG_INFO() << "--------------------------------------------"; } int main( int argc, char ** argv ) { + //fix for misplaced text in Qt4.8 and Mavericks #ifdef Q_OS_MAC #if QT_VERSION < 0x050000 @@ -140,9 +149,13 @@ int main( int argc, char ** argv ) QApplication app( argc, argv ); +#ifdef FORCE_ANGLE + app.setAttribute(Qt::AA_UseOpenGLES); +#endif + app.setApplicationName("YACReaderLibrary"); app.setOrganizationName("YACReader"); - qApp->setAttribute(Qt::AA_UseHighDpiPixmaps); + app.setAttribute(Qt::AA_UseHighDpiPixmaps); //simple command line parser //will be replaced by QCommandLineParser in the future //TODO: --headless, --server=[on|off], support for file and directory arguments @@ -208,7 +221,6 @@ int main( int argc, char ** argv ) if(settings->value(SERVER_ON,true).toBool()) { - s->start(); } #endif diff --git a/YACReaderLibrary/qml/GridComicsView.qml b/YACReaderLibrary/qml/GridComicsView.qml index 2c317116..c8c04d11 100644 --- a/YACReaderLibrary/qml/GridComicsView.qml +++ b/YACReaderLibrary/qml/GridComicsView.qml @@ -150,11 +150,14 @@ Rectangle { } onReleased: { - /*if(mouse.button != Qt.RightButton && !(mouse.modifiers & Qt.ControlModifier || mouse.modifiers & Qt.ShiftModifier)) + if(mouse.button == Qt.LeftButton && !(mouse.modifiers & Qt.ControlModifier || mouse.modifiers & Qt.ShiftModifier)) { - comicsSelectionHelper.setCurrentIndex(index) - grid.currentIndex = index; - }*/ + if(comicsSelectionHelper.isSelectedIndex(index)) + { + comicsSelectionHelper.setCurrentIndex(index) + grid.currentIndex = index; + } + } } } @@ -166,30 +169,44 @@ Rectangle { //cover Image { id: coverElement - width: 148 - height: 224 - anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 4} + width: 156 + height: 236 + anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0} source: cover_path fillMode: Image.PreserveAspectCrop smooth: true mipmap: true asynchronous : true - cache: false //TODO clear cache only when it is neede + cache: false //TODO clear cache only when it is needed + } + + //border + Rectangle { + width: 156 + height: 236 + anchors {horizontalCenter: parent.horizontalCenter; top: realCell.top; topMargin: 0} + color: "transparent" + border { + color: "#20FFFFFF" + width: 1 + } + } + //mark Image { id: mark width: 23 height: 23 source: read_column&&show_marks?"tick.png":has_been_opened&&show_marks?"reading.png":"" - anchors {right: coverElement.right; top: coverElement.top; topMargin: 11; rightMargin: 11} + anchors {right: coverElement.right; top: coverElement.top; topMargin: 9; rightMargin: 9} asynchronous : true } //title Text { id : titleText - anchors { top: realCell.top; left: realCell.left; leftMargin: 4; rightMargin: 4; topMargin: 234; } + anchors { top: realCell.top; left: realCell.left; leftMargin: 4; rightMargin: 4; topMargin: 238; } width: 148 maximumLineCount: 2 wrapMode: Text.WordWrap @@ -339,6 +356,12 @@ Rectangle { currentIndex: 0 cacheBuffer: 0 + footer: Rectangle { //fix for the scroll issue, TODO find what causes the issue (some times the bottoms cells are hidden for the toolbar, no full scroll) + height : 25 + width : parent.width + color : backgroundColor + } + move: Transition { NumberAnimation { properties: "x,y"; duration: 250 } } diff --git a/YACReaderLibrary/qml/page.png b/YACReaderLibrary/qml/page.png index 100db8a0..83e35370 100644 Binary files a/YACReaderLibrary/qml/page.png and b/YACReaderLibrary/qml/page.png differ diff --git a/YACReaderLibrary/qml/star.png b/YACReaderLibrary/qml/star.png index 4e3c4b6a..61d8782c 100644 Binary files a/YACReaderLibrary/qml/star.png and b/YACReaderLibrary/qml/star.png differ diff --git a/YACReaderLibrary/server/controllers/comiccontroller.cpp b/YACReaderLibrary/server/controllers/comiccontroller.cpp index 7f6468f1..52ae4e00 100644 --- a/YACReaderLibrary/server/controllers/comiccontroller.cpp +++ b/YACReaderLibrary/server/controllers/comiccontroller.cpp @@ -56,6 +56,9 @@ void ComicController::service(HttpRequest& request, HttpResponse& response) comicFile->moveToThread(thread); + connect(comicFile, SIGNAL(errorOpening()), thread, SLOT(quit())); + connect(comicFile, SIGNAL(errorOpening(QString)), thread, SLOT(quit())); + connect(comicFile, SIGNAL(imagesLoaded()), thread, SLOT(quit())); connect(thread, SIGNAL(started()), comicFile, SLOT(process())); connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); diff --git a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp b/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp index 2924e325..aeab979a 100644 --- a/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp +++ b/YACReaderLibrary/server/controllers/comicdownloadinfocontroller.cpp @@ -10,6 +10,8 @@ ComicDownloadInfoController::ComicDownloadInfoController() {} void ComicDownloadInfoController::service(HttpRequest& request, HttpResponse& response) { + response.setHeader("Content-Type", "plain/text; charset=utf-8"); + QString path = QUrl::fromPercentEncoding(request.getPath()).toUtf8(); QStringList pathElements = path.split('/'); diff --git a/YACReaderLibrary/server/controllers/foldercontroller.cpp b/YACReaderLibrary/server/controllers/foldercontroller.cpp index 7a8f9cb7..30aa657f 100644 --- a/YACReaderLibrary/server/controllers/foldercontroller.cpp +++ b/YACReaderLibrary/server/controllers/foldercontroller.cpp @@ -51,7 +51,7 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) } if(folderId!=1) - t.setVariable("folder.name",folderName); + t.setVariable("folder.name",folderName); else t.setVariable("folder.name",libraryName); QList folderContent = DBHelper::getFolderSubfoldersFromLibrary(libraryId,folderId); @@ -155,75 +155,81 @@ void FolderController::service(HttpRequest& request, HttpResponse& response) t.setVariable(QString("path%1.name").arg(i-1),DBHelper::getFolderName(libraryId,foldersPath[i].first)); } - t.loop("element",numFoldersAtCurrentPage); - int i = 0; - while(iname); - if(item->isDir()) - { - t.setVariable(QString("element%1.class").arg(i),"folder"); - - QList children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id); - if(children.length()>0) + if(folderContent.length() > 0) + { + t.loop("element",numFoldersAtCurrentPage); + int i = 0; + while(iname); + if(item->isDir()) { - const ComicDB * comic = static_cast(children.at(0)); - t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(comic->info.hash)); + t.setVariable(QString("element%1.class").arg(i),"folder"); + + QList children = DBHelper::getFolderComicsFromLibrary(libraryId, item->id); + if(children.length()>0) + { + const ComicDB * comic = static_cast(children.at(0)); + t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg?folderCover=true").arg(libraryId).arg(comic->info.hash)); + } + else + t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png"); + + t.setVariable(QString("element%1.browse").arg(i),QString("BROWSE").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id))); + t.setVariable(QString("element%1.cover.browse").arg(i),QString("").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id))); + t.setVariable(QString("element%1.cover.browse.end").arg(i),""); + //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("IMPORT").arg("/library/"+QString::number(libraryId)+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id))); + t.setVariable(QString("element%1.read").arg(i),""); + + t.setVariable(QString("element%1.size").arg(i),""); + t.setVariable(QString("element%1.pages").arg(i),""); + t.setVariable(QString("element%1.status").arg(i),""); } else - t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png"); - - t.setVariable(QString("element%1.browse").arg(i),QString("BROWSE").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id))); - t.setVariable(QString("element%1.cover.browse").arg(i),QString("").arg(QString("/library/%1/folder/%2").arg(libraryId).arg(item->id))); - t.setVariable(QString("element%1.cover.browse.end").arg(i),""); - //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("IMPORT").arg("/library/"+QString::number(libraryId)+"/folder/"+QString("%1/info").arg(folderContent.at(i + (page*elementsPerPage))->id))); - t.setVariable(QString("element%1.read").arg(i),""); - - t.setVariable(QString("element%1.size").arg(i),""); - t.setVariable(QString("element%1.pages").arg(i),""); - t.setVariable(QString("element%1.status").arg(i),""); - } - 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.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("IMPORT").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id))); - else if (session.isComicOnDevice(comic->info.hash)) + { + 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.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("IMPORT").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id))); + else if (session.isComicOnDevice(comic->info.hash)) t.setVariable(QString("element%1.download").arg(i),QString("
IMPORTED
")); - else - t.setVariable(QString("element%1.download").arg(i),QString("
IMPORTING
")); - - //t.setVariable(QString("element%1.image.url").arg(i),"/images/f.png"); + else + t.setVariable(QString("element%1.download").arg(i),QString("
IMPORTING
")); - t.setVariable(QString("element%1.read").arg(i),QString("READ").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id)+"/remote")); + //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(libraryId).arg(comic->info.hash)); + t.setVariable(QString("element%1.read").arg(i),QString("READ").arg("/library/"+QString::number(libraryId)+"/comic/"+QString("%1").arg(comic->id)+"/remote")); - t.setVariable(QString("element%1.size").arg(i),"" + QString::number(comic->info.hash.right(comic->info.hash.length()-40).toInt()/1024.0/1024.0,'f',2)+"Mb"); - if(comic->info.hasBeenOpened) - t.setVariable(QString("element%1.pages").arg(i),QString("%1/%2 pages").arg(comic->info.currentPage).arg(comic->info.numPages.toInt())); - else - t.setVariable(QString("element%1.pages").arg(i),QString("%1 pages").arg(comic->info.numPages.toInt())); + t.setVariable(QString("element%1.image.url").arg(i),QString("/library/%1/cover/%2.jpg").arg(libraryId).arg(comic->info.hash)); - if(comic->info.read) - t.setVariable(QString("element%1.status").arg(i), QString("
")); - else if(comic->info.hasBeenOpened) - t.setVariable(QString("element%1.status").arg(i), QString("
")); - else - t.setVariable(QString("element%1.status").arg(i),""); + t.setVariable(QString("element%1.size").arg(i),"" + QString::number(comic->info.hash.right(comic->info.hash.length()-40).toInt()/1024.0/1024.0,'f',2)+"Mb"); + if(comic->info.hasBeenOpened) + t.setVariable(QString("element%1.pages").arg(i),QString("%1/%2 pages").arg(comic->info.currentPage).arg(comic->info.numPages.toInt())); + else + t.setVariable(QString("element%1.pages").arg(i),QString("%1 pages").arg(comic->info.numPages.toInt())); - t.setVariable(QString("element%1.cover.browse").arg(i),""); - t.setVariable(QString("element%1.cover.browse.end").arg(i),""); - } - i++; - } + if(comic->info.read) + t.setVariable(QString("element%1.status").arg(i), QString("
")); + else if(comic->info.hasBeenOpened) + t.setVariable(QString("element%1.status").arg(i), QString("
")); + else + t.setVariable(QString("element%1.status").arg(i),""); + + t.setVariable(QString("element%1.cover.browse").arg(i),""); + t.setVariable(QString("element%1.cover.browse.end").arg(i),""); + } + i++; + } + } else + { + t.loop("element",0); + } if(numPages > 1) { diff --git a/YACReaderLibrary/server/controllers/synccontroller.cpp b/YACReaderLibrary/server/controllers/synccontroller.cpp new file mode 100644 index 00000000..7c4c1859 --- /dev/null +++ b/YACReaderLibrary/server/controllers/synccontroller.cpp @@ -0,0 +1,55 @@ +#include "synccontroller.h" + +#include "QsLog.h" +#include + +#include "comic_db.h" +#include "db_helper.h" + +SyncController::SyncController() +{ + +} + +void SyncController::service(HttpRequest &request, HttpResponse &response) +{ + QString postData = QString::fromUtf8(request.getBody()); + + QLOG_INFO() << "POST DATA: " << postData; + + if(postData.length()>0) { + QList data = postData.split("\n"); + + qulonglong libraryId; + qulonglong comicId; + int currentPage; + QString hash; + foreach(QString comicInfo, data) + { + QList comicInfoProgress = comicInfo.split("\t"); + + if(comicInfoProgress.length() == 4) + { + libraryId = comicInfoProgress.at(0).toULongLong(); + comicId = comicInfoProgress.at(1).toULongLong(); + hash = comicInfoProgress.at(2); + currentPage = comicInfoProgress.at(3).toInt(); + + ComicInfo info; + info.currentPage = currentPage; + info.hash = hash; //TODO remove the hash check and add UUIDs for libraries + info.id = comicId; + DBHelper::updateFromRemoteClient(libraryId,info); + } + } + } + else + { + response.setStatus(412,"No comic info received"); + response.writeText("",true); + return; + } + + response.write("OK",true); +} + diff --git a/YACReaderLibrary/server/controllers/synccontroller.h b/YACReaderLibrary/server/controllers/synccontroller.h new file mode 100644 index 00000000..6f6a5d76 --- /dev/null +++ b/YACReaderLibrary/server/controllers/synccontroller.h @@ -0,0 +1,21 @@ +#ifndef SYNCCONTROLLER_H +#define SYNCCONTROLLER_H + +#include + +#include "httprequest.h" +#include "httpresponse.h" +#include "httprequesthandler.h" + +class SyncController : public HttpRequestHandler { + Q_OBJECT + Q_DISABLE_COPY(SyncController); +public: + /** Constructor */ + SyncController(); + + /** Generates the response */ + void service(HttpRequest& request, HttpResponse& response); +}; + +#endif // SYNCCONTROLLER_H diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp index c085937d..b1044b4a 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpconnectionhandler.cpp @@ -99,66 +99,72 @@ void HttpConnectionHandler::disconnected() { } void HttpConnectionHandler::read() { + while (socket.bytesAvailable()) { #ifdef SUPERVERBOSE - qDebug("HttpConnectionHandler (%p): read input",this); + 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); + // Create new HttpRequest object if necessary + if (!currentRequest) { + currentRequest=new HttpRequest(settings); } - // Finalize sending the response if not already done - if (!response.hasSentLastPart()) { - response.write(QByteArray(),true); + // 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); + } } - socket.disconnectFromHost(); //CAMBIADO sólo se van a soportar conexiones NO persistentes + // 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; + } - // 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; + // 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; + } + else + { + qDebug("HttpConnectionHandler (%p): received request",this); + } } } diff --git a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp b/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp index af7dc50c..87ce7d4a 100644 --- a/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp +++ b/YACReaderLibrary/server/lib/bfHttpServer/httpsessionstore.cpp @@ -14,7 +14,7 @@ HttpSessionStore::HttpSessionStore(QSettings* settings, QObject* parent) connect(&cleanupTimer,SIGNAL(timeout()),this,SLOT(timerEvent())); cleanupTimer.start(60000); cookieName=settings->value("cookieName","sessionid").toByteArray(); - expirationTime=settings->value("expirationTime",86400000).toInt(); + expirationTime=settings->value("expirationTime",864000000).toInt(); qDebug("HttpSessionStore: Sessions expire after %i milliseconds",expirationTime); } @@ -90,7 +90,9 @@ void HttpSessionStore::timerEvent() { ++i; HttpSession session=prev.value(); qint64 lastAccess=session.getLastAccess(); - if (now-lastAccess>expirationTime) { + if (now-lastAccess>expirationTime) { //TODO cleaning up will cause current opened comic to be deleted, so clients won't be able to download it + //If the cleaning occurs in the midle of a download it going to cause issues + //Temporal fix: use a big expirationTime = 10 days qDebug("HttpSessionStore: session %s expired",session.getId().data()); sessions.erase(prev); } diff --git a/YACReaderLibrary/server/lib/bfLogging/logger.h b/YACReaderLibrary/server/lib/bfLogging/logger.h index bbd278ca..adcedfd3 100644 --- a/YACReaderLibrary/server/lib/bfLogging/logger.h +++ b/YACReaderLibrary/server/lib/bfLogging/logger.h @@ -6,6 +6,8 @@ #ifndef LOGGER_H #define LOGGER_H +#include + #include #include #include diff --git a/YACReaderLibrary/server/requestmapper.cpp b/YACReaderLibrary/server/requestmapper.cpp index 87057869..66b03413 100644 --- a/YACReaderLibrary/server/requestmapper.cpp +++ b/YACReaderLibrary/server/requestmapper.cpp @@ -21,6 +21,7 @@ #include "controllers/updatecomiccontroller.h" #include "controllers/errorcontroller.h" #include "controllers/comicdownloadinfocontroller.h" +#include "controllers/synccontroller.h" #include "db_helper.h" #include "yacreader_libraries.h" @@ -91,81 +92,86 @@ void RequestMapper::loadSession(HttpRequest & request, HttpResponse& response) } void RequestMapper::service(HttpRequest& request, HttpResponse& response) { - QByteArray path=request.getPath(); - qDebug("RequestMapper: path=%s",path.data()); + QByteArray path=request.getPath(); + qDebug("RequestMapper: path=%s",path.data()); - QRegExp folder("/library/.+/folder/[0-9]+/?");//get comic content - QRegExp folderInfo("/library/.+/folder/[0-9]+/info/?"); //get folder info + QRegExp folder("/library/.+/folder/[0-9]+/?");//get comic content + QRegExp folderInfo("/library/.+/folder/[0-9]+/info/?"); //get folder info QRegExp comicDownloadInfo("/library/.+/comic/[0-9]+/?"); //get comic info (basic/download info) QRegExp comicFullInfo("/library/.+/comic/[0-9]+/info/?"); //get comic info (full info) QRegExp comicOpen("/library/.+/comic/[0-9]+/remote/?"); //the server will open for reading the comic QRegExp comicUpdate("/library/.+/comic/[0-9]+/update/?"); //get comic info - QRegExp comicClose("/library/.+/comic/[0-9]+/close/?"); //the server will close the comic and free memory - QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) - QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page + QRegExp comicClose("/library/.+/comic/[0-9]+/close/?"); //the server will close the comic and free memory + QRegExp cover("/library/.+/cover/[0-9a-f]+.jpg"); //get comic cover (navigation) + QRegExp comicPage("/library/.+/comic/[0-9]+/page/[0-9]+/?"); //get comic page QRegExp comicPageRemote("/library/.+/comic/[0-9]+/page/[0-9]+/remote?"); //get comic page (remote reading) + QRegExp sync("/sync"); + QRegExp library("/library/([0-9]+)/.+"); //permite verificar que la biblioteca solicitada existe path = QUrl::fromPercentEncoding(path).toUtf8(); - loadSession(request, response); + if(!sync.exactMatch(path)) //no session is needed for syncback info, until security will be added + loadSession(request, response); - //primera petición, se ha hecho un post, se sirven las bibliotecas si la seguridad mediante login no está habilitada + //primera petición, se ha hecho un post, se sirven las bibliotecas si la seguridad mediante login no está habilitada if(path == "/") //Don't send data to the server using '/' !!!! { - LibrariesController().service(request, response); - } - - else - { - - //se comprueba que la sesión sea la correcta con el fin de evitar accesos no autorizados - HttpSession session=Static::sessionStore->getSession(request,response,false); - if(!session.isNull() && session.contains("ySession")) + LibrariesController().service(request, response); + } + else + { + if(sync.exactMatch(path)) + SyncController().service(request, response); + else { - if(library.indexIn(path)!=-1 && DBHelper::getLibraries().contains(library.cap(1).toInt()) ) - { - //listar el contenido del folder - if(folder.exactMatch(path)) - { - FolderController().service(request, response); - } - else if (folderInfo.exactMatch(path)) - { - FolderInfoController().service(request, response); - } - else if(cover.exactMatch(path)) - { - CoverController().service(request, response); - } - else if(comicDownloadInfo.exactMatch(path)) + //se comprueba que la sesión sea la correcta con el fin de evitar accesos no autorizados + HttpSession session=Static::sessionStore->getSession(request,response,false); + if(!session.isNull() && session.contains("ySession")) + { + if(library.indexIn(path)!=-1 && DBHelper::getLibraries().contains(library.cap(1).toInt()) ) { - ComicDownloadInfoController().service(request, response); + //listar el contenido del folder + if(folder.exactMatch(path)) + { + FolderController().service(request, response); + } + else if (folderInfo.exactMatch(path)) + { + FolderInfoController().service(request, response); + } + else if(cover.exactMatch(path)) + { + CoverController().service(request, response); + } + else if(comicDownloadInfo.exactMatch(path)) + { + ComicDownloadInfoController().service(request, response); + } + else if(comicFullInfo.exactMatch(path) || comicOpen.exactMatch(path))//start download or start remote reading + { + ComicController().service(request, response); + } + else if(comicPage.exactMatch(path) || comicPageRemote.exactMatch(path)) + { + PageController().service(request,response); + } + else if(comicUpdate.exactMatch(path)) + { + UpdateComicController().service(request, response); + } } - else if(comicFullInfo.exactMatch(path) || comicOpen.exactMatch(path))//start download or start remote reading - { - ComicController().service(request, response); - } - else if(comicPage.exactMatch(path) || comicPageRemote.exactMatch(path)) - { - PageController().service(request,response); - } - else if(comicUpdate.exactMatch(path)) + else { - UpdateComicController().service(request, response); + //response.writeText(library.cap(1)); + Static::staticFileController->service(request, response); } - } - else - { - //response.writeText(library.cap(1)); - Static::staticFileController->service(request, response); - } + } + else //acceso no autorizado, redirección + { + ErrorController(300).service(request,response); + } } - else //acceso no autorizado, redirección - { - ErrorController(300).service(request,response); - } - } - + } } diff --git a/YACReaderLibrary/server/server.pri b/YACReaderLibrary/server/server.pri index 5fc65043..4be20612 100644 --- a/YACReaderLibrary/server/server.pri +++ b/YACReaderLibrary/server/server.pri @@ -5,31 +5,33 @@ HEADERS += \ $$PWD/static.h \ $$PWD/startup.h \ $$PWD/requestmapper.h \ - $$PWD/controllers/comiccontroller.h \ - $$PWD/controllers/errorcontroller.h \ - $$PWD/controllers/foldercontroller.h \ - $$PWD/controllers/folderinfocontroller.h \ - $$PWD/controllers/librariescontroller.h \ - $$PWD/controllers/pagecontroller.h \ - $$PWD/controllers/sessionmanager.h \ - $$PWD/controllers/covercontroller.h \ - server/controllers/updatecomiccontroller.h \ - server/controllers/comicdownloadinfocontroller.h + $$PWD/controllers/comiccontroller.h \ + $$PWD/controllers/errorcontroller.h \ + $$PWD/controllers/foldercontroller.h \ + $$PWD/controllers/folderinfocontroller.h \ + $$PWD/controllers/librariescontroller.h \ + $$PWD/controllers/pagecontroller.h \ + $$PWD/controllers/sessionmanager.h \ + $$PWD/controllers/covercontroller.h \ + $$PWD/controllers/updatecomiccontroller.h \ + $$PWD/controllers/comicdownloadinfocontroller.h \ + $$PWD/controllers/synccontroller.h SOURCES += \ $$PWD/static.cpp \ $$PWD/startup.cpp \ $$PWD/requestmapper.cpp \ - $$PWD/controllers/comiccontroller.cpp \ - $$PWD/controllers/errorcontroller.cpp \ - $$PWD/controllers/foldercontroller.cpp \ - $$PWD/controllers/folderinfocontroller.cpp \ - $$PWD/controllers/librariescontroller.cpp \ - $$PWD/controllers/pagecontroller.cpp \ - $$PWD/controllers/sessionmanager.cpp \ - $$PWD/controllers/covercontroller.cpp \ - server/controllers/updatecomiccontroller.cpp \ - server/controllers/comicdownloadinfocontroller.cpp + $$PWD/controllers/comiccontroller.cpp \ + $$PWD/controllers/errorcontroller.cpp \ + $$PWD/controllers/foldercontroller.cpp \ + $$PWD/controllers/folderinfocontroller.cpp \ + $$PWD/controllers/librariescontroller.cpp \ + $$PWD/controllers/pagecontroller.cpp \ + $$PWD/controllers/sessionmanager.cpp \ + $$PWD/controllers/covercontroller.cpp \ + $$PWD/controllers/updatecomiccontroller.cpp \ + $$PWD/controllers/comicdownloadinfocontroller.cpp \ + $$PWD/controllers/synccontroller.cpp include(lib/bfLogging/bfLogging.pri) include(lib/bfHttpServer/bfHttpServer.pri) diff --git a/common/comic.cpp b/common/comic.cpp index 22f6e6c5..9a148cda 100644 --- a/common/comic.cpp +++ b/common/comic.cpp @@ -1,4 +1,4 @@ -#include "comic.h" + #include "comic.h" #include #include @@ -18,9 +18,13 @@ const QStringList Comic::imageExtensions = QStringList() << "*.jpg" << "*.jpeg" << "*.png" << "*.gif" << "*.tiff" << "*.tif" << "*.bmp" << "*.webp"; const QStringList Comic::literalImageExtensions = QStringList() << "jpg" << "jpeg" << "png" << "gif" << "tiff" << "tif" << "bmp" << "webp"; +#ifndef use_unarr const QStringList Comic::comicExtensions = QStringList() << "*.cbr" << "*.cbz" << "*.rar" << "*.zip" << "*.tar" << "*.pdf" << "*.7z" << "*.cb7" << "*.arj" << "*.cbt"; const QStringList Comic::literalComicExtensions = QStringList() << "cbr" << "cbz" << "rar" << "zip" << "tar" << "pdf" << "7z" << "cb7" << "arj" << "cbt"; - +#else +const QStringList Comic::comicExtensions = QStringList() << "*.cbr" << "*.cbz" << "*.rar" << "*.zip" << "*.tar" << "*.pdf" << "*.cbt"; +const QStringList Comic::literalComicExtensions = QStringList() << "cbr" << "cbz" << "rar" << "zip" << "tar" << "pdf" << "cbt"; +#endif //----------------------------------------------------------------------------- Comic::Comic() :_pages(),_index(0),_path(),_loaded(false),bm(new Bookmarks()),_loadedPages(),_isPDF(false) @@ -305,6 +309,7 @@ bool FileComic::load(const QString & path, const ComicDB & comic) else { //QMessageBox::critical(NULL,tr("Not found"),tr("Comic not found")+" : " + path); + moveToThread(QApplication::instance()->thread()); emit errorOpening(); return false; } @@ -471,12 +476,14 @@ void FileComic::process() CompressedArchive archive(_path); if(!archive.toolsLoaded()) { + moveToThread(QApplication::instance()->thread()); emit errorOpening(tr("7z not found")); return; } if(!archive.isValid()) { + moveToThread(QApplication::instance()->thread()); emit errorOpening(tr("Format not supported")); return; } @@ -488,6 +495,7 @@ void FileComic::process() if(_fileNames.size()==0) { //QMessageBox::critical(NULL,tr("File error"),tr("File not found or not images in file")); + moveToThread(QApplication::instance()->thread()); emit errorOpening(); return; } @@ -507,6 +515,10 @@ void FileComic::process() if(_firstPage == -1) _firstPage = bm->getLastPage(); + + if(_firstPage >= _pages.length()) + _firstPage = 0; + _index = _firstPage; emit(openAt(_index)); @@ -527,9 +539,8 @@ void FileComic::process() emit imageLoaded(sortedIndex); emit imageLoaded(sortedIndex,_pages[sortedIndex]); }*/ - + moveToThread(QApplication::instance()->thread()); emit imagesLoaded(); - //moveToThread(QApplication::instance()->thread()); } @@ -587,6 +598,7 @@ void FolderComic::process() { //TODO emitir este mensaje en otro sitio //QMessageBox::critical(NULL,QObject::tr("No images found"),QObject::tr("There are not images on the selected folder")); + moveToThread(QApplication::instance()->thread()); emit errorOpening(); } else @@ -594,6 +606,9 @@ void FolderComic::process() if(_firstPage == -1) _firstPage = bm->getLastPage(); + if(_firstPage >= _pages.length()) + _firstPage = 0; + _index = _firstPage; emit(openAt(_index)); @@ -617,8 +632,8 @@ void FolderComic::process() count++; } } + moveToThread(QApplication::instance()->thread()); emit imagesLoaded(); - moveToThread(QApplication::instance()->thread()); } //////////////////////////////////////////////////////////////////////////////// @@ -660,6 +675,7 @@ bool PDFComic::load(const QString & path, int atPage) } else { + moveToThread(QApplication::instance()->thread()); emit errorOpening(); return false; } @@ -682,6 +698,7 @@ bool PDFComic::load(const QString & path, const ComicDB & comic) else { //QMessageBox::critical(NULL,tr("Not found"),tr("Comic not found")+" : " + path); + moveToThread(QApplication::instance()->thread()); emit errorOpening(); return false; } @@ -705,11 +722,13 @@ void PDFComic::process() { //delete pdfComic; //pdfComic = 0; + moveToThread(QApplication::instance()->thread()); emit errorOpening(); return; } if (pdfComic->isLocked()) { + moveToThread(QApplication::instance()->thread()); emit errorOpening(); return; } @@ -730,6 +749,10 @@ void PDFComic::process() if(_firstPage == -1) _firstPage = bm->getLastPage(); + + if(_firstPage >= _pages.length()) + _firstPage = 0; + _index = _firstPage; emit(openAt(_index)); @@ -741,8 +764,8 @@ void PDFComic::process() renderPage(i); delete pdfComic; + moveToThread(QApplication::instance()->thread()); emit imagesLoaded(); - moveToThread(QApplication::instance()->thread()); } void PDFComic::renderPage(int page) diff --git a/common/gl/yacreader_flow_gl.cpp b/common/gl/yacreader_flow_gl.cpp index 1309d0d9..6ab56239 100644 --- a/common/gl/yacreader_flow_gl.cpp +++ b/common/gl/yacreader_flow_gl.cpp @@ -355,7 +355,8 @@ void YACReaderFlowGL::paintGL() void YACReaderFlowGL::resizeGL(int width, int height) { - fontSize = (width + height) * 0.010; + float pixelRatio = devicePixelRatio(); + fontSize = (width + height) * 0.010 * pixelRatio; if(fontSize < 10) fontSize = 10; @@ -368,12 +369,13 @@ void YACReaderFlowGL::resizeGL(int width, int height) void YACReaderFlowGL::udpatePerspective(int width, int height) { - glViewport(0, 0, width, height); + float pixelRatio = devicePixelRatio(); + glViewport(0, 0, width*pixelRatio, height*pixelRatio); glMatrixMode(GL_PROJECTION); glLoadIdentity(); - gluPerspective(20.0, GLdouble(width) / (float)height, 1.0, 200.0); + gluPerspective(20.0, GLdouble(width) / (float)height, 1.0, 200.0); glMatrixMode(GL_MODELVIEW); } @@ -384,7 +386,7 @@ void YACReaderFlowGL::calcPos(YACReader3DImage & image, int pos) { if(pos == 0){ image.current = centerPos; - }else{ + }else{ if(pos > 0){ image.current.x = (config.centerDistance)+(config.xDistance*pos); image.current.y = config.yDistance*pos*-1; @@ -804,6 +806,8 @@ void YACReaderFlowGL::populate(int n) void YACReaderFlowGL::reset() { + makeCurrent(); + startAnimationTimer(); currentSelected = 0; @@ -819,6 +823,8 @@ void YACReaderFlowGL::reset() if(!hasBeenInitialized) lazyPopulateObjects = -1; + + doneCurrent(); } void YACReaderFlowGL::reload() @@ -1101,8 +1107,9 @@ void YACReaderFlowGL::mousePressEvent(QMouseEvent *event) if(event->button() == Qt::LeftButton) { float x,y; - x = event->x(); - y = event->y(); + float pixelRatio = devicePixelRatio(); + x = event->x()*pixelRatio; + y = event->y()*pixelRatio; GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; @@ -1139,8 +1146,9 @@ void YACReaderFlowGL::mouseDoubleClickEvent(QMouseEvent* event) { makeCurrent(); float x,y; - x = event->x(); - y = event->y(); + float pixelRatio = devicePixelRatio(); + x = event->x()*pixelRatio; + y = event->y()*pixelRatio; GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; @@ -1269,6 +1277,8 @@ void YACReaderComicFlowGL::updateImageData() return; } } + + delete[] indexes; } void YACReaderComicFlowGL::remove(int item) @@ -1405,6 +1415,8 @@ void YACReaderPageFlowGL::updateImageData() return; } } + + delete[] indexes; } void YACReaderPageFlowGL::populate(int n) diff --git a/common/gl_legacy/yacreader_flow_gl.cpp b/common/gl_legacy/yacreader_flow_gl.cpp index c585189b..d367e20b 100644 --- a/common/gl_legacy/yacreader_flow_gl.cpp +++ b/common/gl_legacy/yacreader_flow_gl.cpp @@ -238,10 +238,10 @@ YACReaderFlowGL::YACReaderFlowGL(QWidget *parent,struct Preset p) loaderThread->start();*/ - QGLFormat f = format(); + /*QGLFormat f = format(); f.setVersion(2, 1); - f.setSwapInterval(0); - setFormat(f); + f.setSwapInterval(0); + setFormat(f);*/ timerId = startTimer(updateInterval); @@ -325,8 +325,8 @@ void YACReaderFlowGL::paintGL() void YACReaderFlowGL::resizeGL(int width, int height) { - - fontSize = (width + height) * 0.010; + float pixelRatio = devicePixelRatio(); + fontSize = (width + height) * 0.010 * pixelRatio; if(fontSize < 10) fontSize = 10; @@ -339,7 +339,8 @@ void YACReaderFlowGL::resizeGL(int width, int height) void YACReaderFlowGL::udpatePerspective(int width, int height) { - glViewport(0, 0, width, height); + float pixelRatio = devicePixelRatio(); + glViewport(0, 0, width*pixelRatio, height*pixelRatio); glMatrixMode(GL_PROJECTION); glLoadIdentity(); @@ -938,26 +939,25 @@ void YACReaderFlowGL::setPerformance(Performance performance) } void YACReaderFlowGL::useVSync(bool b) -{ - if(bUseVSync != b) +{/*if(bUseVSync != b) { bUseVSync = b; if(b) { QGLFormat f = format(); - f.setVersion(2, 1); + //f.setVersion(2, 1); f.setSwapInterval(1); setFormat(f); } else { QGLFormat f = format(); - f.setVersion(2, 1); + //f.setVersion(2, 1); f.setSwapInterval(0); setFormat(f); } - reset(); - } + reset(); + }*/ } void YACReaderFlowGL::setShowMarks(bool value) { @@ -1079,8 +1079,9 @@ void YACReaderFlowGL::mousePressEvent(QMouseEvent *event) if(event->button() == Qt::LeftButton) { float x,y; - x = event->x(); - y = event->y(); + float pixelRatio = devicePixelRatio(); + x = event->x()*pixelRatio; + y = event->y()*pixelRatio; GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; @@ -1114,8 +1115,9 @@ void YACReaderFlowGL::mousePressEvent(QMouseEvent *event) void YACReaderFlowGL::mouseDoubleClickEvent(QMouseEvent* event) { float x,y; - x = event->x(); - y = event->y(); + float pixelRatio = devicePixelRatio(); + x = event->x()*pixelRatio; + y = event->y()*pixelRatio; GLint viewport[4]; GLdouble modelview[16]; GLdouble projection[16]; @@ -1243,6 +1245,7 @@ void YACReaderComicFlowGL::updateImageData() return; } } + delete[] indexes; } void YACReaderComicFlowGL::remove(int item) @@ -1369,6 +1372,7 @@ void YACReaderPageFlowGL::updateImageData() return; } } + delete[] indexes; } void YACReaderPageFlowGL::populate(int n) diff --git a/common/opengl_checker.cpp b/common/opengl_checker.cpp new file mode 100644 index 00000000..d62bfac1 --- /dev/null +++ b/common/opengl_checker.cpp @@ -0,0 +1,69 @@ +#include "opengl_checker.h" + +#include "QsLog.h" + +OpenGLChecker::OpenGLChecker() + :compatibleOpenGLVersion(true) +{ + QOpenGLContext * openGLContext = new QOpenGLContext(); + openGLContext->create(); + + if(!openGLContext->isValid()) + { + compatibleOpenGLVersion = false; + description = "unable to create QOpenGLContext"; + } + + QSurfaceFormat format = openGLContext->format(); + + int majorVersion = format.majorVersion(); + int minorVersion = format.minorVersion(); + QString type; + + switch (format.renderableType()) { + case QSurfaceFormat::OpenGL: + type = "desktop"; + break; + + case QSurfaceFormat::OpenGLES: + type = "OpenGL ES"; + break; + + case QSurfaceFormat::OpenVG: + type = "OpenVG"; + + default: case QSurfaceFormat::DefaultRenderableType: + type = "unknown"; + break; + } + + delete openGLContext; + + description = QString("%1.%2 %3").arg(majorVersion).arg(minorVersion).arg(type); + + if(format.renderableType() != QSurfaceFormat::OpenGL) //Desktop OpenGL + compatibleOpenGLVersion = false; + +#ifdef Q_OS_WIN //TODO check Qt version, and set this values depending on the use of QOpenGLWidget or QGLWidget + static const int majorTargetVersion = 1; + static const int minorTargetVersion = 4; +#else + static const int majorTargetVersion = 2; + static const int minorTargetVersion = 0; +#endif + + if(majorVersion < majorTargetVersion) + compatibleOpenGLVersion = false; + if(majorVersion == majorTargetVersion && minorVersion < minorTargetVersion) + compatibleOpenGLVersion = false; +} + +QString OpenGLChecker::textVersionDescription() +{ + return description; +} + +bool OpenGLChecker::hasCompatibleOpenGLVersion() +{ + return compatibleOpenGLVersion; +} diff --git a/common/opengl_checker.h b/common/opengl_checker.h new file mode 100644 index 00000000..cce9772f --- /dev/null +++ b/common/opengl_checker.h @@ -0,0 +1,17 @@ +#ifndef OPENGL_CHECKER_H +#define OPENGL_CHECKER_H + +#include + +class OpenGLChecker +{ +public: + OpenGLChecker(); + bool hasCompatibleOpenGLVersion(); + QString textVersionDescription(); +private: + QString description; + bool compatibleOpenGLVersion; +}; + +#endif // OPENGL_CHECKER_H diff --git a/compileOSX.sh b/compileOSX.sh index 644549df..18228b93 100755 --- a/compileOSX.sh +++ b/compileOSX.sh @@ -5,14 +5,14 @@ fi echo "Compiling YACReader" cd ./YACReader -/Users/luisangel/Qt/5.4/clang_64/bin/qmake -spec macx-clang "CONFIG+=release" +/Users/luisangel/my_dev/Qt5.5.0/5.5/clang_64/bin/qmake -spec macx-clang "CONFIG+=release" #qmake -spec macx-g++ "CONFIG+=release" make cd .. echo "Compiling YACReaderLibrary" cd ./YACReaderLibrary -/Users/luisangel/Qt/5.4/clang_64/bin/qmake -spec macx-clang "CONFIG+=release" +/Users/luisangel/my_dev/Qt5.5.0/5.5/clang_64/bin/qmake -spec macx-clang "CONFIG+=release" #qmake -spec macx-g++ "CONFIG+=release" make cd .. diff --git a/compressed_archive/README_7zip.txt b/compressed_archive/README_7zip.txt index 3e4a5864..1653bbdd 100644 --- a/compressed_archive/README_7zip.txt +++ b/compressed_archive/README_7zip.txt @@ -1,7 +1,13 @@ -If you are trying to compile YACReader, you need to donwload de source code of 7zip (Windows) or p7zip (Linux/MacOSX). +If you are trying to compile YACReader with a 7zip decompression backend, you need to download de source code of 7zip (Windows) or p7zip (Linux/MacOSX). -Please, extract it and rename the folder to lib7zip (Windows) or libp7zip (Linux/MacOSX), then copy it to $YACREADER_SRC/compressed_archive/ (this -folder). If you are using a 64 bit Linux-System, please apply libp7zip.patch for successfull compilation or use compileX11.sh. +Please extract it and rename the folder to lib7zip (Windows) or libp7zip (Linux/MacOSX), then copy it to $YACREADER_SRC/compressed_archive/ (this +folder). -YACReader is compiled using 7zip/p7zip 9.20.1 +YACReader is compiled using 7zip/p7zip 9.20.1 and will not work with newer versions. +On Linux/Unix this means your YACReader installation will stop working if you update your installation of p7zip to a newer version. If you wish to keep using +p7zip with YACReader, you can copy 7z.so and Codecs/Rar29.so from p7zip 9.20.1 to "/usr/lib/yacreader/". YACReader will then detect these files and use +them instead of the system provided p7zip files which allows you to keep both YACReader and an up to date p7zip installation. + +Please keep in mind this is only a workaround that is provided for backwards compatibility and not intended as a long time solution. +It is recommended that you switch to unarr as a decompression backend instead (see README.txt in compressed_archive/unarr). \ No newline at end of file diff --git a/compressed_archive/compressed_archive.cpp b/compressed_archive/compressed_archive.cpp index 83d1895e..fe665cca 100644 --- a/compressed_archive/compressed_archive.cpp +++ b/compressed_archive/compressed_archive.cpp @@ -18,7 +18,9 @@ DEFINE_GUID(CLSID_CFormatRar, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, DEFINE_GUID(CLSID_CFormatZip, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x01, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatTar, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0xee, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatArj, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x04, 0x00, 0x00); -DEFINE_GUID(CLSID_CFormatBZip2, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x02, 0x00, 0x00); + +//unused Formats +/*DEFINE_GUID(CLSID_CFormatBZip2, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x02, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatCab, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatChm, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0xe9, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatCompound,0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0xe5, 0x00, 0x00); @@ -32,7 +34,7 @@ DEFINE_GUID(CLSID_CFormatNsis, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, DEFINE_GUID(CLSID_CFormatRpm, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0xeb, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatSplit, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0xea, 0x00, 0x00); DEFINE_GUID(CLSID_CFormatWim, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0xe6, 0x00, 0x00); -DEFINE_GUID(CLSID_CFormatZ, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x05, 0x00, 0x00); +DEFINE_GUID(CLSID_CFormatZ, 0x23170f69, 0x40c1, 0x278a, 0x10, 0x00, 0x00, 0x01, 0x10, 0x05, 0x00, 0x00);*/ #ifdef Q_OS_WIN GUID _supportedFileFormats[] = {CLSID_CFormatRar,CLSID_CFormatZip,CLSID_CFormatTar,CLSID_CFormat7z,CLSID_CFormatArj}; @@ -71,6 +73,13 @@ struct SevenZipInterface { //SevenZipInterface * szInterface; +const char rar[7]={static_cast(0x52), static_cast(0x61), static_cast(0x72), static_cast(0x21), static_cast(0x1A), static_cast(0x07), static_cast(0x00)}; +const char rar5[8]={static_cast(0x52), static_cast(0x61), static_cast(0x72), static_cast(0x21), static_cast(0x1A), static_cast(0x07), static_cast(0x01), static_cast(0x00)}; +const char zip[2]={static_cast(0x50), static_cast(0x4B)}; +const char sevenz[6]={static_cast(0x37), static_cast(0x7A), static_cast(0xBC), static_cast(0xAF), static_cast(0x27), static_cast(0x1C)}; +const char tar[6]="ustar"; +const char arj[2]={static_cast(0x60), static_cast(0xEA)}; + CompressedArchive::CompressedArchive(const QString & filePath, QObject *parent) : QObject(parent),sevenzLib(0),valid(false),tools(false) #ifdef Q_OS_UNIX @@ -97,63 +106,145 @@ CompressedArchive::CompressedArchive(const QString & filePath, QObject *parent) openCallbackSpec->PasswordIsDefined = false; // openCallbackSpec->PasswordIsDefined = true; // openCallbackSpec->Password = L"1"; - - for(unsigned int i=0;iOpen((LPCTSTR)filePath.toStdWString().c_str())) +#else + if (!fileSpec->Open((LPCTSTR)filePath.toStdString().c_str())) +#endif + { + qDebug() << "unable to load" + filePath; + return; + } + //GUID uuid = supportedFileFormats[i]; //qDebug() << "trying : " << uuid << endl; - if (szInterface->createObjectFunc(&supportedFileFormats[i], &IID_InArchive, (void **)&szInterface->archive) != S_OK) - { - qDebug() << "wrong format"; - continue; - } -#ifdef UNICODE - if (!fileSpec->Open((LPCTSTR)filePath.toStdWString().c_str())) -#else - if (!fileSpec->Open((LPCTSTR)filePath.toStdString().c_str())) -#endif - { - qDebug() << "unable to load" + filePath; - continue; - } - //qDebug() << "Can not open archive file : " + filePath << endl; + if (szInterface->createObjectFunc(&supportedFileFormats[i], &IID_InArchive, (void **)&szInterface->archive) == S_OK) + { + //qDebug() << "Can not open archive file : " + filePath << endl; if (szInterface->archive->Open(file, 0, openCallback) == S_OK) - { - valid = formatFound = true; - break; - } - else - qDebug() << "Can not open archive file : " + filePath << endl; + { + valid = formatFound = true; + qDebug() << "Opened archive file : " + filePath << endl; + setupFilesNames(); + return; + } } - if(!formatFound) - { + + + #ifdef Q_OS_WIN + if(!formatFound) + { qDebug() << "Can not open archive" << endl; + } + } +} #else + } + else + { + if (memcmp(magicNumber,rar5,7) == 0) + return;//we don't support rar5 + + isRar=true; //tell the destructor we *tried* to open a rar file! if (szInterface->createObjectFunc(&CLSID_CFormatRar, &IID_InArchive, (void **)&szInterface->archive) != S_OK) { qDebug() << "Error creating rar archive :" + filePath; return; } + + CMyComPtr codecsInfo; - CMyComPtr codecsInfo; if (szInterface->archive->QueryInterface(IID_ISetCompressCodecsInfo,(void **)&codecsInfo) != S_OK) { qDebug() << "Error getting rar codec :" + filePath; return; } - if (codecsInfo->SetCompressCodecsInfo(this) != S_OK) { qDebug() << "Error setting rar codec"; - return; + return; } #ifdef UNICODE - if (!fileSpec->Open((LPCTSTR)filePath.toStdWString().data())) + if (!fileSpec->Open((LPCTSTR)filePath.toStdWString().c_str())) #else - if (!fileSpec->Open((LPCTSTR)filePath.toStdString().data())) + if (!fileSpec->Open((LPCTSTR)filePath.toStdString().c_str())) #endif { qDebug() << "Error opening rar file :" + filePath; @@ -164,25 +255,31 @@ CompressedArchive::CompressedArchive(const QString & filePath, QObject *parent) if (szInterface->archive->Open(file, 0, openCallback) == S_OK) { valid = formatFound = true; - isRar = true; + setupFilesNames(); + //isRar = true; } - else - qDebug() << "Error opening rar archive"; - - -#endif } } } +#endif + CompressedArchive::~CompressedArchive() { + //always close the archive! + if (szInterface->archive) + { + szInterface->archive->Close(); + } + #ifdef Q_OS_UNIX - if(isRar) //TODO: fix this!!! Possible memory leak. If AddRef is not used, a crash occurs in "delete szInterface" + if(isRar) //TODO: Memory leak!!!! If AddRef is not used, a crash occurs in "delete szInterface" + { szInterface->archive->AddRef(); + } #endif - if(valid) //TODO: fix this!!! Memory leak. - delete szInterface; + delete szInterface; + #ifdef Q_OS_UNIX delete rarLib; #endif @@ -191,27 +288,27 @@ CompressedArchive::~CompressedArchive() bool CompressedArchive::loadFunctions() { - //LOAD library - //TODO check if this works in OSX (7z.so instead of 7z.dylib) - // fix1: try to load "7z.so" - // fix2: rename 7z.so to 7z.dylib - if(sevenzLib == 0) + //LOAD library + //TODO check if this works in OSX (7z.so instead of 7z.dylib) + // fix1: try to load "7z.so" + // fix2: rename 7z.so to 7z.dylib + if(sevenzLib == 0) { #if defined Q_OS_UNIX - #if defined Q_OS_MAC + #if defined Q_OS_MAC rarLib = new QLibrary(QCoreApplication::applicationDirPath()+"/utils/Codecs/Rar29"); - #else - //check if a yacreader specific version of p7zip exists on the system - QFileInfo rarCodec(QString(LIBDIR)+"/yacreader/Codecs/Rar29.so"); - if (rarCodec.exists()) - { - rarLib = new QLibrary(rarCodec.absoluteFilePath()); - } - else - { - rarLib = new QLibrary(QString(LIBDIR)+"/p7zip/Codecs/Rar29.so"); - } - #endif + #else + //check if a yacreader specific version of p7zip exists on the system + QFileInfo rarCodec(QString(LIBDIR)+"/yacreader/Codecs/Rar29.so"); + if (rarCodec.exists()) + { + rarLib = new QLibrary(rarCodec.absoluteFilePath()); + } + else + { + rarLib = new QLibrary(QString(LIBDIR)+"/p7zip/Codecs/Rar29.so"); + } + #endif if(!rarLib->load()) { qDebug() << "Error Loading Rar29.so : " + rarLib->errorString() << endl; @@ -220,43 +317,43 @@ bool CompressedArchive::loadFunctions() } #endif #if defined Q_OS_UNIX && !defined Q_OS_MAC - QFileInfo sevenzlibrary(QString(LIBDIR)+"/yacreader/7z.so"); - if (sevenzlibrary.exists()) - { - sevenzLib = new QLibrary(sevenzlibrary.absoluteFilePath()); - } - else - { - sevenzLib = new QLibrary(QString(LIBDIR)+"/p7zip/7z.so"); - } + QFileInfo sevenzlibrary(QString(LIBDIR)+"/yacreader/7z.so"); + if (sevenzlibrary.exists()) + { + sevenzLib = new QLibrary(sevenzlibrary.absoluteFilePath()); + } + else + { + sevenzLib = new QLibrary(QString(LIBDIR)+"/p7zip/7z.so"); + } #else sevenzLib = new QLibrary(QCoreApplication::applicationDirPath()+"/utils/7z"); #endif } - if(!sevenzLib->load()) - { + if(!sevenzLib->load()) + { qDebug() << "Error Loading 7z.dll : " + sevenzLib->errorString() << endl; QCoreApplication::exit(700); //TODO yacreader_global can't be used here, it is GUI dependant, YACReader::SevenZNotFound - return false; - } - else - { - qDebug() << "Loading functions" << endl; + return false; + } + else + { + qDebug() << "Loading functions" << endl; - if((szInterface->createObjectFunc = (CreateObjectFunc)sevenzLib->resolve("CreateObject")) == 0) - qDebug() << "fail loading function : CreateObject" << endl; - if((szInterface->getMethodPropertyFunc = (GetMethodPropertyFunc)sevenzLib->resolve("GetMethodProperty")) == 0) - qDebug() << "fail loading function : GetMethodProperty" << endl; - if((szInterface->getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)sevenzLib->resolve("GetNumberOfMethods")) == 0) - qDebug() << "fail loading function : GetNumberOfMethods" << endl; - if((szInterface->getNumberOfFormatsFunc = (GetNumberOfFormatsFunc)sevenzLib->resolve("GetNumberOfFormats")) == 0) - qDebug() << "fail loading function : GetNumberOfFormats" << endl; - if((szInterface->getHandlerPropertyFunc = (GetHandlerPropertyFunc)sevenzLib->resolve("GetHandlerProperty")) == 0) - qDebug() << "fail loading function : GetHandlerProperty" << endl; - if((szInterface->getHandlerPropertyFunc2 = (GetHandlerPropertyFunc2)sevenzLib->resolve("GetHandlerProperty2")) == 0) - qDebug() << "fail loading function : GetHandlerProperty2" << endl; - if((szInterface->setLargePageModeFunc = (SetLargePageModeFunc)sevenzLib->resolve("SetLargePageMode")) == 0) - qDebug() << "fail loading function : SetLargePageMode" << endl; + if((szInterface->createObjectFunc = (CreateObjectFunc)sevenzLib->resolve("CreateObject")) == 0) + qDebug() << "fail loading function : CreateObject" << endl; + if((szInterface->getMethodPropertyFunc = (GetMethodPropertyFunc)sevenzLib->resolve("GetMethodProperty")) == 0) + qDebug() << "fail loading function : GetMethodProperty" << endl; + if((szInterface->getNumberOfMethodsFunc = (GetNumberOfMethodsFunc)sevenzLib->resolve("GetNumberOfMethods")) == 0) + qDebug() << "fail loading function : GetNumberOfMethods" << endl; + if((szInterface->getNumberOfFormatsFunc = (GetNumberOfFormatsFunc)sevenzLib->resolve("GetNumberOfFormats")) == 0) + qDebug() << "fail loading function : GetNumberOfFormats" << endl; + if((szInterface->getHandlerPropertyFunc = (GetHandlerPropertyFunc)sevenzLib->resolve("GetHandlerProperty")) == 0) + qDebug() << "fail loading function : GetHandlerProperty" << endl; + if((szInterface->getHandlerPropertyFunc2 = (GetHandlerPropertyFunc2)sevenzLib->resolve("GetHandlerProperty2")) == 0) + qDebug() << "fail loading function : GetHandlerProperty2" << endl; + if((szInterface->setLargePageModeFunc = (SetLargePageModeFunc)sevenzLib->resolve("SetLargePageMode")) == 0) + qDebug() << "fail loading function : SetLargePageMode" << endl; #ifdef Q_OS_UNIX if((szInterface->createObjectFuncRar = (CreateObjectFunc)rarLib->resolve("CreateObject")) == 0) @@ -266,95 +363,127 @@ bool CompressedArchive::loadFunctions() if((szInterface->getNumberOfMethodsFuncRar = (GetNumberOfMethodsFunc)rarLib->resolve("GetNumberOfMethods")) == 0) qDebug() << "fail loading function (rar) : GetNumberOfMethods" << endl; #endif - } + } - return true; + return true; +} + +void CompressedArchive::setupFilesNames() +{ + quint32 numItems = getNumEntries(); + quint32 p = 0; + for (quint32 i = 0; i < numItems; i++) + { + + // Get name of file + NWindows::NCOM::CPropVariant prop; + szInterface->archive->GetProperty(i, kpidIsDir, &prop); + bool isDir; + if (prop.vt == VT_BOOL) + isDir = VARIANT_BOOLToBool(prop.boolVal); + else if (prop.vt == VT_EMPTY) + isDir = false; + + if(!isDir) + { + szInterface->archive->GetProperty(i, kpidPath, &prop); + UString s = ConvertPropVariantToString(prop); + const wchar_t * chars = s.operator const wchar_t *(); + files.append(QString::fromWCharArray(chars)); + offsets.append(i); + indexesToPages.insert(i,p); + p++; + } + + } +} + +QVector CompressedArchive::translateIndexes(const QVector & indexes) +{ + QVector translatedIndexes; + + foreach(quint32 i, indexes) + { + if(i < offsets.length()) + translatedIndexes.append(offsets.at(i)); + } + + return translatedIndexes; } QList CompressedArchive::getFileNames() { - QList files; - quint32 numItems = getNumFiles(); - for (quint32 i = 0; i < numItems; i++) - { - { - // Get name of file - NWindows::NCOM::CPropVariant prop; - /*szInterface->archive->GetProperty(i, kpidIsDir, &prop); - bool isDir; - if (prop.vt == VT_BOOL) - isDir = VARIANT_BOOLToBool(prop.boolVal); - else if (prop.vt == VT_EMPTY) - isDir = false; - - if(!isDir) - {*/ - szInterface->archive->GetProperty(i, kpidPath, &prop); - UString s = ConvertPropVariantToString(prop); - const wchar_t * chars = s.operator const wchar_t *(); - files.append(QString::fromWCharArray(chars)); - //} - } - } - return files; + return files; } bool CompressedArchive::isValid() { - return valid; + return valid; } bool CompressedArchive::toolsLoaded() { - return tools; + return tools; } int CompressedArchive::getNumFiles() { - quint32 numItems = 0; - szInterface->archive->GetNumberOfItems(&numItems); - return numItems; + return files.length(); +} + +int CompressedArchive::getNumEntries() +{ + quint32 numItems = 0; + szInterface->archive->GetNumberOfItems(&numItems); + return numItems; } QList CompressedArchive::getAllData(const QVector & indexes, ExtractDelegate * delegate) { - CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback(true,delegate); - CMyComPtr extractCallback(extractCallbackSpec); - extractCallbackSpec->Init(szInterface->archive, L""); // second parameter is output folder path - extractCallbackSpec->PasswordIsDefined = false; + CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback(indexesToPages, true, delegate); + CMyComPtr extractCallback(extractCallbackSpec); + extractCallbackSpec->Init(szInterface->archive, L""); // second parameter is output folder path + extractCallbackSpec->PasswordIsDefined = false; - HRESULT result; - if(indexes.isEmpty()) - result = szInterface->archive->Extract(NULL, -1, false, extractCallback); - else - result = szInterface->archive->Extract(indexes.data(), indexes.count(), false, extractCallback); - if (result != S_OK) - { - qDebug() << "Extract Error" << endl; - } - - return extractCallbackSpec->allFiles; + QVector currentIndexes = translateIndexes(indexes); + + HRESULT result; + if(indexes.isEmpty()) + result = szInterface->archive->Extract(NULL, -1, false, extractCallback); + else + result = szInterface->archive->Extract(currentIndexes.data(), currentIndexes.count(), false, extractCallback); + if (result != S_OK) + { + qDebug() << "Extract Error" << endl; + } + + return extractCallbackSpec->allFiles; } QByteArray CompressedArchive::getRawDataAtIndex(int index) { - if(index>=0 && index < getNumFiles()) - { - CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback; - CMyComPtr extractCallback(extractCallbackSpec); - extractCallbackSpec->Init(szInterface->archive, L""); // second parameter is output folder path - extractCallbackSpec->PasswordIsDefined = false; + if(index>=0 && index < getNumFiles()) + { + CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback(indexesToPages); + CMyComPtr extractCallback(extractCallbackSpec); + extractCallbackSpec->Init(szInterface->archive, L""); // second parameter is output folder path + extractCallbackSpec->PasswordIsDefined = false; - UInt32 indices[1]; - indices[0] = index; - HRESULT result = szInterface->archive->Extract(indices, 1, false, extractCallback); - if (result != S_OK) - { - qDebug() << "Extract Error" << endl; - } - - return QByteArray((char *)extractCallbackSpec->data,extractCallbackSpec->newFileSize); - } + UInt32 indices[1]; + + if(index < offsets.length()) + indices[0] = offsets.at(index); + else + indices[0] = index; + + HRESULT result = szInterface->archive->Extract(indices, 1, false, extractCallback); + if (result != S_OK) + { + qDebug() << "Extract Error" << endl; + } + + return QByteArray((char *)extractCallbackSpec->data,extractCallbackSpec->newFileSize); + } return QByteArray(); } @@ -384,5 +513,3 @@ STDMETHODIMP CompressedArchive::CreateEncoder(UInt32 index, const GUID *interfac } #endif - - diff --git a/compressed_archive/compressed_archive.h b/compressed_archive/compressed_archive.h index 21d24e46..75ace901 100644 --- a/compressed_archive/compressed_archive.h +++ b/compressed_archive/compressed_archive.h @@ -28,6 +28,7 @@ typedef quint32 (_MY_WINAPI *SetLargePageModeFunc)(); class QLibrary; #include #include +#include struct SevenZipInterface; @@ -59,6 +60,7 @@ signals: public slots: int getNumFiles(); + int getNumEntries(); QList getAllData(const QVector & indexes, ExtractDelegate * delegate = 0); QByteArray getRawDataAtIndex(int index); QList getFileNames(); @@ -66,6 +68,7 @@ public slots: bool toolsLoaded(); private: SevenZipInterface * szInterface; + QLibrary * sevenzLib; #ifdef Q_OS_UNIX QLibrary * rarLib; @@ -73,6 +76,12 @@ private: bool loadFunctions(); bool tools; bool valid; + QList files; + QList offsets; + QMap indexesToPages; + + void setupFilesNames(); + QVector translateIndexes(const QVector &indexes); friend class MyCodecs; }; diff --git a/compressed_archive/extract_callbacks.h b/compressed_archive/extract_callbacks.h index 998b8734..7b51cee9 100644 --- a/compressed_archive/extract_callbacks.h +++ b/compressed_archive/extract_callbacks.h @@ -90,8 +90,9 @@ public: UString Password; Byte * data; UInt64 newFileSize; + QMap indexesToPages; - CArchiveExtractCallback(bool c = false,ExtractDelegate * d = 0) : PasswordIsDefined(false),all(c),delegate(d) {} + CArchiveExtractCallback(const QMap & indexesToPages ,bool c = false,ExtractDelegate * d = 0) : PasswordIsDefined(false),all(c),delegate(d),indexesToPages(indexesToPages) {} ~CArchiveExtractCallback() {MidFree(data);} }; @@ -117,7 +118,11 @@ STDMETHODIMP CArchiveExtractCallback::GetStream(UInt32 index, { *outStream = 0; _outFileStream.Release(); - _index = index; + + if(indexesToPages.isEmpty()) + _index = index; + else + _index = indexesToPages.value(index); { // Get Name diff --git a/compressed_archive/unarr/README.txt b/compressed_archive/unarr/README.txt new file mode 100644 index 00000000..bc96eb93 --- /dev/null +++ b/compressed_archive/unarr/README.txt @@ -0,0 +1,6 @@ +To use unarr as a decompression engine when building YACReader, download https://github.com/zeniko/unarr/archive/master.zip and extract it in this folder. +This will build unarr as a part of YACReader (static build). + +If you're on a Linux/Unix system and prefer to use unarr as a shared library, have a look at https://github.com/selmf/unarr/ +This fork of unarr includes a CMake based build system that allows you to build and install unarr as a shared library. YACReader will detect and use +the installed library at build time if it is installed. \ No newline at end of file diff --git a/compressed_archive/unarr/compressed_archive.cpp b/compressed_archive/unarr/compressed_archive.cpp new file mode 100644 index 00000000..b7594986 --- /dev/null +++ b/compressed_archive/unarr/compressed_archive.cpp @@ -0,0 +1,127 @@ +#include "compressed_archive.h" + +#include +#include + +#include "extract_delegate.h" + +extern"C" { +#include "unarr.h" +} + +CompressedArchive::CompressedArchive(const QString & filePath, QObject *parent) : + QObject(parent),valid(false),tools(true),numFiles(0),ar(NULL),stream(NULL) +{ + //open file + stream = ar_open_file(filePath.toStdString().c_str()); + if (!stream) + { + return; + } + + //open archive + ar = ar_open_rar_archive(stream); + //TODO: build unarr with 7z support and test this! + //if (!ar) ar = ar_open_7z_archive(stream); + if (!ar) ar = ar_open_tar_archive(stream); + //zip detection is costly, so it comes last... + if (!ar) ar = ar_open_zip_archive(stream, false); + if (!ar) + { + return; + } + + //initial parse + while (ar_parse_entry(ar)) + { + //make sure we really got a file header + if (ar_entry_get_size(ar) > 0) + { + fileNames.append(ar_entry_get_name(ar)); + offsets.append(ar_entry_get_offset(ar)); + numFiles++; + } + } + if (!ar_at_eof(ar)) + { + //fail if the initial parse didn't reach EOF + //this might be a bit too drastic + qDebug() << "Error while parsing archive"; + return; + } + if (numFiles > 0) + { + valid = true; + } +} + +CompressedArchive::~CompressedArchive() +{ + ar_close_archive(ar); + ar_close(stream); +} + +QList CompressedArchive::getFileNames() +{ + return fileNames; +} + +bool CompressedArchive::isValid() +{ + return valid; +} + +bool CompressedArchive::toolsLoaded() +{ + //for backwards compatibilty + return tools; +} + +int CompressedArchive::getNumFiles() +{ + return numFiles; +} + +void CompressedArchive::getAllData(const QVector & indexes, ExtractDelegate * delegate) +{ + if (indexes.isEmpty()) + return; + + QByteArray buffer; + + int i=0; + while (i < indexes.count()) + { + //use the offset list so we generated so we're not getting any non-page files + ar_parse_entry_at(ar, offsets.at(indexes.at(i))); //set ar_entry to start of indexes + buffer.resize(ar_entry_get_size(ar)); + if (ar_entry_uncompress(ar, buffer.data(), buffer.size())) //did we extract it? + { + delegate->fileExtracted(indexes.at(i), buffer); //return extracted file + } + else + { + delegate->crcError(indexes.at(i)); //we could not extract it... + } + i++; + } +} + +QByteArray CompressedArchive::getRawDataAtIndex(int index) +{ + QByteArray buffer; + if(index >= 0 && index < getNumFiles()) + { + ar_parse_entry_at(ar, offsets.at(index)); + buffer.resize(ar_entry_get_size(ar)); + if(ar_entry_uncompress(ar, buffer.data(), buffer.size())) + { + return buffer; + } + else + { + return QByteArray(); + } + } + return buffer; +} diff --git a/compressed_archive/unarr/compressed_archive.h b/compressed_archive/unarr/compressed_archive.h new file mode 100644 index 00000000..c0a99938 --- /dev/null +++ b/compressed_archive/unarr/compressed_archive.h @@ -0,0 +1,37 @@ +#ifndef COMPRESSED_ARCHIVE_H +#define COMPRESSED_ARCHIVE_H + +#include +#include "extract_delegate.h" +extern"C" { +#include "unarr.h" +} + +class CompressedArchive : public QObject +{ + Q_OBJECT +public: + explicit CompressedArchive(const QString & filePath, QObject *parent = 0); + ~CompressedArchive(); + +signals: + +public slots: + int getNumFiles(); + void getAllData(const QVector & indexes, ExtractDelegate * delegate=0); + QByteArray getRawDataAtIndex(int index); + QList getFileNames(); + bool isValid(); + bool toolsLoaded(); +private: + + bool tools; + bool valid; + QList fileNames; + int numFiles; + ar_archive *ar; + ar_stream *stream; + QList offsets; +}; + +#endif // COMPRESSED_ARCHIVE_H diff --git a/compressed_archive/unarr/extract_delegate.h b/compressed_archive/unarr/extract_delegate.h new file mode 100644 index 00000000..888d886a --- /dev/null +++ b/compressed_archive/unarr/extract_delegate.h @@ -0,0 +1,14 @@ +#ifndef EXTRACT_DELEGATE_H +#define EXTRACT_DELEGATE_H + +#include + +class ExtractDelegate +{ + public: + virtual void fileExtracted(int index, const QByteArray & rawData) = 0; + virtual void crcError(int index) = 0; + virtual void unknownError(int index) = 0; +}; + +#endif //EXTRACT_DELEGATE_H \ No newline at end of file diff --git a/compressed_archive/unarr/unarr-wrapper.pri b/compressed_archive/unarr/unarr-wrapper.pri new file mode 100644 index 00000000..2ba188c6 --- /dev/null +++ b/compressed_archive/unarr/unarr-wrapper.pri @@ -0,0 +1,36 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +HEADERS += $$PWD/extract_delegate.h \ + $$PWD/compressed_archive.h \ + +SOURCES += $$PWD/compressed_archive.cpp \ + +unix:!macx:exists (/usr/include/unarr.h) { + message(Using system provided unarr installation) + LIBS+=-lunarr + DEFINES+=use_unarr + } +else:macx:exists (../../dependencies/unarr/libunarr.dynlib) { + LIBS += -L../../dependencies/unarr/ -lunarr + DEFINES+=use_unarr + } + +else:win32:exists (../../dependencies/unarr/unarr.dll) { + LIBS += -L../../dependencies/unarr/ -lunarr + DEFINES+=use_unarr + } + +else:exists ($$PWD/unarr-master) { + message(Found unarr source-code) + message(Unarr will be build as a part of YACReader) + + #qmake based unarr build system + #this should only be used for testing or as a last resort + include(unarr.pro) + DEFINES+=use_unarr + } + else { + error(Missing dependency: unarr decrompression backend. Please install libunarr on your system\ + or provide a copy of the unarr source code in compressed_archive/unarr/unarr-master) + } \ No newline at end of file diff --git a/compressed_archive/unarr/unarr.pro b/compressed_archive/unarr/unarr.pro new file mode 100644 index 00000000..f024872f --- /dev/null +++ b/compressed_archive/unarr/unarr.pro @@ -0,0 +1,51 @@ +INCLUDEPATH += $$PWD/unarr-master/ +DEPENDPATH += $$PWD/unarr-master/ + +HEADERS+=$$PWD/unarr-master/common/allocator.h\ + $$PWD/unarr-master/common/unarr-imp.h\ + $$PWD/unarr-master/lzmasdk/CpuArch.h\ + $$PWD/unarr-master/lzmasdk/Ppmd7.h\ + $$PWD/unarr-master/lzmasdk/Ppmd.h\ + $$PWD/unarr-master/lzmasdk/LzmaDec.h\ + $$PWD/unarr-master/lzmasdk/Ppmd8.h\ + $$PWD/unarr-master/lzmasdk/Types.h\ + $$PWD/unarr-master/lzmasdk/CpuArch.h\ + $$PWD/unarr-master/lzmasdk/Ppmd7.h\ + $$PWD/unarr-master/lzmasdk/Ppmd.h\ + $$PWD/unarr-master/lzmasdk/LzmaDec.h\ + $$PWD/unarr-master/lzmasdk/Ppmd8.h\ + $$PWD/unarr-master/lzmasdk/Types.h\ + $$PWD/unarr-master/lzmasdk/CpuArch.h\ + $$PWD/unarr-master/lzmasdk/Ppmd7.h\ + $$PWD/unarr-master/lzmasdk/Ppmd.h\ + $$PWD/unarr-master/lzmasdk/LzmaDec.h\ + $$PWD/unarr-master/lzmasdk/Ppmd8.h\ + $$PWD/unarr-master/lzmasdk/Types.h\ + $$PWD/unarr-master/tar/tar.h\ + $$PWD/unarr-master/_7z/_7z.h\ + $$PWD/unarr-master/unarr.h + +SOURCES+=$$PWD/unarr-master/common/conv.c\ + $$PWD/unarr-master/common/custalloc.c\ + $$PWD/unarr-master/common/unarr.c\ + $$PWD/unarr-master/common/crc32.c\ + $$PWD/unarr-master/common/stream.c\ + $$PWD/unarr-master/lzmasdk/CpuArch.c\ + $$PWD/unarr-master/lzmasdk/Ppmd7.c\ + $$PWD/unarr-master/lzmasdk/Ppmd8.c\ + $$PWD/unarr-master/lzmasdk/LzmaDec.c\ + $$PWD/unarr-master/lzmasdk/Ppmd7Dec.c\ + $$PWD/unarr-master/lzmasdk/Ppmd8Dec.c\ + $$PWD/unarr-master/zip/inflate.c\ + $$PWD/unarr-master/zip/parse-zip.c\ + $$PWD/unarr-master/zip/uncompress-zip.c\ + $$PWD/unarr-master/zip/zip.c\ + $$PWD/unarr-master/rar/filter-rar.c\ + $$PWD/unarr-master/rar/parse-rar.c\ + $$PWD/unarr-master/rar/rarvm.c\ + $$PWD/unarr-master/rar/huffman-rar.c\ + $$PWD/unarr-master/rar/rar.c\ + $$PWD/unarr-master/rar/uncompress-rar.c\ + $$PWD/unarr-master/tar/parse-tar.c\ + $$PWD/unarr-master/tar/tar.c\ + $$PWD/unarr-master/_7z/_7z.c \ No newline at end of file diff --git a/compressed_archive/wrapper.pri b/compressed_archive/wrapper.pri index 933a27e7..9ae4b25b 100644 --- a/compressed_archive/wrapper.pri +++ b/compressed_archive/wrapper.pri @@ -1,6 +1,23 @@ INCLUDEPATH += $$PWD DEPENDPATH += $$PWD +win32 { +!exists (../compressed_archive/lib7zip) { + error(You\'ll need 7zip source code to compile YACReader. \ + Please check the compressed_archive folder for further instructions.) +} +} + +unix { +exists (../compressed_archive/libp7zip) { + message(Found p7zip source code...) + system(patch -N -p0 -i libp7zip.patch) +} else { + error(You\'ll need 7zip source code to compile YACReader. \ + Please check the compressed_archive folder for further instructions.) +} +} + CONFIG += precompile_header win32 {PRECOMPILED_HEADER = $$PWD/StdAfx.h} diff --git a/config.pri b/config.pri new file mode 100644 index 00000000..0d4733bd --- /dev/null +++ b/config.pri @@ -0,0 +1,40 @@ +#functions to automatically initialize some of YACReader's build options to +#default values if they're not set on build time +#for a more detailed description, see INSTALL.TXT + +#check Qt version +QT_VERSION = $$[QT_VERSION] +QT_VERSION = $$split(QT_VERSION, ".") +QT_VER_MAJ = $$member(QT_VERSION, 0) +QT_VER_MIN = $$member(QT_VERSION, 1) + +lessThan(QT_VER_MAJ, 5) { +error(YACReader requires Qt 5 or newer but Qt $$[QT_VERSION] was detected.) + } +lessThan(QT_VER_MIN, 4):!CONFIG(no_opengl) { + CONFIG += legacy_gl_widget + message ("Qt < 5.4 detected. Using QGLWidget for coverflow.") + } +lessThan(QT_VER_MIN, 3){ + error ("You need at least Qt 5.3 to build YACReader or YACReaderLibrary") + } + +#build without opengl widget support +CONFIG(no_opengl) { + DEFINES += NO_OPENGL +} + +!CONFIG(unarr):!CONFIG(7zip) { + unix { + !macx { + CONFIG+=unarr + } + else { + CONFIG+=7zip + } + + } + win32 { + CONFIG+=7zip + } +} \ No newline at end of file diff --git a/custom_widgets/yacreader_macosx_toolbar.mm b/custom_widgets/yacreader_macosx_toolbar.mm index b5c52d3d..1570e9b8 100644 --- a/custom_widgets/yacreader_macosx_toolbar.mm +++ b/custom_widgets/yacreader_macosx_toolbar.mm @@ -175,7 +175,7 @@ YACReaderMacOSXToolbar::YACReaderMacOSXToolbar(QObject *parent) { yosemite = true; //TODO yosemite new constants are not found in compilation time - [nswindow setTitleVisibility:1]; //NSWindowTitleHidden + [nswindow setTitleVisibility:NSWindowTitleHidden]; //TODO NSFullSizeContentViewWindowMask produces an offset in the windows' content //nswindow.styleMask |= 1 << 15; // NSFullSizeContentViewWindowMask; [nativeToolBar setSizeMode:NSToolbarSizeModeSmall]; //TODO figure out how to load specific images in Yosemite @@ -215,7 +215,7 @@ void YACReaderMacOSXToolbar::addSpace(int size) QMacToolBarItem *toolBarItem = addItem(QIcon(),""); NSToolbarItem * nativeItem = toolBarItem->nativeToolBarItem(); - static const NSRect frameRect = { { 0.0, 0.0 }, { size, 16.0 } }; + static const NSRect frameRect = { { 0.0, 0.0 }, { CGFloat(size), 16.0 } }; NSView *view = [[NSView alloc] initWithFrame:frameRect]; [nativeItem setView:view]; diff --git a/custom_widgets/yacreader_options_dialog.cpp b/custom_widgets/yacreader_options_dialog.cpp index 4047acea..db4d1f34 100644 --- a/custom_widgets/yacreader_options_dialog.cpp +++ b/custom_widgets/yacreader_options_dialog.cpp @@ -46,6 +46,9 @@ YACReaderOptionsDialog::YACReaderOptionsDialog(QWidget * parent) #ifndef NO_OPENGL useGL = new QCheckBox(tr("Use hardware acceleration (restart needed)")); connect(useGL,SIGNAL(stateChanged(int)),this,SLOT(saveUseGL(int))); +#endif +#ifdef FORCE_ANGLE + useGL->setHidden(true); #endif //sw CONNECTIONS connect(sw->radio1,SIGNAL(toggled(bool)),this,SLOT(setClassicConfigSW())); @@ -395,4 +398,4 @@ void YACReaderOptionsDialog::setRouletteConfig() saveFlowParameters(); } -#endif \ No newline at end of file +#endif diff --git a/custom_widgets/yacreader_titled_toolbar.cpp b/custom_widgets/yacreader_titled_toolbar.cpp index e47139f7..9651a59d 100644 --- a/custom_widgets/yacreader_titled_toolbar.cpp +++ b/custom_widgets/yacreader_titled_toolbar.cpp @@ -85,9 +85,9 @@ YACReaderTitledToolBar::YACReaderTitledToolBar(const QString & title, QWidget *p setLayout(mainLayout); - setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum); + setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum); - setMinimumHeight(25); + setMinimumHeight(25); } @@ -97,9 +97,9 @@ void YACReaderTitledToolBar::addAction(QAction * action) QToolButton * tb = new QToolButton(this); tb->setCursor(QCursor(Qt::ArrowCursor)); - tb->setDefaultAction(action); - tb->setIconSize(QSize(16,16)); - tb->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); + tb->setDefaultAction(action); + tb->setIconSize(QSize(16,16)); + tb->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); //tb->setStyleSheet("QToolButton:hover {background-color:#C5C5C5;}"); mainLayout->addWidget(tb); diff --git a/custom_widgets/yacreader_tool_bar_stretch.h b/custom_widgets/yacreader_tool_bar_stretch.h index d4817176..e2d71de3 100644 --- a/custom_widgets/yacreader_tool_bar_stretch.h +++ b/custom_widgets/yacreader_tool_bar_stretch.h @@ -4,10 +4,10 @@ #include #include -class QToolBarStretch : public QWidget +class YACReaderToolBarStretch : public QWidget { public: - QToolBarStretch(QWidget * parent=0):QWidget(parent) + YACReaderToolBarStretch(QWidget * parent=0):QWidget(parent) { QHBoxLayout * l= new QHBoxLayout(); l->addStretch(); diff --git a/mktarball.sh b/mktarball.sh index 47e1227a..d1a84f8c 100755 --- a/mktarball.sh +++ b/mktarball.sh @@ -2,7 +2,7 @@ #Script to create a source tarball for YACReader distribution and packaging #This should be run from YACReaders top source directory -YACVERSION=7.2.0 +YACVERSION=8.0 if [ -f Makefile ] then make distclean @@ -17,6 +17,6 @@ fi #Use tar's --exclude feature to make sure we get a pristine tar for distribution. #Exclude all version control system related files and rename the top directory in the tarball using --transform. tar cfJ yacreader-${YACVERSION}-src.tar.xz --exclude '*.rej' --exclude '*.orig' --exclude '*.gch' --exclude 'dependencies' --exclude '*.o' \ ---exclude 'yacreader*tar*' --exclude '.hg*' --exclude 'lib7zip' --exclude 'libp7zip' --exclude-vcs ./* --transform s/./yacreader-${YACVERSION}/ +--exclude 'yacreader*tar*' --exclude '.hg*' --exclude 'lib7zip' --exclude 'libp7zip' --exclude 'unarr-master' --exclude-vcs ./* --transform s/./yacreader-${YACVERSION}/ #Calculate checksum to enable packagers to verify whether they are using the original tarball. md5sum yacreader-${YACVERSION}-src.tar.xz > yacreader-${YACVERSION}-src.tar.xz.md5sum \ No newline at end of file diff --git a/release/server/docroot/css/styles_ipad.css b/release/server/docroot/css/styles_ipad.css index 8f415579..1419c4b2 100644 --- a/release/server/docroot/css/styles_ipad.css +++ b/release/server/docroot/css/styles_ipad.css @@ -5,7 +5,7 @@ body{ /* libraries */ #contentLibraries{ - width: 300px; + width: 400px; border: 1px solid #C6C6C6; background-color: white; margin-left: auto; @@ -36,7 +36,7 @@ body{ #contentLibraries .library-link { - width: 211px; + width: 311px; height: 28px; border: none; padding: 11px 0 0 0px; @@ -47,6 +47,7 @@ body{ font-size: 16px; text-decoration: none; color: #525252 ; + overflow: hidden; } #contentLibraries a diff --git a/release/server/docroot/css/styles_iphone.css b/release/server/docroot/css/styles_iphone.css index fdf3dc0d..200ce45f 100644 --- a/release/server/docroot/css/styles_iphone.css +++ b/release/server/docroot/css/styles_iphone.css @@ -5,11 +5,10 @@ body{ /* libraries */ #contentLibraries{ - width: 300px; border: 1px solid #C6C6C6; background-color: white; - margin-left: auto; - margin-right: auto; + margin-left: 20px; + margin-right: 20px; margin-top: 9px; } @@ -36,7 +35,7 @@ body{ #contentLibraries .library-link { - width: 211px; + width: 65%; height: 28px; border: none; padding: 11px 0 0 0px; @@ -47,6 +46,7 @@ body{ font-size: 16px; text-decoration: none; color: #525252 ; + overflow: hidden; } #contentLibraries a @@ -62,7 +62,7 @@ body{ #contentLibraries .library-indicator { - float: left; + float: right; background-color: white; height: 8px; padding: 16px 16px 15px 16px; @@ -181,10 +181,10 @@ body{ #itemContainer li { -width: 300px; + height: 120px; border: 1px solid #E2E2E2; -margin: 9px auto 0px auto; +margin: 9px 10px 0px 10px; background-color: white; overflow: hidden; position: relative; @@ -218,22 +218,22 @@ overflow: hidden; .info { padding: 8px 0px 0px 0px; -float: left; + position: relative; height: 115px; -width: 212px; +padding-left: 82px; } .buttons { position:absolute; bottom:0px; - left:0px; + left:80px; + right: 0px; border-top: 1px solid #e2e2e2; padding-top: 3px; height: 25px; - width: 220px; font-family: Arial; color: #6e6e6e; font-size: 10px; @@ -245,7 +245,7 @@ width: 212px; bottom:24px; padding-top: 3px; height: 25px; - width: 220px; + width: 100%; font-family: Arial; color: #adadad; font-size: 10px; diff --git a/releaseOSX.sh b/releaseOSX.sh index 060a744e..79e82966 100755 --- a/releaseOSX.sh +++ b/releaseOSX.sh @@ -1,7 +1,7 @@ #!/bin/bash -/Users/luisangel/Qt/5.4/clang_64/bin/macdeployqt YACReader.app -/Users/luisangel/Qt/5.4/clang_64/bin/macdeployqt YACReaderLibrary.app -qmldir=./YACReaderLibrary/qml +/Users/luisangel/my_dev/Qt5.5.0/5.5/clang_64/bin/macdeployqt YACReader.app +/Users/luisangel/my_dev/Qt5.5.0/5.5/clang_64/bin/macdeployqt YACReaderLibrary.app -qmldir=./YACReaderLibrary/qml #macdeployqt YACReader.app #macdeployqt YACReaderLibrary.app diff --git a/tests/compressed_archive_test/compressed_archive_test.pro b/tests/compressed_archive_test/compressed_archive_test.pro new file mode 100644 index 00000000..a6c55cc4 --- /dev/null +++ b/tests/compressed_archive_test/compressed_archive_test.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +CONFIG += console + +SOURCES += \ + main.cpp \ + +QT += core + +win32 { + LIBS += -loleaut32 -lole32 + QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT + QMAKE_LFLAGS_RELEASE += /LTCG + CONFIG -= embed_manifest_exe +} + +!CONFIG(unarr){ + include(../../compressed_archive/wrapper.pri) +} else { + include(../../compressed_archive/unarr/unarr-wrapper.pri) +} + + + diff --git a/tests/compressed_archive_test/main.cpp b/tests/compressed_archive_test/main.cpp new file mode 100644 index 00000000..f2686a51 --- /dev/null +++ b/tests/compressed_archive_test/main.cpp @@ -0,0 +1,86 @@ + +#include +#include +#include + +#include "compressed_archive.h" + +#include + +using namespace std; + + +//This program uses PROTOS Genome Test Suite c10-archive [0] for testing the CompressedArchive wrapper files support +//It tests the following formats: RAR, ZIP, TAR +//Arter downloading c10-archive-r1.iso, open it and full extract RAR_TAR.BZ2, ZIP_TAR.BZ2, TAR_TAR.BZ2 files into a folder +//This program takes the path to that folder as an argument +// +// [0] https://www.ee.oulu.fi/research/ouspg/PROTOS_Test-Suite_c10-archive#Download +// +int main(int argc, char *argv[]) +{ + if(argc < 2) + { + cout << "Usage: compressed_archive_test PATH" << endl; + return 0; + } + + //QCoreApplication app(argc, argv); + + QString s(argv[1]); + + QStringList supportedFormats; + supportedFormats << "rar"<< "zip" << "tar"; + + QElapsedTimer timer; + timer.start(); + + quint32 totalFiles = 0; + foreach (QString format, supportedFormats) { + QDir rootDir(s); + if(!rootDir.cd(format)) + { + cout << "Folder for format '" << format.toStdString() << "' not found" << endl; + continue; + } + rootDir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + + QFileInfoList files = rootDir.entryInfoList(); + quint32 totalFormat = 0; + quint32 errors = 0; + quint64 init = timer.elapsed(); + + foreach(QFileInfo fileInfo, files) + { + totalFiles++; + totalFormat++; + CompressedArchive archive(fileInfo.filePath()); + if(!archive.isValid()) + errors++; + else + { + int i = archive.getNumFiles(); + cerr << i; + QList filenames = archive.getFileNames(); + if (!filenames.isEmpty()) + { + cerr << archive.getFileNames().at(0).toStdString(); + } + } + } + quint64 end = timer.elapsed(); + + + cout << "Format '" << format.toStdString() <<"'" << endl; + cout << "Total files : " << totalFormat << endl; + cout << "Errors : " << errors << endl; + cout << "Elapsed time : " << (end - init) / 1000 <<"s" << endl; + cout << endl; + } + + cout << endl; + cout << "Total time : " << timer.elapsed() / 1000 <<"s" <