From: Norbert Preining Date: Wed, 13 Jan 2021 01:32:42 +0000 (+0000) Subject: Import kguiaddons_5.78.0.orig.tar.xz X-Git-Tag: archive/raspbian/5.101.0-1+rpi1~2^2^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=206e505abe48066fa8d1efa2862463ade585bc6b;p=kguiaddons.git Import kguiaddons_5.78.0.orig.tar.xz [dgit import orig kguiaddons_5.78.0.orig.tar.xz] --- 206e505abe48066fa8d1efa2862463ade585bc6b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3330ae3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# Ignore the following files +*~ +*.[oa] +*.diff +*.kate-swp +*.kdev4 +.kdev_include_paths +*.kdevelop.pcs +*.moc +*.moc.cpp +*.orig +*.user +.*.swp +.swp.* +Doxyfile +Makefile +avail +random_seed +/build*/ +CMakeLists.txt.user* +*.unc-backup* +.cmake/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9233bbd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,100 @@ +cmake_minimum_required(VERSION 3.5) + +set(KF5_VERSION "5.78.0") # handled by release scripts +project(KGuiAddons VERSION ${KF5_VERSION}) + +include(FeatureSummary) +find_package(ECM 5.78.0 NO_MODULE) +set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://commits.kde.org/extra-cmake-modules") +feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) + + + +option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF) +add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)") + +if (UNIX AND NOT ANDROID AND NOT APPLE) + option(WITH_WAYLAND "Build with support for KeySequenceEditor inhibiting shortcuts on Wayland" ON) + add_feature_info(WAYLAND ${WITH_WAYLAND} "KeySequenceEditor inhibiting shortcuts on Wayland") +else() + set(WITH_WAYLAND OFF) +endif() + +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + +include(KDEInstallDirs) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(KDECMakeSettings) + +set(REQUIRED_QT_VERSION 5.14.0) +if (WITH_WAYLAND) + set(_qtgui_find_components COMPONENTS Private) +endif() +# QtGui must be found becore Qt5WaylandClient +find_package(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE ${_qtgui_find_components}) + +if (NOT APPLE AND NOT WIN32) + find_package(X11 MODULE) + find_package(XCB MODULE COMPONENTS XCB) +endif() + +if (WITH_WAYLAND) + find_package(Qt5WaylandClient ${REQUIRED_QT_VERSION} NO_MODULE) + find_package(QtWaylandScanner REQUIRED) + find_package(Wayland 1.9 REQUIRED Client) +endif() + +include(GenerateExportHeader) +include(ECMSetupVersion) +include(ECMGenerateHeaders) +include(ECMQtDeclareLoggingCategory) +include(ECMAddQch) + +ecm_setup_version(PROJECT VARIABLE_PREFIX KGUIADDONS + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kguiaddons_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5GuiAddonsConfigVersion.cmake" + SOVERSION 5) + + +add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050e00) +add_definitions(-DQT_NO_FOREACH) +add_subdirectory(src) +if (BUILD_TESTING) + add_subdirectory(autotests) + add_subdirectory(tests) +endif() + +# create a Config.cmake and a ConfigVersion.cmake file and install them +set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5GuiAddons") + +if (BUILD_QCH) + ecm_install_qch_export( + TARGETS KF5GuiAddons_QCH + FILE KF5GuiAddonsQchTargets.cmake + DESTINATION "${CMAKECONFIG_INSTALL_DIR}" + COMPONENT Devel + ) + set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KF5GuiAddonsQchTargets.cmake\")") +endif() + +include(CMakePackageConfigHelpers) + +configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KF5GuiAddonsConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/KF5GuiAddonsConfig.cmake" + INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} + ) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KF5GuiAddonsConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/KF5GuiAddonsConfigVersion.cmake" + DESTINATION "${CMAKECONFIG_INSTALL_DIR}" + COMPONENT Devel ) + + +install(EXPORT KF5GuiAddonsTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" + FILE KF5GuiAddonsTargets.cmake NAMESPACE KF5:: COMPONENT Devel) + + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kguiaddons_version.h + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel ) + +feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/KF5GuiAddonsConfig.cmake.in b/KF5GuiAddonsConfig.cmake.in new file mode 100644 index 0000000..fd7330a --- /dev/null +++ b/KF5GuiAddonsConfig.cmake.in @@ -0,0 +1,10 @@ +@PACKAGE_INIT@ + +# Any changes in this file will be overwritten by CMake. + +include(CMakeFindDependencyMacro) +find_dependency(Qt5Gui @REQUIRED_QT_VERSION@) + + +include("${CMAKE_CURRENT_LIST_DIR}/KF5GuiAddonsTargets.cmake") +@PACKAGE_INCLUDE_QCHTARGETS@ diff --git a/LICENSES/GPL-2.0-only.txt b/LICENSES/GPL-2.0-only.txt new file mode 100644 index 0000000..0f3d641 --- /dev/null +++ b/LICENSES/GPL-2.0-only.txt @@ -0,0 +1,319 @@ +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software +is covered by the GNU Lesser General Public License instead.) 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 this service 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 make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or +for a fee, you must give the recipients all the rights that you have. 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. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its recipients +to know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will individually +obtain patent licenses, in effect making the program proprietary. To prevent +this, we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms +of this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or translated +into another language. (Hereinafter, translation is included without limitation +in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running the Program +is not restricted, and the output from the Program is covered only if its +contents constitute a work based on the Program (independent of having been +made by running the Program). Whether that is true depends on what the Program +does. + +1. You may copy and distribute 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 and disclaimer of warranty; +keep intact all the notices that refer to this License and to the absence +of any warranty; and give any other recipients of the Program a copy of this +License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, +thus forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + +a) You must cause the modified files to carry prominent notices stating that +you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in whole or +in part contains or is derived from the Program or any part thereof, to be +licensed as a whole at no charge to all third parties under the terms of this +License. + +c) If the modified program normally reads commands interactively when run, +you must cause it, when started running for such interactive use in the most +ordinary way, to print or display an announcement including an appropriate +copyright notice and a notice that there is no warranty (or else, saying that +you provide a warranty) and that users may redistribute the program under +these conditions, and telling the user how to view a copy of this License. +(Exception: if the Program itself is interactive but does not normally print +such an announcement, your work based on the Program is not required to print +an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Program, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Program. + +In addition, mere aggregation of another work not based on the Program with +the Program (or with a work based on the Program) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under Section +2) in object code or executable form under the terms of Sections 1 and 2 above +provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, +which must be distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +b) Accompany it with a written offer, valid for at least three years, to give +any third party, for a charge no more than your cost of physically performing +source distribution, a complete machine-readable copy of the corresponding +source code, to be distributed under the terms of Sections 1 and 2 above on +a medium customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to distribute +corresponding source code. (This alternative is allowed only for noncommercial +distribution and only if you received the program in object code or executable +form with such an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +If distribution of executable or object code is made by offering access to +copy from a designated place, then offering equivalent access to copy the +source code from the same place counts as distribution of the source code, +even though third parties are not compelled to copy the source along with +the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except +as expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses terminated +so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Program or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Program +(or any work based on the Program), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor +to copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of +the rights granted herein. You are not responsible for enforcing compliance +by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Program at all. For example, if a +patent license would not permit royalty-free redistribution of the Program +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system, which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Program under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of +the 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 +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Program does not specify a version number of this License, you may choose +any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing and reuse +of software generally. + + NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +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. +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 convey 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)< yyyy> + +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 2 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, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when +it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision 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, the commands you use may be +called something other than `show w' and `show c'; they could even be mouse-clicks +or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' +(which makes passes at compilers) written by James Hacker. + +, 1 April 1989 Ty Coon, President of Vice This 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. diff --git a/LICENSES/GPL-3.0-only.txt b/LICENSES/GPL-3.0-only.txt new file mode 100644 index 0000000..e142a52 --- /dev/null +++ b/LICENSES/GPL-3.0-only.txt @@ -0,0 +1,625 @@ +GNU GENERAL PUBLIC LICENSE + +Version 3, 29 June 2007 + +Copyright © 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/LICENSES/LGPL-2.0-only.txt b/LICENSES/LGPL-2.0-only.txt new file mode 100644 index 0000000..5c96471 --- /dev/null +++ b/LICENSES/LGPL-2.0-only.txt @@ -0,0 +1,446 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. + +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is numbered 2 because +it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Library General Public License, applies to some specially +designated Free Software Foundation software, and to any other libraries whose +authors decide to use it. You can use it for your libraries, 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 this service 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 make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +a program with the library, you must provide complete object files to the +recipients so that they can relink them with the library, after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +Our method of protecting your rights has two steps: (1) copyright the library, +and (2) offer you this license which gives you legal permission to copy, distribute +and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone +understands that there is no warranty for this free library. If the library +is modified by someone else and passed on, we want its recipients to know +that what they have is not the original version, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that companies distributing free software will individually +obtain patent licenses, thus in effect transforming the program into proprietary +software. To prevent this, we have made it clear that any patent must be licensed +for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License, which was designed for utility programs. This license, +the GNU Library General Public License, applies to certain designated libraries. +This license is quite different from the ordinary one; be sure to read it +in full, and don't assume that anything in it is the same as in the ordinary +license. + +The reason we have a separate public license for some libraries is that they +blur the distinction we usually make between modifying or adding to a program +and simply using it. Linking a program with a library, without changing the +library, is in some sense simply using the library, and is analogous to running +a utility program or application program. However, in a textual and legal +sense, the linked executable is a combined work, a derivative of the original +library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License +for libraries did not effectively promote software sharing, because most developers +did not use the libraries. We concluded that weaker conditions might promote +sharing better. + +However, unrestricted linking of non-free programs would deprive the users +of those programs of all benefit from the free status of the libraries themselves. +This Library General Public License is intended to permit developers of non-free +programs to use free libraries, while preserving your freedom as a user of +such programs to change the free libraries that are incorporated in them. +(We have not seen how to achieve this as regards changes in header files, +but we have achieved it as regards changes in the actual functions of the +Library.) The hope is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General +Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a +notice placed by the copyright holder or other authorized party saying it +may be distributed under the terms of this Library General Public License +(also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also compile or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +c) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +d) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the source code distributed need +not include anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the operating +system on which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Library 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 Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. + +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This library 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 Library General Public License for more +details. + +You should have received a copy of the GNU Library General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-2.0-or-later.txt b/LICENSES/LGPL-2.0-or-later.txt new file mode 100644 index 0000000..5c96471 --- /dev/null +++ b/LICENSES/LGPL-2.0-or-later.txt @@ -0,0 +1,446 @@ +GNU LIBRARY GENERAL PUBLIC LICENSE + +Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. + +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is numbered 2 because +it goes with version 2 of the ordinary GPL.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Library General Public License, applies to some specially +designated Free Software Foundation software, and to any other libraries whose +authors decide to use it. You can use it for your libraries, 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 this service 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 make restrictions that forbid anyone to +deny you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library, or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +a program with the library, you must provide complete object files to the +recipients so that they can relink them with the library, after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +Our method of protecting your rights has two steps: (1) copyright the library, +and (2) offer you this license which gives you legal permission to copy, distribute +and/or modify the library. + +Also, for each distributor's protection, we want to make certain that everyone +understands that there is no warranty for this free library. If the library +is modified by someone else and passed on, we want its recipients to know +that what they have is not the original version, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that companies distributing free software will individually +obtain patent licenses, thus in effect transforming the program into proprietary +software. To prevent this, we have made it clear that any patent must be licensed +for everyone's free use or not licensed at all. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License, which was designed for utility programs. This license, +the GNU Library General Public License, applies to certain designated libraries. +This license is quite different from the ordinary one; be sure to read it +in full, and don't assume that anything in it is the same as in the ordinary +license. + +The reason we have a separate public license for some libraries is that they +blur the distinction we usually make between modifying or adding to a program +and simply using it. Linking a program with a library, without changing the +library, is in some sense simply using the library, and is analogous to running +a utility program or application program. However, in a textual and legal +sense, the linked executable is a combined work, a derivative of the original +library, and the ordinary General Public License treats it as such. + +Because of this blurred distinction, using the ordinary General Public License +for libraries did not effectively promote software sharing, because most developers +did not use the libraries. We concluded that weaker conditions might promote +sharing better. + +However, unrestricted linking of non-free programs would deprive the users +of those programs of all benefit from the free status of the libraries themselves. +This Library General Public License is intended to permit developers of non-free +programs to use free libraries, while preserving your freedom as a user of +such programs to change the free libraries that are incorporated in them. +(We have not seen how to achieve this as regards changes in header files, +but we have achieved it as regards changes in the actual functions of the +Library.) The hope is that this will lead to faster development of free libraries. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, while the latter only works together with the library. + +Note that it is possible for a library to be covered by the ordinary General +Public License rather than by this special one. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library which contains a +notice placed by the copyright holder or other authorized party saying it +may be distributed under the terms of this Library General Public License +(also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also compile or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +c) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +d) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the source code distributed need +not include anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the operating +system on which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties to this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Library 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 Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +one line to give the library's name and an idea of what it does. + +Copyright (C) year name of author + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Library General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This library 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 Library General Public License for more +details. + +You should have received a copy of the GNU Library General Public License +along with this library; if not, write to the Free Software Foundation, Inc., +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +signature of Ty Coon, 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-2.1-only.txt b/LICENSES/LGPL-2.1-only.txt new file mode 100644 index 0000000..130dffb --- /dev/null +++ b/LICENSES/LGPL-2.1-only.txt @@ -0,0 +1,467 @@ +GNU LESSER GENERAL PUBLIC LICENSE + +Version 2.1, February 1999 + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. + +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts as the +successor of the GNU Library Public License, version 2, hence the version +number 2.1.] + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public Licenses are intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. + +This license, the Lesser General Public License, applies to some specially +designated software packages--typically libraries--of the Free Software Foundation +and other authors who decide to use it. You can use it too, but we suggest +you first think carefully about whether this license or the ordinary General +Public License is the better strategy to use in any particular case, based +on the explanations below. + +When we speak of free software, we are referring to freedom of use, 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 this service if you +wish); that you receive source code or can get it if you want it; that you +can change the software and use pieces of it in new free programs; and that +you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors +to deny you these rights or to ask you to surrender these rights. These restrictions +translate to certain responsibilities for you if you distribute copies of +the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for +a fee, you must give the recipients all the rights that we gave you. You must +make sure that they, too, receive or can get the source code. If you link +other code with the library, you must provide complete object files to the +recipients, so that they can relink them with the library after making changes +to the library and recompiling it. And you must show them these terms so they +know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, +and (2) we offer you this license, which gives you legal permission to copy, +distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no +warranty for the free library. Also, if the library is modified by someone +else and passed on, the recipients should know that what they have is not +the original version, so that the original author's reputation will not be +affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free +program. We wish to make sure that a company cannot effectively restrict the +users of a free program by obtaining a restrictive license from a patent holder. +Therefore, we insist that any patent license obtained for a version of the +library must be consistent with the full freedom of use specified in this +license. + +Most GNU software, including some libraries, is covered by the ordinary GNU +General Public License. This license, the GNU Lesser General Public License, +applies to certain designated libraries, and is quite different from the ordinary +General Public License. We use this license for certain libraries in order +to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared +library, the combination of the two is legally speaking a combined work, a +derivative of the original library. The ordinary General Public License therefore +permits such linking only if the entire combination fits its criteria of freedom. +The Lesser General Public License permits more lax criteria for linking other +code with the library. + +We call this license the "Lesser" General Public License because it does Less +to protect the user's freedom than the ordinary General Public License. It +also provides other free software developers Less of an advantage over competing +non-free programs. These disadvantages are the reason we use the ordinary +General Public License for many libraries. However, the Lesser license provides +advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the +widest possible use of a certain library, so that it becomes a de-facto standard. +To achieve this, non-free programs must be allowed to use the library. A more +frequent case is that a free library does the same job as widely used non-free +libraries. In this case, there is little to gain by limiting the free library +to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs +enables a greater number of people to use a large body of free software. For +example, permission to use the GNU C Library in non-free programs enables +many more people to use the whole GNU operating system, as well as its variant, +the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' +freedom, it does ensure that the user of a program that is linked with the +Library has the freedom and the wherewithal to run that program using a modified +version of the Library. + +The precise terms and conditions for copying, distribution and modification +follow. Pay close attention to the difference between a "work based on the +library" and a "work that uses the library". The former contains code derived +from the library, whereas the latter must be combined with the library in +order to run. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Lesser General +Public License (also called "this License"). Each licensee is addressed as +"you". + +A "library" means a collection of software functions and/or data prepared +so as to be conveniently linked with application programs (which use some +of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has +been distributed under these terms. A "work based on the Library" means either +the Library or any derivative work under copyright law: that is to say, a +work containing the Library or a portion of it, either verbatim or with modifications +and/or translated straightforwardly into another language. (Hereinafter, translation +is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications +to it. For a library, complete source code means all the source code for all +modules it contains, plus any associated interface definition files, plus +the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered +by this License; they are outside its scope. The act of running a program +using the Library is not restricted, and output from such a program is covered +only if its contents constitute a work based on the Library (independent of +the use of the Library in a tool for writing it). Whether that is true depends +on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and disclaimer +of warranty; keep intact all the notices that refer to this License and to +the absence of any warranty; and distribute a copy of this License along with +the Library. + +You may charge a fee for the physical act of transferring a copy, and you +may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, +thus forming a work based on the Library, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all +of these conditions: + + a) The modified work must itself be a software library. + +b) You must cause the files modified to carry prominent notices stating that +you changed the files and the date of any change. + +c) You must cause the whole of the work to be licensed at no charge to all +third parties under the terms of this License. + +d) If a facility in the modified Library refers to a function or a table of +data to be supplied by an application program that uses the facility, other +than as an argument passed when the facility is invoked, then you must make +a good faith effort to ensure that, in the event an application does not supply +such function or table, the facility still operates, and performs whatever +part of its purpose remains meaningful. + +(For example, a function in a library to compute square roots has a purpose +that is entirely well-defined independent of the application. Therefore, Subsection +2d requires that any application-supplied function or table used by this function +must be optional: if the application does not supply it, the square root function +must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Library, and can be reasonably +considered independent and separate works in themselves, then this License, +and its terms, do not apply to those sections when you distribute them as +separate works. But when you distribute the same sections as part of a whole +which is a work based on the Library, the distribution of the whole must be +on the terms of this License, whose permissions for other licensees extend +to the entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise +the right to control the distribution of derivative or collective works based +on the Library. + +In addition, mere aggregation of another work not based on the Library with +the Library (or with a work based on the Library) on a volume of a storage +or distribution medium does not bring the other work under the scope of this +License. + +3. You may opt to apply the terms of the ordinary GNU General Public License +instead of this License to a given copy of the Library. To do this, you must +alter all the notices that refer to this License, so that they refer to the +ordinary GNU General Public License, version 2, instead of to this License. +(If a newer version than version 2 of the ordinary GNU General Public License +has appeared, then you can specify that version instead if you wish.) Do not +make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, +so the ordinary GNU General Public License applies to all subsequent copies +and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library +into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of +it, under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you accompany it with the complete corresponding +machine-readable source code, which must be distributed under the terms of +Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated +place, then offering equivalent access to copy the source code from the same +place satisfies the requirement to distribute the source code, even though +third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but +is designed to work with the Library by being compiled or linked with it, +is called a "work that uses the Library". Such a work, in isolation, is not +a derivative work of the Library, and therefore falls outside the scope of +this License. + +However, linking a "work that uses the Library" with the Library creates an +executable that is a derivative of the Library (because it contains portions +of the Library), rather than a "work that uses the library". The executable +is therefore covered by this License. Section 6 states terms for distribution +of such executables. + +When a "work that uses the Library" uses material from a header file that +is part of the Library, the object code for the work may be a derivative work +of the Library even though the source code is not. Whether this is true is +especially significant if the work can be linked without the Library, or if +the work is itself a library. The threshold for this to be true is not precisely +defined by law. + +If such an object file uses only numerical parameters, data structure layouts +and accessors, and small macros and small inline functions (ten lines or less +in length), then the use of the object file is unrestricted, regardless of +whether it is legally a derivative work. (Executables containing this object +code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute +the object code for the work under the terms of Section 6. Any executables +containing that work also fall under Section 6, whether or not they are linked +directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work +that uses the Library" with the Library to produce a work containing portions +of the Library, and distribute that work under terms of your choice, provided +that the terms permit modification of the work for the customer's own use +and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library +is used in it and that the Library and its use are covered by this License. +You must supply a copy of this License. If the work during execution displays +copyright notices, you must include the copyright notice for the Library among +them, as well as a reference directing the user to the copy of this License. +Also, you must do one of these things: + +a) Accompany the work with the complete corresponding machine-readable source +code for the Library including whatever changes were used in the work (which +must be distributed under Sections 1 and 2 above); and, if the work is an +executable linked with the Library, with the complete machine-readable "work +that uses the Library", as object code and/or source code, so that the user +can modify the Library and then relink to produce a modified executable containing +the modified Library. (It is understood that the user who changes the contents +of definitions files in the Library will not necessarily be able to recompile +the application to use the modified definitions.) + +b) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the library +already present on the user's computer system, rather than copying library +functions into the executable, and (2) will operate properly with a modified +version of the library, if the user installs one, as long as the modified +version is interface-compatible with the version that the work was made with. + +c) Accompany the work with a written offer, valid for at least three years, +to give the same user the materials specified in Subsection 6a, above, for +a charge no more than the cost of performing this distribution. + +d) If distribution of the work is made by offering access to copy from a designated +place, offer equivalent access to copy the above specified materials from +the same place. + +e) Verify that the user has already received a copy of these materials or +that you have already sent this user a copy. + +For an executable, the required form of the "work that uses the Library" must +include any data and utility programs needed for reproducing the executable +from it. However, as a special exception, the materials to be distributed +need not include anything that is normally distributed (in either source or +binary form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component itself +accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of +other proprietary libraries that do not normally accompany the operating system. +Such a contradiction means you cannot use both them and the Library together +in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side +in a single library together with other library facilities not covered by +this License, and distribute such a combined library, provided that the separate +distribution of the work based on the Library and of the other library facilities +is otherwise permitted, and provided that you do these two things: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities. This must be distributed +under the terms of the Sections above. + +b) Give prominent notice with the combined library of the fact that part of +it is a work based on the Library, and explaining where to find the accompanying +uncombined form of the same work. + +8. You may not copy, modify, sublicense, link with, or distribute the Library +except as expressly provided under this License. Any attempt otherwise to +copy, modify, sublicense, link with, or distribute the Library is void, and +will automatically terminate your rights under this License. However, parties +who have received copies, or rights, from you under this License will not +have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed +it. However, nothing else grants you permission to modify or distribute the +Library or its derivative works. These actions are prohibited by law if you +do not accept this License. Therefore, by modifying or distributing the Library +(or any work based on the Library), you indicate your acceptance of this License +to do so, and all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), +the recipient automatically receives a license from the original licensor +to copy, distribute, link with or modify the Library subject to these terms +and conditions. You may not impose any further restrictions on the recipients' +exercise of the rights granted herein. You are not responsible for enforcing +compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement +or for any other reason (not limited to patent issues), 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 distribute so as to satisfy simultaneously your +obligations under this License and any other pertinent obligations, then as +a consequence you may not distribute the Library at all. For example, if a +patent license would not permit royalty-free redistribution of the Library +by all those who receive copies directly or indirectly through you, then the +only way you could satisfy both it and this License would be to refrain entirely +from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents +or other property right claims or to contest validity of any such claims; +this section has the sole purpose of protecting the integrity of the free +software distribution system which is implemented by public license practices. +Many people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose +that choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain +countries either by patents or by copyrighted interfaces, the original copyright +holder who places the Library under this License may add an explicit geographical +distribution limitation excluding those countries, so that distribution is +permitted only in or among countries not thus excluded. In such case, this +License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of +the Lesser 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 Library specifies +a version number of this License which applies to it and "any later version", +you have the option of following the terms and conditions either of that version +or of any later version published by the Free Software Foundation. If the +Library does not specify a license version number, you may choose any version +ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs +whose distribution conditions are incompatible with these, write to the author +to ask for permission. For software which is copyrighted by the Free Software +Foundation, write to the Free Software Foundation; we sometimes make exceptions +for this. Our decision will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY +"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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH +HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest possible +use to the public, we recommend making it free software that everyone can +redistribute and change. You can do so by permitting redistribution under +these terms (or, alternatively, under the terms of the ordinary General Public +License). + +To apply these terms, attach the following notices to the library. It is safest +to attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the "copyright" +line and a pointer to where the full notice is found. + +< one line to give the library's name and an idea of what it does. > + +Copyright (C) < year > < name of author > + +This library is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 2.1 of the License, or (at your option) +any later version. + +This library 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 Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this library; if not, write to the Free Software Foundation, Inc., 51 +Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information +on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the library, if necessary. Here +is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in + +the library `Frob' (a library for tweaking knobs) written + +by James Random Hacker. + +< signature of Ty Coon > , 1 April 1990 + +Ty Coon, President of Vice + +That's all there is to it! diff --git a/LICENSES/LGPL-3.0-only.txt b/LICENSES/LGPL-3.0-only.txt new file mode 100644 index 0000000..bd405af --- /dev/null +++ b/LICENSES/LGPL-3.0-only.txt @@ -0,0 +1,163 @@ +GNU LESSER 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. + +This version of the GNU Lesser General Public License incorporates the terms +and conditions of version 3 of the GNU General Public License, supplemented +by the additional permissions listed below. + + 0. Additional Definitions. + + + +As used herein, "this License" refers to version 3 of the GNU Lesser General +Public License, and the "GNU GPL" refers to version 3 of the GNU General Public +License. + + + +"The Library" refers to a covered work governed by this License, other than +an Application or a Combined Work as defined below. + + + +An "Application" is any work that makes use of an interface provided by the +Library, but which is not otherwise based on the Library. Defining a subclass +of a class defined by the Library is deemed a mode of using an interface provided +by the Library. + + + +A "Combined Work" is a work produced by combining or linking an Application +with the Library. The particular version of the Library with which the Combined +Work was made is also called the "Linked Version". + + + +The "Minimal Corresponding Source" for a Combined Work means the Corresponding +Source for the Combined Work, excluding any source code for portions of the +Combined Work that, considered in isolation, are based on the Application, +and not on the Linked Version. + + + +The "Corresponding Application Code" for a Combined Work means the object +code and/or source code for the Application, including any data and utility +programs needed for reproducing the Combined Work from the Application, but +excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + +You may convey a covered work under sections 3 and 4 of this License without +being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + +If you modify a copy of the Library, and, in your modifications, a facility +refers to a function or data to be supplied by an Application that uses the +facility (other than as an argument passed when the facility is invoked), +then you may convey a copy of the modified version: + +a) under this License, provided that you make a good faith effort to ensure +that, in the event an Application does not supply the function or data, the +facility still operates, and performs whatever part of its purpose remains +meaningful, or + +b) under the GNU GPL, with none of the additional permissions of this License +applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + +The object code form of an Application may incorporate material from a header +file that is part of the Library. You may convey such object code under terms +of your choice, provided that, if the incorporated material is not limited +to numerical parameters, data structure layouts and accessors, or small macros, +inline functions and templates (ten or fewer lines in length), you do both +of the following: + +a) Give prominent notice with each copy of the object code that the Library +is used in it and that the Library and its use are covered by this License. + +b) Accompany the object code with a copy of the GNU GPL and this license document. + + 4. Combined Works. + +You may convey a Combined Work under terms of your choice that, taken together, +effectively do not restrict modification of the portions of the Library contained +in the Combined Work and reverse engineering for debugging such modifications, +if you also do each of the following: + +a) Give prominent notice with each copy of the Combined Work that the Library +is used in it and that the Library and its use are covered by this License. + +b) Accompany the Combined Work with a copy of the GNU GPL and this license +document. + +c) For a Combined Work that displays copyright notices during execution, include +the copyright notice for the Library among these notices, as well as a reference +directing the user to the copies of the GNU GPL and this license document. + + d) Do one of the following: + +0) Convey the Minimal Corresponding Source under the terms of this License, +and the Corresponding Application Code in a form suitable for, and under terms +that permit, the user to recombine or relink the Application with a modified +version of the Linked Version to produce a modified Combined Work, in the +manner specified by section 6 of the GNU GPL for conveying Corresponding Source. + +1) Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (a) uses at run time a copy of the Library +already present on the user's computer system, and (b) will operate properly +with a modified version of the Library that is interface-compatible with the +Linked Version. + +e) Provide Installation Information, but only if you would otherwise be required +to provide such information under section 6 of the GNU GPL, and only to the +extent that such information is necessary to install and execute a modified +version of the Combined Work produced by recombining or relinking the Application +with a modified version of the Linked Version. (If you use option 4d0, the +Installation Information must accompany the Minimal Corresponding Source and +Corresponding Application Code. If you use option 4d1, you must provide the +Installation Information in the manner specified by section 6 of the GNU GPL +for conveying Corresponding Source.) + + 5. Combined Libraries. + +You may place library facilities that are a work based on the Library side +by side in a single library together with other library facilities that are +not Applications and are not covered by this License, and convey such a combined +library under terms of your choice, if you do both of the following: + +a) Accompany the combined library with a copy of the same work based on the +Library, uncombined with any other library facilities, conveyed under the +terms of this License. + +b) Give prominent notice with the combined library that part of it is a work +based on the Library, and explaining where to find the accompanying uncombined +form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + +The Free Software Foundation may publish revised and/or new versions of the +GNU Lesser 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 Library as you +received it specifies that a certain numbered version of the GNU Lesser General +Public License "or any later version" applies to it, you have the option of +following the terms and conditions either of that published version or of +any later version published by the Free Software Foundation. If the Library +as you received it does not specify a version number of the GNU Lesser General +Public License, you may choose any version of the GNU Lesser General Public +License ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide whether +future versions of the GNU Lesser General Public License shall apply, that +proxy's public statement of acceptance of any version is permanent authorization +for you to choose that version for the Library. diff --git a/LICENSES/LicenseRef-KDE-Accepted-GPL.txt b/LICENSES/LicenseRef-KDE-Accepted-GPL.txt new file mode 100644 index 0000000..60a2dff --- /dev/null +++ b/LICENSES/LicenseRef-KDE-Accepted-GPL.txt @@ -0,0 +1,12 @@ +This library 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) at any later version that is +accepted by the membership of KDE e.V. (or its successor +approved by the membership of KDE e.V.), which shall act as a +proxy as defined in Section 14 of version 3 of the license. + +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. diff --git a/LICENSES/LicenseRef-KDE-Accepted-LGPL.txt b/LICENSES/LicenseRef-KDE-Accepted-LGPL.txt new file mode 100644 index 0000000..232b3c5 --- /dev/null +++ b/LICENSES/LicenseRef-KDE-Accepted-LGPL.txt @@ -0,0 +1,12 @@ +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3 of the license or (at your option) any later version +that is accepted by the membership of KDE e.V. (or its successor +approved by the membership of KDE e.V.), which shall act as a +proxy as defined in Section 6 of version 3 of the license. + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..063d19f --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# KDE GUI Addons + +Utilities for graphical user interfaces + +## Introduction + +The KDE GUI addons provide utilities for graphical user interfaces in the areas +of colors, fonts, text, images, keyboard input. + diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt new file mode 100644 index 0000000..244b556 --- /dev/null +++ b/autotests/CMakeLists.txt @@ -0,0 +1,14 @@ +find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test) + +remove_definitions(-DQT_NO_CAST_FROM_ASCII) + +include(ECMAddTests) + +ecm_add_tests( + kwordwraptest.cpp + kcolorutilstest.cpp + kiconutilstest.cpp + kcursorsavertest.cpp + keysequencerecordertest.cpp + LINK_LIBRARIES KF5::GuiAddons Qt5::Test +) diff --git a/autotests/kcolorutilstest.cpp b/autotests/kcolorutilstest.cpp new file mode 100644 index 0000000..1db4f0b --- /dev/null +++ b/autotests/kcolorutilstest.cpp @@ -0,0 +1,135 @@ +#include "kcolorutilstest.h" + +#include + +#include +#include "../colors/kcolorspaces_p.h" // private header +#include "../colors/kcolorspaces.cpp" // private implementation + +void tst_KColorUtils::testOverlay() +{ + QColor color1(10, 10, 100); + QColor color2(10, 10, 160); + QColor blended = KColorUtils::overlayColors(color1, color2); + QCOMPARE(blended, color2); // no transparency. + + QColor previous; + // check that when altering the alpha of color2 to be less and less transparent this + // means we are moving more and more towards color2 + for (int i = 10; i <= 255; i += 10) { + color2.setAlpha(i); + blended = KColorUtils::overlayColors(color1, color2); + if (previous.isValid()) { + QCOMPARE(previous.red(), 10); + QCOMPARE(previous.green(), 10); + QVERIFY(previous.blue() <= blended.blue()); + } + previous = blended; + } + + // only the alpha of color 2 alters the output + color2.setAlpha(255); //opaque + color1.setAlpha(80); //opaque + blended = KColorUtils::overlayColors(color2, color2); + QCOMPARE(blended.red(), color2.red()); + QCOMPARE(blended.green(), color2.green()); + QCOMPARE(blended.blue(), color2.blue()); + + // merge from itself to itself gives; TADA; itself again ;) + color2.setAlpha(127); + blended = KColorUtils::overlayColors(color2, color2); + QCOMPARE(blended.red(), color2.red()); + QCOMPARE(blended.green(), color2.green()); + QCOMPARE(blended.blue(), color2.blue()); +} + +#define compareColors(c1, c2) \ + if ( c1 != c2 ) { \ + fprintf( stderr, "%08x != expected value %08x\n", c1.rgb(), c2.rgb() ); \ + QCOMPARE( c1, c2 ); \ + } (void)0 + +void tst_KColorUtils::testMix() +{ + int r, g, b, k; + for (r = 0; r < 52; r++) { + for (g = 0; g < 52; g++) { + for (b = 0; b < 52; b++) { + QColor color(r * 5, g * 5, b * 5); + // Test blend-to-black spectrum + for (k = 5; k >= 0; k--) { + QColor result = KColorUtils::mix(Qt::black, color, k * 0.2); + compareColors(result, QColor(r * k, g * k, b * k)); + } + // Test blend-to-white spectrum + for (k = 5; k >= 0; k--) { + int n = 51 * (5 - k); + QColor result = KColorUtils::mix(Qt::white, color, k * 0.2); + compareColors(result, QColor(n + r * k, n + g * k, n + b * k)); + } + // Test blend-to-self across a couple bias values + for (k = 5; k >= 0; k--) { + QColor result = KColorUtils::mix(color, color, k * 0.2); + compareColors(result, color); + } + } + } + } +} + +void tst_KColorUtils::testHCY() +{ + int r, g, b; + for (r = 0; r < 256; r += 5) { + for (g = 0; g < 256; g += 5) { + for (b = 0; b < 256; b += 5) { + QColor color(r, g, b); + KColorSpaces::KHCY hcy(color); + compareColors(hcy.qColor(), color); + } + } + } +} + +void tst_KColorUtils::testContrast() +{ + QCOMPARE(KColorUtils::contrastRatio(Qt::black, Qt::white), qreal(21.0)); + QCOMPARE(KColorUtils::contrastRatio(Qt::white, Qt::black), qreal(21.0)); + QCOMPARE(KColorUtils::contrastRatio(Qt::black, Qt::black), qreal(1.0)); + QCOMPARE(KColorUtils::contrastRatio(Qt::white, Qt::white), qreal(1.0)); + + // TODO better tests :-) +} + +void checkIsGray(const QColor &color, int line) +{ + KColorSpaces::KHCY hcy(color); + if (hcy.c != qreal(0.0)) { + fprintf(stderr, "%08x has chroma %g, expected gray!\n", color.rgb(), hcy.c); + } + QTest::qCompare(hcy.c, qreal(0.0), "hcy.c", "0.0", __FILE__, line); +} + +void tst_KColorUtils::testShading() +{ + const QColor testGray(128, 128, 128); // Qt::gray isn't pure gray! + + // Test that KHCY gets chroma correct for white/black, grays + checkIsGray(Qt::white, __LINE__); + checkIsGray(testGray, __LINE__); + checkIsGray(Qt::black, __LINE__); + + // Test that lighten/darken/shade don't change chroma for grays + checkIsGray(KColorUtils::shade(Qt::white, -0.1), __LINE__); + checkIsGray(KColorUtils::shade(Qt::black, 0.1), __LINE__); + checkIsGray(KColorUtils::darken(Qt::white, 0.1), __LINE__); + checkIsGray(KColorUtils::darken(testGray, 0.1), __LINE__); + checkIsGray(KColorUtils::darken(testGray, -0.1), __LINE__); + checkIsGray(KColorUtils::darken(Qt::black, -0.1), __LINE__); + checkIsGray(KColorUtils::lighten(Qt::black, 0.1), __LINE__); + checkIsGray(KColorUtils::lighten(testGray, 0.1), __LINE__); + checkIsGray(KColorUtils::lighten(testGray, -0.1), __LINE__); + checkIsGray(KColorUtils::lighten(Qt::white, -0.1), __LINE__); +} + +QTEST_MAIN(tst_KColorUtils) diff --git a/autotests/kcolorutilstest.h b/autotests/kcolorutilstest.h new file mode 100644 index 0000000..dfb1ae7 --- /dev/null +++ b/autotests/kcolorutilstest.h @@ -0,0 +1,17 @@ +#ifndef KCOLORUTILSTEST_H +#define KCOLORUTILSTEST_H + +#include + +class tst_KColorUtils : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testOverlay(); + void testMix(); + void testHCY(); + void testContrast(); + void testShading(); +}; + +#endif // KCOLORUTILSTEST_H diff --git a/autotests/kcursorsavertest.cpp b/autotests/kcursorsavertest.cpp new file mode 100644 index 0000000..d3ea093 --- /dev/null +++ b/autotests/kcursorsavertest.cpp @@ -0,0 +1,35 @@ +/* + SPDX-License-Identifier: LGPL-2.0-or-later + SPDX-FileCopyrightText: 2020 Laurent Montel +*/ + +#include "kcursorsavertest.h" +#include "util/kcursorsaver.h" +#include +QTEST_MAIN(KCursorSaverTest) + +KCursorSaverTest::KCursorSaverTest(QObject *parent) + : QObject(parent) +{ + +} + +void KCursorSaverTest::ignoreWarning() +{ + { + //Emit qwarning when we call twice restoreCursor + KCursorSaver saver(Qt::WaitCursor); + saver.restoreCursor(); + QTest::ignoreMessage(QtWarningMsg, "This KCursorSaver doesn't own the cursor anymore, invalid call to restoreCursor()."); + saver.restoreCursor(); + } + { + //Emit qwarning when "moving A to B and then calling A::restoreCursor()" + KCursorSaver saverA(Qt::WaitCursor); + KCursorSaver saverB = std::move(saverA); + QTest::ignoreMessage(QtWarningMsg, "This KCursorSaver doesn't own the cursor anymore, invalid call to restoreCursor()."); + saverA.restoreCursor(); + QTest::ignoreMessage(QtWarningMsg, "This KCursorSaver doesn't own the cursor anymore, invalid call to restoreCursor()."); + saverB.restoreCursor(); + } +} diff --git a/autotests/kcursorsavertest.h b/autotests/kcursorsavertest.h new file mode 100644 index 0000000..64c119b --- /dev/null +++ b/autotests/kcursorsavertest.h @@ -0,0 +1,22 @@ +/* + SPDX-License-Identifier: LGPL-2.0-or-later + SPDX-FileCopyrightText: 2020 Laurent Montel +*/ + + +#ifndef KCURSORSAVERTEST_H +#define KCURSORSAVERTEST_H + +#include + +class KCursorSaverTest : public QObject +{ + Q_OBJECT +public: + explicit KCursorSaverTest(QObject *parent = nullptr); + ~KCursorSaverTest() = default; +private Q_SLOTS: + void ignoreWarning(); +}; + +#endif // KCURSORSAVERTEST_H diff --git a/autotests/keysequencerecordertest.cpp b/autotests/keysequencerecordertest.cpp new file mode 100644 index 0000000..a425f30 --- /dev/null +++ b/autotests/keysequencerecordertest.cpp @@ -0,0 +1,169 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#include "keysequencerecordertest.h" + +#include + +#include +#include +#include + +QTEST_MAIN(KeySequenceRecorderTest) + +void KeySequenceRecorderTest::initTestCase() +{ + m_window = new QWindow; +} + +void KeySequenceRecorderTest::cleanupTestCase() +{ + delete m_window; +} + +void KeySequenceRecorderTest::testValidWindow() +{ + KeySequenceRecorder recorder(nullptr); + QSignalSpy recordingSpy(&recorder, &KeySequenceRecorder::recordingChanged); + + recorder.startRecording(); + QCOMPARE(recordingSpy.count(), 0); + QVERIFY(!recorder.isRecording()); + + recorder.setWindow(m_window); + recorder.startRecording(); + QCOMPARE(recordingSpy.count(), 1); + QVERIFY(recorder.isRecording()); +} + +void KeySequenceRecorderTest::testRecording() +{ + KeySequenceRecorder recorder(m_window); + QSignalSpy recordingSpy(&recorder, &KeySequenceRecorder::recordingChanged); + QSignalSpy sequenceSpy(&recorder, &KeySequenceRecorder::currentKeySequenceChanged); + QSignalSpy resultSpy(&recorder, &KeySequenceRecorder::gotKeySequence); + + recorder.startRecording(); + QVERIFY(recorder.isRecording()); + QCOMPARE(recordingSpy.count(), 1); + QCOMPARE(sequenceSpy.count(), 1); + + QTest::keyClick(m_window, Qt::Key_A, Qt::ControlModifier); + QVERIFY(recorder.isRecording()); + QCOMPARE(recordingSpy.count(), 1); + QCOMPARE(sequenceSpy.count(), 4); // two key events + modifier release + QCOMPARE(resultSpy.count(), 0); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::Key_A | Qt::ControlModifier)); + + QTest::qWait(800); + QCOMPARE(sequenceSpy.count(), 4); + QCOMPARE(recordingSpy.count(), 2); + QVERIFY(!recorder.isRecording()); + QCOMPARE(resultSpy.count(), 1); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::Key_A | Qt::ControlModifier)); + QCOMPARE(resultSpy.takeFirst().at(0).value(), QKeySequence(Qt::Key_A | Qt::ControlModifier)); +} + +void KeySequenceRecorderTest::testModifiers() +{ + KeySequenceRecorder recorder(m_window); + QSignalSpy recordingSpy(&recorder, &KeySequenceRecorder::recordingChanged); + QSignalSpy sequenceSpy(&recorder, &KeySequenceRecorder::currentKeySequenceChanged); + QSignalSpy resultSpy(&recorder, &KeySequenceRecorder::gotKeySequence); + + recorder.startRecording(); + QCOMPARE(sequenceSpy.count(), 1); + QCOMPARE(recordingSpy.count(), 1); + QVERIFY(recorder.isRecording()); + + QTest::keyPress(m_window, Qt::Key_Control); + QCOMPARE(sequenceSpy.count(), 2); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::ControlModifier)); + + // Releasing a different key because when releasing Key_Meta, QTest sends a KeyRelease event + // with MetaModifier even though modifieres should be empty + QTest::keyRelease(m_window, Qt::Key_A, Qt::NoModifier); + QCOMPARE(sequenceSpy.count(), 3); + QCOMPARE(recorder.currentKeySequence(), QKeySequence()); + QCOMPARE(recordingSpy.count(), 1); + QVERIFY(recorder.isRecording()); + + QTest::qWait(800); + QVERIFY(recorder.isRecording()); + + QTest::keyPress(m_window, Qt::Key_Control); + QCOMPARE(sequenceSpy.count(), 4); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::ControlModifier)); + + QTest::keyPress(m_window, Qt::Key_Alt, Qt::ControlModifier); + QCOMPARE(sequenceSpy.count(), 6); // QTest sends two key events, one for each modifier + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::AltModifier | Qt::ControlModifier)); + + QCOMPARE(resultSpy.count(), 0); //modifierless not allowed +} + +void KeySequenceRecorderTest::testModifierless() +{ + KeySequenceRecorder recorder(m_window); + QSignalSpy resultSpy(&recorder, &KeySequenceRecorder::gotKeySequence); + QSignalSpy sequenceSpy(&recorder, &KeySequenceRecorder::currentKeySequenceChanged); + + recorder.startRecording(); + QVERIFY(recorder.isRecording()); + QCOMPARE(sequenceSpy.count(), 1); + + recorder.setModifierlessAllowed(false); + QTest::keyPress(m_window, Qt::Key_A); + QTest::qWait(800); + QVERIFY(recorder.isRecording()); + QCOMPARE(sequenceSpy.count(), 1); + QCOMPARE(resultSpy.count(), 0); + QCOMPARE(recorder.currentKeySequence(), QKeySequence()); + + recorder.setModifierlessAllowed(true); + QTest::keyPress(m_window, Qt::Key_A); + QTest::qWait(800); + QVERIFY(!recorder.isRecording()); + QCOMPARE(sequenceSpy.count(), 2); + QCOMPARE(resultSpy.count(), 1); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::Key_A)); +} + +void KeySequenceRecorderTest::testMultiKeyAllowed() +{ + KeySequenceRecorder recorder(m_window); + QSignalSpy recordingSpy(&recorder, &KeySequenceRecorder::recordingChanged); + QSignalSpy resultSpy(&recorder, &KeySequenceRecorder::gotKeySequence); + + recorder.startRecording(); + + recorder.setMultiKeyShortcutsAllowed(true); + int keys[4] = {0}; + for (int i = 0; i < 4; ++i) { + QVERIFY(recorder.isRecording()); + QCOMPARE(recordingSpy.count(), 1); + QCOMPARE(resultSpy.count(), 0); + keys[i] = Qt::Key_A | Qt::ControlModifier; + QKeySequence result(keys[0], keys[1], keys[2], keys[3]); + QTest::keyPress(m_window, Qt::Key_A, Qt::ControlModifier); + QTest::keyRelease(m_window, Qt::Key_A, Qt::ControlModifier); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(keys[0], keys[1], keys[2], keys[3])); + } + QVERIFY(!recorder.isRecording()); + QCOMPARE(recordingSpy.count(), 2); + QCOMPARE(resultSpy.count(), 1); + QCOMPARE(resultSpy.takeFirst().at(0).value(), QKeySequence(keys[0], keys[1], keys[2], keys[3])); + + recorder.setMultiKeyShortcutsAllowed(false); + recorder.startRecording(); + QVERIFY(recorder.isRecording()); + QCOMPARE(recordingSpy.count(), 3); + QTest::keyPress(m_window, Qt::Key_A, Qt::ControlModifier); + QCOMPARE(recorder.currentKeySequence(), QKeySequence(Qt::Key_A | Qt::ControlModifier)); + QVERIFY(!recorder.isRecording()); + QCOMPARE(recordingSpy.count(), 4); + QCOMPARE(resultSpy.count(), 1); + QCOMPARE(resultSpy.takeAt(0).at(0).value(), QKeySequence(Qt::Key_A | Qt::ControlModifier)); +} diff --git a/autotests/keysequencerecordertest.h b/autotests/keysequencerecordertest.h new file mode 100644 index 0000000..79f6d69 --- /dev/null +++ b/autotests/keysequencerecordertest.h @@ -0,0 +1,27 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#ifndef KEYSEQUENCERECORDERTEST_H +#define KEYSEQUENCERECORDERTEST_H + +#include + +class QWindow; + +class KeySequenceRecorderTest : public QObject { + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void testValidWindow(); + void testRecording(); + void testModifiers(); + void testModifierless(); + void testMultiKeyAllowed(); +private: + QWindow *m_window; +}; + +#endif diff --git a/autotests/kiconutilstest.cpp b/autotests/kiconutilstest.cpp new file mode 100644 index 0000000..63e04e3 --- /dev/null +++ b/autotests/kiconutilstest.cpp @@ -0,0 +1,190 @@ +/* + SPDX-FileCopyrightText: 2013 Martin Klapetek + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + */ + +#include "kiconutilstest.h" +#include + +#include +#include +#include +#include + +QTEST_MAIN(KIconUtilsTest) + +void KIconUtilsTest::addOverlayTest() +{ + QPixmap rectanglePixmap(32, 32); + rectanglePixmap.fill(Qt::red); + + QIcon icon(rectanglePixmap); + + QPixmap overlay(32, 32); + overlay.fill(Qt::blue); + + QIcon overlayIcon(overlay); + + QIcon iconWithOverlay = KIconUtils::addOverlay(icon, overlayIcon, Qt::BottomRightCorner); + QImage result = iconWithOverlay.pixmap(32, 32).toImage(); + + int bluePixels = 0; + int redPixels = 0; + + // Go over the image and count red and blue pixels + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + if (qRed(result.pixel(x, y)) == 255) { + redPixels++; + } else if (qBlue(result.pixel(x, y)) == 255) { + bluePixels++; + } + } + } + + // For icon of size 32x32, the overlay should be 16x16 (=256) + QCOMPARE(bluePixels, 256); + QCOMPARE(redPixels, 768); + + // Try different size and position + rectanglePixmap = rectanglePixmap.scaled(96, 96); + + icon = QIcon(rectanglePixmap); + + overlay = overlay.scaled(96, 96); + + overlayIcon = QIcon(overlay); + + iconWithOverlay = KIconUtils::addOverlay(icon, overlayIcon, Qt::BottomRightCorner); + + // Test if unsetting the overlay works; + // the result should have just one blue square + iconWithOverlay = KIconUtils::addOverlay(icon, QIcon(), Qt::BottomRightCorner); + + iconWithOverlay = KIconUtils::addOverlay(icon, overlayIcon, Qt::TopLeftCorner); + result = iconWithOverlay.pixmap(96, 96).toImage(); + + bluePixels = 0; + redPixels = 0; + + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + if (qRed(result.pixel(x, y)) == 255) { + redPixels++; + } else if (qBlue(result.pixel(x, y)) == 255) { + bluePixels++; + } + } + } + + // 96x96 big icon will have 32x32 big overlay (=1024 blue pixels) + QCOMPARE(bluePixels, 1024); + QCOMPARE(redPixels, 8192); + + // Try paint method + icon = QIcon(rectanglePixmap); + iconWithOverlay = KIconUtils::addOverlay(icon, overlayIcon, Qt::TopLeftCorner); + + QPixmap a(96, 96); + QPainter p(&a); + iconWithOverlay.paint(&p, a.rect(), Qt::AlignCenter, QIcon::Normal, QIcon::Off); + + result = a.toImage(); + + bluePixels = 0; + redPixels = 0; + + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + if (qRed(result.pixel(x, y)) == 255) { + redPixels++; + } else if (qBlue(result.pixel(x, y)) == 255) { + bluePixels++; + } + } + } + + // 96x96 big icon will have 32x32 big overlay (=1024 blue pixels) + QCOMPARE(bluePixels, 1024); + QCOMPARE(redPixels, 8192); +} + +void KIconUtilsTest::addOverlaysTest() +{ + QPixmap rectanglePixmap(32, 32); + rectanglePixmap.fill(Qt::red); + + QIcon icon(rectanglePixmap); + + QPixmap overlay(32, 32); + overlay.fill(Qt::blue); + + QIcon overlayIcon(overlay); + + QHash overlays; + overlays.insert(Qt::BottomRightCorner, overlayIcon); + overlays.insert(Qt::TopLeftCorner, overlayIcon); + + QIcon iconWithOverlay = KIconUtils::addOverlays(icon, overlays); + QImage result = iconWithOverlay.pixmap(32, 32).toImage(); + + int bluePixels = 0; + int redPixels = 0; + + // Go over the image and count red and blue pixels + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + if (qRed(result.pixel(x, y)) == 255) { + redPixels++; + } else if (qBlue(result.pixel(x, y)) == 255) { + bluePixels++; + } + } + } + + // Two blue overlays in icon size 32x32 would intersect with 16 pixels, + // so the amount of blue pixels should be 2x256-16 = 496 + QCOMPARE(bluePixels, 496); + QCOMPARE(redPixels, 528); + + // Try different size + + rectanglePixmap = rectanglePixmap.scaled(96, 96); + icon = QIcon(rectanglePixmap); + overlay = overlay.scaled(96, 96); + overlayIcon = QIcon(overlay); + + // Clear the old sizes first + overlays.clear(); + overlays.insert(Qt::BottomRightCorner, overlayIcon); + overlays.insert(Qt::TopRightCorner, overlayIcon); + overlays.insert(Qt::TopLeftCorner, overlayIcon); + + // Now it will have 3 overlays + iconWithOverlay = KIconUtils::addOverlays(icon, overlays); + + QPixmap a(96, 96); + QPainter p(&a); + iconWithOverlay.paint(&p, a.rect(), Qt::AlignCenter, QIcon::Normal, QIcon::Off); + + result = a.toImage(); + + bluePixels = 0; + redPixels = 0; + + for (int y = 0; y < result.height(); ++y) { + for (int x = 0; x < result.width(); ++x) { + if (qRed(result.pixel(x, y)) == 255) { + redPixels++; + } else if (qBlue(result.pixel(x, y)) == 255) { + bluePixels++; + } + } + } + + // 96x96 big icon will have 32x32 big overlays (=3072 blue pixels) + QCOMPARE(bluePixels, 3072); + QCOMPARE(redPixels, 6144); +} + diff --git a/autotests/kiconutilstest.h b/autotests/kiconutilstest.h new file mode 100644 index 0000000..f7c7aef --- /dev/null +++ b/autotests/kiconutilstest.h @@ -0,0 +1,20 @@ +/* + SPDX-FileCopyrightText: 2013 Martin Klapetek + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + */ + +#ifndef KICONUTILSTEST_H +#define KICONUTILSTEST_H + +#include + +class KIconUtilsTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void addOverlayTest(); + void addOverlaysTest(); +}; + +#endif // KICONUTILSTEST_H diff --git a/autotests/kwordwraptest.cpp b/autotests/kwordwraptest.cpp new file mode 100644 index 0000000..ae2f60e --- /dev/null +++ b/autotests/kwordwraptest.cpp @@ -0,0 +1,61 @@ +/* + SPDX-FileCopyrightText: 2003, 2008 David Faure + + SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include + +#include + +#include +#include "kwordwrap.h" + +class KWordWrap_UnitTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase() + { + // Qt5 TODO: how to set the dpi? + // Only found readonly QScreen::logicalDotsPerInch... +#if 0 + QX11Info::setAppDpiX(0, 96); + QX11Info::setAppDpiY(0, 96); +#endif + } + + void oldTruncationTest() + { + QFont font(QStringLiteral("helvetica"), 12); // let's hope we all have the same... + QFontMetrics fm(font); + QRect r(0, 0, 100, -1); + QString str = QStringLiteral("test wadabada [/foo/bar/waba] and some more text here"); + KWordWrap ww = KWordWrap::formatText(fm, r, 0, str); + //qDebug() << str << " => " << ww.truncatedString(); + QVERIFY(ww.truncatedString().endsWith("...")); + + str = QStringLiteral("

