commit 2edfe534bd0b0482ec4b85b88ce50ceb474fd526
Author: Felix Kauselmann <2039670+selmf@users.noreply.github.com>
Date: Mon Oct 17 22:01:23 2016 +0200
Refactoring: fix mixed indentation and add curly braces in comic.cpp
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 00000000..51e0b35c
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,149 @@
+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.
+Corregido error que marcaba un comic terminado como empezado si se volvía a leer.
+Añadidos 2 estados para las carpetas (Completo/Terminado)
+Corregido bug en la comunicación YACReaderLibrary <-> YACReader
+Añadidas las acciones relativas a los comics al menú contextual de la tabla de cómics.
+Corrgido bug que provocaba el crecimiento ilimatado del log del servidor
+Corregidos bugs menores
+
+7.0.2 (Sólo MacOSX)
+Eliminado el uso de Poppler en la versión de MacOSX
+Trabajo en traducciones.
+Corregidos bugs menores
+
+7.0.1
+Añadido QsLog a YACReader
+Corregido bug en la comunicación YACReaderLibrary <-> YACReader
+
+7.0 (Final)
+Corregidos eventos de teclado en algunos diálogos
+Corregido soporte para archivos Rar en sistemas Unix
+Corregidos problemas borrando cómics
+Mejorada la gestión de errores
+Corregida la comunicación entre YACReader y YACReaderLibrary
+Corregida la toolBar en MacOSX
+Mejorada la compatabilidad de OpenGL en tarjetas NVIDIA
+Corregidos bugs menores
+
+6.9 (No pública)
+Añadida la apertura automática del siguiente/anterior cómic al llegar al final/portada del cómic actual
+Corregido el comportamiento del diálogo de nueva versión detectada. Ahora avisa una vez al día o si el usuario lo elige cada 14 días.
+Corregido el ajuste a lo ancho del título de la toolbar en YACReaderLibrary.
+Añadido log a YACReaderLibrary (permitirá a los usuarios ofrecer más información sobre sus bugs)
+Corregido bug en el historial de navegación (y al editar comics) después de usar el motor de búsqueda.
+
+6.8 (No pública)
+Corregido bug que causaba un cierre inesperado después de cambiar el modo de sincronización vertical (flow)
+Corregido bug que causaba que la toolbar en el visor no se pudiese ocultar/mostrar sin un cómic abierto
+Mejorada la gestión de errores al abrir cómics
+Corregidos algunos bugs relacionados con la apertura de cómics
+Añadida función de rating
+El visor ahora puede abrir archivos de imagen directamente. Si se abre un archivo de imagen se abre el directorio que lo contiene con todas las imágenes.
+Corregida la ordenación de carpetas y cómics usada en la navegación desde dispositivos iOS
+
+6.7 (No pública)
+Añadidos nuevos campos en la base de datos para almacenar información adicional sobre cómics: rating, página actual, bookmarks y configuración de imagen
+Añadida comunicación entre YACReaderLibrary y YACReader para poder almacenar el progreso de los cómics e información adicional
+
+6.6 (No pública)
+Modificado YACReader para que abra los archivos comprimidos usando 7z.dll (.so, .dylib)
+YACReader abre ahora los cómics por la última página leída.
+Corregido bug que causaba que algunos cómics no se pudiesen abrir desde YACReaderLibrary en YACReader
+Corregido el modo en el que se actualizaba la "information label"
+
+6.5
+Nueva interfaz principal de YACReaderLibrary y YACReader
+Corregido bug que causaba que el servidor no se activase en el primer arranque en MacOSX
+Corregido bug que causaba un fallo al cerrar YACReaderLibrary cada vez que se usaba el servidor
+Nuevo diseño para el diálogo de propiedades de los cómics.
+Añadida navegación alante y atrás de las carpetas visitadas.
+La edición del nombre de una biblioteca no fuerza ahora que se recargue la biblioteca
+Corregido el color de fondo en la lupa
+Nuevo botón para ajustar a lo alto
+Eliminada la opción always on top
+Mostrar en carpeta contenedora arreglado en Windows y MacOSX
+
+6.4 (No pública)
+Normalizado el renderizado de páginas en modo doble página
+Añadida la función de borrar cómics desde el disco duro
+Nuevos iconos de la barra de herramientas de cómics
+
+6.3 (No pública)
+Mejorada la gestión de errores relacionada con las bibliotecas
+Añadido botón que permite ocultar las portadas en la pantalla de importación
+Añadidos títulos "Bibliotecas" y "Carpetas" a la barra de navegación
+Nuevos iconos para seleccionar la carpeta raíz, expandir y contraer todos.
+Botón para cambiar el puerto del servidor por el usuario.
+Ahora las columnas de la lista de cómics pueden reordenarse
+Ahora YACReaderLibrary sólo permite una instancia ejecutandose.
+Columna leído añadida.
+Cambiado estilo de la lista de cómics
+Corregidos bugs relacionados con realizar operaciones sobre cómics cuando no había ninguno seleccionado en la lista de cómics
+
+6.2
+Nueva ventana de "bienvenida"
+Nueva ventana de importar/actualizar
+Nuevo control para la búsqueda
+Nueva imagen para las marcas de cómics leídos (sólo en OpenGL)
+Cambiada la distribución de algunos iconos
+Cambiado el modo de eliminar la metainformación (borrar base de datos/portadas de disco)
+Ocultadas las opciones avanzadas de configuración de YACReader Flow, accesibles ahora tras pulsar un botón (diálogos de configuración más simples)
+
+6.0.1 (No pública)
+Corregido bug al usar las teclas Inicio/fin
+Corregido bug que al arrancar YACReaderLibrary por primera vez causaba que no se mostrasen las portadas (sólo bajo ciertas circunstancias)
+Añadidos algunos atajos de teclado a YACReaderLibrary a los ya existentes
+
+6.0
+
+Mejorada la velocidad de inicio gracias al uso de /LTCG como opción de compilación
+Corregido bug relacionado con OpenGL que causaba consumo excesivo de CPU en tarjetas NVidia
+Añadidos iconos para cada tipo de archivo soportado en YACReaderLibrary
+Cambiado el icono "folder" en YACReaderLibrary
+Añadida barra para ajustar el ancho de página en la toolbar de YACReader
+Añadido widget para la information label
+Añadido nuevo estilo visual a goToFlow
+Añadidos filtros para controlar el brillo, el contraste y el valor gamma
+Añadidas notificaciones de portada y última página
+InformationLabel se muestra ahora en la esquina superiror derecha.
+InformationLabel se muestra en 3 tamaños diferentes en función de la resolución
+Corregido bug que causaba que las marcas de cómic leído no se dibujasen adecuadamente.
+Se recuerda si se debe mostrar o no la "label" información.
+Corregido bug que provocaba el fallo de YACReader al pasar muy rápido las páginas.
+Añadida columna "Tamaño" a la lista de cómics en YACReaderLibrary
+Añadida la ordinación "natural" de los comics que hay en directorio del cómic actual.
+Corregido bug que causaba que se abriese el cómic erroneo en YACReaderLibrary.
+Cambiado el modo en el que se cargan los lenguages, ahora se pueden añadir traducciones sin necesidad de recompilar.
\ No newline at end of file
diff --git a/COPYING.txt b/COPYING.txt
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/COPYING.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/INSTALL.txt b/INSTALL.txt
new file mode 100644
index 00000000..8bd13166
--- /dev/null
+++ b/INSTALL.txt
@@ -0,0 +1,89 @@
+COMPILATION GUIDE FOR LINUX/UNIX USERS
+**********************************
+YACReader and YACReaderLibrary are build using qmake. To build and install the program, run:
+
+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.
+
+For the headless version of YACReaderLibrary enter the YACReaderLibrary folder and run:
+
+qmake-qt5 YACReaderLibraryServer.pro
+
+This will select the build config for the headless version instead of the gui version. You can then continue the build as described above.
+
+
+Dependencies:
+-----------------------
+
+- Qt >= 5.3 with the following modules:
+ - declarative
+ - quickcontrols
+ - sql
+ - script
+ - multimedia
+ - imageformats
+ - opengl
+ - sql-sqlite
+ - network
+
+- poppler-qt5
+- qrencode
+- sqllite
+- glu
+- a decompression backend, either 7zip or unarr (see below)
+
+Please note that not all of these dependencies are needed at build time. A good example for this is YACReaderLibrary's GridView mode which will
+silently fail and only show a white page if the proper qml modules (declarative, quickcontrols) are not installed.
+
+
+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").
+
+For packaging purposes, you can use "make install INSTALL_ROOT=DIR" to install to a different location than the prefix.
+
+Default values:
+
+PREFIX=/usr
+INSTALL_ROOT=""
+
+On embedded devices that don't support desktop OpenGL, it is recommended to use the no_opengl config option:
+
+qmake CONFIG+=no_opengl
+
+This will remove any dependency on desktop OpenGL and hardlock YACReader's coverflow to software rendering. Please note that it
+doesn't actually remove OpenGL from the build, the Qt toolkit will still make use of it.
+
+
+DO YOU WANT TO HELP YACREADER?
+******************************
+If you have experience creating packages, please help to create a package for your favourite Linux distro! Send me an e-mail to: info@yacreader.com
diff --git a/QsLog/QsLog.cpp b/QsLog/QsLog.cpp
new file mode 100644
index 00000000..3114e4f6
--- /dev/null
+++ b/QsLog/QsLog.cpp
@@ -0,0 +1,249 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLog.h"
+#include "QsLogDest.h"
+#ifdef QS_LOG_SEPARATE_THREAD
+#include
+#include
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace QsLogging
+{
+typedef QVector DestinationList;
+
+static const char TraceString[] = "TRACE";
+static const char DebugString[] = "DEBUG";
+static const char InfoString[] = "INFO ";
+static const char WarnString[] = "WARN ";
+static const char ErrorString[] = "ERROR";
+static const char FatalString[] = "FATAL";
+
+// not using Qt::ISODate because we need the milliseconds too
+static const QString fmtDateTime("yyyy-MM-ddThh:mm:ss.zzz");
+
+static Logger* sInstance = 0;
+
+static const char* LevelToText(Level theLevel)
+{
+ switch (theLevel) {
+ case TraceLevel:
+ return TraceString;
+ case DebugLevel:
+ return DebugString;
+ case InfoLevel:
+ return InfoString;
+ case WarnLevel:
+ return WarnString;
+ case ErrorLevel:
+ return ErrorString;
+ case FatalLevel:
+ return FatalString;
+ case OffLevel:
+ return "";
+ default: {
+ assert(!"bad log level");
+ return InfoString;
+ }
+ }
+}
+
+#ifdef QS_LOG_SEPARATE_THREAD
+class LogWriterRunnable : public QRunnable
+{
+public:
+ LogWriterRunnable(QString message, Level level);
+ virtual void run();
+
+private:
+ QString mMessage;
+ Level mLevel;
+};
+#endif
+
+class LoggerImpl
+{
+public:
+ LoggerImpl();
+
+#ifdef QS_LOG_SEPARATE_THREAD
+ QThreadPool threadPool;
+#endif
+ QMutex logMutex;
+ Level level;
+ DestinationList destList;
+};
+
+#ifdef QS_LOG_SEPARATE_THREAD
+LogWriterRunnable::LogWriterRunnable(QString message, Level level)
+ : QRunnable()
+ , mMessage(message)
+ , mLevel(level)
+{
+}
+
+void LogWriterRunnable::run()
+{
+ Logger::instance().write(mMessage, mLevel);
+}
+#endif
+
+
+LoggerImpl::LoggerImpl()
+ : level(InfoLevel)
+{
+ // assume at least file + console
+ destList.reserve(2);
+#ifdef QS_LOG_SEPARATE_THREAD
+ threadPool.setMaxThreadCount(1);
+ threadPool.setExpiryTimeout(-1);
+#endif
+}
+
+
+Logger::Logger()
+ : d(new LoggerImpl)
+{
+}
+
+Logger& Logger::instance()
+{
+ if (!sInstance)
+ sInstance = new Logger;
+
+ return *sInstance;
+}
+
+void Logger::destroyInstance()
+{
+ delete sInstance;
+ sInstance = 0;
+}
+
+// tries to extract the level from a string log message. If available, conversionSucceeded will
+// contain the conversion result.
+Level Logger::levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded)
+{
+ if (conversionSucceeded)
+ *conversionSucceeded = true;
+
+ if (logMessage.startsWith(QLatin1String(TraceString)))
+ return TraceLevel;
+ if (logMessage.startsWith(QLatin1String(DebugString)))
+ return DebugLevel;
+ if (logMessage.startsWith(QLatin1String(InfoString)))
+ return InfoLevel;
+ if (logMessage.startsWith(QLatin1String(WarnString)))
+ return WarnLevel;
+ if (logMessage.startsWith(QLatin1String(ErrorString)))
+ return ErrorLevel;
+ if (logMessage.startsWith(QLatin1String(FatalString)))
+ return FatalLevel;
+
+ if (conversionSucceeded)
+ *conversionSucceeded = false;
+ return OffLevel;
+}
+
+Logger::~Logger()
+{
+#ifdef QS_LOG_SEPARATE_THREAD
+ d->threadPool.waitForDone();
+#endif
+ delete d;
+ d = 0;
+}
+
+void Logger::addDestination(DestinationPtr destination)
+{
+ assert(destination.data());
+ d->destList.push_back(destination);
+}
+
+void Logger::setLoggingLevel(Level newLevel)
+{
+ d->level = newLevel;
+}
+
+Level Logger::loggingLevel() const
+{
+ return d->level;
+}
+
+//! creates the complete log message and passes it to the logger
+void Logger::Helper::writeToLog()
+{
+ const char* const levelName = LevelToText(level);
+ const QString completeMessage(QString("%1 %2 %3")
+ .arg(levelName)
+ .arg(QDateTime::currentDateTime().toString(fmtDateTime))
+ .arg(buffer)
+ );
+
+ Logger::instance().enqueueWrite(completeMessage, level);
+}
+
+Logger::Helper::~Helper()
+{
+ try {
+ writeToLog();
+ }
+ catch(std::exception&) {
+ // you shouldn't throw exceptions from a sink
+ assert(!"exception in logger helper destructor");
+ //CHANGED throw;
+ }
+}
+
+//! directs the message to the task queue or writes it directly
+void Logger::enqueueWrite(const QString& message, Level level)
+{
+#ifdef QS_LOG_SEPARATE_THREAD
+ LogWriterRunnable *r = new LogWriterRunnable(message, level);
+ d->threadPool.start(r);
+#else
+ write(message, level);
+#endif
+}
+
+//! Sends the message to all the destinations. The level for this message is passed in case
+//! it's useful for processing in the destination.
+void Logger::write(const QString& message, Level level)
+{
+ QMutexLocker lock(&d->logMutex);
+ for (DestinationList::iterator it = d->destList.begin(),
+ endIt = d->destList.end();it != endIt;++it) {
+ (*it)->write(message, level);
+ }
+}
+
+} // end namespace
diff --git a/QsLog/QsLog.h b/QsLog/QsLog.h
new file mode 100644
index 00000000..f72e827c
--- /dev/null
+++ b/QsLog/QsLog.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef QSLOG_H
+#define QSLOG_H
+
+#include "QsLogLevel.h"
+#include "QsLogDest.h"
+#include
+#include
+
+#define QS_LOG_VERSION "2.0b3"
+
+namespace QsLogging
+{
+class Destination;
+class LoggerImpl; // d pointer
+
+class QSLOG_SHARED_OBJECT Logger
+{
+public:
+ static Logger& instance();
+ static void destroyInstance();
+ static Level levelFromLogMessage(const QString& logMessage, bool* conversionSucceeded = 0);
+
+ ~Logger();
+
+ //! Adds a log message destination. Don't add null destinations.
+ void addDestination(DestinationPtr destination);
+ //! Logging at a level < 'newLevel' will be ignored
+ void setLoggingLevel(Level newLevel);
+ //! The default level is INFO
+ Level loggingLevel() const;
+
+ //! The helper forwards the streaming to QDebug and builds the final
+ //! log message.
+ class QSLOG_SHARED_OBJECT Helper
+ {
+ public:
+ explicit Helper(Level logLevel) :
+ level(logLevel),
+ qtDebug(&buffer) {}
+ ~Helper();
+ QDebug& stream(){ return qtDebug; }
+
+ private:
+ void writeToLog();
+
+ Level level;
+ QString buffer;
+ QDebug qtDebug;
+ };
+
+private:
+ Logger();
+ Logger(const Logger&); // not available
+ Logger& operator=(const Logger&); // not available
+
+ void enqueueWrite(const QString& message, Level level);
+ void write(const QString& message, Level level);
+
+ LoggerImpl* d;
+
+ friend class LogWriterRunnable;
+};
+
+} // end namespace
+
+//! Logging macros: define QS_LOG_LINE_NUMBERS to get the file and line number
+//! in the log output.
+#ifndef QS_LOG_LINE_NUMBERS
+#define QLOG_TRACE() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::TraceLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::TraceLevel).stream()
+#define QLOG_DEBUG() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::DebugLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::DebugLevel).stream()
+#define QLOG_INFO() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::InfoLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::InfoLevel).stream()
+#define QLOG_WARN() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::WarnLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::WarnLevel).stream()
+#define QLOG_ERROR() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::ErrorLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream()
+#define QLOG_FATAL() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::FatalLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::FatalLevel).stream()
+#else
+#define QLOG_TRACE() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::TraceLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_DEBUG() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::DebugLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_INFO() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::InfoLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_WARN() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::WarnLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_ERROR() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::ErrorLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() << __FILE__ << '@' << __LINE__
+#define QLOG_FATAL() \
+ if (QsLogging::Logger::instance().loggingLevel() > QsLogging::FatalLevel) {} \
+ else QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() << __FILE__ << '@' << __LINE__
+#endif
+
+#ifdef QS_LOG_DISABLE
+#include "QsLogDisableForThisFile.h"
+#endif
+
+#endif // QSLOG_H
diff --git a/QsLog/QsLog.pri b/QsLog/QsLog.pri
new file mode 100644
index 00000000..4afc6b47
--- /dev/null
+++ b/QsLog/QsLog.pri
@@ -0,0 +1,22 @@
+INCLUDEPATH += $$PWD
+#DEFINES += QS_LOG_LINE_NUMBERS # automatically writes the file and line for each log message
+#DEFINES += QS_LOG_DISABLE # logging code is replaced with a no-op
+#DEFINES += QS_LOG_SEPARATE_THREAD # messages are queued and written from a separate thread
+SOURCES += $$PWD/QsLogDest.cpp \
+ $$PWD/QsLog.cpp \
+ $$PWD/QsLogDestConsole.cpp \
+ $$PWD/QsLogDestFile.cpp \
+ $$PWD/QsLogDestFunctor.cpp
+
+HEADERS += $$PWD/QSLogDest.h \
+ $$PWD/QsLog.h \
+ $$PWD/QsLogDestConsole.h \
+ $$PWD/QsLogLevel.h \
+ $$PWD/QsLogDestFile.h \
+ $$PWD/QsLogDisableForThisFile.h \
+ $$PWD/QsLogDestFunctor.h
+
+OTHER_FILES += \
+ $$PWD/QsLogChanges.txt \
+ $$PWD/QsLogReadme.txt \
+ $$PWD/LICENSE.txt
diff --git a/QsLog/QsLogDest.cpp b/QsLog/QsLogDest.cpp
new file mode 100644
index 00000000..ae9f44bc
--- /dev/null
+++ b/QsLog/QsLogDest.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLogDest.h"
+#include "QsLogDestConsole.h"
+#include "QsLogDestFile.h"
+#include "QsLogDestFunctor.h"
+#include
+
+namespace QsLogging
+{
+
+Destination::~Destination()
+{
+}
+
+//! destination factory
+DestinationPtr DestinationFactory::MakeFileDestination(const QString& filePath,
+ LogRotationOption rotation, const MaxSizeBytes &sizeInBytesToRotateAfter,
+ const MaxOldLogCount &oldLogsToKeep)
+{
+ if (EnableLogRotation == rotation) {
+ QScopedPointer logRotation(new SizeRotationStrategy);
+ logRotation->setMaximumSizeInBytes(sizeInBytesToRotateAfter.size);
+ logRotation->setBackupCount(oldLogsToKeep.count);
+
+ return DestinationPtr(new FileDestination(filePath, RotationStrategyPtr(logRotation.take())));
+ }
+
+ return DestinationPtr(new FileDestination(filePath, RotationStrategyPtr(new NullRotationStrategy)));
+}
+
+DestinationPtr DestinationFactory::MakeDebugOutputDestination()
+{
+ return DestinationPtr(new DebugOutputDestination);
+}
+
+DestinationPtr DestinationFactory::MakeFunctorDestination(QsLogging::Destination::LogFunction f)
+{
+ return DestinationPtr(new FunctorDestination(f));
+}
+
+DestinationPtr DestinationFactory::MakeFunctorDestination(QObject *receiver, const char *member)
+{
+ return DestinationPtr(new FunctorDestination(receiver, member));
+}
+
+} // end namespace
diff --git a/QsLog/QsLogDest.h b/QsLog/QsLogDest.h
new file mode 100644
index 00000000..a404487b
--- /dev/null
+++ b/QsLog/QsLogDest.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef QSLOGDEST_H
+#define QSLOGDEST_H
+
+#include "QsLogLevel.h"
+#include
+#include
+class QString;
+class QObject;
+
+#ifdef QSLOG_IS_SHARED_LIBRARY
+#define QSLOG_SHARED_OBJECT Q_DECL_EXPORT
+#elif QSLOG_IS_SHARED_LIBRARY_IMPORT
+#define QSLOG_SHARED_OBJECT Q_DECL_IMPORT
+#else
+#define QSLOG_SHARED_OBJECT
+#endif
+
+namespace QsLogging
+{
+
+class QSLOG_SHARED_OBJECT Destination
+{
+public:
+ typedef void (*LogFunction)(const QString &message, Level level);
+
+public:
+ virtual ~Destination();
+ virtual void write(const QString& message, Level level) = 0;
+ virtual bool isValid() = 0; // returns whether the destination was created correctly
+};
+typedef QSharedPointer DestinationPtr;
+
+
+// a series of "named" paramaters, to make the file destination creation more readable
+enum LogRotationOption
+{
+ DisableLogRotation = 0,
+ EnableLogRotation = 1
+};
+
+struct QSLOG_SHARED_OBJECT MaxSizeBytes
+{
+ MaxSizeBytes() : size(0) {}
+ explicit MaxSizeBytes(qint64 size_) : size(size_) {}
+ qint64 size;
+};
+
+struct QSLOG_SHARED_OBJECT MaxOldLogCount
+{
+ MaxOldLogCount() : count(0) {}
+ explicit MaxOldLogCount(int count_) : count(count_) {}
+ int count;
+};
+
+
+//! Creates logging destinations/sinks. The caller shares ownership of the destinations with the logger.
+//! After being added to a logger, the caller can discard the pointers.
+class QSLOG_SHARED_OBJECT DestinationFactory
+{
+public:
+ static DestinationPtr MakeFileDestination(const QString& filePath,
+ LogRotationOption rotation = DisableLogRotation,
+ const MaxSizeBytes &sizeInBytesToRotateAfter = MaxSizeBytes(),
+ const MaxOldLogCount &oldLogsToKeep = MaxOldLogCount());
+ static DestinationPtr MakeDebugOutputDestination();
+ // takes a pointer to a function
+ static DestinationPtr MakeFunctorDestination(Destination::LogFunction f);
+ // takes a QObject + signal/slot
+ static DestinationPtr MakeFunctorDestination(QObject *receiver, const char *member);
+};
+
+} // end namespace
+
+#endif // QSLOGDEST_H
diff --git a/QsLog/QsLogDestConsole.cpp b/QsLog/QsLogDestConsole.cpp
new file mode 100644
index 00000000..ed7fc53b
--- /dev/null
+++ b/QsLog/QsLogDestConsole.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLogDestConsole.h"
+#include
+#include
+
+#if defined(Q_OS_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include
+void QsDebugOutput::output( const QString& message )
+{
+ OutputDebugStringW(reinterpret_cast(message.utf16()));
+ OutputDebugStringW(L"\n");
+}
+#elif defined(Q_OS_UNIX)
+#include
+void QsDebugOutput::output( const QString& message )
+{
+ fprintf(stderr, "%s\n", qPrintable(message));
+ fflush(stderr);
+}
+#endif
+
+void QsLogging::DebugOutputDestination::write(const QString& message, Level)
+{
+ QsDebugOutput::output(message);
+}
+
+bool QsLogging::DebugOutputDestination::isValid()
+{
+ return true;
+}
diff --git a/QsLog/QsLogDestConsole.h b/QsLog/QsLogDestConsole.h
new file mode 100644
index 00000000..f80f490e
--- /dev/null
+++ b/QsLog/QsLogDestConsole.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef QSLOGDESTCONSOLE_H
+#define QSLOGDESTCONSOLE_H
+
+#include "QsLogDest.h"
+
+class QString;
+
+class QsDebugOutput
+{
+public:
+ static void output(const QString& a_message);
+};
+
+namespace QsLogging
+{
+
+// debugger sink
+class DebugOutputDestination : public Destination
+{
+public:
+ virtual void write(const QString& message, Level level);
+ virtual bool isValid();
+};
+
+}
+
+#endif // QSLOGDESTCONSOLE_H
diff --git a/QsLog/QsLogDestFile.cpp b/QsLog/QsLogDestFile.cpp
new file mode 100644
index 00000000..0f8f8048
--- /dev/null
+++ b/QsLog/QsLogDestFile.cpp
@@ -0,0 +1,155 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLogDestFile.h"
+#include
+#include
+#include
+#include
+
+const int QsLogging::SizeRotationStrategy::MaxBackupCount = 10;
+
+QsLogging::RotationStrategy::~RotationStrategy()
+{
+}
+
+QsLogging::SizeRotationStrategy::SizeRotationStrategy()
+ : mCurrentSizeInBytes(0)
+ , mMaxSizeInBytes(0)
+ , mBackupsCount(0)
+{
+}
+
+void QsLogging::SizeRotationStrategy::setInitialInfo(const QFile &file)
+{
+ mFileName = file.fileName();
+ mCurrentSizeInBytes = file.size();
+}
+
+void QsLogging::SizeRotationStrategy::includeMessageInCalculation(const QString &message)
+{
+ mCurrentSizeInBytes += message.toUtf8().size();
+}
+
+bool QsLogging::SizeRotationStrategy::shouldRotate()
+{
+ return mCurrentSizeInBytes > mMaxSizeInBytes;
+}
+
+// Algorithm assumes backups will be named filename.X, where 1 <= X <= mBackupsCount.
+// All X's will be shifted up.
+void QsLogging::SizeRotationStrategy::rotate()
+{
+ if (!mBackupsCount) {
+ if (!QFile::remove(mFileName))
+ std::cerr << "QsLog: backup delete failed " << qPrintable(mFileName);
+ return;
+ }
+
+ // 1. find the last existing backup than can be shifted up
+ const QString logNamePattern = mFileName + QString::fromUtf8(".%1");
+ int lastExistingBackupIndex = 0;
+ for (int i = 1;i <= mBackupsCount;++i) {
+ const QString backupFileName = logNamePattern.arg(i);
+ if (QFile::exists(backupFileName))
+ lastExistingBackupIndex = qMin(i, mBackupsCount - 1);
+ else
+ break;
+ }
+
+ // 2. shift up
+ for (int i = lastExistingBackupIndex;i >= 1;--i) {
+ const QString oldName = logNamePattern.arg(i);
+ const QString newName = logNamePattern.arg(i + 1);
+ QFile::remove(newName);
+ const bool renamed = QFile::rename(oldName, newName);
+ if (!renamed) {
+ std::cerr << "QsLog: could not rename backup " << qPrintable(oldName)
+ << " to " << qPrintable(newName);
+ }
+ }
+
+ // 3. rename current log file
+ const QString newName = logNamePattern.arg(1);
+ if (QFile::exists(newName))
+ QFile::remove(newName);
+ if (!QFile::rename(mFileName, newName)) {
+ std::cerr << "QsLog: could not rename log " << qPrintable(mFileName)
+ << " to " << qPrintable(newName);
+ }
+}
+
+QIODevice::OpenMode QsLogging::SizeRotationStrategy::recommendedOpenModeFlag()
+{
+ return QIODevice::Append;
+}
+
+void QsLogging::SizeRotationStrategy::setMaximumSizeInBytes(qint64 size)
+{
+ Q_ASSERT(size >= 0);
+ mMaxSizeInBytes = size;
+}
+
+void QsLogging::SizeRotationStrategy::setBackupCount(int backups)
+{
+ Q_ASSERT(backups >= 0);
+ mBackupsCount = qMin(backups, SizeRotationStrategy::MaxBackupCount);
+}
+
+
+QsLogging::FileDestination::FileDestination(const QString& filePath, RotationStrategyPtr rotationStrategy)
+ : mRotationStrategy(rotationStrategy)
+{
+ mFile.setFileName(filePath);
+ if (!mFile.open(QFile::WriteOnly | QFile::Text | mRotationStrategy->recommendedOpenModeFlag()))
+ std::cerr << "QsLog: could not open log file " << qPrintable(filePath);
+ mOutputStream.setDevice(&mFile);
+ mOutputStream.setCodec(QTextCodec::codecForName("UTF-8"));
+
+ mRotationStrategy->setInitialInfo(mFile);
+}
+
+void QsLogging::FileDestination::write(const QString& message, Level)
+{
+ mRotationStrategy->includeMessageInCalculation(message);
+ if (mRotationStrategy->shouldRotate()) {
+ mOutputStream.setDevice(NULL);
+ mFile.close();
+ mRotationStrategy->rotate();
+ if (!mFile.open(QFile::WriteOnly | QFile::Text | mRotationStrategy->recommendedOpenModeFlag()))
+ std::cerr << "QsLog: could not reopen log file " << qPrintable(mFile.fileName());
+ mRotationStrategy->setInitialInfo(mFile);
+ mOutputStream.setDevice(&mFile);
+ }
+
+ mOutputStream << message << endl;
+ mOutputStream.flush();
+}
+
+bool QsLogging::FileDestination::isValid()
+{
+ return mFile.isOpen();
+}
+
diff --git a/QsLog/QsLogDestFile.h b/QsLog/QsLogDestFile.h
new file mode 100644
index 00000000..ee7b5232
--- /dev/null
+++ b/QsLog/QsLogDestFile.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef QSLOGDESTFILE_H
+#define QSLOGDESTFILE_H
+
+#include "QsLogDest.h"
+#include
+#include
+#include
+#include
+
+namespace QsLogging
+{
+class RotationStrategy
+{
+public:
+ virtual ~RotationStrategy();
+
+ virtual void setInitialInfo(const QFile &file) = 0;
+ virtual void includeMessageInCalculation(const QString &message) = 0;
+ virtual bool shouldRotate() = 0;
+ virtual void rotate() = 0;
+ virtual QIODevice::OpenMode recommendedOpenModeFlag() = 0;
+};
+
+// Never rotates file, overwrites existing file.
+class NullRotationStrategy : public RotationStrategy
+{
+public:
+ virtual void setInitialInfo(const QFile &) {}
+ virtual void includeMessageInCalculation(const QString &) {}
+ virtual bool shouldRotate() { return false; }
+ virtual void rotate() {}
+ virtual QIODevice::OpenMode recommendedOpenModeFlag() { return QIODevice::Truncate; }
+};
+
+// Rotates after a size is reached, keeps a number of <= 10 backups, appends to existing file.
+class SizeRotationStrategy : public RotationStrategy
+{
+public:
+ SizeRotationStrategy();
+ static const int MaxBackupCount;
+
+ virtual void setInitialInfo(const QFile &file);
+ virtual void includeMessageInCalculation(const QString &message);
+ virtual bool shouldRotate();
+ virtual void rotate();
+ virtual QIODevice::OpenMode recommendedOpenModeFlag();
+
+ void setMaximumSizeInBytes(qint64 size);
+ void setBackupCount(int backups);
+
+private:
+ QString mFileName;
+ qint64 mCurrentSizeInBytes;
+ qint64 mMaxSizeInBytes;
+ int mBackupsCount;
+};
+
+typedef QSharedPointer RotationStrategyPtr;
+
+// file message sink
+class FileDestination : public Destination
+{
+public:
+ FileDestination(const QString& filePath, RotationStrategyPtr rotationStrategy);
+ virtual void write(const QString& message, Level level);
+ virtual bool isValid();
+
+private:
+ QFile mFile;
+ QTextStream mOutputStream;
+ QSharedPointer mRotationStrategy;
+};
+
+}
+
+#endif // QSLOGDESTFILE_H
diff --git a/QsLog/QsLogDestFunctor.cpp b/QsLog/QsLogDestFunctor.cpp
new file mode 100644
index 00000000..601139d9
--- /dev/null
+++ b/QsLog/QsLogDestFunctor.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) 2014, Razvan Petru
+// Copyright (c) 2014, Omar Carey
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "QsLogDestFunctor.h"
+#include
+#include
+
+QsLogging::FunctorDestination::FunctorDestination(LogFunction f)
+ : QObject(NULL)
+ , mLogFunction(f)
+{
+}
+
+QsLogging::FunctorDestination::FunctorDestination(QObject *receiver, const char *member)
+ : QObject(NULL)
+ , mLogFunction(NULL)
+{
+ connect(this, SIGNAL(logMessageReady(QString,int)), receiver, member, Qt::QueuedConnection);
+}
+
+
+void QsLogging::FunctorDestination::write(const QString &message, QsLogging::Level level)
+{
+ if (mLogFunction)
+ mLogFunction(message, level);
+
+ if (level > QsLogging::TraceLevel)
+ emit logMessageReady(message, static_cast(level));
+}
+
+bool QsLogging::FunctorDestination::isValid()
+{
+ return true;
+}
diff --git a/QsLog/QsLogDestFunctor.h b/QsLog/QsLogDestFunctor.h
new file mode 100644
index 00000000..e34631f0
--- /dev/null
+++ b/QsLog/QsLogDestFunctor.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2014, Razvan Petru
+// Copyright (c) 2014, Omar Carey
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef QSLOGDESTFUNCTOR_H
+#define QSLOGDESTFUNCTOR_H
+
+#include "QsLogDest.h"
+#include
+
+namespace QsLogging
+{
+// Offers various types of function-like sinks.
+// This is an advanced destination type. Depending on your configuration, LogFunction might be
+// called from a different thread or even a different binary. You should not access QsLog from
+// inside LogFunction and should not perform any time-consuming operations.
+// logMessageReady is connected through a queued connection and trace messages are not included
+class FunctorDestination : public QObject, public Destination
+{
+ Q_OBJECT
+public:
+ explicit FunctorDestination(LogFunction f);
+ FunctorDestination(QObject *receiver, const char *member);
+
+ virtual void write(const QString &message, Level level);
+ virtual bool isValid();
+
+protected:
+ // int used to avoid registering a new enum type
+ Q_SIGNAL void logMessageReady(const QString &message, int level);
+
+private:
+ LogFunction mLogFunction;
+};
+}
+
+#endif // QSLOGDESTFUNCTOR_H
diff --git a/QsLog/QsLogDisableForThisFile.h b/QsLog/QsLogDisableForThisFile.h
new file mode 100644
index 00000000..c70af103
--- /dev/null
+++ b/QsLog/QsLogDisableForThisFile.h
@@ -0,0 +1,22 @@
+#ifndef QSLOGDISABLEFORTHISFILE_H
+#define QSLOGDISABLEFORTHISFILE_H
+
+#include
+// When included AFTER QsLog.h, this file will disable logging in that C++ file. When included
+// before, it will lead to compiler warnings or errors about macro redefinitions.
+
+#undef QLOG_TRACE
+#undef QLOG_DEBUG
+#undef QLOG_INFO
+#undef QLOG_WARN
+#undef QLOG_ERROR
+#undef QLOG_FATAL
+
+#define QLOG_TRACE() if (1) {} else qDebug()
+#define QLOG_DEBUG() if (1) {} else qDebug()
+#define QLOG_INFO() if (1) {} else qDebug()
+#define QLOG_WARN() if (1) {} else qDebug()
+#define QLOG_ERROR() if (1) {} else qDebug()
+#define QLOG_FATAL() if (1) {} else qDebug()
+
+#endif // QSLOGDISABLEFORTHISFILE_H
diff --git a/QsLog/QsLogLevel.h b/QsLog/QsLogLevel.h
new file mode 100644
index 00000000..62984732
--- /dev/null
+++ b/QsLog/QsLogLevel.h
@@ -0,0 +1,45 @@
+// Copyright (c) 2013, Razvan Petru
+// All rights reserved.
+
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice, this
+// list of conditions and the following disclaimer in the documentation and/or other
+// materials provided with the distribution.
+// * The name of the contributors may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+// OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef QSLOGLEVEL_H
+#define QSLOGLEVEL_H
+
+namespace QsLogging
+{
+
+enum Level
+{
+ TraceLevel = 0,
+ DebugLevel,
+ InfoLevel,
+ WarnLevel,
+ ErrorLevel,
+ FatalLevel,
+ OffLevel
+};
+
+}
+
+#endif // QSLOGLEVEL_H
diff --git a/QsLog/QsLogSharedLibrary.pro b/QsLog/QsLogSharedLibrary.pro
new file mode 100644
index 00000000..51320684
--- /dev/null
+++ b/QsLog/QsLogSharedLibrary.pro
@@ -0,0 +1,35 @@
+# This pro file will build QsLog as a shared library
+include(QsLog.pri)
+
+TARGET = QsLog
+VERSION = "2.0.0"
+QT -= gui
+CONFIG -= console
+CONFIG -= app_bundle
+CONFIG += shared
+TEMPLATE = lib
+
+DESTDIR = $$PWD/build-QsLogShared
+OBJECTS_DIR = $$DESTDIR/obj
+MOC_DIR = $$DESTDIR/moc
+
+win32 {
+ DEFINES += QSLOG_IS_SHARED_LIBRARY
+}
+
+unix:!macx {
+ # make install will install the shared object in the appropriate folders
+ headers.files = QsLog.h QsLogDest.h QsLogLevel.h
+ headers.path = /usr/include/$(QMAKE_TARGET)
+
+ other_files.files = *.txt
+ other_files.path = /usr/local/share/$(QMAKE_TARGET)
+
+ contains(QT_ARCH, x86_64) {
+ target.path = /usr/lib64
+ } else {
+ target.path = /usr/lib
+ }
+
+ INSTALLS += headers target other_files
+}
diff --git a/README.txt b/README.txt
new file mode 100644
index 00000000..b9d66e4a
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,22 @@
+LICENSE
+*******
+This software has been developed by Luis Ángel San Martín Rodríguez (luisangelsm@gmail.com) under GPL v3 license
+(for more details read COPYING.txt).
+
+CONTACT
+*******
+Project home page : www.yacreader.com
+e-mail:
+ info@yacreader.com
+ support@yacreader.com
+Social:
+ Facebook - http://www.facebook.com/YACReader
+ Twitter - https://twitter.com/yacreader
+ YouTube - https://www.youtube.com/user/yacreader
+
+If you need help or have any suggestion, please, send me an e-mail.
+
+DONATIONS
+*********
+YACReader is free but it needs money to still be alive, so please,
+if you like YACReader, visit the home page and make a donation.
\ No newline at end of file
diff --git a/YACReader.1 b/YACReader.1
new file mode 100644
index 00000000..4f3d7987
--- /dev/null
+++ b/YACReader.1
@@ -0,0 +1,50 @@
+.\" Manpage for YACReader.
+.\" Contact yoann.gauthier9@gmail.com to correct errors or typos.
+.TH man 1 "28 September 2014" "2.0" "YACReader man page"
+.SH NAME
+YACReader \- launch YACReader application.
+.SH SYNOPSIS
+YACReader [\fBFile\fR | \fBDirectory\fR]
+.br
+YACReader [\fB\-h\fR | \fB\-\-help\fR]
+.br
+YACReader [\fB\-v\fR | \fB\-\-version\fR]
+.SH DESCRIPTION
+YACReader is a free cross-platform comic reader with support for multiple comic files and image formats.
+.SH OPTIONS
+.TP
+.BR File
+Open comic file.
+.TP
+.BR Directory
+Open comic directory.
+.TP
+.BR \-h, \-\- help
+Display this text and exit.
+.TP
+.BR \-v, \-\- version
+Display version information and exit.
+.SH FEATURES
+- rar, zip, cbr, cbz, tar and pdf comics support with compatibility for jpeg, gif, png, tiff, webp and bmp images.
+.TP
+- Fast and easy to use.
+.TP
+- Use your keyboard or mouse for easy navigation.
+.TP
+- Fully customizable magnifying glass.
+.TP
+- Image rotation, double page mode, full size view, fullscreen mode, customizable background color and more.
+.TP
+- Bookmarks and resume reading.
+.TP
+- Find any page easily and quickly with "go to flow", a customizable eye candy effect for browsing comic pages.
+.SH CONTACTS
+To report bug or contact developpers, send a mail to :
+.RS 3
+.TP
+\fBinfo@yacreader.com\fR : for general information or suggestions about YACReader.
+.TP
+\fBsupport@yacreader.com\fR : for problems with YACReader or bugs detected.
+.RE
+.SH AUTHOR
+Luis Ãngel San MartÃn RodrÃguez (luisangelsm@gmail.com)
\ No newline at end of file
diff --git a/YACReader.desktop b/YACReader.desktop
new file mode 100644
index 00000000..32d5caf3
--- /dev/null
+++ b/YACReader.desktop
@@ -0,0 +1,13 @@
+[Desktop Entry]
+Name=YACReader
+GenericName=Yet Another Comic Reader
+Comment=A comic reader with support for .cb*, .pdf and whole directories.
+Exec=YACReader %f
+Icon=/usr/share/yacreader/icon.png
+Terminal=false
+Type=Application
+StartupNotify=true
+Categories=Graphics;Viewer;
+MimeType=application/x-cbz;application/x-cbr;application/x-cbt;application/x-cb7;application/x-pdf;application/x-zip;application/x-rar;application/x-7z;inode/directory;
+Keywords=comic;viewer;pdf;reader;
+X-Desktop-File-Install-Version=0.22
diff --git a/YACReader.pro b/YACReader.pro
new file mode 100644
index 00000000..528d3c41
--- /dev/null
+++ b/YACReader.pro
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+SUBDIRS = YACReader YACReaderLibrary
+YACReaderLibrary.depends = YACReader
\ No newline at end of file
diff --git a/YACReader/YACReader.icns b/YACReader/YACReader.icns
new file mode 100644
index 00000000..9943539f
Binary files /dev/null and b/YACReader/YACReader.icns differ
diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro
new file mode 100644
index 00000000..57a500de
--- /dev/null
+++ b/YACReader/YACReader.pro
@@ -0,0 +1,272 @@
+# #####################################################################
+# Automatically generated by qmake (2.01a) mié 8. oct 20:54:05 2008
+# #####################################################################
+TEMPLATE = app
+TARGET = YACReader
+DEPENDPATH += . \
+ release
+
+DEFINES += NOMINMAX YACREADER
+QMAKE_MAC_SDK = macosx10.11
+QMAKE_MAC_SDK = macosx10.11
+
+#load default build flags
+include (../config.pri)
+
+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
+}
+
+SOURCES += main.cpp
+
+INCLUDEPATH += ../common \
+ ../custom_widgets
+
+!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 {
+ 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
+
+ QMAKE_CXXFLAGS_RELEASE += /MP /Ob2 /Oi /Ot /GT /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 += ../common/pdf_comic.mm
+HEADERS += ../common/pdf_comic.h
+}
+
+QT += network widgets core
+!CONFIG(no_opengl) {
+ QT += opengl
+}
+
+#CONFIG += release
+CONFIG -= flat
+
+QT += multimedia
+
+# Input
+HEADERS += ../common/comic.h \
+ configuration.h \
+ goto_dialog.h \
+ magnifying_glass.h \
+ main_window_viewer.h \
+ viewer.h \
+ goto_flow.h \
+ options_dialog.h \
+ ../common/bookmarks.h \
+ bookmarks_dialog.h \
+ render.h \
+ shortcuts_dialog.h \
+ translator.h \
+ goto_flow_widget.h \
+ page_label_widget.h \
+ goto_flow_toolbar.h \
+ width_slider.h \
+ notifications_label_widget.h \
+ ../common/pictureflow.h \
+ ../common/custom_widgets.h \
+ ../common/check_new_version.h \
+ ../common/qnaturalsorting.h \
+ ../common/yacreader_global.h \
+ ../common/yacreader_global_gui.h \
+ ../common/onstart_flow_selection_dialog.h \
+ ../common/comic_db.h \
+ ../common/folder.h \
+ ../common/library_item.h \
+ yacreader_local_client.h \
+ ../common/http_worker.h \
+ ../common/exit_check.h \
+ ../common/scroll_management.h \
+ ../common/opengl_checker.h
+
+!CONFIG(no_opengl) {
+ CONFIG(legacy_gl_widget) {
+ message("using legacy YACReaderFlowGL (QGLWidget) header")
+ DEFINES += YACREADER_LEGACY_FLOW_GL
+ HEADERS += ../common/gl_legacy/yacreader_flow_gl.h
+ } else {
+ HEADERS += ../common/gl/yacreader_flow_gl.h
+ }
+ HEADERS += goto_flow_gl.h
+}
+
+SOURCES += ../common/comic.cpp \
+ configuration.cpp \
+ goto_dialog.cpp \
+ magnifying_glass.cpp \
+ main_window_viewer.cpp \
+ viewer.cpp \
+ goto_flow.cpp \
+ options_dialog.cpp \
+ ../common/bookmarks.cpp \
+ bookmarks_dialog.cpp \
+ render.cpp \
+ shortcuts_dialog.cpp \
+ translator.cpp \
+ goto_flow_widget.cpp \
+ page_label_widget.cpp \
+ goto_flow_toolbar.cpp \
+ width_slider.cpp \
+ notifications_label_widget.cpp \
+ ../common/pictureflow.cpp \
+ ../common/custom_widgets.cpp \
+ ../common/check_new_version.cpp \
+ ../common/qnaturalsorting.cpp \
+ ../common/onstart_flow_selection_dialog.cpp \
+ ../common/comic_db.cpp \
+ ../common/folder.cpp \
+ ../common/library_item.cpp \
+ yacreader_local_client.cpp \
+ ../common/http_worker.cpp \
+ ../common/yacreader_global.cpp \
+ ../common/yacreader_global_gui.cpp \
+ ../common/exit_check.cpp \
+ ../common/scroll_management.cpp \
+ ../common/opengl_checker.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 += goto_flow_gl.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 \
+ yacreader_files.qrc
+
+win32:RESOURCES += yacreader_images_win.qrc
+unix:!macx:RESOURCES += yacreader_images_win.qrc
+macx:RESOURCES += yacreader_images_osx.qrc
+
+
+include(../QsLog/QsLog.pri)
+
+RC_FILE = icon.rc
+
+macx {
+ ICON = YACReader.icns
+}
+
+TRANSLATIONS = yacreader_es.ts \
+ yacreader_fr.ts \
+ yacreader_ru.ts \
+ yacreader_pt.ts \
+ yacreader_nl.ts \
+ yacreader_tr.ts \
+ yacreader_de.ts \
+ yacreader_source.ts
+
+unix:!macx {
+#set install prefix if it's empty
+isEmpty(PREFIX) {
+ PREFIX = /usr
+}
+
+BINDIR = $$PREFIX/bin
+LIBDIR = $$PREFIX/lib
+DATADIR = $$PREFIX/share
+
+DEFINES += "LIBDIR=\\\"$$LIBDIR\\\"" "DATADIR=\\\"$$DATADIR\\\""
+
+#MAKE INSTALL
+
+INSTALLS += bin docs icon desktop translation manpage
+
+bin.path = $$BINDIR
+isEmpty(DESTDIR) {
+ bin.files = YACReader
+} else {
+ bin.files = $$DESTDIR/YACReader
+}
+
+docs.path = $$DATADIR/doc/yacreader
+
+#rename docs for better packageability
+docs.extra = cp ../CHANGELOG.txt ../changelog; cp ../README.txt ../README
+docs.files = ../README ../changelog
+
+icon.path = $$DATADIR/yacreader
+icon.files = ../images/icon.png
+
+desktop.path = $$DATADIR/applications
+desktop.extra = desktop-file-edit --set-icon=$$DATADIR/yacreader/icon.png ../YACReader.desktop
+desktop.files = ../YACReader.desktop
+
+#TODO: icons should be located at /usr/share/icons and have the same basename as their application
+
+translation.path = $$DATADIR/yacreader/languages
+translation.files = ../release/languages/yacreader_*
+
+manpage.path = $$DATADIR/man/man1
+manpage.files = ../YACReader.1
+
+#remove leftover doc files when 'make clean' is invoked
+QMAKE_CLEAN += "../changelog" "../README"
+}
diff --git a/YACReader/bookmarks_dialog.cpp b/YACReader/bookmarks_dialog.cpp
new file mode 100644
index 00000000..07df7222
--- /dev/null
+++ b/YACReader/bookmarks_dialog.cpp
@@ -0,0 +1,197 @@
+#include "bookmarks_dialog.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "bookmarks.h"
+
+BookmarksDialog::BookmarksDialog(QWidget * parent)
+ :QDialog(parent)
+{
+ setModal(true);
+
+ //animation = new QPropertyAnimation(this,"windowOpacity");
+ //animation->setDuration(150);
+
+ QHBoxLayout * layout = new QHBoxLayout();
+
+ //bookmarks
+ QGridLayout * bookmarksL = new QGridLayout();
+
+ pages.push_back(new QLabel(tr("Lastest Page")));
+ for(int i=0;i<3;i++)
+ pages.push_back(new QLabel("-"));
+
+ QString labelsStyle = "QLabel {color:white;}";
+
+ foreach(QLabel * label,pages)
+ {
+ label->setStyleSheet(labelsStyle);
+ }
+
+ int heightDesktopResolution = QApplication::desktop()->screenGeometry().height();
+ int height,width;
+ height = heightDesktopResolution*0.50;
+ width = height*0.65;
+
+ coverSize = QSize(width,height);
+
+ for(int i=0;i<4;i++)
+ {
+ QLabel * l = new QLabel();
+ l->setFixedSize(coverSize);
+ l->setScaledContents(false);
+ //l->setPixmap(QPixmap(":/images/notCover.png"));
+ l->installEventFilter(this);
+ images.push_back(l);
+ }
+
+ for(int i=0;i<3;i++)
+ bookmarksL->addWidget(pages.at(i+1),0,i,Qt::AlignCenter);
+
+ for(int i=0;i<3;i++)
+ bookmarksL->addWidget(images.at(i+1),1,i,Qt::AlignCenter);
+
+
+ //last page
+ QGridLayout * lp = new QGridLayout();
+ lp->addWidget(pages.at(0),0,0,Qt::AlignCenter);
+ lp->addWidget(images.at(0),1,0,Qt::AlignCenter);
+
+ layout->addLayout(bookmarksL);
+ QFrame *f = new QFrame( this );
+ f->setFrameStyle( QFrame::VLine | QFrame::Sunken );
+ layout->addWidget(f);
+ layout->addLayout(lp);
+
+ QHBoxLayout * buttons = new QHBoxLayout();
+
+ cancel = new QPushButton(tr("Close"));
+ cancel->setFlat(true);
+ connect(cancel,SIGNAL(clicked()),this,SLOT(hide()));
+ buttons->addStretch();
+ buttons->addWidget(cancel);
+
+ cancel->setStyleSheet("QPushButton {border: 1px solid #242424; background: #2e2e2e; color:white; padding: 5px 26px 5px 26px; font-size:12px;font-family:Arial; font-weight:bold;}");
+
+ QVBoxLayout * l = new QVBoxLayout();
+
+ l->addWidget(new QLabel(""+tr("Click on any image to go to the bookmark")+""),0,Qt::AlignCenter);
+ l->addLayout(layout);
+#ifdef Q_OS_MAC
+ l->addLayout(buttons);
+#endif
+
+ QPalette Pal(palette());
+ // set black background
+ Pal.setColor(QPalette::Background, QColor("#454545"));
+ this->setAutoFillBackground(true);
+ this->setPalette(Pal);
+
+ setLayout(l);
+}
+
+void BookmarksDialog::setBookmarks(const Bookmarks & bm)
+{
+ lastPage = bm.getLastPage();
+ if (lastPage > 0)
+ {
+ QPixmap p = QPixmap::fromImage(bm.getLastPagePixmap());
+ if(p.isNull())
+ {
+ images.at(0)->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
+ images.at(0)->setText(tr("Loading..."));
+ }
+ else
+ {
+ images.at(0)->setAlignment(Qt::AlignHCenter|Qt::AlignBottom);
+ images.at(0)->setPixmap(p.scaled(coverSize,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ }
+ }
+ else
+ {
+ images.at(0)->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
+ images.at(0)->setPixmap(QPixmap(":/images/notCover.png").scaled(coverSize,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+
+ }
+
+ QList l = bm.getBookmarkPages();
+ int s = l.count();
+ for(int i=0;isetText(QString::number(l.at(i)+1));
+ QPixmap p = QPixmap::fromImage(bm.getBookmarkPixmap(l.at(i)));
+ if(p.isNull())
+ {
+ images.at(i+1)->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
+ images.at(i+1)->setText(tr("Loading..."));
+ }
+ else
+ {
+ images.at(i+1)->setAlignment(Qt::AlignHCenter|Qt::AlignBottom);
+ images.at(i+1)->setPixmap(p.scaled(coverSize,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ }
+ }
+ for(int i=s;i<3;i++)
+ {
+ pages.at(i+1)->setText("-");
+ images.at(i+1)->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
+ images.at(i+1)->setPixmap(QPixmap(":/images/notCover.png").scaled(coverSize,Qt::KeepAspectRatio,Qt::SmoothTransformation));
+ }
+}
+
+bool BookmarksDialog::eventFilter(QObject *obj, QEvent *event)
+{
+ if(event->type() == QEvent::MouseButtonPress)
+ {
+ if (obj == images.at(0))
+ {
+ emit(goToPage(lastPage));
+ close();
+ event->accept();
+ }
+ for(int i=1;i<=3;i++)
+ {
+ if(obj == images.at(i))
+ {
+ bool b;
+ int page = pages.at(i)->text().toInt(&b)-1;
+ if(b)
+ {
+ emit(goToPage(page));
+ close();
+ }
+ event->accept();
+ }
+ }
+ }
+ // pass the event on to the parent class
+ return QDialog::eventFilter(obj, event);
+}
+
+void BookmarksDialog::keyPressEvent(QKeyEvent * event)
+{
+ if(event->key() == Qt::Key_M)
+ hide();
+}
+/*
+void BookmarksDialog::show()
+{
+ QDialog::show();
+ disconnect(animation,SIGNAL(finished()),this,SLOT(close()));
+ animation->setStartValue(0);
+ animation->setEndValue(1);
+ animation->start();
+}
+
+void BookmarksDialog::hide()
+{
+ connect(animation,SIGNAL(finished()),this,SLOT(close()));
+ animation->setStartValue(1);
+ animation->setEndValue(0);
+ animation->start();
+}*/
diff --git a/YACReader/bookmarks_dialog.h b/YACReader/bookmarks_dialog.h
new file mode 100644
index 00000000..c83a2c7e
--- /dev/null
+++ b/YACReader/bookmarks_dialog.h
@@ -0,0 +1,45 @@
+#ifndef __BOOKMARKS_DIALOG_H
+#define __BOOKMARKS_DIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "bookmarks.h"
+
+ class BookmarksDialog : public QDialog
+ {
+ Q_OBJECT
+
+ protected:
+ QList pages;
+ QList images;
+
+ int lastPage;
+
+ QPushButton * accept;
+ QPushButton * cancel;
+
+ QSize coverSize;
+
+ bool eventFilter(QObject *obj, QEvent *event);
+ void keyPressEvent(QKeyEvent * event);
+ //QPropertyAnimation * animation;
+
+ public:
+ BookmarksDialog(QWidget * parent = 0);
+
+ public slots:
+ void setBookmarks(const Bookmarks & bookmarks);
+ //void show();
+ //void hide();
+
+ signals:
+ void goToPage(unsigned int page);
+ };
+
+#endif // BOOKMARKS_DIALOG_H
diff --git a/YACReader/configuration.cpp b/YACReader/configuration.cpp
new file mode 100644
index 00000000..d3b954a7
--- /dev/null
+++ b/YACReader/configuration.cpp
@@ -0,0 +1,72 @@
+#include "configuration.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "yacreader_global.h"
+
+Configuration::Configuration()
+{
+ //read configuration
+ //load("/YACReader.conf");
+}
+
+/*Configuration::Configuration(const Configuration & conf)
+{
+ //nothing
+}*/
+
+void Configuration::load(QSettings * settings)
+{
+ this->settings = settings;
+
+ //TODO set defaults
+ if(!settings->contains(PATH))
+ settings->setValue(PATH,".");
+ if(!settings->contains(GO_TO_FLOW_SIZE))
+ settings->setValue(GO_TO_FLOW_SIZE,QSize(126,200));
+ if(!settings->contains(MAG_GLASS_SIZE))
+ settings->setValue(MAG_GLASS_SIZE,QSize(350,175));
+ if(!settings->contains(ZOOM_LEVEL))
+ settings->setValue(MAG_GLASS_SIZE,QSize(350,175));
+ if(!settings->contains(FLOW_TYPE))
+ settings->setValue(FLOW_TYPE,0);
+ if(!settings->contains(FULLSCREEN))
+ settings->setValue(FULLSCREEN,false);
+ if(!settings->contains(Y_WINDOW_SIZE))
+ settings->setValue(Y_WINDOW_SIZE,QSize(0,0));
+ if(!settings->contains(MAXIMIZED))
+ settings->setValue(MAXIMIZED,false);
+ if(!settings->contains(DOUBLE_PAGE))
+ settings->setValue(DOUBLE_PAGE,false);
+ if(!settings->contains(BACKGROUND_COLOR))
+ settings->setValue(BACKGROUND_COLOR,QColor(40,40,40));
+ if(!settings->contains(ALWAYS_ON_TOP))
+ settings->setValue(ALWAYS_ON_TOP,false);
+ if(!settings->contains(SHOW_TOOLBARS))
+ settings->setValue(SHOW_TOOLBARS, true);
+ //old fit stuff
+ /*if(!settings->contains(FIT))
+ settings->setValue(FIT,false);
+ if(!settings->contains(FIT_TO_WIDTH_RATIO))
+ settings->setValue(FIT_TO_WIDTH_RATIO,1);
+ if(!settings->contains(ADJUST_TO_FULL_SIZE))
+ settings->setValue(ADJUST_TO_FULL_SIZE,false);
+ */
+ }
+void Configuration::updateOpenRecentList (QString path)
+{
+ QStringList list = openRecentList();
+ list.removeAll(path);
+ list.prepend(path);
+ //TODO: Make list lenght configurable
+ while (list.length() > 10)
+ {
+ list.removeLast();
+ }
+ settings->setValue("recentFiles", list);
+}
\ No newline at end of file
diff --git a/YACReader/configuration.h b/YACReader/configuration.h
new file mode 100644
index 00000000..4208c0ff
--- /dev/null
+++ b/YACReader/configuration.h
@@ -0,0 +1,113 @@
+#ifndef __CONFIGURATION_H
+#define __CONFIGURATION_H
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "yacreader_global_gui.h"
+
+#define CONF_FILE_PATH "."
+#define SLIDE_ASPECT_RATIO 1.585
+
+using namespace YACReader;
+
+ class Configuration : public QObject
+ {
+ Q_OBJECT
+
+ private:
+ QSettings * settings;
+
+ QString defaultPath;
+ //configuration properties
+ QSize magnifyingGlassSize;
+ QSize gotoSlideSize;
+ float zoomLevel;
+ bool adjustToWidth;
+ bool fullScreen;
+ FlowType flowType;
+ float fitToWidthRatio;
+ QPoint windowPos;
+ QSize windowSize;
+ bool maximized;
+ bool doublePage;
+ bool doubleMangaPage;
+ bool alwaysOnTop;
+ bool adjustToFullSize;
+ QColor backgroundColor;
+
+ Configuration();
+ //Configuration(const Configuration & conf);
+ void load(const QString & path = CONF_FILE_PATH);
+
+
+ public:
+ static Configuration & getConfiguration()
+ {
+ static Configuration configuration;
+ return configuration;
+ };
+ void load(QSettings * settings);
+ QString getDefaultPath() { return settings->value(PATH).toString(); }
+ void setDefaultPath(QString defaultPath){settings->setValue(PATH,defaultPath);}
+ QSize getMagnifyingGlassSize() { return settings->value(MAG_GLASS_SIZE).toSize();}
+ void setMagnifyingGlassSize(const QSize & mgs) { settings->setValue(MAG_GLASS_SIZE,mgs);}
+ QSize getGotoSlideSize() { return settings->value(GO_TO_FLOW_SIZE).toSize();}
+ void setGotoSlideSize(const QSize & gss) { settings->setValue(GO_TO_FLOW_SIZE,gss);}
+ float getZoomLevel() { return settings->value(ZOOM_LEVEL).toFloat();}
+ void setZoomLevel(float zl) { settings->setValue(ZOOM_LEVEL,zl);}
+
+ //Unified enum based fitmode
+ YACReader::FitMode getFitMode() { return static_cast(settings->value(FITMODE, YACReader::FitMode::FullPage).toInt()); }
+ void setFitMode ( YACReader::FitMode fitMode ){ settings->setValue(FITMODE, static_cast(fitMode)); }
+
+ //openRecent
+ QStringList openRecentList() { return settings->value("recentFiles").toStringList(); }
+ void updateOpenRecentList (QString path);
+ void clearOpenRecentList() { settings->remove("recentFiles"); }
+
+ //Old fitmodes
+ /*
+ bool getAdjustToWidth() {return settings->value(FIT).toBool();}
+ void setAdjustToWidth(bool atw=true) {settings->setValue(FIT,atw);}
+ float getFitToWidthRatio(){return settings->value(FIT_TO_WIDTH_RATIO).toFloat();}
+ void setFitToWidthRatio(float r){settings->setValue(FIT_TO_WIDTH_RATIO,r);}
+ bool getAdjustToFullSize(){return settings->value(ADJUST_TO_FULL_SIZE).toBool();}
+ void setAdjustToFullSize(bool b){settings->setValue(ADJUST_TO_FULL_SIZE,b);}
+ */
+
+ FlowType getFlowType(){return (FlowType)settings->value(FLOW_TYPE_SW).toInt();}
+ void setFlowType(FlowType type){settings->setValue(FLOW_TYPE_SW,type);}
+ bool getFullScreen(){return settings->value(FULLSCREEN).toBool();}
+ void setFullScreen(bool f){settings->setValue(FULLSCREEN,f);}
+
+ QPoint getPos(){return settings->value(Y_WINDOW_POS).toPoint();}
+ void setPos(QPoint p){settings->setValue(Y_WINDOW_POS,p);}
+ QSize getSize(){return settings->value(Y_WINDOW_SIZE).toSize();}
+ void setSize(QSize s){settings->setValue(Y_WINDOW_SIZE,s);}
+ bool getMaximized(){return settings->value(MAXIMIZED).toBool();}
+ void setMaximized(bool b){settings->setValue(MAXIMIZED,b);}
+ bool getDoublePage(){return settings->value(DOUBLE_PAGE).toBool();}
+ void setDoublePage(bool b){settings->setValue(DOUBLE_PAGE,b);}
+ bool getDoubleMangaPage(){return settings->value(DOUBLE_MANGA_PAGE).toBool();}
+ void setDoubleMangaPage(bool b){settings->setValue(DOUBLE_MANGA_PAGE,b);}
+
+ QColor getBackgroundColor(){return settings->value(BACKGROUND_COLOR).value();}
+ void setBackgroundColor(const QColor& color){settings->value(BACKGROUND_COLOR,color);}
+ bool getAlwaysOnTop(){return settings->value(ALWAYS_ON_TOP).toBool();}
+ void setAlwaysOnTop(bool b){ settings->setValue(ALWAYS_ON_TOP,b);}
+ bool getShowToolbars(){return settings->value(SHOW_TOOLBARS).toBool();}
+ void setShowToolbars(bool b){settings->setValue(SHOW_TOOLBARS,b);}
+ bool getShowInformation(){return settings->value(SHOW_INFO,false).toBool();}
+ void setShowInformation(bool b){settings->setValue(SHOW_INFO,b);}
+ QDate getLastVersionCheck(){return settings->value(LAST_VERSION_CHECK).toDate();}
+ void setLastVersionCheck(const QDate & date){ settings->setValue(LAST_VERSION_CHECK,date);}
+ int getNumDaysBetweenVersionChecks() {return settings->value(NUM_DAYS_BETWEEN_VERSION_CHECKS,1).toInt();}
+ void setNumDaysBetweenVersionChecks(int days) {return settings->setValue(NUM_DAYS_BETWEEN_VERSION_CHECKS,days);}
+ };
+
+#endif
diff --git a/YACReader/goto_dialog.cpp b/YACReader/goto_dialog.cpp
new file mode 100644
index 00000000..ed774432
--- /dev/null
+++ b/YACReader/goto_dialog.cpp
@@ -0,0 +1,84 @@
+#include "goto_dialog.h"
+
+#include
+#include
+#include
+
+
+
+GoToDialog::GoToDialog(QWidget * parent)
+:QDialog(parent)
+{
+ setupUI();
+}
+
+void GoToDialog::setupUI()
+{
+ textLabel = new QLabel(tr("Page : "));
+ pageNumber = new QLineEdit;
+ v = new QIntValidator(this);
+ v->setBottom(1);
+ pageNumber->setValidator(v);
+ textLabel->setBuddy(pageNumber);
+ textLabel->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+
+ accept = new QPushButton(tr("Go To"));
+ connect(accept,SIGNAL(clicked()),this,SLOT(goTo()));
+ cancel = new QPushButton(tr("Cancel"));
+ connect(cancel,SIGNAL(clicked()),this,SLOT(close()));
+
+ QHBoxLayout *topLayout = new QHBoxLayout;
+
+ topLayout->addWidget(textLabel);
+ topLayout->addWidget(pageNumber);
+
+ QHBoxLayout *bottomLayout = new QHBoxLayout;
+ bottomLayout->addStretch();
+ bottomLayout->addWidget(accept);
+ bottomLayout->addWidget(cancel);
+
+ QVBoxLayout *mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(numPagesLabel = new QLabel(tr("Total pages : ")));
+ mainLayout->addLayout(topLayout);
+ mainLayout->addStretch();
+ mainLayout->addLayout(bottomLayout);
+
+ QHBoxLayout *imgMainLayout = new QHBoxLayout;
+ QLabel * imgLabel = new QLabel();
+ QPixmap p(":/images/goto.png");
+ imgLabel->setPixmap(p);
+ imgMainLayout->addWidget(imgLabel);
+ imgMainLayout->addLayout(mainLayout);
+
+ setLayout(imgMainLayout);
+
+ setWindowTitle(tr("Go to..."));
+ setModal (true);
+
+ pageNumber->setFocusPolicy(Qt::StrongFocus);
+ pageNumber->setFocus();
+}
+
+void GoToDialog::goTo()
+{
+ unsigned int page = pageNumber->text().toInt();
+ pageNumber->clear();
+
+ if(page >= 1)
+ emit(goToPage(page-1));
+
+ close();
+
+}
+
+void GoToDialog::setNumPages(unsigned int numPages)
+{
+ numPagesLabel->setText(tr("Total pages : ")+QString::number(numPages));
+ v->setTop(numPages);
+}
+
+void GoToDialog::open()
+{
+ pageNumber->setFocus();
+ QDialog::open();
+}
diff --git a/YACReader/goto_dialog.h b/YACReader/goto_dialog.h
new file mode 100644
index 00000000..54253605
--- /dev/null
+++ b/YACReader/goto_dialog.h
@@ -0,0 +1,32 @@
+#ifndef __GOTODIALOG_H
+#define __GOTODIALOG_H
+
+#include
+#include
+#include
+#include
+#include
+
+ class GoToDialog : public QDialog
+ {
+ Q_OBJECT
+ public:
+ GoToDialog(QWidget * parent = 0);
+ private:
+ QLabel * numPagesLabel;
+ QLabel * textLabel;
+ QLineEdit * pageNumber;
+ QIntValidator * v;
+ QPushButton * accept;
+ QPushButton * cancel;
+ void setupUI();
+ public slots:
+ void goTo();
+ void setNumPages(unsigned int numPages);
+ void open();
+ signals:
+ void goToPage(unsigned int page);
+ };
+
+#endif
+
diff --git a/YACReader/goto_flow.cpp b/YACReader/goto_flow.cpp
new file mode 100644
index 00000000..fdb17057
--- /dev/null
+++ b/YACReader/goto_flow.cpp
@@ -0,0 +1,318 @@
+#include "goto_flow.h"
+#include "configuration.h"
+#include "comic.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "yacreader_flow.h"
+
+#include "goto_flow_toolbar.h"
+
+
+GoToFlow::GoToFlow(QWidget *parent,FlowType flowType)
+:GoToFlowWidget(parent),ready(false)
+{
+ updateTimer = new QTimer;
+ connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateImageData()));
+
+ worker = new PageLoader(&mutexGoToFlow);
+
+ flow = new YACReaderFlow(this,flowType);
+ flow->setReflectionEffect(PictureFlow::PlainReflection);
+ imageSize = Configuration::getConfiguration().getGotoSlideSize();
+
+ flow->setSlideSize(imageSize);
+ connect(flow,SIGNAL(centerIndexChanged(int)),this,SLOT(setPageNumber(int)));
+ connect(flow,SIGNAL(selected(unsigned int)),this,SIGNAL(goToPage(unsigned int)));
+
+ connect(toolBar,SIGNAL(goTo(unsigned int)),this,SIGNAL(goToPage(unsigned int)));
+ connect(toolBar,SIGNAL(setCenter(unsigned int)),flow,SLOT(showSlide(unsigned int)));
+
+ mainLayout->addWidget(flow);
+ toolBar->raise();
+
+ resize(static_cast(5*imageSize.width()),toolBar->height() + static_cast(imageSize.height()*1.7));
+
+ this->setCursor(QCursor(Qt::ArrowCursor));
+}
+
+GoToFlow::~GoToFlow()
+{
+ delete flow;
+ delete updateTimer;
+ worker->deleteLater();
+}
+
+void GoToFlow::keyPressEvent(QKeyEvent *event)
+{
+ switch (event->key())
+ {
+ case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up:
+ QApplication::sendEvent(flow,event);
+ return;
+ default:
+ break;
+ }
+
+ GoToFlowWidget::keyPressEvent(event);
+}
+
+void GoToFlow::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+
+ toolBar->move(0, event->size().height() - toolBar->height());
+ toolBar->setFixedWidth(width());
+}
+
+
+void GoToFlow::centerSlide(int slide)
+{
+ if(flow->centerIndex()!=slide)
+ {
+ flow->setCenterIndex(slide);
+ if(ready)// load images if pages are loaded.
+ {
+ //worker->reset(); //BUG FIXED : image didn't load if worker was working
+ preload();
+ }
+ }
+}
+
+void GoToFlow::setNumSlides(unsigned int slides)
+{
+ // numPagesLabel->setText(tr("Total pages : ")+QString::number(slides));
+ // numPagesLabel->adjustSize();
+ imagesReady.clear();
+ imagesReady.fill(false,slides);
+
+ rawImages.clear();
+ rawImages.resize(slides);
+
+ toolBar->setTop(slides);
+
+ SlideInitializer * si = new SlideInitializer(&mutexGoToFlow,flow,slides);
+
+ imagesLoaded.clear();
+ imagesLoaded.fill(false,slides);
+
+ imagesSetted.clear();
+ imagesSetted.fill(false,slides);
+
+ numImagesLoaded = 0;
+
+ connect(flow, SIGNAL(centerIndexChanged(int)), this, SLOT(preload()));
+ connect(flow, SIGNAL(centerIndexChangedSilent(int)), this, SLOT(preload()));
+
+ ready = true;
+ worker->reset();
+
+ si->start();
+}
+
+void GoToFlow::reset()
+{
+ updateTimer->stop();
+ /*imagesLoaded.clear();
+ numImagesLoaded = 0;
+ imagesReady.clear();
+ rawImages.clear();*/
+ ready = false;
+}
+
+void GoToFlow::setImageReady(int index,const QByteArray & image)
+{
+ rawImages[index]=image;
+ imagesReady[index]=true;
+ preload();
+}
+
+void GoToFlow::preload()
+{
+ if(numImagesLoaded < imagesLoaded.size())
+ updateTimer->start(30); //TODO comprobar rendimiento, antes era 70
+}
+
+void GoToFlow::updateImageData()
+{
+ // can't do anything, wait for the next possibility
+ if(worker->busy())
+ return;
+
+ // set image of last one
+ int idx = worker->index();
+ if( idx >= 0 && !worker->result().isNull())
+ {
+ if(!imagesSetted[idx])
+ {
+ flow->setSlide(idx, worker->result());
+ imagesSetted[idx] = true;
+ numImagesLoaded++;
+ rawImages[idx].clear();; //release memory
+ imagesLoaded[idx]=true;
+ }
+
+ }
+
+ // try to load only few images on the left and right side
+ // i.e. all visible ones plus some extra
+#define COUNT 8
+ int indexes[2*COUNT+1];
+ int center = flow->centerIndex();
+ indexes[0] = center;
+ for(int j = 0; j < COUNT; j++)
+ {
+ indexes[j*2+1] = center+j+1;
+ indexes[j*2+2] = center-j-1;
+ }
+ for(int c = 0; c < 2*COUNT+1; c++)
+ {
+ int i = indexes[c];
+ if((i >= 0) && (i < flow->slideCount()))
+ if(!imagesLoaded[i]&&imagesReady[i])//slide(i).isNull())
+ {
+ // schedule thumbnail generation
+
+ worker->generate(i, flow->slideSize(),rawImages[i]);
+ return;
+ }
+
+ }
+
+ // no need to generate anything? stop polling...
+ updateTimer->stop();
+}
+
+void GoToFlow::wheelEvent(QWheelEvent * event)
+{
+ if(event->delta()<0)
+ flow->showNext();
+ else
+ flow->showPrevious();
+ event->accept();
+}
+
+void GoToFlow::setFlowType(FlowType flowType)
+{
+ flow->setFlowType(flowType);
+}
+
+void GoToFlow::updateSize() //TODO : fix. it doesn't work.
+{
+ imageSize = Configuration::getConfiguration().getGotoSlideSize();
+ flow->setSlideSize(imageSize);
+ resize(static_cast(5*imageSize.width()),static_cast(imageSize.height()*1.7));
+}
+
+void GoToFlow::updateConfig(QSettings * settings)
+{
+ Q_UNUSED(settings)
+}
+//-----------------------------------------------------------------------------
+//SlideInitializer
+//-----------------------------------------------------------------------------
+SlideInitializer::SlideInitializer(QMutex * m,PictureFlow * flow,int slides)
+:QThread(),mutex(m),_flow(flow),_slides(slides)
+{
+
+}
+void SlideInitializer::run()
+{
+ mutex->lock();
+
+ _flow->clear();
+ for(int i=0;i<_slides;i++)
+ _flow->addSlide(QImage());
+ _flow->setCenterIndex(0);
+
+ mutex->unlock();
+}
+//-----------------------------------------------------------------------------
+//PageLoader
+//-----------------------------------------------------------------------------
+
+
+PageLoader::PageLoader(QMutex * m):
+QThread(),mutex(m), restart(false), working(false), idx(-1)
+{
+}
+
+PageLoader::~PageLoader()
+{
+ mutex->lock();
+ condition.wakeOne();
+ mutex->unlock();
+ wait();
+}
+
+bool PageLoader::busy() const
+{
+ return isRunning() ? working : false;
+}
+
+void PageLoader::generate(int index, QSize size,const QByteArray & rImage)
+{
+ mutex->lock();
+ this->idx = index;
+ //this->img = QImage();
+ this->size = size;
+ this->rawImage = rImage;
+ mutex->unlock();
+
+ if (!isRunning())
+ start();
+ else
+ {
+ // already running, wake up whenever ready
+ restart = true;
+ condition.wakeOne();
+ }
+}
+
+void PageLoader::run()
+{
+ for(;;)
+ {
+ // copy necessary data
+ mutex->lock();
+ this->working = true;
+ //int idx = this->idx;
+
+
+ QImage image;
+ image.loadFromData(this->rawImage);
+ // let everyone knows it is ready
+ image = image.scaled(this->size,Qt::KeepAspectRatio,Qt::SmoothTransformation);
+
+ mutex->unlock();
+
+ mutex->lock();
+ this->working = false;
+ this->img = image;
+ mutex->unlock();
+
+ // put to sleep
+ mutex->lock();
+ if (!this->restart)
+ condition.wait(mutex);
+ restart = false;
+ mutex->unlock();
+ }
+}
diff --git a/YACReader/goto_flow.h b/YACReader/goto_flow.h
new file mode 100644
index 00000000..53176d1c
--- /dev/null
+++ b/YACReader/goto_flow.h
@@ -0,0 +1,113 @@
+#ifndef __GOTO_FLOW_H
+#define __GOTO_FLOW_H
+
+#include "goto_flow_widget.h"
+#include "yacreader_global_gui.h"
+
+#include
+
+#include
+#include
+
+class QLineEdit;
+class QPushButton;
+class QPixmap;
+class QThread;
+class QSize;
+class QIntValidator;
+class QWaitCondition;
+class QEvent;
+class QLabel;
+
+
+class Comic;
+class SlideInitializer;
+class PageLoader;
+class YACReaderFlow;
+class PictureFlow;
+class QKeyEvent;
+
+class GoToFlow : public GoToFlowWidget
+{
+ Q_OBJECT
+public:
+ GoToFlow(QWidget* parent = 0,FlowType flowType = CoverFlowLike);
+ ~GoToFlow();
+ bool ready; //comic is ready for read.
+private:
+ YACReaderFlow * flow;
+ void keyPressEvent(QKeyEvent* event);
+ //Comic * comic;
+ QSize imageSize;
+
+ QVector imagesLoaded;
+ QVector imagesSetted;
+ int numImagesLoaded;
+ QVector imagesReady;
+ QVector rawImages;
+ QTimer* updateTimer;
+ PageLoader* worker;
+ virtual void wheelEvent(QWheelEvent * event);
+ QMutex mutexGoToFlow;
+
+private slots:
+ void preload();
+ void updateImageData();
+ void resizeEvent(QResizeEvent *event);
+
+ public slots:
+ void centerSlide(int slide);
+ void reset();
+ void setNumSlides(unsigned int slides);
+ void setImageReady(int index,const QByteArray & image);
+ void setFlowType(FlowType flowType);
+ void updateSize();
+ void updateConfig(QSettings * settings);
+signals:
+ void goToPage(unsigned int page);
+
+};
+//-----------------------------------------------------------------------------
+//SlideInitializer
+//-----------------------------------------------------------------------------
+class SlideInitializer : public QThread
+{
+public:
+ SlideInitializer(QMutex * m,PictureFlow * flow,int slides);
+private:
+ QMutex * mutex;
+ PictureFlow * _flow;
+ int _slides;
+ void run();
+};
+//-----------------------------------------------------------------------------
+//PageLoader
+//-----------------------------------------------------------------------------
+
+class PageLoader : public QThread
+{
+public:
+ PageLoader(QMutex * m);
+ ~PageLoader();
+ // returns FALSE if worker is still busy and can't take the task
+ bool busy() const;
+ void generate(int index, QSize size,const QByteArray & rImage);
+ void reset(){idx = -1;};
+ int index() const { return idx; }
+ QImage result() const { return img; }
+protected:
+ void run();
+private:
+ QMutex * mutex;
+ QWaitCondition condition;
+
+ bool restart;
+ bool working;
+ int idx;
+
+ QSize size;
+ QImage img;
+ QByteArray rawImage;
+};
+
+#endif
diff --git a/YACReader/goto_flow_gl.cpp b/YACReader/goto_flow_gl.cpp
new file mode 100644
index 00000000..0612540a
--- /dev/null
+++ b/YACReader/goto_flow_gl.cpp
@@ -0,0 +1,166 @@
+#include "goto_flow_gl.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "configuration.h"
+
+#include "goto_flow_toolbar.h"
+
+
+GoToFlowGL::GoToFlowGL(QWidget* parent, FlowType flowType)
+ :GoToFlowWidget(parent)
+{
+ Q_UNUSED(flowType)
+ flow = new YACReaderPageFlowGL(this);
+ flow->setShowMarks(false);
+
+ imageSize = Configuration::getConfiguration().getGotoSlideSize();
+
+ flow->setSlideSize(imageSize);
+ connect(flow,SIGNAL(centerIndexChanged(int)),this,SLOT(setPageNumber(int)));
+ connect(flow,SIGNAL(selected(unsigned int)),this,SIGNAL(goToPage(unsigned int)));
+
+ connect(toolBar,SIGNAL(goTo(unsigned int)),this,SIGNAL(goToPage(unsigned int)));
+ connect(toolBar,SIGNAL(setCenter(unsigned int)),flow,SLOT(setCenterIndex(unsigned int)));
+
+ mainLayout->addWidget(flow);
+ toolBar->raise();
+
+ resize(static_cast(5*imageSize.width()),toolBar->height() + static_cast(imageSize.height()*1.7));
+
+ this->setCursor(QCursor(Qt::ArrowCursor));
+}
+
+GoToFlowGL::~GoToFlowGL()
+{
+ delete flow;
+}
+
+void GoToFlowGL::reset()
+{
+ flow->reset();
+}
+
+void GoToFlowGL::centerSlide(int slide)
+{
+ if(flow->centerIndex()!=slide)
+ {
+ flow->setCenterIndex(slide);
+ }
+}
+
+void GoToFlowGL::setFlowType(FlowType flowType)
+{
+ if(flowType == CoverFlowLike)
+ flow->setPreset(presetYACReaderFlowClassicConfig);
+ else if(flowType == Strip)
+ flow->setPreset(presetYACReaderFlowStripeConfig);
+ else if(flowType == StripOverlapped)
+ flow->setPreset(presetYACReaderFlowOverlappedStripeConfig);
+ else
+ flow->setPreset(defaultYACReaderFlowConfig);
+}
+
+void GoToFlowGL::setNumSlides(unsigned int slides)
+{
+ flow->populate(slides);
+ toolBar->setTop(slides);
+}
+void GoToFlowGL::setImageReady(int index,const QByteArray & imageData)
+{
+ flow->rawImages[index] = imageData;
+ flow->imagesReady[index] = true;
+}
+
+void GoToFlowGL::updateSize()
+{
+
+}
+
+void GoToFlowGL::updateConfig(QSettings * settings)
+{
+ Performance performance = medium;
+
+ switch (settings->value(PERFORMANCE).toInt())
+ {
+ case 0:
+ performance = low;
+ break;
+ case 1:
+ performance = medium;
+ break;
+ case 2:
+ performance = high;
+ break;
+ case 3:
+ performance = ultraHigh;
+ break;
+ }
+
+ flow->setPerformance(performance);
+
+ switch (settings->value(FLOW_TYPE_GL).toInt())
+ {
+ case 0:
+ flow->setPreset(presetYACReaderFlowClassicConfig);
+ return;
+ case 1:
+ flow->setPreset(presetYACReaderFlowStripeConfig);
+ return;
+ case 2:
+ flow->setPreset(presetYACReaderFlowOverlappedStripeConfig);
+ return;
+ case 3:
+ flow->setPreset(defaultYACReaderFlowConfig);
+ return;
+ case 4:
+ flow->setPreset(pressetYACReaderFlowDownConfig);
+ return;
+ }
+
+
+ //custom config
+
+ flow->setCF_RX(settings->value(X_ROTATION).toInt());
+ flow->setCF_Y(settings->value(Y_POSITION).toInt());
+ flow->setX_Distance(settings->value(COVER_DISTANCE).toInt());
+ flow->setCenter_Distance(settings->value(CENTRAL_DISTANCE).toInt());
+ flow->setCF_Z(settings->value(ZOOM_LEVEL).toInt());
+ flow->setY_Distance(settings->value(Y_COVER_OFFSET).toInt());
+ flow->setZ_Distance(settings->value(Z_COVER_OFFSET).toInt());
+ flow->setRotation(settings->value(COVER_ROTATION).toInt());
+ flow->setFadeOutDist(settings->value(FADE_OUT_DIST).toInt());
+ flow->setLightStrenght(settings->value(LIGHT_STRENGTH).toInt());
+ flow->setMaxAngle(settings->value(MAX_ANGLE).toInt());
+
+/* flow->setVisibility(settings->value("visibilityDistance").toInt());
+ flow->setLightStrenght(settings->value("lightStrength").toInt())*/;
+
+}
+
+void GoToFlowGL::keyPressEvent(QKeyEvent* event)
+{
+ switch (event->key())
+ {
+ case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up:
+ QApplication::sendEvent(flow,event);
+ return;
+ default:
+ break;
+ }
+
+ GoToFlowWidget::keyPressEvent(event);
+}
+
+void GoToFlowGL::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+
+ toolBar->move(0, event->size().height() - toolBar->height());
+ toolBar->setFixedWidth(width());
+}
diff --git a/YACReader/goto_flow_gl.h b/YACReader/goto_flow_gl.h
new file mode 100644
index 00000000..31bd65e0
--- /dev/null
+++ b/YACReader/goto_flow_gl.h
@@ -0,0 +1,40 @@
+#ifndef __GOTO_FLOW_GL_H
+#define __GOTO_FLOW_GL_H
+
+#include "yacreader_global.h"
+#include "goto_flow_widget.h"
+#include "yacreader_flow_gl.h"
+
+class QLineEdit;
+class QIntValidator;
+class QPushButton;
+class QPushButton;
+class QSize;
+class QKeyEvent;
+
+class GoToFlowGL : public GoToFlowWidget
+{
+ Q_OBJECT
+public:
+ GoToFlowGL(QWidget* parent = 0,FlowType flowType = CoverFlowLike);
+ ~GoToFlowGL();
+ void reset();
+ void centerSlide(int slide);
+ void setFlowType(FlowType flowType);
+ void setNumSlides(unsigned int slides);
+ void setImageReady(int index,const QByteArray & image);
+ void updateSize();
+
+ void updateConfig(QSettings * settings);
+
+signals:
+ void goToPage(unsigned int page);
+private:
+ YACReaderPageFlowGL * flow;
+ void keyPressEvent(QKeyEvent* event);
+ void resizeEvent(QResizeEvent *event);
+ //Comic * comic;
+ QSize imageSize;
+};
+
+#endif
diff --git a/YACReader/goto_flow_toolbar.cpp b/YACReader/goto_flow_toolbar.cpp
new file mode 100644
index 00000000..87692151
--- /dev/null
+++ b/YACReader/goto_flow_toolbar.cpp
@@ -0,0 +1,98 @@
+#include "goto_flow_toolbar.h"
+
+#include
+
+GoToFlowToolBar::GoToFlowToolBar(QWidget * parent)
+ :QWidget(parent)
+{
+ //elementos interactivos
+ QVBoxLayout * mainLayout = new QVBoxLayout;
+ bar = new QWidget(this);
+ QHBoxLayout * bottom = new QHBoxLayout(bar);
+ bottom->addStretch();
+ bottom->addWidget(new QLabel("" + tr("Page : ") + "",bar));
+ bottom->addWidget(edit = new QLineEdit(bar));
+ v = new QIntValidator(bar);
+ v->setBottom(1);
+ edit->setValidator(v);
+ edit->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
+ edit->setStyleSheet("QLineEdit {border: 1px solid #77000000; background: #55000000; color: white; padding: 3px 5px 5px 5px; margin: 13px 5px 12px 5px; font-weight:bold}");
+ QPixmap p(":/images/imgEdit.png");
+ edit->setFixedSize(54,50);
+ edit->setAttribute(Qt::WA_MacShowFocusRect,false);
+ //edit->setAttribute(Qt::WA_LayoutUsesWidgetRect,true);
+ //edit->resize(QSize(54,50));
+ edit->setSizePolicy(QSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed));
+ //edit->setAutoFillBackground(false);
+ connect(edit,SIGNAL(returnPressed()),this,SLOT(goTo()));
+
+ QString centerButtonCSS = "QPushButton {background-image: url(:/images/imgCenterSlide.png); width: 100%; height:100%; background-repeat: none; border: none;} "
+ "QPushButton:focus { border: none; outline: none;}"
+ "QPushButton:pressed {background-image: url(:/images/imgCenterSlidePressed.png); width: 100%; height:100%; background-repeat: none; border: none;} ";
+ centerButton = new QPushButton(bar);
+ //centerButton->setIcon(QIcon(":/images/center.png"));
+ centerButton->setStyleSheet(centerButtonCSS);
+ centerButton->setFixedSize(26,50);
+ centerButton->setAttribute(Qt::WA_LayoutUsesWidgetRect,true);
+ connect(centerButton,SIGNAL(clicked()),this,SLOT(centerSlide()));
+ bottom->addWidget(centerButton);
+
+ QString goToButtonCSS = "QPushButton {background-image: url(:/images/imgGoToSlide.png); width: 100%; height:100%; background-repeat: none; border: none;} "
+ "QPushButton:focus { border: none; outline: none;}"
+ "QPushButton:pressed {background-image: url(:/images/imgGoToSlidePressed.png); width: 100%; height:100%; background-repeat: none; border: none;} ";
+ goToButton = new QPushButton(bar);
+ //goToButton->setIcon(QIcon(":/images/goto.png"));
+ goToButton->setStyleSheet(goToButtonCSS);
+ goToButton->setFixedSize(32,50);
+ goToButton->setAttribute(Qt::WA_LayoutUsesWidgetRect,true);
+
+ connect(goToButton,SIGNAL(clicked()),this,SLOT(goTo()));
+ bottom->addWidget(goToButton);
+
+ bottom->addStretch();
+ bottom->setMargin(0);
+ bottom->setSpacing(0);
+
+ bar->setLayout(bottom);
+
+ mainLayout->setMargin(0);
+ mainLayout->setSpacing(0);
+ mainLayout->addWidget(bar);
+
+ setLayout(mainLayout);
+
+ setFixedHeight(50);
+}
+
+void GoToFlowToolBar::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+
+#ifdef YACREADER_LEGACY_FLOW_GL
+ painter.fillRect(0,0,width(),height(),QColor("#FF000000"));
+#else
+ painter.fillRect(0,0,width(),height(),QColor("#99000000"));
+#endif
+}
+
+void GoToFlowToolBar::setPage(int pageNumber)
+{
+ edit->setText(QString::number(pageNumber+1));
+}
+
+void GoToFlowToolBar::setTop(int numPages)
+{
+ v->setTop(numPages);
+}
+
+void GoToFlowToolBar::goTo()
+{
+ if(edit->text().toInt()!=0)
+ emit(goTo(edit->text().toInt()-1));
+}
+
+void GoToFlowToolBar::centerSlide()
+{
+ if(edit->text().toInt()!=0)
+ emit(setCenter(edit->text().toInt()-1));
+}
diff --git a/YACReader/goto_flow_toolbar.h b/YACReader/goto_flow_toolbar.h
new file mode 100644
index 00000000..cdd5bad7
--- /dev/null
+++ b/YACReader/goto_flow_toolbar.h
@@ -0,0 +1,33 @@
+#ifndef GOTO_FLOW_TOOLBAR_H
+#define GOTO_FLOW_TOOLBAR_H
+
+#include
+
+class QLineEdit;
+class QIntValidator;
+class QPushButton;
+
+class GoToFlowToolBar : public QWidget
+{
+ Q_OBJECT
+ private:
+ QLineEdit * edit;
+ QIntValidator * v;
+ QPushButton * centerButton;
+ QPushButton * goToButton;
+ QWidget * bar;
+ void paintEvent(QPaintEvent *);
+
+ public:
+ GoToFlowToolBar(QWidget * parent = 0);
+ public slots:
+ void setPage(int pageNumber);
+ void setTop(int numPages);
+ void goTo();
+ void centerSlide();
+ signals:
+ void setCenter(unsigned int);
+ void goTo(unsigned int);
+};
+
+#endif
diff --git a/YACReader/goto_flow_widget.cpp b/YACReader/goto_flow_widget.cpp
new file mode 100644
index 00000000..83487b62
--- /dev/null
+++ b/YACReader/goto_flow_widget.cpp
@@ -0,0 +1,69 @@
+#include "goto_flow_widget.h"
+
+#include
+#include
+#include
+#include
+
+#include "goto_flow_toolbar.h"
+
+GoToFlowWidget::GoToFlowWidget(QWidget * parent)
+ :QWidget(parent)
+{
+ mainLayout = new QVBoxLayout;
+
+ mainLayout->setMargin(0);
+ mainLayout->setSpacing(0);
+
+ toolBar = new GoToFlowToolBar(this);
+
+ mainLayout->setMargin(0);
+ mainLayout->setSpacing(0);
+
+ setLayout(mainLayout);
+
+ //toolBar->installEventFilter(this);
+}
+
+GoToFlowWidget::~GoToFlowWidget() {
+ delete toolBar;
+ delete mainLayout;
+}
+
+void GoToFlowWidget::setPageNumber(int page)
+{
+ toolBar->setPage(page);
+}
+
+void GoToFlowWidget::keyPressEvent(QKeyEvent* event)
+{
+ switch (event->key())
+ {
+ case Qt::Key_Return: case Qt::Key_Enter:
+ toolBar->goTo();
+ toolBar->centerSlide();
+ break;
+ case Qt::Key_Space:
+ toolBar->centerSlide();
+ break;
+ case Qt::Key_S:
+ QCoreApplication::sendEvent(this->parent(),event);
+ break;
+ }
+
+ event->accept();
+}
+
+/*bool GoToFlowWidget::eventFilter(QObject * target, QEvent * event)
+{
+ if(event->type() == QEvent::KeyPress)
+ {
+ QKeyEvent * e = static_cast(event);
+ if(e->key()==Qt::Key_S || e->key() == Qt::Key_Space)
+ {
+ this->keyPressEvent(e);
+ return true;
+ }
+ }
+ return QWidget::eventFilter(target,event);
+}*/
diff --git a/YACReader/goto_flow_widget.h b/YACReader/goto_flow_widget.h
new file mode 100644
index 00000000..fbb05028
--- /dev/null
+++ b/YACReader/goto_flow_widget.h
@@ -0,0 +1,39 @@
+#ifndef __GOTO_FLOW_WIDGET_H
+#define __GOTO_FLOW_WIDGET_H
+
+#include
+#include
+#include "yacreader_global_gui.h"
+
+using namespace YACReader;
+
+class QSettings;
+class GoToFlowToolBar;
+class QVBoxLayout;
+
+class GoToFlowWidget : public QWidget
+{
+ Q_OBJECT
+protected:
+ QVBoxLayout * mainLayout;
+ GoToFlowToolBar * toolBar;
+public:
+ GoToFlowWidget(QWidget * paret = 0);
+ virtual ~GoToFlowWidget() = 0;
+public slots:
+ virtual void reset() = 0;
+ virtual void centerSlide(int slide) = 0;
+ virtual void setPageNumber(int page);
+ virtual void setFlowType(FlowType flowType) = 0;
+ virtual void setNumSlides(unsigned int slides) = 0;
+ virtual void setImageReady(int index,const QByteArray & image) = 0;
+ virtual void updateSize() = 0;
+ virtual void updateConfig(QSettings * settings) = 0;
+
+protected:
+ void keyPressEvent(QKeyEvent* event);
+ //bool eventFilter(QObject *, QEvent *);
+
+};
+
+#endif
diff --git a/YACReader/icon.ico b/YACReader/icon.ico
new file mode 100644
index 00000000..6c31de5a
Binary files /dev/null and b/YACReader/icon.ico differ
diff --git a/YACReader/icon.rc b/YACReader/icon.rc
new file mode 100644
index 00000000..86e8e9b3
--- /dev/null
+++ b/YACReader/icon.rc
@@ -0,0 +1 @@
+IDI_ICON1 ICON DISCARDABLE "icon.ico"
diff --git a/YACReader/magnifying_glass.cpp b/YACReader/magnifying_glass.cpp
new file mode 100644
index 00000000..c86f202e
--- /dev/null
+++ b/YACReader/magnifying_glass.cpp
@@ -0,0 +1,292 @@
+#include "magnifying_glass.h"
+#include "viewer.h"
+#include "configuration.h"
+#include "shortcuts_manager.h"
+
+#include
+
+MagnifyingGlass::MagnifyingGlass(int w, int h, QWidget * parent)
+:QLabel(parent),zoomLevel(0.5)
+{
+ setup(QSize(w,h));
+}
+
+MagnifyingGlass::MagnifyingGlass(const QSize & size, QWidget * parent)
+:QLabel(parent),zoomLevel(0.5)
+{
+ setup(size);
+}
+
+void MagnifyingGlass::setup(const QSize & size)
+{
+ resize(size);
+ setScaledContents(true);
+ setMouseTracking(true);
+ setCursor(QCursor(QBitmap(1,1),QBitmap(1,1)));
+}
+
+void MagnifyingGlass::mouseMoveEvent(QMouseEvent * event)
+{
+ updateImage();
+ event->accept();
+}
+
+void MagnifyingGlass::updateImage(int x, int y)
+{
+ //image section augmented
+ int zoomWidth = static_cast(width() * zoomLevel);
+ int zoomHeight = static_cast(height() * zoomLevel);
+ Viewer * p = (Viewer *)parent();
+ int currentPos = p->verticalScrollBar()->sliderPosition();
+ const QPixmap * image = p->pixmap();
+ int iWidth = image->width();
+ int iHeight = image->height();
+ float wFactor = static_cast(iWidth) / p->widget()->width();
+ float hFactor = static_cast(iHeight) / p->widget()->height();
+ zoomWidth *= wFactor;
+ zoomHeight *= hFactor;
+ if(p->verticalScrollBar()->minimum()==p->verticalScrollBar()->maximum())
+ {
+ int xp = static_cast(((x-p->widget()->pos().x())*wFactor)-zoomWidth/2);
+ int yp = static_cast((y-p->widget()->pos().y()+currentPos)*hFactor-zoomHeight/2);
+ int xOffset=0;
+ int yOffset=0;
+ int zw=zoomWidth;
+ int zh=zoomHeight;
+ //int wOffset,hOffset=0;
+ bool outImage = false;
+ if(xp<0)
+ {
+ xOffset = -xp;
+ xp=0;
+ zw = zw - xOffset;
+ outImage = true;
+ }
+ if(yp<0)
+ {
+ yOffset = -yp;
+ yp=0;
+ zh = zh - yOffset;
+ outImage = true;
+ }
+
+ if(xp+zoomWidth >= image->width())
+ {
+ zw -= xp+zw - image->width();
+ outImage = true;
+ }
+ if(yp+zoomHeight >= image->height())
+ {
+ zh -= yp+zh - image->height();
+ outImage = true;
+ }
+ if(outImage)
+ {
+ QImage img(zoomWidth,zoomHeight,QImage::Format_RGB32);
+ img.fill(Configuration::getConfiguration().getBackgroundColor());
+ if(zw>0&&zh>0)
+ {
+ QPainter painter(&img);
+ painter.drawPixmap(xOffset,yOffset,p->pixmap()->copy(xp,yp,zw,zh));
+ }
+ setPixmap(QPixmap().fromImage(img));
+ }
+ else
+ setPixmap(p->pixmap()->copy(xp,yp,zoomWidth,zoomHeight));
+ }
+ else
+ {
+ int xp = static_cast(((x-p->widget()->pos().x())*wFactor)-zoomWidth/2);
+ int yp = static_cast((y+currentPos)*hFactor-zoomHeight/2);
+ int xOffset=0;
+ int yOffset=0;
+ int zw=zoomWidth;
+ int zh=zoomHeight;
+ //int wOffset,hOffset=0;
+ bool outImage = false;
+ if(xp<0)
+ {
+ xOffset = -xp;
+ xp=0;
+ zw = zw - xOffset;
+ outImage = true;
+ }
+ if(yp<0)
+ {
+ yOffset = -yp;
+ yp=0;
+ zh = zh - yOffset;
+ outImage = true;
+ }
+
+ if(xp+zoomWidth >= image->width())
+ {
+ zw -= xp+zw - image->width();
+ outImage = true;
+ }
+ if(yp+zoomHeight >= image->height())
+ {
+ zh -= yp+zh - image->height();
+ outImage = true;
+ }
+ if(outImage)
+ {
+ QImage img(zoomWidth,zoomHeight,QImage::Format_RGB32);
+ img.fill(Configuration::getConfiguration().getBackgroundColor());
+ if(zw>0&&zh>0)
+ {
+ QPainter painter(&img);
+ painter.drawPixmap(xOffset,yOffset,p->pixmap()->copy(xp,yp,zw,zh));
+ }
+ setPixmap(QPixmap().fromImage(img));
+ }
+ else
+ setPixmap(p->pixmap()->copy(xp,yp,zoomWidth,zoomHeight));
+ }
+ move(static_cast(x-float(width())/2),static_cast(y-float(height())/2));
+}
+
+void MagnifyingGlass::updateImage()
+{
+ if(isVisible())
+ {
+ QPoint p = QPoint(cursor().pos().x(),cursor().pos().y());
+ p = this->parentWidget()->mapFromGlobal(p);
+ updateImage(p.x(),p.y());
+ }
+}
+void MagnifyingGlass::wheelEvent(QWheelEvent * event)
+{
+ switch(event->modifiers())
+ {
+ //size
+ case Qt::NoModifier:
+ if(event->delta()<0)
+ sizeUp();
+ else
+ sizeDown();
+ break;
+ //size height
+ case Qt::ControlModifier:
+ if(event->delta()<0)
+ heightUp();
+ else
+ heightDown();
+ break;
+ //size width
+ case Qt::AltModifier:
+ if(event->delta()<0)
+ widthUp();
+ else
+ widthDown();
+ break;
+ //zoom level
+ case Qt::ShiftModifier:
+ if(event->delta()<0)
+ zoomIn();
+ else
+ zoomOut();
+ break;
+ }
+ updateImage();
+ event->setAccepted(true);
+}
+void MagnifyingGlass::zoomIn()
+{
+ if(zoomLevel>0.2f)
+ zoomLevel -= 0.025f;
+}
+
+void MagnifyingGlass::zoomOut()
+{
+ if(zoomLevel<0.9f)
+ zoomLevel += 0.025f;
+}
+
+void MagnifyingGlass::sizeUp()
+{
+ Viewer * p = (Viewer *)parent();
+ if(width()<(p->width()*0.90f))
+ resize(width()+30,height()+15);
+}
+
+void MagnifyingGlass::sizeDown()
+{
+ if(width()>175)
+ resize(width()-30,height()-15);
+}
+
+void MagnifyingGlass::heightUp()
+{
+ Viewer * p = (Viewer *)parent();
+ if(height()<(p->height()*0.90f))
+ resize(width(),height()+15);
+}
+
+void MagnifyingGlass::heightDown()
+{
+ if(height()>80)
+ resize(width(),height()-15);
+}
+
+void MagnifyingGlass::widthUp()
+{
+ Viewer * p = (Viewer *)parent();
+ if(width()<(p->width()*0.90f))
+ resize(width()+30,height());
+}
+
+void MagnifyingGlass::widthDown()
+{
+ if(width()>175)
+ resize(width()-30,height());
+}
+
+void MagnifyingGlass::keyPressEvent(QKeyEvent *event)
+{
+ bool validKey = false;
+
+ int _key = event->key();
+ Qt::KeyboardModifiers modifiers = event->modifiers();
+
+ if(modifiers & Qt::ShiftModifier)
+ _key |= Qt::SHIFT;
+ if (modifiers & Qt::ControlModifier)
+ _key |= Qt::CTRL;
+ if (modifiers & Qt::MetaModifier)
+ _key |= Qt::META;
+ if (modifiers & Qt::AltModifier)
+ _key |= Qt::ALT;
+
+ QKeySequence key(_key);
+
+ if (key == ShortcutsManager::getShortcutsManager().getShortcut(SIZE_UP_MGLASS_ACTION_Y))
+ {
+ sizeUp();
+ validKey = true;
+ }
+
+ else if (key == ShortcutsManager::getShortcutsManager().getShortcut(SIZE_DOWN_MGLASS_ACTION_Y))
+ {
+ sizeDown();
+ validKey = true;
+ }
+
+ else if (key == ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_IN_MGLASS_ACTION_Y))
+ {
+ zoomIn();
+ validKey = true;
+ }
+
+ else if (key == ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_OUT_MGLASS_ACTION_Y))
+ {
+ zoomOut();
+ validKey = true;
+ }
+
+ if(validKey)
+ {
+ updateImage();
+ event->setAccepted(true);
+ }
+}
diff --git a/YACReader/magnifying_glass.h b/YACReader/magnifying_glass.h
new file mode 100644
index 00000000..519e3404
--- /dev/null
+++ b/YACReader/magnifying_glass.h
@@ -0,0 +1,34 @@
+#ifndef __MAGNIFYING_GLASS
+#define __MAGNIFYING_GLASS
+
+#include
+#include
+#include
+#include
+
+ class MagnifyingGlass : public QLabel
+ {
+ Q_OBJECT
+ private:
+ float zoomLevel;
+ void setup(const QSize & size);
+ void keyPressEvent(QKeyEvent * event);
+ public:
+ MagnifyingGlass(int width,int height,QWidget * parent);
+ MagnifyingGlass(const QSize & size, QWidget * parent);
+ void mouseMoveEvent(QMouseEvent * event);
+ public slots:
+ void updateImage(int x, int y);
+ void updateImage();
+ void wheelEvent(QWheelEvent * event);
+ void zoomIn();
+ void zoomOut();
+ void sizeUp();
+ void sizeDown();
+ void heightUp();
+ void heightDown();
+ void widthUp();
+ void widthDown();
+ };
+
+#endif
diff --git a/YACReader/main.cpp b/YACReader/main.cpp
new file mode 100644
index 00000000..efd8d012
--- /dev/null
+++ b/YACReader/main.cpp
@@ -0,0 +1,173 @@
+#include
+#include
+#include
+//#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "main_window_viewer.h"
+#include "configuration.h"
+#include "exit_check.h"
+
+#include "QsLog.h"
+#include "QsLogDest.h"
+
+using namespace QsLogging;
+
+ #if defined(WIN32) && defined(_DEBUG)
+ #define _CRTDBG_MAP_ALLOC
+ #include
+ #include
+ #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
+ #define new DEBUG_NEW
+ #endif
+
+#ifdef Q_OS_MAC
+#include
+class YACReaderApplication: public QApplication
+{
+ public:
+ YACReaderApplication(int & argc, char ** argv) : QApplication(argc,argv)
+ {}
+
+ void setWindow(MainWindowViewer * w)
+ {
+ window = w;
+ }
+
+ protected:
+ bool event(QEvent * event)
+ {
+ switch(event->type())
+ {
+ case QEvent::FileOpen:
+ window->openComicFromPath(static_cast(event)->file());
+ return true;
+ default:
+ return QApplication::event(event);
+ }
+ }
+ private:
+ MainWindowViewer * window;
+};
+#endif
+
+int main(int argc, char * argv[])
+{
+
+ #if defined(_MSC_VER) && defined(_DEBUG)
+ _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+#endif
+
+//fix for misplaced text in Qt4.8 and Mavericks
+#ifdef Q_OS_MAC
+ #if QT_VERSION < 0x050000
+ if(QSysInfo::MacintoshVersion > QSysInfo::MV_10_8)
+ QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
+ #endif
+#endif
+
+
+#ifdef Q_OS_MAC
+ YACReaderApplication app(argc,argv);
+#else
+ QApplication app(argc, argv);
+#endif
+
+#ifdef FORCE_ANGLE
+ app.setAttribute(Qt::AA_UseOpenGLES);
+#endif
+
+ app.setApplicationName("YACReader");
+ app.setOrganizationName("YACReader");
+ app.setAttribute(Qt::AA_UseHighDpiPixmaps);
+ //simple command line parser
+ //will be replaced by QCommandLineParser in the future
+ QStringList optlist;
+ QStringList arglist;
+
+ if (argc > 1)
+ {
+ //extract options and arguments
+ optlist = QCoreApplication::arguments().filter(QRegExp ("^-{1,2}")); //options starting with "-"
+ arglist = QCoreApplication::arguments().filter(QRegExp ("^(?!-{1,2})")); //positional arguments
+ //deal with standard options
+ if (!optlist.isEmpty())
+ {
+ QTextStream parser(stdout);
+ if (optlist.contains("--version") || optlist.contains("-v"))
+ {
+ parser << app.applicationName() << " " << QString(VERSION) << endl << "Copyright 2014 by Luis Angel San Martin Rodriguez" << endl;
+ return 0;
+ }
+ if (optlist.contains("--help") || optlist.contains("-h"))
+ {
+ parser << endl << "Usage: YACReader [File|Directory|Option]" << endl << endl;
+ parser << "Options:" << endl;
+ parser << " -h, --help\t\tDisplay this text and exit." << endl;
+ parser << " -v, --version\t\tDisplay version information and exit." << endl << endl;
+ parser << "Arguments:" << endl;
+ parser << " file\t\t\tOpen comic file." < 1)
+ {
+ if (!optlist.filter("--comicId=").isEmpty() && !optlist.filter("--libraryId=").isEmpty())
+ {
+ if (arglist.count()>1)
+ {
+ mwv->open(arglist.at(1), optlist.filter("--comicId=").at(0).split("=").at(1).toULongLong(), optlist.filter("--libraryId=").at(0).split("=").at(1).toULongLong());
+ }
+ }
+ else if ((arglist.count()>1))
+ {
+ //open first positional argument, silently ignore all following positional arguments
+ mwv->openComicFromPath(arglist.at(1));
+ }
+ }
+#ifdef Q_OS_MAC
+ app.setWindow(mwv);
+#endif
+ mwv->show();
+
+ int ret = app.exec();
+
+ //Configuration::getConfiguration().save();
+
+ YACReader::exitCheck(ret);
+
+ return ret;
+}
diff --git a/YACReader/main_window_viewer.cpp b/YACReader/main_window_viewer.cpp
new file mode 100644
index 00000000..d114108d
--- /dev/null
+++ b/YACReader/main_window_viewer.cpp
@@ -0,0 +1,1531 @@
+#include "main_window_viewer.h"
+#include "configuration.h"
+#include "viewer.h"
+#include "goto_dialog.h"
+#include "custom_widgets.h"
+#include "options_dialog.h"
+#include "check_new_version.h"
+#include "comic.h"
+#include "bookmarks_dialog.h"
+#include "shortcuts_dialog.h"
+#include "width_slider.h"
+#include "qnaturalsorting.h"
+#include "help_about_dialog.h"
+#include "yacreader_tool_bar_stretch.h"
+
+#include "comic_db.h"
+#include "yacreader_local_client.h"
+
+#include "yacreader_global.h"
+#include "edit_shortcuts_dialog.h"
+#include "shortcuts_manager.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* TODO remove, no longer used
+#ifdef Q_OS_MAC
+class MacToolBarSeparator : public QWidget
+{
+public:
+ MacToolBarSeparator(QWidget * parent =0)
+ :QWidget(parent)
+ {
+ setFixedWidth(2);
+ }
+
+ void paintEvent(QPaintEvent *event)
+ {
+ Q_UNUSED(event);
+ QPainter painter(this);
+
+ QLinearGradient lG(0,0,0,height());
+
+ lG.setColorAt(0,QColor(128,128,128,0));
+ lG.setColorAt(0.5,QColor(128,128,128,255));
+ lG.setColorAt(1,QColor(128,128,128,0));
+
+ painter.fillRect(0,0,1,height(),lG);
+
+ QLinearGradient lG2(1,0,1,height());
+
+ lG2.setColorAt(0,QColor(220,220,220,0));
+ lG2.setColorAt(0.5,QColor(220,220,220,255));
+ lG2.setColorAt(1,QColor(220,220,220,0));
+
+ painter.fillRect(1,0,1,height(),lG2);
+ }
+};
+#endif*/
+
+MainWindowViewer::MainWindowViewer()
+:QMainWindow(),fullscreen(false),toolbars(true),alwaysOnTop(false),currentDirectory("."),currentDirectoryImgDest("."),isClient(false)
+{
+ loadConfiguration();
+ setupUI();
+}
+
+MainWindowViewer::~MainWindowViewer()
+{
+ delete settings;
+ delete viewer;
+ delete had;
+
+ //delete sliderAction;
+ delete openAction;
+ delete openFolderAction;
+ delete saveImageAction;
+ delete openPreviousComicAction;
+ delete openNextComicAction;
+ delete prevAction;
+ delete nextAction;
+ delete adjustHeightAction;
+ delete adjustWidthAction;
+ delete leftRotationAction;
+ delete rightRotationAction;
+ delete doublePageAction;
+ delete doubleMangaPageAction;
+ delete increasePageZoomAction;
+ delete decreasePageZoomAction;
+ delete resetZoomAction;
+ delete goToPageAction;
+ delete optionsAction;
+ delete helpAboutAction;
+ delete showMagnifyingGlassAction;
+ delete setBookmarkAction;
+ delete showBookmarksAction;
+ delete showShorcutsAction;
+ delete showInfoAction;
+ delete closeAction;
+ delete showDictionaryAction;
+ delete alwaysOnTopAction;
+ delete adjustToFullSizeAction;
+ delete fitToPageAction;
+ delete showFlowAction;
+
+}
+void MainWindowViewer::loadConfiguration()
+{
+ settings = new QSettings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
+
+ Configuration & config = Configuration::getConfiguration();
+ config.load(settings);
+ currentDirectory = config.getDefaultPath();
+ fullscreen = config.getFullScreen();
+}
+
+void MainWindowViewer::setupUI()
+{
+ setWindowIcon(QIcon(":/images/icon.png"));
+
+ //setUnifiedTitleAndToolBarOnMac(true);
+
+ viewer = new Viewer(this);
+ connect(viewer,SIGNAL(reset()),this,SLOT(processReset()));
+ //detected end of comic
+ connect(viewer,SIGNAL(openNextComic()),this,SLOT(openNextComic()));
+ //detected start of comic
+ connect(viewer,SIGNAL(openPreviousComic()),this,SLOT(openPreviousComic()));
+
+ setCentralWidget(viewer);
+ int heightDesktopResolution = QApplication::desktop()->screenGeometry().height();
+ int widthDesktopResolution = QApplication::desktop()->screenGeometry().width();
+ int height,width;
+ height = static_cast(heightDesktopResolution*0.84);
+ width = static_cast(height*0.70);
+ Configuration & conf = Configuration::getConfiguration();
+ QPoint p = conf.getPos();
+ QSize s = conf.getSize();
+ if(s.width()!=0)
+ {
+ move(p);
+ resize(s);
+ }
+ else
+ {
+ move(QPoint((widthDesktopResolution-width)/2,((heightDesktopResolution-height)-40)/2));
+ resize(QSize(width,height));
+ }
+
+ had = new HelpAboutDialog(this); //TODO load data
+
+ had->loadAboutInformation(":/files/about.html");
+ had->loadHelp(":/files/helpYACReader.html");
+
+ optionsDialog = new OptionsDialog(this);
+ connect(optionsDialog,SIGNAL(accepted()),viewer,SLOT(updateOptions()));
+ connect(optionsDialog, SIGNAL(optionsChanged()),this,SLOT(reloadOptions()));
+ connect(optionsDialog,SIGNAL(changedFilters(int,int,int)),viewer,SLOT(updateFilters(int,int,int)));
+
+ optionsDialog->restoreOptions(settings);
+ //shortcutsDialog = new ShortcutsDialog(this);
+ editShortcutsDialog = new EditShortcutsDialog(this);
+ connect(optionsDialog,SIGNAL(editShortcuts()),editShortcutsDialog,SLOT(show()));
+
+ createActions();
+ setUpShortcutsManagement();
+
+ createToolBars();
+
+ setWindowTitle("YACReader");
+
+ checkNewVersion();
+
+ viewer->setFocusPolicy(Qt::StrongFocus);
+
+
+ //if(Configuration::getConfiguration().getAlwaysOnTop())
+ //{
+ // setWindowFlags(this->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
+ //}
+
+ if(fullscreen)
+ toFullScreen();
+ if(conf.getMaximized())
+ showMaximized();
+
+ setAcceptDrops(true);
+
+ if(Configuration::getConfiguration().getShowToolbars() && !Configuration::getConfiguration().getFullScreen())
+ showToolBars();
+ else
+ hideToolBars();
+}
+
+void MainWindowViewer::createActions()
+{
+ openAction = new QAction(tr("&Open"),this);
+ openAction->setIcon(QIcon(":/images/viewer_toolbar/open.png"));
+ openAction->setToolTip(tr("Open a comic"));
+ openAction->setData(OPEN_ACTION_Y);
+ openAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(OPEN_ACTION_Y));
+ connect(openAction, SIGNAL(triggered()), this, SLOT(open()));
+
+ openFolderAction = new QAction(tr("Open Folder"),this);
+ openFolderAction->setIcon(QIcon(":/images/viewer_toolbar/openFolder.png"));
+ openFolderAction->setToolTip(tr("Open image folder"));
+ openFolderAction->setData(OPEN_FOLDER_ACTION_Y);
+ openFolderAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(OPEN_FOLDER_ACTION_Y));
+ connect(openFolderAction, SIGNAL(triggered()), this, SLOT(openFolder()));
+
+ QAction* recentFileAction = 0;
+ //TODO: Replace limit with a configurable value
+ for (int i = 0; i < 10; i++)
+ {
+ recentFileAction = new QAction(this);
+ recentFileAction->setVisible(false);
+ QObject::connect(recentFileAction, &QAction::triggered, this, &MainWindowViewer::openRecent);
+ recentFilesActionList.append(recentFileAction);
+ }
+
+ clearRecentFilesAction = new QAction(tr("Clear"),this);
+ clearRecentFilesAction->setToolTip(tr("Clear openrecent list"));
+ connect(clearRecentFilesAction, &QAction::triggered, this, &MainWindowViewer::clearRecentFiles);
+
+ saveImageAction = new QAction(tr("Save"),this);
+ saveImageAction->setIcon(QIcon(":/images/viewer_toolbar/save.png"));
+ saveImageAction->setToolTip(tr("Save current page"));
+ saveImageAction->setDisabled(true);
+ saveImageAction->setData(SAVE_IMAGE_ACTION_Y);
+ saveImageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SAVE_IMAGE_ACTION_Y));
+ connect(saveImageAction,SIGNAL(triggered()),this,SLOT(saveImage()));
+
+ openPreviousComicAction = new QAction(tr("Previous Comic"),this);
+ openPreviousComicAction->setIcon(QIcon(":/images/viewer_toolbar/openPrevious.png"));
+ openPreviousComicAction->setToolTip(tr("Open previous comic"));
+ openPreviousComicAction->setDisabled(true);
+ openPreviousComicAction->setData(OPEN_PREVIOUS_COMIC_ACTION_Y);
+ openPreviousComicAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(OPEN_PREVIOUS_COMIC_ACTION_Y));
+ connect(openPreviousComicAction,SIGNAL(triggered()),this,SLOT(openPreviousComic()));
+
+ openNextComicAction = new QAction(tr("Next Comic"),this);
+ openNextComicAction->setIcon(QIcon(":/images/viewer_toolbar/openNext.png"));
+ openNextComicAction->setToolTip(tr("Open next comic"));
+ openNextComicAction->setDisabled(true);
+ openNextComicAction->setData(OPEN_NEXT_COMIC_ACTION_Y);
+ openNextComicAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(OPEN_NEXT_COMIC_ACTION_Y));
+ connect(openNextComicAction,SIGNAL(triggered()),this,SLOT(openNextComic()));
+
+ prevAction = new QAction(tr("&Previous"),this);
+ prevAction->setIcon(QIcon(":/images/viewer_toolbar/previous.png"));
+ prevAction->setShortcutContext(Qt::WidgetShortcut);
+ prevAction->setToolTip(tr("Go to previous page"));
+ prevAction->setDisabled(true);
+ prevAction->setData(PREV_ACTION_Y);
+ prevAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(PREV_ACTION_Y));
+ connect(prevAction, SIGNAL(triggered()),viewer,SLOT(prev()));
+
+ nextAction = new QAction(tr("&Next"),this);
+ nextAction->setIcon(QIcon(":/images/viewer_toolbar/next.png"));
+ nextAction->setShortcutContext(Qt::WidgetShortcut);
+ nextAction->setToolTip(tr("Go to next page"));
+ nextAction->setDisabled(true);
+ nextAction->setData(NEXT_ACTION_Y);
+ nextAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(NEXT_ACTION_Y));
+ connect(nextAction, SIGNAL(triggered()),viewer,SLOT(next()));
+
+ adjustHeightAction = new QAction(tr("Fit Height"),this);
+ adjustHeightAction->setIcon(QIcon(":/images/viewer_toolbar/toHeight.png"));
+ //adjustWidth->setCheckable(true);
+ adjustHeightAction->setDisabled(true);
+ adjustHeightAction->setToolTip(tr("Fit image to height"));
+ //adjustWidth->setIcon(QIcon(":/images/fitWidth.png"));
+ adjustHeightAction->setData(ADJUST_HEIGHT_ACTION_Y);
+ adjustHeightAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ADJUST_HEIGHT_ACTION_Y));
+ adjustHeightAction->setCheckable(true);
+ connect(adjustHeightAction, SIGNAL(triggered()),this,SLOT(fitToHeight()));
+
+ adjustWidthAction = new QAction(tr("Fit Width"),this);
+ adjustWidthAction->setIcon(QIcon(":/images/viewer_toolbar/toWidth.png"));
+ //adjustWidth->setCheckable(true);
+ adjustWidthAction->setDisabled(true);
+ adjustWidthAction->setToolTip(tr("Fit image to width"));
+ //adjustWidth->setIcon(QIcon(":/images/fitWidth.png"));
+ adjustWidthAction->setData(ADJUST_WIDTH_ACTION_Y);
+ adjustWidthAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ADJUST_WIDTH_ACTION_Y));
+ adjustWidthAction->setCheckable(true);
+ connect(adjustWidthAction, SIGNAL(triggered()),this,SLOT(fitToWidth()));
+
+ adjustToFullSizeAction = new QAction(tr("Show full size"),this);
+ adjustToFullSizeAction->setIcon(QIcon(":/images/viewer_toolbar/full.png"));
+ adjustToFullSizeAction->setCheckable(false);
+ adjustToFullSizeAction->setDisabled(true);
+ adjustToFullSizeAction->setData(ADJUST_TO_FULL_SIZE_ACTION_Y);
+ adjustToFullSizeAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ADJUST_TO_FULL_SIZE_ACTION_Y));
+ adjustToFullSizeAction->setCheckable(true);
+ connect(adjustToFullSizeAction,SIGNAL(triggered()),this,SLOT(adjustToFullSizeSwitch()));
+
+ fitToPageAction = new QAction(tr("Fit to page"),this);
+ fitToPageAction->setIcon(QIcon(":/images/viewer_toolbar/fitToPage.png"));
+ fitToPageAction->setDisabled(true);
+ fitToPageAction->setData(FIT_TO_PAGE_ACTION_Y);
+ fitToPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(FIT_TO_PAGE_ACTION_Y));
+ fitToPageAction->setCheckable(true);
+ connect(fitToPageAction,SIGNAL(triggered()),this,SLOT(fitToPageSwitch()));
+
+ //fit modes have to be exclusive and checkable
+ QActionGroup *fitModes = new QActionGroup(this);
+ fitModes->addAction(adjustHeightAction);
+ fitModes->addAction(adjustWidthAction);
+ fitModes->addAction(adjustToFullSizeAction);
+ fitModes->addAction(fitToPageAction);
+
+ switch(Configuration::getConfiguration().getFitMode())
+ {
+ case YACReader::FitMode::ToWidth:
+ adjustWidthAction->setChecked(true);
+ break;
+ case YACReader::FitMode::ToHeight:
+ adjustHeightAction->setChecked(true);
+ break;
+ case YACReader::FitMode::FullRes:
+ adjustToFullSizeAction->setChecked(true);
+ break;
+ case YACReader::FitMode::FullPage:
+ fitToPageAction->setChecked(true);
+ break;
+ default:
+ fitToPageAction->setChecked(true);
+ }
+
+ resetZoomAction = new QAction(tr("Reset zoom"), this);
+ resetZoomAction->setDisabled(true);
+ resetZoomAction->setData(RESET_ZOOM_ACTION_Y);
+ resetZoomAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(RESET_ZOOM_ACTION_Y));
+ connect(resetZoomAction,SIGNAL(triggered()),this,SLOT(resetZoomLevel()));
+
+ showZoomSliderlAction = new QAction(tr("Show zoom slider"), this);
+ showZoomSliderlAction->setIcon(QIcon(":/images/viewer_toolbar/zoom.png"));
+ showZoomSliderlAction->setDisabled(true);
+
+ increasePageZoomAction = new QAction(tr("Zoom+"),this);
+ increasePageZoomAction->setDisabled(true);
+ increasePageZoomAction->setData(ZOOM_PLUS_ACTION_Y);
+ increasePageZoomAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_PLUS_ACTION_Y));
+ connect(increasePageZoomAction,SIGNAL(triggered()),this,SLOT(increasePageZoomLevel()));
+
+ decreasePageZoomAction = new QAction(tr("Zoom-"),this);
+ decreasePageZoomAction->setDisabled(true);
+ decreasePageZoomAction->setData(ZOOM_MINUS_ACTION_Y);
+ decreasePageZoomAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_MINUS_ACTION_Y));
+ connect(decreasePageZoomAction,SIGNAL(triggered()),this,SLOT(decreasePageZoomLevel()));
+
+ leftRotationAction = new QAction(tr("Rotate image to the left"),this);
+ leftRotationAction->setIcon(QIcon(":/images/viewer_toolbar/rotateL.png"));
+ leftRotationAction->setDisabled(true);
+ leftRotationAction->setData(LEFT_ROTATION_ACTION_Y);
+ leftRotationAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(LEFT_ROTATION_ACTION_Y));
+ connect(leftRotationAction, SIGNAL(triggered()),viewer,SLOT(rotateLeft()));
+
+ rightRotationAction = new QAction(tr("Rotate image to the right"),this);
+ rightRotationAction->setIcon(QIcon(":/images/viewer_toolbar/rotateR.png"));
+ rightRotationAction->setDisabled(true);
+ rightRotationAction->setData(RIGHT_ROTATION_ACTION_Y);
+ rightRotationAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(RIGHT_ROTATION_ACTION_Y));
+ connect(rightRotationAction, SIGNAL(triggered()),viewer,SLOT(rotateRight()));
+
+ doublePageAction = new QAction(tr("Double page mode"),this);
+ doublePageAction->setToolTip(tr("Switch to double page mode"));
+ doublePageAction->setIcon(QIcon(":/images/viewer_toolbar/doublePage.png"));
+ doublePageAction->setDisabled(true);
+ doublePageAction->setCheckable(true);
+ doublePageAction->setChecked(Configuration::getConfiguration().getDoublePage());
+ doublePageAction->setData(DOUBLE_PAGE_ACTION_Y);
+ doublePageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(DOUBLE_PAGE_ACTION_Y));
+ connect(doublePageAction, SIGNAL(triggered()),viewer,SLOT(doublePageSwitch()));
+
+ //inversed pictures mode
+ doubleMangaPageAction = new QAction(tr("Double page manga mode"),this);
+ doubleMangaPageAction->setToolTip(tr("Reverse reading order in double page mode"));
+ doubleMangaPageAction->setIcon(QIcon(":/images/viewer_toolbar/doubleMangaPage.png"));
+ doubleMangaPageAction->setDisabled(true);
+ doubleMangaPageAction->setCheckable(true);
+ doubleMangaPageAction->setChecked(Configuration::getConfiguration().getDoubleMangaPage());
+ doubleMangaPageAction->setData(DOUBLE_MANGA_PAGE_ACTION_Y);
+ doubleMangaPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(DOUBLE_MANGA_PAGE_ACTION_Y));
+ connect(doubleMangaPageAction, SIGNAL(triggered()),viewer,SLOT(doubleMangaPageSwitch()));
+
+ goToPageAction = new QAction(tr("Go To"),this);
+ goToPageAction->setIcon(QIcon(":/images/viewer_toolbar/goto.png"));
+ goToPageAction->setDisabled(true);
+ goToPageAction->setToolTip(tr("Go to page ..."));
+ goToPageAction->setData(GO_TO_PAGE_ACTION_Y);
+ goToPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(GO_TO_PAGE_ACTION_Y));
+ connect(goToPageAction, SIGNAL(triggered()),viewer,SLOT(showGoToDialog()));
+
+ optionsAction = new QAction(tr("Options"),this);
+ optionsAction->setToolTip(tr("YACReader options"));
+ optionsAction->setData(OPTIONS_ACTION_Y);
+ optionsAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(OPTIONS_ACTION_Y));
+ optionsAction->setIcon(QIcon(":/images/viewer_toolbar/options.png"));
+
+ connect(optionsAction, SIGNAL(triggered()),optionsDialog,SLOT(show()));
+
+ helpAboutAction = new QAction(tr("Help"),this);
+ helpAboutAction->setToolTip(tr("Help, About YACReader"));
+ helpAboutAction->setIcon(QIcon(":/images/viewer_toolbar/help.png"));
+ helpAboutAction->setData(HELP_ABOUT_ACTION_Y);
+ helpAboutAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(HELP_ABOUT_ACTION_Y));
+ connect(helpAboutAction, SIGNAL(triggered()),had,SLOT(show()));
+
+ showMagnifyingGlassAction = new QAction(tr("Magnifying glass"),this);
+ showMagnifyingGlassAction->setToolTip(tr("Switch Magnifying glass"));
+ showMagnifyingGlassAction->setIcon(QIcon(":/images/viewer_toolbar/magnifyingGlass.png"));
+ showMagnifyingGlassAction->setDisabled(true);
+ showMagnifyingGlassAction->setCheckable(true);
+ showMagnifyingGlassAction->setData(SHOW_MAGNIFYING_GLASS_ACTION_Y);
+ showMagnifyingGlassAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_MAGNIFYING_GLASS_ACTION_Y));
+ connect(showMagnifyingGlassAction, SIGNAL(triggered()),viewer,SLOT(magnifyingGlassSwitch()));
+
+ setBookmarkAction = new QAction(tr("Set bookmark"),this);
+ setBookmarkAction->setToolTip(tr("Set a bookmark on the current page"));
+ setBookmarkAction->setIcon(QIcon(":/images/viewer_toolbar/bookmark.png"));
+ setBookmarkAction->setDisabled(true);
+ 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(setBookmark(bool)));
+ connect(viewer,SIGNAL(pageAvailable(bool)),setBookmarkAction,SLOT(setEnabled(bool)));
+ connect(viewer,SIGNAL(pageIsBookmark(bool)),setBookmarkAction,SLOT(setChecked(bool)));
+
+ showBookmarksAction = new QAction(tr("Show bookmarks"),this);
+ showBookmarksAction->setToolTip(tr("Show the bookmarks of the current comic"));
+ showBookmarksAction->setIcon(QIcon(":/images/viewer_toolbar/showBookmarks.png"));
+ showBookmarksAction->setDisabled(true);
+ showBookmarksAction->setData(SHOW_BOOKMARKS_ACTION_Y);
+ showBookmarksAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_BOOKMARKS_ACTION_Y));
+ connect(showBookmarksAction, SIGNAL(triggered()),viewer->getBookmarksDialog(),SLOT(show()));
+
+ showShorcutsAction = new QAction(tr("Show keyboard shortcuts"), this );
+ showShorcutsAction->setIcon(QIcon(":/images/viewer_toolbar/shortcuts.png"));
+ showShorcutsAction->setData(SHOW_SHORCUTS_ACTION_Y);
+ showShorcutsAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_SHORCUTS_ACTION_Y));
+ //connect(showShorcutsAction, SIGNAL(triggered()),shortcutsDialog,SLOT(show()));
+ connect(showShorcutsAction, SIGNAL(triggered()), editShortcutsDialog, SLOT(show()));
+
+ showInfoAction = new QAction(tr("Show Info"),this);
+ showInfoAction->setIcon(QIcon(":/images/viewer_toolbar/info.png"));
+ showInfoAction->setDisabled(true);
+ showInfoAction->setData(SHOW_INFO_ACTION_Y);
+ showInfoAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_INFO_ACTION_Y));
+ connect(showInfoAction, SIGNAL(triggered()),viewer,SLOT(informationSwitch()));
+
+ closeAction = new QAction(tr("Close"),this);
+ closeAction->setIcon(QIcon(":/images/viewer_toolbar/close.png"));
+ closeAction->setData(CLOSE_ACTION_Y);
+ closeAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(CLOSE_ACTION_Y));
+ connect(closeAction,SIGNAL(triggered()),this,SLOT(close()));
+
+ showDictionaryAction = new QAction(tr("Show Dictionary"),this);
+ showDictionaryAction->setIcon(QIcon(":/images/viewer_toolbar/translator.png"));
+ //showDictionaryAction->setCheckable(true);
+ showDictionaryAction->setDisabled(true);
+ showDictionaryAction->setData(SHOW_DICTIONARY_ACTION_Y);
+ showDictionaryAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_DICTIONARY_ACTION_Y));
+ connect(showDictionaryAction,SIGNAL(triggered()),viewer,SLOT(translatorSwitch()));
+
+ //deprecated
+ alwaysOnTopAction = new QAction(tr("Always on top"),this);
+ alwaysOnTopAction->setIcon(QIcon(":/images/alwaysOnTop.png"));
+ alwaysOnTopAction->setCheckable(true);
+ alwaysOnTopAction->setDisabled(true);
+ alwaysOnTopAction->setChecked(Configuration::getConfiguration().getAlwaysOnTop());
+ alwaysOnTopAction->setData(ALWAYS_ON_TOP_ACTION_Y);
+ alwaysOnTopAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ALWAYS_ON_TOP_ACTION_Y));
+ connect(alwaysOnTopAction,SIGNAL(triggered()),this,SLOT(alwaysOnTopSwitch()));
+
+ showFlowAction = new QAction(tr("Show go to flow"),this);
+ showFlowAction->setIcon(QIcon(":/images/viewer_toolbar/flow.png"));
+ showFlowAction->setDisabled(true);
+ showFlowAction->setData(SHOW_FLOW_ACTION_Y);
+ showFlowAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_FLOW_ACTION_Y));
+ connect(showFlowAction,SIGNAL(triggered()),viewer,SLOT(goToFlowSwitch()));
+
+ showEditShortcutsAction = new QAction(tr("Edit shortcuts"),this);
+ showEditShortcutsAction->setData(SHOW_EDIT_SHORTCUTS_ACTION_Y);
+ showEditShortcutsAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SHOW_EDIT_SHORTCUTS_ACTION_Y));
+ connect(showEditShortcutsAction,SIGNAL(triggered()),editShortcutsDialog,SLOT(show()));
+}
+
+void MainWindowViewer::createToolBars()
+{
+#ifdef Q_OS_MAC
+ comicToolBar = new YACReaderMacOSXToolbar(this);
+#else
+ comicToolBar = addToolBar(tr("&File"));
+#endif
+
+#ifdef Q_OS_MAC
+ //comicToolBar->setIconSize(QSize(16,16));
+#else
+ comicToolBar->setIconSize(QSize(18,18));
+ comicToolBar->setStyleSheet("QToolBar{border:none;}");
+#endif
+
+#ifdef Q_OS_MAC
+ comicToolBar->addAction(openAction);
+ comicToolBar->addAction(openFolderAction);
+#else
+ QMenu * recentmenu = new QMenu(tr("Open recent"));
+ recentmenu->addActions(recentFilesActionList);
+ recentmenu->addSeparator();
+ recentmenu->addAction(clearRecentFilesAction);
+ refreshRecentFilesActionList();
+
+ QToolButton * tb = new QToolButton();
+ tb->addAction(openAction);
+ tb->addAction(openFolderAction);
+ tb->addAction(recentmenu->menuAction());
+ tb->setPopupMode(QToolButton::MenuButtonPopup);
+ tb->setDefaultAction(openAction);
+
+ comicToolBar->addWidget(tb);
+#endif
+ comicToolBar->addAction(saveImageAction);
+ comicToolBar->addAction(openPreviousComicAction);
+ comicToolBar->addAction(openNextComicAction);
+
+ comicToolBar->addSeparator();
+
+ comicToolBar->addAction(prevAction);
+ comicToolBar->addAction(nextAction);
+ comicToolBar->addAction(goToPageAction);
+
+ comicToolBar->addSeparator();
+
+ comicToolBar->addAction(adjustWidthAction);
+ comicToolBar->addAction(adjustHeightAction);
+ comicToolBar->addAction(adjustToFullSizeAction);
+ comicToolBar->addAction(fitToPageAction);
+
+ zoomSliderAction = new YACReaderSlider(this);
+ zoomSliderAction->hide();
+
+ comicToolBar->addAction(showZoomSliderlAction);
+
+ connect(showZoomSliderlAction,SIGNAL(triggered()),this,SLOT(toggleFitToWidthSlider()));
+ connect(zoomSliderAction, SIGNAL(zoomRatioChanged(int)),viewer,SLOT(updateZoomRatio(int)));
+ connect(viewer,SIGNAL(zoomUpdated(int)),zoomSliderAction,SLOT(updateZoomRatio(int)));
+
+ comicToolBar->addAction(leftRotationAction);
+ comicToolBar->addAction(rightRotationAction);
+ comicToolBar->addAction(doublePageAction);
+ comicToolBar->addAction(doubleMangaPageAction);
+
+ comicToolBar->addSeparator();
+
+ comicToolBar->addAction(showMagnifyingGlassAction);
+
+ comicToolBar->addSeparator();
+
+ comicToolBar->addAction(setBookmarkAction);
+ comicToolBar->addAction(showBookmarksAction);
+
+ comicToolBar->addSeparator();
+
+ comicToolBar->addAction(showDictionaryAction);
+ comicToolBar->addAction(showFlowAction);
+ comicToolBar->addAction(showInfoAction);
+
+#ifdef Q_OS_MAC
+ comicToolBar->addStretch();
+#else
+ comicToolBar->addWidget(new YACReaderToolBarStretch());
+#endif
+
+
+ comicToolBar->addAction(showShorcutsAction);
+ comicToolBar->addAction(optionsAction);
+ comicToolBar->addAction(helpAboutAction);
+ //comicToolBar->addAction(closeAction);
+
+#ifndef Q_OS_MAC
+ comicToolBar->setMovable(false);
+#endif
+
+ viewer->addAction(openAction);
+ viewer->addAction(openFolderAction);
+ viewer->addAction(saveImageAction);
+ viewer->addAction(openPreviousComicAction);
+ viewer->addAction(openNextComicAction);
+ YACReader::addSperator(viewer);
+
+ viewer->addAction(prevAction);
+ viewer->addAction(nextAction);
+ viewer->addAction(goToPageAction);
+ viewer->addAction(adjustHeightAction);
+ viewer->addAction(adjustWidthAction);
+ viewer->addAction(adjustToFullSizeAction);
+ viewer->addAction(fitToPageAction);
+ viewer->addAction(leftRotationAction);
+ viewer->addAction(rightRotationAction);
+ viewer->addAction(doublePageAction);
+ viewer->addAction(doubleMangaPageAction);
+ YACReader::addSperator(viewer);
+
+ viewer->addAction(showMagnifyingGlassAction);
+ viewer->addAction(increasePageZoomAction);
+ viewer->addAction(decreasePageZoomAction);
+ viewer->addAction(resetZoomAction);
+ YACReader::addSperator(viewer);
+
+ viewer->addAction(setBookmarkAction);
+ viewer->addAction(showBookmarksAction);
+ YACReader::addSperator(viewer);
+
+ viewer->addAction(showDictionaryAction);
+ viewer->addAction(showFlowAction);
+ viewer->addAction(showInfoAction);
+ YACReader::addSperator(viewer);
+
+ viewer->addAction(showShorcutsAction);
+ viewer->addAction(showEditShortcutsAction);
+ viewer->addAction(optionsAction);
+ viewer->addAction(helpAboutAction);
+ YACReader::addSperator(viewer);
+
+ viewer->addAction(closeAction);
+
+ viewer->setContextMenuPolicy(Qt::ActionsContextMenu);
+
+ //MacOSX app menus
+#ifdef Q_OS_MAC
+ QMenuBar * menuBar = this->menuBar();
+ //about / preferences
+ //TODO
+
+ //file
+ QMenu * fileMenu = new QMenu(tr("File"));
+
+ fileMenu->addAction(openAction);
+ fileMenu->addAction(openFolderAction);
+ fileMenu->addSeparator();
+ fileMenu->addAction(saveImageAction);
+
+ //tool bar
+ //QMenu * toolbarMenu = new QMenu(tr("Toolbar"));
+ //toolbarMenu->addAction();
+ //TODO
+
+ menuBar->addMenu(fileMenu);
+ //menu->addMenu(toolbarMenu);
+
+ //attach toolbar
+
+ comicToolBar->attachToWindow(this->windowHandle());
+
+#endif
+
+}
+
+void MainWindowViewer::refreshRecentFilesActionList()
+{
+ QStringList recentFilePaths = Configuration::getConfiguration().openRecentList();
+
+ //TODO: Replace limit with something configurable
+ int iteration = (recentFilePaths.size() < 10) ? recentFilePaths.size() : 10;
+
+ for (int i = 0; i < iteration; i++)
+ {
+ QString strippedName = QFileInfo(recentFilePaths.at(i)).fileName();
+ recentFilesActionList.at(i)->setText(strippedName);
+ recentFilesActionList.at(i)->setData(recentFilePaths.at(i));
+ recentFilesActionList.at(i)->setVisible(true);
+ }
+
+ for (int i = iteration; i < 10; i++)
+ {
+ recentFilesActionList.at(i)->setVisible(false);
+ }
+}
+
+void MainWindowViewer::clearRecentFiles()
+{
+ Configuration::getConfiguration().clearOpenRecentList();
+ refreshRecentFilesActionList();
+}
+
+void MainWindowViewer::openRecent()
+{
+ QAction *action = qobject_cast(sender());
+ if (action)
+ {
+ QFileInfo info1 (action->data().toString());
+ if (info1.exists())
+ {
+ if (info1.isFile())
+ {
+ openComicFromPath(action->data().toString());
+ }
+ else if (info1.isDir())
+ {
+ openFolderFromPath(action->data().toString());
+ }
+ }
+
+ }
+}
+
+void MainWindowViewer::reloadOptions()
+{
+ viewer->updateConfig(settings);
+}
+
+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);
+ }
+}
+
+void MainWindowViewer::open(QString path, ComicDB & comic, QList & siblings)
+{
+ //currentComicDB = comic;
+ //siblingComics = siblings;
+
+ QFileInfo fi(path);
+
+ if(!comic.info.title.isNull() && !comic.info.title.toString().isEmpty())
+ setWindowTitle("YACReader - " + comic.info.title.toString());
+ else
+ setWindowTitle("YACReader - " + fi.fileName());
+
+ viewer->open(path,comic);
+ enableActions();
+ int index = siblings.indexOf(comic);
+
+ optionsDialog->setFilters(currentComicDB.info.brightness, currentComicDB.info.contrast, currentComicDB.info.gamma);
+
+ if(index>0)
+ openPreviousComicAction->setDisabled(false);
+ else
+ openPreviousComicAction->setDisabled(true);
+
+ if(index+1setDisabled(false);
+ else
+ openNextComicAction->setDisabled(true);
+}
+
+void MainWindowViewer::open(QString path, qint64 comicId, qint64 libraryId)
+{
+ //QString pathFile = QCoreApplication::arguments().at(1);
+ currentDirectory = path;
+ //quint64 comicId = QCoreApplication::arguments().at(2).split("=").at(1).toULongLong();
+ //libraryId = QCoreApplication::arguments().at(3).split("=").at(1).toULongLong();
+ this->libraryId=libraryId;
+// this->path=path;
+
+ enableActions();
+
+ currentComicDB.id = comicId;
+ YACReaderLocalClient client;
+ int tries = 1;
+ bool success = false;
+ while(!(success = client.requestComicInfo(libraryId,currentComicDB,siblingComics)) && tries != 0)
+ tries--;
+
+ if(success)
+ {
+ isClient = true;
+ open(path+currentComicDB.path,currentComicDB,siblingComics);
+ }
+ else
+ {
+ isClient = false;
+ QMessageBox::information(this,"Connection Error", "Unable to connect to YACReaderLibrary");
+ //error
+ }
+
+ optionsDialog->setFilters(currentComicDB.info.brightness, currentComicDB.info.contrast, currentComicDB.info.gamma);
+}
+
+void MainWindowViewer::openComicFromPath(QString pathFile)
+{
+ openComic(pathFile);
+ isClient = false; //this method is used for direct openings
+}
+
+//isClient shouldn't be modified when a siblinig comic is opened
+void MainWindowViewer::openSiblingComic(QString pathFile)
+{
+ openComic(pathFile);
+}
+
+void MainWindowViewer::openComic(QString pathFile)
+{
+ QFileInfo fi(pathFile);
+ currentDirectory = fi.dir().absolutePath();
+ getSiblingComics(fi.absolutePath(),fi.fileName());
+
+ setWindowTitle("YACReader - " + fi.fileName());
+
+ enableActions();
+
+ viewer->open(pathFile);
+ Configuration::getConfiguration().updateOpenRecentList(pathFile);
+ refreshRecentFilesActionList();
+ }
+
+void MainWindowViewer::openFolder()
+{
+ QFileDialog openDialog;
+ QString pathDir = openDialog.getExistingDirectory(this,tr("Open folder"),currentDirectory);
+ if (!pathDir.isEmpty())
+ {
+ openFolderFromPath(pathDir);
+ isClient = false;
+ }
+}
+
+void MainWindowViewer::openFolderFromPath(QString pathDir)
+{
+ currentDirectory = pathDir; //TODO ??
+ QFileInfo fi(pathDir);
+ getSiblingComics(fi.absolutePath(),fi.fileName());
+
+ setWindowTitle("YACReader - " + fi.fileName());
+
+ enableActions();
+
+ viewer->open(pathDir);
+ Configuration::getConfiguration().updateOpenRecentList(pathDir);
+ refreshRecentFilesActionList();
+}
+
+void MainWindowViewer::openFolderFromPath(QString pathDir, QString atFileName)
+{
+ currentDirectory = pathDir; //TODO ??
+ QFileInfo fi(pathDir);
+ getSiblingComics(fi.absolutePath(),fi.fileName());
+
+ setWindowTitle("YACReader - " + fi.fileName());
+
+ enableActions();
+
+ QDir d(pathDir);
+ d.setFilter(QDir::Files|QDir::NoDotAndDotDot);
+ d.setNameFilters(Comic::getSupportedImageFormats());
+ d.setSorting(QDir::Name|QDir::IgnoreCase|QDir::LocaleAware);
+ QStringList list = d.entryList();
+
+ qSort(list.begin(),list.end(),naturalSortLessThanCI);
+ int i = 0;
+ foreach(QString path,list)
+ {
+ if(path.endsWith(atFileName))
+ break;
+ i++;
+ }
+
+ int index = 0;
+ if(i < list.count())
+ index = i;
+
+ viewer->open(pathDir,index);
+}
+
+void MainWindowViewer::saveImage()
+{
+ QFileDialog saveDialog;
+ QString pathFile = saveDialog.getSaveFileName(this,tr("Save current page"),currentDirectoryImgDest+"/"+tr("page_%1.jpg").arg(viewer->getIndex()),tr("Image files (*.jpg)"));
+ if (!pathFile.isEmpty())
+ {
+ QFileInfo fi(pathFile);
+ currentDirectoryImgDest = fi.absolutePath();
+ const QPixmap * p = viewer->pixmap();
+ if(p!=NULL)
+ p->save(pathFile);
+ }
+}
+
+void MainWindowViewer::enableActions()
+{
+ saveImageAction->setDisabled(false);
+ prevAction->setDisabled(false);
+ nextAction->setDisabled(false);
+ adjustHeightAction->setDisabled(false);
+ adjustWidthAction->setDisabled(false);
+ goToPageAction->setDisabled(false);
+ //alwaysOnTopAction->setDisabled(false);
+ leftRotationAction->setDisabled(false);
+ rightRotationAction->setDisabled(false);
+ showMagnifyingGlassAction->setDisabled(false);
+ doublePageAction->setDisabled(false);
+ doubleMangaPageAction->setDisabled(false);
+ adjustToFullSizeAction->setDisabled(false);
+ adjustToFullSizeAction->setDisabled(false);
+ fitToPageAction->setDisabled(false);
+ showZoomSliderlAction->setDisabled(false);
+ increasePageZoomAction->setDisabled(false);
+ decreasePageZoomAction->setDisabled(false);
+ resetZoomAction->setDisabled(false);
+ //setBookmark->setDisabled(false);
+ showBookmarksAction->setDisabled(false);
+ showInfoAction->setDisabled(false); //TODO enable goTo and showInfo (or update) when numPages emited
+ showDictionaryAction->setDisabled(false);
+ showFlowAction->setDisabled(false);
+}
+void MainWindowViewer::disableActions()
+{
+ saveImageAction->setDisabled(true);
+ prevAction->setDisabled(true);
+ nextAction->setDisabled(true);
+ adjustHeightAction->setDisabled(true);
+ adjustWidthAction->setDisabled(true);
+ goToPageAction->setDisabled(true);
+ //alwaysOnTopAction->setDisabled(true);
+ leftRotationAction->setDisabled(true);
+ rightRotationAction->setDisabled(true);
+ showMagnifyingGlassAction->setDisabled(true);
+ doublePageAction->setDisabled(true);
+ doubleMangaPageAction->setDisabled(true);
+ adjustToFullSizeAction->setDisabled(true);
+ fitToPageAction->setDisabled(true);
+ showZoomSliderlAction->setDisabled(true);
+ increasePageZoomAction->setDisabled(true);
+ decreasePageZoomAction->setDisabled(true);
+ resetZoomAction->setDisabled(true);
+ setBookmarkAction->setDisabled(true);
+ showBookmarksAction->setDisabled(true);
+ showInfoAction->setDisabled(true); //TODO enable goTo and showInfo (or update) when numPages emited
+ openPreviousComicAction->setDisabled(true);
+ openNextComicAction->setDisabled(true);
+ showDictionaryAction->setDisabled(true);
+ showFlowAction->setDisabled(true);
+}
+
+void MainWindowViewer::keyPressEvent(QKeyEvent *event)
+{
+ //TODO remove unused keys
+ int _key = event->key();
+ Qt::KeyboardModifiers modifiers = event->modifiers();
+
+ if(modifiers & Qt::ShiftModifier)
+ _key |= Qt::SHIFT;
+ if (modifiers & Qt::ControlModifier)
+ _key |= Qt::CTRL;
+ if (modifiers & Qt::MetaModifier)
+ _key |= Qt::META;
+ if (modifiers & Qt::AltModifier)
+ _key |= Qt::ALT;
+
+ QKeySequence key(_key);
+
+ if (key == ShortcutsManager::getShortcutsManager().getShortcut(TOGGLE_FULL_SCREEN_ACTION_Y))
+ {
+ toggleFullScreen();
+ event->accept();
+ }
+ else if (key == ShortcutsManager::getShortcutsManager().getShortcut(TOGGLE_TOOL_BARS_ACTION_Y))
+ {
+ toggleToolBars();
+ event->accept();
+ }
+ else if (key == ShortcutsManager::getShortcutsManager().getShortcut(CHANGE_FIT_ACTION_Y))
+ {
+ toggleWidthHeight();
+ event->accept();
+ }
+ else
+ QWidget::keyPressEvent(event);
+}
+
+void MainWindowViewer::mouseDoubleClickEvent ( QMouseEvent * event )
+{
+ toggleFullScreen();
+ event->accept();
+}
+
+void MainWindowViewer::toggleFullScreen()
+{
+ fullscreen?toNormal():toFullScreen();
+ Configuration::getConfiguration().setFullScreen(fullscreen = !fullscreen);
+}
+
+void MainWindowViewer::toFullScreen()
+{
+ fromMaximized = this->isMaximized();
+
+ hideToolBars();
+ viewer->hide();
+ viewer->fullscreen = true;//TODO, change by the right use of windowState();
+ setWindowState(Qt::WindowFullScreen);
+ viewer->show();
+ if(viewer->magnifyingGlassIsVisible())
+ viewer->showMagnifyingGlass();
+}
+
+void MainWindowViewer::toNormal()
+{
+ //show all
+ viewer->hide();
+ viewer->fullscreen = false;//TODO, change by the right use of windowState();
+ //viewer->hideMagnifyingGlass();
+ if(fromMaximized)
+ showMaximized();
+ else
+ showNormal();
+
+ if(Configuration::getConfiguration().getShowToolbars())
+ showToolBars();
+ viewer->show();
+ if(viewer->magnifyingGlassIsVisible())
+ viewer->showMagnifyingGlass();
+}
+
+void MainWindowViewer::toggleToolBars()
+{
+ toolbars?hideToolBars():showToolBars();
+
+ Configuration::getConfiguration().setShowToolbars(toolbars);
+#ifndef Q_OS_MAC
+ comicToolBar->setMovable(false);
+#endif
+}
+void MainWindowViewer::hideToolBars()
+{
+ //hide all
+ this->comicToolBar->hide();
+ toolbars = false;
+}
+
+void MainWindowViewer::showToolBars()
+{
+ this->comicToolBar->show();
+ toolbars = true;
+}
+void MainWindowViewer::fitToWidth()
+{
+ Configuration::getConfiguration().setFitMode(YACReader::FitMode::ToWidth);
+ viewer->setZoomFactor(100);
+ viewer->updatePage();
+}
+void MainWindowViewer::fitToHeight()
+{
+ Configuration::getConfiguration().setFitMode(YACReader::FitMode::ToHeight);
+ viewer->setZoomFactor(100);
+ viewer->updatePage();
+}
+
+void MainWindowViewer::toggleWidthHeight()
+{
+ //Only switch to "Fit to height" when we're in "Fit to width"
+ if (Configuration::getConfiguration().getFitMode() == YACReader::FitMode::ToWidth)
+ {
+ adjustHeightAction->trigger();
+ }
+ //Default to "Fit to width" in all other cases
+ else
+ {
+ adjustWidthAction->trigger();
+ }
+}
+void MainWindowViewer::checkNewVersion()
+{
+ Configuration & conf = Configuration::getConfiguration();
+ QDate lastCheck = conf.getLastVersionCheck();
+ QDate current = QDate::currentDate();
+ if(lastCheck.isNull() || lastCheck.daysTo(current) >= conf.getNumDaysBetweenVersionChecks())
+ {
+ versionChecker = new HttpVersionChecker();
+
+ connect(versionChecker,SIGNAL(newVersionDetected()),
+ this,SLOT(newVersion()));
+
+ QTimer * tT = new QTimer;
+ tT->setSingleShot(true);
+ connect(tT, SIGNAL(timeout()), versionChecker, SLOT(get()));
+ //versionChecker->get(); //TOD�
+ tT->start(100);
+
+ conf.setLastVersionCheck(current);
+ }
+}
+
+void MainWindowViewer::processReset()
+{
+ if(isClient)
+ {
+ if(siblingComics.count()>1)
+ {
+ bool openNextB = openNextComicAction->isEnabled();
+ bool openPrevB = openPreviousComicAction->isEnabled();
+ disableActions();
+ openNextComicAction->setEnabled(openNextB);
+ openPreviousComicAction->setEnabled(openPrevB);
+ }
+ else
+ disableActions();
+ }
+ else
+ disableActions();
+}
+
+void MainWindowViewer::setUpShortcutsManagement()
+{
+ //actions holder
+ QObject * orphanActions = new QObject;
+
+ QList allActions;
+ QList tmpList;
+
+
+ editShortcutsDialog->addActionsGroup(tr("Comics"),QIcon(":/images/shortcuts_group_comics.png"),
+ tmpList = QList()
+ << openAction
+ << openFolderAction
+ << saveImageAction
+ << openPreviousComicAction
+ << openNextComicAction);
+
+ allActions << tmpList;
+
+ //keys without actions (General)
+ QAction * toggleFullScreenAction = new QAction(tr("Toggle fullscreen mode"),orphanActions);
+ toggleFullScreenAction->setData(TOGGLE_FULL_SCREEN_ACTION_Y);
+ toggleFullScreenAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(TOGGLE_FULL_SCREEN_ACTION_Y));
+
+ QAction * toggleToolbarsAction = new QAction(tr("Hide/show toolbar"),orphanActions);
+ toggleToolbarsAction->setData(TOGGLE_TOOL_BARS_ACTION_Y);
+ toggleToolbarsAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(TOGGLE_TOOL_BARS_ACTION_Y));
+
+ editShortcutsDialog->addActionsGroup(tr("General"),QIcon(":/images/shortcuts_group_general.png"),
+ tmpList = QList()
+ << optionsAction
+ << helpAboutAction
+ << showShorcutsAction
+ << showInfoAction
+ << closeAction
+ << showDictionaryAction
+ << showFlowAction
+ << toggleFullScreenAction
+ << toggleToolbarsAction
+ << showEditShortcutsAction);
+
+ allActions << tmpList;
+
+ //keys without actions (MGlass)
+ QAction * sizeUpMglassAction = new QAction(tr("Size up magnifying glass"),orphanActions);
+ sizeUpMglassAction->setData(SIZE_UP_MGLASS_ACTION_Y);
+ sizeUpMglassAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SIZE_UP_MGLASS_ACTION_Y));
+
+ QAction * sizeDownMglassAction = new QAction(tr("Size down magnifying glass"),orphanActions);
+ sizeDownMglassAction->setData(SIZE_DOWN_MGLASS_ACTION_Y);
+ sizeDownMglassAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(SIZE_DOWN_MGLASS_ACTION_Y));
+
+ QAction * zoomInMglassAction = new QAction(tr("Zoom in magnifying glass"),orphanActions);
+ zoomInMglassAction->setData(ZOOM_IN_MGLASS_ACTION_Y);
+ zoomInMglassAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_IN_MGLASS_ACTION_Y));
+
+ QAction * zoomOutMglassAction = new QAction(tr("Zoom out magnifying glass"),orphanActions);
+ zoomOutMglassAction->setData(ZOOM_OUT_MGLASS_ACTION_Y);
+ zoomOutMglassAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(ZOOM_OUT_MGLASS_ACTION_Y));
+
+ editShortcutsDialog->addActionsGroup(tr("Magnifiying glass"),QIcon(":/images/shortcuts_group_mglass.png"),
+ tmpList = QList()
+ << showMagnifyingGlassAction
+ << sizeUpMglassAction
+ << sizeDownMglassAction
+ << zoomInMglassAction
+ << zoomOutMglassAction);
+
+ allActions << tmpList;
+
+ //keys without actions
+ QAction * toggleFitToScreenAction = new QAction(tr("Toggle between fit to width and fit to height"),orphanActions);
+ toggleFitToScreenAction->setData(CHANGE_FIT_ACTION_Y);
+ toggleFitToScreenAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(CHANGE_FIT_ACTION_Y));
+
+ editShortcutsDialog->addActionsGroup(tr("Page adjustement"),QIcon(":/images/shortcuts_group_page.png"),
+ tmpList = QList()
+ << adjustHeightAction
+ << adjustWidthAction
+ << toggleFitToScreenAction
+ << leftRotationAction
+ << rightRotationAction
+ << doublePageAction
+ << doubleMangaPageAction
+ << adjustToFullSizeAction
+ << increasePageZoomAction
+ << decreasePageZoomAction
+ << resetZoomAction);
+
+ allActions << tmpList;
+
+ QAction * autoScrollForwardAction = new QAction(tr("Autoscroll down"),orphanActions);
+ autoScrollForwardAction->setData(AUTO_SCROLL_FORWARD_ACTION_Y);
+ autoScrollForwardAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(AUTO_SCROLL_FORWARD_ACTION_Y));
+
+ QAction * autoScrollBackwardAction = new QAction(tr("Autoscroll up"),orphanActions);
+ autoScrollBackwardAction->setData(AUTO_SCROLL_BACKWARD_ACTION_Y);
+ autoScrollBackwardAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(AUTO_SCROLL_BACKWARD_ACTION_Y));
+
+ QAction * moveDownAction = new QAction(tr("Move down"),orphanActions);
+ moveDownAction->setData(MOVE_DOWN_ACTION_Y);
+ moveDownAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(MOVE_DOWN_ACTION_Y));
+
+ QAction * moveUpAction = new QAction(tr("Move up"),orphanActions);
+ moveUpAction->setData(MOVE_UP_ACTION_Y);
+ moveUpAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(MOVE_UP_ACTION_Y));
+
+ QAction * moveLeftAction = new QAction(tr("Move left"),orphanActions);
+ moveLeftAction->setData(MOVE_LEFT_ACTION_Y);
+ moveLeftAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(MOVE_LEFT_ACTION_Y));
+
+ QAction * moveRightAction = new QAction(tr("Move right"),orphanActions);
+ moveRightAction->setData(MOVE_RIGHT_ACTION_Y);
+ moveRightAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(MOVE_RIGHT_ACTION_Y));
+
+ QAction * goToFirstPageAction = new QAction(tr("Go to the first page"),orphanActions);
+ goToFirstPageAction->setData(GO_TO_FIRST_PAGE_ACTION_Y);
+ goToFirstPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(GO_TO_FIRST_PAGE_ACTION_Y));
+
+ QAction * goToLastPageAction = new QAction(tr("Go to the last page"),orphanActions);
+ goToLastPageAction->setData(GO_TO_LAST_PAGE_ACTION_Y);
+ goToLastPageAction->setShortcut(ShortcutsManager::getShortcutsManager().getShortcut(GO_TO_LAST_PAGE_ACTION_Y));
+
+ editShortcutsDialog->addActionsGroup(tr("Reading"),QIcon(":/images/shortcuts_group_reading.png"),
+ tmpList = QList()
+ << nextAction
+ << prevAction
+ << setBookmarkAction
+ << showBookmarksAction
+ << autoScrollForwardAction
+ << autoScrollBackwardAction
+ << moveDownAction
+ << moveUpAction
+ << moveLeftAction
+ << moveRightAction
+ << goToFirstPageAction
+ << goToLastPageAction
+ << goToPageAction);
+
+ allActions << tmpList;
+
+ ShortcutsManager::getShortcutsManager().registerActions(allActions);
+
+}
+
+void MainWindowViewer::toggleFitToWidthSlider()
+{
+ int y;
+
+#ifdef Q_OS_MAC
+ y = 0;
+#else
+ y = this->comicToolBar->frameSize().height();
+#endif
+
+ if(zoomSliderAction->isVisible())
+ {
+ zoomSliderAction->hide();
+ }
+ else
+ {
+ zoomSliderAction->move(250, y);
+ zoomSliderAction->show();
+ }
+}
+
+void MainWindowViewer::newVersion()
+{
+ QMessageBox msgBox;
+ msgBox.setText(tr("There is a new version available"));
+ msgBox.setInformativeText(tr("Do you want to download the new version?"));
+ msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Ignore | QMessageBox::No);
+ msgBox.setDefaultButton(QMessageBox::Yes);
+ msgBox.button(QMessageBox::Ignore)->setText(tr("Remind me in 14 days"));
+ msgBox.button(QMessageBox::No)->setText(tr("Not now"));
+ msgBox.setWindowFlags(Qt::WindowStaysOnTopHint);
+ msgBox.setModal(true);
+ int ret = msgBox.exec();
+
+ switch(ret)
+ {
+ case QMessageBox::Yes:
+ QDesktopServices::openUrl(QUrl("http://www.yacreader.com"));
+ break;
+ case QMessageBox::No:
+ Configuration::getConfiguration().setNumDaysBetweenVersionChecks(1);
+ break;
+ case QMessageBox::Ignore:
+ Configuration::getConfiguration().setNumDaysBetweenVersionChecks(14);
+ break;
+ }
+}
+
+void MainWindowViewer::closeEvent ( QCloseEvent * event )
+{
+ Q_UNUSED(event)
+
+ if(isClient)
+ sendComic();
+
+ viewer->save();
+ Configuration & conf = Configuration::getConfiguration();
+ if(!fullscreen && !isMaximized())
+ {
+ conf.setPos(pos());
+ conf.setSize(size());
+ }
+ conf.setMaximized(isMaximized());
+
+ emit (closed());
+}
+
+void MainWindowViewer::openPreviousComic()
+{
+ if(!siblingComics.isEmpty() && isClient)
+ {
+ sendComic();
+
+ int currentIndex = siblingComics.indexOf(currentComicDB);
+ if (currentIndex == -1)
+ return;
+ if(currentIndex-1 >= 0 && currentIndex-1 < siblingComics.count())
+ {
+ siblingComics[currentIndex] = currentComicDB; //updated
+ currentComicDB = siblingComics.at(currentIndex-1);
+ open(currentDirectory+currentComicDB.path,currentComicDB,siblingComics);
+ }
+ return;
+ }
+ if(!previousComicPath.isEmpty())
+ {
+ openSiblingComic(previousComicPath);
+ }
+}
+
+void MainWindowViewer::openNextComic()
+{
+ if(!siblingComics.isEmpty() && isClient)
+ {
+ sendComic();
+
+ int currentIndex = siblingComics.indexOf(currentComicDB);
+ if (currentIndex == -1)
+ return;
+ if(currentIndex+1 > 0 && currentIndex+1 < siblingComics.count())
+ {
+ siblingComics[currentIndex] = currentComicDB; //updated
+ currentComicDB = siblingComics.at(currentIndex+1);
+ open(currentDirectory+currentComicDB.path,currentComicDB,siblingComics);
+ }
+ return;
+ }
+ if(!nextComicPath.isEmpty())
+ {
+ openSiblingComic(nextComicPath);
+ }
+}
+
+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);
+ //std::sort(list.begin(),list.end(),naturalSortLessThanCI);
+ int index = list.indexOf(currentComic);
+ if(index == -1) //comic not found
+ {
+ /*QFile f(QCoreApplication::applicationDirPath()+"/errorLog.txt");
+ if(!f.open(QIODevice::WriteOnly))
+ {
+ QMessageBox::critical(NULL,tr("Saving error log file...."),tr("There was a problem saving YACReader error log file. Please, check if you have enough permissions in the YACReader root folder."));
+ }
+ else
+ {
+ QTextStream txtS(&f);
+ txtS << "METHOD : MainWindowViewer::getSiblingComics" << '\n';
+ txtS << "ERROR : current comic not found in its own path" << '\n';
+ txtS << path << '\n';
+ txtS << currentComic << '\n';
+ txtS << "Comic list count : " + list.count() << '\n';
+ foreach(QString s, list){
+ txtS << s << '\n';
+ }
+ f.close();
+ }*/
+ }
+
+ previousComicPath = nextComicPath = "";
+ if(index>0)
+ {
+ previousComicPath = path+"/"+list.at(index-1);
+ openPreviousComicAction->setDisabled(false);
+ }
+ else
+ openPreviousComicAction->setDisabled(true);
+
+ if(index+1setDisabled(false);
+ }
+ else
+ openNextComicAction->setDisabled(true);
+}
+
+void MainWindowViewer::dropEvent(QDropEvent *event)
+{
+ QList urlList;
+ QString fName;
+ QFileInfo info;
+
+ if (event->mimeData()->hasUrls())
+ {
+ urlList = event->mimeData()->urls();
+
+ if ( urlList.size() > 0 )
+ {
+ fName = urlList[0].toLocalFile(); // convert first QUrl to local path
+ info.setFile( fName ); // information about file
+ if (info.isFile())
+ {
+ QStringList imageSuffixs = Comic::getSupportedImageLiteralFormats();
+ if(imageSuffixs.contains(info.suffix())) //image dropped
+ openFolderFromPath(info.absoluteDir().absolutePath(),info.fileName());
+ else
+ openComicFromPath(fName); // if is file, setText
+ }
+ else
+ if(info.isDir())
+ openFolderFromPath(fName);
+
+ isClient = false;
+ }
+ }
+
+ event->acceptProposedAction();
+}
+void MainWindowViewer::dragEnterEvent(QDragEnterEvent *event)
+{
+ // accept just text/uri-list mime format
+ if (event->mimeData()->hasFormat("text/uri-list"))
+ {
+ event->acceptProposedAction();
+ isClient = false;
+ }
+}
+
+void MainWindowViewer::alwaysOnTopSwitch()
+{
+ if(!Configuration::getConfiguration().getAlwaysOnTop())
+ {
+ setWindowFlags(this->windowFlags() | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint); //always on top
+ show();
+ }
+ else
+ {
+ setWindowFlags(this->windowFlags() ^ (Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint));
+ show();
+ }
+ Configuration::getConfiguration().setAlwaysOnTop(!Configuration::getConfiguration().getAlwaysOnTop());
+}
+
+void MainWindowViewer::adjustToFullSizeSwitch()
+{
+ Configuration::getConfiguration().setFitMode(YACReader::FitMode::FullRes);
+ viewer->setZoomFactor(100);
+ viewer->updatePage();
+}
+
+void MainWindowViewer::fitToPageSwitch()
+{
+ Configuration::getConfiguration().setFitMode(YACReader::FitMode::FullPage);
+ viewer->setZoomFactor(100);
+ viewer->updatePage();
+}
+
+void MainWindowViewer::resetZoomLevel()
+{
+ viewer->setZoomFactor(100);
+ viewer->updatePage();
+}
+
+void MainWindowViewer::increasePageZoomLevel()
+{
+ viewer->increaseZoomFactor();
+}
+
+void MainWindowViewer::decreasePageZoomLevel()
+{
+ viewer->decreaseZoomFactor();
+}
+
+void MainWindowViewer::sendComic()
+{
+ YACReaderLocalClient * client = new YACReaderLocalClient;
+ currentComicDB.info.hasBeenOpened = true;
+ viewer->updateComic(currentComicDB);
+ int retries = 1;
+ while(!client->sendComicInfo(libraryId,currentComicDB) && retries!=0)
+ retries--;
+ connect(client,SIGNAL(finished()),client,SLOT(deleteLater()));
+ //delete client;
+}
diff --git a/YACReader/main_window_viewer.h b/YACReader/main_window_viewer.h
new file mode 100644
index 00000000..beaef34e
--- /dev/null
+++ b/YACReader/main_window_viewer.h
@@ -0,0 +1,179 @@
+#ifndef __MAIN_WINDOW_VIEWER_H
+#define __MAIN_WINDOW_VIEWER_H
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef Q_OS_MAC
+ #include "yacreader_macosx_toolbar.h"
+#endif
+
+#include "comic_db.h"
+
+class Comic;
+class Viewer;
+class OptionsDialog;
+class HelpAboutDialog;
+class HttpVersionChecker;
+class ShortcutsDialog;
+class YACReaderSliderAction;
+class YACReaderSlider;
+class EditShortcutsDialog;
+
+ class MainWindowViewer : public QMainWindow
+ {
+ Q_OBJECT
+
+ public slots:
+ void open();
+ void open(QString path, ComicDB & comic, QList & siblings);
+ void open(QString path, qint64 comicId, qint64 libraryId);
+ void openFolder();
+ void openRecent();
+ void saveImage();
+ void toggleToolBars();
+ void hideToolBars();
+ void showToolBars();
+ void enableActions();
+ void disableActions();
+ void toggleFullScreen();
+ void toFullScreen();
+ void toNormal();
+ void loadConfiguration();
+ void newVersion();
+ void openPreviousComic();
+ void openNextComic();
+ void openComicFromPath(QString pathFile);
+ void openSiblingComic(QString pathFile);
+ void openComic(QString pathFile);
+ void openFolderFromPath(QString pathDir);
+ void openFolderFromPath(QString pathFile, QString atFileName);
+ void alwaysOnTopSwitch();
+ void adjustToFullSizeSwitch();
+ void fitToPageSwitch();
+ void resetZoomLevel();
+ void increasePageZoomLevel();
+ void decreasePageZoomLevel();
+ void reloadOptions();
+ void fitToWidth();
+ void fitToHeight();
+ void toggleWidthHeight();
+ void checkNewVersion();
+ void processReset();
+ void setUpShortcutsManagement();
+
+ void toggleFitToWidthSlider();
+
+ /*void viewComic();
+ void prev();
+ void next();
+ void updatePage();*/
+
+
+ private:
+ //!State
+ bool fullscreen;
+ bool toolbars;
+ bool alwaysOnTop;
+ bool fromMaximized;
+
+ //QTBUG-41883
+ QSize _size;
+ QPoint _pos;
+
+ QString currentDirectory;
+ QString currentDirectoryImgDest;
+ //!Widgets
+ Viewer * viewer;
+ //GoToDialog * goToDialog;
+ OptionsDialog * optionsDialog;
+ HelpAboutDialog * had;
+ //ShortcutsDialog * shortcutsDialog;
+ EditShortcutsDialog * editShortcutsDialog;
+
+ //! ToolBars
+ #ifdef Q_OS_MAC
+ YACReaderMacOSXToolbar * comicToolBar;
+#else
+ QToolBar * comicToolBar;
+#endif
+
+ //! Actions
+ QAction *openAction;
+ QAction *openFolderAction;
+ QList recentFilesActionList;
+ QAction *clearRecentFilesAction;
+ QAction *saveImageAction;
+ QAction *openPreviousComicAction;
+ QAction *openNextComicAction;
+ QAction *nextAction;
+ QAction *prevAction;
+ QAction *adjustWidthAction;
+ QAction *adjustHeightAction;
+ QAction *goToPageAction;
+ QAction *optionsAction;
+ QAction *helpAboutAction;
+ QAction *showMagnifyingGlassAction;
+ QAction *setBookmarkAction;
+ QAction *showBookmarksAction;
+ QAction *leftRotationAction;
+ QAction *rightRotationAction;
+ QAction *showInfoAction;
+ QAction *closeAction;
+ QAction *doublePageAction;
+ QAction *doubleMangaPageAction;
+ QAction *showShorcutsAction;
+ QAction *showDictionaryAction;
+ QAction *alwaysOnTopAction;
+ QAction *adjustToFullSizeAction;
+ QAction *fitToPageAction;
+ QAction *resetZoomAction;
+ QAction *showZoomSliderlAction;
+ QAction *increasePageZoomAction;
+ QAction *decreasePageZoomAction;
+ QAction *showFlowAction;
+
+ QAction *showEditShortcutsAction;
+
+ YACReaderSlider * zoomSliderAction;
+
+ HttpVersionChecker * versionChecker;
+ QString previousComicPath;
+ QString nextComicPath;
+ //! Método que inicializa el interfaz.
+ void setupUI();
+ void createActions();
+ void createToolBars();
+ void refreshRecentFilesActionList();
+ void clearRecentFiles();
+ void getSiblingComics(QString path,QString currentComic);
+
+ //! Manejadores de evento:
+ void keyPressEvent(QKeyEvent *event);
+ //void resizeEvent(QResizeEvent * event);
+ void mouseDoubleClickEvent ( QMouseEvent * event );
+ void dropEvent(QDropEvent *event);
+ void dragEnterEvent(QDragEnterEvent *event);
+
+ QSettings * settings;
+
+ ComicDB currentComicDB;
+ QList siblingComics;
+ bool isClient;
+ QString startComicPath;
+ quint64 libraryId;
+signals:
+ void closed();
+ protected:
+ virtual void closeEvent ( QCloseEvent * event );
+ void sendComic();
+ public:
+ MainWindowViewer();
+ ~MainWindowViewer();
+ };
+#endif
diff --git a/YACReader/notifications_label_widget.cpp b/YACReader/notifications_label_widget.cpp
new file mode 100644
index 00000000..a1767dfd
--- /dev/null
+++ b/YACReader/notifications_label_widget.cpp
@@ -0,0 +1,76 @@
+#include "notifications_label_widget.h"
+
+#include
+
+NotificationsLabelWidget::NotificationsLabelWidget(QWidget * parent)
+ :QWidget(parent)
+{
+ QVBoxLayout *layout = new QVBoxLayout;
+ layout->setSpacing(0);
+ layout->setMargin(0);
+
+ setAttribute(Qt::WA_LayoutUsesWidgetRect,true);
+ effect = new QGraphicsOpacityEffect(this);
+ effect->setOpacity(1.0);
+
+ anim = new QPropertyAnimation(effect,"opacity");
+ anim->setDuration(500);
+ anim->setStartValue(1.0);
+ anim->setEndValue(0.0);
+ anim->setEasingCurve(QEasingCurve::InExpo);
+
+ connect(anim,SIGNAL(finished()),this,SLOT(hide()));
+
+ textLabel = new QLabel(this);
+ textLabel->setAlignment(Qt::AlignVCenter|Qt::AlignHCenter);
+ textLabel->setStyleSheet("QLabel { color : white; font-size:24px; }");
+ textLabel->setAttribute(Qt::WA_LayoutUsesWidgetRect,true);
+
+ textLabel->setFixedSize(200, 120);
+
+ //TODO check if the effects still be broken in OSX yet
+#ifndef Q_OS_MAC
+ this->setGraphicsEffect(effect);
+#endif
+
+ layout->addWidget(textLabel);
+ setLayout(layout);
+
+ setFixedSize(200, 120);
+ updatePosition();
+}
+
+void NotificationsLabelWidget::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+ QPainterPath path;
+ path.addRoundedRect(QRectF(0, 0, width(), height()), 5.0, 5.0);
+ painter.setPen(Qt::NoPen);
+ painter.fillPath(path, QColor("#BB000000"));
+ painter.drawPath(path);
+}
+
+void NotificationsLabelWidget::flash()
+{
+ updatePosition();
+ anim->stop();
+ anim->start();
+
+ setVisible(true);
+}
+
+void NotificationsLabelWidget::setText(const QString & text)
+{
+ textLabel->setText(text);
+}
+
+void NotificationsLabelWidget::updatePosition()
+{
+ QWidget * parent = dynamic_cast(this->parent());
+ if(parent == 0)
+ {
+ return;
+ }
+ move(QPoint((parent->geometry().size().width()-this->width())/2,(parent->geometry().size().height()-this->height())/2));
+}
diff --git a/YACReader/notifications_label_widget.h b/YACReader/notifications_label_widget.h
new file mode 100644
index 00000000..12ded1ac
--- /dev/null
+++ b/YACReader/notifications_label_widget.h
@@ -0,0 +1,30 @@
+#ifndef NOTIFICATIONS_LABEL_WIDGET_H
+#define NOTIFICATIONS_LABEL_WIDGET_H
+
+#include
+
+class QLabel;
+class QPropertyAnimation;
+class QGraphicsOpacityEffect;
+
+class NotificationsLabelWidget : public QWidget
+{
+Q_OBJECT
+private:
+ QLabel * textLabel;
+ QPropertyAnimation * anim;
+ QGraphicsOpacityEffect * effect;
+
+protected:
+ void paintEvent(QPaintEvent *);
+
+public:
+ NotificationsLabelWidget(QWidget * parent);
+
+public slots:
+ void flash();
+ void setText(const QString & text);
+ void updatePosition();
+};
+
+#endif
diff --git a/YACReader/options_dialog.cpp b/YACReader/options_dialog.cpp
new file mode 100644
index 00000000..77f1d4e9
--- /dev/null
+++ b/YACReader/options_dialog.cpp
@@ -0,0 +1,312 @@
+#include "options_dialog.h"
+#include "configuration.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "yacreader_spin_slider_widget.h"
+#include "yacreader_flow_config_widget.h"
+#ifndef NO_OPENGL
+#include "yacreader_gl_flow_config_widget.h"
+#endif
+
+OptionsDialog::OptionsDialog(QWidget * parent)
+:YACReaderOptionsDialog(parent)
+{
+
+ QTabWidget * tabWidget = new QTabWidget();
+
+ QVBoxLayout * layout = new QVBoxLayout(this);
+
+ QWidget * pageGeneral = new QWidget();
+ QWidget * pageFlow = new QWidget();
+ QWidget * pageImage = new QWidget();
+ QVBoxLayout * layoutGeneral = new QVBoxLayout();
+ QVBoxLayout * layoutFlow = new QVBoxLayout();
+ QVBoxLayout * layoutImageV = new QVBoxLayout();
+ QGridLayout * layoutImage = new QGridLayout();
+
+ QGroupBox *slideSizeBox = new QGroupBox(tr("\"Go to flow\" size"));
+ //slideSizeLabel = new QLabel(,this);
+ slideSize = new QSlider(this);
+ slideSize->setMinimum(125);
+ slideSize->setMaximum(350);
+ slideSize->setPageStep(5);
+ slideSize->setOrientation(Qt::Horizontal);
+ QHBoxLayout * slideLayout = new QHBoxLayout();
+ slideLayout->addWidget(slideSize);
+ slideSizeBox->setLayout(slideLayout);
+
+ QGroupBox *pathBox = new QGroupBox(tr("My comics path"));
+
+ QHBoxLayout * path = new QHBoxLayout();
+ path->addWidget(pathEdit = new QLineEdit());
+ path->addWidget(pathFindButton = new QPushButton(QIcon(":/images/find_folder.png"),""));
+ pathBox->setLayout(path);
+
+ connect(pathFindButton,SIGNAL(clicked()),this,SLOT(findFolder()));
+
+ //fitToWidthRatioLabel = new QLabel(tr("Page width stretch"),this);
+ /*QGroupBox *fitBox = new QGroupBox(tr("Page width stretch"));
+ fitToWidthRatioS = new QSlider(this);
+ fitToWidthRatioS->setMinimum(50);
+ fitToWidthRatioS->setMaximum(100);
+ fitToWidthRatioS->setPageStep(5);
+ fitToWidthRatioS->setOrientation(Qt::Horizontal);
+ //connect(fitToWidthRatioS,SIGNAL(valueChanged(int)),this,SLOT(fitToWidthRatio(int)));
+ QHBoxLayout * fitLayout = new QHBoxLayout;
+ fitLayout->addWidget(fitToWidthRatioS);
+ fitBox->setLayout(fitLayout);*/
+
+ QHBoxLayout * colorSelection = new QHBoxLayout;
+ backgroundColor = new QLabel();
+ QPalette pal = backgroundColor->palette();
+ pal.setColor(backgroundColor->backgroundRole(), Qt::black);
+ backgroundColor->setPalette(pal);
+ backgroundColor->setAutoFillBackground(true);
+
+ colorDialog = new QColorDialog(Qt::red,this);
+ connect(colorDialog,SIGNAL(colorSelected(QColor)),this,SLOT(updateColor(QColor)));
+
+ QGroupBox *colorBox = new QGroupBox(tr("Background color"));
+ //backgroundColor->setMinimumWidth(100);
+ colorSelection->addWidget(backgroundColor);
+ colorSelection->addWidget(selectBackgroundColorButton = new QPushButton(tr("Choose")));
+ colorSelection->setStretchFactor(backgroundColor,1);
+ colorSelection->setStretchFactor(selectBackgroundColorButton,0);
+ //colorSelection->addStretch();
+ connect(selectBackgroundColorButton, SIGNAL(clicked()), colorDialog, SLOT(show()));
+ colorBox->setLayout(colorSelection);
+
+ brightnessS = new YACReaderSpinSliderWidget(this,true);
+ brightnessS->setRange(0,100);
+ //brightnessS->setText(tr("Brightness"));
+ brightnessS->setTracking(false);
+ connect(brightnessS,SIGNAL(valueChanged(int)),this,SLOT(brightnessChanged(int)));
+
+ contrastS = new YACReaderSpinSliderWidget(this,true);
+ contrastS->setRange(0,250);
+ //contrastS->setText(tr("Contrast"));
+ contrastS->setTracking(false);
+ connect(contrastS,SIGNAL(valueChanged(int)),this,SLOT(contrastChanged(int)));
+
+ gammaS = new YACReaderSpinSliderWidget(this,true);
+ gammaS->setRange(0,250);
+ //gammaS->setText(tr("Gamma"));
+ gammaS->setTracking(false);
+ connect(gammaS,SIGNAL(valueChanged(int)),this,SLOT(gammaChanged(int)));
+ //connect(brightnessS,SIGNAL(valueChanged(int)),this,SIGNAL(changedOptions()));
+
+ QHBoxLayout * buttons = new QHBoxLayout();
+ buttons->addStretch();
+ buttons->addWidget(new QLabel(tr("Restart is needed")));
+ buttons->addWidget(accept);
+ buttons->addWidget(cancel);
+
+ layoutGeneral->addWidget(pathBox);
+ layoutGeneral->addWidget(slideSizeBox);
+ //layoutGeneral->addWidget(fitBox);
+ layoutGeneral->addWidget(colorBox);
+ layoutGeneral->addWidget(shortcutsBox);
+ layoutGeneral->addStretch();
+ layoutFlow->addWidget(sw);
+#ifndef NO_OPENGL
+ layoutFlow->addWidget(gl);
+ layoutFlow->addWidget(useGL);
+#endif
+ layoutFlow->addStretch();
+ layoutImage->addWidget(new QLabel(tr("Brightness")),0,0);
+ layoutImage->addWidget(new QLabel(tr("Contrast")),1,0);
+ layoutImage->addWidget(new QLabel(tr("Gamma")),2,0);
+ layoutImage->addWidget(brightnessS,0,1);
+ layoutImage->addWidget(contrastS,1,1);
+ layoutImage->addWidget(gammaS,2,1);
+ QPushButton * pushButton = new QPushButton(tr("Reset"));
+ connect(pushButton,SIGNAL(pressed()),this,SLOT(resetImageConfig()));
+ layoutImage->addWidget(pushButton,3,0);
+ layoutImage->setColumnStretch(1,1);
+
+
+ QGroupBox *imageBox = new QGroupBox(tr("Image options"));
+ imageBox->setLayout(layoutImage);
+ layoutImageV->addWidget(imageBox);
+ layoutImageV->addStretch();
+
+
+ pageGeneral->setLayout(layoutGeneral);
+ pageFlow->setLayout(layoutFlow);
+ pageImage->setLayout(layoutImageV);
+
+ tabWidget->addTab(pageGeneral,tr("General"));
+ tabWidget->addTab(pageFlow,tr("Page Flow"));
+ tabWidget->addTab(pageImage,tr("Image adjustment"));
+
+ layout->addWidget(tabWidget);
+ layout->addLayout(buttons);
+
+ setLayout(layout);
+
+ //disable vSyncCheck
+#ifndef NO_OPENGL
+ gl->vSyncCheck->hide();
+#endif
+ //restoreOptions(); //load options
+ //resize(400,0);
+ setModal (true);
+ setWindowTitle(tr("Options"));
+
+ this->layout()->setSizeConstraint(QLayout::SetFixedSize);
+}
+
+void OptionsDialog::findFolder()
+{
+ QString s = QFileDialog::getExistingDirectory(0,tr("Comics directory"),".");
+ if(!s.isEmpty())
+ {
+ pathEdit->setText(s);
+ }
+}
+
+void OptionsDialog::saveOptions()
+{
+
+ settings->setValue(GO_TO_FLOW_SIZE,QSize(static_cast(slideSize->sliderPosition()/SLIDE_ASPECT_RATIO),slideSize->sliderPosition()));
+
+ if(sw->radio1->isChecked())
+ settings->setValue(FLOW_TYPE_SW,0);
+ if(sw->radio2->isChecked())
+ settings->setValue(FLOW_TYPE_SW,1);
+ if(sw->radio3->isChecked())
+ settings->setValue(FLOW_TYPE_SW,2);
+
+ settings->setValue(PATH,pathEdit->text());
+
+ settings->setValue(BACKGROUND_COLOR,colorDialog->currentColor());
+ //settings->setValue(FIT_TO_WIDTH_RATIO,fitToWidthRatioS->sliderPosition()/100.0);
+
+ YACReaderOptionsDialog::saveOptions();
+}
+
+void OptionsDialog::restoreOptions(QSettings * settings)
+{
+ YACReaderOptionsDialog::restoreOptions(settings);
+
+ slideSize->setSliderPosition(settings->value(GO_TO_FLOW_SIZE).toSize().height());
+ switch(settings->value(FLOW_TYPE_SW).toInt())
+ {
+ case 0:
+ sw->radio1->setChecked(true);
+ break;
+ case 1:
+ sw->radio2->setChecked(true);
+ break;
+ case 2:
+ sw->radio3->setChecked(true);
+ break;
+ default:
+ sw->radio1->setChecked(true);
+ break;
+ }
+
+ pathEdit->setText(settings->value(PATH).toString());
+
+ updateColor(settings->value(BACKGROUND_COLOR).value());
+ //fitToWidthRatioS->setSliderPosition(settings->value(FIT_TO_WIDTH_RATIO).toFloat()*100);
+
+ brightnessS->setValue(settings->value(BRIGHTNESS,0).toInt());
+ contrastS->setValue(settings->value(CONTRAST,100).toInt());
+ gammaS->setValue(settings->value(GAMMA,100).toInt());
+}
+
+
+void OptionsDialog::updateColor(const QColor & color)
+{
+ QPalette pal = backgroundColor->palette();
+ pal.setColor(backgroundColor->backgroundRole(), color);
+ backgroundColor->setPalette(pal);
+ backgroundColor->setAutoFillBackground(true);
+ colorDialog->setCurrentColor(color);
+
+ settings->setValue(BACKGROUND_COLOR,color);
+
+ emit(changedOptions());
+}
+
+/*void OptionsDialog::fitToWidthRatio(int value)
+{
+ Configuration::getConfiguration().setFitToWidthRatio(value/100.0);
+ emit(fitToWidthRatioChanged(value/100.0));
+}*/
+
+void OptionsDialog::brightnessChanged(int value)
+{
+ QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
+ settings.setValue(BRIGHTNESS,value);
+ emit changedFilters(brightnessS->getValue(), contrastS->getValue(), gammaS->getValue());
+ //emit(changedImageOptions());
+}
+
+void OptionsDialog::contrastChanged(int value)
+{
+ QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
+ settings.setValue(CONTRAST,value);
+ emit changedFilters(brightnessS->getValue(), contrastS->getValue(), gammaS->getValue());
+ ///emit(changedImageOptions());
+}
+
+void OptionsDialog::gammaChanged(int value)
+{
+ QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
+ settings.setValue(GAMMA,value);
+ emit changedFilters(brightnessS->getValue(), contrastS->getValue(), gammaS->getValue());
+ //emit(changedImageOptions());
+}
+
+void OptionsDialog::resetImageConfig()
+{
+ brightnessS->setValue(0);
+ contrastS->setValue(100);
+ gammaS->setValue(100);
+ QSettings settings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
+ settings.setValue(BRIGHTNESS,0);
+ settings.setValue(CONTRAST,100);
+ settings.setValue(GAMMA,100);
+ emit changedFilters(brightnessS->getValue(), contrastS->getValue(), gammaS->getValue());
+ //emit(changedImageOptions());
+}
+
+void OptionsDialog::show()
+{
+ //TODO solucionar el tema de las settings, esto sólo deberÃa aparecer en una única lÃnea de código
+ QSettings *s = new QSettings(YACReader::getSettingsPath()+"/YACReader.ini",QSettings::IniFormat);
+ //fitToWidthRatioS->disconnect();
+ //fitToWidthRatioS->setSliderPosition(settings->value(FIT_TO_WIDTH_RATIO).toFloat()*100);
+ //connect(fitToWidthRatioS,SIGNAL(valueChanged(int)),this,SLOT(fitToWidthRatio(int)));
+ QDialog::show();
+ delete s;
+}
+
+void OptionsDialog::setFilters(int brightness, int contrast, int gamma)
+{
+ if(brightness != -1)
+ brightnessS->setValue(brightness);
+ else
+ brightnessS->setValue(0);
+ if(contrast != -1)
+ contrastS->setValue(contrast);
+ else
+ contrastS->setValue(100);
+ if(gamma != -1)
+ gammaS->setValue(gamma);
+ else
+ gammaS->setValue(100);
+
+}
diff --git a/YACReader/options_dialog.h b/YACReader/options_dialog.h
new file mode 100644
index 00000000..767b28fd
--- /dev/null
+++ b/YACReader/options_dialog.h
@@ -0,0 +1,70 @@
+#ifndef __OPTIONS_DIALOG_H
+#define __OPTIONS_DIALOG_H
+
+#include "yacreader_options_dialog.h"
+
+class QDialog;
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QSlider;
+class QPushButton;
+class QRadioButton;
+class QColorDialog;
+class YACReaderSpinSliderWidget;
+
+
+class OptionsDialog : public YACReaderOptionsDialog
+{
+Q_OBJECT
+ public:
+ OptionsDialog(QWidget * parent = 0);
+ private:
+ //QLabel * pathLabel;
+ QLineEdit * pathEdit;
+ QPushButton * pathFindButton;
+
+ QLabel * magGlassSizeLabel;
+
+ QLabel * zoomLevel;
+
+ //QLabel * slideSizeLabel;
+ QSlider * slideSize;
+
+ //QLabel * fitToWidthRatioLabel;
+ //QSlider * fitToWidthRatioS;
+
+ QLabel * backgroundColor;
+ QPushButton * selectBackgroundColorButton;
+
+ QColorDialog * colorDialog;
+
+ YACReaderSpinSliderWidget * brightnessS;
+
+ YACReaderSpinSliderWidget * contrastS;
+
+ YACReaderSpinSliderWidget * gammaS;
+
+ public slots:
+ void saveOptions();
+ void restoreOptions(QSettings * settings);
+ void findFolder();
+ void updateColor(const QColor & color);
+ //void fitToWidthRatio(int value);
+ void brightnessChanged(int value);
+ void contrastChanged(int value);
+ void gammaChanged(int value);
+ void resetImageConfig();
+ void show();
+ void setFilters(int brightness, int contrast, int gamma);
+
+signals:
+ void changedOptions();
+ void changedImageOptions();
+ void changedFilters(int brightness, int contrast, int gamma);
+ //void fitToWidthRatioChanged(float ratio);
+
+};
+
+
+#endif
diff --git a/YACReader/page_label_widget.cpp b/YACReader/page_label_widget.cpp
new file mode 100644
index 00000000..19a12c9b
--- /dev/null
+++ b/YACReader/page_label_widget.cpp
@@ -0,0 +1,105 @@
+#include "page_label_widget.h"
+
+#include
+
+PageLabelWidget::PageLabelWidget(QWidget * parent)
+ :QWidget(parent)
+{
+ animation = new QPropertyAnimation(this,"pos");
+ animation->setDuration(150);
+ animation->setEndValue(QPoint((parent->geometry().size().width()-this->width()),-this->height()));
+
+ int verticalRes = QApplication::desktop()->screenGeometry().height();
+
+ QHBoxLayout *layout = new QHBoxLayout;
+ layout->setMargin(0);
+ setContentsMargins(0,0,0,0);
+
+ QSize labelSize;
+ if (verticalRes <= 1024)
+ labelSize = QSize(135, 30);
+ else if (verticalRes <= 1200)
+ labelSize = QSize(170, 35);
+ else
+ labelSize = QSize(205, 45);
+
+ textLabel = new QLabel(this);
+ textLabel->setAlignment(Qt::AlignVCenter|Qt::AlignHCenter);
+ if(verticalRes <= 1024)
+ textLabel->setStyleSheet("QLabel { color : white; font-size:12px; padding-left:8px; }");
+ else if (verticalRes <= 1200)
+ textLabel->setStyleSheet("QLabel { color : white; font-size:16px; padding-left:8px;}");
+ else
+ textLabel->setStyleSheet("QLabel { color : white; font-size:20px; padding-left:8px; }");
+
+ setFixedSize(labelSize);
+
+ if(parent != 0)
+ move(QPoint((parent->geometry().size().width()-this->width()),-this->height()));
+
+ layout->addWidget(textLabel, 0 , Qt::AlignCenter);
+ setLayout(layout);
+}
+
+void PageLabelWidget::show()
+{
+ if(this->pos().y() <= 0 && animation->state()!=QPropertyAnimation::Running)
+ {
+ QWidget * parent = dynamic_cast(this->parent());
+ if(parent == 0)
+ {
+ return;
+ }
+
+ QWidget::show();
+ //connect(animation,SIGNAL(finished()),this,SLOT(QWidget::hide()));
+ animation->disconnect();
+
+ animation->setStartValue(QPoint((parent->geometry().size().width()-this->width()),-this->height()));
+ animation->setEndValue(QPoint((parent->geometry().size().width()-this->width()),0));
+ animation->start();
+ }
+}
+
+void PageLabelWidget::hide()
+{
+ if(this->pos().y() >= 0 && animation->state()!=QPropertyAnimation::Running)
+ {
+ QWidget * parent = dynamic_cast(this->parent());
+ if(parent == 0)
+ {
+ return;
+ }
+ //connect(animation,SIGNAL(finished()),this,SLOT(setHidden()));
+ animation->setStartValue(QPoint((parent->geometry().size().width()-this->width()),0));
+ animation->setEndValue(QPoint((parent->geometry().size().width()-this->width()),-this->height()));
+ animation->start();
+ }
+}
+
+void PageLabelWidget::setText(const QString & text)
+{
+ textLabel->setText(text);
+}
+
+void PageLabelWidget::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+
+ painter.fillRect(0,0,width(),height(),QColor("#BB000000"));
+}
+
+void PageLabelWidget::updatePosition()
+{
+ QWidget * parent = dynamic_cast(this->parent());
+ if(parent == 0)
+ {
+ return;
+ }
+
+ animation->stop();
+ if (animation->endValue().toPoint().y() == 0)
+ move(QPoint((parent->geometry().size().width()-this->width()),0));
+ else
+ move(QPoint((parent->geometry().size().width()-this->width()),-this->height()));
+}
diff --git a/YACReader/page_label_widget.h b/YACReader/page_label_widget.h
new file mode 100644
index 00000000..5d70d74f
--- /dev/null
+++ b/YACReader/page_label_widget.h
@@ -0,0 +1,29 @@
+#ifndef PAGE_LABEL_WIDGET_H
+#define PAGE_LABEL_WIDGET_H
+
+#include
+
+class QLabel;
+class QPropertyAnimation;
+
+class PageLabelWidget : public QWidget
+{
+Q_OBJECT
+private:
+ QLabel * textLabel;
+ QPropertyAnimation * animation;
+
+protected:
+ virtual void paintEvent(QPaintEvent *);
+
+public:
+ PageLabelWidget(QWidget * parent);
+
+public slots:
+ void show();
+ void hide();
+ void setText(const QString & text);
+ void updatePosition();
+};
+
+#endif
diff --git a/YACReader/render.cpp b/YACReader/render.cpp
new file mode 100644
index 00000000..ff4177c9
--- /dev/null
+++ b/YACReader/render.cpp
@@ -0,0 +1,1186 @@
+#include "render.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "comic_db.h"
+#include "yacreader_global_gui.h"
+
+template
+inline const T& kClamp( const T& x, const T& low, const T& high )
+{
+ if ( x < low ) return low;
+ else if ( high < x ) return high;
+ else return x;
+}
+
+inline
+int changeBrightness( int value, int brightness )
+ {
+ return kClamp( value + brightness * 255 / 100, 0, 255 );
+ }
+
+inline
+int changeContrast( int value, int contrast )
+ {
+ return kClamp((( value - 127 ) * contrast / 100 ) + 127, 0, 255 );
+ }
+
+inline
+int changeGamma( int value, int gamma )
+ {
+ return kClamp( int( pow( value / 255.0, 100.0 / gamma ) * 255 ), 0, 255 );
+ }
+
+inline
+int changeUsingTable( int value, const int table[] )
+ {
+ return table[ value ];
+ }
+
+template< int operation( int, int ) >
+static
+QImage changeImage( const QImage& image, int value )
+ {
+ QImage im = image;
+ im.detach();
+ if( im.colorCount() == 0 ) /* truecolor */
+ {
+ if( im.format() != QImage::Format_RGB32 ) /* just in case */
+ im = im.convertToFormat( QImage::Format_RGB32 );
+ int table[ 256 ];
+ for( int i = 0;
+ i < 256;
+ ++i )
+ table[ i ] = operation( i, value );
+ if( im.hasAlphaChannel() )
+ {
+ for( int y = 0;
+ y < im.height();
+ ++y )
+ {
+ QRgb* line = reinterpret_cast< QRgb* >( im.scanLine( y ));
+ for( int x = 0;
+ x < im.width();
+ ++x )
+ line[ x ] = qRgba( changeUsingTable( qRed( line[ x ] ), table ),
+ changeUsingTable( qGreen( line[ x ] ), table ),
+ changeUsingTable( qBlue( line[ x ] ), table ),
+ changeUsingTable( qAlpha( line[ x ] ), table ));
+ }
+ }
+ else
+ {
+ for( int y = 0;
+ y < im.height();
+ ++y )
+ {
+ QRgb* line = reinterpret_cast< QRgb* >( im.scanLine( y ));
+ for( int x = 0;
+ x < im.width();
+ ++x )
+ line[ x ] = qRgb( changeUsingTable( qRed( line[ x ] ), table ),
+ changeUsingTable( qGreen( line[ x ] ), table ),
+ changeUsingTable( qBlue( line[ x ] ), table ));
+ }
+ }
+ }
+ else
+ {
+ QVector colors = im.colorTable();
+ for( int i = 0;
+ i < im.colorCount();
+ ++i )
+ colors[ i ] = qRgb( operation( qRed( colors[ i ] ), value ),
+ operation( qGreen( colors[ i ] ), value ),
+ operation( qBlue( colors[ i ] ), value ));
+ im.setColorTable(colors);
+ }
+ return im;
+ }
+
+
+// brightness is multiplied by 100 in order to avoid floating point numbers
+QImage changeBrightness( const QImage& image, int brightness )
+ {
+ if( brightness == 0 ) // no change
+ return image;
+ return changeImage< changeBrightness >( image, brightness );
+ }
+
+
+// contrast is multiplied by 100 in order to avoid floating point numbers
+QImage changeContrast( const QImage& image, int contrast )
+ {
+ if( contrast == 100 ) // no change
+ return image;
+ return changeImage< changeContrast >( image, contrast );
+ }
+
+// gamma is multiplied by 100 in order to avoid floating point numbers
+QImage changeGamma( const QImage& image, int gamma )
+ {
+ if( gamma == 100 ) // no change
+ return image;
+ return changeImage< changeGamma >( image, gamma );
+ }
+
+
+
+//-----------------------------------------------------------------------------
+// MeanNoiseReductionFilter
+//-----------------------------------------------------------------------------
+
+MeanNoiseReductionFilter::MeanNoiseReductionFilter(enum NeighborghoodSize ns)
+:neighborghoodSize(ns)
+{
+
+}
+
+QImage MeanNoiseReductionFilter::setFilter(const QImage & image)
+{
+ int width = image.width();
+ int height = image.height();
+ QImage result(width,height,image.format());
+ int filterSize = sqrt((float)neighborghoodSize);
+ int bound = filterSize/2;
+ QRgb pix;
+ int r,g,b;
+ for(int j=bound;j redChannel;
+ QList greenChannel;
+ QList blueChannel;
+ for(int j=bound;j hist(256,0);
+
+ for(int j=0;j 1; i--)
+ {
+ new_count += hist[i];
+ percentage = new_count/count;
+ next_percentage = (new_count+hist[i-1])/count;
+ if(fabs (percentage - 0.006) < fabs (next_percentage - 0.006))
+ {
+ max = i-1;
+ break;
+ }
+ }
+ QColor c;
+ int range = max - min;
+ for(int j=0;j f)
+:QThread(),
+render(r),
+numPage(np),
+data(rd),
+page(p),
+degrees(d),
+filters(f)
+{
+}
+
+void PageRender::run()
+{
+ QMutexLocker locker(&(render->mutex));
+
+ QImage img;
+ img.loadFromData(data);
+ if(degrees > 0)
+ {
+ QMatrix m;
+ m.rotate(degrees);
+ img = img.transformed(m,Qt::SmoothTransformation);
+ }
+ for(int i=0;isetFilter(img);
+ }
+
+
+ *page = img;
+
+ emit pageReady(numPage);
+}
+
+//-----------------------------------------------------------------------------
+// Render
+//-----------------------------------------------------------------------------
+
+Render::Render()
+:currentIndex(0),doublePage(false),doubleMangaPage(false),comic(0),loadedComic(false),imageRotation(0),numLeftPages(4),numRightPages(4)
+{
+ int size = numLeftPages+numRightPages+1;
+ currentPageBufferedIndex = numLeftPages;
+ for(int i = 0; imoveToThread(QApplication::instance()->thread());
+ comic->deleteLater();
+ }
+
+ foreach(ImageFilter * filter, filters)
+ delete filter;
+
+ foreach(PageRender * pr,pageRenders)
+ if(pr !=0)
+ {
+ if(pr->wait())
+ 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.
+void Render::render()
+{
+ updateBuffer();
+ if(buffer[currentPageBufferedIndex]->isNull())
+ {
+ if(pagesReady.size()>0)
+ {
+ if(pagesReady[currentIndex])
+ {
+ 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
+
+ //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
+ 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Ã
+ 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.
+ emit processingPage();
+ }
+ else
+ //no hay ninguna página lista para ser renderizada, es necesario esperar.
+ emit processingPage();
+ }
+ else
+ // la página actual está lista
+ {
+ //emit currentPageReady();
+ //make prepareAvailablePage the only function that emits currentPageReady()
+ prepareAvailablePage(currentIndex);
+ }
+ fillBuffer();
+}
+
+QPixmap * Render::getCurrentPage()
+{
+ QPixmap * page = new QPixmap();
+ *page = page->fromImage(*buffer[currentPageBufferedIndex]);
+ return page;
+}
+
+QPixmap * Render::getCurrentDoublePage()
+{
+ if (currentPageIsDoublePage())
+ {
+ QPoint leftpage(0,0);
+ QPoint rightpage(0,0);
+ QSize leftsize = buffer[currentPageBufferedIndex]->size();
+ QSize rightsize = buffer[currentPageBufferedIndex+1]->size();
+ int totalWidth,totalHeight;
+ switch (imageRotation)
+ {
+ case 0:
+ totalHeight = qMax(leftsize.rheight(),rightsize.rheight());
+ leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ totalWidth = leftsize.rwidth() + rightsize.rwidth();
+ rightpage.setX(leftsize.rwidth());
+ break;
+ case 90:
+ totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth());
+ leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ totalHeight = leftsize.rheight() + rightsize.rheight();
+ rightpage.setY(leftsize.rheight());
+ break;
+ case 180:
+ totalHeight = qMax(leftsize.rheight(),rightsize.rheight());
+ leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ totalWidth = leftsize.rwidth() + rightsize.rwidth();
+ leftpage.setX(rightsize.rwidth());
+ break;
+ case 270:
+ totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth());
+ leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ totalHeight = leftsize.rheight() + rightsize.rheight();
+ leftpage.setY(rightsize.rheight());
+ break;
+ default:
+ return NULL;
+ }
+ QPixmap * page = new QPixmap(totalWidth, totalHeight);
+ QPainter painter(page);
+ painter.drawImage(QRect(leftpage,leftsize), *buffer[currentPageBufferedIndex]);
+ painter.drawImage(QRect(rightpage,rightsize), *buffer[currentPageBufferedIndex+1]);
+ return page;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+QPixmap * Render::getCurrentDoubleMangaPage()
+{
+ if (currentPageIsDoublePage())
+ {
+ QPoint leftpage(0,0);
+ QPoint rightpage(0,0);
+ QSize leftsize = buffer[currentPageBufferedIndex+1]->size();
+ QSize rightsize = buffer[currentPageBufferedIndex]->size();
+ int totalWidth,totalHeight;
+ switch (imageRotation)
+ {
+ case 0:
+ totalHeight = qMax(leftsize.rheight(),rightsize.rheight());
+ leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ totalWidth = leftsize.rwidth() + rightsize.rwidth();
+ rightpage.setX(leftsize.rwidth());
+ break;
+ case 90:
+ totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth());
+ leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ totalHeight = leftsize.rheight() + rightsize.rheight();
+ rightpage.setY(leftsize.rheight());
+ break;
+ case 180:
+ totalHeight = qMax(leftsize.rheight(),rightsize.rheight());
+ leftsize.scale(leftsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(rightsize.rwidth(), totalHeight, Qt::KeepAspectRatioByExpanding);
+ totalWidth = leftsize.rwidth() + rightsize.rwidth();
+ leftpage.setX(rightsize.rwidth());
+ break;
+ case 270:
+ totalWidth = qMax(leftsize.rwidth(), rightsize.rwidth());
+ leftsize.scale(totalWidth, leftsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ rightsize.scale(totalWidth, rightsize.rheight(), Qt::KeepAspectRatioByExpanding);
+ totalHeight = leftsize.rheight() + rightsize.rheight();
+ leftpage.setY(rightsize.rheight());
+ break;
+ default:
+ return NULL;
+ }
+ QPixmap * page = new QPixmap(totalWidth, totalHeight);
+ QPainter painter(page);
+ painter.drawImage(QRect(rightpage, rightsize), *buffer[currentPageBufferedIndex]);
+ painter.drawImage(QRect(leftpage, leftsize), *buffer[currentPageBufferedIndex+1]);
+ return page;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+bool Render::currentPageIsDoublePage()
+{
+ if (buffer[currentPageBufferedIndex]->isNull() || buffer[currentPageBufferedIndex+1]->isNull())
+ {
+ return false;
+ }
+ if (imageRotation == 0 || imageRotation == 180)
+ {
+ if (buffer[currentPageBufferedIndex]->height() > buffer[currentPageBufferedIndex]->width() &&
+ buffer[currentPageBufferedIndex+1]->height() > buffer[currentPageBufferedIndex+1]->width())
+ {
+ return true;
+ }
+ }
+ else if (imageRotation == 90 || imageRotation == 270)
+ {
+ if (buffer[currentPageBufferedIndex]->width() > buffer[currentPageBufferedIndex]->height() &&
+ buffer[currentPageBufferedIndex+1]->width() > buffer[currentPageBufferedIndex+1]->height())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Render::nextPageIsDoublePage()
+{
+ //this function is not used right now
+ if (buffer[currentPageBufferedIndex+2]->isNull() || buffer[currentPageBufferedIndex+3]->isNull())
+ {
+ return false;
+ }
+ if (imageRotation == 0 || imageRotation == 180)
+ {
+ if (buffer[currentPageBufferedIndex+2]->height() > buffer[currentPageBufferedIndex+2]->width() &&
+ buffer[currentPageBufferedIndex+3]->height() > buffer[currentPageBufferedIndex+3]->width())
+ {
+ return true;
+ }
+ }
+ else if (imageRotation == 90 || imageRotation == 270)
+ {
+ if (buffer[currentPageBufferedIndex]->width() > buffer[currentPageBufferedIndex]->height() &&
+ buffer[currentPageBufferedIndex+1]->width() > buffer[currentPageBufferedIndex+1]->height())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Render::previousPageIsDoublePage()
+{
+ if (buffer[currentPageBufferedIndex-1]->isNull() || buffer[currentPageBufferedIndex-2]->isNull())
+ {
+ return false;
+ }
+ if (imageRotation == 0 || imageRotation == 180)
+ {
+ if (buffer[currentPageBufferedIndex-1]->height() > buffer[currentPageBufferedIndex-1]->width() &&
+ buffer[currentPageBufferedIndex-2]->height() > buffer[currentPageBufferedIndex-2]->width())
+ {
+ return true;
+ }
+ }
+ else if (imageRotation == 90 || imageRotation == 270)
+ {
+ if (buffer[currentPageBufferedIndex-1]->width() > buffer[currentPageBufferedIndex-1]->height() &&
+ buffer[currentPageBufferedIndex-2]->width() > buffer[currentPageBufferedIndex-2]->height())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Render::setRotation(int degrees)
+{
+ Q_UNUSED(degrees)
+}
+
+void Render::setComic(Comic * c)
+{
+ if(comic !=0)
+ {
+ comic->moveToThread(QApplication::instance()->thread());
+ comic->disconnect();
+ comic->deleteLater();
+ }
+ comic = c;
+}
+
+void Render::prepareAvailablePage(int page)
+{
+ if(!doublePage)
+ {
+ if (currentIndex == page)
+ {
+ emit currentPageReady();
+ }
+ }
+ else
+ {
+ //check for last page in double page mode
+ if ((currentIndex == page) && (currentIndex + 1) >= (int)comic->numPages())
+ {
+ emit currentPageReady();
+ }
+ else if ((currentIndex == page && !buffer[currentPageBufferedIndex+1]->isNull()) ||
+ (currentIndex+1 == page && !buffer[currentPageBufferedIndex]->isNull()))
+ {
+ emit currentPageReady();
+ }
+ }
+}
+
+void Render::update()
+{
+ render();
+}
+//-----------------------------------------------------------------------------
+// Comic interface
+//-----------------------------------------------------------------------------
+void Render::load(const QString & path, int atPage)
+{
+ createComic(path);
+ if (comic !=0)
+ {
+ loadComic(path,atPage);
+ startLoad();
+ }
+}
+
+//-----------------------------------------------------------------------------
+void Render::load(const QString & path, const ComicDB & comicDB)
+{
+ //TODO prepare filters
+ for(int i = 0; i < filters.count(); i++)
+ {
+ if(typeid(*filters[i]) == typeid(BrightnessFilter))
+ if(comicDB.info.brightness == -1)
+ filters[i]->setLevel(0);
+ else
+ filters[i]->setLevel(comicDB.info.brightness);
+ if(typeid(*filters[i]) == typeid(ContrastFilter))
+ if(comicDB.info.contrast == -1)
+ filters[i]->setLevel(100);
+ else
+ filters[i]->setLevel(comicDB.info.contrast);
+ if(typeid(*filters[i]) == typeid(GammaFilter))
+ if(comicDB.info.gamma == -1)
+ filters[i]->setLevel(100);
+ else
+ filters[i]->setLevel(comicDB.info.gamma);
+ }
+ createComic(path);
+ if (comic!=0)
+ {
+ loadComic(path,comicDB);
+ startLoad();
+ }
+}
+
+void Render::createComic(const QString & path)
+{
+ if(comic!=0)
+ {
+ //comic->moveToThread(QApplication::instance()->thread());
+ comic->disconnect();
+ comic->deleteLater();
+ }
+ //comic->moveToThread(QApplication::instance()->thread());
+ comic = FactoryComic::newComic(path);
+
+
+ if(comic == NULL)//archivo no encontrado o no válido
+ {
+ emit errorOpening();
+ reset();
+ return;
+ }
+
+ previousIndex = currentIndex = 0;
+
+ connect(comic,SIGNAL(errorOpening()),this,SIGNAL(errorOpening()));
+ connect(comic,SIGNAL(errorOpening(QString)),this,SIGNAL(errorOpening(QString)));
+ connect(comic,SIGNAL(crcErrorFound(QString)),this,SIGNAL(crcError(QString)));
+ connect(comic,SIGNAL(errorOpening()),this,SLOT(reset()));
+ connect(comic,SIGNAL(imageLoaded(int)),this,SIGNAL(imageLoaded(int)));
+ connect(comic,SIGNAL(imageLoaded(int)),this,SLOT(pageRawDataReady(int)));
+ connect(comic,SIGNAL(openAt(int)),this,SLOT(renderAt(int)));
+ connect(comic,SIGNAL(numPages(unsigned int)),this,SIGNAL(numPages(unsigned int)));
+ 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(bookmarksUpdated()),this,SIGNAL(bookmarksUpdated()));
+
+ //connect(comic,SIGNAL(isLast()),this,SIGNAL(isLast()));
+ //connect(comic,SIGNAL(isCover()),this,SIGNAL(isCover()));
+
+ pagesReady.clear();
+}
+void Render::loadComic(const QString & path,const ComicDB & comicDB)
+{
+ comic->load(path,comicDB);
+}
+void Render::loadComic(const QString & path, int atPage)
+{
+ comic->load(path,atPage);
+}
+
+void Render::startLoad()
+{
+ QThread * thread = NULL;
+
+ thread = new QThread();
+
+ 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()));
+
+ if(thread != NULL)
+ thread->start();
+
+ invalidate();
+ loadedComic = true;
+ update();
+}
+
+void Render::renderAt(int page)
+{
+ previousIndex = currentIndex = page;
+ emit pageChanged(page);
+}
+
+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.
+void Render::nextPage()
+{
+ int nextPage; //indica cuál será la próxima página
+ nextPage = comic->nextPage();
+ //se fuerza renderizado si la página ha cambiado
+ if(currentIndex != nextPage)
+ {
+ previousIndex = currentIndex;
+ currentIndex = nextPage;
+ update();
+ emit pageChanged(currentIndex);
+ }
+ else if (hasLoadedComic() && (currentIndex == numPages()-1))
+ {
+ emit isLast();
+ }
+}
+void Render::nextDoublePage()
+{
+ int nextPage;
+ if (currentIndex +2 < (int)comic->numPages())
+ {
+ nextPage = currentIndex+2;
+ }
+ else
+ {
+ nextPage = currentIndex;
+ }
+ if(currentIndex != nextPage)
+ {
+ comic->setIndex(nextPage);
+ previousIndex = currentIndex;
+ currentIndex = nextPage;
+ update();
+ emit pageChanged(currentIndex);
+ }
+ else if (hasLoadedComic() && ((unsigned int)currentIndex >= numPages()-2))
+ {
+ emit isLast();
+ }
+}
+
+//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
+ previousPage = comic->previousPage();
+
+ //se fuerza renderizado si la página ha cambiado
+ if(currentIndex != previousPage)
+ {
+ previousIndex = currentIndex;
+ currentIndex = previousPage;
+ update();
+ emit pageChanged(currentIndex);
+ }
+ else if (hasLoadedComic() && (currentIndex == 0))
+ {
+ emit isCover();
+ }
+}
+
+void Render::previousDoublePage()
+{
+ int previousPage; //indica cuál será la próxima página
+ previousPage = qMax(currentIndex-2,0);
+ if(currentIndex != previousPage)
+ {
+ comic->setIndex(previousPage);
+ previousIndex = currentIndex;
+ currentIndex = previousPage;
+ update();
+ emit pageChanged(currentIndex);
+ }
+}
+
+unsigned int Render::getIndex()
+{
+ return comic->getIndex();
+}
+unsigned int Render::numPages()
+{
+ return comic->numPages();
+}
+
+bool Render::hasLoadedComic()
+{
+ if(comic!=0)
+ return comic->loaded();
+ return false;
+}
+
+void Render::setNumPages(unsigned int numPages)
+{
+ pagesReady.fill(false,numPages);
+}
+
+void Render::pageRawDataReady(int page)
+{
+ if (!hasLoadedComic())
+ return;
+
+ pagesEmited.push_back(page);
+ if(pageRenders.size()>0)
+ {
+ for(int i=0;i= pagesReady.size())
+ return; //Oooops, something went wrong
+
+ pagesReady[pagesEmited.at(i)] = true;
+ if(pagesEmited.at(i) == currentIndex)
+ update();
+ else
+ {
+ if ( ((pagesEmited.at(i) < currentIndex) && (pagesEmited.at(i) > currentIndex-numLeftPages)) ||
+ ((pagesEmited.at(i) > currentIndex) && (pagesEmited.at(i) < currentIndex+numRightPages)) )
+ {
+ fillBuffer();
+ }
+ }
+ }
+ pagesEmited.clear();
+ }
+}
+
+//sólo se renderiza la página, si ha habido un cambio de página
+void Render::goTo(int index)
+{
+
+ if(currentIndex != index)
+ {
+ comic->setIndex(index);
+ previousIndex = currentIndex;
+ currentIndex = index;
+ update();
+ emit pageChanged(currentIndex);
+ }
+}
+
+void Render::rotateRight()
+{
+ imageRotation = (imageRotation+90) % 360;
+ reload();
+}
+void Render::rotateLeft()
+{
+ if(imageRotation == 0)
+ imageRotation = 270;
+ else
+ imageRotation = imageRotation - 90;
+ 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)
+void Render::updateBuffer()
+{
+ QMutexLocker locker(&mutex);
+ int windowSize = currentIndex - previousIndex;
+
+ if(windowSize > 0)//add pages to right pages and remove on the left
+ {
+ windowSize = qMin(windowSize,buffer.size());
+ for(int i = 0; i < windowSize; i++)
+ {
+ //renders
+ PageRender * pr = pageRenders.front();
+ pageRenders.pop_front();
+ if(pr !=0)
+ {
+ if(pr->wait())
+ delete pr;
+ }
+ pageRenders.push_back(0);
+
+ //images
+
+ if(buffer.front()!=0)
+ delete buffer.front();
+ buffer.pop_front();
+ buffer.push_back(new QImage());
+ }
+ }
+ else //add pages to left pages and remove on the right
+ if(windowSize<0)
+ {
+ windowSize = -windowSize;
+ windowSize = qMin(windowSize,buffer.size());
+ for(int i = 0; i < windowSize; i++)
+ {
+ //renders
+ PageRender * pr = pageRenders.back();
+ pageRenders.pop_back();
+ if(pr !=0)
+ {
+ if(pr->wait())
+ delete pr;
+ }
+ pageRenders.push_front(0);
+
+ //images
+ buffer.push_front(new QImage());
+ QImage * p = buffer.back();
+ if(p!=0)
+ delete p;
+ buffer.pop_back();
+ }
+ }
+ previousIndex = currentIndex;
+}
+
+void Render::fillBuffer()
+{
+ for(int i = 1; i <= qMax(numLeftPages,numRightPages); i++)
+ {
+ if ((currentIndex+i < (int)comic->numPages()) &&
+ buffer[currentPageBufferedIndex+i]->isNull() &&
+ i <= numRightPages &&
+ pageRenders[currentPageBufferedIndex+i]==0 &&
+ pagesReady[currentIndex+i]) //preload next pages
+ {
+ pageRenders[currentPageBufferedIndex+i] = new PageRender(this,currentIndex+i,comic->getRawData()->at(currentIndex+i),buffer[currentPageBufferedIndex+i],imageRotation,filters);
+ connect(pageRenders[currentPageBufferedIndex+i],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
+ pageRenders[currentPageBufferedIndex+i]->start();
+ }
+
+ if ((currentIndex-i > 0) &&
+ buffer[currentPageBufferedIndex-i]->isNull() &&
+ i <= numLeftPages &&
+ pageRenders[currentPageBufferedIndex-i]==0 &&
+ pagesReady[currentIndex-i]) //preload previous pages
+ {
+ pageRenders[currentPageBufferedIndex-i] = new PageRender(this,currentIndex-i,comic->getRawData()->at(currentIndex-i),buffer[currentPageBufferedIndex-i],imageRotation,filters);
+ connect(pageRenders[currentPageBufferedIndex-i],SIGNAL(pageReady(int)),this,SLOT(prepareAvailablePage(int)));
+ pageRenders[currentPageBufferedIndex-i]->start();
+ }
+ }
+}
+
+
+//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;iwait();
+ delete pageRenders[i];
+ pageRenders[i] = 0;
+ }
+ }
+
+ for(int i=0;inumPages()))
+ {
+ if (currentPageIsDoublePage())
+ {
+ if (doubleMangaPage)
+ s = QString::number(currentIndex+2) + "-" + s;
+ else
+ s += "-"+QString::number(currentIndex+2);
+ }
+ }
+ s += "/"+QString::number(comic->numPages());
+ return s;
+}
+
+void Render::setBookmark()
+{
+ comic->setBookmark();
+}
+
+void Render::removeBookmark()
+{
+ comic->removeBookmark();
+}
+
+void Render::save()
+{
+ comic->saveBookmarks();
+}
+
+Bookmarks * Render::getBookmarks()
+{
+ return comic->bm;
+}
+
+void Render::reload()
+{
+ if(comic)
+ {
+ invalidate();
+ update();
+ }
+}
+
+void Render::updateFilters(int brightness, int contrast, int gamma)
+{
+ for(int i = 0; i < filters.count(); i++)
+ {
+ if(typeid(*filters[i]) == typeid(BrightnessFilter))
+ filters[i]->setLevel(brightness);
+ if(typeid(*filters[i]) == typeid(ContrastFilter))
+ filters[i]->setLevel(contrast);
+ if(typeid(*filters[i]) == typeid(GammaFilter))
+ filters[i]->setLevel(gamma);
+ }
+
+ reload();
+}
diff --git a/YACReader/render.h b/YACReader/render.h
new file mode 100644
index 00000000..d231be38
--- /dev/null
+++ b/YACReader/render.h
@@ -0,0 +1,216 @@
+ #ifndef RENDER_H
+#define RENDER_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "comic.h"
+//-----------------------------------------------------------------------------
+// FILTERS
+//-----------------------------------------------------------------------------
+
+#include
+
+class Comic;
+class ComicDB;
+class Render;
+
+class ImageFilter {
+public:
+ ImageFilter(){};
+ virtual ~ImageFilter() {};
+ virtual QImage setFilter(const QImage & image) = 0;
+ inline int getLevel() {return level;};
+ inline void setLevel(int l) {level = l;};
+protected:
+ int level;
+};
+
+class MeanNoiseReductionFilter : public ImageFilter {
+public:
+ enum NeighborghoodSize{SMALL=9, LARGE=25 };
+ MeanNoiseReductionFilter(enum NeighborghoodSize ns = SMALL);
+ virtual QImage setFilter(const QImage & image);
+private:
+ enum NeighborghoodSize neighborghoodSize;
+};
+
+class MedianNoiseReductionFilter : public ImageFilter {
+public:
+ enum NeighborghoodSize{SMALL=9, LARGE=25 };
+ MedianNoiseReductionFilter(enum NeighborghoodSize ns = SMALL);
+ virtual QImage setFilter(const QImage & image);
+private:
+ enum NeighborghoodSize neighborghoodSize;
+};
+
+class BrightnessFilter : public ImageFilter {
+public:
+ BrightnessFilter(int l=-1);
+ virtual QImage setFilter(const QImage & image);
+};
+
+class ContrastFilter : public ImageFilter {
+public:
+ ContrastFilter(int l=-1);
+ virtual QImage setFilter(const QImage & image);
+};
+
+class GammaFilter : public ImageFilter {
+public:
+ GammaFilter(int l=-1);
+ virtual QImage setFilter(const QImage & image);
+};
+
+//-----------------------------------------------------------------------------
+// RENDER
+//-----------------------------------------------------------------------------
+
+class PageRender : public QThread
+{
+ Q_OBJECT
+public:
+ PageRender();
+ PageRender(Render * render,int numPage, const QByteArray & rawData, QImage * page,unsigned int degrees=0, QVector filters = QVector());
+ int getNumPage(){return numPage;};
+ void setData(const QByteArray & rawData){data = rawData;};
+ void setPage(QImage * p){page = p;};
+ void setRotation(unsigned int d){degrees = d;};
+ void setFilters(QVector f){filters = f;};
+private:
+ int numPage;
+ QByteArray data;
+ QImage * page;
+ unsigned int degrees;
+ QVector filters;
+ void run();
+ Render * render;
+signals:
+ void pageReady(int);
+
+};
+//-----------------------------------------------------------------------------
+// RENDER
+//-----------------------------------------------------------------------------
+
+/*class DoublePageRender : public PageRender
+{
+ Q_OBJECT
+public:
+ DoublePageRender(Render * render, int firstPage, const QByteArray & firstPageData,const QByteArray & secondPageData, QImage * page,unsigned int degrees=0, QVector filters = QVector());
+private:
+ int numPage;
+ QByteArray data;
+ QByteArray data2;
+ QImage * page;
+ unsigned int degrees;
+ QVector