diff --git a/share/extensions/courtesy_accidentals/LICENSE b/share/extensions/courtesy_accidentals/LICENSE
new file mode 100644
index 0000000000000..e62ec04cdeece
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/LICENSE
@@ -0,0 +1,674 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/share/extensions/courtesy_accidentals/README.md b/share/extensions/courtesy_accidentals/README.md
new file mode 100644
index 0000000000000..fff21b70f3b6f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/README.md
@@ -0,0 +1,9 @@
+# Courtesy Accidentals
+A plugin for MuseScore 4 that adds courtesy accidentals to your score.
+Choose from a wide range of settings, to control when to add them and how they look!
+
+## Features
+- Add or remove courtesy accidentals from your score
+- Choose from a wide variety of settings
+- Live updating settings previews, to show you exactly what you get
+- Sleek & modern UI styled after MuseScore 4
diff --git a/share/extensions/courtesy_accidentals/add.js b/share/extensions/courtesy_accidentals/add.js
deleted file mode 100644
index a07f743687b9b..0000000000000
--- a/share/extensions/courtesy_accidentals/add.js
+++ /dev/null
@@ -1,292 +0,0 @@
-//==============================================
-// add courtesy accidentals v1.0
-//
-// Copyright (C)2012-2019 Jörn Eichler (heuchi)
-//
-// 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 .
-//==============================================
-
-
-function main() {
- curScore.startCmd()
- addAcc()
- curScore.endCmd()
-
- quit()
-}
-
-// configuration
-// This has changed for MuseScore v3
-// 0 = no bracket, 1 = parenthesis, 2 = bracket
-var useBracket = 0
-
-// if nothing is selected process whole score
-var processAll = false
-
-// function tpcName
-//
-// return name of note
-const TPC_NAMES = [
- "Fbb", "Cbb", "Gbb", "Dbb", "Abb", "Ebb", "Bbb",
- "Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb",
- "F", "C", "G", "D", "A", "E", "B",
- "F#", "C#", "G#", "D#", "A#", "E#", "B#",
- "F##", "C##", "G##", "D##", "A##", "E##", "B##"
- ];
-
-function tpcName(tpc) {
- return(TPC_NAMES[tpc+1]);
-}
-
-// function getEndStaffOfPart
-//
-// return the first staff that does not belong to
-// the part containing given start staff.
-
-function getEndStaffOfPart(startStaff) {
- var startTrack = startStaff * 4;
- var parts = curScore.parts;
-
- for(var i = 0; i < parts.length; i++) {
- var part = parts[i];
-
- if( (part.startTrack <= startTrack)
- && (part.endTrack > startTrack) ) {
- return(part.endTrack/4);
- }
- }
-
- // not found!
- console.log("error: part for " + startStaff + " not found!");
- quit();
-}
-
-// function processNote
-//
-// for each measure we create a table that contains
-// the actual 'noteName' of each 'noteClass'
-//
-// a 'noteClass' is the natural name of a space
-// or line of the staff and the octave:
-// C5, F6, B3 are 'noteClass'
-//
-// a 'noteName' would be C, F#, Bb for example
-// (we don't need the octave here)
-//
-// curMeasureArray[] =
-
-function processNote(note,prevMeasureArray,curMeasureArray) {
- var octave=Math.floor(note.pitch/12);
-
- // use tpc1 instead of tpc for octave correction
- // since this will also work for transposing instruments
- // correct octave for Cb and Cbb
- if(note.tpc1 == 7 || note.tpc1 == 0) {
- octave++; // belongs to higher octave
- }
- // correct octave for B# and B##
- if(note.tpc1 == 26 || note.tpc1 == 33) {
- octave--; // belongs to lower octave
- }
-
- var noteName = tpcName(note.tpc);
- var noteClass = noteName.charAt(0)+octave;
-
- // remember note for next measure
- curMeasureArray[noteClass]=noteName;
-
- // check if current note needs courtesy acc
- if(typeof prevMeasureArray[noteClass] !== 'undefined') {
- if(prevMeasureArray[noteClass] != noteName) {
- // this note needs an accidental
- // if there's none present anyway
- if(note.accidental == null) {
- // calculate type of needed accidental
- var accidental=Accidental.NONE;
- if(note.tpc < 6) {
- accidental = Accidental.FLAT2;
- } else if(note.tpc < 13) {
- accidental = Accidental.FLAT;
- } else if(note.tpc < 20) {
- accidental = Accidental.NATURAL;
- } else if(note.tpc < 27) {
- accidental = Accidental.SHARP;
- } else {
- accidental = Accidental.SHARP2;
- }
- note.accidentalType = accidental;
- // put bracket on accidental
- note.accidental.accidentalBracket = useBracket;
- }
- }
- // delete entry to make sure we don't create the
- // same accidental again in the same measure
- delete prevMeasureArray[noteClass];
- }
-}
-
-// function processPart
-//
-// do the actual work: process all given tracks in parallel
-// add courtesy accidentals where needed.
-//
-// We go through all tracks simultaneously, because we also want courtesy
-// accidentals for notes across different staves when they are in the
-// same octave and for notes of different voices in the same octave
-
-function processPart(cursor,endTick,startTrack,endTrack) {
- if(processAll) {
- // we need to reset track first, otherwise
- // rewind(0) doesn't work correctly
- cursor.track=0;
- cursor.rewind(0);
- } else {
- cursor.rewind(1);
- }
-
- var segment = cursor.segment;
-
- // we use the cursor to know measure boundaries
- cursor.nextMeasure();
-
- var curMeasureArray = [];
- var prevMeasureArray = [];
-
- // we use a segment, because the cursor always proceeds to
- // the next element in the given track and we don't know
- // in which track the element is.
- var inLastMeasure=false;
- while(segment && (processAll || segment.tick < endTick)) {
- // check if still inside same measure
- if(!inLastMeasure && !(segment.tick < cursor.tick)) {
- // new measure
- prevMeasureArray = curMeasureArray;
- curMeasureArray = [];
- if(!cursor.nextMeasure()) {
- inLastMeasure=true;
- }
- }
-
- // we search for key signatures in first voice of
- // first staff:
- var keySigTrack = startTrack - (startTrack % 4);
-
- for(var track=startTrack; track 0) {
- var graceChords = segment.elementAt(track).graceNotes;
-
- for(var j=0;j endStaff) {
- curEndStaff = endStaff;
- }
-
- // do the work
- processPart(cursor,endTick,curStartStaff*4,curEndStaff*4);
-
- // next part
- curStartStaff = curEndStaff;
- }
-
- console.log("end add courtesy accidentals");
-}
-
-
-
diff --git a/share/extensions/courtesy_accidentals/assets/AddAccItem.qml b/share/extensions/courtesy_accidentals/assets/AddAccItem.qml
new file mode 100644
index 0000000000000..f69c594f2b143
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/AddAccItem.qml
@@ -0,0 +1,57 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Layouts 1.2
+import MuseScore.Ui 1.0
+import MuseScore.UiComponents 1.0 as MU
+
+Item {
+ id: root
+
+ height: bracketBox.height //childrenRect.height
+ //requires indicated width
+
+ property alias checked: checkBox.checked
+ property alias currentValue: bracketBox.currentValue
+
+ signal clicked
+ signal setv(bool checked, int value)
+
+ MU.CheckBox {
+ id: checkBox
+ anchors.left: parent.left
+ anchors.verticalCenter: parent.verticalCenter
+ text: qsTr("Add Courtesy Accidentals")
+ onClicked: {checked = !checked; root.clicked()}
+ signal setv(bool checked)
+ onSetv: function(value) {checked = value; root.clicked()}
+ }
+ BracketBox {
+ id: bracketBox
+ anchors.right: parent.right
+ anchors.verticalCenter: parent.verticalCenter
+ enabled: checkBox.checked
+ onActivated: root.clicked()
+ }
+ onSetv: function(checked, value) {
+ checkBox.setv(checked)
+ bracketBox.setv(value)
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/BracketBox.qml b/share/extensions/courtesy_accidentals/assets/BracketBox.qml
new file mode 100644
index 0000000000000..e093296afeb41
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/BracketBox.qml
@@ -0,0 +1,58 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Layouts 1.2
+import MuseScore.Ui 1.0
+import MuseScore.UiComponents 1.0 as MU
+
+Row {
+ id: layout
+ spacing: 6
+ signal activated(int index, var value)
+ property var currentValue: control.currentValue
+ opacity: enabled ? 1.0 : ui.theme.itemOpacityDisabled
+ signal setv(int index)
+
+ MU.StyledTextLabel {
+ id: label
+ text: qsTr("Brackets:")
+ anchors.verticalCenter: control.verticalCenter
+ }
+
+ MU.StyledDropdown {
+ id: control
+ textRole: "text"
+ valueRole: "fact"
+ currentIndex: 0
+ model: [
+ {text: qsTr("None"), fact: 0},
+ {text: qsTr("Parentheses"), fact: 1},
+ {text: qsTr("Brackets"), fact: 2}
+ ]
+ onActivated: function(index, value) {
+ currentIndex = index
+ layout.activated(index, value)
+ }
+ }
+ onSetv: function(index) {
+ control.currentIndex = index
+ control.activated(index, Utils.getItemValue(control.model, index, control.valueRole, ""))
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/CancelModeItem.qml b/share/extensions/courtesy_accidentals/assets/CancelModeItem.qml
new file mode 100644
index 0000000000000..16e2a220676ef
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/CancelModeItem.qml
@@ -0,0 +1,52 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import MuseScore.Ui 1.0
+import MuseScore.UiComponents 1.0 as MU
+
+Column {
+ id: layout
+ property int value: radioButton1.checked ? 1 : 2
+ spacing: style.regSpace
+ opacity: enabled ? 1.0 : ui.theme.itemOpacityDisabled
+ width: parent.width
+
+ signal clicked
+ signal setv(int value)
+
+ MU.RoundedRadioButton {
+ id: radioButton1
+ implicitWidth: parent.width
+ text: qsTr("Stop after note is cancelled in original octave")
+ onClicked: layout.clicked()
+ checked: true
+ }
+ MU.RoundedRadioButton {
+ id: radioButton2
+ implicitWidth: parent.width
+ text: qsTr("Always cancel in all octaves")
+ onClicked: layout.clicked()
+ }
+ onSetv: function (nvalue) {
+ radioButton1.checked = nvalue == 1
+ radioButton2.checked = nvalue == 2
+ clicked()
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/DurationModeItem.qml b/share/extensions/courtesy_accidentals/assets/DurationModeItem.qml
new file mode 100644
index 0000000000000..dd677f74d6343
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/DurationModeItem.qml
@@ -0,0 +1,69 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import MuseScore.Ui 1.0
+import MuseScore.UiComponents 1.0 as MU
+
+GroupBox {
+ id: root
+ spacing: style.regSpace
+ anchors.topMargin: style.minSpace
+ opacity: enabled ? 1.0 : ui.theme.itemOpacityDisabled
+
+ property int value: radioButton0.checked ? 0 : (radioButton1.checked ? 1 : 2)
+
+ signal clicked
+ signal setv(int nvalue)
+
+ label: StyledLabel {text: qsTr("Add cautionary accidentals to:")}
+
+ Column {
+ spacing: style.regSpace
+ //leftPadding: style.regSpace
+ width: parent.width
+
+ MU.RoundedRadioButton {
+ id: radioButton0
+ implicitWidth: parent.width
+ text: qsTr("All notes after the note with accidental")
+ onClicked: root.clicked()
+ checked: true
+ }
+ MU.RoundedRadioButton {
+ id: radioButton1
+ implicitWidth: parent.width
+ text: qsTr("Notes on the same beat as the note with accidental")
+ onClicked: root.clicked()
+ }
+ MU.RoundedRadioButton {
+ id: radioButton2
+ implicitWidth: parent.width
+ text: qsTr("Notes played any time throughout the note with accidental")
+ onClicked: root.clicked()
+ }
+ }
+ onSetv: function (nvalue) {
+ radioButton0.checked = nvalue == 0
+ radioButton1.checked = nvalue == 1
+ radioButton2.checked = nvalue == 2
+ clicked()
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/DynamicImage.qml b/share/extensions/courtesy_accidentals/assets/DynamicImage.qml
new file mode 100644
index 0000000000000..84c3839ec85ed
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/DynamicImage.qml
@@ -0,0 +1,43 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Layouts 1.2
+import MuseScore.Ui 1.0
+
+Rectangle {
+ property int realWidth: 400
+ property int cornerRadius: style.regSpace
+ property var source: "logo.png" // something
+ radius: cornerRadius
+ border.color: ui.theme.accentColor
+ border.width: 2
+ //Layout.preferredWidth: realWidth
+ width: parent.width
+ height: image.height + cornerRadius
+ //anchors.horizontalCenter: parent.horizontalCenter
+ Image {
+ id: image
+ source: parent.source
+ width: parent.width - cornerRadius
+ anchors.centerIn: parent
+ fillMode: Image.PreserveAspectFit // ensure it fits
+ mipmap: true // smoothing
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/GraceNotesCheckBox.qml b/share/extensions/courtesy_accidentals/assets/GraceNotesCheckBox.qml
new file mode 100644
index 0000000000000..ddb3d303b635f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/GraceNotesCheckBox.qml
@@ -0,0 +1,32 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.15
+import MuseScore.UiComponents 1.0 as MU
+
+
+MU.CheckBox {
+ property bool key: false
+ text: key ? qsTr("Add courtesy if note before key change is a grace note")
+ : qsTr("Add courtesy if note with accidental is a grace note")
+ signal changed
+ onClicked: {checked = !checked; changed()}
+ signal setv(bool checked)
+ onSetv: function(value) {checked = value; changed()}
+}
diff --git a/share/extensions/courtesy_accidentals/assets/MainMenuSection.qml b/share/extensions/courtesy_accidentals/assets/MainMenuSection.qml
new file mode 100644
index 0000000000000..97709d5180f45
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/MainMenuSection.qml
@@ -0,0 +1,45 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import MuseScore.UiComponents 1.0 as MU
+import MuseScore.Ui 1.0
+
+Column {
+ id: root
+ property alias title: menuButton.title
+ property alias isExpanded: menuButton.isExpanded
+ default property alias content: column.children
+
+ width: parent.width
+ spacing: 0
+
+ MenuButton {
+ id: menuButton
+ }
+ Column {
+ id: column
+ spacing: 0
+ padding: style.regSpace
+ topPadding: 0
+ bottomPadding: 0
+ width: parent.width
+ visible: root.isExpanded
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/MenuButton.qml b/share/extensions/courtesy_accidentals/assets/MenuButton.qml
new file mode 100644
index 0000000000000..f51637c78f15c
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/MenuButton.qml
@@ -0,0 +1,44 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import MuseScore.UiComponents 1.0 as MU
+import MuseScore.Ui 1.0
+
+Item {
+ height: toggle.height + 2 * style.regSpace
+ width: parent.width //fix
+
+ property alias title: toggle.title
+ property alias isExpanded: toggle.isExpanded
+
+ MU.ExpandableBlankSection {
+ id: toggle
+ isExpanded: false
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ leftMargin: style.regSpace
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: toggle.isExpanded = !toggle.isExpanded
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/OptionalCancelModeItem.qml b/share/extensions/courtesy_accidentals/assets/OptionalCancelModeItem.qml
new file mode 100644
index 0000000000000..38e2fe048128b
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/OptionalCancelModeItem.qml
@@ -0,0 +1,56 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+import MuseScore.Ui 1.0
+import MuseScore.UiComponents 1.0 as MU
+
+GroupBox {
+ id: root
+ opacity: enabled ? 1.0 : ui.theme.itemOpacityDisabled
+
+ property alias checked: checkBox.checked // both values are currently needed separately, but may be combinable in future
+ property alias value: cancelModeItem.value
+
+ signal clicked
+ signal setv(bool checked, int value)
+
+ label: MU.CheckBox {
+ id: checkBox
+ enabled: root.enabled
+ opacity: enabled ? 1.0 : ui.theme.itemOpacityDisabled
+ text: qsTr("Add cautionary accidentals to notes in any octave")
+ checked: false
+ onClicked: {checked = !checked; root.clicked()}
+ signal setv(bool checked)
+ onSetv: function(value) {checked = value}
+ }
+
+ CancelModeItem {
+ id: cancelModeItem
+ enabled: root.enabled && checkBox.checked
+ onClicked: root.clicked()
+ }
+
+ onSetv: function(checked, value) {
+ checkBox.setv(checked)
+ cancelModeItem.setv(value)
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/PluginStyle.qml b/share/extensions/courtesy_accidentals/assets/PluginStyle.qml
new file mode 100644
index 0000000000000..f840778fbe93e
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/PluginStyle.qml
@@ -0,0 +1,27 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import MuseScore.Ui 1.0
+
+QtObject {
+ readonly property int maxSpace: 15
+ readonly property int regSpace: 10
+ readonly property int minSpace: 5
+}
diff --git a/share/extensions/courtesy_accidentals/assets/StyledFrame.qml b/share/extensions/courtesy_accidentals/assets/StyledFrame.qml
new file mode 100644
index 0000000000000..e79a8415eb78e
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/StyledFrame.qml
@@ -0,0 +1,28 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import MuseScore.Ui 1.0
+
+Frame {
+ background: Rectangle {color: "transparent"; border.color: ui.theme.strokeColor}
+ padding: style.regSpace
+ width: parent.width
+}
diff --git a/share/extensions/courtesy_accidentals/assets/StyledLabel.qml b/share/extensions/courtesy_accidentals/assets/StyledLabel.qml
new file mode 100644
index 0000000000000..9e4d00d506497
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/StyledLabel.qml
@@ -0,0 +1,26 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import MuseScore.Ui 1.0
+
+Label {
+ font: ui.theme.bodyFont
+}
diff --git a/share/extensions/courtesy_accidentals/assets/StyledSeparatorLine.qml b/share/extensions/courtesy_accidentals/assets/StyledSeparatorLine.qml
new file mode 100644
index 0000000000000..e4a72c01ed6cf
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/StyledSeparatorLine.qml
@@ -0,0 +1,29 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Layouts 1.2
+import MuseScore.UiComponents 1.0 as MU
+
+MU.SeparatorLine {
+ anchors.leftMargin: 0
+ anchors.rightMargin: 0
+ anchors.topMargin: style.regSpace
+ anchors.bottomMargin: style.regSpace
+}
diff --git a/share/extensions/courtesy_accidentals/assets/SubMenuSection.qml b/share/extensions/courtesy_accidentals/assets/SubMenuSection.qml
new file mode 100644
index 0000000000000..1cd08a5fad441
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/SubMenuSection.qml
@@ -0,0 +1,61 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import MuseScore.UiComponents 1.0 as MU
+import MuseScore.Ui 1.0
+
+Column {
+ id: root
+ property alias title: menuButton.title
+ property alias isExpanded: menuButton.isExpanded
+ default property alias content: column.children
+
+ property alias source: dynamicImage.source
+
+ width: parent.width
+ spacing: 0
+ anchors.margins: 0
+
+ MenuButton {
+ id: menuButton
+ }
+ StyledFrame {
+ visible: root.isExpanded
+ width: parent.width - 2 * root.parent.padding
+ padding: style.regSpace
+ bottomPadding: 0
+
+ Column {
+ spacing: 0
+ width: parent.width
+ //height: childrenRect.height
+
+ DynamicImage {id: dynamicImage}
+
+ Column {
+ id: column
+ spacing: style.regSpace
+ width: parent.width
+ //height: childrenRect.height
+ padding: style.regSpace
+ }
+ }
+ }
+}
diff --git a/share/extensions/courtesy_accidentals/assets/accidentals.js b/share/extensions/courtesy_accidentals/assets/accidentals.js
new file mode 100644
index 0000000000000..824ef9c2fec58
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/accidentals.js
@@ -0,0 +1,511 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+function tpcToName(tpc) {
+ var tpcNames = [ //-1 thru 33
+ "Fbb", "Cbb", "Gbb", "Dbb", "Abb", "Ebb", "Bbb",
+ "Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb",
+ "F", "C", "G", "D", "A", "E", "B",
+ "F#", "C#", "G#", "D#", "A#", "E#", "B#",
+ "F##", "C##", "G##", "D##", "A##", "E##", "B##"
+ ]
+ return tpcNames[tpc+1]
+}
+
+function runPlugin(type) {
+ curScore.startCmd()
+ var full = false;
+ if (!curScore.selection.elements.length) {
+ console.log("No selection. Applying plugin to all notes...")
+ cmd("select-all")
+ full = true;
+ } else {
+ console.log("Applying plugin to selection")
+ }
+ switch (type) {
+ case "add": {
+ addCourtesyAccidentals()
+ break;
+ }
+ case "remove": {
+ removeCourtesyAccidentals()
+ break;
+ }
+ default: console.warn("Unknown action requested.")
+ }
+ if (full) {
+ curScore.selection.clear()
+ }
+ curScore.endCmd()
+}
+
+function addCourtesyAccidentals() {
+ if (options.uSettings && JSON.parse(options.uSettings).edited) {
+ loadSettings(JSON.parse(options.uSettings))
+ } else {
+ loadSettings(DSettings.read())
+ }
+ var notes = []
+ for (var i in curScore.selection.elements) {
+ if (curScore.selection.elements[i].type == Element.NOTE && !curScore.selection.elements[i].staff.part.hasDrumStaff) {
+ notes.push(curScore.selection.elements[i])
+ }
+ }
+
+ // Exception: Only 1 note is selected
+ if (notes.length == 1) {
+ restateAccidental(notes[0], false, 0)
+ return
+ }
+
+ notes.sort(function (a,b) {
+ //sort notes by tick, prioritise notes with accidentals, prioritise non-doubles to avoid excessive brackets
+ if (isSameTick(a,b)) {
+ var testCount = 0
+ if (a.accidental) {
+ testCount--
+ }
+ if (b.accidental) {
+ testCount++
+ }
+ if (testCount == 0) {
+ if (a.accidentalType == Accidental.SHARP2 || a.accidentalType == Accidental.FLAT2) {
+ testCount++
+ }
+ if (b.accidentalType == Accidental.SHARP2 || b.accidentalType == Accidental.FLAT2) {
+ testCount++//-- ??
+ }
+ }
+ return testCount
+ } else {
+ return (tickOfNote(a) - tickOfNote(b))
+ }
+ })
+
+ for (var i = notes.length-1; i >= 0; i--) {
+ if (notes[i].accidental && notes[i].accidental.visible) {
+ var notes2 = notes.slice(0)
+ addAccidentals(notes2.splice(i, notes2.length)) //notes.subarray non-functional
+ } else {
+ if (setting7.addAccidentals || setting8.addAccidentals) {
+ var notes2 = notes.slice(0)
+ keySigTest(notes2.splice(i, notes2.length))
+ }
+ }
+ }
+}
+
+function addAccidentals(noteList) {
+ var testNote = noteList.shift()
+ var testName = tpcToNote(testNote.tpc)
+ console.log("Note with accidental found (" + tpcToName(testNote.tpc) + ").\r\n"
+ + "Attempting to add cautionary accidentals to " + noteList.length + " note(s).")
+ var cancelledNotes = []
+ for (var j in noteList) {
+ var note = noteList[j]
+ var changeNote = false
+ var changeBracket = []
+ if (!note.tieBack) {
+ if (setting1.addAccidentals) {
+ if (isSameNoteName(note, testNote) && !isSamePitch(note, testNote) &&
+ isSameMeasure(note, testNote) && isSameStaff(note, testNote) && (setting1.parseGraceNotes || !isGraceNote(testNote))) {
+ if (durationModeIsValid(setting1.durationMode, note, testNote)) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && ((setting9.a && isSamePitch(testNote, cancelledNotes[k])) ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k]) && isSameMeasure(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ changeNote = true
+ changeBracket.push(setting1.bracketType)
+ if (isSameNoteName(note, testNote) && isSameStaff(note, testNote)) {
+ //isSameStaff might not be needed here
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+ }
+
+ if (setting2.addAccidentals) {
+ if (isSameNoteName(note, testNote) && !isSamePitch(note, testNote) && isSameOctave(note, testNote) && isSameMeasure(note, testNote) &&
+ !isSameStaff(note, testNote) && isSamePart(note, testNote) && (setting2.parseGraceNotes || !isGraceNote(testNote))) {
+ if (durationModeIsValid(setting2.durationMode, note, testNote)) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && ((setting9.a && isSamePitch(testNote, cancelledNotes[k])) ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k]) && isSameMeasure(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ changeNote = true
+ changeBracket.push(setting2.bracketType)
+ if (isSameNoteName(note, testNote) && isSamePart(note, testNote)) {
+ //isSamePart might not be needed here
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+ }
+
+ if (setting3.addAccidentals) {
+ if (isSameNoteName(note, testNote) && !isSamePitch(note, testNote) && !isSameOctave(note, testNote) && isSameMeasure(note, testNote) &&
+ !isSameStaff(note, testNote) && isSamePart(note, testNote) && (setting3.parseGraceNotes || !isGraceNote(testNote))) {
+ if (durationModeIsValid(setting3.durationMode, note, testNote)) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && ((setting9.a && isSamePitch(testNote, cancelledNotes[k])) ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k]) && isSameMeasure(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ changeNote = true
+ changeBracket.push(setting3.bracketType)
+ if (isSameNoteName(note, testNote) && isSamePart(note, testNote)) {
+ //isSamePart might not be needed here
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+ }
+
+ if (setting4.a.addAccidentals) {
+ if (isSameNoteName(note, testNote) && !isSamePitch(note, testNote) &&
+ (isNextMeasure(note, testNote) || isNextMeasure(note, testNote.lastTiedNote)) && isSameStaff(note, testNote)) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && ((setting9.b && isSamePitch(testNote, cancelledNotes[k])) ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k]) && isSameMeasure(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ if (isSameOctave(note, testNote) && (setting4.parseGraceNotes || !isGraceNote(testNote))) {
+ changeNote = true
+ changeBracket.push(setting4.bracketType)
+ } else if (setting4.b.addAccidentals && (setting4.b.parseGraceNotes || !isGraceNote(testNote))) {
+ changeNote = true
+ changeBracket.push(setting4.bracketType)
+ }
+ if (isSameNoteName(note, testNote) && isSameStaff(note, testNote)) {
+ //isSameStaff might not be needed here
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+
+ if (setting5.a.addAccidentals) {
+ if (isSameNoteName(note, testNote) && !isSamePitch(note, testNote) &&
+ (isNextMeasure(note, testNote) || isNextMeasure(note, testNote.lastTiedNote)) && !isSameStaff(note, testNote) && isSamePart(note, testNote)) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && ((setting9.b && isSamePitch(testNote, cancelledNotes[k])) ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k]) && isSameMeasure(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ if (isSameOctave(note, testNote) && (setting5.a.parseGraceNotes || !isGraceNote(testNote))) {
+ changeNote = true
+ changeBracket.push(setting5.bracketType)
+ } else if (setting5.b.addAccidentals && (setting5.b.parseGraceNotes || !isGraceNote(testNote))) {
+ changeNote = true
+ changeBracket.push(setting5.b.bracketType)
+ }
+ if (isSameNoteName(note, testNote) && isSamePart(note, testNote)) {
+ //isSamePart might not be needed here
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+
+ if (setting6.a.addAccidentals) {
+ if (isSameNoteName(note, testNote) && isSamePitch(note, testNote) && isGraceNote(testNote) && !isGraceNote(note) &&
+ isSameMeasure(note, testNote) && (setting6.b.addAccidentals ? isSamePart(note, testNote) : isSameStaff(note, testNote))) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && isSamePitch(note, cancelledNotes[k]) && isSameStaff(note, cancelledNotes[k])) {
+ //optional: change isSameStaff to (setting6.b.addAccidentals ? isSamePart(note, testNote) : isSameStaff(note, testNote))
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ changeNote = true
+ cancelledNotes.push(note)
+ if (isSameStaff(note, testNote)) {
+ changeBracket.push(setting6.a.bracketType)
+ } else {
+ changeBracket.push(setting6.b.bracketType)
+ }
+ }
+ }
+ }
+
+ if (changeNote) {
+ if (isSameTick(note, testNote) && (testNote.tpc > 26 || testNote.tpc < 6)) {
+ changeBracket.push(0) //dont add brackets to reduced accidentals on same beat //TODO: same measure?
+ }
+ changeBracket.sort()
+ restateAccidental(note, shouldCancelDouble(testNote), changeBracket[0])
+ if (isSameNoteName(note, testNote) && isSameOctave(note, testNote)) {
+ cancelledNotes.push(note)
+ //only stop adding cautionary accidentals if note is of the same octave
+ }
+ }
+ }
+ }
+}
+
+function keySigTest(noteList) {
+ var testNote = noteList.shift()
+ var testName = tpcToNote(testNote.tpc)
+ console.log("Testing for key signature changes")
+ var cancelledNotes = []
+ for (var j in noteList) {
+ var note = noteList[j]
+ var changeNote = false
+ var changeBracket = []
+ if (!note.tieBack) {
+ if (setting7.addAccidentals) {
+ if (isSameNoteName(note, testNote) && (setting7.cancelOctaves ? !isOctavedPitch(note, testNote) : (isSameOctave(note, testNote) && !isSamePitch(note, testNote))) &&
+ note.accidentalType == Accidental.NONE && isNextMeasure(note, testNote) && isSameStaff(note, testNote) && (setting7.parseGraceNotes || !isGraceNote(testNote))) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && (setting7.cancelMode ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ changeNote = true
+ changeBracket.push(setting7.bracketType)
+ if (isSameNoteName(note, testNote) && isSameStaff(note, testNote) && (!setting7.cancelMode || isSameOctave(note, testNote))) {
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+
+ if (setting8.addAccidentals) {
+ if (isSameNoteName(note, testNote) && (setting8.cancelOctaves ? !isOctavedPitch(note, testNote) : (isSameOctave(note, testNote) && !isSamePitch(note, testNote))) &&
+ note.accidentalType == Accidental.NONE && isSameMeasure(note, testNote) && isSameStaff(note, testNote) && (setting8.parseGraceNotes || !isGraceNote(testNote))) {
+ var check = true
+ for (var k in cancelledNotes) {
+ if (isSameNoteName(note, cancelledNotes[k]) && (setting8.cancelMode ? isOctavedPitch(note, cancelledNotes[k]) : isSamePitch(note, cancelledNotes[k])) &&
+ isSameStaff(note, cancelledNotes[k])) {
+ console.log("The accidental in question has been cancelled, no need to add further cautionary accidentals")
+ check = false
+ break
+ }
+ }
+ if (check) {
+ changeNote = true
+ changeBracket.push(setting8.bracketType)
+ if (isSameNoteName(note, testNote) && isSameStaff(note, testNote) && (!setting8.cancelMode || isSameOctave(note, testNote))) {
+ cancelledNotes.push(note)
+ }
+ }
+ }
+ }
+
+ if (changeNote) {
+ if (isSameTick(note, testNote) && (testNote.tpc > 26 || testNote.tpc < 6)) {
+ changeBracket.push(0)
+ }
+ changeBracket.sort()
+ restateAccidental(note, shouldCancelDouble(testNote), changeBracket[0])
+ }
+ }
+ }
+}
+
+function loadSettings(settingObj) {
+ setting0 = settingObj.setting0
+ setting1 = settingObj.setting1
+ setting2 = settingObj.setting2
+ setting3 = settingObj.setting3
+ setting4 = settingObj.setting4
+ setting5 = settingObj.setting5
+ setting6 = settingObj.setting6
+ setting7 = settingObj.setting7
+ setting8 = settingObj.setting8
+ setting9 = settingObj.setting9
+}
+
+function tpcToNote(tpc) {
+ var noteNames = ["C", "G", "D", "A", "E", "B", "F"]
+ return noteNames[(tpc+7) % 7]
+}
+
+function removeCourtesyAccidentals() {
+ var notes = []
+ for (var i in curScore.selection.elements) {
+ if (curScore.selection.elements[i].type == Element.NOTE) {
+ destateAccidental(curScore.selection.elements[i])
+ }
+ }
+}
+
+function tickOfNote(note) {
+ return isGraceNote(note) ? note.parent.parent.parent.tick : note.parent.parent.tick
+}
+function isGraceNote(note) {
+ return note.noteType != 0
+}
+
+function isSameNoteName(note1, note2) {
+ return tpcToNote(note1.tpc) == tpcToNote(note2.tpc)
+}
+
+function isSamePitch(note1, note2) {
+ return note1.pitch == note2.pitch
+}
+
+function isSameOctave(note1, note2) {
+ //return 12 * Math.round(note1.pitch/12) == 12 * Math.round(note2.pitch/12)
+ return Math.abs(note1.pitch - note2.pitch) < 5
+ //only to be used in conjunction with isSameNoteName
+}
+
+function isOctavedPitch(note1, note2) {
+ return note1.pitch % 12 == note2.pitch % 12
+}
+
+function isSameTick(note1, note2) {
+ return tickOfNote(note1) == tickOfNote(note2)
+}
+
+function isSameBeat(note1, note2) {
+ return tickOfNote(note1) < (tickOfNote(note2) + durationOfNote(note2))
+}
+
+function durationOfNote(note) {
+ return isGraceNote(note) ? 0 : note.parent.duration.ticks
+}
+
+function isSameMeasure(note1, note2) {
+ return measureOf(note1).is(measureOf(note2))
+}
+
+function isNextMeasure(note1, note2) { // order is relevant here
+ return measureOf(note1).is(curScore.firstMeasure) ? false : measureOf(note1).prevMeasure.is(measureOf(note2))
+}
+
+function measureOf(note) {
+ return isGraceNote(note) ? note.parent.parent.parent.parent : note.parent.parent.parent
+}
+
+function isSameStaff(note1, note2) {
+ return note1.staff.is(note2.staff)
+}
+
+function isSamePart(note1, note2) {
+ return note1.staff.part.is(note2.staff.part)
+}
+
+function durationModeIsValid(durationMode, note, testNote) { // order is relevant here
+ return durationMode == 0 || (durationMode == 1 && isSameTick(note, testNote)) || (durationMode == 2 && isSameBeat(note, testNote))
+}
+
+function shouldCancelDouble(note) {
+ return setting0.addNaturals ? (note.tpc > 26 || note.tpc < 6) : false
+}
+
+function restateAccidental(note, cancelDouble, bracketType) {
+ var oldAccidental = note.accidentalType
+ var accidental = Accidental.NONE
+ switch (true) {
+ case (note.tpc > 26): {
+ accidental = Accidental.SHARP2
+ break
+ }
+ case (note.tpc > 19): {
+ if (cancelDouble) {
+ accidental = Accidental.NATURAL_SHARP
+ } else {
+ accidental = Accidental.SHARP
+ }
+ break
+ }
+ case (note.tpc > 12): {
+ accidental = Accidental.NATURAL
+ break
+ }
+ case (note.tpc > 5): {
+ if (cancelDouble) {
+ accidental = Accidental.NATURAL_FLAT
+ } else {
+ accidental = Accidental.FLAT
+ }
+ break
+ }
+ default: {
+ accidental = Accidental.FLAT2
+ }
+ }
+ if (accidental != oldAccidental) {
+ note.accidentalType = accidental
+ note.accidental.visible = note.visible
+ note.accidental.accidentalBracket = bracketType
+ console.log("Added a cautionary accidental to note " + tpcToName(note.tpc))
+ //0 = none, 1 = parentheses, 2 = brackets
+ }
+}
+
+function destateAccidental(note) {
+ if (note.accidental) {
+ var oldAccidental = note.accidentalType
+ if (note.accidentalType == Accidental.NATURAL_FLAT) {
+ oldAccidental = Accidental.FLAT
+ }
+ if (note.accidentalType == Accidental.NATURAL_SHARP) {
+ oldAccidental = Accidental.SHARP
+ }
+ }
+ var oldPitch = note.pitch
+ note.accidentalType = Accidental.NONE
+ if (note.pitch != oldPitch) {
+ note.accidentalType = oldAccidental
+ console.log("Keeping existing accidental for note " + tpcToName(note.tpc))
+ } else {
+ console.log("Removing accidental from note " + tpcToName(note.tpc))
+ }
+}
diff --git a/share/plugins/courtesy_accidentals/accidentals.png b/share/extensions/courtesy_accidentals/assets/accidentals.png
old mode 100755
new mode 100644
similarity index 100%
rename from share/plugins/courtesy_accidentals/accidentals.png
rename to share/extensions/courtesy_accidentals/assets/accidentals.png
diff --git a/share/extensions/courtesy_accidentals/assets/defaultsettings.js b/share/extensions/courtesy_accidentals/assets/defaultsettings.js
new file mode 100644
index 0000000000000..fdc2e5d680647
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/defaultsettings.js
@@ -0,0 +1,98 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+function read () {
+ var settings = {
+ version: "4.0-beta",
+ setting0: {
+ addNaturals: false
+ },
+ setting1: {
+ addAccidentals: true,
+ bracketType: 0,
+ parseGraceNotes: true,
+ durationMode: 0
+ },
+ setting2: {
+ addAccidentals: true,
+ bracketType: 0,
+ parseGraceNotes: true,
+ durationMode: 0
+ },
+ setting3: {
+ addAccidentals: true,
+ bracketType: 0,
+ parseGraceNotes: false,
+ durationMode: 0
+ },
+ setting4: {
+ a: {
+ addAccidentals: true,
+ bracketType: 0,
+ parseGraceNotes: false
+ },
+ b: {
+ addAccidentals: false,
+ bracketType: 0,
+ parseGraceNotes: false
+ }
+ },
+ setting5: {
+ a: {
+ addAccidentals: true,
+ bracketType: 0,
+ parseGraceNotes: false
+ },
+ b: {
+ addAccidentals: false,
+ bracketType: 0,
+ parseGraceNotes: false
+ }
+ },
+ setting6: {
+ a: {
+ addAccidentals: true,
+ bracketType: 0
+ },
+ b: {
+ addAccidentals: false,
+ bracketType: 0
+ }
+ },
+ setting7: {
+ addAccidentals: true,
+ bracketType: 0,
+ cancelOctaves: false,
+ parseGraceNotes: true,
+ cancelMode: true
+ },
+ setting8: {
+ addAccidentals: true,
+ bracketType: 0,
+ cancelOctaves: true,
+ parseGraceNotes: true,
+ cancelMode: true
+ },
+ setting9: {
+ a: true,
+ b: true
+ }
+ }
+ return settings
+}
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting0/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting0/example-false.svg
new file mode 100644
index 0000000000000..65181e2a846fa
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting0/example-false.svg
@@ -0,0 +1,79 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting0/example-true.svg b/share/extensions/courtesy_accidentals/assets/examples/setting0/example-true.svg
new file mode 100644
index 0000000000000..6a2d91e57333e
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting0/example-true.svg
@@ -0,0 +1,79 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting0/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting0/example.mscz
new file mode 100644
index 0000000000000..7c299f57144b6
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting0/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-00.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-00.svg
new file mode 100644
index 0000000000000..c29fb15ff35a1
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-00.svg
@@ -0,0 +1,63 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-01.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-01.svg
new file mode 100644
index 0000000000000..e91dd03cb1134
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-01.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-02.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-02.svg
new file mode 100644
index 0000000000000..04c1433d73bd9
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-02.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-10.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-10.svg
new file mode 100644
index 0000000000000..8c8c66c1b6129
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-10.svg
@@ -0,0 +1,71 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-11.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-11.svg
new file mode 100644
index 0000000000000..2b9a78f565fac
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-11.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-12.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-12.svg
new file mode 100644
index 0000000000000..80de5503591e9
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-12.svg
@@ -0,0 +1,65 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-20.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-20.svg
new file mode 100644
index 0000000000000..edfe789b42b5e
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-20.svg
@@ -0,0 +1,71 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-21.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-21.svg
new file mode 100644
index 0000000000000..6eca984b56b2a
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-21.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-22.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-22.svg
new file mode 100644
index 0000000000000..7e55ecd1eb797
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-22.svg
@@ -0,0 +1,65 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-false.svg
new file mode 100644
index 0000000000000..dc3fbd480f014
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting1/example-false.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting1/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting1/example.mscz
new file mode 100644
index 0000000000000..559cd20a1b484
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting1/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-00.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-00.svg
new file mode 100644
index 0000000000000..eae2f6eebcbcc
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-00.svg
@@ -0,0 +1,105 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-01.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-01.svg
new file mode 100644
index 0000000000000..a9e20b88b8f20
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-01.svg
@@ -0,0 +1,102 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-02.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-02.svg
new file mode 100644
index 0000000000000..45739db932207
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-02.svg
@@ -0,0 +1,104 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-10.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-10.svg
new file mode 100644
index 0000000000000..36d810678919a
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-10.svg
@@ -0,0 +1,117 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-11.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-11.svg
new file mode 100644
index 0000000000000..8b6db60b42962
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-11.svg
@@ -0,0 +1,108 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-12.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-12.svg
new file mode 100644
index 0000000000000..0deade0e1e396
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-12.svg
@@ -0,0 +1,114 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-20.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-20.svg
new file mode 100644
index 0000000000000..121fc6ac78947
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-20.svg
@@ -0,0 +1,117 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-21.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-21.svg
new file mode 100644
index 0000000000000..6db5926db8666
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-21.svg
@@ -0,0 +1,108 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-22.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-22.svg
new file mode 100644
index 0000000000000..2b062214ab815
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-22.svg
@@ -0,0 +1,114 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-false.svg
new file mode 100644
index 0000000000000..807497de103fa
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting2/example-false.svg
@@ -0,0 +1,100 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting2/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting2/example.mscz
new file mode 100644
index 0000000000000..51e8ac3410df3
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting2/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-00.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-00.svg
new file mode 100644
index 0000000000000..f99047a465c92
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-00.svg
@@ -0,0 +1,107 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-01.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-01.svg
new file mode 100644
index 0000000000000..f07cc383b2b12
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-01.svg
@@ -0,0 +1,103 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-02.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-02.svg
new file mode 100644
index 0000000000000..ce146398507fa
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-02.svg
@@ -0,0 +1,106 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-10.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-10.svg
new file mode 100644
index 0000000000000..fb5714c994c65
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-10.svg
@@ -0,0 +1,119 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-11.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-11.svg
new file mode 100644
index 0000000000000..a2850ba0e6057
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-11.svg
@@ -0,0 +1,107 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-12.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-12.svg
new file mode 100644
index 0000000000000..c6bf6d9002a8f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-12.svg
@@ -0,0 +1,116 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-20.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-20.svg
new file mode 100644
index 0000000000000..818542bf8f08f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-20.svg
@@ -0,0 +1,119 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-21.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-21.svg
new file mode 100644
index 0000000000000..7f05e3bce09e3
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-21.svg
@@ -0,0 +1,107 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-22.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-22.svg
new file mode 100644
index 0000000000000..945eebffb7de0
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-22.svg
@@ -0,0 +1,116 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-false.svg
new file mode 100644
index 0000000000000..d3c3540d34f05
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting3/example-false.svg
@@ -0,0 +1,101 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting3/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting3/example.mscz
new file mode 100644
index 0000000000000..b9b3272a6317a
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting3/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-00.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-00.svg
new file mode 100644
index 0000000000000..7499487e2ca3c
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-00.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-01.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-01.svg
new file mode 100644
index 0000000000000..a73ea04125e4c
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-01.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-02.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-02.svg
new file mode 100644
index 0000000000000..d46191259de6b
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-02.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-10.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-10.svg
new file mode 100644
index 0000000000000..8579098b3fb58
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-10.svg
@@ -0,0 +1,55 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-11.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-11.svg
new file mode 100644
index 0000000000000..da48fa66fdf57
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-11.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-12.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-12.svg
new file mode 100644
index 0000000000000..310fc6ba3d1cb
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-12.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-false.svg
new file mode 100644
index 0000000000000..f91ff44644da0
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example-false.svg
@@ -0,0 +1,53 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4a/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example.mscz
new file mode 100644
index 0000000000000..393913e67d6fa
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting4a/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-00.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-00.svg
new file mode 100644
index 0000000000000..810936d42cbb2
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-00.svg
@@ -0,0 +1,59 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-01.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-01.svg
new file mode 100644
index 0000000000000..d822c67ecf602
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-01.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-02.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-02.svg
new file mode 100644
index 0000000000000..7b4b461f1101b
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-02.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-10.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-10.svg
new file mode 100644
index 0000000000000..ab8c9e117bb7e
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-10.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-11.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-11.svg
new file mode 100644
index 0000000000000..818668917ad6a
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-11.svg
@@ -0,0 +1,64 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-12.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-12.svg
new file mode 100644
index 0000000000000..b606c9fdc1f58
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-12.svg
@@ -0,0 +1,64 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-false.svg
new file mode 100644
index 0000000000000..8e814021717c7
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example-false.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting4b/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example.mscz
new file mode 100644
index 0000000000000..4898e78816c46
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting4b/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-1.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-1.svg
new file mode 100644
index 0000000000000..8c8b1d151b39f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-1.svg
@@ -0,0 +1,82 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-2.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-2.svg
new file mode 100644
index 0000000000000..b5d6c06c70bbe
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-2.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-3.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-3.svg
new file mode 100644
index 0000000000000..dc02cb95378ca
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-3.svg
@@ -0,0 +1,85 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-4.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-4.svg
new file mode 100644
index 0000000000000..c53cfb45680e0
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example-4.svg
@@ -0,0 +1,85 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5a/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example.mscz
new file mode 100644
index 0000000000000..84d416d879347
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting5a/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-1.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-1.svg
new file mode 100644
index 0000000000000..5273ed04e4526
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-1.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-2.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-2.svg
new file mode 100644
index 0000000000000..61d81cc24ffa5
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-2.svg
@@ -0,0 +1,56 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-3.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-3.svg
new file mode 100644
index 0000000000000..5fe81cba08828
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-3.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-4.svg b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-4.svg
new file mode 100644
index 0000000000000..abbbcc864427a
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example-4.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting5b/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example.mscz
new file mode 100644
index 0000000000000..313c450249a03
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting5b/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting6/example-1.svg b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-1.svg
new file mode 100644
index 0000000000000..f47fafe614d6b
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-1.svg
@@ -0,0 +1,68 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting6/example-2.svg b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-2.svg
new file mode 100644
index 0000000000000..3e984ce5c01c8
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-2.svg
@@ -0,0 +1,69 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting6/example-3.svg b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-3.svg
new file mode 100644
index 0000000000000..f94497fbd2515
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-3.svg
@@ -0,0 +1,71 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting6/example-4.svg b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-4.svg
new file mode 100644
index 0000000000000..2facd8a086eb8
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting6/example-4.svg
@@ -0,0 +1,71 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting6/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting6/example.mscz
new file mode 100644
index 0000000000000..8d4045acd5468
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting6/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-000.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-000.svg
new file mode 100644
index 0000000000000..2f143fdc26a13
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-000.svg
@@ -0,0 +1,61 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-001.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-001.svg
new file mode 100644
index 0000000000000..89a26469c91a7
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-001.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-002.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-002.svg
new file mode 100644
index 0000000000000..a3d0112e8a5a3
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-002.svg
@@ -0,0 +1,63 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-010.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-010.svg
new file mode 100644
index 0000000000000..403c69dd26ff0
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-010.svg
@@ -0,0 +1,67 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-011.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-011.svg
new file mode 100644
index 0000000000000..002b588577167
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-011.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-012.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-012.svg
new file mode 100644
index 0000000000000..68c7bb5f524a2
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-012.svg
@@ -0,0 +1,73 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-020.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-020.svg
new file mode 100644
index 0000000000000..0d9d7a55a53d7
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-020.svg
@@ -0,0 +1,67 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-021.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-021.svg
new file mode 100644
index 0000000000000..2697f91a3af1b
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-021.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-022.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-022.svg
new file mode 100644
index 0000000000000..b402016570af5
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-022.svg
@@ -0,0 +1,73 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-100.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-100.svg
new file mode 100644
index 0000000000000..9c37adc8b04a3
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-100.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-101.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-101.svg
new file mode 100644
index 0000000000000..d38982e1bcf70
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-101.svg
@@ -0,0 +1,63 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-102.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-102.svg
new file mode 100644
index 0000000000000..826bfdbd059a6
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-102.svg
@@ -0,0 +1,64 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-110.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-110.svg
new file mode 100644
index 0000000000000..c806401aa7889
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-110.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-111.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-111.svg
new file mode 100644
index 0000000000000..2d5765b883074
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-111.svg
@@ -0,0 +1,73 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-112.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-112.svg
new file mode 100644
index 0000000000000..763bab9ebc974
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-112.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-120.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-120.svg
new file mode 100644
index 0000000000000..f1dbb4c520072
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-120.svg
@@ -0,0 +1,70 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-121.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-121.svg
new file mode 100644
index 0000000000000..f7f1019c2d630
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-121.svg
@@ -0,0 +1,73 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-122.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-122.svg
new file mode 100644
index 0000000000000..01db242d39507
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-122.svg
@@ -0,0 +1,76 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-false.svg
new file mode 100644
index 0000000000000..8ca8286bfc846
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting7/example-false.svg
@@ -0,0 +1,58 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting7/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting7/example.mscz
new file mode 100644
index 0000000000000..570a6917250e8
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting7/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-00.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-00.svg
new file mode 100644
index 0000000000000..25f843b9a350d
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-00.svg
@@ -0,0 +1,36 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-01.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-01.svg
new file mode 100644
index 0000000000000..0fa4a805cfb0f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-01.svg
@@ -0,0 +1,37 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-02.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-02.svg
new file mode 100644
index 0000000000000..deb47f5eb808b
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-02.svg
@@ -0,0 +1,38 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-10.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-10.svg
new file mode 100644
index 0000000000000..055176488a6de
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-10.svg
@@ -0,0 +1,40 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-11.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-11.svg
new file mode 100644
index 0000000000000..1e24fdbcc72f2
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-11.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-12.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-12.svg
new file mode 100644
index 0000000000000..db4d3d7492a36
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-12.svg
@@ -0,0 +1,46 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-20.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-20.svg
new file mode 100644
index 0000000000000..a2b2744f16b3f
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-20.svg
@@ -0,0 +1,40 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-21.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-21.svg
new file mode 100644
index 0000000000000..a1ede50707a40
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-21.svg
@@ -0,0 +1,43 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-22.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-22.svg
new file mode 100644
index 0000000000000..ad44c2891f427
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-22.svg
@@ -0,0 +1,46 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example-false.svg b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-false.svg
new file mode 100644
index 0000000000000..c9c0a4fde91ad
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting8/example-false.svg
@@ -0,0 +1,34 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting8/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting8/example.mscz
new file mode 100644
index 0000000000000..1e47460c9789b
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting8/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting9a/example-1.svg b/share/extensions/courtesy_accidentals/assets/examples/setting9a/example-1.svg
new file mode 100644
index 0000000000000..53614cc35ba93
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting9a/example-1.svg
@@ -0,0 +1,95 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting9a/example-2.svg b/share/extensions/courtesy_accidentals/assets/examples/setting9a/example-2.svg
new file mode 100644
index 0000000000000..a6fbbafdd0e33
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting9a/example-2.svg
@@ -0,0 +1,98 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting9a/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting9a/example.mscz
new file mode 100644
index 0000000000000..b013aeb950b6d
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting9a/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting9b/example-1.svg b/share/extensions/courtesy_accidentals/assets/examples/setting9b/example-1.svg
new file mode 100644
index 0000000000000..37aa50875413a
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting9b/example-1.svg
@@ -0,0 +1,60 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting9b/example-2.svg b/share/extensions/courtesy_accidentals/assets/examples/setting9b/example-2.svg
new file mode 100644
index 0000000000000..32d256ccbb9e8
--- /dev/null
+++ b/share/extensions/courtesy_accidentals/assets/examples/setting9b/example-2.svg
@@ -0,0 +1,62 @@
+
+
diff --git a/share/extensions/courtesy_accidentals/assets/examples/setting9b/example.mscz b/share/extensions/courtesy_accidentals/assets/examples/setting9b/example.mscz
new file mode 100644
index 0000000000000..cf9c36a3fabb4
Binary files /dev/null and b/share/extensions/courtesy_accidentals/assets/examples/setting9b/example.mscz differ
diff --git a/share/extensions/courtesy_accidentals/configure.qml b/share/extensions/courtesy_accidentals/configure.qml
index 5404462cd4e3a..4b133625ed03d 100644
--- a/share/extensions/courtesy_accidentals/configure.qml
+++ b/share/extensions/courtesy_accidentals/configure.qml
@@ -1,7 +1,7 @@
//==============================================
-// courtesy accidentals v1.0
-//
-// Copyright (C)2012-2019 Jörn Eichler (heuchi)
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
//
// 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
@@ -17,618 +17,659 @@
// along with this program. If not, see .
//==============================================
-import QtQuick 2.15
-import QtQuick.Controls 2.15
-import QtQuick.Layouts 1.15
-
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.2
import MuseScore 3.0
-import Muse.UiComponents 1.0
+import MuseScore.UiComponents 1.0 as MU
+import MuseScore.Ui 1.0
+import "assets"
+import "assets/defaultsettings.js" as DSettings
MuseScore {
+ title: qsTr("Configure Courtesy Accidentals")
+ description: qsTr("Choose when to add courtesy accidentals to your scores, and how they look.")
+ version: "4.0"
+ categoryCode: "composing-arranging-tools"
+ thumbnailName: "assets/accidentals.png"
+ requiresScore: false
+
+ //onRun: mainWindow.show()
+
+ ApplicationWindow {
+ id: mainWindow
+ minimumHeight: 400
+ minimumWidth: 480
+ background: Rectangle {color: ui.theme.backgroundSecondaryColor}
+ title: qsTr("Courtesy Accidentals: Settings")
+ flags: Qt.Dialog
+
+ MU.StyledFlickable {
+ id: flickable
+ anchors.fill: parent
+ focus: true
+ contentWidth: contentItem.childrenRect.width + 2 * mainColumn.x
+ contentHeight: contentItem.childrenRect.height + 2 * mainColumn.y
+ Keys.onUpPressed: scrollBar.decrease()
+ Keys.onDownPressed: scrollBar.increase()
+ ScrollBar.vertical: MU.StyledScrollBar {id: scrollBar}
+ Column {
+ id: mainColumn
+ spacing: 0
+ width: mainWindow.width
+
+ MainMenuSection {
+ title: qsTr("General Settings")
+ isExpanded: true
+
+ SubMenuSection {
+ id: setting0Image
+ title: qsTr("Double accidentals")
+
+ MU.CheckBox {
+ id: setting0Box
+ anchors.leftMargin: style.regSpace
+ text: qsTr("Use natural flats/sharps when cancelling double accidentals")
+ onClicked: {checked = !checked; updatesetting0Img()}
+ signal setv(bool checked)
+ onSetv: function(value) {checked = value; updatesetting0Img()}
+ }
+ }
- width: 344
- height: 330
-
- // configuration
- property bool useBracket: false
-
- property var typeNextMeasure: 1
- property var typeNumMeasures: 2
- property var typeEvent: 3
- property var typeDodecaphonic: 4
-
- property var eventFullRest: 1
- property var eventDoubleBar: 2
- property var eventRehearsalMark: 4
- property var eventEndScore: 8 // we don't really need this, but...
-
- property var operationMode;
- property var numMeasures;
- property var eventTypes;
-
- Component.onCompleted: {
- console.log("MuseScore Version = "+mscoreVersion);
- console.log("MajorVersion = "+mscoreMajorVersion);
- console.log("MinorVersion = "+mscoreMinorVersion);
- console.log("UpdateVersion= "+mscoreUpdateVersion);
-
- // These options don't work in MuseScore v3
- optDoubleBar.checked = false;
- optDoubleBar.enabled = false;
- optDoubleBar.opacity = 0.5;
- optFullRest.checked = false;
- optFullRest.enabled = false;
- optFullRest.opacity = 0.5;
- optRehearsalMark.checked = false;
- optRehearsalMark.enabled = false;
- optRehearsalMark.opacity = 0.5;
- }
+ SubMenuSection {
+ id: setting6Image
+ title: qsTr("Restating grace note accidentals")
- // Error dialog
+ Column {
+ spacing: style.minSpace
+ width: parent.width
- MessageDialog {
- id: errorDialog
- visible: false
- //icon: StandardIcon.Warning
- }
+ StyledLabel {text: qsTr("In same staff:")}
- // Dialog window
+ AddAccItem {
+ id: setting6aAcc
+ anchors.leftMargin: style.regSpace
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.leftMargin - anchors.rightMargin
+ onClicked: updatesetting6Img()
+ }
+ }
- function setUseBracketState() {
- if (optDodecaphonic.checked == true) {
- // disable brackets
- optUseBracket.enabled = false;
- optUseBracket.opacity = 0.5;
- } else {
- optUseBracket.enabled = true;
- optUseBracket.opacity = 1.0;
- }
- }
+ Column {
+ spacing: style.minSpace
+ width: parent.width
- Item {
- id: rect1
- anchors.fill: parent
- anchors.margins: 8
+ StyledLabel {text: qsTr("In different staves of same instrument:")}
- ColumnLayout {
- id: col1
- anchors.left: parent.left
- anchors.right: parent.right
+ AddAccItem {
+ id: setting6bAcc
+ anchors.leftMargin: style.regSpace
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.leftMargin - anchors.rightMargin
+ }
+ }
+ }
- ButtonGroup {id: typeGroup}
+ SubMenuSection {
+ id: setting9aImage
+ title: qsTr("Cancelling in the same measure")
- Label {
- text: "Add courtesy accidentals for"
- }
+ CancelModeItem {
+ id: setting9aCancel
+ width: parent.width - anchors.leftMargin
+ onClicked: updatesetting9aImg()
+ }
+ }
+
+ SubMenuSection {
+ id: setting9bImage
+ title: qsTr("Cancelling in the next measure")
- RowLayout {
- Rectangle { // for indentation
- width: 10
+ CancelModeItem {
+ id: setting9bCancel
+ width: parent.width - anchors.leftMargin
+ onClicked: updatesetting9bImg()
+ }
+ bottomPadding: isExpanded ? style.regSpace : 0
+ }
}
- ColumnLayout {
+ MU.SeparatorLine {width: mainWindow.width}
- Rectangle {height: 2}
- RadioButton {
- id: optNextMeasure
- text: "notes up to the next measure"
- checked: true
- ButtonGroup.group: typeGroup
- onClicked: { setUseBracketState(); }
- }
+ MainMenuSection {
+ title: qsTr("Notes in the same staff")
- Rectangle {height: 2}
- RowLayout {
- RadioButton {
- id: optNumMeasures
- text: "notes up to the next"
- ButtonGroup.group: typeGroup
- onClicked: { setUseBracketState(); }
- }
+ SubMenuSection {
+ title: qsTr("Notes in the same octave in the next measure")
+ id: setting4aColumn
+ property bool accOn: setting4aAcc.checked
- SpinBox {
- id: valNumMeasures
- implicitWidth: 45
- from: 2
- to: 99
+ AddAccItem {
+ id: setting4aAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting4aImg()
}
- Label {
- text: "measures"
+ GraceNotesCheckBox {
+ id: setting4a3Box
+ enabled: setting4aColumn.accOn
+ onChanged: updatesetting4aImg()
}
}
- RowLayout {
- RadioButton {
- Layout.alignment: Qt.AlignTop | Qt.AlignLeft
- id: optEvent
- text: "notes up to the"
- ButtonGroup.group: typeGroup
- onClicked: { setUseBracketState(); }
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the same measure")
+ id: setting1Column
+ property bool accOn: setting1Acc.checked
+
+ AddAccItem {
+ id: setting1Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting1Img()
}
- ColumnLayout {
- CheckBox {
- id: optFullRest
- text: "next full measure rest"
- checked: true
- }
- CheckBox {
- id: optDoubleBar
- text: "next double bar line"
- checked: true
- }
- CheckBox {
- id: optRehearsalMark
- text: "next rehearsal mark"
- checked: false
- }
- CheckBox {
- id: optEndScore
- text: "end of the score"
- checked: true
- }
+ GraceNotesCheckBox {
+ id: setting13Box
+ enabled: setting1Column.accOn
+ onChanged: updatesetting1Img()
}
- }
- Rectangle {height: 2}
- RadioButton {
- id: optDodecaphonic
- text:"all notes (dodecaphonic style)"
- ButtonGroup.group: typeGroup
- onClicked: { setUseBracketState(); }
+ DurationModeItem {
+ id: setting1Duration
+ width: parent.width
+ enabled: setting1Column.accOn
+ onClicked: updatesetting1Img()
+ }
}
- }
- }
- Rectangle {height: 4}
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the next measure")
+ id: setting4bColumn
+ property bool accOn: setting4bAcc.checked
- // Parenthesis option
- CheckBox {
- id: optUseBracket
- text: "Put accidentals in parenthesis"
- checked: false
- }
+ AddAccItem {
+ id: setting4bAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting4bImg()
+ }
- // preserve user settings
- Settings {
- category: "CourtesyAccidentalPlugin"
- property alias typeNextMeasure: optNextMeasure.checked
- property alias typeNumMeasures: optNumMeasures.checked
- property alias valueNumMeasure: valNumMeasures.value
- property alias typeEvent: optEvent.checked
- property alias typeFullRest: optFullRest.checked
- property alias typeDoubleBar: optDoubleBar.checked
- property alias typeRehearsalM: optRehearsalMark.checked
- property alias typeEndScore: optEndScore.checked
- property alias valueDodecaphonic: optDodecaphonic.checked
- property alias valueUseBracket: optUseBracket.checked
- }
- }
- // The buttons
-
- FlatButton {
- text:"Add accidentals"
- anchors {
- top: col1.bottom
- topMargin: 15
- left: rect1.left
- leftMargin: 10
- }
- onClicked: {
- var hasError = false;
-
- // set configuration
- useBracket = optUseBracket.checked;
-
- // set type
- if (optNextMeasure.checked) {
- operationMode = typeNextMeasure;
- } else if (optNumMeasures.checked) {
- operationMode = typeNumMeasures;
- numMeasures = valNumMeasures.value;
- } else if (optEvent.checked) {
- operationMode = typeEvent;
- eventTypes = 0;
- if (optFullRest.checked) {
- eventTypes |= eventFullRest;
- }
- if (optDoubleBar.checked) {
- eventTypes |= eventDoubleBar;
- }
- if (optRehearsalMark.checked) {
- eventTypes |= eventRehearsalMark;
- }
- if (optEndScore.checked) {
- eventTypes |= eventEndScore;
- }
- if (!eventTypes) {
- // show error: at least one item needs to be selected
- //console.log("ERROR: configuration");
- hasError = true;
- errorDialog.text = "No terminating event selected";
- errorDialog.visible = true;
+ GraceNotesCheckBox {
+ id: setting4b3Box
+ enabled: setting4bColumn.accOn
+ onChanged: updatesetting4bImg()
+ }
+
+ bottomPadding: isExpanded ? style.regSpace : 0
}
- } else if (optDodecaphonic.checked) {
- operationMode = typeDodecaphonic;
}
- if (!hasError) {
- curScore.startCmd();
- addAcc();
- curScore.endCmd();
- }
- quit();
- }
- }
+ MU.SeparatorLine {width: mainWindow.width}
- FlatButton {
- text: "Cancel"
- anchors {
- top: col1.bottom
- topMargin: 15
- right: rect1.right
- rightMargin: 10
- }
- onClicked: {
- quit();
- }
- }
- }
+ MainMenuSection {
+ title: qsTr("Notes in different staves of the same instrument")
- // if nothing is selected process whole score
- property bool processAll: false
+ SubMenuSection {
+ title: qsTr("Notes in the same octave in the same measure")
+ id: setting2Column
+ property bool accOn: setting2Acc.checked
- // function tpcName
- //
- // return name of note
-
- function tpcName(tpc) {
- var tpcNames = new Array(
- "Fbb", "Cbb", "Gbb", "Dbb", "Abb", "Ebb", "Bbb",
- "Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb",
- "F", "C", "G", "D", "A", "E", "B",
- "F#", "C#", "G#", "D#", "A#", "E#", "B#",
- "F##", "C##", "G##", "D##", "A##", "E##", "B##"
- );
+ AddAccItem {
+ id: setting2Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting2Img()
+ }
- return(tpcNames[tpc+1]);
- }
+ GraceNotesCheckBox {
+ id: setting23Box
+ enabled: setting2Column.accOn
+ onChanged: updatesetting2Img()
+ }
- // function getEndStaffOfPart
- //
- // return the first staff that does not belong to
- // the part containing given start staff.
+ DurationModeItem {
+ id: setting2Duration
+ width: parent.width
+ enabled: setting2Column.accOn
+ onClicked: updatesetting2Img()
+ }
+ }
- function getEndStaffOfPart(startStaff) {
- var startTrack = startStaff * 4;
- var parts = curScore.parts;
+ SubMenuSection {
+ title: qsTr("Notes in the same octave in the next measure")
+ id: setting5aColumn
+ property bool accOn: setting5aAcc.checked
- for(var i = 0; i < parts.length; i++) {
- var part = parts[i];
+ AddAccItem {
+ id: setting5aAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting5aImg()
+ }
- if( (part.startTrack <= startTrack)
- && (part.endTrack > startTrack) ) {
- return(part.endTrack/4);
- }
- }
+ GraceNotesCheckBox {
+ id: setting5a3Box
+ enabled: setting5aColumn.accOn
+ onChanged: updatesetting5aImg()
+ }
- // not found!
- console.log("error: part for " + startStaff + " not found!");
- quit();
- }
+ }
- // function addAccidental
- //
- // add correct accidental to note
-
- function addAccidental(note) {
- if(note.accidental == null) {
- // calculate type of needed accidental
- var accidental=Accidental.NONE;
- if(note.tpc < 6) {
- accidental = Accidental.FLAT2;
- } else if(note.tpc < 13) {
- accidental = Accidental.FLAT;
- } else if(note.tpc < 20) {
- accidental = Accidental.NATURAL;
- } else if(note.tpc < 27) {
- accidental = Accidental.SHARP;
- } else {
- accidental = Accidental.SHARP2;
- }
- note.accidentalType = accidental;
- // put bracket on accidental if not in dodecaphonic mode
- if (operationMode != typeDodecaphonic
- && note.accidental) {
- if(useBracket) {
- note.accidental.accidentalBracket = 1;
- } else {
- note.accidental.accidentalBracket = 0;
- }
- }
- }
- }
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the same measure")
+ id: setting3Column
+ property bool accOn: setting3Acc.checked
- // function processNote
- //
- // for each measure we create a table that contains
- // the actual 'noteName' of each 'noteClass'
- //
- // a 'noteClass' is the natural name of a space
- // or line of the staff and the octave:
- // C5, F6, B3 are 'noteClass'
- //
- // a 'noteName' would be C, F#, Bb for example
- // (we don't need the octave here)
- //
- // we also remember the measure number that note was found
- // if we operate in typeNumMeasures mode. Thus:
- //
- // curMeasureArray[] = [,]
-
- function processNote(note,prevMeasureArray,curMeasureArray,curMeasureNum) {
- var octave=Math.floor(note.pitch/12);
-
- // use tpc1 instead of tpc for octave correction
- // since this will also work for transposing instruments
- // correct octave for Cb and Cbb
- if(note.tpc1 == 7 || note.tpc1 == 0) {
- octave++; // belongs to higher octave
- }
- // correct octave for B# and B##
- if(note.tpc1 == 26 || note.tpc1 == 33) {
- octave--; // belongs to lower octave
- }
+ AddAccItem {
+ id: setting3Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting3Img()
+ }
- var noteName = tpcName(note.tpc);
- var noteClass = noteName.charAt(0)+octave;
+ GraceNotesCheckBox {
+ id: setting33Box
+ enabled: setting3Column.accOn
+ onChanged: updatesetting3Img()
+ }
- // remember note for next measure
- curMeasureArray[noteClass]=[noteName,curMeasureNum];
+ DurationModeItem {
+ id: setting3Duration
+ width: parent.width
+ enabled: setting3Column.accOn
+ onClicked: updatesetting3Img()
+ }
+ }
- if (operationMode == typeDodecaphonic) {
- addAccidental(note);
- } else if (typeof prevMeasureArray[noteClass] !== 'undefined') {
- // check if current note needs courtesy acc
- if(prevMeasureArray[noteClass][0] != noteName) {
- // this note needs an accidental
- // if there's none present anyway
- addAccidental(note);
- }
- // delete entry to make sure we don't create the
- // same accidental again in the same measure
- delete prevMeasureArray[noteClass];
- }
- }
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the next measure")
+ id: setting5bColumn
+ property bool accOn: setting5bAcc.checked
- // function processPart
- //
- // do the actual work: process all given tracks in parallel
- // add courtesy accidentals where needed.
- //
- // We go through all tracks simultaneously, because we also want courtesy
- // accidentals for notes across different staves when they are in the
- // same octave and for notes of different voices in the same octave
-
- function processPart(cursor,endTick,startTrack,endTrack) {
- if(processAll) {
- // we need to reset track first, otherwise
- // rewind(0) doesn't work correctly
- cursor.track=0;
- cursor.rewind(0);
- } else {
- cursor.rewind(1);
- }
+ AddAccItem {
+ id: setting5bAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting5bImg()
+ }
- var curMeasureNum = 0;
- var segment = cursor.segment;
-
- var curMeasureArray = new Array();
- var prevMeasureArray = new Array();
-
- // we use a segment, because the cursor always proceeds to
- // the next element in the given track and we don't know
- // in which track the next element is.
-
- while(segment && (processAll || segment.tick < endTick)) {
- // we search for key signatures and bar lines
- // in first voice of first staff:
- var keySigTrack = startTrack - (startTrack % 4);
-
- // check for new measure
- if(segment.elementAt(keySigTrack)
- && segment.elementAt(keySigTrack).type == Element.BAR_LINE) {
- // if double bar line and in nextEvent mode check
- // if this leads to reset of prevMeasureArray
-
- curMeasureNum++;
-
- // depending on operationMode: update prevMeasureArray
- switch (operationMode) {
- case typeNextMeasure:
- prevMeasureArray = curMeasureArray;
- break;
-
- case typeNumMeasures:
- // delete all entries that are too old
- var toDelete = [];
- for (var n in prevMeasureArray) {
- if (curMeasureNum - prevMeasureArray[n][1] > numMeasures) {
- toDelete.push(n);
+ GraceNotesCheckBox {
+ id: setting5b3Box
+ enabled: setting5bColumn.accOn
+ onChanged: updatesetting5bImg()
}
- }
- // now delete, otherwise iterating (n in prevMeasureArray) will not work
- for (var x = 0; x < toDelete.length; x++)
- delete prevMeasureArray[toDelete[x]];
- // fall through!
- case typeEvent:
- // copy entries from curMeasureArray
- for (var n in curMeasureArray) {
- prevMeasureArray[n] = curMeasureArray[n];
- }
- break;
- }
- // if barline is double, might need to forget
- // previous mesaure...
- var barLine = segment.elementAt(keySigTrack);
- if ((operationMode==typeEvent)
- && (eventTypes & eventDoubleBar)
- && (barLine.barLineType == BarLine.DOUBLE)) {
- prevMeasureArray = new Array();
+ bottomPadding: isExpanded ? style.regSpace : 0
+ }
}
- // reset curMeasureArray
- curMeasureArray = new Array();
- }
+ MU.SeparatorLine {width: mainWindow.width}
- // check for new key signature
- // we only do this for the first track of the first staff
- // this means we miss the event of having two different
- // key signatures in different staves of the same part
- // This remains for future version if needed
- // we look inside this loop to make sure we don't miss
- // any segments. This could be improved for speed.
- // A KeySig that has generated == true was created by
- // layout, and is probably at the beginning of a new line
- // so we don't need it.
-
- if (segment.elementAt(keySigTrack)
- && segment.elementAt(keySigTrack).type == Element.KEYSIG
- && (!segment.elementAt(keySigTrack).generated)) {
- //console.log("found KEYSIG");
- // just forget the previous measure info
- // to not generate any courtesy accidentals
- prevMeasureArray = new Array();
- }
+ MainMenuSection {
+ title: qsTr("Notes after key signature changes")
- // BUG: access to annotations is broken in 2.0.3
- //
- // check for rehearsal mark
- //var annotations = segment.annotations;
-
- //if (annotations && annotations.length > 0) {
- // for (var i = 0; i < annotations.length; i++) {
- // var mark = annotations[i];
- // if (mark.type == Element.REHEARSAL_MARK) {
- // if (operationMode == typeEvent
- // && (eventTypes & eventRehearsalMark)) {
- // // reset array
- // prevMeasureArray = new Array();
- // }
- // console.log("found rehearsal mark");
- // }
- // }
- //}
-
- // if we find a full measure rest, it needs to be in the whole part
- var allTracksFullMeasureRest = true;
- var restFound = false;
-
- // scann music
- for(var track=startTrack; track 0) {
- var graceChords = segment.elementAt(track).graceNotes;
-
- for(var j=0;j endStaff) {
- curEndStaff = endStaff;
+ settingObj.setting5 = {
+ a: {
+ addAccidentals: setting5aAcc.checked,
+ bracketType: setting5aAcc.currentValue,
+ parseGraceNotes: setting5a3Box.checked
+ },
+ b: {
+ addAccidentals: setting5bAcc.checked,
+ bracketType: setting5bAcc.currentValue,
+ parseGraceNotes: setting5b3Box.checked
}
-
- // do the work
- processPart(cursor,endTick,curStartStaff*4,curEndStaff*4);
-
- // next part
- curStartStaff = curEndStaff;
}
+ settingObj.setting6 = {
+ a: {
+ addAccidentals: setting6aAcc.checked,
+ bracketType: setting6aAcc.currentValue
+ },
+ b: {
+ addAccidentals: setting6bAcc.checked,
+ bracketType: setting6bAcc.currentValue
+ }
+ }
+ settingObj.setting7 = {
+ addAccidentals: setting7Acc.checked,
+ bracketType: setting7Acc.currentValue,
+ cancelOctaves: setting7Cancel.checked,
+ parseGraceNotes: setting74Box.checked,
+ cancelMode: setting7Cancel.value == 1
+ }
+ settingObj.setting8 = {
+ addAccidentals: setting8Acc.checked,
+ bracketType: setting8Acc.currentValue,
+ cancelOctaves: setting8Cancel.checked,
+ parseGraceNotes: setting84Box.checked,
+ cancelMode: setting8Cancel.value == 1
+ }
+ settingObj.setting9 = {
+ a: setting9aCancel.value == 1,
+ b: setting9bCancel.value == 1
+ }
+ return settingObj
+ }
+ function updatesetting0Img() {
+ setting0Image.source = "examples/setting0/example-" + setting0Box.checked.toString() + ".svg"
+ }
+ function updatesetting1Img() {
+ var imgsource = "examples/setting1/example-"
+ if (setting1Acc.checked) {
+ imgsource += setting1Acc.currentValue.toString()
+ imgsource += setting1Duration.value.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting1Column.source = imgsource
+ }
+ function updatesetting2Img() {
+ var imgsource = "examples/setting2/example-"
+ if (setting2Acc.checked) {
+ imgsource += setting2Acc.currentValue.toString()
+ imgsource += setting2Duration.value.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting2Column.source = imgsource
+ }
+ function updatesetting3Img() {
+ var imgsource = "examples/setting3/example-"
+ if (setting3Acc.checked) {
+ imgsource += setting3Acc.currentValue.toString()
+ imgsource += setting3Duration.value.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting3Column.source = imgsource
+ }
+ function updatesetting4aImg() {
+ var imgsource = "examples/setting4a/example-"
+ if (setting4aAcc.checked) {
+ imgsource += setting4a3Box.checked ? "1" : "0"
+ imgsource += setting4aAcc.currentValue.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting4aColumn.source = imgsource
+ }
+ function updatesetting4bImg() {
+ var imgsource = "examples/setting4b/example-"
+ if (setting4bAcc.checked) {
+ imgsource += setting4b3Box.checked ? "1" : "0"
+ imgsource += setting4bAcc.currentValue.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting4bColumn.source = imgsource
+ }
+ function updatesetting5aImg() {
+ var imgsource = "examples/setting5a/example-"
+ imgsource += setting5aAcc.checked ? (setting5aAcc.currentValue + 2).toString() : "1"
+ imgsource += ".svg"
+ setting5aColumn.source = imgsource
+ }
+ function updatesetting5bImg() {
+ var imgsource = "examples/setting5b/example-"
+ imgsource += setting5bAcc.checked ? (setting5bAcc.currentValue + 2).toString() : "1"
+ imgsource += ".svg"
+ setting5bColumn.source = imgsource
+ }
+ function updatesetting6Img() {
+ var imgsource = "examples/setting6/example-"
+ imgsource += setting6aAcc.checked ? (setting6aAcc.currentValue + 2).toString() : "1"
+ imgsource += ".svg"
+ setting6Image.source = imgsource
+ }
+ function updatesetting7Img() {
+ var imgsource = "examples/setting7/example-"
+ if (setting7Acc.checked) {
+ imgsource += setting74Box.checked ? "1" : "0"
+ imgsource += setting7Acc.currentValue.toString()
+ imgsource += setting7Cancel.checked ? setting7Cancel.value.toString() : "0"
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting7Column.source = imgsource
+ }
+ function updatesetting8Img() {
+ var imgsource = "examples/setting8/example-"
+ if (setting8Acc.checked) {
+ imgsource += setting8Acc.currentValue.toString()
+ imgsource += setting8Cancel.checked ? setting8Cancel.value.toString() : "0"
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting8Column.source = imgsource
+ }
+ function updatesetting9aImg() {
+ setting9aImage.source = "examples/setting9a/example-" + setting9aCancel.value.toString() + ".svg"
+ }
+ function updatesetting9bImg() {
+ setting9bImage.source = "examples/setting9b/example-" + setting9bCancel.value.toString() + ".svg"
+ }
+ Settings {
+ id: options
+ category: "Courtesy Accidentals Plugin"
+ property var uSettings: '{
+ "version": "4.0-beta",
+ "edited": false
+ }'
+ //Qt.labs.settings doesn't like working with object types
+ }
+ function smartQuit() {
+ mainWindow.close()
+ quit()
}
}
diff --git a/share/extensions/courtesy_accidentals/remove.js b/share/extensions/courtesy_accidentals/remove.js
deleted file mode 100644
index b71d99827f3ca..0000000000000
--- a/share/extensions/courtesy_accidentals/remove.js
+++ /dev/null
@@ -1,289 +0,0 @@
-//==============================================
-// remove courtesy accidentals v1.0
-//
-// Copyright (C)2012-2019 Jörn Eichler (heuchi)
-//
-// 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 .
-//==============================================
-
-function main() {
- curScore.startCmd()
- removeAcc()
- curScore.endCmd()
-
- quit()
-}
-
-// if nothing is selected process whole score
-var processAll = false
-
-// function tpcName
-//
-// return name of note
-var TPC_NAMES = [
- "Fbb", "Cbb", "Gbb", "Dbb", "Abb", "Ebb", "Bbb",
- "Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb",
- "F", "C", "G", "D", "A", "E", "B",
- "F#", "C#", "G#", "D#", "A#", "E#", "B#",
- "F##", "C##", "G##", "D##", "A##", "E##", "B##"
- ];
-
-function tpcName(tpc) {
-
- return(TPC_NAMES[tpc+1]);
-}
-
-// function processNote
-//
-// for each measure we create a table that contains
-// the actual 'noteName' of each 'noteClass'
-//
-// a 'noteClass' is the natural name of a space
-// or line of the staff and the octave:
-// C5, F6, B3 are 'noteClass'
-//
-// a 'noteName' would be C, F#, Bb for example
-// (we don't need the octave here)
-//
-// curMeasureArray[] =
-
-function processNote(note,curMeasureArray,keySig) {
- var octave=Math.floor(note.pitch/12);
-
- // correct octave for Cb and Cbb
- if(note.tpc1 == 7 || note.tpc1 == 0) {
- octave++; // belongs to higher octave
- }
- // correct octave for B# and B##
- if(note.tpc1 == 26 || note.tpc1 == 33) {
- octave--; // belongs to lower octave
- }
-
- var noteName = tpcName(note.tpc);
- var noteClass = noteName.charAt(0)+octave;
-
- // a tied back note never needs an accidental
- if (note.tieBack != null) {
- if(note.accidental != null) {
- // security checks
- var thisPitch = note.pitch;
- var thisAcc = note.accidentalType;
-
- // remove
- note.accidentalType = Accidental.NONE;
-
- // if pitch changed, we were wrong...
- if(note.pitch != thisPitch) {
- console.log("ERROR1: pitch of note changed!");
- //note.color = "#ff0000";
- note.accidentalType = thisAcc;
- }
- }
- // if the tied back note is not part of
- // the current key sig, we need to remember it.
- //if( ! (note.tpc > keySig+12 && note.tpc < keySig+20)) {
- // curMeasureArray[noteClass]=noteName;
- //}
-
- // we're done for a tied back note.
- return;
- }
-
- // check if current note needs acc
- if(typeof curMeasureArray[noteClass] !== 'undefined') {
- // we have information on the previous note
- // in the same measure:
- // if this note is the same noteClass and noteName
- // it doesn't need an accidental
- if(curMeasureArray[noteClass] == noteName) {
- // remove accidental if present
- if(note.accidental != null) {
- // security checks
- var thisPitch = note.pitch;
- var thisAcc = note.accidentalType;
-
- // remove
- note.accidentalType = Accidental.NONE;
-
- // if pitch changed, we were wrong...
- if(note.pitch != thisPitch) {
- console.log("ERROR2: pitch of note changed!");
- //note.color = "#ff0000";
- note.accidentalType = thisAcc;
- }
- }
- }
- } else {
- // we don't have this note in the current measure
- // so it depends on the current key signature
- if(note.tpc > keySig+12 && note.tpc < keySig+20) {
- // we don't need an accidental in the current key sig
- // remove accidental if present
- if(note.accidental != null) {
- // security checks
- var thisPitch = note.pitch;
- var thisAcc = note.accidentalType;
-
- // remove
- note.accidentalType = Accidental.NONE;
-
- // if pitch changed, we were wrong...
- if(note.pitch != thisPitch) {
- console.log("ERROR3: pitch of note changed!");
- //note.color = "#ff0000";
- note.accidentalType = thisAcc;
- console.log("KeySig="+keySig+", tpc="+note.tpc);
- }
- }
- }
- }
-
- curMeasureArray[noteClass]=noteName;
-}
-
-// function processPart
-//
-// do the actual work: process all given tracks in parallel
-// add courtesy accidentals where needed.
-//
-// We go through all tracks simultaneously, because we also want courtesy
-// accidentals for notes across different staves when they are in the
-// same octave and for notes of different voices in the same octave
-
-function processPart(cursor,endTick,startTrack,endTrack) {
- if(processAll) {
- // we need to reset track first, otherwise
- // rewind(0) doesn't work correctly
- // we need to set staffIdx and voice to
- // get correct key signature.
- cursor.staffIdx = startTrack / 4;
- cursor.voice = 0;
- cursor.rewind(0);
- } else {
- cursor.rewind(1);
- // we need to set staffIdx and voice to
- // get correct key signature.
- cursor.staffIdx = startTrack / 4;
- cursor.voice = 0;
- }
-
- var segment = cursor.segment;
-
- // we use the cursor to know measure boundaries
- // and to get the current key signature
- var keySig = cursor.keySignature;
- cursor.nextMeasure();
-
- var curMeasureArray = [];
-
- // we use a segment, because the cursor always proceeds to
- // the next element in the given track and we don't know
- // in which track the element is.
- var inLastMeasure=false;
- while(segment && (processAll || segment.tick < endTick)) {
- // check if still inside same measure
- if(!inLastMeasure && !(segment.tick < cursor.tick)) {
- // new measure
- curMeasureArray = [];
- keySig = cursor.keySignature;
- if(!cursor.nextMeasure()) {
- inLastMeasure=true;
- }
- }
-
- for(var track=startTrack; track 0) {
- var graceChords = segment.elementAt(track).graceNotes;
-
- for(var j=0;j startTrack) ) {
- return(part.endTrack/4);
- }
- }
-
- // not found!
- console.log("error: part for " + startStaff + " not found!");
- quit();
+ // Notes in same measure at different octave
+ property var setting1: {
+ "addAccidentals": true, // If to cancel, bracket type
+ "bracketType": 0, // 0 = no brackets, 1 = round, 2 = square
+ "parseGraceNotes": true, // Include grace notes in calculations and adding
+ "durationMode": 0 // How to parse durations (0": not before, 1": instantaneous, 2": during)
}
- // function processNote
- //
- // for each measure we create a table that contains
- // the actual 'noteName' of each 'noteClass'
- //
- // a 'noteClass' is the natural name of a space
- // or line of the staff and the octave:
- // C5, F6, B3 are 'noteClass'
- //
- // a 'noteName' would be C, F#, Bb for example
- // (we don't need the octave here)
- //
- // curMeasureArray[] =
-
- function processNote(note,prevMeasureArray,curMeasureArray) {
- var octave=Math.floor(note.pitch/12);
-
- // use tpc1 instead of tpc for octave correction
- // since this will also work for transposing instruments
- // correct octave for Cb and Cbb
- if(note.tpc1 == 7 || note.tpc1 == 0) {
- octave++; // belongs to higher octave
- }
- // correct octave for B# and B##
- if(note.tpc1 == 26 || note.tpc1 == 33) {
- octave--; // belongs to lower octave
- }
-
- var noteName = tpcName(note.tpc);
- var noteClass = noteName.charAt(0)+octave;
-
- // remember note for next measure
- curMeasureArray[noteClass]=noteName;
-
- // check if current note needs courtesy acc
- if(typeof prevMeasureArray[noteClass] !== 'undefined') {
- if(prevMeasureArray[noteClass] != noteName) {
- // this note needs an accidental
- // if there's none present anyway
- if(note.accidental == null) {
- // calculate type of needed accidental
- var accidental=Accidental.NONE;
- if(note.tpc < 6) {
- accidental = Accidental.FLAT2;
- } else if(note.tpc < 13) {
- accidental = Accidental.FLAT;
- } else if(note.tpc < 20) {
- accidental = Accidental.NATURAL;
- } else if(note.tpc < 27) {
- accidental = Accidental.SHARP;
- } else {
- accidental = Accidental.SHARP2;
- }
- note.accidentalType = accidental;
- // put bracket on accidental
- note.accidental.accidentalBracket = useBracket;
- }
- }
- // delete entry to make sure we don't create the
- // same accidental again in the same measure
- delete prevMeasureArray[noteClass];
- }
+ // Notes in same measure in different staves (of same instrument)
+ property var setting2: {
+ "addAccidentals": true,
+ "bracketType": 0,
+ "parseGraceNotes": true,
+ "durationMode": 0
}
- // function processPart
- //
- // do the actual work: process all given tracks in parallel
- // add courtesy accidentals where needed.
- //
- // We go through all tracks simultaneously, because we also want courtesy
- // accidentals for notes across different staves when they are in the
- // same octave and for notes of different voices in the same octave
-
- function processPart(cursor,endTick,startTrack,endTrack) {
- if(processAll) {
- // we need to reset track first, otherwise
- // rewind(0) doesn't work correctly
- cursor.track=0;
- cursor.rewind(0);
- } else {
- cursor.rewind(1);
- }
-
- var segment = cursor.segment;
-
- // we use the cursor to know measure boundaries
- cursor.nextMeasure();
-
- var curMeasureArray = new Array();
- var prevMeasureArray = new Array();
-
- // we use a segment, because the cursor always proceeds to
- // the next element in the given track and we don't know
- // in which track the element is.
- var inLastMeasure=false;
- while(segment && (processAll || segment.tick < endTick)) {
- // check if still inside same measure
- if(!inLastMeasure && !(segment.tick < cursor.tick)) {
- // new measure
- prevMeasureArray = curMeasureArray;
- curMeasureArray = new Array();
- if(!cursor.nextMeasure()) {
- inLastMeasure=true;
- }
- }
-
- // we search for key signatures in first voice of
- // first staff:
- var keySigTrack = startTrack - (startTrack % 4);
-
- for(var track=startTrack; track 0) {
- var graceChords = segment.elementAt(track).graceNotes;
-
- for(var j=0;j endStaff) {
- curEndStaff = endStaff;
- }
-
- // do the work
- processPart(cursor,endTick,curStartStaff*4,curEndStaff*4);
+ // Notes over a key change (new bar)
+ property var setting7: {
+ "addAccidentals": true, // If to run
+ "bracketType": 0, // Bracket type
+ "cancelOctaves": false, // Cancel in different octaves
+ "parseGraceNotes": true, // Include grace notes in calculation and adding
+ "cancelMode": true // Excessive cancelling mode (see setting9)
+ }
- // next part
- curStartStaff = curEndStaff;
- }
+ // Notes over a key change (mid bar)
+ property var setting8: {
+ "addAccidentals": true,
+ "bracketType": 0,
+ "cancelOctaves": false,
+ "parseGraceNotes": true,
+ "cancelMode": true
+ }
- console.log("end add courtesy accidentals");
+ // How to handle excessive cancelling
+ property var setting9: {
+ "a": true, // In same measure
+ "b": true // In different measures
}
+ //option true: add accidentals as needed until cancelled in original octave
+ // notes of same tick get cancelled
+ // cancelling has to happen in original staff
+ //option false: continue to add accidentals as needed if not previously cancelled in same octave
- onRun: {
- curScore.startCmd()
- addAcc()
- curScore.endCmd()
+ onRun: Accidentals.runPlugin("add")
- quit()
+ Settings {
+ id: options
+ category: "Courtesy Accidentals Plugin"
+ property var uSettings
}
}
diff --git a/share/plugins/courtesy_accidentals/configureCourtesyAccidentals.qml b/share/plugins/courtesy_accidentals/configureCourtesyAccidentals.qml
new file mode 100644
index 0000000000000..07fb7dca9ec6a
--- /dev/null
+++ b/share/plugins/courtesy_accidentals/configureCourtesyAccidentals.qml
@@ -0,0 +1,675 @@
+//==============================================
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
+//
+// 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 .
+//==============================================
+
+import QtQuick 2.9
+import QtQuick.Controls 2.2
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.2
+import MuseScore 3.0
+import Muse.UiComponents 1.0 as MU
+import Muse.Ui 1.0
+import "assets"
+import "assets/defaultsettings.js" as DSettings
+
+MuseScore {
+ title: qsTr("Configure Courtesy Accidentals")
+ description: qsTr("Choose when to add courtesy accidentals to your scores, and how they look.")
+ version: "4.0"
+ categoryCode: "composing-arranging-tools"
+ thumbnailName: "assets/accidentals.png"
+ requiresScore: false
+
+ onRun: mainWindow.show()
+
+ ApplicationWindow {
+ id: mainWindow
+ minimumHeight: 400
+ minimumWidth: 480
+ background: Rectangle {color: ui.theme.backgroundSecondaryColor}
+ title: qsTr("Courtesy Accidentals: Settings")
+ flags: Qt.Dialog
+
+ MU.StyledFlickable {
+ id: flickable
+ anchors.fill: parent
+ focus: true
+ contentWidth: contentItem.childrenRect.width + 2 * mainColumn.x
+ contentHeight: contentItem.childrenRect.height + 2 * mainColumn.y
+ Keys.onUpPressed: scrollBar.decrease()
+ Keys.onDownPressed: scrollBar.increase()
+ ScrollBar.vertical: MU.StyledScrollBar {id: scrollBar}
+ Column {
+ id: mainColumn
+ spacing: 0
+ width: mainWindow.width
+
+ MainMenuSection {
+ title: qsTr("General Settings")
+ isExpanded: true
+
+ SubMenuSection {
+ id: setting0Image
+ title: qsTr("Double accidentals")
+
+ MU.CheckBox {
+ id: setting0Box
+ anchors.leftMargin: style.regSpace
+ text: qsTr("Use natural flats/sharps when cancelling double accidentals")
+ onClicked: {checked = !checked; updatesetting0Img()}
+ signal setv(bool checked)
+ onSetv: function(value) {checked = value; updatesetting0Img()}
+ }
+ }
+
+ SubMenuSection {
+ id: setting6Image
+ title: qsTr("Restating grace note accidentals")
+
+ Column {
+ spacing: style.minSpace
+ width: parent.width
+
+ StyledLabel {text: qsTr("In same staff:")}
+
+ AddAccItem {
+ id: setting6aAcc
+ anchors.leftMargin: style.regSpace
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.leftMargin - anchors.rightMargin
+ onClicked: updatesetting6Img()
+ }
+ }
+
+ Column {
+ spacing: style.minSpace
+ width: parent.width
+
+ StyledLabel {text: qsTr("In different staves of same instrument:")}
+
+ AddAccItem {
+ id: setting6bAcc
+ anchors.leftMargin: style.regSpace
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.leftMargin - anchors.rightMargin
+ }
+ }
+ }
+
+ SubMenuSection {
+ id: setting9aImage
+ title: qsTr("Cancelling in the same measure")
+
+ CancelModeItem {
+ id: setting9aCancel
+ width: parent.width - anchors.leftMargin
+ onClicked: updatesetting9aImg()
+ }
+ }
+
+ SubMenuSection {
+ id: setting9bImage
+ title: qsTr("Cancelling in the next measure")
+
+ CancelModeItem {
+ id: setting9bCancel
+ width: parent.width - anchors.leftMargin
+ onClicked: updatesetting9bImg()
+ }
+ bottomPadding: isExpanded ? style.regSpace : 0
+ }
+ }
+
+ MU.SeparatorLine {width: mainWindow.width}
+
+ MainMenuSection {
+ title: qsTr("Notes in the same staff")
+
+ SubMenuSection {
+ title: qsTr("Notes in the same octave in the next measure")
+ id: setting4aColumn
+ property bool accOn: setting4aAcc.checked
+
+ AddAccItem {
+ id: setting4aAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting4aImg()
+ }
+
+ GraceNotesCheckBox {
+ id: setting4a3Box
+ enabled: setting4aColumn.accOn
+ onChanged: updatesetting4aImg()
+ }
+ }
+
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the same measure")
+ id: setting1Column
+ property bool accOn: setting1Acc.checked
+
+ AddAccItem {
+ id: setting1Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting1Img()
+ }
+
+ GraceNotesCheckBox {
+ id: setting13Box
+ enabled: setting1Column.accOn
+ onChanged: updatesetting1Img()
+ }
+
+ DurationModeItem {
+ id: setting1Duration
+ width: parent.width
+ enabled: setting1Column.accOn
+ onClicked: updatesetting1Img()
+ }
+ }
+
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the next measure")
+ id: setting4bColumn
+ property bool accOn: setting4bAcc.checked
+
+ AddAccItem {
+ id: setting4bAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting4bImg()
+ }
+
+ GraceNotesCheckBox {
+ id: setting4b3Box
+ enabled: setting4bColumn.accOn
+ onChanged: updatesetting4bImg()
+ }
+
+ bottomPadding: isExpanded ? style.regSpace : 0
+ }
+ }
+
+ MU.SeparatorLine {width: mainWindow.width}
+
+ MainMenuSection {
+ title: qsTr("Notes in different staves of the same instrument")
+
+ SubMenuSection {
+ title: qsTr("Notes in the same octave in the same measure")
+ id: setting2Column
+ property bool accOn: setting2Acc.checked
+
+ AddAccItem {
+ id: setting2Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting2Img()
+ }
+
+ GraceNotesCheckBox {
+ id: setting23Box
+ enabled: setting2Column.accOn
+ onChanged: updatesetting2Img()
+ }
+
+ DurationModeItem {
+ id: setting2Duration
+ width: parent.width
+ enabled: setting2Column.accOn
+ onClicked: updatesetting2Img()
+ }
+ }
+
+ SubMenuSection {
+ title: qsTr("Notes in the same octave in the next measure")
+ id: setting5aColumn
+ property bool accOn: setting5aAcc.checked
+
+ AddAccItem {
+ id: setting5aAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting5aImg()
+ }
+
+ GraceNotesCheckBox {
+ id: setting5a3Box
+ enabled: setting5aColumn.accOn
+ onChanged: updatesetting5aImg()
+ }
+
+ }
+
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the same measure")
+ id: setting3Column
+ property bool accOn: setting3Acc.checked
+
+ AddAccItem {
+ id: setting3Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting3Img()
+ }
+
+ GraceNotesCheckBox {
+ id: setting33Box
+ enabled: setting3Column.accOn
+ onChanged: updatesetting3Img()
+ }
+
+ DurationModeItem {
+ id: setting3Duration
+ width: parent.width
+ enabled: setting3Column.accOn
+ onClicked: updatesetting3Img()
+ }
+ }
+
+ SubMenuSection {
+ title: qsTr("Notes in different octaves in the next measure")
+ id: setting5bColumn
+ property bool accOn: setting5bAcc.checked
+
+ AddAccItem {
+ id: setting5bAcc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - anchors.rightMargin
+ onClicked: updatesetting5bImg()
+ }
+
+ GraceNotesCheckBox {
+ id: setting5b3Box
+ enabled: setting5bColumn.accOn
+ onChanged: updatesetting5bImg()
+ }
+
+ bottomPadding: isExpanded ? style.regSpace : 0
+ }
+ }
+
+ MU.SeparatorLine {width: mainWindow.width}
+
+ MainMenuSection {
+ title: qsTr("Notes after key signature changes")
+
+ SubMenuSection {
+ title: qsTr("Notes after measure key signature changes")
+ id: setting7Column
+ property bool accOn: setting7Acc.checked
+
+ AddAccItem {
+ id: setting7Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - 2 * parent.padding
+ onClicked: updatesetting7Img()
+ }
+ GraceNotesCheckBox {
+ id: setting74Box
+ key: true
+ enabled: setting7Column.accOn
+ onChanged: updatesetting7Img()
+ }
+ OptionalCancelModeItem {
+ id: setting7Cancel
+ enabled: setting7Column.accOn
+ width: parent.width - 2 * parent.padding
+ onClicked: updatesetting7Img()
+ }
+ }
+
+ SubMenuSection {
+ title: qsTr("Notes after mid-measure key signature changes")
+ id: setting8Column
+ property bool accOn: setting8Acc.checked
+
+ AddAccItem {
+ id: setting8Acc
+ anchors.rightMargin: style.regSpace
+ width: parent.width - 2 * parent.padding
+ onClicked: updatesetting8Img()
+ }
+ GraceNotesCheckBox {
+ id: setting84Box
+ key: true
+ enabled: setting8Column.accOn
+ onChanged: updatesetting8Img()
+ }
+ OptionalCancelModeItem {
+ id: setting8Cancel
+ enabled: setting8Column.accOn
+ width: parent.width - 2 * parent.padding
+ onClicked: updatesetting8Img()
+ }
+ bottomPadding: isExpanded ? style.regSpace : 0
+ }
+ }
+ }
+ }
+ Rectangle {
+ height: style.maxSpace
+ anchors.top: flickable.top
+ anchors.left: flickable.left
+ anchors.right: flickable.right
+ anchors.rightMargin: scrollBar.width
+ visible: !flickable.atYBeginning
+ gradient: Gradient {
+ GradientStop {position: 0.0; color: ui.theme.backgroundSecondaryColor}
+ GradientStop {position: 1.0; color: "transparent"}
+ }
+ }
+ Rectangle {
+ height: style.maxSpace
+ anchors.left: flickable.left
+ anchors.right: flickable.right
+ anchors.rightMargin: scrollBar.width
+ anchors.bottom: flickable.bottom
+ visible: !flickable.atYEnd
+ gradient: Gradient {
+ GradientStop {position: 0.0; color: "transparent"}
+ GradientStop {position: 1.0; color: ui.theme.backgroundSecondaryColor}
+ }
+ }
+ footer: Rectangle {
+ color: ui.theme.backgroundPrimaryColor
+ height: okButton.height + (2 * style.regSpace) + 1
+ MU.SeparatorLine {
+ anchors.top: parent.top
+ }
+ MU.FlatButton {
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.margins: style.regSpace
+ text: qsTr("Reset Settings")
+ onClicked: loadSettings(DSettings.read())
+ }
+ Row {
+ id: okButton
+ spacing: style.regSpace
+ anchors.margins: style.regSpace
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ MU.FlatButton {
+ text: qsTr("Cancel")
+ onClicked: smartQuit()
+ }
+ MU.FlatButton {
+ text: qsTr("OK")
+ accentButton: true
+ onClicked: {
+ options.uSettings = JSON.stringify(writeSettings())
+ smartQuit()
+ }
+ }
+ }
+ }
+ Component.onCompleted: {
+ if (JSON.parse(options.uSettings).edited) {
+ loadSettings(JSON.parse(options.uSettings))
+ } else {
+ loadSettings(DSettings.read())
+ }
+ }
+ }
+
+ PluginStyle {id: style}
+
+ function loadSettings(settingObj) {
+ setting0Box.setv(settingObj.setting0.addNaturals)
+ //
+ setting1Acc.setv(settingObj.setting1.addAccidentals, settingObj.setting1.bracketType)
+ setting13Box.setv(settingObj.setting1.parseGraceNotes)
+ setting1Duration.setv(settingObj.setting1.durationMode)
+ //
+ setting2Acc.setv(settingObj.setting2.addAccidentals, settingObj.setting2.bracketType)
+ setting23Box.setv(settingObj.setting2.parseGraceNotes)
+ setting2Duration.setv(settingObj.setting2.durationMode)
+ //
+ setting3Acc.setv(settingObj.setting3.addAccidentals, settingObj.setting3.bracketType)
+ setting33Box.setv(settingObj.setting3.parseGraceNotes)
+ setting3Duration.setv(settingObj.setting3.durationMode)
+ //
+ setting4aAcc.setv(settingObj.setting4.a.addAccidentals, settingObj.setting4.a.bracketType)
+ setting4a3Box.setv(settingObj.setting4.a.parseGraceNotes)
+ //
+ setting4bAcc.setv(settingObj.setting4.b.addAccidentals, settingObj.setting4.b.bracketType)
+ setting4b3Box.setv(settingObj.setting4.b.parseGraceNotes)
+ //
+ setting5aAcc.setv(settingObj.setting5.a.addAccidentals, settingObj.setting5.a.bracketType)
+ setting5a3Box.setv(settingObj.setting5.a.parseGraceNotes)
+ //
+ setting5bAcc.setv(settingObj.setting5.b.addAccidentals, settingObj.setting5.b.bracketType)
+ setting5b3Box.setv(settingObj.setting5.b.parseGraceNotes)
+ //
+ setting6aAcc.setv(settingObj.setting6.a.addAccidentals, settingObj.setting6.a.bracketType)
+ //
+ setting6bAcc.setv(settingObj.setting6.b.addAccidentals, settingObj.setting6.b.bracketType)
+ //
+ setting7Acc.setv(settingObj.setting7.addAccidentals, settingObj.setting7.bracketType)
+ setting7Cancel.setv(settingObj.setting7.cancelOctaves, settingObj.setting7.cancelMode ? 1 : 2)
+ setting74Box.setv(settingObj.setting7.parseGraceNotes)
+ //
+ setting8Acc.setv(settingObj.setting8.addAccidentals, settingObj.setting8.bracketType)
+ setting8Cancel.setv(settingObj.setting8.cancelOctaves, settingObj.setting8.cancelMode ? 1 : 2)
+ setting84Box.setv(settingObj.setting8.parseGraceNotes)
+ //
+ setting9aCancel.setv(settingObj.setting9.a ? 1 : 2)
+ //
+ setting9bCancel.setv(settingObj.setting9.a ? 1 : 2)
+ }
+ function writeSettings() {
+ var settingObj = {}
+ settingObj.edited = true
+ settingObj.setting0 = {
+ addNaturals: setting0Box.checked
+ }
+ settingObj.setting1 = {
+ addAccidentals: setting1Acc.checked,
+ bracketType: setting1Acc.currentValue,
+ parseGraceNotes: setting13Box.checked,
+ durationMode: setting1Duration.value
+ }
+ settingObj.setting2 = {
+ addAccidentals: setting1Acc.checked,
+ bracketType: setting1Acc.currentValue,
+ parseGraceNotes: setting13Box.checked,
+ durationMode: setting1Duration.value
+ }
+ settingObj.setting3 = {
+ addAccidentals: setting3Acc.checked,
+ bracketType: setting3Acc.currentValue,
+ parseGraceNotes: setting33Box.checked,
+ durationMode: setting3Duration.value
+ }
+ settingObj.setting4 = {
+ a: {
+ addAccidentals: setting4aAcc.checked,
+ bracketType: setting4aAcc.currentValue,
+ parseGraceNotes: setting4a3Box.checked
+ },
+ b: {
+ addAccidentals: setting4bAcc.checked,
+ bracketType: setting4bAcc.currentValue,
+ parseGraceNotes: setting4b3Box.checked
+ }
+ }
+ settingObj.setting5 = {
+ a: {
+ addAccidentals: setting5aAcc.checked,
+ bracketType: setting5aAcc.currentValue,
+ parseGraceNotes: setting5a3Box.checked
+ },
+ b: {
+ addAccidentals: setting5bAcc.checked,
+ bracketType: setting5bAcc.currentValue,
+ parseGraceNotes: setting5b3Box.checked
+ }
+ }
+ settingObj.setting6 = {
+ a: {
+ addAccidentals: setting6aAcc.checked,
+ bracketType: setting6aAcc.currentValue
+ },
+ b: {
+ addAccidentals: setting6bAcc.checked,
+ bracketType: setting6bAcc.currentValue
+ }
+ }
+ settingObj.setting7 = {
+ addAccidentals: setting7Acc.checked,
+ bracketType: setting7Acc.currentValue,
+ cancelOctaves: setting7Cancel.checked,
+ parseGraceNotes: setting74Box.checked,
+ cancelMode: setting7Cancel.value == 1
+ }
+ settingObj.setting8 = {
+ addAccidentals: setting8Acc.checked,
+ bracketType: setting8Acc.currentValue,
+ cancelOctaves: setting8Cancel.checked,
+ parseGraceNotes: setting84Box.checked,
+ cancelMode: setting8Cancel.value == 1
+ }
+ settingObj.setting9 = {
+ a: setting9aCancel.value == 1,
+ b: setting9bCancel.value == 1
+ }
+ return settingObj
+ }
+ function updatesetting0Img() {
+ setting0Image.source = "examples/setting0/example-" + setting0Box.checked.toString() + ".svg"
+ }
+ function updatesetting1Img() {
+ var imgsource = "examples/setting1/example-"
+ if (setting1Acc.checked) {
+ imgsource += setting1Acc.currentValue.toString()
+ imgsource += setting1Duration.value.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting1Column.source = imgsource
+ }
+ function updatesetting2Img() {
+ var imgsource = "examples/setting2/example-"
+ if (setting2Acc.checked) {
+ imgsource += setting2Acc.currentValue.toString()
+ imgsource += setting2Duration.value.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting2Column.source = imgsource
+ }
+ function updatesetting3Img() {
+ var imgsource = "examples/setting3/example-"
+ if (setting3Acc.checked) {
+ imgsource += setting3Acc.currentValue.toString()
+ imgsource += setting3Duration.value.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting3Column.source = imgsource
+ }
+ function updatesetting4aImg() {
+ var imgsource = "examples/setting4a/example-"
+ if (setting4aAcc.checked) {
+ imgsource += setting4a3Box.checked ? "1" : "0"
+ imgsource += setting4aAcc.currentValue.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting4aColumn.source = imgsource
+ }
+ function updatesetting4bImg() {
+ var imgsource = "examples/setting4b/example-"
+ if (setting4bAcc.checked) {
+ imgsource += setting4b3Box.checked ? "1" : "0"
+ imgsource += setting4bAcc.currentValue.toString()
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting4bColumn.source = imgsource
+ }
+ function updatesetting5aImg() {
+ var imgsource = "examples/setting5a/example-"
+ imgsource += setting5aAcc.checked ? (setting5aAcc.currentValue + 2).toString() : "1"
+ imgsource += ".svg"
+ setting5aColumn.source = imgsource
+ }
+ function updatesetting5bImg() {
+ var imgsource = "examples/setting5b/example-"
+ imgsource += setting5bAcc.checked ? (setting5bAcc.currentValue + 2).toString() : "1"
+ imgsource += ".svg"
+ setting5bColumn.source = imgsource
+ }
+ function updatesetting6Img() {
+ var imgsource = "examples/setting6/example-"
+ imgsource += setting6aAcc.checked ? (setting6aAcc.currentValue + 2).toString() : "1"
+ imgsource += ".svg"
+ setting6Image.source = imgsource
+ }
+ function updatesetting7Img() {
+ var imgsource = "examples/setting7/example-"
+ if (setting7Acc.checked) {
+ imgsource += setting74Box.checked ? "1" : "0"
+ imgsource += setting7Acc.currentValue.toString()
+ imgsource += setting7Cancel.checked ? setting7Cancel.value.toString() : "0"
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting7Column.source = imgsource
+ }
+ function updatesetting8Img() {
+ var imgsource = "examples/setting8/example-"
+ if (setting8Acc.checked) {
+ imgsource += setting8Acc.currentValue.toString()
+ imgsource += setting8Cancel.checked ? setting8Cancel.value.toString() : "0"
+ } else {
+ imgsource += "false"
+ }
+ imgsource += ".svg"
+ setting8Column.source = imgsource
+ }
+ function updatesetting9aImg() {
+ setting9aImage.source = "examples/setting9a/example-" + setting9aCancel.value.toString() + ".svg"
+ }
+ function updatesetting9bImg() {
+ setting9bImage.source = "examples/setting9b/example-" + setting9bCancel.value.toString() + ".svg"
+ }
+ Settings {
+ id: options
+ category: "Courtesy Accidentals Plugin"
+ property var uSettings: '{
+ "version": "4.0-beta",
+ "edited": false
+ }'
+ //Qt.labs.settings doesn't like working with object types
+ }
+ function smartQuit() {
+ mainWindow.close()
+ quit()
+ }
+}
diff --git a/share/plugins/courtesy_accidentals/removeCourtesyAccidentals.qml_disabled b/share/plugins/courtesy_accidentals/removeCourtesyAccidentals.qml_disabled
index 8b4484ccdde97..01035d009b231 100644
--- a/share/plugins/courtesy_accidentals/removeCourtesyAccidentals.qml_disabled
+++ b/share/plugins/courtesy_accidentals/removeCourtesyAccidentals.qml_disabled
@@ -1,7 +1,7 @@
//==============================================
-// remove courtesy accidentals v1.0
-//
-// Copyright (C)2012-2019 Jörn Eichler (heuchi)
+// Cautionary Accidentals v4.0
+// https://github.com/XiaoMigros/Cautionary-Accidentals
+// Copyright (C)2023 XiaoMigros
//
// 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
@@ -19,282 +19,15 @@
import QtQuick 2.0
import MuseScore 3.0
+import "assets/accidentals.js" as Accidentals
MuseScore {
- version: "1.0"
- description: "This plugin removes courtesy accidentals"
- title: "Remove Courtesy Accidentals"
+ title: qsTr("Remove Courtesy Accidentals")
+ version: "4.0"
+ description: qsTr("This plugin removes cautionary accidentals from the score")
categoryCode: "composing-arranging-tools"
- thumbnailName: "accidentals.png"
-
- //pluginType: "dock"
+ thumbnailName: "assets/accidentals.png"
requiresScore: true
- // if nothing is selected process whole score
- property bool processAll: false
-
- // function tpcName
- //
- // return name of note
-
- function tpcName(tpc) {
- var tpcNames = new Array(
- "Fbb", "Cbb", "Gbb", "Dbb", "Abb", "Ebb", "Bbb",
- "Fb", "Cb", "Gb", "Db", "Ab", "Eb", "Bb",
- "F", "C", "G", "D", "A", "E", "B",
- "F#", "C#", "G#", "D#", "A#", "E#", "B#",
- "F##", "C##", "G##", "D##", "A##", "E##", "B##"
- );
-
- return(tpcNames[tpc+1]);
- }
-
- // function processNote
- //
- // for each measure we create a table that contains
- // the actual 'noteName' of each 'noteClass'
- //
- // a 'noteClass' is the natural name of a space
- // or line of the staff and the octave:
- // C5, F6, B3 are 'noteClass'
- //
- // a 'noteName' would be C, F#, Bb for example
- // (we don't need the octave here)
- //
- // curMeasureArray[] =
-
- function processNote(note,curMeasureArray,keySig) {
- var octave=Math.floor(note.pitch/12);
-
- // correct octave for Cb and Cbb
- if(note.tpc1 == 7 || note.tpc1 == 0) {
- octave++; // belongs to higher octave
- }
- // correct octave for B# and B##
- if(note.tpc1 == 26 || note.tpc1 == 33) {
- octave--; // belongs to lower octave
- }
-
- var noteName = tpcName(note.tpc);
- var noteClass = noteName.charAt(0)+octave;
-
- // a tied back note never needs an accidental
- if (note.tieBack != null) {
- if(note.accidental != null) {
- // security checks
- var thisPitch = note.pitch;
- var thisAcc = note.accidentalType;
-
- // remove
- note.accidentalType = Accidental.NONE;
-
- // if pitch changed, we were wrong...
- if(note.pitch != thisPitch) {
- console.log("ERROR1: pitch of note changed!");
- //note.color = "#ff0000";
- note.accidentalType = thisAcc;
- }
- }
- // if the tied back note is not part of
- // the current key sig, we need to remember it.
- //if( ! (note.tpc > keySig+12 && note.tpc < keySig+20)) {
- // curMeasureArray[noteClass]=noteName;
- //}
-
- // we're done for a tied back note.
- return;
- }
-
- // check if current note needs acc
- if(typeof curMeasureArray[noteClass] !== 'undefined') {
- // we have information on the previous note
- // in the same measure:
- // if this note is the same noteClass and noteName
- // it doesn't need an accidental
- if(curMeasureArray[noteClass] == noteName) {
- // remove accidental if present
- if(note.accidental != null) {
- // security checks
- var thisPitch = note.pitch;
- var thisAcc = note.accidentalType;
-
- // remove
- note.accidentalType = Accidental.NONE;
-
- // if pitch changed, we were wrong...
- if(note.pitch != thisPitch) {
- console.log("ERROR2: pitch of note changed!");
- //note.color = "#ff0000";
- note.accidentalType = thisAcc;
- }
- }
- }
- } else {
- // we don't have this note in the current measure
- // so it depends on the current key signature
- if(note.tpc > keySig+12 && note.tpc < keySig+20) {
- // we don't need an accidental in the current key sig
- // remove accidental if present
- if(note.accidental != null) {
- // security checks
- var thisPitch = note.pitch;
- var thisAcc = note.accidentalType;
-
- // remove
- note.accidentalType = Accidental.NONE;
-
- // if pitch changed, we were wrong...
- if(note.pitch != thisPitch) {
- console.log("ERROR3: pitch of note changed!");
- //note.color = "#ff0000";
- note.accidentalType = thisAcc;
- console.log("KeySig="+keySig+", tpc="+note.tpc);
- }
- }
- }
- }
-
- curMeasureArray[noteClass]=noteName;
- }
-
- // function processPart
- //
- // do the actual work: process all given tracks in parallel
- // add courtesy accidentals where needed.
- //
- // We go through all tracks simultaneously, because we also want courtesy
- // accidentals for notes across different staves when they are in the
- // same octave and for notes of different voices in the same octave
-
- function processPart(cursor,endTick,startTrack,endTrack) {
- if(processAll) {
- // we need to reset track first, otherwise
- // rewind(0) doesn't work correctly
- // we need to set staffIdx and voice to
- // get correct key signature.
- cursor.staffIdx = startTrack / 4;
- cursor.voice = 0;
- cursor.rewind(0);
- } else {
- cursor.rewind(1);
- // we need to set staffIdx and voice to
- // get correct key signature.
- cursor.staffIdx = startTrack / 4;
- cursor.voice = 0;
- }
-
- var segment = cursor.segment;
-
- // we use the cursor to know measure boundaries
- // and to get the current key signature
- var keySig = cursor.keySignature;
- cursor.nextMeasure();
-
- var curMeasureArray = new Array();
-
- // we use a segment, because the cursor always proceeds to
- // the next element in the given track and we don't know
- // in which track the element is.
- var inLastMeasure=false;
- while(segment && (processAll || segment.tick < endTick)) {
- // check if still inside same measure
- if(!inLastMeasure && !(segment.tick < cursor.tick)) {
- // new measure
- curMeasureArray = new Array();
- keySig = cursor.keySignature;
- if(!cursor.nextMeasure()) {
- inLastMeasure=true;
- }
- }
-
- for(var track=startTrack; track 0) {
- var graceChords = segment.elementAt(track).graceNotes;
-
- for(var j=0;j