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