"); + for (; r.width() > 0; r.setWidth(r.width() - 10)) { + ww = KWordWrap::formatText(fm, r, 0, str); + //qDebug() << str << " => " << ww.truncatedString(); + QVERIFY(ww.truncatedString().endsWith("...")); + } + } + + void testWithExistingNewlines() // when the input string has \n already + { + QRect r(0, 0, 1000, -1); // very wide + QFont font(QStringLiteral("helvetica"), 12); // let's hope we all have the same... + QFontMetrics fm(font); + QString inputString = QStringLiteral("The title here\nFoo (bar)\nFoo2 (bar2)"); + KWordWrap ww = KWordWrap::formatText(fm, r, 0, inputString); + QString str = ww.wrappedString(); + QCOMPARE(str, inputString); + } +}; + +QTEST_MAIN(KWordWrap_UnitTest) + +#include "kwordwraptest.moc" diff --git a/autotests/pythontest.py b/autotests/pythontest.py new file mode 100644 index 0000000..b0a3ca8 --- /dev/null +++ b/autotests/pythontest.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python +#-*- coding: utf-8 -*- + +import sys + +sys.path.append(sys.argv[1]) + +from PyQt5 import QtCore +from PyQt5 import QtGui + +from PyKF5 import KGuiAddons + +def main(): + app = QtGui.QGuiApplication(sys.argv) + + colorCollection = KGuiAddons.KColorCollection() + + colorCollection.addColor(QtCore.Qt.blue) + colorCollection.addColor(QtCore.Qt.red) + + assert(colorCollection.count() == 2) + + painter = QtGui.QPainter() + p = QtCore.QSizeF() + foo = KGuiAddons.KFontUtils.adaptFontSize(painter, "foo", p) + +if __name__ == '__main__': + main() diff --git a/metainfo.yaml b/metainfo.yaml new file mode 100644 index 0000000..320f7d3 --- /dev/null +++ b/metainfo.yaml @@ -0,0 +1,21 @@ +maintainer: +description: Addons to QtGui +tier: 1 +type: functional +platforms: + - name: Linux + - name: FreeBSD + - name: Windows + - name: MacOSX + - name: Android +portingAid: false +deprecated: false +release: true +libraries: + - qmake: KGuiAddons + cmake: "KF5::GuiAddons" +cmakename: KF5GuiAddons + +public_lib: true +group: Frameworks +subgroup: Tier 1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..9d32bb4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,183 @@ + +set(kguiaddons_LIB_SRCS + colors/kcolorspaces.cpp + colors/kcolorutils.cpp + colors/kcolorcollection.cpp + colors/kcolormimedata.cpp + text/kdatevalidator.cpp + text/kwordwrap.cpp + fonts/kfontutils.cpp + util/kiconutils.cpp + util/klocalimagecacheimpl.cpp + util/kmodifierkeyinfo.cpp + util/kmodifierkeyinfoprovider.cpp + util/urlhandler.cpp + util/kcursorsaver.cpp + recorder/keyboardgrabber.cpp + recorder/keysequencerecorder.cpp +) + + +set (kguiaddons_LIB_SRCS ${kguiaddons_LIB_SRCS} util/kmodifierkeyinfoprovider.cpp) + +ecm_qt_declare_logging_category(kguiaddons_LIB_SRCS + HEADER kguiaddons_debug.h + IDENTIFIER KGUIADDONS_LOG + CATEGORY_NAME kf.guiaddons + DESCRIPTION "KGuiAddons" + EXPORT KGUIADDONS +) + +add_library(KF5GuiAddons ${kguiaddons_LIB_SRCS}) + +if(WITH_WAYLAND) + ecm_add_qtwayland_client_protocol(wayland_SRCS + PROTOCOL recorder/keyboard-shortcuts-inhibit-unstable-v1.xml + BASENAME keyboard-shortcuts-inhibit-unstable-v1 + ) + target_sources(KF5GuiAddons PRIVATE recorder/waylandinhibition.cpp ${wayland_SRCS}) + target_compile_definitions(KF5GuiAddons PRIVATE WITH_WAYLAND) + target_link_libraries(KF5GuiAddons PRIVATE Qt5::GuiPrivate Qt5::WaylandClient Wayland::Client) +endif() + +generate_export_header(KF5GuiAddons BASE_NAME KGuiAddons) +add_library(KF5::GuiAddons ALIAS KF5GuiAddons) + +set(kguiaddons_INCLUDES + ${CMAKE_CURRENT_SOURCE_DIR}/colors + ${CMAKE_CURRENT_SOURCE_DIR}/fonts + ${CMAKE_CURRENT_SOURCE_DIR}/text + ${CMAKE_CURRENT_SOURCE_DIR}/util + ${CMAKE_CURRENT_SOURCE_DIR}/recorder + ) +target_include_directories(KF5GuiAddons PUBLIC "$") +target_include_directories(KF5GuiAddons INTERFACE "$" ) +target_compile_definitions(KF5GuiAddons INTERFACE "$") +target_link_libraries(KF5GuiAddons PUBLIC Qt5::Gui) + +set(WITH_XCB) +if (NOT APPLE AND X11_FOUND AND X11_Xkb_FOUND AND XCB_XCB_FOUND) + find_package(Qt5X11Extras ${REQUIRED_QT_VERSION} NO_MODULE) + if (Qt5X11Extras_FOUND) + add_library(kmodifierkey_xcb MODULE util/kmodifierkeyinfoprovider_xcb.cpp) + target_include_directories (kmodifierkey_xcb PRIVATE + ${X11_Xkb_INCLUDE_PATH} + ${X11_Xlib_INCLUDE_PATH} + ${XCB_XCB_INCLUDE_DIR} + ) + target_link_libraries(kmodifierkey_xcb PRIVATE ${X11_LIBRARIES} ${XCB_XCB_LIBRARY} Qt5::X11Extras KF5::GuiAddons) + install( TARGETS kmodifierkey_xcb DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kguiaddons/kmodifierkey/) + endif() +endif () + +set_target_properties(KF5GuiAddons PROPERTIES VERSION ${KGUIADDONS_VERSION_STRING} + SOVERSION ${KGUIADDONS_SOVERSION} + EXPORT_NAME GuiAddons +) + +ecm_generate_headers(KGuiAddons_HEADERS + HEADER_NAMES + KColorUtils + KColorCollection + KColorMimeData + + RELATIVE colors + REQUIRED_HEADERS KGuiAddons_HEADERS +) +ecm_generate_headers(KGuiAddons_HEADERS + HEADER_NAMES + KDateValidator + KWordWrap + + RELATIVE text + REQUIRED_HEADERS KGuiAddons_HEADERS +) +ecm_generate_headers(KGuiAddons_HEADERS + HEADER_NAMES + KFontUtils + + RELATIVE fonts + REQUIRED_HEADERS KGuiAddons_HEADERS +) +ecm_generate_headers(KGuiAddons_HEADERS + HEADER_NAMES + KIconUtils + KImageCache + KModifierKeyInfo + KCursorSaver + + RELATIVE util + REQUIRED_HEADERS KGuiAddons_HEADERS +) + +ecm_generate_headers(KGuiAddons_HEADERS + HEADER_NAMES + KeySequenceRecorder + + RELATIVE recorder + REQUIRED_HEADERS KGuiAddons_HEADERS +) + +find_package(PythonModuleGeneration) + +if (PythonModuleGeneration_FOUND) + ecm_generate_python_binding( + TARGET KF5::GuiAddons + PYTHONNAMESPACE PyKF5 + MODULENAME KGuiAddons + SIP_DEPENDS + QtGui/QtGuimod.sip + HEADERS + colors/kcolorutils.h + colors/kcolorcollection.h + colors/kcolormimedata.h + text/kdatevalidator.h + text/kwordwrap.h + fonts/kfontutils.h + util/kiconutils.h + util/kimagecache.h + util/kmodifierkeyinfo.h + ) +endif() + +install(TARGETS KF5GuiAddons EXPORT KF5GuiAddonsTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kguiaddons_export.h + util/kmodifierkeyinfoprovider_p.h + util/klocalimagecacheimpl.h # implementation detail, no forwarding header + ${KGuiAddons_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KGuiAddons COMPONENT Devel +) + +if(BUILD_QCH) + ecm_add_qch( + KF5GuiAddons_QCH + NAME KGuiAddons + BASE_NAME KF5GuiAddons + VERSION ${KF5_VERSION} + ORG_DOMAIN org.kde + SOURCES # using only public headers, to cover only public API + ${KGuiAddons_HEADERS} + MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" + LINK_QCHS + Qt5Gui_QCH + INCLUDE_DIRS + ${CMAKE_CURRENT_BINARY_DIR} + ${kguiaddons_INCLUDES} + BLANK_MACROS + KGUIADDONS_EXPORT + TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} + QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} + COMPONENT Devel + ) +endif() + +include(ECMGeneratePriFile) +ecm_generate_pri_file(BASE_NAME KGuiAddons LIB_NAME KF5GuiAddons DEPS "gui" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KGuiAddons) +install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) + +ecm_qt_install_logging_categories( + EXPORT KGUIADDONS + FILE kguiaddons.categories + DESTINATION "${KDE_INSTALL_LOGGINGCATEGORIESDIR}" +) diff --git a/src/colors/kcolorcollection.cpp b/src/colors/kcolorcollection.cpp new file mode 100644 index 0000000..ebc34e0 --- /dev/null +++ b/src/colors/kcolorcollection.cpp @@ -0,0 +1,272 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 1999 Waldo Bastian + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +// KDE color collection + +#include "kcolorcollection.h" + +#include +#include +#include +#include +#include + +//BEGIN KColorCollectionPrivate +class KColorCollectionPrivate +{ +public: + KColorCollectionPrivate(const QString &); + KColorCollectionPrivate(const KColorCollectionPrivate &); + ~KColorCollectionPrivate() {} + struct ColorNode { + ColorNode(const QColor &c, const QString &n) + : color(c), name(n) {} + QColor color; + QString name; + }; + QList colorList; + + QString name; + QString desc; + KColorCollection::Editable editable; +}; + +KColorCollectionPrivate::KColorCollectionPrivate(const QString &_name) + : name(_name) +{ + if (name.isEmpty()) { + return; + } + + QString filename = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, QLatin1String("colors/") + name); + if (filename.isEmpty()) { + return; + } + + QFile paletteFile(filename); + if (!paletteFile.exists()) { + return; + } + if (!paletteFile.open(QIODevice::ReadOnly)) { + return; + } + + // Read first line + // Expected "GIMP Palette" + QString line = QString::fromLocal8Bit(paletteFile.readLine()); + if (line.contains(QLatin1String(" Palette"))) { + return; + } + + while (!paletteFile.atEnd()) { + line = QString::fromLocal8Bit(paletteFile.readLine()); + if (line[0] == QLatin1Char('#')) { + // This is a comment line + line.remove(0, 1); // Strip '#' + line = line.trimmed(); // Strip remaining white space.. + if (!line.isEmpty()) { + desc += line + QLatin1Char('\n'); // Add comment to description + } + } else { + // This is a color line, hopefully + line = line.trimmed(); + if (line.isEmpty()) { + continue; + } + int r, g, b; + int pos = 0; + if (sscanf(line.toLatin1().constData(), "%d %d %d%n", &r, &g, &b, &pos) >= 3) { + r = qBound(0, r, 255); + g = qBound(0, g, 255); + b = qBound(0, b, 255); + QString name = line.mid(pos).trimmed(); + colorList.append(ColorNode(QColor(r, g, b), name)); + } + } + } +} + +KColorCollectionPrivate::KColorCollectionPrivate(const KColorCollectionPrivate &p) + : colorList(p.colorList), name(p.name), desc(p.desc), editable(p.editable) +{ +} +//END KColorCollectionPrivate + +QStringList KColorCollection::installedCollections() +{ + const QStringList paletteDirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, + QStringLiteral("colors"), + QStandardPaths::LocateDirectory); + + QStringList paletteList; + for (const QString &dir : paletteDirs) { + paletteList += QDir(dir).entryList(QDir::Files | QDir::NoDotAndDotDot); + } + paletteList.removeDuplicates(); + + return paletteList; +} + +KColorCollection::KColorCollection(const QString &name) +{ + d = new KColorCollectionPrivate(name); +} + +KColorCollection::KColorCollection(const KColorCollection &p) +{ + d = new KColorCollectionPrivate(*p.d); +} + +KColorCollection::~KColorCollection() +{ + // Need auto-save? + delete d; +} + +bool +KColorCollection::save() +{ + QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/colors/") + + d->name; + QSaveFile sf(filename); + if (!sf.open(QIODevice::WriteOnly)) { + return false; + } + + QTextStream str(&sf); + + QString description = d->desc.trimmed(); +#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) + description = QLatin1Char('#') + description.split(QLatin1Char('\n'), QString::KeepEmptyParts).join(QLatin1String("\n#")); +#else + description = QLatin1Char('#') + description.split(QLatin1Char('\n'), Qt::KeepEmptyParts).join(QLatin1String("\n#")); +#endif + + str << QLatin1String("KDE RGB Palette\n"); + str << description << QLatin1Char('\n'); + for (const KColorCollectionPrivate::ColorNode &node : qAsConst(d->colorList)) { + int r, g, b; + node.color.getRgb(&r, &g, &b); + str << r << " " << g << " " << b << " " << node.name << "\n"; + } + + return sf.commit(); +} + +QString KColorCollection::description() const +{ + return d->desc; +} + +void KColorCollection::setDescription(const QString &desc) +{ + d->desc = desc; +} + +QString KColorCollection::name() const +{ + return d->name; +} + +void KColorCollection::setName(const QString &name) +{ + d->name = name; +} + +KColorCollection::Editable KColorCollection::editable() const +{ + return d->editable; +} + +void KColorCollection::setEditable(Editable editable) +{ + d->editable = editable; +} + +int KColorCollection::count() const +{ + return d->colorList.count(); +} + +KColorCollection & +KColorCollection::operator=(const KColorCollection &p) +{ + if (&p == this) { + return *this; + } + d->colorList = p.d->colorList; + d->name = p.d->name; + d->desc = p.d->desc; + d->editable = p.d->editable; + return *this; +} + +QColor +KColorCollection::color(int index) const +{ + if ((index < 0) || (index >= count())) { + return QColor(); + } + + return d->colorList[index].color; +} + +int +KColorCollection::findColor(const QColor &color) const +{ + for (int i = 0; i < d->colorList.size(); ++i) { + if (d->colorList[i].color == color) { + return i; + } + } + return -1; +} + +QString +KColorCollection::name(int index) const +{ + if ((index < 0) || (index >= count())) { + return QString(); + } + + return d->colorList[index].name; +} + +QString KColorCollection::name(const QColor &color) const +{ + return name(findColor(color)); +} + +int +KColorCollection::addColor(const QColor &newColor, const QString &newColorName) +{ + d->colorList.append(KColorCollectionPrivate::ColorNode(newColor, newColorName)); + return count() - 1; +} + +int +KColorCollection::changeColor(int index, + const QColor &newColor, + const QString &newColorName) +{ + if ((index < 0) || (index >= count())) { + return -1; + } + + KColorCollectionPrivate::ColorNode &node = d->colorList[index]; + node.color = newColor; + node.name = newColorName; + + return index; +} + +int KColorCollection::changeColor(const QColor &oldColor, + const QColor &newColor, + const QString &newColorName) +{ + return changeColor(findColor(oldColor), newColor, newColorName); +} + diff --git a/src/colors/kcolorcollection.h b/src/colors/kcolorcollection.h new file mode 100644 index 0000000..69e554c --- /dev/null +++ b/src/colors/kcolorcollection.h @@ -0,0 +1,195 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 1999 Waldo Bastian + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +// KDE color collection. + +#ifndef KDELIBS_KCOLORCOLLECTION_H +#define KDELIBS_KCOLORCOLLECTION_H + +#include + +#include +#include +#include + +/** + * @class KColorCollection kcolorcollection.h KColorCollection + * + * Class for handling color collections ("palettes"). + * + * This class makes it easy to handle color collections, sometimes referred to + * as "palettes". This class can read and write collections from and to a file. + * + * This class uses the "GIMP" palette file format. + * + * @author Waldo Bastian (bastian@kde.org) + */ +class KGUIADDONS_EXPORT KColorCollection +{ +public: + /** + * Query which KDE color collections are installed. + * + * @return A list with installed color collection names. + */ + static QStringList installedCollections(); + + /** + * KColorCollection constructor. Creates a KColorCollection from a file + * the filename is derived from the name. + * @param name The name of collection as returned by installedCollections() + */ + explicit KColorCollection(const QString &name = QString()); + + /** + * KColorCollection copy constructor. + */ + KColorCollection(const KColorCollection &); + + /** + * KColorCollection destructor. + */ + ~KColorCollection(); + + /** + * KColorCollection assignment operator + */ + KColorCollection &operator=(const KColorCollection &); + + /** + * Save the collection + * + * @return 'true' if successful + */ + bool save(); + + /** + * Get the description of the collection. + * @return the description of the collection. + */ + QString description() const; + + /** + * Set the description of the collection. + * @param desc the new description + */ + void setDescription(const QString &desc); + + /** + * Get the name of the collection. + * @return the name of the collection + */ + QString name() const; + + /** + * Set the name of the collection. + * @param name the name of the collection + */ + void setName(const QString &name); + + /** + * Used to specify whether a collection may be edited. + * @see editable() + * @see setEditable() + */ + enum Editable { Yes, ///< Collection may be edited + No, ///< Collection may not be edited + Ask ///< Ask user before editing + }; + + /** + * Returns whether the collection may be edited. + * @return the state of the collection + */ + Editable editable() const; + + /** + * Change whether the collection may be edited. + * @param editable the state of the collection + */ + void setEditable(Editable editable); + + /** + * Return the number of colors in the collection. + * @return the number of colors + */ + int count() const; + + /** + * Find color by index. + * @param index the index of the desired color + * @return The @p index -th color of the collection, null if not found. + */ + QColor color(int index) const; + + /** + * Find index by @p color. + * @param color the color to find + * @return The index of the color in the collection or -1 if the + * color is not found. + */ + int findColor(const QColor &color) const; + + /** + * Find color name by @p index. + * @param index the index of the color + * @return The name of the @p index -th color. + * Note that not all collections have named the colors. Null is + * returned if the color does not exist or has no name. + */ + QString name(int index) const; + + /** + * Find color name by @p color. + * @return The name of color according to this collection. + * Note that not all collections have named the colors. + * Note also that each collection can give the same color + * a different name. + */ + QString name(const QColor &color) const; + + /** + * Add a color. + * @param newColor The color to add. + * @param newColorName The name of the color, null to remove + * the name. + * @return The index of the added color. + */ + int addColor(const QColor &newColor, + const QString &newColorName = QString()); + + /** + * Change a color. + * @param index Index of the color to change + * @param newColor The new color. + * @param newColorName The new color name, null to remove + * the name. + * @return The index of the new color or -1 if the color couldn't + * be changed. + */ + int changeColor(int index, + const QColor &newColor, + const QString &newColorName = QString()); + + /** + * Change a color. + * @param oldColor The original color + * @param newColor The new color. + * @param newColorName The new color name, null to remove + * the name. + * @return The index of the new color or -1 if the color couldn't + * be changed. + */ + int changeColor(const QColor &oldColor, + const QColor &newColor, + const QString &newColorName = QString()); + +private: + class KColorCollectionPrivate *d; +}; + +#endif // KDELIBS_KCOLORCOLLECTION_H + diff --git a/src/colors/kcolormimedata.cpp b/src/colors/kcolormimedata.cpp new file mode 100644 index 0000000..303e9eb --- /dev/null +++ b/src/colors/kcolormimedata.cpp @@ -0,0 +1,65 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 1999 Steffen Hansen + SPDX-FileCopyrightText: 2005 Joseph Wenninger + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "kcolormimedata.h" + +#include +#include +#include +#include + +void +KColorMimeData::populateMimeData(QMimeData *mimeData, const QColor &color) +{ + mimeData->setColorData(color); + mimeData->setText(color.name()); +} + +bool +KColorMimeData::canDecode(const QMimeData *mimeData) +{ + if (mimeData->hasColor()) { + return true; + } + if (mimeData->hasText()) { + const QString colorName = mimeData->text(); + if ((colorName.length() >= 4) && (colorName[0] == QLatin1Char('#'))) { + return true; + } + } + return false; +} + +QColor +KColorMimeData::fromMimeData(const QMimeData *mimeData) +{ + if (mimeData->hasColor()) { + return mimeData->colorData().value(); + } + if (canDecode(mimeData)) { + return QColor(mimeData->text()); + } + return QColor(); +} + +QDrag * +KColorMimeData::createDrag(const QColor &color, QObject *dragsource) +{ + QDrag *drag = new QDrag(dragsource); + QMimeData *mime = new QMimeData; + populateMimeData(mime, color); + drag->setMimeData(mime); + QPixmap colorpix(25, 20); + colorpix.fill(color); + QPainter p(&colorpix); + p.setPen(Qt::black); + p.drawRect(0, 0, 24, 19); + p.end(); + drag->setPixmap(colorpix); + drag->setHotSpot(QPoint(-5, -7)); + return drag; +} diff --git a/src/colors/kcolormimedata.h b/src/colors/kcolormimedata.h new file mode 100644 index 0000000..bf8f053 --- /dev/null +++ b/src/colors/kcolormimedata.h @@ -0,0 +1,55 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 1999 Steffen Hansen + SPDX-FileCopyrightText: 2005 Joseph Wenninger + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef _KCOLORMIMEDATA_H +#define _KCOLORMIMEDATA_H + +#include + +class QColor; +class QDrag; +class QMimeData; +class QObject; + +/** + * Drag-and-drop and clipboard mimedata manipulation for QColor objects. The according MIME type + * is set to application/x-color. + * + * See the Qt drag'n'drop documentation. + */ +namespace KColorMimeData +{ +/** + * Sets the color and text representation fields for the specified color in the mimedata object: + * application/x-color and text/plain types are set + */ +KGUIADDONS_EXPORT void populateMimeData(QMimeData *mimeData, const QColor &color); + +/** + * Returns true if the MIME data @p mimeData contains a color object. + * First checks for application/x-color and if that fails, for a text/plain entry, which + * represents a color in the format \#hexnumbers + */ +KGUIADDONS_EXPORT bool canDecode(const QMimeData *mimeData); + +/** + * Decodes the MIME data @p mimeData and returns the resulting color. + * First tries application/x-color and if that fails, a text/plain entry, which + * represents a color in the format \#hexnumbers. If this fails too, + * an invalid QColor object is returned, use QColor::isValid() to test it. + */ +KGUIADDONS_EXPORT QColor fromMimeData(const QMimeData *mimeData); + +/** + * Creates a color drag object. Either you have to start this drag or delete it + * The drag object's mime data has the application/x-color and text/plain type set and a pixmap + * filled with the specified color, which is going to be displayed next to the mouse cursor + */ +KGUIADDONS_EXPORT QDrag *createDrag(const QColor &color, QObject *dragsource); +} + +#endif // _KCOLORMIMEDATA_H diff --git a/src/colors/kcolorspaces.cpp b/src/colors/kcolorspaces.cpp new file mode 100644 index 0000000..f869092 --- /dev/null +++ b/src/colors/kcolorspaces.cpp @@ -0,0 +1,162 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2007 Matthew Woehlke + SPDX-FileCopyrightText: 2007 Olaf Schmidt + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ +#include "kcolorspaces_p.h" +#include "kguiaddons_colorhelpers_p.h" + +#include + +#include + +using namespace KColorSpaces; + +static inline qreal wrap(qreal a, qreal d = 1.0) +{ + qreal r = fmod(a, d); + return (r < 0.0 ? d + r : (r > 0.0 ? r : 0.0)); +} + +/////////////////////////////////////////////////////////////////////////////// +// HCY color space + +#define HCY_REC 709 // use 709 for now +#if HCY_REC == 601 +static const qreal yc[3] = { 0.299, 0.587, 0.114 }; +#elif HCY_REC == 709 +static const qreal yc[3] = {0.2126, 0.7152, 0.0722}; +#else // use Qt values +static const qreal yc[3] = { 0.34375, 0.5, 0.15625 }; +#endif + +qreal KHCY::gamma(qreal n) +{ + return pow(normalize(n), 2.2); +} + +qreal KHCY::igamma(qreal n) +{ + return pow(normalize(n), 1.0 / 2.2); +} + +qreal KHCY::lumag(qreal r, qreal g, qreal b) +{ + return r * yc[0] + g * yc[1] + b * yc[2]; +} + +KHCY::KHCY(qreal h_, qreal c_, qreal y_, qreal a_) +{ + h = h_; + c = c_; + y = y_; + a = a_; +} + +KHCY::KHCY(const QColor &color) +{ + qreal r = gamma(color.redF()); + qreal g = gamma(color.greenF()); + qreal b = gamma(color.blueF()); + a = color.alphaF(); + + // luma component + y = lumag(r, g, b); + + // hue component + qreal p = qMax(qMax(r, g), b); + qreal n = qMin(qMin(r, g), b); + qreal d = 6.0 * (p - n); + if (n == p) { + h = 0.0; + } else if (r == p) { + h = ((g - b) / d); + } else if (g == p) { + h = ((b - r) / d) + (1.0 / 3.0); + } else { + h = ((r - g) / d) + (2.0 / 3.0); + } + + // chroma component + if (r == g && g == b) { + c = 0.0; + } else { + c = qMax((y - n) / y, (p - y) / (1 - y)); + } +} + +QColor KHCY::qColor() const +{ + // start with sane component values + qreal _h = wrap(h); + qreal _c = normalize(c); + qreal _y = normalize(y); + + // calculate some needed variables + qreal _hs = _h * 6.0, th, tm; + if (_hs < 1.0) { + th = _hs; + tm = yc[0] + yc[1] * th; + } else if (_hs < 2.0) { + th = 2.0 - _hs; + tm = yc[1] + yc[0] * th; + } else if (_hs < 3.0) { + th = _hs - 2.0; + tm = yc[1] + yc[2] * th; + } else if (_hs < 4.0) { + th = 4.0 - _hs; + tm = yc[2] + yc[1] * th; + } else if (_hs < 5.0) { + th = _hs - 4.0; + tm = yc[2] + yc[0] * th; + } else { + th = 6.0 - _hs; + tm = yc[0] + yc[2] * th; + } + + // calculate RGB channels in sorted order + qreal tn, to, tp; + if (tm >= _y) { + tp = _y + _y * _c * (1.0 - tm) / tm; + to = _y + _y * _c * (th - tm) / tm; + tn = _y - (_y * _c); + } else { + tp = _y + (1.0 - _y) * _c; + to = _y + (1.0 - _y) * _c * (th - tm) / (1.0 - tm); + tn = _y - (1.0 - _y) * _c * tm / (1.0 - tm); + } + + // return RGB channels in appropriate order + if (_hs < 1.0) { + return QColor::fromRgbF(igamma(tp), igamma(to), igamma(tn), a); + } else if (_hs < 2.0) { + return QColor::fromRgbF(igamma(to), igamma(tp), igamma(tn), a); + } else if (_hs < 3.0) { + return QColor::fromRgbF(igamma(tn), igamma(tp), igamma(to), a); + } else if (_hs < 4.0) { + return QColor::fromRgbF(igamma(tn), igamma(to), igamma(tp), a); + } else if (_hs < 5.0) { + return QColor::fromRgbF(igamma(to), igamma(tn), igamma(tp), a); + } else { + return QColor::fromRgbF(igamma(tp), igamma(tn), igamma(to), a); + } +} + +qreal KHCY::hue(const QColor &color) +{ + return wrap(KHCY(color).h); +} + +qreal KHCY::chroma(const QColor &color) +{ + return KHCY(color).c; +} + +qreal KHCY::luma(const QColor &color) +{ + return lumag(gamma(color.redF()), + gamma(color.greenF()), + gamma(color.blueF())); +} + diff --git a/src/colors/kcolorspaces_p.h b/src/colors/kcolorspaces_p.h new file mode 100644 index 0000000..afe1c0d --- /dev/null +++ b/src/colors/kcolorspaces_p.h @@ -0,0 +1,39 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2007 Matthew Woehlke + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +/* + * If you use KColorSpaces in your own KDE code, please drop me a line at + * mw_triad@users.sourceforge.net, as I would like to track if people find it + * useful. Thanks! + */ + +#ifndef KCOLORSPACES_H +#define KCOLORSPACES_H + +#include + +namespace KColorSpaces +{ + +class KHCY +{ +public: + explicit KHCY(const QColor &); + explicit KHCY(qreal h_, qreal c_, qreal y_, qreal a_ = 1.0); + QColor qColor() const; + qreal h, c, y, a; + static qreal hue(const QColor &); + static qreal chroma(const QColor &); + static qreal luma(const QColor &); +private: + static qreal gamma(qreal); + static qreal igamma(qreal); + static qreal lumag(qreal, qreal, qreal); +}; + +} + +#endif diff --git a/src/colors/kcolorutils.cpp b/src/colors/kcolorutils.cpp new file mode 100644 index 0000000..7ea4247 --- /dev/null +++ b/src/colors/kcolorutils.cpp @@ -0,0 +1,169 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2007 Matthew Woehlke + SPDX-FileCopyrightText: 2007 Thomas Zander + SPDX-FileCopyrightText: 2007 Zack Rusin + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ +#include +#include "kcolorspaces_p.h" +#include "kguiaddons_colorhelpers_p.h" + +#include +#include +#include // qIsNaN + +#include + +// BEGIN internal helper functions +static inline qreal mixQreal(qreal a, qreal b, qreal bias) +{ + return a + (b - a) * bias; +} +// END internal helper functions + +qreal KColorUtils::hue(const QColor &color) +{ + return KColorSpaces::KHCY::hue(color); +} + +qreal KColorUtils::chroma(const QColor &color) +{ + return KColorSpaces::KHCY::chroma(color); +} + +qreal KColorUtils::luma(const QColor &color) +{ + return KColorSpaces::KHCY::luma(color); +} + +void KColorUtils::getHcy(const QColor &color, qreal *h, qreal *c, qreal *y, qreal *a) +{ + if (!c || !h || !y) { + return; + } + KColorSpaces::KHCY khcy(color); + *c = khcy.c; + *h = khcy.h + (khcy.h < 0.0 ? 1.0 : 0.0); + *y = khcy.y; + if (a) { + *a = khcy.a; + } +} + +QColor KColorUtils::hcyColor(qreal h, qreal c, qreal y, qreal a) +{ + return KColorSpaces::KHCY(h, c, y, a).qColor(); +} + +static qreal contrastRatioForLuma(qreal y1, qreal y2) +{ + if (y1 > y2) { + return (y1 + 0.05) / (y2 + 0.05); + } else { + return (y2 + 0.05) / (y1 + 0.05); + } +} + +qreal KColorUtils::contrastRatio(const QColor &c1, const QColor &c2) +{ + return contrastRatioForLuma(luma(c1), luma(c2)); +} + +QColor KColorUtils::lighten(const QColor &color, qreal ky, qreal kc) +{ + KColorSpaces::KHCY c(color); + c.y = 1.0 - normalize((1.0 - c.y) * (1.0 - ky)); + c.c = 1.0 - normalize((1.0 - c.c) * kc); + return c.qColor(); +} + +QColor KColorUtils::darken(const QColor &color, qreal ky, qreal kc) +{ + KColorSpaces::KHCY c(color); + c.y = normalize(c.y * (1.0 - ky)); + c.c = normalize(c.c * kc); + return c.qColor(); +} + +QColor KColorUtils::shade(const QColor &color, qreal ky, qreal kc) +{ + KColorSpaces::KHCY c(color); + c.y = normalize(c.y + ky); + c.c = normalize(c.c + kc); + return c.qColor(); +} + +static QColor tintHelper(const QColor &base, qreal baseLuma, const QColor &color, qreal amount) +{ + KColorSpaces::KHCY result(KColorUtils::mix(base, color, pow(amount, 0.3))); + result.y = mixQreal(baseLuma, result.y, amount); + + return result.qColor(); +} + +QColor KColorUtils::tint(const QColor &base, const QColor &color, qreal amount) +{ + if (amount <= 0.0) { + return base; + } + if (amount >= 1.0) { + return color; + } + if (qIsNaN(amount)) { + return base; + } + + qreal baseLuma = luma(base); //cache value because luma call is expensive + double ri = contrastRatioForLuma(baseLuma, luma(color)); + double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); + double u = 1.0, l = 0.0; + QColor result; + for (int i = 12; i; --i) { + double a = 0.5 * (l + u); + result = tintHelper(base, baseLuma, color, a); + double ra = contrastRatioForLuma(baseLuma, luma(result)); + if (ra > rg) { + u = a; + } else { + l = a; + } + } + return result; +} + +QColor KColorUtils::mix(const QColor &c1, const QColor &c2, qreal bias) +{ + if (bias <= 0.0) { + return c1; + } + if (bias >= 1.0) { + return c2; + } + if (qIsNaN(bias)) { + return c1; + } + + qreal r = mixQreal(c1.redF(), c2.redF(), bias); + qreal g = mixQreal(c1.greenF(), c2.greenF(), bias); + qreal b = mixQreal(c1.blueF(), c2.blueF(), bias); + qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); + + return QColor::fromRgbF(r, g, b, a); +} + +QColor KColorUtils::overlayColors(const QColor &base, const QColor &paint, + QPainter::CompositionMode comp) +{ + // This isn't the fastest way, but should be "fast enough". + // It's also the only safe way to use QPainter::CompositionMode + QImage img(1, 1, QImage::Format_ARGB32_Premultiplied); + QPainter p(&img); + QColor start = base; + start.setAlpha(255); // opaque + p.fillRect(0, 0, 1, 1, start); + p.setCompositionMode(comp); + p.fillRect(0, 0, 1, 1, paint); + p.end(); + return img.pixel(0, 0); +} diff --git a/src/colors/kcolorutils.h b/src/colors/kcolorutils.h new file mode 100644 index 0000000..7fcb0e7 --- /dev/null +++ b/src/colors/kcolorutils.h @@ -0,0 +1,189 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2007 Matthew Woehlke + SPDX-FileCopyrightText: 2007 Thomas Zander + SPDX-FileCopyrightText: 2007 Zack Rusin + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KCOLORUTILS_H +#define KCOLORUTILS_H + +#include +#include + +class QColor; + +/** + * A set of methods used to work with colors. + */ +namespace KColorUtils +{ +/** + * Calculate the hue of a color. The range is from 0.0 (red) to almost 1.0 (slightly blue-ish red). + * + * The result is computed in linear (not sRGB) color space and may differ slightly from QColor::hue(). + * + * @see https://en.wikipedia.org/wiki/Hue + * @since 5.68 + */ +KGUIADDONS_EXPORT qreal hue(const QColor &); + +/** + * Calculate the chroma of a color. The range is from 0.0 (none) to 1.0 (full). + * + * The result is computed in linear (not sRGB) color space. + * + * @see https://en.wikipedia.org/wiki/Colorfulness + * @since 5.68 + */ +KGUIADDONS_EXPORT qreal chroma(const QColor &); + +/** + * Calculate the luma of a color. Luma is weighted sum of gamma-adjusted + * R'G'B' components of a color. The result is similar to qGray. The range + * is from 0.0 (black) to 1.0 (white). + * + * The result is computed in linear (not sRGB) color space. + * + * KColorUtils::darken(), KColorUtils::lighten() and KColorUtils::shade() + * operate on the luma of a color. + * + * @see http://en.wikipedia.org/wiki/Luma_(video) + */ +KGUIADDONS_EXPORT qreal luma(const QColor &); + +/** + * Calculate hue, chroma and luma of a color in one call. + * + * The range of hue is from 0.0 (red) to almost 1.0 (slightly blue-ish red). + * The range of chroma is from 0.0 (none) to 1.0 (full). + * The range of luma is from 0.0 (black) to 1.0 (white). + * + * The hue, chroma and luma values are computed in linear (not sRGB) color space. + * + * @since 5.0 + */ +KGUIADDONS_EXPORT void getHcy(const QColor &, qreal *hue, qreal *chroma, qreal *luma, qreal *alpha = nullptr); + +/** + * Return a QColor based on the given hue, chroma, luma and alpha values. + * + * The range of hue is cyclical. For example, 0.0 and 1.0 are both red while -0.166667 and 0.833333 are both magenta. + * The range of chroma is from 0.0 (none) to 1.0 (full). Out of range values will be clamped. + * The range of luma is from 0.0 (black) to 1.0 (white). Out of range values will be clamped. + * + * The hue, chroma and luma values are computed in linear (not sRGB) color space. + * + * @since 5.68 + */ +KGUIADDONS_EXPORT QColor hcyColor(qreal hue, qreal chroma, qreal luma, qreal alpha = 1.0); + +/** + * Calculate the contrast ratio between two colors, according to the + * W3C/WCAG2.0 algorithm, (Lmax + 0.05)/(Lmin + 0.05), where Lmax and Lmin + * are the luma values of the lighter color and the darker color, + * respectively. + * + * A contrast ration of 5:1 (result == 5.0) is the minimum for "normal" + * text to be considered readable (large text can go as low as 3:1). The + * ratio ranges from 1:1 (result == 1.0) to 21:1 (result == 21.0). + * + * @see KColorUtils::luma + */ +KGUIADDONS_EXPORT qreal contrastRatio(const QColor &, const QColor &); + +/** + * Adjust the luma of a color by changing its distance from white. + * + * @li amount == 1.0 gives white + * @li amount == 0.5 results in a color whose luma is halfway between 1.0 + * and that of the original color + * @li amount == 0.0 gives the original color + * @li amount == -1.0 gives a color that is 'twice as far from white' as + * the original color, that is luma(result) == 1.0 - 2*(1.0 - luma(color)) + * + * @param amount factor by which to adjust the luma component of the color + * @param chromaInverseGain (optional) factor by which to adjust the chroma + * component of the color; 1.0 means no change, 0.0 maximizes chroma + * @see KColorUtils::shade + */ +KGUIADDONS_EXPORT QColor lighten(const QColor &, qreal amount = 0.5, qreal chromaInverseGain = 1.0); + +/** + * Adjust the luma of a color by changing its distance from black. + * + * @li amount == 1.0 gives black + * @li amount == 0.5 results in a color whose luma is halfway between 0.0 + * and that of the original color + * @li amount == 0.0 gives the original color + * @li amount == -1.0 gives a color that is 'twice as far from black' as + * the original color, that is luma(result) == 2*luma(color) + * + * @param amount factor by which to adjust the luma component of the color + * @param chromaGain (optional) factor by which to adjust the chroma + * component of the color; 1.0 means no change, 0.0 minimizes chroma + * @see KColorUtils::shade + */ +KGUIADDONS_EXPORT QColor darken(const QColor &, qreal amount = 0.5, qreal chromaGain = 1.0); + +/** + * Adjust the luma and chroma components of a color. The amount is added + * to the corresponding component. + * + * @param lumaAmount amount by which to adjust the luma component of the + * color; 0.0 results in no change, -1.0 turns anything black, 1.0 turns + * anything white + * @param chromaAmount (optional) amount by which to adjust the chroma + * component of the color; 0.0 results in no change, -1.0 minimizes chroma, + * 1.0 maximizes chroma + * @see KColorUtils::luma + */ +KGUIADDONS_EXPORT QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount = 0.0); + +/** + * Create a new color by tinting one color with another. This function is + * meant for creating additional colors withings the same class (background, + * foreground) from colors in a different class. Therefore when @p amount + * is low, the luma of @p base is mostly preserved, while the hue and + * chroma of @p color is mostly inherited. + * + * @param base color to be tinted + * @param color color with which to tint + * @param amount how strongly to tint the base; 0.0 gives @p base, + * 1.0 gives @p color + */ +KGUIADDONS_EXPORT QColor tint(const QColor &base, const QColor &color, qreal amount = 0.3); + +/** + * Blend two colors into a new color by linear combination. + * @code + QColor lighter = KColorUtils::mix(myColor, Qt::white) + * @endcode + * @param c1 first color. + * @param c2 second color. + * @param bias weight to be used for the mix. @p bias <= 0 gives @p c1, + * @p bias >= 1 gives @p c2. @p bias == 0.5 gives a 50% blend of @p c1 + * and @p c2. + */ +KGUIADDONS_EXPORT QColor mix(const QColor &c1, const QColor &c2, + qreal bias = 0.5); + +/** + * Blend two colors into a new color by painting the second color over the + * first using the specified composition mode. + * @code + QColor white(Qt::white); + white.setAlphaF(0.5); + QColor lighter = KColorUtils::overlayColors(myColor, white); + @endcode + * @param base the base color (alpha channel is ignored). + * @param paint the color to be overlayed onto the base color. + * @param comp the CompositionMode used to do the blending. + */ +KGUIADDONS_EXPORT QColor overlayColors(const QColor &base, const QColor &paint, + QPainter::CompositionMode comp = QPainter::CompositionMode_SourceOver); + +} + +#endif // KCOLORUTILS_H diff --git a/src/colors/kguiaddons_colorhelpers_p.h b/src/colors/kguiaddons_colorhelpers_p.h new file mode 100644 index 0000000..c0e9110 --- /dev/null +++ b/src/colors/kguiaddons_colorhelpers_p.h @@ -0,0 +1,18 @@ +/* This file is part of the KDE project + SPDX-FileCopyrightText: 2007 Matthew Woehlke + SPDX-FileCopyrightText: 2007 Olaf Schmidt + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KGUIADDONS_COLORHELPERS_P_H +#define KGUIADDONS_COLORHELPERS_P_H + +// normalize: like qBound(a, 0.0, 1.0) but without needing the args and with +// "safer" behavior on NaN (isnan(a) -> return 0.0) +static inline qreal normalize(qreal a) +{ + return (a < 1.0 ? (a > 0.0 ? a : 0.0) : 1.0); +} + +#endif // KGUIADDONS_KCOLORHELPERS_P_H diff --git a/src/fonts/kfontutils.cpp b/src/fonts/kfontutils.cpp new file mode 100644 index 0000000..23d3fb5 --- /dev/null +++ b/src/fonts/kfontutils.cpp @@ -0,0 +1,77 @@ +/* + SPDX-FileCopyrightText: 2005, 2009, 2014 Albert Astals Cid + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "kfontutils.h" + +#include +#include + +static bool checkFits(QPainter &painter, const QString &string, qreal width, qreal height, qreal size, KFontUtils::AdaptFontSizeOptions flags) +{ + QFont f = painter.font(); + f.setPointSizeF(size); + painter.setFont(f); + int qtFlags = Qt::AlignCenter | Qt::TextWordWrap; + if (flags & KFontUtils::DoNotAllowWordWrap) { + qtFlags &= ~Qt::TextWordWrap; + } + const QRectF boundingRect = painter.boundingRect(QRectF(0, 0, width, height), qtFlags, string); + if (boundingRect.width() == 0.0 || boundingRect.height() == 0.0) { + return false; + } else if (boundingRect.width() > width || boundingRect.height() > height) { + return false; + } + return true; +} + +qreal KFontUtils::adaptFontSize(QPainter &painter, const QString &string, qreal width, qreal height, qreal maxFontSize, qreal minFontSize, AdaptFontSizeOptions flags) +{ + // A invalid range is an error (-1) + if (maxFontSize < minFontSize) + return -1; + + // If the max font size already fits, return it + if (checkFits(painter, string, width, height, maxFontSize, flags)) + return maxFontSize; + + qreal fontSizeDoesNotFit = maxFontSize; + + // If the min font size does not fit, try to see if a font size of 1 fits, + // if it does not return error (-1) + // if it does, we'll return a fontsize smaller than the minFontSize as documented + if (!checkFits(painter, string, width, height, minFontSize, flags)) { + fontSizeDoesNotFit = minFontSize; + + minFontSize = 1; + if (!checkFits(painter, string, width, height, minFontSize, flags)) + return -1; + } + + qreal fontSizeFits = minFontSize; + qreal nextFontSizeToTry = (fontSizeDoesNotFit + fontSizeFits) / 2; + + while (qFloor(fontSizeFits) != qFloor(nextFontSizeToTry)) { + if (checkFits(painter, string, width, height, nextFontSizeToTry, flags)) { + fontSizeFits = nextFontSizeToTry; + nextFontSizeToTry = (fontSizeDoesNotFit + fontSizeFits) / 2; + } else { + fontSizeDoesNotFit = nextFontSizeToTry; + nextFontSizeToTry = (nextFontSizeToTry + fontSizeFits) / 2; + } + } + + QFont f = painter.font(); + f.setPointSizeF(fontSizeFits); + painter.setFont(f); + + return fontSizeFits; +} + +qreal KFontUtils::adaptFontSize(QPainter &painter, const QString &text, const QSizeF &availableSize, qreal maxFontSize, qreal minFontSize, AdaptFontSizeOptions flags) +{ + return adaptFontSize(painter, text, availableSize.width(), availableSize.height(), maxFontSize, minFontSize, flags); +} + diff --git a/src/fonts/kfontutils.h b/src/fonts/kfontutils.h new file mode 100644 index 0000000..3e1870c --- /dev/null +++ b/src/fonts/kfontutils.h @@ -0,0 +1,74 @@ +/* + SPDX-FileCopyrightText: 2005, 2009 Albert Astals Cid + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KFONTUTILS_H +#define KFONTUTILS_H + +#include + +#include + +class QPainter; +class QSizeF; +class QString; + +/** + * @namespace KFontUtils + * Provides utility functions for font data. + */ +namespace KFontUtils +{ +/** + * Modifiers for the adaptFontSize function + * @see AdaptFontSizeOptions + */ +enum AdaptFontSizeOption { + NoFlags = 0x01, ///< No modifier + DoNotAllowWordWrap = 0x02 ///< Do not use word wrapping +}; +/** + * Stores a combination of #AdaptFontSizeOption values. + */ +Q_DECLARE_FLAGS(AdaptFontSizeOptions, AdaptFontSizeOption) +Q_DECLARE_OPERATORS_FOR_FLAGS(AdaptFontSizeOptions) + +/** Helper function that calculates the biggest font size (in points) used + drawing a centered text using word wrapping. + @param painter The painter where the text will be painted. The font set + in the painter is used for the calculation. Note the + painter font size is modified by this call + @param text The text you want to draw + @param width The available width for drawing + @param height The available height for drawing + @param maxFontSize The maximum font size (in points) to consider + @param minFontSize The minimum font size (in points) to consider + @param flags The modifiers for how the text is painted + @return The calculated biggest font size (in points) that draws the text + in the given dimensions. Can return smaller than minFontSize, + that means the text doesn't fit in the given rectangle. Can + return -1 on error + @since 4.7 +*/ +qreal KGUIADDONS_EXPORT adaptFontSize(QPainter &painter, + const QString &text, + qreal width, + qreal height, + qreal maxFontSize = 28.0, + qreal minFontSize = 1.0, + AdaptFontSizeOptions flags = NoFlags); + +/** Convenience function for adaptFontSize that accepts a QSizeF instead two qreals + @since 4.7 +*/ +qreal KGUIADDONS_EXPORT adaptFontSize(QPainter &painter, + const QString &text, + const QSizeF &availableSize, + qreal maxFontSize = 28.0, + qreal minFontSize = 1.0, + AdaptFontSizeOptions flags = NoFlags); +} + +#endif diff --git a/src/recorder/keyboard-shortcuts-inhibit-unstable-v1.xml b/src/recorder/keyboard-shortcuts-inhibit-unstable-v1.xml new file mode 100644 index 0000000..2774876 --- /dev/null +++ b/src/recorder/keyboard-shortcuts-inhibit-unstable-v1.xml @@ -0,0 +1,143 @@ + + + + + Copyright © 2017 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol specifies a way for a client to request the compositor + to ignore its own keyboard shortcuts for a given seat, so that all + key events from that seat get forwarded to a surface. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + A global interface used for inhibiting the compositor keyboard shortcuts. + + + + + Destroy the keyboard shortcuts inhibitor manager. + + + + + + Create a new keyboard shortcuts inhibitor object associated with + the given surface for the given seat. + + If shortcuts are already inhibited for the specified seat and surface, + a protocol error "already_inhibited" is raised by the compositor. + + + + + + + + + + + + + + A keyboard shortcuts inhibitor instructs the compositor to ignore + its own keyboard shortcuts when the associated surface has keyboard + focus. As a result, when the surface has keyboard focus on the given + seat, it will receive all key events originating from the specified + seat, even those which would normally be caught by the compositor for + its own shortcuts. + + The Wayland compositor is however under no obligation to disable + all of its shortcuts, and may keep some special key combo for its own + use, including but not limited to one allowing the user to forcibly + restore normal keyboard events routing in the case of an unwilling + client. The compositor may also use the same key combo to reactivate + an existing shortcut inhibitor that was previously deactivated on + user request. + + When the compositor restores its own keyboard shortcuts, an + "inactive" event is emitted to notify the client that the keyboard + shortcuts inhibitor is not effectively active for the surface and + seat any more, and the client should not expect to receive all + keyboard events. + + When the keyboard shortcuts inhibitor is inactive, the client has + no way to forcibly reactivate the keyboard shortcuts inhibitor. + + The user can chose to re-enable a previously deactivated keyboard + shortcuts inhibitor using any mechanism the compositor may offer, + in which case the compositor will send an "active" event to notify + the client. + + If the surface is destroyed, unmapped, or loses the seat's keyboard + focus, the keyboard shortcuts inhibitor becomes irrelevant and the + compositor will restore its own keyboard shortcuts but no "inactive" + event is emitted in this case. + + + + + Remove the keyboard shortcuts inhibitor from the associated wl_surface. + + + + + + This event indicates that the shortcut inhibitor is active. + + The compositor sends this event every time compositor shortcuts + are inhibited on behalf of the surface. When active, the client + may receive input events normally reserved by the compositor + (see zwp_keyboard_shortcuts_inhibitor_v1). + + This occurs typically when the initial request "inhibit_shortcuts" + first becomes active or when the user instructs the compositor to + re-enable and existing shortcuts inhibitor using any mechanism + offered by the compositor. + + + + + + This event indicates that the shortcuts inhibitor is inactive, + normal shortcuts processing is restored by the compositor. + + + + diff --git a/src/recorder/keyboardgrabber.cpp b/src/recorder/keyboardgrabber.cpp new file mode 100644 index 0000000..9b0719d --- /dev/null +++ b/src/recorder/keyboardgrabber.cpp @@ -0,0 +1,42 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#include "keyboardgrabber_p.h" + +#include + +KeyboardGrabber::KeyboardGrabber(QWindow *window) + : ShortcutInhibition() + , m_grabbedWindow(window) + , m_grabbingKeyboard(false) +{ +} + +KeyboardGrabber::~KeyboardGrabber() +{ + disableInhibition(); +} + +void KeyboardGrabber::enableInhibition() +{ + if (m_grabbingKeyboard || !m_grabbedWindow) { + return; + } + m_grabbingKeyboard = m_grabbedWindow->setKeyboardGrabEnabled(true); +} + +void KeyboardGrabber::disableInhibition() +{ + if (!m_grabbingKeyboard) { + return; + } + m_grabbingKeyboard = !(m_grabbedWindow->setKeyboardGrabEnabled(false)); +} + +bool KeyboardGrabber::shortcutsAreInhibited() const +{ + return m_grabbingKeyboard; +} + diff --git a/src/recorder/keyboardgrabber_p.h b/src/recorder/keyboardgrabber_p.h new file mode 100644 index 0000000..2413808 --- /dev/null +++ b/src/recorder/keyboardgrabber_p.h @@ -0,0 +1,24 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#ifndef GRABBINGINHIBITION_H +#define GRABBINGINHIBITION_H + +#include "shortcutinhibition_p.h" + +class KeyboardGrabber : public ShortcutInhibition +{ +public: + explicit KeyboardGrabber(QWindow *window); + ~KeyboardGrabber(); + void enableInhibition() override; + void disableInhibition() override; + bool shortcutsAreInhibited() const override; +private: + QWindow *m_grabbedWindow; + bool m_grabbingKeyboard; +}; + +#endif diff --git a/src/recorder/keysequencerecorder.cpp b/src/recorder/keysequencerecorder.cpp new file mode 100644 index 0000000..a2e04f8 --- /dev/null +++ b/src/recorder/keysequencerecorder.cpp @@ -0,0 +1,492 @@ +/* + SPDX-FileCopyrightText: 1998 Mark Donohoe + SPDX-FileCopyrightText: 2001 Ellis Whitehead + SPDX-FileCopyrightText: 2007 Andreas Hartmetz + SPDX-FileCopyrightText: 2020 David Redondo + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "keysequencerecorder.h" + +#include "keyboardgrabber_p.h" +#include "kguiaddons_debug.h" +#include "shortcutinhibition_p.h" +#include "waylandinhibition_p.h" + +#include +#include +#include +#include +#include + +#include +#include + +constexpr Qt::KeyboardModifiers modifierMask = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier; + +// Copied here from KKeyServer +static bool isShiftAsModifierAllowed(int keyQt) +{ + // remove any modifiers + keyQt &= ~Qt::KeyboardModifierMask; + + // Shift only works as a modifier with certain keys. It's not possible + // to enter the SHIFT+5 key sequence for me because this is handled as + // '%' by qt on my keyboard. + // The working keys are all hardcoded here :-( + if (keyQt >= Qt::Key_F1 && keyQt <= Qt::Key_F35) { + return true; + } + + if (QChar(keyQt).isLetter()) { + return true; + } + + switch (keyQt) { + case Qt::Key_Return: + case Qt::Key_Space: + case Qt::Key_Backspace: + case Qt::Key_Tab: + case Qt::Key_Backtab: + case Qt::Key_Escape: + case Qt::Key_Print: + case Qt::Key_ScrollLock: + case Qt::Key_Pause: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Insert: + case Qt::Key_Delete: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_Up: + case Qt::Key_Down: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Enter: + case Qt::Key_SysReq: + case Qt::Key_CapsLock: + case Qt::Key_NumLock: + case Qt::Key_Help: + case Qt::Key_Back: + case Qt::Key_Forward: + case Qt::Key_Stop: + case Qt::Key_Refresh: + case Qt::Key_Favorites: + case Qt::Key_LaunchMedia: + case Qt::Key_OpenUrl: + case Qt::Key_HomePage: + case Qt::Key_Search: + case Qt::Key_VolumeDown: + case Qt::Key_VolumeMute: + case Qt::Key_VolumeUp: + case Qt::Key_BassBoost: + case Qt::Key_BassUp: + case Qt::Key_BassDown: + case Qt::Key_TrebleUp: + case Qt::Key_TrebleDown: + case Qt::Key_MediaPlay: + case Qt::Key_MediaStop: + case Qt::Key_MediaPrevious: + case Qt::Key_MediaNext: + case Qt::Key_MediaRecord: + case Qt::Key_MediaPause: + case Qt::Key_MediaTogglePlayPause: + case Qt::Key_LaunchMail: + case Qt::Key_Calculator: + case Qt::Key_Memo: + case Qt::Key_ToDoList: + case Qt::Key_Calendar: + case Qt::Key_PowerDown: + case Qt::Key_ContrastAdjust: + case Qt::Key_Standby: + case Qt::Key_MonBrightnessUp: + case Qt::Key_MonBrightnessDown: + case Qt::Key_KeyboardLightOnOff: + case Qt::Key_KeyboardBrightnessUp: + case Qt::Key_KeyboardBrightnessDown: + case Qt::Key_PowerOff: + case Qt::Key_WakeUp: + case Qt::Key_Eject: + case Qt::Key_ScreenSaver: + case Qt::Key_WWW: + case Qt::Key_Sleep: + case Qt::Key_LightBulb: + case Qt::Key_Shop: + case Qt::Key_History: + case Qt::Key_AddFavorite: + case Qt::Key_HotLinks: + case Qt::Key_BrightnessAdjust: + case Qt::Key_Finance: + case Qt::Key_Community: + case Qt::Key_AudioRewind: + case Qt::Key_BackForward: + case Qt::Key_ApplicationLeft: + case Qt::Key_ApplicationRight: + case Qt::Key_Book: + case Qt::Key_CD: + case Qt::Key_Clear: + case Qt::Key_ClearGrab: + case Qt::Key_Close: + case Qt::Key_Copy: + case Qt::Key_Cut: + case Qt::Key_Display: + case Qt::Key_DOS: + case Qt::Key_Documents: + case Qt::Key_Excel: + case Qt::Key_Explorer: + case Qt::Key_Game: + case Qt::Key_Go: + case Qt::Key_iTouch: + case Qt::Key_LogOff: + case Qt::Key_Market: + case Qt::Key_Meeting: + case Qt::Key_MenuKB: + case Qt::Key_MenuPB: + case Qt::Key_MySites: + case Qt::Key_News: + case Qt::Key_OfficeHome: + case Qt::Key_Option: + case Qt::Key_Paste: + case Qt::Key_Phone: + case Qt::Key_Reply: + case Qt::Key_Reload: + case Qt::Key_RotateWindows: + case Qt::Key_RotationPB: + case Qt::Key_RotationKB: + case Qt::Key_Save: + case Qt::Key_Send: + case Qt::Key_Spell: + case Qt::Key_SplitScreen: + case Qt::Key_Support: + case Qt::Key_TaskPane: + case Qt::Key_Terminal: + case Qt::Key_Tools: + case Qt::Key_Travel: + case Qt::Key_Video: + case Qt::Key_Word: + case Qt::Key_Xfer: + case Qt::Key_ZoomIn: + case Qt::Key_ZoomOut: + case Qt::Key_Away: + case Qt::Key_Messenger: + case Qt::Key_WebCam: + case Qt::Key_MailForward: + case Qt::Key_Pictures: + case Qt::Key_Music: + case Qt::Key_Battery: + case Qt::Key_Bluetooth: + case Qt::Key_WLAN: + case Qt::Key_UWB: + case Qt::Key_AudioForward: + case Qt::Key_AudioRepeat: + case Qt::Key_AudioRandomPlay: + case Qt::Key_Subtitle: + case Qt::Key_AudioCycleTrack: + case Qt::Key_Time: + case Qt::Key_Select: + case Qt::Key_View: + case Qt::Key_TopMenu: + case Qt::Key_Suspend: + case Qt::Key_Hibernate: + case Qt::Key_Launch0: + case Qt::Key_Launch1: + case Qt::Key_Launch2: + case Qt::Key_Launch3: + case Qt::Key_Launch4: + case Qt::Key_Launch5: + case Qt::Key_Launch6: + case Qt::Key_Launch7: + case Qt::Key_Launch8: + case Qt::Key_Launch9: + case Qt::Key_LaunchA: + case Qt::Key_LaunchB: + case Qt::Key_LaunchC: + case Qt::Key_LaunchD: + case Qt::Key_LaunchE: + case Qt::Key_LaunchF: + return true; + + default: + return false; + } +} + + +static bool isOkWhenModifierless(int key) +{ + //this whole function is a hack, but especially the first line of code + if (QKeySequence(key).toString().length() == 1) { + return false; + } + + switch (key) { + case Qt::Key_Return: + case Qt::Key_Space: + case Qt::Key_Tab: + case Qt::Key_Backtab: //does this ever happen? + case Qt::Key_Backspace: + case Qt::Key_Delete: + return false; + default: + return true; + } +} + +static QKeySequence appendToSequence(const QKeySequence &sequence, int key) { + std::array keys{sequence[0], sequence[1], sequence[2], sequence[3]}; + keys[sequence.count()] = key; + return QKeySequence(keys[0], keys[1], keys[2], keys[3]); +} + +class KeySequenceRecorderPrivate : public QObject { + Q_OBJECT +public: + KeySequenceRecorderPrivate(KeySequenceRecorder *q); + + void controlModifierlessTimeout(); + bool eventFilter(QObject *watched, QEvent *event) override; + void handleKeyPress(QKeyEvent *event); + void handleKeyRelease(QKeyEvent *event); + void finishRecording(); + + KeySequenceRecorder *q; + QKeySequence m_currentKeySequence; + QPointer m_window; + bool m_isRecording; + bool m_multiKeyShortcutsAllowed; + bool m_modifierlessAllowed; + + Qt::KeyboardModifiers m_currentModifiers; + QTimer m_modifierlessTimer; + std::unique_ptr m_inhibition; +}; + +KeySequenceRecorderPrivate::KeySequenceRecorderPrivate(KeySequenceRecorder *q) + : QObject(q) + , q(q) +{ +} + +void KeySequenceRecorderPrivate::controlModifierlessTimeout() +{ + if (m_currentKeySequence != 0 && !m_currentModifiers) { + // No modifier key pressed currently. Start the timout + m_modifierlessTimer.start(600); + } else { + // A modifier is pressed. Stop the timeout + m_modifierlessTimer.stop(); + } +} + +bool KeySequenceRecorderPrivate::eventFilter(QObject *watched, QEvent *event) +{ + if (!m_isRecording) { + return QObject::eventFilter(watched, event); + } + + if (event->type() == QEvent::ShortcutOverride || event->type() == QEvent::ContextMenu) { + event->accept(); + return true; + } + if (event->type() == QEvent::KeyRelease) { + handleKeyRelease(static_cast(event)); + return true; + } + if (event->type() == QEvent::KeyPress) { + handleKeyPress(static_cast(event)); + return true; + } + return QObject::eventFilter(watched, event); +} + +void KeySequenceRecorderPrivate::handleKeyPress(QKeyEvent *event) +{ + + m_currentModifiers = event->modifiers() & modifierMask; + int key = event->key(); + switch (key) { + case -1: + qCWarning(KGUIADDONS_LOG) << "Got unknown key"; + // Old behavior was to stop recording here instead of continuing like this + return; + case 0: + break; + case Qt::Key_AltGr: + //or else we get unicode salad + break; + case Qt::Key_Super_L: + case Qt::Key_Super_R: + // Qt doesn't properly recognize Super_L/Super_R as MetaModifier + m_currentModifiers |= Qt::MetaModifier; + Q_FALLTHROUGH(); + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Alt: + case Qt::Key_Meta: + controlModifierlessTimeout(); + Q_EMIT q->currentKeySequenceChanged(); + break; + default: + if (m_currentKeySequence.count() == 0 && !(m_currentModifiers & ~Qt::ShiftModifier)) { + // It's the first key and no modifier pressed. Check if this is allowed + if (!(isOkWhenModifierless(key) || m_modifierlessAllowed)) { + // No it's not + return; + } + } + + // We now have a valid key press. + if ((key == Qt::Key_Backtab) && (m_currentModifiers & Qt::ShiftModifier)) { + key = Qt::Key_Tab | m_currentModifiers; + } else if (isShiftAsModifierAllowed(key)) { + key |= m_currentModifiers; + } else { + key |= (m_currentModifiers & ~Qt::ShiftModifier); + } + + m_currentKeySequence = appendToSequence(m_currentKeySequence, key); + Q_EMIT q->currentKeySequenceChanged(); + + if ((!m_multiKeyShortcutsAllowed) || (m_currentKeySequence.count() == 4)) { + finishRecording(); + break; + } + controlModifierlessTimeout(); + } + event->accept(); +} + +void KeySequenceRecorderPrivate::handleKeyRelease(QKeyEvent *event) +{ + Qt::KeyboardModifiers modifiers = event->modifiers() & modifierMask; + switch (event->key()) { + case -1: + return; + case Qt::Key_Super_L: + case Qt::Key_Super_R: + // Qt doesn't properly recognize Super_L/Super_R as MetaModifier + modifiers &= ~Qt::MetaModifier; + } + if ((modifiers & m_currentModifiers) < m_currentModifiers) { + m_currentModifiers = modifiers; + controlModifierlessTimeout(); + Q_EMIT q->currentKeySequenceChanged(); + } +} + +void KeySequenceRecorderPrivate::finishRecording() +{ + m_modifierlessTimer.stop(); + m_isRecording = false; + m_currentModifiers = Qt::NoModifier; + if (m_inhibition) { + m_inhibition->disableInhibition(); + } + Q_EMIT q->recordingChanged(); + Q_EMIT q->gotKeySequence(m_currentKeySequence); +} + +KeySequenceRecorder::KeySequenceRecorder(QWindow *window, QObject *parent) + : QObject(parent) + , d(new KeySequenceRecorderPrivate(this)) +{ + d->m_isRecording = false; + d->m_modifierlessAllowed = false; + d->m_multiKeyShortcutsAllowed = true; + + setWindow(window); + connect(&d->m_modifierlessTimer, &QTimer::timeout, d.get(), &KeySequenceRecorderPrivate::finishRecording); +} + +KeySequenceRecorder::~KeySequenceRecorder() noexcept +{ +} + +void KeySequenceRecorder::startRecording() +{ + if (!d->m_window) { + qCWarning(KGUIADDONS_LOG) << "Cannot record without a window"; + return; + } + d->m_isRecording = true; + d->m_currentKeySequence = QKeySequence(); + if (d->m_inhibition) { + d->m_inhibition->enableInhibition(); + } + Q_EMIT recordingChanged(); + Q_EMIT currentKeySequenceChanged(); +} + +bool KeySequenceRecorder::isRecording() const +{ + return d->m_isRecording; +} + +QKeySequence KeySequenceRecorder::currentKeySequence() const +{ + return d->m_isRecording ? appendToSequence(d->m_currentKeySequence, d->m_currentModifiers) : d->m_currentKeySequence; +} + +QWindow* KeySequenceRecorder::window() const +{ + return d->m_window; +} + +void KeySequenceRecorder::setWindow(QWindow *window) +{ + if (window == d->m_window) { + return; + } + + if (d->m_window) { + d->m_window->removeEventFilter(d.get()); + } + + if (window) { + window->installEventFilter(d.get()); + qCDebug(KGUIADDONS_LOG) << "listening for events in" << window; + } + + if (qApp->platformName() == QLatin1String("wayland")) { +#ifdef WITH_WAYLAND + d->m_inhibition.reset(new WaylandInhibition(window)); +#endif + } else { + d->m_inhibition.reset(new KeyboardGrabber(window)); + } + + d->m_window = window; + + Q_EMIT windowChanged(); +} + +bool KeySequenceRecorder::multiKeyShortcutsAllowed() const +{ + return d->m_multiKeyShortcutsAllowed; +} + +void KeySequenceRecorder::setMultiKeyShortcutsAllowed(bool allowed) +{ + if (allowed == d->m_multiKeyShortcutsAllowed) { + return; + } + d->m_multiKeyShortcutsAllowed = allowed; + Q_EMIT multiKeyShortcutsAllowedChanged(); +} + +bool KeySequenceRecorder::modifierlessAllowed() const +{ + return d->m_modifierlessAllowed; +} + +void KeySequenceRecorder::setModifierlessAllowed(bool allowed) +{ + if (allowed == d->m_modifierlessAllowed) { + return; + } + d->m_modifierlessAllowed = allowed; + Q_EMIT modifierlessAllowedChanged(); +} + +#include "keysequencerecorder.moc" diff --git a/src/recorder/keysequencerecorder.h b/src/recorder/keysequencerecorder.h new file mode 100644 index 0000000..98e740e --- /dev/null +++ b/src/recorder/keysequencerecorder.h @@ -0,0 +1,132 @@ +/* + SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead + SPDX-FileCopyrightText: 2007 Andreas Hartmetz + SPDX-FileCopyrightText: 2020 David Redondo + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + + +#ifndef KSHORTCUTRECORDER_H +#define KSHORTCUTRECORDER_H + +#include + +#include +#include + +#include + +class KeySequenceRecorderPrivate; + +class QWindow; + +/** + * @class KeySequenceRecorder keysequencerecorder.h KeySequenceRecorder + * + * @short Record a QKeySequence by listening to key events in a window. + * + * After calling startRecording key events in the set window will be captured until a valid + * QKeySequence has been recorded and gotKeySequence is emitted. See multiKeyShortcutsAllowed and + * modifierlessAllowed for what constitutes a valid key sequence. + * + * During recording any shortcuts are inhibited and cannot be triggered. Either by using the + * + * keyboard-shortcuts-inhibit protocol on Wayland or grabbing the keyboard. + * + * For graphical elements that record key sequences and can optionally perform conflict checking + * against existing shortcuts see KKeySequenceWidget and KeySequenceItem. + * @since 5.77 + * @see KKeySequenceWidget, KeySequenceItem + */ + +class KGUIADDONS_EXPORT KeySequenceRecorder : public QObject +{ + Q_OBJECT + + /** + * Whether key events are currently recorded + */ + Q_PROPERTY(bool isRecording READ isRecording NOTIFY recordingChanged) + /** + * The recorded key sequence. + * After construction this is empty. + * + * During recording it is continiously updated with the newest user input. + * + * After recording it contains the last recorded QKeySequence + */ + Q_PROPERTY(QKeySequence currentKeySequence READ currentKeySequence NOTIFY currentKeySequenceChanged) + /** + * The window in which the key events are happening that should be recorded + */ + Q_PROPERTY(QWindow* window READ window WRITE setWindow NOTIFY windowChanged) + /** + * If key presses of "plain" keys without a modifier are considered to be a valid finished + * key combination. + * Plain keys include letter and symbol keys and text editing keys (Return, Space, Tab, + * Backspace, Delete). Other keys like F1, Cursor keys, Insert, PageDown will always work. + * + * By default this is `false`. + */ + Q_PROPERTY(bool modifierlessAllowed READ modifierlessAllowed WRITE setModifierlessAllowed NOTIFY modifierlessAllowedChanged) + /** Controls the amount of key combinations that are captured until recording stops and gotKeySequence + * is emitted. + * By default this is `true` and "Emacs-style" key sequences are recorded. Recording does not + * stop until four valid key combination have been recorded. Afterwards `currentKeySequence().count()` + * will be 4. + * + * Otherwise only one key combination is recorded before gotKeySequence is emitted with a + * QKeySequence with a `count()` of 1. + * @see QKeySequence + */ + Q_PROPERTY(bool multiKeyShortcutsAllowed READ multiKeyShortcutsAllowed WRITE setMultiKeyShortcutsAllowed NOTIFY multiKeyShortcutsAllowedChanged) +public: + /** + * Constructor. + * + * @par window The window whose key events will be recorded. + * @see window + */ + explicit KeySequenceRecorder(QWindow *window, QObject *parent = nullptr); + ~KeySequenceRecorder(); + + /** + * Start recording. + * Calling startRecording when window() is `nullptr` has no effect. + */ + void Q_INVOKABLE startRecording(); + + bool isRecording() const; + + QKeySequence currentKeySequence() const; + + QWindow* window() const; + void setWindow(QWindow *window); + + bool multiKeyShortcutsAllowed() const; + void setMultiKeyShortcutsAllowed(bool allowed); + + void setModifierlessAllowed(bool allowed); + bool modifierlessAllowed() const; + +Q_SIGNALS: + /** + * This signal is emitted when a key sequence has been recorded. + * + * Compared to currentKeySequenceChanged and currentKeySequence this is signal is not emmitted + * continiously during recording but only after recording has finished. + */ + void gotKeySequence(const QKeySequence &keySequence); + + void recordingChanged(); + void windowChanged(); + void currentKeySequenceChanged(); + void multiKeyShortcutsAllowedChanged(); + void modifierlessAllowedChanged(); +private: + friend class KeySequenceRecorderPrivate; + std::unique_ptr d; +}; + +#endif diff --git a/src/recorder/shortcutinhibition_p.h b/src/recorder/shortcutinhibition_p.h new file mode 100644 index 0000000..8871d99 --- /dev/null +++ b/src/recorder/shortcutinhibition_p.h @@ -0,0 +1,19 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#ifndef SHORTCUTINHIBITION_H +#define SHORTCUTINHIBITION_H + +class QWindow; + +class ShortcutInhibition { +public: + virtual ~ShortcutInhibition(){} + virtual void enableInhibition() = 0; + virtual void disableInhibition() = 0; + virtual bool shortcutsAreInhibited() const = 0; +}; + +#endif diff --git a/src/recorder/waylandinhibition.cpp b/src/recorder/waylandinhibition.cpp new file mode 100644 index 0000000..fe327f2 --- /dev/null +++ b/src/recorder/waylandinhibition.cpp @@ -0,0 +1,104 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#include "waylandinhibition_p.h" + +#include +#include +#include + +#include "qwayland-keyboard-shortcuts-inhibit-unstable-v1.h" + +class ShortcutsInhibitManager + : public QWaylandClientExtensionTemplate + , public QtWayland::zwp_keyboard_shortcuts_inhibit_manager_v1 +{ +public: + ShortcutsInhibitManager() + : QWaylandClientExtensionTemplate(1) + { + // QWaylandClientExtensionTemplate invokes this with a QueuedConnection but we want shortcuts + // to be inhibited immediately. + QMetaObject::invokeMethod(this, "addRegistryListener"); + } + ~ShortcutsInhibitManager() + { + if (isInitialized()) { + destroy(); + } + } +}; + +class ShortcutsInhibitor : public QtWayland::zwp_keyboard_shortcuts_inhibitor_v1 +{ +public: + ShortcutsInhibitor(::zwp_keyboard_shortcuts_inhibitor_v1 *id) + : QtWayland::zwp_keyboard_shortcuts_inhibitor_v1(id) + { + } + + ~ShortcutsInhibitor() + { + destroy(); + } + + void zwp_keyboard_shortcuts_inhibitor_v1_active() override + { + m_active = true; + } + + void zwp_keyboard_shortcuts_inhibitor_v1_inactive() override + { + m_active = false; + } + + bool isActive() const + { + return m_active; + } + +private: + bool m_active = false; +}; + +WaylandInhibition::WaylandInhibition(QWindow *window) + : ShortcutInhibition() + , m_manager(new ShortcutsInhibitManager) + , m_window(window) +{ +} + +WaylandInhibition::~WaylandInhibition() = default; + +bool WaylandInhibition::shortcutsAreInhibited() const +{ + return m_inhibitor && m_inhibitor->isActive(); +} + +void WaylandInhibition::enableInhibition() +{ + if (m_inhibitor || !m_manager->isActive()) { + return; + } + QPlatformNativeInterface *nativeInterface = qApp->platformNativeInterface(); + if (!nativeInterface) { + return; + } + auto seat = static_cast(nativeInterface->nativeResourceForIntegration("wl_seat")); + auto surface = static_cast(nativeInterface->nativeResourceForWindow("surface", m_window)); + if (!seat || !surface) { + return; + } + m_inhibitor.reset(new ShortcutsInhibitor(m_manager->inhibit_shortcuts(surface, seat))); +} + +void WaylandInhibition::disableInhibition() +{ + if (!m_inhibitor) { + return; + } + m_inhibitor.reset(); +} + diff --git a/src/recorder/waylandinhibition_p.h b/src/recorder/waylandinhibition_p.h new file mode 100644 index 0000000..2244b34 --- /dev/null +++ b/src/recorder/waylandinhibition_p.h @@ -0,0 +1,31 @@ +/* + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL + SPDX-FileCopyrightText: 2020 David Redondo +*/ + +#ifndef WAYLANDSHORTCUTINHIBITOR_H +#define WAYLANDSHORTCUTINHIBITOR_H + +#include "shortcutinhibition_p.h" + +#include + +class ShortcutsInhibitManager; +class ShortcutsInhibitor; + +class WaylandInhibition : public ShortcutInhibition +{ +public: + explicit WaylandInhibition(QWindow *window); + ~WaylandInhibition() override; + bool shortcutsAreInhibited() const override; + void enableInhibition() override; + void disableInhibition() override; +private: + + std::unique_ptr m_manager; + std::unique_ptr m_inhibitor; + QWindow *m_window; +}; + +#endif diff --git a/src/text/kdatevalidator.cpp b/src/text/kdatevalidator.cpp new file mode 100644 index 0000000..5cf39e2 --- /dev/null +++ b/src/text/kdatevalidator.cpp @@ -0,0 +1,62 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + SPDX-FileCopyrightText: 1997 Tim D. Gilman + SPDX-FileCopyrightText: 1998-2001 Mirko Boehm + SPDX-FileCopyrightText: 2007 John Layt + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "kdatevalidator.h" + +#include +#include + +class Q_DECL_HIDDEN KDateValidator::KDateValidatorPrivate +{ +public: + KDateValidatorPrivate(KDateValidator *q): q(q) + { + } + + ~KDateValidatorPrivate() + { + } + + KDateValidator *const q; +}; + +KDateValidator::KDateValidator(QObject *parent) : QValidator(parent), d(nullptr) +{ + Q_UNUSED(d); +} + +QValidator::State KDateValidator::validate(QString &text, int &unused) const +{ + Q_UNUSED(unused); + + QDate temp; + // ----- everything is tested in date(): + return date(text, temp); +} + +QValidator::State KDateValidator::date(const QString &text, QDate &d) const +{ + QLocale::FormatType formats[] = { QLocale::LongFormat, QLocale::ShortFormat, QLocale::NarrowFormat }; + QLocale locale; + + for (int i = 0; i < 3; i++) { + QDate tmp = locale.toDate(text, formats[i]); + if (tmp.isValid()) { + d = tmp; + return Acceptable; + } + } + + return QValidator::Intermediate; +} + +void KDateValidator::fixup(QString &) const +{ +} + diff --git a/src/text/kdatevalidator.h b/src/text/kdatevalidator.h new file mode 100644 index 0000000..b3b5187 --- /dev/null +++ b/src/text/kdatevalidator.h @@ -0,0 +1,36 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + SPDX-FileCopyrightText: 1997 Tim D. Gilman + SPDX-FileCopyrightText: 1998-2001 Mirko Boehm + SPDX-FileCopyrightText: 2007 John Layt + + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KDATEVALIDATOR_H +#define KDATEVALIDATOR_H + +#include + +#include + +/** + * @class KDateValidator kdatevalidator.h KDateValidator + * + * Validates user-entered dates. + */ +class KGUIADDONS_EXPORT KDateValidator : public QValidator +{ + Q_OBJECT +public: + explicit KDateValidator(QObject *parent = nullptr); + State validate(QString &text, int &e) const override; + void fixup(QString &input) const override; + State date(const QString &text, QDate &date) const; +private: + class KDateValidatorPrivate; + friend class KDateValidatorPrivate; + KDateValidatorPrivate *const d; +}; + +#endif // KDATEVALIDATOR_H diff --git a/src/text/kwordwrap.cpp b/src/text/kwordwrap.cpp new file mode 100644 index 0000000..a78eab9 --- /dev/null +++ b/src/text/kwordwrap.cpp @@ -0,0 +1,311 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 2001 David Faure + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "kwordwrap.h" + +#include +#include + +class KWordWrapPrivate : public QSharedData +{ +public: + QRect m_constrainingRect; + QVector m_breakPositions; + QVector m_lineWidths; + QRect m_boundingRect; + QString m_text; +}; + +KWordWrap::KWordWrap(const QRect &r) + : d(new KWordWrapPrivate) +{ + d->m_constrainingRect = r; +} + +KWordWrap KWordWrap::formatText(QFontMetrics &fm, const QRect &r, int /*flags*/, const QString &str, int len) +{ + KWordWrap kw(r); + // The wordwrap algorithm + // The variable names and the global shape of the algorithm are inspired + // from QTextFormatterBreakWords::format(). + //qDebug() << "KWordWrap::formatText " << str << " r=" << r.x() << "," << r.y() << " " << r.width() << "x" << r.height(); + int height = fm.height(); + if (len == -1) { + kw.d->m_text = str; + } else { + kw.d->m_text = str.left(len); + } + if (len == -1) { + len = str.length(); + } + int lastBreak = -1; + int lineWidth = 0; + int x = 0; + int y = 0; + int w = r.width(); + int textwidth = 0; + bool isBreakable = false; + bool wasBreakable = false; // value of isBreakable for last char (i-1) + bool isParens = false; // true if one of ({[ + bool wasParens = false; // value of isParens for last char (i-1) + QString inputString = str; + + for (int i = 0; i < len; ++i) { + const QChar c = inputString.at(i); + const int ww = fm.charWidth(inputString, i); + + isParens = (c == QLatin1Char('(') || c == QLatin1Char('[') + || c == QLatin1Char('{')); + // isBreakable is true when we can break _after_ this character. + isBreakable = (c.isSpace() || c.isPunct() || c.isSymbol()) & !isParens; + + // Special case for '(', '[' and '{': we want to break before them + if (!isBreakable && i < len - 1) { + const QChar nextc = inputString.at(i + 1); // look at next char + isBreakable = (nextc == QLatin1Char('(') + || nextc == QLatin1Char('[') + || nextc == QLatin1Char('{')); + } + // Special case for '/': after normal chars it's breakable (e.g. inside a path), + // but after another breakable char it's not (e.g. "mounted at /foo") + // Same thing after a parenthesis (e.g. "dfaure [/fool]") + if (c == QLatin1Char('/') && (wasBreakable || wasParens)) { + isBreakable = false; + } + + /*qDebug() << "c='" << QString(c) << "' i=" << i << "/" << len + << " x=" << x << " ww=" << ww << " w=" << w + << " lastBreak=" << lastBreak << " isBreakable=" << isBreakable << endl;*/ + int breakAt = -1; + if (x + ww > w && lastBreak != -1) { // time to break and we know where + breakAt = lastBreak; + } + if (x + ww > w - 4 && lastBreak == -1) { // time to break but found nowhere [-> break here] + breakAt = i; + } + if (i == len - 2 && x + ww + fm.charWidth(inputString, i + 1) > w) { // don't leave the last char alone + breakAt = lastBreak == -1 ? i - 1 : lastBreak; + } + if (c == QLatin1Char('\n')) { // Forced break here + if (breakAt == -1 && lastBreak != -1) { // only break if not already breaking + breakAt = i - 1; + lastBreak = -1; + } + // remove the line feed from the string + kw.d->m_text.remove(i, 1); + inputString.remove(i, 1); + len--; + } + if (breakAt != -1) { + //qDebug() << "KWordWrap::formatText breaking after " << breakAt; + kw.d->m_breakPositions.append(breakAt); + int thisLineWidth = lastBreak == -1 ? x + ww : lineWidth; + kw.d->m_lineWidths.append(thisLineWidth); + textwidth = qMax(textwidth, thisLineWidth); + x = 0; + y += height; + wasBreakable = true; + wasParens = false; + if (lastBreak != -1) { + // Breakable char was found, restart from there + i = lastBreak; + lastBreak = -1; + continue; + } + } else if (isBreakable) { + lastBreak = i; + lineWidth = x + ww; + } + x += ww; + wasBreakable = isBreakable; + wasParens = isParens; + } + textwidth = qMax(textwidth, x); + kw.d->m_lineWidths.append(x); + y += height; + //qDebug() << "KWordWrap::formatText boundingRect:" << r.x() << "," << r.y() << " " << textwidth << "x" << y; + if (r.height() >= 0 && y > r.height()) { + textwidth = r.width(); + } + int realY = y; + if (r.height() >= 0) { + while (realY > r.height()) { + realY -= height; + } + realY = qMax(realY, 0); + } + kw.d->m_boundingRect.setRect(0, 0, textwidth, realY); + return kw; +} + +KWordWrap::~KWordWrap() +{ +} + +KWordWrap::KWordWrap(const KWordWrap &other) + : d(other.d) +{ +} + +KWordWrap &KWordWrap::operator=(const KWordWrap &other) +{ + d = other.d; + return *this; +} + +QString KWordWrap::wrappedString() const +{ + // We use the calculated break positions to insert '\n' into the string + QString ws; + int start = 0; + for (int i = 0; i < d->m_breakPositions.count(); ++i) { + int end = d->m_breakPositions.at(i); + ws += d->m_text.midRef(start, end - start + 1); + ws += QLatin1Char('\n'); + start = end + 1; + } + ws += d->m_text.midRef(start); + return ws; +} + +QString KWordWrap::truncatedString(bool dots) const +{ + if (d->m_breakPositions.isEmpty()) { + return d->m_text; + } + + QString ts = d->m_text.left(d->m_breakPositions.first() + 1); + if (dots) { + ts += QLatin1String("..."); + } + return ts; +} + +static QColor mixColors(double p1, QColor c1, QColor c2) +{ + return QColor(int(c1.red() * p1 + c2.red() * (1.0 - p1)), + int(c1.green() * p1 + c2.green() * (1.0 - p1)), + int(c1.blue() * p1 + c2.blue() * (1.0 - p1))); +} + +void KWordWrap::drawFadeoutText(QPainter *p, int x, int y, int maxW, + const QString &t) +{ + QFontMetrics fm = p->fontMetrics(); + QColor bgColor = p->background().color(); + QColor textColor = p->pen().color(); + + if ((fm.boundingRect(t).width() > maxW) && (t.length() > 1)) { + int tl = 0; + int w = 0; + while (tl < t.length()) { + w += fm.charWidth(t, tl); + if (w >= maxW) { + break; + } + tl++; + } + + int n = qMin(tl, 3); + if (t.isRightToLeft()) { + x += maxW; // start from the right side for RTL string + if (tl > 3) { + x -= fm.horizontalAdvance(t.left(tl - 3)); + p->drawText(x, y, t.left(tl - 3)); + } + for (int i = 0; i < n; i++) { + p->setPen(mixColors(0.70 - i * 0.25, textColor, bgColor)); + QString s(t.at(tl - n + i)); + x -= fm.horizontalAdvance(s); + p->drawText(x, y, s); + } + } else { + if (tl > 3) { + p->drawText(x, y, t.left(tl - 3)); + x += fm.horizontalAdvance(t.left(tl - 3)); + } + for (int i = 0; i < n; i++) { + p->setPen(mixColors(0.70 - i * 0.25, textColor, bgColor)); + QString s(t.at(tl - n + i)); + p->drawText(x, y, s); + x += fm.horizontalAdvance(s); + } + } + } else { + p->drawText(x, y, t); + } +} + +void KWordWrap::drawTruncateText(QPainter *p, int x, int y, int maxW, + const QString &t) +{ + QString tmpText = p->fontMetrics().elidedText(t, Qt::ElideRight, maxW); + p->drawText(x, y, tmpText); +} + +void KWordWrap::drawText(QPainter *painter, int textX, int textY, int flags) const +{ + //qDebug() << "KWordWrap::drawText text=" << wrappedString() << " x=" << textX << " y=" << textY; + // We use the calculated break positions to draw the text line by line using QPainter + int start = 0; + int y = 0; + QFontMetrics fm = painter->fontMetrics(); + int height = fm.height(); // line height + int ascent = fm.ascent(); + int maxwidth = d->m_boundingRect.width(); + int i; + int lwidth = 0; + int end = 0; + for (i = 0; i < d->m_breakPositions.count(); ++i) { + // if this is the last line, leave the loop + if ((d->m_constrainingRect.height() >= 0) && + ((y + 2 * height) > d->m_constrainingRect.height())) { + break; + } + end = d->m_breakPositions.at(i); + lwidth = d->m_lineWidths.at(i); + int x = textX; + if (flags & Qt::AlignHCenter) { + x += (maxwidth - lwidth) / 2; + } else if (flags & Qt::AlignRight) { + x += maxwidth - lwidth; + } + painter->drawText(x, textY + y + ascent, d->m_text.mid(start, end - start + 1)); + y += height; + start = end + 1; + } + + // Draw the last line + lwidth = d->m_lineWidths.last(); + int x = textX; + if (flags & Qt::AlignHCenter) { + x += (maxwidth - lwidth) / 2; + } else if (flags & Qt::AlignRight) { + x += maxwidth - lwidth; + } + if ((d->m_constrainingRect.height() < 0) || + ((y + height) <= d->m_constrainingRect.height())) { + if (i == d->m_breakPositions.count()) { + painter->drawText(x, textY + y + ascent, d->m_text.mid(start)); + } else if (flags & FadeOut) + drawFadeoutText(painter, textX, textY + y + ascent, + d->m_constrainingRect.width(), + d->m_text.mid(start)); + else if (flags & Truncate) + drawTruncateText(painter, textX, textY + y + ascent, + d->m_constrainingRect.width(), + d->m_text.mid(start)); + else + painter->drawText(x, textY + y + ascent, + d->m_text.mid(start)); + } +} + +QRect KWordWrap::boundingRect() const +{ + return d->m_boundingRect; +} + diff --git a/src/text/kwordwrap.h b/src/text/kwordwrap.h new file mode 100644 index 0000000..7ba138a --- /dev/null +++ b/src/text/kwordwrap.h @@ -0,0 +1,143 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 2001 David Faure + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#ifndef kwordwrap_h +#define kwordwrap_h + +#include +#include +#include + +class QFontMetrics; +class QRect; +class QString; +class QPainter; +class KWordWrapPrivate; + +/** + * @class KWordWrap kwordwrap.h KWordWrap + * + * Word-wrap algorithm that takes into account beautifulness ;) + * + * That means: + * @li not letting a letter alone on the last line, + * @li breaking at punctuation signs (not only at spaces) + * @li improved handling of (), [] and {} + * @li improved handling of '/' (e.g. for paths) + * + * Usage: call the static method, formatText, with the text to + * wrap and the constraining rectangle etc., it will return an instance of KWordWrap + * containing internal data, result of the word-wrapping. + * From that instance you can retrieve the boundingRect, and invoke drawing. + * + * This design allows to call the word-wrap algorithm only when the text changes + * and not every time we want to know the bounding rect or draw the text. + * + * @author David Faure + */ +class KGUIADDONS_EXPORT KWordWrap +{ +public: + /** + * Use this flag in drawText() if you want to fade out the text if it does + * not fit into the constraining rectangle. + */ + enum { FadeOut = 0x10000000, Truncate = 0x20000000 }; + + /** + * Main method for wrapping text. + * + * @param fm Font metrics, for the chosen font. Better cache it, creating a QFontMetrics is expensive. + * @param r Constraining rectangle. Only the width and height matter. With + * negative height the complete text will be rendered + * @param flags currently unused + * @param str The text to be wrapped. + * @param len Length of text to wrap (default is -1 for all). + * @return a KWordWrap instance. The caller is responsible for storing and deleting the result. + */ + static KWordWrap formatText(QFontMetrics &fm, const QRect &r, int flags, const QString &str, int len = -1); + + /** + * @return the bounding rect, calculated by formatText. The width is the + * width of the widest text line, and never wider than + * the rectangle given to formatText. The height is the + * text block. X and Y are always 0. + */ + QRect boundingRect() const; + + /** + * @return the original string, with '\n' inserted where + * the text is broken by the wordwrap algorithm. + */ + QString wrappedString() const; // gift for Dirk :) + + /** + * @return the original string, truncated to the first line. + * If @p dots was set, '...' is appended in case the string was truncated. + * Bug: Note that the '...' come out of the bounding rect. + */ + QString truncatedString(bool dots = true) const; + + /** + * Draw the text that has been previously wrapped, at position x,y. + * Flags are for alignment, e.g. Qt::AlignHCenter. Default is + * Qt::AlignAuto. + * @param painter the QPainter to use. + * @param x the horizontal position of the text + * @param y the vertical position of the text + * @param flags the ORed text alignment flags from the Qt namespace, + * ORed with FadeOut if you want the text to fade out if it + * does not fit (the @p painter's background must be set + * accordingly) + */ + void drawText(QPainter *painter, int x, int y, int flags = Qt::AlignLeft) const; + + /** + * Destructor. + */ + ~KWordWrap(); + + /** + * Copy constructor + */ + KWordWrap(const KWordWrap &other); + /** + * Assignment operator + */ + KWordWrap &operator=(const KWordWrap &other); + + /** + * Draws the string @p t at the given coordinates, if it does not + * @p fit into @p maxW the text will be faded out. + * @param p the painter to use. Must have set the pen for the text + * color and the background for the color to fade out + * @param x the horizontal position of the text + * @param y the vertical position of the text + * @param maxW the maximum width of the text (including the fade-out + * effect) + * @param t the text to draw + */ + static void drawFadeoutText(QPainter *p, int x, int y, int maxW, + const QString &t); + + /** + * Draws the string @p t at the given coordinates, if it does not + * @p fit into @p maxW the text will be truncated. + * @param p the painter to use + * @param x the horizontal position of the text + * @param y the vertical position of the text + * @param maxW the maximum width of the text (including the '...') + * @param t the text to draw + */ + static void drawTruncateText(QPainter *p, int x, int y, int maxW, + const QString &t); + +private: + KWordWrap(const QRect &r); + QExplicitlySharedDataPointer d; +}; + +#endif diff --git a/src/util/kcursorsaver.cpp b/src/util/kcursorsaver.cpp new file mode 100644 index 0000000..cf7dc78 --- /dev/null +++ b/src/util/kcursorsaver.cpp @@ -0,0 +1,56 @@ +/* + SPDX-License-Identifier: LGPL-2.0-or-later + SPDX-FileCopyrightText: 2003 Marc Mutz + SPDX-FileCopyrightText: 2020 Laurent Montel +*/ + + + +#include "kcursorsaver.h" +#include "kguiaddons_debug.h" +#include + +class KCursorSaverPrivate +{ +public: + bool ownsCursor = true; +}; + +KCursorSaver::KCursorSaver(Qt::CursorShape shape) + : d(new KCursorSaverPrivate) +{ + QGuiApplication::setOverrideCursor(QCursor(shape)); + d->ownsCursor = true; +} + +KCursorSaver::KCursorSaver(KCursorSaver &&other) + : d(other.d) +{ + *this = std::move(other); +} + +KCursorSaver::~KCursorSaver() +{ + if (d->ownsCursor) { + QGuiApplication::restoreOverrideCursor(); + delete d; + } +} + +void KCursorSaver::restoreCursor() +{ + if (!d->ownsCursor) { + qCWarning(KGUIADDONS_LOG) << "This KCursorSaver doesn't own the cursor anymore, invalid call to restoreCursor()."; + return; + } + d->ownsCursor = false; + QGuiApplication::restoreOverrideCursor(); +} + +KCursorSaver &KCursorSaver::operator =(KCursorSaver &&other) +{ + if (this != &other) { + d->ownsCursor = false; + } + return *this; +} diff --git a/src/util/kcursorsaver.h b/src/util/kcursorsaver.h new file mode 100644 index 0000000..59bfeb7 --- /dev/null +++ b/src/util/kcursorsaver.h @@ -0,0 +1,52 @@ +/* + SPDX-License-Identifier: LGPL-2.0-or-later + SPDX-FileCopyrightText: 2003 Marc Mutz + SPDX-FileCopyrightText: 2020 Laurent Montel +*/ + +#ifndef KCURSORSAVER_H +#define KCURSORSAVER_H +#include + +#include + +class KCursorSaverPrivate; + +/** + * @class KCursorSaver kcursorsaver.h KCursorSaver + * + * @short Class to temporarily set a mouse cursor and restore the previous one on destruction + * + * Create a KCursorSaver object when you want to set the cursor. + * As soon as it gets out of scope, it will restore the original + * cursor. + * @code + KCursorSaver saver(Qt::WaitCursor); + ... long-running operation here ... + @endcode + * @since 5.73 + */ +class KGUIADDONS_EXPORT KCursorSaver +{ +public: + /// Creates a KCursorSaver, setting the mouse cursor to @p shape. + explicit KCursorSaver(Qt::CursorShape shape); + + /// Move-constructs a KCursorSaver from other + KCursorSaver(KCursorSaver &&other); + + /// restore the cursor + ~KCursorSaver(); + + /// call this to explicitly restore the cursor + void restoreCursor(); + + KCursorSaver& operator=(KCursorSaver &&other); + +private: + KCursorSaver(KCursorSaver &other) = delete; + void operator=(const KCursorSaver &rhs) = delete; + KCursorSaverPrivate *const d; ///< @internal +}; + +#endif diff --git a/src/util/kiconutils.cpp b/src/util/kiconutils.cpp new file mode 100644 index 0000000..fd58ef2 --- /dev/null +++ b/src/util/kiconutils.cpp @@ -0,0 +1,153 @@ +/* + SPDX-FileCopyrightText: 2013 Martin Klapetek + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "kiconutils.h" + +#include +#include +#include + +class KOverlayIconEngine : public QIconEngine +{ +public: + KOverlayIconEngine(const QIcon &icon, const QIcon &overlay, Qt::Corner position); + KOverlayIconEngine(const QIcon &icon, const QHash &overlays); + void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override; + QIconEngine *clone() const override; + + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override; + + void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) override; + void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) override; + +private: + QIcon m_base; + QHash m_overlays; +}; + +KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QIcon &overlay, Qt::Corner position) + : QIconEngine() +{ + m_base = icon; + m_overlays.insert(position, overlay); +} + +KOverlayIconEngine::KOverlayIconEngine(const QIcon &icon, const QHash &overlays) + : QIconEngine() +{ + m_base = icon; + m_overlays = overlays; +} + +QIconEngine *KOverlayIconEngine::clone() const +{ + return new KOverlayIconEngine(*this); +} + +QSize KOverlayIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + return m_base.actualSize(size, mode, state); +} + +QPixmap KOverlayIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + QPixmap pixmap(size); + pixmap.fill(Qt::transparent); + QPainter p(&pixmap); + + paint(&p, pixmap.rect(), mode, state); + + return pixmap; +} + +void KOverlayIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) +{ + m_base.addPixmap(pixmap, mode, state); +} + +void KOverlayIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + m_base.addFile(fileName, size, mode, state); +} + +void KOverlayIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) +{ + // Paint the base icon as the first layer + m_base.paint(painter, rect, Qt::AlignCenter, mode, state); + + if (m_overlays.isEmpty()) { + return; + } + + const int width = rect.width(); + const int height = rect.height(); + const int iconSize = qMin(width, height); + // Determine the overlay icon size + int overlaySize; + if (iconSize < 32) { + overlaySize = 8; + } else if (iconSize <= 48) { + overlaySize = 16; + } else if (iconSize <= 64) { + overlaySize = 22; + } else if (iconSize <= 96) { + overlaySize = 32; + } else if (iconSize <= 128) { + overlaySize = 48; + } else { + overlaySize = (int)(iconSize / 4); + } + + // Iterate over stored overlays + QHash::const_iterator i = m_overlays.constBegin(); + while (i != m_overlays.constEnd()) { + + const QPixmap overlayPixmap = i.value().pixmap(overlaySize, overlaySize, mode, state); + if (overlayPixmap.isNull()) { + ++i; + continue; + } + + QPoint startPoint; + switch (i.key()) { + case Qt::BottomLeftCorner: + startPoint = QPoint(2, height - overlaySize - 2); + break; + case Qt::BottomRightCorner: + startPoint = QPoint(width - overlaySize - 2, height - overlaySize - 2); + break; + case Qt::TopRightCorner: + startPoint = QPoint(width - overlaySize - 2, 2); + break; + case Qt::TopLeftCorner: + startPoint = QPoint(2, 2); + break; + } + + // Draw the overlay pixmap + painter->drawPixmap(startPoint, overlayPixmap); + + ++i; + } +} + +// ============================================================================ + +namespace KIconUtils +{ + +QIcon addOverlay(const QIcon &icon, const QIcon &overlay, Qt::Corner position) +{ + return QIcon(new KOverlayIconEngine(icon, overlay, position)); +} + +QIcon addOverlays(const QIcon &icon, const QHash &overlays) +{ + return QIcon(new KOverlayIconEngine(icon, overlays)); +} + +} diff --git a/src/util/kiconutils.h b/src/util/kiconutils.h new file mode 100644 index 0000000..7acf1ac --- /dev/null +++ b/src/util/kiconutils.h @@ -0,0 +1,42 @@ +/* + SPDX-FileCopyrightText: 2013 Martin Klapetek + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KICONUTILS_H +#define KICONUTILS_H + +#include + +#include + +/** + * @namespace KIconUtils + * Provides utility functions for icons. + */ +namespace KIconUtils +{ + +/** + * Adds the \a overlay over the \a icon in the specified \a position + * + * The \a overlay icon is scaled down approx. to 1/3 or 1/4 (depending on the icon size) + * and placed in one of the corners of the base icon. + */ +KGUIADDONS_EXPORT QIcon addOverlay(const QIcon &icon, const QIcon &overlay, Qt::Corner position); + +/** + * Adds \a overlays over the \a icon + * + * The \a overlays is a QHash of Qt::Corner and QIcon. The Qt::Corner value + * decides where the overlay icon will be painted, the QIcon value + * is the overlay icon to be painted. + * + * The overlay icon is scaled down to 1/3 or 1/4 (depending on the icon size) + * and placed in one of the corners of the base icon. + */ +KGUIADDONS_EXPORT QIcon addOverlays(const QIcon &icon, const QHash &overlays); +} + +#endif // KICONUTILS_H diff --git a/src/util/kimagecache.h b/src/util/kimagecache.h new file mode 100644 index 0000000..20f8013 --- /dev/null +++ b/src/util/kimagecache.h @@ -0,0 +1,218 @@ +/* This file is part of the KDE project. + SPDX-FileCopyrightText: 2010 Michael Pyne + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#ifndef KIMAGECACHE_H +#define KIMAGECACHE_H + +// check that KGUIADDONS_LIB is defined in case the application is not using CMake +// (if KGUIADDONS_LIB is not defined, we cannot assume that KCOREADDONS_LIB not being +// defined means that we are not linked against KCoreAddons) +#if defined(KGUIADDONS_LIB) && !defined(KCOREADDONS_LIB) +#ifdef __GNUC__ +#warning "KImageCache requires KF5CoreAddons (for kshareddatacache.h)" +#else +#pragma message("KImageCache requires KF5CoreAddons (for kshareddatacache.h)") +#endif +#endif + +#include +#include +#include +#include + +#define KImageCache KSharedPixmapCacheMixin + +/** + * @brief A simple wrapping layer over KSharedDataCache to support caching + * images and pixmaps. + * + * This class can be used to share images between different processes, which + * is useful when it is known that such images will be used across many + * processes, or when creating the image is expensive. + * + * In addition, the class also supports caching QPixmaps in a single + * process using the setPixmapCaching() function. + * + * Tips for use: If you already have QPixmaps that you intend to use, and + * you do not need access to the actual image data, then try to store and + * retrieve QPixmaps for use. + * + * On the other hand, if you will need to store and retrieve actual image + * data (to modify the image after retrieval for instance) then you should + * use QImage to save the conversion cost from QPixmap to QImage. + * + * KSharedPixmapCacheMixin is a subclass of KSharedDataCache, so all of the methods that + * can be used with KSharedDataCache can be used with KSharedPixmapCacheMixin, + * with the exception of KSharedDataCache::insert() and + * KSharedDataCache::find(). + * + * @author Michael Pyne + * @since 4.5 + */ +template +class KSharedPixmapCacheMixin : public T, private KLocalImageCacheImplementation +{ +public: + /** + * Constructs an image cache, named by @p cacheName, with a default + * size of @p defaultCacheSize. + * + * @param cacheName Name of the cache to use. + * @param defaultCacheSize The default size, in bytes, of the cache. + * The actual on-disk size will be slightly larger. If the cache already + * exists, it will not be resized. If it is required to resize the + * cache then use the deleteCache() function to remove that cache first. + * @param expectedItemSize The expected general size of the items to be + * added to the image cache, in bytes. Use 0 if you just want a default + * item size. + */ + KSharedPixmapCacheMixin(const QString &cacheName, + unsigned defaultCacheSize, + unsigned expectedItemSize = 0) + : T(cacheName, defaultCacheSize, expectedItemSize), + KLocalImageCacheImplementation(defaultCacheSize) {} + + /** + * Inserts the pixmap given by @p pixmap to the cache, accessible with + * @p key. The pixmap must be converted to a QImage in order to be stored + * into shared memory. In order to prevent unnecessary conversions from + * taking place @p pixmap will also be cached (but not in shared + * memory) and would be accessible using findPixmap() if pixmap caching is + * enabled. + * + * @param key Name to access @p pixmap with. + * @param pixmap The pixmap to add to the cache. + * @return true if the pixmap was successfully cached, false otherwise. + * @see setPixmapCaching() + */ + bool insertPixmap(const QString &key, const QPixmap &pixmap) + { + insertLocalPixmap(key, pixmap); + + // One thing to think about is only inserting things to the shared cache + // that are frequently used. But that would require tracking the use count + // in our local cache too, which I think is probably too much work. + + return insertImage(key, pixmap.toImage()); + } + + /** + * Inserts the @p image into the shared cache, accessible with @p key. This + * variant is preferred over insertPixmap() if your source data is already a + * QImage, if it is essential that the image be in shared memory (such as + * for SVG icons which have a high render time), or if it will need to be + * in QImage form after it is retrieved from the cache. + * + * @param key Name to access @p image with. + * @param image The image to add to the shared cache. + * @return true if the image was successfully cached, false otherwise. + */ + bool insertImage(const QString &key, const QImage &image) + { + if (this->insert(key, serializeImage(image))) { + updateModifiedTime(); + return true; + } + + return false; + } + + /** + * Copies the cached pixmap identified by @p key to @p destination. If no such + * pixmap exists @p destination is unchanged. + * + * @return true if the pixmap identified by @p key existed, false otherwise. + * @see setPixmapCaching() + */ + bool findPixmap(const QString &key, QPixmap *destination) const + { + if (findLocalPixmap(key, destination)) { + return true; + } + + QByteArray cachedData; + if (!this->find(key, &cachedData) || cachedData.isNull()) { + return false; + } + + if (destination) { + destination->loadFromData(cachedData, "PNG"); + + // Manually re-insert to pixmap cache if we'll be using this one. + insertLocalPixmap(key, *destination); + } + + return true; + } + + /** + * Copies the cached image identified by @p key to @p destination. If no such + * image exists @p destination is unchanged. + * + * @return true if the image identified by @p key existed, false otherwise. + */ + bool findImage(const QString &key, QImage *destination) const + { + QByteArray cachedData; + if (!this->find(key, &cachedData) || cachedData.isNull()) { + return false; + } + + if (destination) { + destination->loadFromData(cachedData, "PNG"); + } + + return true; + } + + /** + * Removes all entries from the cache. In addition any cached pixmaps (as per + * setPixmapCaching()) are also removed. + */ + void clear() + { + clearLocalCache(); + T::clear(); + } + + /** + * @return The time that an image or pixmap was last inserted into a cache. + */ + using KLocalImageCacheImplementation::lastModifiedTime; + + /** + * @return if QPixmaps added with insertPixmap() will be stored in a local + * pixmap cache as well as the shared image cache. The default is to cache + * pixmaps locally. + */ + using KLocalImageCacheImplementation::pixmapCaching; + + /** + * Enables or disables local pixmap caching. If it is anticipated that a pixmap + * will be frequently needed then this can actually save memory overall since the + * X server or graphics card will not have to store duplicate copies of the same + * image. + * + * @param enable Enables pixmap caching if true, disables otherwise. + */ + using KLocalImageCacheImplementation::setPixmapCaching; + + /** + * @return The highest memory size in bytes to be used by cached pixmaps. + * @since 4.6 + */ + using KLocalImageCacheImplementation::pixmapCacheLimit; + + /** + * Sets the highest memory size the pixmap cache should use. + * + * @param size The size in bytes + * @since 4.6 + */ + using KLocalImageCacheImplementation::setPixmapCacheLimit; +}; + +#endif /* KIMAGECACHE_H */ diff --git a/src/util/klocalimagecacheimpl.cpp b/src/util/klocalimagecacheimpl.cpp new file mode 100644 index 0000000..31453b9 --- /dev/null +++ b/src/util/klocalimagecacheimpl.cpp @@ -0,0 +1,149 @@ +/* This file is part of the KDE project. + SPDX-FileCopyrightText: 2010 Michael Pyne + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#include "klocalimagecacheimpl.h" + +#include +#include +#include +#include + +#include +#include + +/** + * This is a QObject subclass so we can catch the signal that the application is about + * to close and properly release any QPixmaps we have cached. + */ +class KLocalImageCacheImplementationPrivate : public QObject +{ + Q_OBJECT + +public: + KLocalImageCacheImplementationPrivate(QObject *parent = nullptr) + : QObject(parent) + , timestamp(QDateTime::currentDateTime()) + { + QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, + this, &KLocalImageCacheImplementationPrivate::clearPixmaps); + } + + /** + * Inserts a pixmap into the pixmap cache if the pixmap cache is enabled, with + * weighting based on image size and bit depth. + */ + bool insertPixmap(const QString &key, QPixmap *pixmap) + { + if (enablePixmapCaching && pixmap && !pixmap->isNull()) { + // "cost" parameter is based on both image size and depth to make cost + // based on size in bytes instead of area on-screen. + return pixmapCache.insert(key, pixmap, + pixmap->width() * pixmap->height() * pixmap->depth() / 8); + } + + return false; + } + +public Q_SLOTS: + void clearPixmaps() + { + pixmapCache.clear(); + } + +public: + QDateTime timestamp; + + /** + * This is used to cache pixmaps as they are inserted, instead of always + * converting to image data and storing that in shared memory. + */ + QCache pixmapCache; + + bool enablePixmapCaching = true; +}; + +KLocalImageCacheImplementation::KLocalImageCacheImplementation(unsigned defaultCacheSize) + : d(new KLocalImageCacheImplementationPrivate) +{ + // Use at least 16 KiB for the pixmap cache + d->pixmapCache.setMaxCost(qMax(defaultCacheSize / 8, (unsigned int) 16384)); +} + +KLocalImageCacheImplementation::~KLocalImageCacheImplementation() +{ + delete d; +} + +void KLocalImageCacheImplementation::updateModifiedTime() +{ + d->timestamp = QDateTime::currentDateTime(); +} + +QByteArray KLocalImageCacheImplementation::serializeImage(const QImage &image) const +{ + QBuffer buffer; + buffer.open(QBuffer::WriteOnly); + image.save(&buffer, "PNG"); + return buffer.buffer(); +} + +bool KLocalImageCacheImplementation::insertLocalPixmap(const QString &key, const QPixmap &pixmap) const +{ + return d->insertPixmap(key, new QPixmap(pixmap)); +} + +bool KLocalImageCacheImplementation::findLocalPixmap(const QString &key, QPixmap *destination) const +{ + if (d->enablePixmapCaching) { + QPixmap *cachedPixmap = d->pixmapCache.object(key); + if (cachedPixmap) { + if (destination) { + *destination = *cachedPixmap; + } + + return true; + } + } + + return false; +} + +void KLocalImageCacheImplementation::clearLocalCache() +{ + d->pixmapCache.clear(); +} + +QDateTime KLocalImageCacheImplementation::lastModifiedTime() const +{ + return d->timestamp; +} + +bool KLocalImageCacheImplementation::pixmapCaching() const +{ + return d->enablePixmapCaching; +} + +void KLocalImageCacheImplementation::setPixmapCaching(bool enable) +{ + if (enable != d->enablePixmapCaching) { + d->enablePixmapCaching = enable; + if (!enable) { + d->pixmapCache.clear(); + } + } +} + +int KLocalImageCacheImplementation::pixmapCacheLimit() const +{ + return d->pixmapCache.maxCost(); +} + +void KLocalImageCacheImplementation::setPixmapCacheLimit(int size) +{ + d->pixmapCache.setMaxCost(size); +} + +#include "klocalimagecacheimpl.moc" diff --git a/src/util/klocalimagecacheimpl.h b/src/util/klocalimagecacheimpl.h new file mode 100644 index 0000000..4cd3151 --- /dev/null +++ b/src/util/klocalimagecacheimpl.h @@ -0,0 +1,54 @@ +/* This file is part of the KDE project. + SPDX-FileCopyrightText: 2010 Michael Pyne + + SPDX-License-Identifier: LGPL-2.0-only +*/ + +#ifndef KLOCALIMAGECACHEIMPL_H +#define KLOCALIMAGECACHEIMPL_H + +#include +class KLocalImageCacheImplementationPrivate; + +class QImage; +class QPixmap; +class QByteArray; +class QDateTime; +class QString; + +/** + * You are not supposed to use this class directly, use KImageCache instead + * + * @internal + */ +class KGUIADDONS_EXPORT KLocalImageCacheImplementation +{ +private: + explicit KLocalImageCacheImplementation(unsigned defaultCacheSize); + +public: + virtual ~KLocalImageCacheImplementation(); + + QDateTime lastModifiedTime() const; + + bool pixmapCaching() const; + void setPixmapCaching(bool enable); + + int pixmapCacheLimit() const; + void setPixmapCacheLimit(int size); + +protected: + void updateModifiedTime(); + QByteArray serializeImage(const QImage &image) const; + + bool insertLocalPixmap(const QString &key, const QPixmap &pixmap) const; + bool findLocalPixmap(const QString &key, QPixmap *destination) const; + void clearLocalCache(); + +private: + KLocalImageCacheImplementationPrivate *const d; ///< @internal + + template friend class KSharedPixmapCacheMixin; +}; + +#endif /* KLOCALIMAGECACHEIMPL_H */ diff --git a/src/util/kmodifierkeyinfo.cpp b/src/util/kmodifierkeyinfo.cpp new file mode 100644 index 0000000..45538ad --- /dev/null +++ b/src/util/kmodifierkeyinfo.cpp @@ -0,0 +1,84 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "kmodifierkeyinfo.h" +#include "kmodifierkeyinfoprovider_p.h" +#include + +#include +#include + +KModifierKeyInfoProvider* createProvider() +{ + QPluginLoader loader(QStringLiteral("kf5/kguiaddons/kmodifierkey/kmodifierkey_")+qGuiApp->platformName()); + auto instance = dynamic_cast(loader.instance()); + if (instance) + return instance; + qCWarning(KGUIADDONS_LOG) << "Error: could not load plugin for platform" << loader.fileName() << "error:" << loader.errorString() << loader.instance(); + return new KModifierKeyInfoProvider; +} + +KModifierKeyInfo::KModifierKeyInfo(QObject *parent) + : QObject(parent), p(createProvider()) +{ + connect(p.data(), &KModifierKeyInfoProvider::keyPressed, + this, &KModifierKeyInfo::keyPressed); + connect(p.data(), &KModifierKeyInfoProvider::keyLatched, + this, &KModifierKeyInfo::keyLatched); + connect(p.data(), &KModifierKeyInfoProvider::keyLocked, + this, &KModifierKeyInfo::keyLocked); + connect(p.data(), &KModifierKeyInfoProvider::buttonPressed, + this, &KModifierKeyInfo::buttonPressed); + connect(p.data(), &KModifierKeyInfoProvider::keyAdded, + this, &KModifierKeyInfo::keyAdded); + connect(p.data(), &KModifierKeyInfoProvider::keyRemoved, + this, &KModifierKeyInfo::keyRemoved); +} + +KModifierKeyInfo::~KModifierKeyInfo() +{ +} + +bool KModifierKeyInfo::knowsKey(Qt::Key key) const +{ + return p->knowsKey(key); +} + +const QList KModifierKeyInfo::knownKeys() const +{ + return p->knownKeys(); +} + +bool KModifierKeyInfo::isKeyPressed(Qt::Key key) const +{ + return p->isKeyPressed(key); +} + +bool KModifierKeyInfo::isKeyLatched(Qt::Key key) const +{ + return p->isKeyLatched(key); +} + +bool KModifierKeyInfo::setKeyLatched(Qt::Key key, bool latched) +{ + return p->setKeyLatched(key, latched); +} + +bool KModifierKeyInfo::isKeyLocked(Qt::Key key) const +{ + return p->isKeyLocked(key); +} + +bool KModifierKeyInfo::setKeyLocked(Qt::Key key, bool locked) +{ + return p->setKeyLocked(key, locked); +} + +bool KModifierKeyInfo::isButtonPressed(Qt::MouseButton button) const +{ + return p->isButtonPressed(button); +} + diff --git a/src/util/kmodifierkeyinfo.h b/src/util/kmodifierkeyinfo.h new file mode 100644 index 0000000..eef9746 --- /dev/null +++ b/src/util/kmodifierkeyinfo.h @@ -0,0 +1,178 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KMODIFIERKEYINFO_H +#define KMODIFIERKEYINFO_H + +#include + +#include +#include + +class KModifierKeyInfoProvider; + +/** + * @class KModifierKeyInfo kmodifierkeyinfo.h KModifierKeyInfo + * + * Get information about the state of the keyboard's modifier keys. + * + * This class provides cross-platform information about the state of the + * keyboard's modifier keys and the mouse buttons and allows to change the + * state as well. + * + * It recognizes two states a key can be in: + * @li @em locked: eg. caps-locked (a.k.a. toggled) + * @li @em latched the key is temporarily locked but will be unlocked upon + * the next keypress. + * + * An application can either query the states synchronously (isKeyLatched(), + * isKeyLocked()) or connect to KModifierKeyInfo's signals to be notified about + * changes (keyLatched(), keyLocked()). + */ +class KGUIADDONS_EXPORT KModifierKeyInfo : public QObject +{ + Q_OBJECT + +public: + /** + * Default constructor + */ + explicit KModifierKeyInfo(QObject *parent = nullptr); + + /** + * Destructor + */ + virtual ~KModifierKeyInfo(); + + /** + * Check if a key is known by the underlying window system and can be queried. + * + * @param key The key to check + * @return true if the key is available, false if it is unknown + */ + bool knowsKey(Qt::Key key) const; + + /** + * Get a list of known keys. + * + * @return A list of known keys of which states will be reported. + */ + const QList knownKeys() const; + + /** + * Synchronously check if a key is pressed. + * + * @param key The key to check + * @return true if the key is pressed, false if the key is not pressed or unknown. + * @see isKeyLatched, @see isKeyLocked, @see keyPressed + */ + bool isKeyPressed(Qt::Key key) const; + + /** + * Synchronously check if a key is latched. + * + * @param key The key to check + * @return true if the key is latched, false if the key is not latched or unknown. + * @see isKeyPressed, @see isKeyLocked, @see keyLatched + */ + bool isKeyLatched(Qt::Key key) const; + + /** + * Set the latched state of a key. + * + * @param key The key to latch + * @param latched true to latch the key, false to unlatch it. + * @return false if the key is unknown. true doesn't guarantee you the + * operation worked. + */ + bool setKeyLatched(Qt::Key key, bool latched); + + /** + * Synchronously check if a key is locked. + * + * @param key The key to check + * @return true if the key is locked, false if the key is not locked or unknown. + * @see isKeyPressed, @see isKeyLatched, @see keyLocked + */ + bool isKeyLocked(Qt::Key key) const; + + /** + * Set the locked state of a key. + * + * @param key The key to lock + * @param latched true to lock the key, false to unlock it. + * @return false if the key is unknown. true doesn't guarantee you the + * operation worked. + */ + bool setKeyLocked(Qt::Key key, bool locked); + + /** + * Synchronously check if a mouse button is pressed. + * + * @param button The mouse button to check + * @return true if the mouse button is pressed, false if the mouse button + * is not pressed or its state is unknown. + */ + bool isButtonPressed(Qt::MouseButton button) const; + +Q_SIGNALS: + /** + * This signal is emitted whenever the pressed state of a key changes + * (key press or key release). + * + * @param key The key that changed state + * @param pressed true if the key is now pressed, false if is released. + */ + void keyPressed(Qt::Key key, bool pressed); + + /** + * This signal is emitted whenever the latched state of a key changes. + * + * @param key The key that changed state + * @param latched true if the key is now latched, false if it isn't + */ + void keyLatched(Qt::Key key, bool latched); + + /** + * This signal is emitted whenever the locked state of a key changes. + * + * @param key The key that changed state + * @param locked true if the key is now locked, false if it isn't + */ + void keyLocked(Qt::Key key, bool locked); + + /** + * This signal is emitted whenever the pressed state of a mouse button + * changes (mouse button press or release). + * + * @param button The mouse button that changed state + * @param pressed true if the mouse button is now pressed, false if + * is released. + */ + void buttonPressed(Qt::MouseButton button, bool pressed); + + /** + * This signal is emitted whenever a new modifier is found due to + * the keyboard mapping changing. + * + * @param key The key that was discovered + */ + void keyAdded(Qt::Key key); + + /** + * This signal is emitted whenever a previously known modifier no + * longer exists due to the keyboard mapping changing. + * + * @param key The key that vanished + */ + void keyRemoved(Qt::Key key); + +private: + Q_DISABLE_COPY(KModifierKeyInfo) + QExplicitlySharedDataPointer const p; // krazy:exclude=dpointer +}; + +#endif diff --git a/src/util/kmodifierkeyinfoprovider.cpp b/src/util/kmodifierkeyinfoprovider.cpp new file mode 100644 index 0000000..76ce1dc --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider.cpp @@ -0,0 +1,93 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "kmodifierkeyinfoprovider_p.h" + +KModifierKeyInfoProvider::KModifierKeyInfoProvider() + : QObject(nullptr) +{ +} + +KModifierKeyInfoProvider::~KModifierKeyInfoProvider() +{ +} + +bool KModifierKeyInfoProvider::setKeyLatched(Qt::Key key, bool latched) +{ + Q_UNUSED(key); + Q_UNUSED(latched); + return false; +} + +bool KModifierKeyInfoProvider::setKeyLocked(Qt::Key key, bool locked) +{ + Q_UNUSED(key); + Q_UNUSED(locked); + return false; +} + +bool KModifierKeyInfoProvider::isKeyPressed(Qt::Key key) const +{ + auto it = m_modifierStates.constFind(key); + if (it != m_modifierStates.constEnd()) { + return *it & Pressed; + } + return false; +} + +bool KModifierKeyInfoProvider::isKeyLatched(Qt::Key key) const +{ + auto it = m_modifierStates.constFind(key); + if (it != m_modifierStates.constEnd()) { + return *it & Latched; + } + return false; +} + +bool KModifierKeyInfoProvider::isKeyLocked(Qt::Key key) const +{ + auto it = m_modifierStates.constFind(key); + if (it != m_modifierStates.constEnd()) { + return *it & Locked; + } + return false; +} + +bool KModifierKeyInfoProvider::isButtonPressed(Qt::MouseButton button) const +{ + if (m_buttonStates.contains(button)) { + return m_buttonStates[button]; + } + return false; +} + +bool KModifierKeyInfoProvider::knowsKey(Qt::Key key) const +{ + return m_modifierStates.contains(key); +} + +const QList KModifierKeyInfoProvider::knownKeys() const +{ + return m_modifierStates.keys(); +} + +void KModifierKeyInfoProvider::stateUpdated(Qt::Key key, KModifierKeyInfoProvider::ModifierStates newState) +{ + auto &state = m_modifierStates[key]; + if (newState != state) { + const auto difference = (newState ^ state); + state = newState; + if (difference & Pressed) { + emit keyPressed(key, newState & Pressed); + } + if (difference & Latched) { + emit keyLatched(key, newState & Latched); + } + if (difference & Locked) { + emit keyLocked(key, newState & Locked); + } + } +} diff --git a/src/util/kmodifierkeyinfoprovider_p.h b/src/util/kmodifierkeyinfoprovider_p.h new file mode 100644 index 0000000..c4a078a --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_p.h @@ -0,0 +1,115 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KMODIFIERKEYINFOPROVIDER_P_H +#define KMODIFIERKEYINFOPROVIDER_P_H + +#include +#include +#include +#include "kguiaddons_export.h" + +/** + * Background class that implements the behaviour of KModifierKeyInfo for + * the different supported platforms. + * @internal + */ +class KGUIADDONS_EXPORT KModifierKeyInfoProvider : public QObject, public QSharedData +{ + Q_OBJECT + +public: + enum ModifierState { + Nothing = 0x0, + Pressed = 0x1, + Latched = 0x2, + Locked = 0x4 + }; + Q_ENUM(ModifierState); + Q_DECLARE_FLAGS(ModifierStates, ModifierState) + + KModifierKeyInfoProvider(); + ~KModifierKeyInfoProvider() override; + + /** + * Detect if a key is pressed. + * @param key Modifier key to query + * @return true if the key is pressed, false if it isn't. + */ + bool isKeyPressed(Qt::Key key) const; + + /** + * Detect if a key is latched. + * @param key Modifier key to query + * @return true if the key is latched, false if it isn't. + */ + bool isKeyLatched(Qt::Key key) const; + + /** + * Set the latched state of a key. + * @param key Modifier to set the latched state for + * @param latched true to latch the key, false to unlatch it + * @return true if the key is known, false else + */ + virtual bool setKeyLatched(Qt::Key key, bool latched); + + /** + * Detect if a key is locked. + * @param key Modifier key to query + * @return true if the key is locked, false if it isn't. + */ + bool isKeyLocked(Qt::Key key) const; + + /** + * Set the locked state of a key. + * @param key Modifier to set the locked state for + * @param latched true to lock the key, false to unlock it + * @return true if the key is known, false else + */ + virtual bool setKeyLocked(Qt::Key key, bool locked); + + /** + * Check if a mouse button is pressed. + * @param button Mouse button to check + * @return true if pressed, false else + */ + bool isButtonPressed(Qt::MouseButton button) const; + + /** + * Check if a key is known/can be queried + * @param key Modifier key to check + * @return true if the key is known, false if it isn't. + */ + bool knowsKey(Qt::Key key) const; + + /** + * Get a list of known keys + * @return List of known keys. + */ + const QList knownKeys() const; + +Q_SIGNALS: + void keyLatched(Qt::Key key, bool state); + void keyLocked(Qt::Key key, bool state); + void keyPressed(Qt::Key key, bool state); + void buttonPressed(Qt::MouseButton button, bool state); + void keyAdded(Qt::Key key); + void keyRemoved(Qt::Key key); + +protected: + void stateUpdated(Qt::Key key, KModifierKeyInfoProvider::ModifierStates state); + + // the state of each known modifier + QHash m_modifierStates; + + // the state of each known mouse button + QHash m_buttonStates; +}; + +Q_DECLARE_INTERFACE(KModifierKeyInfoProvider, "org.kde.kguiaddons.KModifierKeyInfoProvider") +Q_DECLARE_OPERATORS_FOR_FLAGS(KModifierKeyInfoProvider::ModifierStates) + +#endif diff --git a/src/util/kmodifierkeyinfoprovider_xcb.cpp b/src/util/kmodifierkeyinfoprovider_xcb.cpp new file mode 100644 index 0000000..41abba7 --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_xcb.cpp @@ -0,0 +1,333 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + SPDX-FileCopyrightText: 2013 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "kmodifierkeyinfo.h" +#include "kmodifierkeyinfoprovider_xcb.h" + +#include +#include + +#define XK_MISCELLANY +#define XK_XKB_KEYS +#include +#include +#include + +struct ModifierDefinition { + ModifierDefinition(Qt::Key _key, unsigned int _mask, const char *_name, KeySym _keysym) + { + key = _key; + mask = _mask; + name = _name; + keysym = _keysym; + } + Qt::Key key; + unsigned int mask; + const char *name; // virtual modifier name + KeySym keysym; +}; + +/* + * Get the real modifiers related to a virtual modifier. + */ +unsigned int xkbVirtualModifier(XkbDescPtr xkb, const char *name) +{ + Q_ASSERT(xkb != nullptr); + + unsigned int mask = 0; + bool nameEqual; + for (int i = 0; i < XkbNumVirtualMods; ++i) { + char *modStr = XGetAtomName(xkb->dpy, xkb->names->vmods[i]); + if (modStr != nullptr) { + nameEqual = (strcmp(name, modStr) == 0); + XFree(modStr); + if (nameEqual) { + XkbVirtualModsToReal(xkb, 1 << i, &mask); + break; + } + } + } + return mask; +} + +KModifierKeyInfoProviderXcb::KModifierKeyInfoProviderXcb() + : KModifierKeyInfoProvider() + , m_xkbEv(0) + , m_xkbAvailable(false) +{ + if (qApp) { + if (qApp->platformName() == QLatin1String("xcb")) { + int code, xkberr, maj, min; + m_xkbAvailable = XkbQueryExtension(QX11Info::display(), &code, &m_xkbEv, &xkberr, &maj, &min); + } + } + if (m_xkbAvailable) { + XkbSelectEvents(QX11Info::display(), XkbUseCoreKbd, + XkbStateNotifyMask | XkbMapNotifyMask, + XkbStateNotifyMask | XkbMapNotifyMask); + unsigned long int stateMask = XkbModifierStateMask | XkbModifierBaseMask | + XkbModifierLatchMask | XkbModifierLockMask | + XkbPointerButtonMask; + XkbSelectEventDetails(QX11Info::display(), XkbUseCoreKbd, XkbStateNotifyMask, + stateMask, stateMask); + } + + xkbUpdateModifierMapping(); + + // add known pointer buttons + m_xkbButtons.insert(Qt::LeftButton, Button1Mask); + m_xkbButtons.insert(Qt::MiddleButton, Button2Mask); + m_xkbButtons.insert(Qt::RightButton, Button3Mask); + m_xkbButtons.insert(Qt::XButton1, Button4Mask); + m_xkbButtons.insert(Qt::XButton2, Button5Mask); + + // get the initial state + if (m_xkbAvailable) { + XkbStateRec state; + XkbGetState(QX11Info::display(), XkbUseCoreKbd, &state); + xkbModifierStateChanged(state.mods, state.latched_mods, state.locked_mods); + xkbButtonStateChanged(state.ptr_buttons); + + QCoreApplication::instance()->installNativeEventFilter(this); + } +} + +KModifierKeyInfoProviderXcb::~KModifierKeyInfoProviderXcb() +{ + if (m_xkbAvailable) { + QCoreApplication::instance()->removeNativeEventFilter(this); + } +} + +bool KModifierKeyInfoProviderXcb::setKeyLatched(Qt::Key key, bool latched) +{ + if (!m_xkbModifiers.contains(key)) { + return false; + } + + return XkbLatchModifiers(QX11Info::display(), XkbUseCoreKbd, + m_xkbModifiers[key], latched ? m_xkbModifiers[key] : 0); +} + +bool KModifierKeyInfoProviderXcb::setKeyLocked(Qt::Key key, bool locked) +{ + if (!m_xkbModifiers.contains(key)) { + return false; + } + + return XkbLockModifiers(QX11Info::display(), XkbUseCoreKbd, + m_xkbModifiers[key], locked ? m_xkbModifiers[key] : 0); +} + +// HACK: xcb-xkb is not yet a public part of xcb. Because of that we have to include the event structure. +namespace +{ +typedef struct _xcb_xkb_map_notify_event_t { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + uint8_t ptrBtnActions; + uint16_t changed; + xcb_keycode_t minKeyCode; + xcb_keycode_t maxKeyCode; + uint8_t firstType; + uint8_t nTypes; + xcb_keycode_t firstKeySym; + uint8_t nKeySyms; + xcb_keycode_t firstKeyAct; + uint8_t nKeyActs; + xcb_keycode_t firstKeyBehavior; + uint8_t nKeyBehavior; + xcb_keycode_t firstKeyExplicit; + uint8_t nKeyExplicit; + xcb_keycode_t firstModMapKey; + uint8_t nModMapKeys; + xcb_keycode_t firstVModMapKey; + uint8_t nVModMapKeys; + uint16_t virtualMods; + uint8_t pad0[2]; +} _xcb_xkb_map_notify_event_t; +typedef struct _xcb_xkb_state_notify_event_t { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + uint8_t mods; + uint8_t baseMods; + uint8_t latchedMods; + uint8_t lockedMods; + uint8_t group; + int16_t baseGroup; + int16_t latchedGroup; + uint8_t lockedGroup; + uint8_t compatState; + uint8_t grabMods; + uint8_t compatGrabMods; + uint8_t lookupMods; + uint8_t compatLoockupMods; + uint16_t ptrBtnState; + uint16_t changed; + xcb_keycode_t keycode; + uint8_t eventType; + uint8_t requestMajor; + uint8_t requestMinor; +} _xcb_xkb_state_notify_event_t; +typedef union { + /* All XKB events share these fields. */ + struct { + uint8_t response_type; + uint8_t xkbType; + uint16_t sequence; + xcb_timestamp_t time; + uint8_t deviceID; + } any; + _xcb_xkb_map_notify_event_t map_notify; + _xcb_xkb_state_notify_event_t state_notify; +} _xkb_event; +} + +bool KModifierKeyInfoProviderXcb::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) +{ + Q_UNUSED(result) + if (!m_xkbAvailable || eventType != "xcb_generic_event_t") { + return false; + } + xcb_generic_event_t *event = static_cast(message); + if ((event->response_type & ~0x80) == m_xkbEv + XkbEventCode) { + _xkb_event *kbevt = reinterpret_cast<_xkb_event *>(event); + unsigned int stateMask = XkbModifierStateMask | XkbModifierBaseMask | + XkbModifierLatchMask | XkbModifierLockMask; + if (kbevt->any.xkbType == XkbMapNotify) { + xkbUpdateModifierMapping(); + } else if (kbevt->any.xkbType == XkbStateNotify) { + if (kbevt->state_notify.changed & stateMask) { + xkbModifierStateChanged(kbevt->state_notify.mods, kbevt->state_notify.latchedMods, + kbevt->state_notify.lockedMods); + } else if (kbevt->state_notify.changed & XkbPointerButtonMask) { + xkbButtonStateChanged(kbevt->state_notify.ptrBtnState); + } + } + } + return false; +} + +void KModifierKeyInfoProviderXcb::xkbModifierStateChanged(unsigned char mods, + unsigned char latched_mods, + unsigned char locked_mods) +{ + // detect keyboard modifiers + ModifierStates newState; + + QHash::const_iterator it; + QHash::const_iterator end = m_xkbModifiers.constEnd(); + for (it = m_xkbModifiers.constBegin(); it != end; ++it) { + if (!m_modifierStates.contains(it.key())) { + continue; + } + newState = Nothing; + + // determine the new state + if (mods & it.value()) { + newState |= Pressed; + } + if (latched_mods & it.value()) { + newState |= Latched; + } + if (locked_mods & it.value()) { + newState |= Locked; + } + + stateUpdated(it.key(), newState); + } +} + +void KModifierKeyInfoProviderXcb::xkbButtonStateChanged(unsigned short ptr_buttons) +{ + // detect mouse button states + bool newButtonState; + + QHash::const_iterator it; + QHash::const_iterator end = m_xkbButtons.constEnd(); + for (it = m_xkbButtons.constBegin(); it != end; ++it) { + newButtonState = (ptr_buttons & it.value()); + if (newButtonState != m_buttonStates[it.key()]) { + m_buttonStates[it.key()] = newButtonState; + emit buttonPressed(it.key(), newButtonState); + } + } +} + +void KModifierKeyInfoProviderXcb::xkbUpdateModifierMapping() +{ + if (!m_xkbAvailable) { + return; + } + m_xkbModifiers.clear(); + + QList srcModifiers; + srcModifiers << ModifierDefinition(Qt::Key_Shift, ShiftMask, nullptr, 0) + << ModifierDefinition(Qt::Key_Control, ControlMask, nullptr, 0) + << ModifierDefinition(Qt::Key_Alt, 0, "Alt", XK_Alt_L) + // << { 0, 0, I18N_NOOP("Win"), "superkey", "" } + << ModifierDefinition(Qt::Key_Meta, 0, "Meta", XK_Meta_L) + << ModifierDefinition(Qt::Key_Super_L, 0, "Super", XK_Super_L) + << ModifierDefinition(Qt::Key_Hyper_L, 0, "Hyper", XK_Hyper_L) + << ModifierDefinition(Qt::Key_AltGr, 0, "AltGr", 0) + << ModifierDefinition(Qt::Key_NumLock, 0, "NumLock", XK_Num_Lock) + << ModifierDefinition(Qt::Key_CapsLock, LockMask, nullptr, 0) + << ModifierDefinition(Qt::Key_ScrollLock, 0, "ScrollLock", XK_Scroll_Lock); + + XkbDescPtr xkb = XkbGetKeyboard(QX11Info::display(), XkbAllComponentsMask, XkbUseCoreKbd); + + QList::const_iterator it; + QList::const_iterator end = srcModifiers.constEnd(); + for (it = srcModifiers.constBegin(); it != end; ++it) { + unsigned int mask = it->mask; + if (mask == 0 && xkb != nullptr) { + // try virtual modifier first + if (it->name != nullptr) { + mask = xkbVirtualModifier(xkb, it->name); + } + if (mask == 0 && it->keysym != 0) { + mask = XkbKeysymToModifiers(QX11Info::display(), it->keysym); + } else if (mask == 0) { + // special case for AltGr + mask = XkbKeysymToModifiers(QX11Info::display(), XK_Mode_switch) | + XkbKeysymToModifiers(QX11Info::display(), XK_ISO_Level3_Shift) | + XkbKeysymToModifiers(QX11Info::display(), XK_ISO_Level3_Latch) | + XkbKeysymToModifiers(QX11Info::display(), XK_ISO_Level3_Lock); + } + } + + if (mask != 0) { + m_xkbModifiers.insert(it->key, mask); + // previously unknown modifier + if (!m_modifierStates.contains(it->key)) { + m_modifierStates.insert(it->key, Nothing); + emit keyAdded(it->key); + } + } + } + + // remove modifiers which are no longer available + QMutableHashIterator i(m_modifierStates); + while (i.hasNext()) { + i.next(); + if (!m_xkbModifiers.contains(i.key())) { + Qt::Key key = i.key(); + i.remove(); + emit keyRemoved(key); + } + } + + if (xkb != nullptr) { + XkbFreeKeyboard(xkb, 0, true); + } +} diff --git a/src/util/kmodifierkeyinfoprovider_xcb.h b/src/util/kmodifierkeyinfoprovider_xcb.h new file mode 100644 index 0000000..a7a91b5 --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_xcb.h @@ -0,0 +1,41 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + SPDX-FileCopyrightText: 2013 Martin Gräßlin + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#ifndef KMODIFIERKEYINFOPROVIDERXCB_H +#define KMODIFIERKEYINFOPROVIDERXCB_H + +#include "kmodifierkeyinfoprovider_p.h" +#include +class KModifierKeyInfoProviderXcb : public KModifierKeyInfoProvider, public QAbstractNativeEventFilter +{ +Q_OBJECT +Q_PLUGIN_METADATA(IID "org.kde.kguiaddons.KModifierKeyInfoProvider.XCB") +public: + KModifierKeyInfoProviderXcb(); + ~KModifierKeyInfoProviderXcb(); + + bool setKeyLatched(Qt::Key key, bool latched) override; + bool setKeyLocked(Qt::Key key, bool locked) override; + + bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override; + + void xkbUpdateModifierMapping(); + void xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, + unsigned char locked_mods); + void xkbButtonStateChanged(unsigned short ptr_buttons); + +private: + int m_xkbEv; + bool m_xkbAvailable; + + // maps a Qt::Key to a modifier mask + QHash m_xkbModifiers; + // maps a Qt::MouseButton to a button mask + QHash m_xkbButtons; +}; + +#endif diff --git a/src/util/urlhandler.cpp b/src/util/urlhandler.cpp new file mode 100644 index 0000000..02de207 --- /dev/null +++ b/src/util/urlhandler.cpp @@ -0,0 +1,70 @@ +/* + SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +class UrlHandler : public QObject +{ + Q_OBJECT +public: + explicit UrlHandler(QObject *parent = nullptr) : QObject(parent) {} + +public Q_SLOTS: + void openHelp(const QUrl &url) + { + const QString appName = QCoreApplication::applicationName(); + + QUrl u(url); + if (u.path() == QLatin1Char('/')) { + u.setPath(appName); + } + + const QString helpcenter = QStandardPaths::findExecutable(QStringLiteral("khelpcenter")); + if (!helpcenter.isEmpty()) { // use khelpcenter if it is available + QProcess::startDetached(helpcenter, QStringList(u.toString())); + return; + } + + //if khelpcenter is not available and it's a KDE application, use docs.kde.org + if (QCoreApplication::organizationDomain() == QLatin1String("kde.org")) { + QString path = url.path(); + QString docPath; + if (appName == QLatin1String("systemsettings") && path.startsWith(QLatin1String("/kcontrol"))) { + // special case for kcm modules + // e.g. "help:/kcontrol/fonts/index.html" >>> "&application=kcontrol/fonts" + docPath = path.remove(0, 1).remove(QLatin1String("/index.html")); + } else { //e.g. "help:/okular", "help:/systemsettings" + docPath = appName + QStringLiteral("&path=") + path; + } + const QUrl httpUrl(QLatin1String("https://docs.kde.org/index.php?branch=stable5&language=") + + QLocale().name() + QLatin1String("&application=") + docPath); + QDesktopServices::openUrl(httpUrl); + return; + } + + // not a KDE application + qCWarning(KGUIADDONS_LOG) << "Could not find a suitable handler for " << u.toString(); + } +}; + +Q_GLOBAL_STATIC(UrlHandler, s_handler) + +static void initializeGlobalSettings() +{ + QDesktopServices::setUrlHandler(QStringLiteral("help"), s_handler, "openHelp"); +} + +Q_COREAPP_STARTUP_FUNCTION(initializeGlobalSettings) + +#include "urlhandler.moc" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..102fb69 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,21 @@ +remove_definitions(-DQT_NO_CAST_FROM_ASCII) +remove_definitions(-DQT_NO_CAST_TO_ASCII) + +include(ECMMarkAsTest) + +find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test Widgets) + +macro(kguiaddons_executable_tests) + foreach(_testname ${ARGN}) + add_executable(${_testname} ${_testname}.cpp) + target_link_libraries(${_testname} Qt5::Widgets KF5::GuiAddons) + ecm_mark_as_test(${_testname}) + endforeach(_testname) +endmacro() + +kguiaddons_executable_tests( + kcolorcollectiontest + kmodifierkeyinfotest + openurltest + kcursorsavergui_test +) diff --git a/tests/kcolorcollectiontest.cpp b/tests/kcolorcollectiontest.cpp new file mode 100644 index 0000000..0831a71 --- /dev/null +++ b/tests/kcolorcollectiontest.cpp @@ -0,0 +1,33 @@ + +#include +#include + +#include + +#include +#include + +int main(int argc, char **argv) +{ + QApplication::setApplicationName(QStringLiteral("KColorCollectionTest")); + QApplication a(argc, argv); + + QStringList collections = KColorCollection::installedCollections(); + for (QStringList::ConstIterator it = collections.constBegin(); + it != collections.constEnd(); ++it) { + printf("Palette = %s\n", (*it).toLatin1().constData()); + + KColorCollection myColorCollection = KColorCollection(*it); + + printf("Palette Name = \"%s\"\n", myColorCollection.name().toLatin1().constData()); + printf("Description:\n\"%s\"\n", myColorCollection.description().toLatin1().constData()); + printf("Nr of Colors = %d\n", myColorCollection.count()); + for (int i = 0; i < myColorCollection.count(); i++) { + int r, g, b; + myColorCollection.color(i).getRgb(&r, &g, &b); + printf("#%d Name = \"%s\" #%02x%02x%02x\n", + i, myColorCollection.name(i).toLatin1().constData(), r, g, b); + } + } +} + diff --git a/tests/kcursorsavergui_test.cpp b/tests/kcursorsavergui_test.cpp new file mode 100644 index 0000000..048c845 --- /dev/null +++ b/tests/kcursorsavergui_test.cpp @@ -0,0 +1,44 @@ +/* + SPDX-FileCopyrightText: 2020 Laurent Montel + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ + +#include +#include +#include +#include +#include + +#include "kcursorsaver.h" + +class KCursorSaverTestWidget : public QWidget +{ + Q_OBJECT + +public: + explicit KCursorSaverTestWidget(QWidget *parent = nullptr); +}; + +KCursorSaverTestWidget::KCursorSaverTestWidget(QWidget *parent) + : QWidget(parent) +{ + QVBoxLayout *layout = new QVBoxLayout(this); + QPushButton *busyButton = new QPushButton(QStringLiteral("busy"), this); + layout->addWidget(busyButton); + connect(busyButton, &QPushButton::clicked, this, [this]() { + KCursorSaver saver(Qt::WaitCursor); + QThread::sleep(3); + }); +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + KCursorSaverTestWidget mainWidget; + mainWidget.show(); + + return app.exec(); +} + +#include "kcursorsavergui_test.moc" diff --git a/tests/kmodifierkeyinfotest.cpp b/tests/kmodifierkeyinfotest.cpp new file mode 100644 index 0000000..35f2a3e --- /dev/null +++ b/tests/kmodifierkeyinfotest.cpp @@ -0,0 +1,188 @@ +/* + SPDX-FileCopyrightText: 2009 Michael Leupold + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kmodifierkeyinfo.h" + +template +class Triple +{ +public: + Triple() {} + Triple(const A _first, const B _second, const C _third) + : first(_first), second(_second), third(_third) {}; + A first; + B second; + C third; +}; + +class TestWidget : public QWidget +{ + Q_OBJECT + +public: + TestWidget(); + +protected Q_SLOTS: + void keyPressed(Qt::Key key, bool state); + void keyLatched(Qt::Key key, bool state); + void keyLocked(Qt::Key key, bool state); + void mouseButtonPressed(Qt::MouseButton button, bool state); + void latch(); + void lock(); + void keyAdded(Qt::Key) + { + qDebug() << "Key added"; + } + void keyRemoved(Qt::Key) + { + qDebug() << "Key removed"; + } + +private: + KModifierKeyInfo m_lock; + QMap > m_leds; + QMap m_mouseLeds; +}; + +TestWidget::TestWidget() : QWidget(nullptr), m_lock(this) +{ + QMap mods; + mods.insert(Qt::Key_Shift, QStringLiteral("Shift")); + mods.insert(Qt::Key_Control, QStringLiteral("Ctrl")); + mods.insert(Qt::Key_Alt, QStringLiteral("Alt")); + mods.insert(Qt::Key_Meta, QStringLiteral("Meta")); + mods.insert(Qt::Key_Super_L, QStringLiteral("Super")); + mods.insert(Qt::Key_Hyper_L, QStringLiteral("Hyper")); + mods.insert(Qt::Key_AltGr, QStringLiteral("AltGr")); + mods.insert(Qt::Key_NumLock, QStringLiteral("NumLock")); + mods.insert(Qt::Key_CapsLock, QStringLiteral("CapsLock")); + mods.insert(Qt::Key_ScrollLock, QStringLiteral("ScrollLock")); + + QMap buttons; + buttons.insert(Qt::LeftButton, QStringLiteral("Left Button")); + buttons.insert(Qt::RightButton, QStringLiteral("Right Button")); + buttons.insert(Qt::MiddleButton, QStringLiteral("Middle Button")); + buttons.insert(Qt::XButton1, QStringLiteral("First X Button")); + buttons.insert(Qt::XButton2, QStringLiteral("Second X Button")); + + QVBoxLayout *layout = new QVBoxLayout(this); + + QMap::const_iterator it; + QMap::const_iterator end = mods.constEnd(); + for (it = mods.constBegin(); it != end; ++it) { + if (m_lock.knowsKey(it.key())) { + QHBoxLayout *hlayout = new QHBoxLayout; + QCheckBox *pressed = new QCheckBox(this); + QCheckBox *latched = new QCheckBox(this); + QCheckBox *locked = new QCheckBox(this); + QPushButton *latch = new QPushButton(QStringLiteral("latch"), this); + latch->setProperty("modifier", it.key()); + connect(latch, &QAbstractButton::clicked, this, &TestWidget::latch); + QPushButton *lock = new QPushButton(QStringLiteral("lock"), this); + lock->setProperty("modifier", it.key()); + connect(lock, &QAbstractButton::clicked, this, &TestWidget::lock); + pressed->setChecked(m_lock.isKeyPressed(it.key())); + latched->setChecked(m_lock.isKeyLatched(it.key())); + locked->setChecked(m_lock.isKeyLocked(it.key())); + m_leds.insert(it.key(), Triple(pressed, latched, locked)); + hlayout->addWidget(pressed); + hlayout->addWidget(latched); + hlayout->addWidget(locked); + hlayout->addWidget(new QLabel(it.value())); + hlayout->addWidget(latch); + hlayout->addWidget(lock); + layout->addLayout(hlayout); + } + } + + QMap::const_iterator it2; + QMap::const_iterator end2 = buttons.constEnd(); + for (it2 = buttons.constBegin(); it2 != end2; ++it2) { + QHBoxLayout *hlayout = new QHBoxLayout; + QCheckBox *pressed = new QCheckBox(this); + pressed->setChecked(m_lock.isButtonPressed(it2.key())); + m_mouseLeds.insert(it2.key(), pressed); + hlayout->addWidget(pressed); + hlayout->addWidget(new QLabel(it2.value())); + layout->addLayout(hlayout); + } + + connect(&m_lock, &KModifierKeyInfo::keyPressed, + this, &TestWidget::keyPressed); + connect(&m_lock, &KModifierKeyInfo::keyLatched, + this, &TestWidget::keyLatched); + connect(&m_lock, &KModifierKeyInfo::keyLocked, + this, &TestWidget::keyLocked); + connect(&m_lock, &KModifierKeyInfo::buttonPressed, + this, &TestWidget::mouseButtonPressed); + connect(&m_lock, &KModifierKeyInfo::keyAdded, + this, &TestWidget::keyAdded); + connect(&m_lock, &KModifierKeyInfo::keyRemoved, + this, &TestWidget::keyRemoved); +} + +void TestWidget::keyPressed(Qt::Key key, bool pressed) +{ + if (m_leds.contains(key)) { + m_leds[key].first->setChecked(pressed); + } +} + +void TestWidget::keyLatched(Qt::Key key, bool latched) +{ + if (m_leds.contains(key)) { + m_leds[key].second->setChecked(latched); + } +} + +void TestWidget::keyLocked(Qt::Key key, bool locked) +{ + if (m_leds.contains(key)) { + m_leds[key].third->setChecked(locked); + } +} + +void TestWidget::mouseButtonPressed(Qt::MouseButton button, bool pressed) +{ + if (m_mouseLeds.contains(button)) { + m_mouseLeds[button]->setChecked(pressed); + } +} + +void TestWidget::latch() +{ + Qt::Key key = (Qt::Key)sender()->property("modifier").toInt(); + m_lock.setKeyLatched(key, !m_lock.isKeyLatched(key)); +} + +void TestWidget::lock() +{ + Qt::Key key = (Qt::Key)sender()->property("modifier").toInt(); + m_lock.setKeyLocked(key, !m_lock.isKeyLocked(key)); +} + +int main(int argc, char *argv[]) +{ + QApplication::setApplicationName(QStringLiteral("simple")); + + QApplication app(argc, argv); + TestWidget mainWidget; + mainWidget.show(); + + return app.exec(); +} + +#include "kmodifierkeyinfotest.moc" diff --git a/tests/openurltest.cpp b/tests/openurltest.cpp new file mode 100644 index 0000000..80cddf4 --- /dev/null +++ b/tests/openurltest.cpp @@ -0,0 +1,23 @@ +/* This file is part of the KDE libraries + SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez + + SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL +*/ + +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + if (app.arguments().count() <= 1 || app.arguments().last() == QLatin1String("--help")) { + qDebug() << "please use ./openurltest "; + return 0; + } + QDesktopServices::openUrl(QUrl(app.arguments().last())); + + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + return app.exec(); +}