diff --git a/GPL.txt b/GPL.txt new file mode 100644 index 0000000..20d40b6 --- /dev/null +++ b/GPL.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 +. \ No newline at end of file diff --git a/JustAudio.pro b/JustAudio.pro index 6edcb5b..4473c71 100644 --- a/JustAudio.pro +++ b/JustAudio.pro @@ -2,6 +2,22 @@ # # Project created by QtCreator 2016-10-01T14:15:33 # +# This file is part of JustAudio. +# +# JustAudio 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. +# +# JustAudio 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 BashWire under the GPL.txt file. If not, see +# . +# #------------------------------------------------- QT += core gui @@ -13,24 +29,15 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = JustAudio TEMPLATE = app -SOURCES += main.cpp\ - gui/ui.cpp\ - gui/icon.cpp \ - io/aud_file.cpp \ - io/conf.cpp \ - io/idm.cpp \ - io/int.cpp +SOURCES += main.cpp \ + aud_file.cpp \ + core.cpp -HEADERS += gui/ui.h\ - gui/icon.h \ - io/aud_file.h \ - io/conf.h \ - io/idm.h \ - io/int.h +HEADERS += \ + aud_file.h \ + core.h -RESOURCES += icon_files.qrc +RESOURCES += \ + res.qrc RC_FILE = logo.rc - -DISTFILES += app_logo.ico \ - logo.rc diff --git a/app_logo.ico b/app_logo.ico deleted file mode 100644 index 9cfd016..0000000 Binary files a/app_logo.ico and /dev/null differ diff --git a/aud_file.cpp b/aud_file.cpp new file mode 100644 index 0000000..d53e4b7 --- /dev/null +++ b/aud_file.cpp @@ -0,0 +1,158 @@ +#include "aud_file.h" + +// This file is part of JustAudio. + +// JustAudio 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. + +// JustAudio 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 BashWire under the GPL.txt file. If not, see +// . + +AudFile::AudFile(QObject *parent) : QFile(parent) +{ + // QMediaPlayer doesn't handle files with an ID3 tag and variable bitrates that well so + // this class was created to offset the ID3 tags without modifying the audio files and + // to have the GUI rely on the actual byte seek position of the audio track rather than + // the time read by QMediaPlayer. + + offset = 0; + v1TagSize = 0; + activeInterupt = false; + timer = new QTimer(this); + + timer->setSingleShot(true); + + connect(timer, SIGNAL(timeout()), this, SIGNAL(endOfPlayback())); +} + +AudFile::~AudFile() +{ + close(); +} + +qint64 AudFile::audSize() +{ + return size() - offset - v1TagSize; +} + +qint64 AudFile::getOffset() +{ + return offset; +} + +bool AudFile::openFile(const QString &path) +{ + close(); + + v1TagSize = 0; + offset = 0; + activeInterupt = false; + + timer->stop(); + + setFileName(path); + + bool ret = open(QFile::ReadOnly); + + if (ret) + { + seek((size() - 1) - 128); + + if (peek(3) == "TAG") + { + // find an ID3v1 tag if it has any. the tag always start with "TAG" and appended + // to the end of the file with a fixed length of 128 bytes. v1 tags don't affect + // playback with QMediaPlayer but this info will still be needed in audSize(). + + v1TagSize = 128; + } + + seek(0); + + if (peek(3) == "ID3") + { + // find the ID3v2 tag if it has any and set the file offset position to skip over it. + // the tag has a 10byte header that starts with "ID3" and ends with a 4 byte + // big endian integer that indicates the tag size. + + QByteArray header = read(10); + QByteArray intBytes = header.mid(6); + quint64 num = 0; + quint64 bit = 1; + + offset += 10; // skip header + + if (header[5] & 16) + { + // footer flag check. if the tag has a footer, an additional 10bytes need + // to be added to the offset. + + offset += 10; // skip footer + } + + for (int i = intBytes.size() - 1; i >= 0; --i) + { + // read the integer bit-by-bit, setting the bits in "num" as it goes. note + // that its reading the bytes from "right to left" for big endian format. + + int byte = intBytes[i]; + + for (int inBit = 1; inBit <= 64; inBit *= 2, bit *= 2) + { + if ((byte & inBit) != 0) num |= bit; + } + } + + offset += num; // skip tag + } + + seek(0); + } + + return ret; +} + +bool AudFile::seek(qint64 off) +{ + // QMediaPlayer constantly seeks the audio file for data while playing. each seek + // resets the endOfPlayback() timer. if that timer is allowed to timeout, that is + // considered finished playback. the player also tend to do another seek after + // being paused so activeInterupt is there to prevent the timer from restarting + // unexpectly after userInterupt() is called. + + if (!activeInterupt) + { + timer->stop(); + timer->start(5000); + } + + emit bytePos(pos()); + + return QFile::seek(offset + off); +} + +void AudFile::userInterupt() +{ + // the timer needs to be aware if the user paused or stopped playback. this way + // the endOfPlayback() signal is not called unexpectedly. + + activeInterupt = true; + + timer->stop(); +} + +void AudFile::userResume() +{ + // after a pause, this class will also need to be aware of a user resume to remove + // the activeInterupt status. + + activeInterupt = false; +} diff --git a/aud_file.h b/aud_file.h new file mode 100644 index 0000000..a946f69 --- /dev/null +++ b/aud_file.h @@ -0,0 +1,61 @@ +#ifndef AUD_FILE_H +#define AUD_FILE_H + +// This file is part of JustAudio. + +// JustAudio 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. + +// JustAudio 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 BashWire under the GPL.txt file. If not, see +// . + +#include +#include +#include +#include +#include +#include + +class AudFile : public QFile +{ + Q_OBJECT + +private: + + QTimer *timer; + qint64 bytesRead; + qint64 offset; + bool activeInterupt; + int v1TagSize; + +public: + + AudFile(QObject *parent = 0); + + bool openFile(const QString &path); + bool seek(qint64 off); + qint64 getOffset(); + qint64 audSize(); + + ~AudFile(); + +public slots: + + void userInterupt(); + void userResume(); + +signals: + + void bytePos(qint64 off); + void endOfPlayback(); +}; + +#endif // AUD_FILE_H diff --git a/core.cpp b/core.cpp new file mode 100644 index 0000000..e043cb7 --- /dev/null +++ b/core.cpp @@ -0,0 +1,450 @@ +#include "core.h" + +// This file is part of JustAudio. + +// JustAudio 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. + +// JustAudio 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 BashWire under the GPL.txt file. If not, see +// . + +Core::Core(QObject *parent) : QObject(parent) +{ + // the Core class acts as a non-gui centralized class that house the majority + // of all functions the application needs to operate. + + nextCheck = new QCheckBox(tr("Play files in directory")); + muteCheck = new QCheckBox(tr("Mute")); + volumeSlider = new QSlider(Qt::Horizontal); + currentFile = new QLabel(tr("Ready")); + audFile = new AudFile(this); + sysTray = 0; + + // QMediaPlayer volume range 0-100. + + volumeSlider->setMinimum(0); + volumeSlider->setMaximum(100); + volumeSlider->setTracking(true); + currentFile->setWordWrap(true); + + QFile file(CONFIG_FILE, this); + + bool ok = false; + + if (file.open(QFile::ReadOnly)) + { + // the CONFIG_FILE is formated in csv so all data is seperated by a ',' + // the data will simply not load if the strings are not in exactly the + // same order as it was saved. + // data formats: + // VOLUME = numeric string + // PLAY_DIR_CHECK = string ("Y" or "N") + // MUTE = string ("Y" or "N") + + QByteArray confData = file.readAll(); + QList split = confData.split(','); + + if (split.size() == 3) + { + int volume = split[VOLUME].toInt(&ok); + + if (ok) + { + volumeSlider->setValue(volume); + + emit setVolume(volume); + + if (split[PLAY_DIR_CHECK] == "Y") nextCheck->setChecked(true); + else nextCheck->setChecked(false); + + if (split[MUTE] == "Y") muteCheck->setChecked(true); + else muteCheck->setChecked(false); + + } + } + } + + if (!ok) + { + // set default values on failure to read the CONFIG_FILE. + + emit setVolume(50); + + volumeSlider->setValue(50); + nextCheck->setChecked(false); + muteCheck->setChecked(false); + } + + file.close(); + + // it's very important that the player stops playback before closing the application + // or it will cause a crash. + + connect(audFile, SIGNAL(endOfPlayback()), this, SLOT(playBackFinished())); + connect(QApplication::instance(), SIGNAL(aboutToQuit()), this, SIGNAL(stop())); +} + +Core::~Core() +{ + audFile->close(); + + QFile file(CONFIG_FILE, this); + + if (file.open(QFile::WriteOnly | QFile::Truncate)) + { + // CONFIG_FILE format: + // [volume],[play_dir_check],[mute] + + // data formats: + // VOLUME = numeric string + // PLAY_DIR_CHECK = string ("Y" or "N") + // MUTE = string ("Y" or "N") + + file.write(QByteArray::number(volumeSlider->value())); + file.write(","); + + if (nextCheck->isChecked()) file.write("Y"); + else file.write("N"); + + file.write(","); + + if (muteCheck->isChecked()) file.write("Y"); + else file.write("N"); + } + + file.close(); +} + +void Core::setFileWatcher(QFileSystemWatcher *watcher) +{ + connect(watcher, SIGNAL(fileChanged(QString)), this, SLOT(processfileChanged(QString))); +} + +void Core::setPlayer(QMediaPlayer *player) +{ + // the Core class does not run funtions directly on the QMediaPlayer + // class that lives in the main function so everything it needs to + // do are connected via slots and signals. + + connect(this, SIGNAL(start()), player, SLOT(play())); + connect(this, SIGNAL(pause()), player, SLOT(pause())); + connect(this, SIGNAL(stop()), player, SLOT(stop())); + connect(this, SIGNAL(setMedia(QMediaContent,QIODevice*)), player, SLOT(setMedia(QMediaContent,QIODevice*))); + connect(this, SIGNAL(setVolume(int)), player, SLOT(setVolume(int))); + connect(this, SIGNAL(muteState(bool)), player, SLOT(setMuted(bool))); + connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(stateChanged(QMediaPlayer::State))); +} + +void Core::setTrayIcon(QSystemTrayIcon *tray) +{ + // must user interaction will be on the system tray icon for this app. + // this funtion sets up the context menu so the user can have access + // to controls like play, pause, next, previous, etc... + + QMenu *contextMenu = new QMenu(); + QWidget *volumeWidget = new QWidget(); + QHBoxLayout *volumeLayout = new QHBoxLayout(volumeWidget); + QWidgetAction *nextCheckAction = new QWidgetAction(this); + QWidgetAction *muteAction = new QWidgetAction(this); + QWidgetAction *volumeAction = new QWidgetAction(this); + QWidgetAction *fileNameAction = new QWidgetAction(this); + QToolButton *volUpIcon = new QToolButton(); + QToolButton *volDownIcon = new QToolButton(); + + fileNameAction->setDefaultWidget(currentFile); + contextMenu->addAction(fileNameAction); + contextMenu->addSeparator(); + + playAction = contextMenu->addAction(QIcon(":/png/play"), tr("Play"), this, SIGNAL(start())); + pauseAction = contextMenu->addAction(QIcon(":/png/pause"), tr("Pause"), this, SIGNAL(pause())); + stopAction = contextMenu->addAction(QIcon(":/png/stop"), tr("Stop"), this, SIGNAL(stop())); + sysTray = tray; + + volUpIcon->setIcon(QIcon(":/png/volume_up")); + volDownIcon->setIcon(QIcon(":/png/volume_down")); + volumeLayout->addWidget(volDownIcon); + volumeLayout->addWidget(volumeSlider); + volumeLayout->addWidget(volUpIcon); + playAction->setEnabled(false); // play, pause, stop, previous and next are disabled initially. + pauseAction->setEnabled(false); // this will change when an audio file is opened + stopAction->setEnabled(false); // (see stateChanged()) + volumeAction->setDefaultWidget(volumeWidget); + nextCheckAction->setDefaultWidget(nextCheck); + muteAction->setDefaultWidget(muteCheck); + contextMenu->addAction(QIcon(":/png/open"), tr("Open"), this, SLOT(openDialog())); + + nextAction = contextMenu->addAction(QIcon(":/png/next"), tr("Next"), this, SLOT(nextFile())); + prevAction = contextMenu->addAction(QIcon(":/png/prev"), tr("Previous"), this, SLOT(prevFile())); + + nextAction->setEnabled(false); + prevAction->setEnabled(false); + contextMenu->addSeparator(); + contextMenu->addAction(nextCheckAction); + contextMenu->addAction(muteAction); + contextMenu->addSeparator(); + contextMenu->addAction(volumeAction); + contextMenu->addSeparator(); + contextMenu->addAction(tr("License"), this, SLOT(showLicense())); + contextMenu->addAction(tr("Exit"), QApplication::instance(), SLOT(quit())); + tray->setContextMenu(contextMenu); + + connect(tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayClicked(QSystemTrayIcon::ActivationReason))); + connect(muteCheck, SIGNAL(clicked(bool)), this, SIGNAL(muteState(bool))); + connect(volumeSlider, SIGNAL(valueChanged(int)), this, SIGNAL(setVolume(int))); + connect(volUpIcon, SIGNAL(clicked()), this, SLOT(volumeUp())); + connect(volDownIcon, SIGNAL(clicked()), this, SLOT(volumeDown())); + connect(pauseAction, SIGNAL(triggered()), audFile, SLOT(userInterupt())); + connect(stopAction, SIGNAL(triggered()), audFile, SLOT(userInterupt())); + connect(playAction, SIGNAL(triggered()), audFile, SLOT(userResume())); +} + +void Core::processfileChanged(const QString &path) +{ + Q_UNUSED(path); + + QFile file(PROCESS_FILE, this); + + if (file.open(QFile::ReadOnly)) + { + // expected data from the PROCESS_FILE should just be a directory path + // string to the audio file to be opened. play() will call out an error + // if it fails to open this file. + + play(file.readAll().trimmed()); + } + + file.close(); +} + +void Core::play(const QString &path) +{ + emit stop(); + + if (audFile->openFile(path)) + { + QFileInfo info(path); + + currentFile->setText(info.fileName()); + + if (sysTray) + { + sysTray->setToolTip(QApplication::applicationName() + " " + QApplication::applicationVersion() + " - " + info.fileName()); + } + + emit setMedia(0, audFile); + emit start(); + } + else + { + QMessageBox box; + + box.setText(tr("Failed to open file: ") + path); + box.setDetailedText(audFile->errorString()); + box.exec(); + } +} + +void Core::nextFile() +{ + fileDir('+'); +} + +void Core::prevFile() +{ + fileDir('-'); +} + +void Core::fileDir(char direction) +{ + // using the current file as a reference, this function will first find out the + // directory the file lives in and then use that directory to list all files within + // it. then using the current file as a reference again, it trys to find it in the + // list of files and then use that file position to find out the next or previous + // file in the directory to play. + + // if the reference file no longer exist then it will attempt to play 1st file in the + // directory. if the directory no longer exist then nothing happens. + + QFileInfo info(audFile->fileName()); + QDir dir(info.path()); + + QStringList list = dir.entryList(audExtensions(), QDir::Files | QDir::Readable, QDir::Name | QDir::IgnoreCase); + int pos = list.indexOf(info.fileName()); + + if (!list.isEmpty()) + { + if (direction == '+') pos++; + else if (direction == '-') pos--; + + if (pos < 0) pos = 0; + + if (pos < list.size()) + { + play(info.path() + QDir::separator() + list[pos]); + } + } +} + +void Core::openDialog() +{ + QFileDialog fileDialog; + QString filterString = tr("Audio Files ("); + QStringList list = audExtensions(); + + for (int i = 0; i < list.size(); ++i) + { + filterString.append(list[i] + " "); + } + + filterString = filterString.trimmed(); + + filterString.append(")"); + + fileDialog.setFileMode(QFileDialog::ExistingFile); + fileDialog.setNameFilter(filterString); + fileDialog.setDirectory(QDir::homePath()); + + if (fileDialog.exec()) + { + play(fileDialog.selectedFiles()[0]); + } +} + +void Core::playBackFinished() +{ + if (nextCheck->isChecked()) nextFile(); +} + +void Core::stateChanged(QMediaPlayer::State state) +{ + mediaState = state; + + switch(state) + { + case QMediaPlayer::PlayingState: + { + playAction->setEnabled(false); + pauseAction->setEnabled(true); + stopAction->setEnabled(true); + prevAction->setEnabled(true); + nextAction->setEnabled(true); + + break; + } + case QMediaPlayer::StoppedState: + { + playAction->setEnabled(true); + pauseAction->setEnabled(false); + stopAction->setEnabled(false); + + break; + } + case QMediaPlayer::PausedState: + { + playAction->setEnabled(true); + pauseAction->setEnabled(false); + stopAction->setEnabled(true); + + break; + } + } +} + +void Core::trayClicked(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::DoubleClick) + { + nextAction->trigger(); + } + else if (reason == QSystemTrayIcon::Trigger) + { + if (mediaState == QMediaPlayer::PlayingState) + { + pauseAction->trigger(); + } + else if ((mediaState == QMediaPlayer::PausedState) || (mediaState == QMediaPlayer::StoppedState)) + { + playAction->trigger(); + } + } +} + +void Core::volumeDown() +{ + volumeSlider->setValue(volumeSlider->value() - 4); +} + +void Core::volumeUp() +{ + volumeSlider->setValue(volumeSlider->value() + 4); +} + +void Core::showLicense() +{ + QDialog dialog; + QFile file(":/gpl", this); + + file.open(QFile::ReadOnly); + + QPlainTextEdit *text = new QPlainTextEdit(file.readAll()); + QVBoxLayout *layout = new QVBoxLayout(&dialog); + QRect rect = QApplication::desktop()->screenGeometry(); + + layout->addWidget(text); + text->setReadOnly(true); + text->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + file.close(); + dialog.setMinimumWidth(rect.width() / 4); + dialog.setMinimumHeight(rect.height() - (rect.height() / 6)); + dialog.exec(); +} + +QStringList Core::audExtensions() +{ + QStringList ret; + + // the media plugins are not checked for file format support. this is just an arbitrary list + // of audio file extentions that are used when filtering for files to play. if a file fails + // to play, the application will remain in silence or move on to the next file if nextCheck + // is enabled. + + ret.append("*.mp3"); + ret.append("*.ogg"); + ret.append("*.flac"); + ret.append("*.wav"); + ret.append("*.au"); + ret.append("*.aif"); + ret.append("*.mid"); + ret.append("*.rmi"); + ret.append("*.m4a"); + ret.append("*.aiff"); + ret.append("*.ape"); + ret.append("*.wv"); + ret.append("*.3gp"); + ret.append("*.aa"); + ret.append("*.aac"); + ret.append("*.aax"); + ret.append("*.act"); + ret.append("*.amr"); + ret.append("*.au"); + ret.append("*.awb"); + ret.append("*.dct"); + ret.append("*.gsm"); + ret.append("*.m4b"); + ret.append("*.oga"); + ret.append("*.mogg"); + ret.append("*.ra"); + ret.append("*.rm"); + ret.append("*.raw"); + + return ret; +} diff --git a/core.h b/core.h new file mode 100644 index 0000000..1ec62a3 --- /dev/null +++ b/core.h @@ -0,0 +1,117 @@ +#ifndef CORE_H +#define CORE_H + +// This file is part of JustAudio. + +// JustAudio 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. + +// JustAudio 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 BashWire under the GPL.txt file. If not, see +// . + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aud_file.h" + +#define SEGMENT_SIZE 10 +#define PROCESS_FILE "process.txt" +#define CONFIG_FILE "conf.csv" + +class Core : public QObject +{ + Q_OBJECT + +private: + + enum ConfValues + { + VOLUME, + PLAY_DIR_CHECK, + MUTE + }; + + AudFile *audFile; + QAction *playAction; + QAction *pauseAction; + QAction *stopAction; + QAction *nextAction; + QAction *prevAction; + QCheckBox *nextCheck; + QCheckBox *muteCheck; + QSlider *volumeSlider; + QLabel *currentFile; + QSystemTrayIcon *sysTray; + int mediaState; + + void fileDir(char direction); + QStringList audExtensions(); + +private slots: + + void trayClicked(QSystemTrayIcon::ActivationReason reason); + void stateChanged(QMediaPlayer::State state); + void processfileChanged(const QString &path); + void nextFile(); + void prevFile(); + void playBackFinished(); + void openDialog(); + void volumeUp(); + void volumeDown(); + void showLicense(); + +public: + + void setTrayIcon(QSystemTrayIcon *tray); + void setPlayer(QMediaPlayer *player); + void setFileWatcher(QFileSystemWatcher *watcher); + void play(const QString &path); + + Core(QObject *parent = 0); + ~Core(); + +signals: + + void start(); + void pause(); + void stop(); + void setMedia(const QMediaContent &media, QIODevice *stream); + void setVolume(int value); + void muteState(bool state); +}; + +#endif // CORE_H diff --git a/gui/icon.cpp b/gui/icon.cpp deleted file mode 100644 index e68c0fd..0000000 --- a/gui/icon.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "icon.h" - -Icon::Icon(IconType t, QWidget *parent) : QToolButton(parent) -{ - QRect rect = QApplication::desktop()->screenGeometry(); - - setMinimumHeight(rect.height() / 40); - setMinimumWidth(rect.height() / 40); - setCursor(Qt::PointingHandCursor); - setToolButtonStyle(Qt::ToolButtonIconOnly); - setPopupMode(QToolButton::InstantPopup); - - switch (t) - { - case PAUSE_PLAY: - { - stateChanged(QMediaPlayer::StoppedState); - setToolTip(tr("Pause/Play")); - - break; - } - case MENU: - { - loadImg(":/svg/menu"); - setToolTip(tr("Menu")); - - break; - } - case OPEN: - { - loadImg(":/svg/open"); - setToolTip(tr("Open File")); - - break; - } - case STOP: - { - loadImg(":/svg/stop"); - setToolTip(tr("Stop")); - - break; - } - case PREV: - { - loadImg(":/svg/prev"); - setToolTip(tr("Previous File")); - - break; - } - case NEXT: - { - loadImg(":/svg/next"); - setToolTip(tr("Next File")); - - break; - } - case VOL_UP: - { - loadImg(":/svg/volume_up"); - setToolTip(tr("Volume Up")); - - break; - } - case VOL_DOWN: - { - loadImg(":/svg/volume_down"); - setToolTip(tr("Volume Down")); - - break; - } - case RESTORE: - { - loadImg(":/svg/restore"); - setToolTip(tr("Restore")); - - break; - } - } - - if (t != MENU) - { - connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked())); - } - - type = t; - volSlider = 0; -} - -void Icon::buttonClicked() -{ - if (type == PAUSE_PLAY) - { - if (playerState == QMediaPlayer::PlayingState) - { - emit pause(); - } - else - { - emit play(); - } - } - else if (type == OPEN) - { - emit open(); - } - else if (type == NEXT) - { - emit next(); - } - else if (type == PREV) - { - emit prev(); - } - else if (type == STOP) - { - emit stop(); - } - else if ((type == VOL_DOWN) && volSlider) - { - volSlider->setValue(volSlider->value() - 1); - } - else if ((type == VOL_UP) && volSlider) - { - volSlider->setValue(volSlider->value() + 1); - } - else if (type == RESTORE) - { - emit restore(); - } -} - -void Icon::paintEvent(QPaintEvent *) -{ - QPainter painter(this); - QSvgRenderer svg(svgFile, this); - - svg.render(&painter); -} - -void Icon::stateChanged(QMediaPlayer::State state) -{ - playerState = state; - - if (state == QMediaPlayer::PlayingState) - { - loadImg(":/svg/pause"); - } - else - { - loadImg(":/svg/play"); - } -} - -void Icon::loadImg(const QString &path) -{ - svgFile = path; - - update(); -} - -void Icon::setSlider(QSlider *slider) -{ - volSlider = slider; -} diff --git a/gui/icon.h b/gui/icon.h deleted file mode 100644 index 0bf6f83..0000000 --- a/gui/icon.h +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef ICON_H -#define ICON_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class Icon : public QToolButton -{ - Q_OBJECT - -public: - - enum IconType - { - PAUSE_PLAY, - MENU, - OPEN, - STOP, - PREV, - NEXT, - VOL_UP, - VOL_DOWN, - RESTORE - }; - - Icon(IconType t, QWidget *parent = 0); - - void setSlider(QSlider *slider); - -public slots: - - void stateChanged(QMediaPlayer::State state); - -private: - - IconType type; - QMediaPlayer::State playerState; - QString svgFile; - QSlider *volSlider; - - void paintEvent(QPaintEvent *); - void loadImg(const QString &path); - -private slots: - - void buttonClicked(); - -signals: - - void pause(); - void play(); - void settings(); - void open(); - void next(); - void prev(); - void stop(); - void restore(); -}; - -#endif // ICON_H diff --git a/gui/ui.cpp b/gui/ui.cpp deleted file mode 100644 index d2c022f..0000000 --- a/gui/ui.cpp +++ /dev/null @@ -1,259 +0,0 @@ -#include "ui.h" - -Ui::Ui(const QStringList &args, QWidget *parent) : QWidget(parent) -{ - QWidget *btnWid = new QWidget(this); - QHBoxLayout *btnLayout = new QHBoxLayout(btnWid); - QVBoxLayout *mainLayout = new QVBoxLayout(this); - QLabel *timeDisp = new QLabel("00:00:00", this); - Icon *trayPausePlay = new Icon(Icon::PAUSE_PLAY, this); - Icon *trayOpen = new Icon(Icon::OPEN, this); - Icon *trayStop = new Icon(Icon::STOP, this); - Icon *trayNext = new Icon(Icon::NEXT, this); - Icon *trayPrev = new Icon(Icon::PREV, this); - Icon *trayRestore = new Icon(Icon::RESTORE, this); - - trayMenu = new QMenu(this); - fileName = new QLabel(this); - slider = new QSlider(this); - menu = new QMenu(this); - trayIcon = new QSystemTrayIcon(QIcon(":/logo"), this); - settings = new Icon(Icon::MENU, this); - pausePlay = new Icon(Icon::PAUSE_PLAY, this); - open = new Icon(Icon::OPEN, this); - stop = new Icon(Icon::STOP, this); - next = new Icon(Icon::NEXT, this); - prev = new Icon(Icon::PREV, this); - player = new QMediaPlayer(this); - ioDev = new AudFile(this); - conf = new Conf(this); - pressed = false; - - QFont fnt = fileName->font(); - - fnt.setBold(true); - - setStyleSheet("background-color:white;"); - - settings->setMenu(menu); - fileName->setText(tr("Ready")); - fileName->setFont(fnt); - slider->setOrientation(Qt::Horizontal); - trayIcon->setVisible(conf->trayIcon()); - trayIcon->setContextMenu(trayMenu); - conf->populateOptionsMenu(menu, this); - player->setVolume(conf->getVolume()); - trayMenu->addAction(new MenuItem(trayPausePlay->toolTip(), trayPausePlay, this)); - trayMenu->addAction(new MenuItem(trayOpen->toolTip(), trayOpen, this)); - trayMenu->addAction(new MenuItem(trayStop->toolTip(), trayStop, this)); - trayMenu->addAction(new MenuItem(trayNext->toolTip(), trayNext, this)); - trayMenu->addAction(new MenuItem(trayPrev->toolTip(), trayPrev, this)); - trayMenu->addAction(new MenuItem(trayRestore->toolTip(), trayRestore, this)); - btnLayout->addWidget(prev); - btnLayout->addWidget(pausePlay); - btnLayout->addWidget(stop); - btnLayout->addWidget(next); - btnLayout->addWidget(open); - btnLayout->addWidget(settings); - mainLayout->addWidget(fileName); - mainLayout->addWidget(slider); - mainLayout->addWidget(timeDisp, 0, Qt::AlignCenter); - mainLayout->addWidget(btnWid, 0, Qt::AlignCenter); - - connect(trayRestore, SIGNAL(restore()), this, SLOT(showNormal())); - connect(trayPausePlay, SIGNAL(pause()), player, SLOT(pause())); - connect(trayPausePlay, SIGNAL(play()), player, SLOT(play())); - connect(trayNext, SIGNAL(next()), this, SLOT(nextFile())); - connect(trayPrev, SIGNAL(prev()), this, SLOT(prevFile())); - connect(trayStop, SIGNAL(stop()), this, SLOT(stopPlayer())); - connect(trayOpen, SIGNAL(open()), this, SLOT(openDialog())); - connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); - connect(pausePlay, SIGNAL(pause()), player, SLOT(pause())); - connect(pausePlay, SIGNAL(play()), player, SLOT(play())); - connect(next, SIGNAL(next()), this, SLOT(nextFile())); - connect(prev, SIGNAL(prev()), this, SLOT(prevFile())); - connect(stop, SIGNAL(stop()), this, SLOT(stopPlayer())); - connect(open, SIGNAL(open()), this, SLOT(openDialog())); - connect(player, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(error(QMediaPlayer::Error))); - connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), pausePlay, SLOT(stateChanged(QMediaPlayer::State))); - connect(player, SIGNAL(stateChanged(QMediaPlayer::State)), trayPausePlay, SLOT(stateChanged(QMediaPlayer::State))); - connect(ioDev, SIGNAL(duration(QString)), timeDisp, SLOT(setText(QString))); - connect(ioDev, SIGNAL(posChanged(qint64)), this, SLOT(posChanged(qint64))); - connect(ioDev, SIGNAL(endOfPlayback()), this, SLOT(nextFile())); - connect(slider, SIGNAL(sliderPressed()), this, SLOT(sliderPressed())); - connect(slider, SIGNAL(sliderReleased()), this, SLOT(sliderReleased())); - connect(conf, SIGNAL(volume(int)), player, SLOT(setVolume(int))); - connect(conf, SIGNAL(enableTrayIcon(bool)), trayIcon, SLOT(setVisible(bool))); - - if (args.size() > 1) - { - play(args[1]); - } -} - -void Ui::play(const QString &path) -{ - stopPlayer(); - - if (ioDev->openFile(path)) - { - QFileInfo info(path); - - conf->setLastPath(info.path()); - fileName->setText(info.fileName()); - player->setMedia(0, ioDev); - slider->setMinimum(ioDev->getOffset()); - slider->setMaximum(ioDev->size()); - player->play(); - - adjustSize(); - } - else - { - error(QMediaPlayer::ResourceError); - } -} - -void Ui::stopPlayer() -{ - ioDev->blockSignals(true); - player->stop(); - ioDev->blockSignals(false); - slider->setValue(ioDev->getOffset()); -} - -void Ui::openDialog() -{ - QFileDialog fileDialog(this); - - fileDialog.setFileMode(QFileDialog::ExistingFile); - fileDialog.setNameFilter(tr("Audio Files (*.mp3 *.ogg *.wav *.flac)")); - fileDialog.setDirectory(conf->getLastPath()); - - if (fileDialog.exec()) - { - play(fileDialog.selectedFiles()[0]); - } -} - -void Ui::error(QMediaPlayer::Error error) -{ - QMessageBox box(this); - - if (error == QMediaPlayer::NoError) - { - box.setText(tr("An unknown error has occured")); - box.setIcon(QMessageBox::Warning); - } - else if (error == QMediaPlayer::FormatError) - { - box.setText(tr("The format of the media resource isn't fully supported. Playback may still be possible.")); - box.setIcon(QMessageBox::Warning); - } - else if (error == QMediaPlayer::AccessDeniedError) - { - box.setText(tr("The appropriate permissions to play the media resource are not present")); - box.setIcon(QMessageBox::Critical); - } - else if (error == QMediaPlayer::ServiceMissingError) - { - box.setText(tr("A valid playback service was not found, playback cannot proceed.")); - box.setIcon(QMessageBox::Critical); - } - else if (error == QMediaPlayer::ResourceError) - { - box.setText(tr("Media resource couldn't be resolved.")); - box.setIcon(QMessageBox::Critical); - } - - box.exec(); -} - -void Ui::sliderPressed() -{ - pressed = true; -} - -void Ui::sliderReleased() -{ - pressed = false; - - float pos = (float) slider->value() - ioDev->getOffset(); - float max = (float) slider->maximum() - ioDev->getOffset(); - float mul = pos / max; - - player->setPosition(mul * player->duration()); -} - -void Ui::posChanged(qint64 pos) -{ - if (!pressed) slider->setSliderPosition(pos); -} - -void Ui::nextFile() -{ - fileDir('+'); -} - -void Ui::prevFile() -{ - fileDir('-'); -} - -void Ui::fileDir(char direction) -{ - QDir dir(conf->getLastPath()); - - QStringList filterList; - - filterList.append("*.mp3"); - filterList.append("*.ogg"); - filterList.append("*.flac"); - filterList.append("*.wav"); - - QStringList list = dir.entryList(filterList, QDir::Files | QDir::Readable, QDir::Name); - int pos = list.indexOf(fileName->text()); - - if (pos != -1) - { - if (direction == '+') pos++; - else if (direction == '-') pos--; - - if (pos < list.size()) - { - play(conf->getLastPath() + QDir::separator() + list[pos]); - } - } -} - -void Ui::trayActivated(QSystemTrayIcon::ActivationReason reason) -{ - if (reason == QSystemTrayIcon::DoubleClick) - { - showNormal(); - raise(); - activateWindow(); - } - else if (reason == QSystemTrayIcon::Trigger) - { - trayMenu->popup(trayIcon->geometry().center()); - } -} - -void Ui::changeEvent(QEvent *event) -{ - if ((event->type() == QEvent::WindowStateChange) && conf->trayIcon()) - { - if (windowState() & Qt::WindowMinimized) - { - hide(); - } - } -} - -void Ui::closeEvent(QCloseEvent *event) -{ - Q_UNUSED(event); - - QCoreApplication::exit(); -} diff --git a/gui/ui.h b/gui/ui.h deleted file mode 100644 index fcfb4ef..0000000 --- a/gui/ui.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef UI_H -#define UI_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "icon.h" -#include "../io/aud_file.h" -#include "../io/conf.h" - -class Ui : public QWidget -{ - Q_OBJECT - -private: - - QLabel *fileName; - QSlider *slider; - QMediaPlayer *player; - AudFile *ioDev; - QToolButton *settings; - QSystemTrayIcon *trayIcon; - Conf *conf; - QMenu *menu; - QMenu *trayMenu; - Icon *pausePlay; - Icon *open; - Icon *next; - Icon *prev; - Icon *stop; - bool pressed; - - void fileDir(char direction); - void changeEvent(QEvent *event); - void closeEvent(QCloseEvent *event); - -private slots: - - void error(QMediaPlayer::Error error); - void sliderPressed(); - void sliderReleased(); - void posChanged(qint64 pos); - void openDialog(); - void nextFile(); - void prevFile(); - void stopPlayer(); - void play(const QString &path); - void trayActivated(QSystemTrayIcon::ActivationReason reason); - -public: - - Ui(const QStringList &args, QWidget *parent = 0); -}; - -#endif // UI_H diff --git a/icon_files.qrc b/icon_files.qrc deleted file mode 100644 index c91a966..0000000 --- a/icon_files.qrc +++ /dev/null @@ -1,29 +0,0 @@ - - - app_logo.ico - - - svg/open.svg - svg/pause.svg - svg/play.svg - svg/menu.svg - svg/next.svg - svg/prev.svg - svg/stop.svg - svg/volume_up.svg - svg/volume_down.svg - svg/restore.svg - - - png/open.png - png/pause.png - png/play.png - png/menu.png - png/next.png - png/prev.png - png/stop.png - png/volume_up.png - png/volume_down.png - png/restore.png - - diff --git a/io/aud_file.cpp b/io/aud_file.cpp deleted file mode 100644 index 70007a8..0000000 --- a/io/aud_file.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "aud_file.h" - -AudFile::AudFile(QObject *parent) : QFile(parent) -{ - offset = 0; - buffRate = 0; - init = true; -} - -AudFile::~AudFile() -{ - close(); -} - -bool AudFile::openFile(const QString &path) -{ - close(); - - init = true; - buffRate = 0; - - setFileName(path); - - bool ret = open(QFile::ReadOnly); - - if (ret) - { - offset = 0; - - if (peek(3) == "ID3") - { - QByteArray header = read(10); - QByteArray intBytes = header.mid(6); - quint64 num = 0; - quint64 bit = 1; - - offset += 10; - - if (header[5] & 16) //Footer flag check - { - offset += 10; - } - - for (int i = intBytes.size() - 1; i >= 0; --i) - { - int byte = intBytes[i]; - - for (int inBit = 1; inBit <= 64; inBit *= 2, bit *= 2) - { - if ((byte & inBit) != 0) num |= bit; - } - } - - offset += num; - } - - seek(0); - } - - return ret; -} - -bool AudFile::seek(qint64 off) -{ - qint64 newPos = offset + off; - - if (init) - { - if ((off) && (!buffRate)) - { - buffRate = off; - - emit duration(getDuration()); - } - - if (newPos >= size()) - { - init = false; - } - } - else - { - emit posChanged(pos()); - - if (newPos >= size()) - { - QTimer::singleShot(2000, this, SLOT(delayFinished())); - } - } - - return QFile::seek(newPos); -} - -qint64 AudFile::getOffset() -{ - return offset; -} - -QString AudFile::getDuration() -{ - QTime time(0, 0, 0, 0); - - time = time.addMSecs((size() / buffRate) * 1000); - - return time.toString("hh:mm:ss"); -} - -void AudFile::delayFinished() -{ - emit endOfPlayback(); -} diff --git a/io/aud_file.h b/io/aud_file.h deleted file mode 100644 index 179fd13..0000000 --- a/io/aud_file.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef AUD_FILE_H -#define AUD_FILE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -class AudFile : public QFile -{ - Q_OBJECT - -private: - - qint64 buffRate; - qint64 offset; - bool init; - -private slots: - - void delayFinished(); - -public: - - AudFile(QObject *parent = 0); - - bool openFile(const QString &path); - bool seek(qint64 off); - qint64 getOffset(); - QString getDuration(); - - ~AudFile(); - -signals: - - void posChanged(qint64 off); - void endOfPlayback(); - void duration(QString timeStr); -}; - -#endif // AUD_FILE_H diff --git a/io/conf.cpp b/io/conf.cpp deleted file mode 100644 index 3a27ba8..0000000 --- a/io/conf.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include "io/conf.h" - -MenuItem::MenuItem(const QString &label, QWidget *widget, QObject *parent) : QWidgetAction(parent) -{ - str = label; - wid = widget; -} - -QWidget *MenuItem::createWidget(QWidget *parent) -{ - if (str.isEmpty()) - { - wid->setParent(parent); - - return wid; - } - else - { - QWidget *widget = new QWidget(parent); - QHBoxLayout *layout = new QHBoxLayout(widget); - - layout->addWidget(wid); - layout->addWidget(new QLabel(str, parent)); - - return widget; - } -} - -Conf::Conf(QObject *parent) : QObject(parent) -{ - QFile file(confPath(), this); - - QByteArray data; - - if (file.open(QFile::ReadOnly)) - { - data = file.readAll(); - } - - file.close(); - - Idm idm(data, this); - - idm.rdHeader(); - - QByteArray pathBa = idm.value(LAST_PATH); - QByteArray nextBa = idm.value(NEXT_FILE); - QByteArray trayBa = idm.value(TRAY_ICON); - QByteArray volBa = idm.value(VOLUME); - - if (!pathBa.isEmpty()) lastPath = pathBa; - else lastPath = QDir::homePath(); - if (!nextBa.isEmpty()) nextFileState = (nextBa == "Y"); - else nextFileState = true; - if (!trayBa.isEmpty()) showTrayIcon = (trayBa == "Y"); - else showTrayIcon = false; - if (!volBa.isEmpty()) volumeValue = rdInt(volBa); - else volumeValue = 50; -} - -void Conf::sync() -{ - Idm idm(this); - - idm.wrHeader(); - idm.insert(LAST_PATH, lastPath.toUtf8()); - idm.insert(VOLUME, wrInt(volumeValue)); - - if (nextFileState) idm.insert(NEXT_FILE, "Y"); - else idm.insert(NEXT_FILE, "N"); - - if (showTrayIcon) idm.insert(TRAY_ICON, "Y"); - else idm.insert(TRAY_ICON, "N"); - - QFile file(confPath(), this); - - file.open(QFile::WriteOnly | QFile::Truncate); - file.write(idm.getBuff()); -} - -void Conf::populateOptionsMenu(QMenu *menu, QWidget *parent) -{ - QWidget *volWidget = new QWidget(parent); - QHBoxLayout *volLayout = new QHBoxLayout(volWidget); - QSlider *volSlider = new QSlider(parent); - QCheckBox *nextBox = new QCheckBox(parent); - QCheckBox *trayBox = new QCheckBox(parent); - Icon *volUp = new Icon(Icon::VOL_UP, parent); - Icon *volDown = new Icon(Icon::VOL_DOWN, parent); - - volUp->setSlider(volSlider); - volDown->setSlider(volSlider); - volSlider->setMinimum(0); - volSlider->setMaximum(100); - volSlider->setValue(getVolume()); - volSlider->setOrientation(Qt::Horizontal); - volLayout->addWidget(volDown); - volLayout->addWidget(volSlider); - volLayout->addWidget(volUp); - nextBox->setText(tr("Auto play next file in directory")); - nextBox->setChecked(nextFile()); - trayBox->setText(tr("Enable tray icon")); - trayBox->setChecked(trayIcon()); - - connect(volSlider, SIGNAL(valueChanged(int)), this, SLOT(setVolume(int))); - connect(volSlider, SIGNAL(valueChanged(int)), this, SIGNAL(volume(int))); - connect(nextBox, SIGNAL(clicked(bool)), this, SLOT(setNextFile(bool))); - connect(trayBox, SIGNAL(clicked(bool)), this, SLOT(setTrayIcon(bool))); - - menu->addAction(new MenuItem("", volWidget, this)); - menu->addAction(new MenuItem("", nextBox, this)); - menu->addAction(new MenuItem("", trayBox, this)); -} - -void Conf::setLastPath(const QString &path) -{ - lastPath = path; sync(); -} - -void Conf::setNextFile(bool state) -{ - nextFileState = state; sync(); -} - -void Conf::setTrayIcon(bool state) -{ - emit enableTrayIcon(state); - - showTrayIcon = state; sync(); -} - -void Conf::setVolume(int value) -{ - emit volume(value); - - volumeValue = value; sync(); -} - -QString Conf::getLastPath() -{ - if (!QDir(lastPath).exists()) - { - lastPath = QDir::homePath(); - } - - return lastPath; -} - -bool Conf::nextFile() -{ - return nextFileState; -} - -bool Conf::trayIcon() -{ - return showTrayIcon; -} - -int Conf::getVolume() -{ - return volumeValue; -} - -QString Conf::confPath() -{ - QString path = QDir::homePath() + '/'; - -#ifndef Q_OS_WIN32 - - path.append("/.config/"); - -#else - - path.append("AppData/Local/"); - -#endif - - QDir dir; - - path.append(QApplication::applicationName() + '/'); - - dir.mkpath(path); - - return path + "conf.idm"; -} diff --git a/io/conf.h b/io/conf.h deleted file mode 100644 index e5c7ac6..0000000 --- a/io/conf.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef CONF_H -#define CONF_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "idm.h" -#include "../gui/icon.h" - -class MenuItem : public QWidgetAction -{ - Q_OBJECT - -private: - - QString str; - QWidget *wid; - - QWidget *createWidget(QWidget *parent); - -public: - - MenuItem(const QString &label, QWidget *widget, QObject *parent = 0); -}; - -class Conf : public QObject -{ - Q_OBJECT - -private: - - enum DataType - { - LAST_PATH = 1, - NEXT_FILE, - VOLUME, - TRAY_ICON - }; - - QString lastPath; - bool showTrayIcon; - bool nextFileState; - int volumeValue; - - void sync(); - QString confPath(); - -public slots: - - void setLastPath(const QString &path); - void setNextFile(bool state); - void setVolume(int value); - void setTrayIcon(bool state); - -public: - - QString getLastPath(); - bool nextFile(); - bool trayIcon(); - void populateOptionsMenu(QMenu *menu, QWidget *parent); - int getVolume(); - - Conf(QObject *parent = 0); - -signals: - - void volume(int value); - void enableTrayIcon(bool state); -}; - -#endif // CONF_H diff --git a/io/idm.cpp b/io/idm.cpp deleted file mode 100644 index 7bd6794..0000000 --- a/io/idm.cpp +++ /dev/null @@ -1,432 +0,0 @@ -#include "idm.h" - -const QByteArray Idm::TAG = "IDM"; -const int Idm::MAX_MAJOR = 1; -const int Idm::MAX_MINOR = 0; - int Idm::DEFAULT_INT_SIZE = 3; - int Idm::DEFAULT_FLAGS = 1; // FIXED_KEY_LEN - int Idm::DEFAULT_KEY_LEN = 1; - int Idm::CURRENT_MAJOR = Idm::MAX_MAJOR; - int Idm::CURRENT_MINOR = Idm::MAX_MINOR; - -Idm::Idm(const QByteArray &data, QObject *parent) : QObject(parent) -{ - buffPtr = data; - - init(); -} - -Idm::Idm(QObject *parent) : QObject(parent) -{ - init(); -} - -void Idm::init() -{ - header = 0; - intSize = DEFAULT_INT_SIZE; - keyLen = DEFAULT_KEY_LEN; - flags = DEFAULT_FLAGS; -} - -void Idm::loadData(const QByteArray &data) -{ - op(RELOAD, data); -} - -QByteArray &Idm::getBuff() -{ - op(GET_BUFF, QByteArray(), QByteArray(), new QByteArray()); - - return buffPtr; -} - -bool Idm::rdHeader() -{ - return rdExHeader(buffPtr); -} - -bool Idm::rdExHeader(const QByteArray &data) -{ - bool ret; - - op(RD_HEADER, data, QByteArray(), 0, 0, 0, &ret); - - return ret; -} - -bool Idm::rdExHeader(const QByteArray &data, int &major, int &minor, int &intSz, int &flgs, int &keyLen) -{ - bool ret = false; - - if (data.startsWith(TAG)) - { - int pos = TAG.size(); - - major = rdInt(data.mid(pos++, 1)); - minor = rdInt(data.mid(pos++, 1)); - intSz = rdInt(data.mid(pos++, 1)); - keyLen = rdInt(data.mid(pos++, 1)); - flgs = rdInt(data.mid(pos, 4)); - - if ((major <= MAX_MAJOR) && (minor <= MAX_MINOR)) - { - ret = true; - } - } - - return ret; -} - -bool Idm::verifyExHeader(const QByteArray &data, int major, int minor) -{ - bool ret = false; - - if (data.startsWith(TAG) && (data.size() == 11)) - { - int pos = TAG.size(); - int ma = rdInt(data.mid(pos++, 1)); - int mi = rdInt(data.mid(pos++, 1)); - - ret = ((ma <= major) && (mi <= minor)); - } - - return ret; -} - -QByteArray Idm::buildHeader(int major, int minor, int intSz, int flgs, int keyLen) -{ - QByteArray ret = TAG; - - ret.append(wrInt(major, 1)); - ret.append(wrInt(minor, 1)); - ret.append(wrInt(intSz, 1)); - ret.append(wrInt(keyLen, 1)); - ret.append(wrInt(flgs, 4)); - - return ret; -} - -void Idm::setKeyLen(int len) -{ - op(SET_KEY_LEN, QByteArray(), QByteArray(), 0, len); -} - -void Idm::setFlags(int flgs) -{ - op(SET_FLAGS, QByteArray(), QByteArray(), 0, flgs); -} - -void Idm::wrHeader() -{ - op(WR_HEADER); -} - -void Idm::setIntSize(int bytes) -{ - op(SET_INT_SIZE, QByteArray(), QByteArray(), 0, bytes); -} - -void Idm::setVersion(int major, int minor) -{ - op(SET_VERSION, QByteArray(), QByteArray(), 0, major, minor); -} - -QByteArray Idm::rdData(int addr, int len, bool *ret) -{ - bool ok = false; - - if ((addr < buffPtr.size()) && (addr >= 0)) - { - if ((len + addr) <= buffPtr.size()) - { - ok = true; - } - } - - if (ret) *ret = ok; - - if (ok) - { - return QByteArray::fromRawData(buffPtr.data() + addr, len); - } - else - { - return QByteArray(); - } -} - -bool Idm::findKey(int &addr, int &dataLen, const QByteArray &key) -{ - bool ok = true; - bool found = false; - - while(addr < buffPtr.size() && ok) - { - int keySize; - - if (flags & FIXED_KEY_LEN) - { - keySize = keyLen; - } - else - { - keySize = rdInt(rdData(addr, intSize, &ok)); - - addr += intSize; - } - - if (ok) - { - if (key == rdData(addr, keySize, &ok)) - { - found = true; - } - - addr += keySize; - - if (ok) - { - dataLen = rdInt(rdData(addr, intSize, &ok)); - - addr += intSize; - - if (found && ok) - { - break; - } - else - { - addr += dataLen; - } - } - } - } - - return found; -} - -QByteArray Idm::value(const QByteArray &key) -{ - QByteArray ret; - - op(RD_VALUE, key, QByteArray(), &ret); - - return ret; -} - -QByteArray Idm::value(quint64 key) -{ - int keySize; - - if (flags & FIXED_KEY_LEN) keySize = keyLen; - else keySize = intSize; - - return value(wrInt(key, keySize)); -} - -quint64 Idm::intValue(const QByteArray &key) -{ - return rdInt(value(key)); -} - -quint64 Idm::intValue(quint64 key) -{ - return rdInt(value(key)); -} - -bool Idm::remove(const QByteArray &key) -{ - bool ret; - - op(RM_VALUE, key, QByteArray(), 0, 0, 0, &ret); - - return ret; -} - -bool Idm::remove(quint64 key) -{ - int keySize; - - if (flags & FIXED_KEY_LEN) keySize = keyLen; - else keySize = intSize; - - return remove(wrInt(key, keySize)); -} - -bool Idm::insert(const QByteArray &key, const QByteArray &data) -{ - bool ret; - - op(WR_VALUE, key, data, 0, 0, 0, &ret); - - return ret; -} - -bool Idm::insert(quint64 key, quint64 num) -{ - int keySize; - - if (flags & FIXED_KEY_LEN) keySize = keyLen; - else keySize = intSize; - - return insert(wrInt(key, keySize), wrInt(num, intSize)); -} - -bool Idm::insert(quint64 key, const QByteArray &data) -{ - int keySize; - - if (flags & FIXED_KEY_LEN) keySize = keyLen; - else keySize = intSize; - - return insert(wrInt(key, keySize), data); -} - -void Idm::op(Operation opr, - const QByteArray &inA, - const QByteArray &inB, - QByteArray *out, - int intA, - int intB, - bool *ok) -{ - QMutex m; - - m.lock(); - - switch(opr) - { - case SET_KEY_LEN: - { - keyLen = intA; break; - } - case SET_FLAGS: - { - flags = intA; break; - } - case GET_BUFF: - { - out = &buffPtr; break; - } - case RELOAD: - { - buffPtr = inA; break; - } - case SET_INT_SIZE: - { - intSize = intA; break; - } - case SET_VERSION: - { - CURRENT_MAJOR = intA; - CURRENT_MINOR = intB; - - break; - } - case WR_HEADER: - { - if (buffPtr.startsWith(TAG)) - { - int pos = TAG.size(); - - buffPtr.replace(pos++, 1, wrInt(CURRENT_MAJOR, 1)); - buffPtr.replace(pos++, 1, wrInt(CURRENT_MINOR, 1)); - buffPtr.replace(pos++, 1, wrInt(intSize, 1)); - buffPtr.replace(pos++, 1, wrInt(keyLen, 1)); - buffPtr.replace(pos++, 4, wrInt(flags, 4)); - } - else - { - buffPtr.insert(0, wrInt(flags, 4)); - buffPtr.insert(0, wrInt(keyLen, 1)); - buffPtr.insert(0, wrInt(intSize, 1)); - buffPtr.insert(0, wrInt(CURRENT_MINOR, 1)); - buffPtr.insert(0, wrInt(CURRENT_MAJOR, 1)); - buffPtr.insert(0, TAG); - } - - header = 8 + TAG.size(); - - break; - } - case RD_HEADER: - { - *ok = rdExHeader(inA, CURRENT_MAJOR, CURRENT_MINOR, intSize, flags, keyLen); - - if (*ok) header = 8 + TAG.size(); - - break; - } - case RD_VALUE: - { - int pos = header; - int len = 0; - - if (findKey(pos, len, inA)) - { - out->clear(); - out->append(buffPtr.mid(pos, len)); - } - - break; - } - case RM_VALUE: - { - int pos = header; - int len = 0; - - *ok = false; - - if (findKey(pos, len, inA)) - { - if (flags & FIXED_KEY_LEN) - { - pos -= keyLen + intSize; - len += keyLen + intSize; - } - else - { - pos -= inA.size() + (intSize * 2); - len += inA.size() + (intSize * 2); - } - - buffPtr.remove(pos, len); - - *ok = true; - } - - break; - } - case WR_VALUE: - { - *ok = false; - - remove(inA); - - if (flags & FIXED_KEY_LEN) - { - if (inA.size() == keyLen) - { - buffPtr.append(inA + wrInt(inB.size(), intSize) + inB); - - *ok = true; - } - } - else - { - if (!inA.isEmpty()) - { - buffPtr.append(wrInt(inA.size(), intSize) + inA); - buffPtr.append(wrInt(inB.size(), intSize) + inB); - - *ok = true; - } - } - - break; - } - } - - m.unlock(); -} diff --git a/io/idm.h b/io/idm.h deleted file mode 100644 index 67050f5..0000000 --- a/io/idm.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef IDM_H -#define IDM_H - -#include -#include -#include - -#include "int.h" - -class Idm : public QObject -{ - Q_OBJECT - -public: - - enum Flags - { - FIXED_KEY_LEN = 1 - }; - - static const QByteArray TAG; - static const int MAX_MAJOR; - static const int MAX_MINOR; - static int CURRENT_MAJOR; - static int CURRENT_MINOR; - static int DEFAULT_INT_SIZE; - static int DEFAULT_FLAGS; - static int DEFAULT_KEY_LEN; - - static bool verifyExHeader(const QByteArray &data, int major, int minor); - static bool rdExHeader(const QByteArray &data, int &major, int &minor, int &intSz, int &flgs, int &keyLen); - static QByteArray buildHeader(int major, int minor, int intSz, int flgs, int keyLen); - - explicit Idm(const QByteArray &data, QObject *parent = 0); - explicit Idm(QObject *parent = 0); - - void setVersion(int major, int minor); - void setFlags(int flgs); - void setIntSize(int bytes); - void wrHeader(); - void setKeyLen(int len); - void loadData(const QByteArray &data); - bool insert(const QByteArray &key, const QByteArray &data); - bool insert(quint64 key, quint64 num); - bool insert(quint64 key, const QByteArray &data); - bool remove(const QByteArray &key); - bool remove(quint64 key); - bool rdHeader(); - bool rdExHeader(const QByteArray &data); - quint64 intValue(quint64 key); - quint64 intValue(const QByteArray &key); - QByteArray value(const QByteArray &key); - QByteArray value(quint64 key); - QByteArray &getBuff(); - -private: - - enum Operation - { - RELOAD, - GET_BUFF, - RD_HEADER, - WR_HEADER, - SET_KEY_LEN, - SET_FLAGS, - SET_INT_SIZE, - SET_VERSION, - RD_VALUE, - WR_VALUE, - RM_VALUE - }; - - QByteArray buffPtr; - int header; - int intSize; - int keyLen; - int flags; - - void op(Operation opr, - const QByteArray &inA = QByteArray(), - const QByteArray &inB = QByteArray(), - QByteArray *out = 0, - int intA = 0, - int intB = 0, - bool *ok = 0); - - void init(); - bool findKey(int &addr, int &dataLen, const QByteArray &key); - QByteArray rdData(int, int, bool *ok = 0); -}; - -#endif // IDM_H diff --git a/io/int.cpp b/io/int.cpp deleted file mode 100644 index 51b2f12..0000000 --- a/io/int.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "int.h" - -quint64 rdInt(const QByteArray &data) -{ - quint64 out = 0; - quint64 outBit = 1; - - for (int i = data.size() - 1; i >= 0; --i) - { - int byte = data[i]; - - for (int inBit = 1; inBit <= 128; inBit *= 2, outBit *= 2) - { - if ((byte & inBit) != 0) out |= outBit; - } - } - - return out; -} - -float rdFloat(const QByteArray &data) -{ - return data.toFloat(); -} - -QByteArray wrInt(quint64 value, int intSize) -{ - QByteArray out; - quint64 inBit = 1; - - while (value != 0) - { - int byte = 0; - - for (int outBit = 1; outBit <= 128; outBit *= 2, inBit *= 2) - { - if (value & inBit) - { - byte |= outBit; - value ^= inBit; - } - } - - out.insert(0, byte); - } - - return out.rightJustified(intSize, 0); -} - -QByteArray wrInt(quint64 value) -{ - return wrInt(value, INT_SIZE); -} - -QByteArray wrFloat(float value) -{ - return QByteArray().setNum(value); -} diff --git a/io/int.h b/io/int.h deleted file mode 100644 index c18ad02..0000000 --- a/io/int.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef INT_H -#define INT_H - -#include -#include - -#define INT_SIZE 6 - -float rdFloat(const QByteArray &data); -quint64 rdInt(const QByteArray &data); -QByteArray wrInt(quint64 value); -QByteArray wrInt(quint64 value, int); -QByteArray wrFloat(float value); - -#endif // INT_H diff --git a/logo.ico b/logo.ico new file mode 100644 index 0000000..1c6801a Binary files /dev/null and b/logo.ico differ diff --git a/logo.rc b/logo.rc index 725c431..48a36e0 100644 --- a/logo.rc +++ b/logo.rc @@ -1 +1 @@ -IDI_ICON1 ICON DISCARDABLE "app_logo.ico" +IDI_ICON1 ICON DISCARDABLE "logo.ico" diff --git a/main.cpp b/main.cpp index 77ceec9..18785b3 100644 --- a/main.cpp +++ b/main.cpp @@ -1,15 +1,114 @@ #include -#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "gui/ui.h" +#include "aud_file.h" +#include "core.h" + +// This file is part of JustAudio. + +// JustAudio 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. + +// JustAudio 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 BashWire under the GPL.txt file. If not, see +// . int main(int argc, char *argv[]) { QApplication app(argc, argv); - Ui win(app.arguments()); + app.setApplicationName("JustAudio"); + app.setApplicationVersion("1.0.0"); app.setQuitOnLastWindowClosed(false); - win.show(); - return app.exec(); + QString path = QDir::homePath() + QDir::separator(); + +#ifndef Q_OS_WIN32 + + path.append(QString(".config") + QDir::separator()); + +#else + + path.append(QString("AppData") + QDir::separator()); + path.append(QString("Local") + QDir::separator()); + +#endif + + QDir dir; + + path.append(QApplication::applicationName()); + dir.mkpath(path); + + QDir::setCurrent(path); + + if (!QFile::exists(PROCESS_FILE)) + { + // QFileSystemWatcher will not work if the PROCESS_FILE file does not exists so + // a base file need to be created to insure it connects to a file. + + QFile file(PROCESS_FILE, &app); + + file.open(QFile::WriteOnly | QFile::Truncate); + file.write(" "); + file.close(); + } + + // using a shared memory segment to determine if an instance of the app is running. + // QSharedMemory::create() returns false if that is the case and true if not. + // if an instance of the app is already runnning, the requested file path to playback + // is written to PROCESS_FILE, that modification to the file is picked up by + // QFileSystemWatcher and then read by the currently running instance and starts + // playback of the file path written to PROCESS_FILE. + + QSharedMemory sharedMem(app.applicationName(), &app); + + if (sharedMem.create(SEGMENT_SIZE)) + { + QFileSystemWatcher processFileWatcher(&app); + QSystemTrayIcon trayIcon(&app); + QMediaPlayer player(&app); + Core core(&app); + + core.setFileWatcher(&processFileWatcher); + core.setPlayer(&player); + core.setTrayIcon(&trayIcon); + processFileWatcher.addPath(PROCESS_FILE); + trayIcon.setToolTip(app.applicationName() + " " + app.applicationVersion()); + trayIcon.setIcon(QIcon(":/png/logo")); + trayIcon.show(); + + if (app.arguments().size() > 1) + { + core.play(app.arguments()[1]); + } + + return app.exec(); + } + else + { + if (app.arguments().size() > 1) + { + QFile file(PROCESS_FILE, &app); + + file.open(QFile::WriteOnly | QFile::Truncate); + file.write(app.arguments()[1].toUtf8()); + file.close(); + } + + return 0; + } } diff --git a/png/logo.png b/png/logo.png new file mode 100644 index 0000000..4caaada Binary files /dev/null and b/png/logo.png differ diff --git a/png/restore.png b/png/restore.png deleted file mode 100644 index 912154b..0000000 Binary files a/png/restore.png and /dev/null differ diff --git a/release_notes.txt b/release_notes.txt new file mode 100644 index 0000000..af179ab --- /dev/null +++ b/release_notes.txt @@ -0,0 +1,10 @@ +5/6/17 - 1.0.0 + +- Made some major changes to the JustAudio project. +- The majority of user interaction is now in the system tray icon. There's no longer a traditional user interface window. +- Added a proper application version number, GPL and source code comments. +- The application will now allow only one instance and will play the file requested in the arguments in the currently running instance. +- Added mute control. +- Settings are now saved in csv file format instead of idm. +- Several bug fixes. +- Seeker removed for now. (might but it back in the future) \ No newline at end of file diff --git a/res.qrc b/res.qrc new file mode 100644 index 0000000..f8a9965 --- /dev/null +++ b/res.qrc @@ -0,0 +1,17 @@ + + + png/logo.png + png/open.png + png/pause.png + png/play.png + png/menu.png + png/next.png + png/prev.png + png/stop.png + png/volume_up.png + png/volume_down.png + + + GPL.txt + + diff --git a/svg/menu.svg b/svg/menu.svg deleted file mode 100644 index cd161c4..0000000 --- a/svg/menu.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/svg/next.svg b/svg/next.svg deleted file mode 100644 index d898ca7..0000000 --- a/svg/next.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/svg/open.svg b/svg/open.svg deleted file mode 100644 index d1d445a..0000000 --- a/svg/open.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/svg/pause.svg b/svg/pause.svg deleted file mode 100644 index 354d21f..0000000 --- a/svg/pause.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/svg/play.svg b/svg/play.svg deleted file mode 100644 index 4c4c1c0..0000000 --- a/svg/play.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/svg/prev.svg b/svg/prev.svg deleted file mode 100644 index 98433bd..0000000 --- a/svg/prev.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/svg/restore.svg b/svg/restore.svg deleted file mode 100644 index 952425f..0000000 --- a/svg/restore.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/svg/stop.svg b/svg/stop.svg deleted file mode 100644 index 9c1db42..0000000 --- a/svg/stop.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/svg/volume_down.svg b/svg/volume_down.svg deleted file mode 100644 index a5bd99c..0000000 --- a/svg/volume_down.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/svg/volume_up.svg b/svg/volume_up.svg deleted file mode 100644 index ebc934f..0000000 --- a/svg/volume_up.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file