From 24b00960c3377b6760aa301876233c073d91b725 Mon Sep 17 00:00:00 2001 From: Maximiliano Curia Date: Fri, 18 Nov 2016 15:04:03 +0000 Subject: [PATCH 1/1] Import kguiaddons_5.28.0.orig.tar.xz [dgit import orig kguiaddons_5.28.0.orig.tar.xz] --- .reviewboardrc | 4 + CMakeLists.txt | 62 +++ COPYING | 346 +++++++++++++ COPYING.LIB | 510 ++++++++++++++++++++ KF5GuiAddonsConfig.cmake.in | 9 + README.md | 9 + autotests/CMakeLists.txt | 12 + autotests/kcolorutilstest.cpp | 135 ++++++ autotests/kcolorutilstest.h | 17 + autotests/kiconutilstest.cpp | 204 ++++++++ autotests/kiconutilstest.h | 34 ++ autotests/kwordwraptest.cpp | 74 +++ metainfo.yaml | 20 + src/CMakeLists.txt | 101 ++++ src/colors/kcolorcollection.cpp | 281 +++++++++++ src/colors/kcolorcollection.h | 207 ++++++++ src/colors/kcolormimedata.cpp | 78 +++ src/colors/kcolormimedata.h | 68 +++ src/colors/kcolorspaces.cpp | 165 +++++++ src/colors/kcolorspaces_p.h | 50 ++ src/colors/kcolorutils.cpp | 167 +++++++ src/colors/kcolorutils.h | 160 ++++++ src/colors/kguiaddons_colorhelpers_p.h | 31 ++ src/fonts/kfontutils.cpp | 93 ++++ src/fonts/kfontutils.h | 82 ++++ src/text/kdatevalidator.cpp | 74 +++ src/text/kdatevalidator.h | 45 ++ src/text/kwordwrap.cpp | 323 +++++++++++++ src/text/kwordwrap.h | 153 ++++++ src/util/kiconutils.cpp | 166 +++++++ src/util/kiconutils.h | 52 ++ src/util/kimagecache.h | 231 +++++++++ src/util/klocalimagecacheimpl.cpp | 163 +++++++ src/util/klocalimagecacheimpl.h | 67 +++ src/util/kmodifierkeyinfo.cpp | 80 +++ src/util/kmodifierkeyinfo.h | 189 ++++++++ src/util/kmodifierkeyinfoprovider.cpp | 65 +++ src/util/kmodifierkeyinfoprovider_dummy.cpp | 53 ++ src/util/kmodifierkeyinfoprovider_p.h | 139 ++++++ src/util/kmodifierkeyinfoprovider_x11.cpp | 362 ++++++++++++++ src/util/urlhandler.cpp | 69 +++ tests/CMakeLists.txt | 20 + tests/kcolorcollectiontest.cpp | 35 ++ tests/kmodifierkeyinfotest.cpp | 199 ++++++++ tests/openurltest.cpp | 37 ++ 45 files changed, 5441 insertions(+) create mode 100644 .reviewboardrc create mode 100644 CMakeLists.txt create mode 100644 COPYING create mode 100644 COPYING.LIB create mode 100644 KF5GuiAddonsConfig.cmake.in create mode 100644 README.md create mode 100644 autotests/CMakeLists.txt create mode 100644 autotests/kcolorutilstest.cpp create mode 100644 autotests/kcolorutilstest.h create mode 100644 autotests/kiconutilstest.cpp create mode 100644 autotests/kiconutilstest.h create mode 100644 autotests/kwordwraptest.cpp create mode 100644 metainfo.yaml create mode 100644 src/CMakeLists.txt create mode 100644 src/colors/kcolorcollection.cpp create mode 100644 src/colors/kcolorcollection.h create mode 100644 src/colors/kcolormimedata.cpp create mode 100644 src/colors/kcolormimedata.h create mode 100644 src/colors/kcolorspaces.cpp create mode 100644 src/colors/kcolorspaces_p.h create mode 100644 src/colors/kcolorutils.cpp create mode 100644 src/colors/kcolorutils.h create mode 100644 src/colors/kguiaddons_colorhelpers_p.h create mode 100644 src/fonts/kfontutils.cpp create mode 100644 src/fonts/kfontutils.h create mode 100644 src/text/kdatevalidator.cpp create mode 100644 src/text/kdatevalidator.h create mode 100644 src/text/kwordwrap.cpp create mode 100644 src/text/kwordwrap.h create mode 100644 src/util/kiconutils.cpp create mode 100644 src/util/kiconutils.h create mode 100644 src/util/kimagecache.h create mode 100644 src/util/klocalimagecacheimpl.cpp create mode 100644 src/util/klocalimagecacheimpl.h create mode 100644 src/util/kmodifierkeyinfo.cpp create mode 100644 src/util/kmodifierkeyinfo.h create mode 100644 src/util/kmodifierkeyinfoprovider.cpp create mode 100644 src/util/kmodifierkeyinfoprovider_dummy.cpp create mode 100644 src/util/kmodifierkeyinfoprovider_p.h create mode 100644 src/util/kmodifierkeyinfoprovider_x11.cpp create mode 100644 src/util/urlhandler.cpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/kcolorcollectiontest.cpp create mode 100644 tests/kmodifierkeyinfotest.cpp create mode 100644 tests/openurltest.cpp diff --git a/.reviewboardrc b/.reviewboardrc new file mode 100644 index 0000000..55c05d6 --- /dev/null +++ b/.reviewboardrc @@ -0,0 +1,4 @@ +REVIEWBOARD_URL = "https://git.reviewboard.kde.org" +REPOSITORY = 'git://anongit.kde.org/kguiaddons' +BRANCH = 'master' +TARGET_GROUPS = 'kdeframeworks' diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f392e90 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(KGuiAddons) + +include(FeatureSummary) +find_package(ECM 5.28.0 NO_MODULE) +set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") +feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) + + +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) + +include(KDEInstallDirs) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(KDECMakeSettings) +set(REQUIRED_QT_VERSION 5.5.0) +find_package(Qt5Gui ${REQUIRED_QT_VERSION} REQUIRED NO_MODULE) + +if (NOT APPLE AND NOT WIN32) + find_package(X11 MODULE) + find_package(XCB MODULE COMPONENTS XCB) +endif() + + +include(GenerateExportHeader) +include(ECMSetupVersion) +include(ECMGenerateHeaders) +set(KF5_VERSION "5.28.0") # handled by release scripts + +ecm_setup_version(${KF5_VERSION} VARIABLE_PREFIX KGUIADDONS + VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kguiaddons_version.h" + PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5GuiAddonsConfigVersion.cmake" + SOVERSION 5) + +add_subdirectory(src) +add_subdirectory(autotests) +add_subdirectory(tests) + +# create a Config.cmake and a ConfigVersion.cmake file and install them +set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5GuiAddons") + +include(ECMPackageConfigHelpers) + +ecm_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/COPYING b/COPYING new file mode 100644 index 0000000..5185fd3 --- /dev/null +++ b/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + 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 Library 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. + + GNU GENERAL PUBLIC LICENSE + 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) 19yy + + 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) 19yy 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 Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 0000000..2d2d780 --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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 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. + + GNU LESSER GENERAL PUBLIC LICENSE + 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. + + + + Copyright (C) + + 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 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/KF5GuiAddonsConfig.cmake.in b/KF5GuiAddonsConfig.cmake.in new file mode 100644 index 0000000..242de46 --- /dev/null +++ b/KF5GuiAddonsConfig.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +# Any changes in this file will be overwritten by CMake. + +find_dependency(Qt5Gui @REQUIRED_QT_VERSION@) + + +include("${CMAKE_CURRENT_LIST_DIR}/KF5GuiAddonsTargets.cmake") + 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..d4edcac --- /dev/null +++ b/autotests/CMakeLists.txt @@ -0,0 +1,12 @@ +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 + LINK_LIBRARIES KF5::GuiAddons Qt5::Test +) diff --git a/autotests/kcolorutilstest.cpp b/autotests/kcolorutilstest.cpp new file mode 100644 index 0000000..506b5b3 --- /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/kiconutilstest.cpp b/autotests/kiconutilstest.cpp new file mode 100644 index 0000000..6ee0240 --- /dev/null +++ b/autotests/kiconutilstest.cpp @@ -0,0 +1,204 @@ +/* + Copyright (C) 2013 Martin Klapetek + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . + */ + +#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..acec5b0 --- /dev/null +++ b/autotests/kiconutilstest.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2013 Martin Klapetek + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . + */ + +#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..ecb5071 --- /dev/null +++ b/autotests/kwordwraptest.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2003, 2008 David Faure + * + 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#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/metainfo.yaml b/metainfo.yaml new file mode 100644 index 0000000..e1c5bb8 --- /dev/null +++ b/metainfo.yaml @@ -0,0 +1,20 @@ +maintainer: +description: Addons to QtGui +tier: 1 +type: functional +platforms: + - name: Linux + - 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..671b081 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,101 @@ + +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 +) + +set (kmodifierkeyinfoprovider_impl "dummy") +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) + include_directories ( + ${X11_Xkb_INCLUDE_PATH} + ${X11_Xlib_INCLUDE_PATH} + ${XCB_XCB_INCLUDE_DIR} + ) + set (kmodifierkeyinfoprovider_impl "x11") + endif() +endif () + +set (kguiaddons_LIB_SRCS ${kguiaddons_LIB_SRCS} util/kmodifierkeyinfoprovider_${kmodifierkeyinfoprovider_impl}.cpp) + +add_library(KF5GuiAddons ${kguiaddons_LIB_SRCS}) + +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 + ) +target_include_directories(KF5GuiAddons PUBLIC "$") +target_include_directories(KF5GuiAddons INTERFACE "$" ) +target_compile_definitions(KF5GuiAddons INTERFACE "$") +target_link_libraries(KF5GuiAddons PUBLIC Qt5::Gui) + +if (kmodifierkeyinfoprovider_impl STREQUAL "x11") + target_link_libraries(KF5GuiAddons LINK_PRIVATE ${X11_LIBRARIES} ${XCB_XCB_LIBRARY} Qt5::X11Extras) +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 + + RELATIVE util + REQUIRED_HEADERS KGuiAddons_HEADERS +) + +install(TARGETS KF5GuiAddons EXPORT KF5GuiAddonsTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/kguiaddons_export.h + util/klocalimagecacheimpl.h # implementation detail, no forwarding header + ${KGuiAddons_HEADERS} + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KGuiAddons COMPONENT Devel +) + +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}) diff --git a/src/colors/kcolorcollection.cpp b/src/colors/kcolorcollection.cpp new file mode 100644 index 0000000..f23698a --- /dev/null +++ b/src/colors/kcolorcollection.cpp @@ -0,0 +1,281 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Waldo Bastian (bastian@kde.org) + + 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. + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +//----------------------------------------------------------------------------- +// 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 = line.mid(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() +{ + QStringList paletteDirs = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, + QStringLiteral("colors"), + QStandardPaths::LocateDirectory); + + QStringList paletteList; + Q_FOREACH (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(); + description = QLatin1Char('#') + description.split(QLatin1Char('\n'), QString::KeepEmptyParts).join(QStringLiteral("\n#")); + + str << QLatin1String("KDE RGB Palette\n"); + str << description << QLatin1Char('\n'); + foreach (const KColorCollectionPrivate::ColorNode &node, 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 (int) 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..11f7593 --- /dev/null +++ b/src/colors/kcolorcollection.h @@ -0,0 +1,207 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Waldo Bastian (bastian@kde.org) + + 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; version + 2 of the License. + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +//----------------------------------------------------------------------------- +// KDE color collection. + +#ifndef KDELIBS_KCOLORCOLLECTION_H +#define KDELIBS_KCOLORCOLLECTION_H + +#include + +#include +#include +#include +#include + +/** + * 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..f511ab7 --- /dev/null +++ b/src/colors/kcolormimedata.cpp @@ -0,0 +1,78 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Steffen Hansen (hansen@kde.org) + Copyright (C) 2005 Joseph Wenninger (jowenn@kde.org) + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#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..814ee1d --- /dev/null +++ b/src/colors/kcolormimedata.h @@ -0,0 +1,68 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Steffen Hansen (hansen@kde.org) + Copyright (C) 2005 Joseph Wenninger (jowenn@kde.org) + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#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..7c9ed89 --- /dev/null +++ b/src/colors/kcolorspaces.cpp @@ -0,0 +1,165 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Olaf Schmidt + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#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::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..8da5bf9 --- /dev/null +++ b/src/colors/kcolorspaces_p.h @@ -0,0 +1,50 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * 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 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..3f4f1c8 --- /dev/null +++ b/src/colors/kcolorutils.cpp @@ -0,0 +1,167 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Thomas Zander + * Copyright (C) 2007 Zack Rusin + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#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::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; + *y = khcy.y; + if (a) { + *a = khcy.a; + } +} + +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..5274418 --- /dev/null +++ b/src/colors/kcolorutils.h @@ -0,0 +1,160 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Thomas Zander + * Copyright (C) 2007 Zack Rusin + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KCOLORUTILS_H +#define KCOLORUTILS_H + +#include +#include + +class QColor; + +/** + * A set of methods used to work with colors. + */ +namespace KColorUtils +{ +/** + * 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). + * + * 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. + * @since 5.0 + */ +KGUIADDONS_EXPORT void getHcy(const QColor &, qreal *hue, qreal *chroma, qreal *luma, qreal *alpha = 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..6d653ae --- /dev/null +++ b/src/colors/kguiaddons_colorhelpers_p.h @@ -0,0 +1,31 @@ +/* This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Olaf Schmidt + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef KGUIADDONS_KCOLORHELPERS_P_H +#define KGUIADDONS_KCOLORHELPERS_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..4638642 --- /dev/null +++ b/src/fonts/kfontutils.cpp @@ -0,0 +1,93 @@ +/********************************************************************************* + * * + * Copyright (C) 2005, 2009, 2014 by Albert Astals Cid * + * * + * 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) version 3, or any * + * later version 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 defined in Section 6 of version 3 of the license. * + * * + * 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, see . * + * * + *********************************************************************************/ + +#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 || boundingRect.height() == 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..5a7efa7 --- /dev/null +++ b/src/fonts/kfontutils.h @@ -0,0 +1,82 @@ +/********************************************************************************* + * * + * Copyright (C) 2005, 2009 by Albert Astals Cid * + * * + * 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) version 3, or any * + * later version 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 defined in Section 6 of version 3 of the license. * + * * + * 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, see . * + * * + *********************************************************************************/ + +#ifndef KFONTMETRICS_H +#define KFONTMETRICS_H + +#include + +#include + +class QPainter; +class QSizeF; +class QString; + +namespace KFontUtils +{ +/** Modifiers for the adaptFontSize function */ +enum AdaptFontSizeOption { + NoFlags = 0x01 /** No modifier */, + DoNotAllowWordWrap = 0x02 /** Do not use word wrapping */ +}; +Q_DECLARE_FLAGS(AdaptFontSizeOptions, AdaptFontSizeOption) + +/** 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); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(KFontUtils::AdaptFontSizeOptions) + +#endif + diff --git a/src/text/kdatevalidator.cpp b/src/text/kdatevalidator.cpp new file mode 100644 index 0000000..b45dd4d --- /dev/null +++ b/src/text/kdatevalidator.cpp @@ -0,0 +1,74 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2007 John Layt + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kdatevalidator.h" + +#include +#include + +class KDateValidator::KDateValidatorPrivate +{ +public: + KDateValidatorPrivate(KDateValidator *q): q(q) + { + } + + ~KDateValidatorPrivate() + { + } + + KDateValidator *q; +}; + +KDateValidator::KDateValidator(QObject *parent) : QValidator(parent), d(0) +{ + 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..6c6ef3a --- /dev/null +++ b/src/text/kdatevalidator.h @@ -0,0 +1,45 @@ +/* -*- C++ -*- + This file is part of the KDE libraries + Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) + (C) 1998-2001 Mirko Boehm (mirko@kde.org) + (C) 2007 John Layt + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KDATEVALIDATOR_H +#define KDATEVALIDATOR_H + +#include + +#include + +/** +* Validates user-entered dates. +*/ +class KGUIADDONS_EXPORT KDateValidator : public QValidator +{ +public: + explicit KDateValidator(QObject *parent = 0); + State validate(QString &text, int &e) const Q_DECL_OVERRIDE; + void fixup(QString &input) const Q_DECL_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..7377a82 --- /dev/null +++ b/src/text/kwordwrap.cpp @@ -0,0 +1,323 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 David Faure + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#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.width(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.width(s); + p->drawText(x, y, s); + } + } else { + if (tl > 3) { + p->drawText(x, y, t.left(tl - 3)); + x += fm.width(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.width(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..f394461 --- /dev/null +++ b/src/text/kwordwrap.h @@ -0,0 +1,153 @@ +/* This file is part of the KDE libraries + Copyright (C) 2001 David Faure + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + 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; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef kwordwrap_h +#define kwordwrap_h + +#include +#include +#include + +class QFontMetrics; +class QRect; +class QString; +class QPainter; +class KWordWrapPrivate; + +/** + * 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/kiconutils.cpp b/src/util/kiconutils.cpp new file mode 100644 index 0000000..5142679 --- /dev/null +++ b/src/util/kiconutils.cpp @@ -0,0 +1,166 @@ +/* + Copyright (C) 2013 Martin Klapetek + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . + */ + +#include "kiconutils.h" + +#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) Q_DECL_OVERRIDE; + QIconEngine *clone() const Q_DECL_OVERRIDE; + + QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; + + void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; + void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_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..1bc521a --- /dev/null +++ b/src/util/kiconutils.h @@ -0,0 +1,52 @@ +/* + Copyright (C) 2013 Martin Klapetek + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . + */ + +#ifndef KICONUTILS_H +#define KICONUTILS_H + +#include + +#include + +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..c7f747d --- /dev/null +++ b/src/util/kimagecache.h @@ -0,0 +1,231 @@ +/* + * This file is part of the KDE project. + * Copyright © 2010 Michael Pyne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#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..d34dbe7 --- /dev/null +++ b/src/util/klocalimagecacheimpl.cpp @@ -0,0 +1,163 @@ +/* + * This file is part of the KDE project. + * Copyright © 2010 Michael Pyne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#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 = 0) + : QObject(parent) + , timestamp(QDateTime::currentDateTime()) + , enablePixmapCaching(true) + { + QObject::connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), + this, SLOT(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; +}; + +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..132a01c --- /dev/null +++ b/src/util/klocalimagecacheimpl.h @@ -0,0 +1,67 @@ +/* + * This file is part of the KDE project. + * Copyright © 2010 Michael Pyne + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * 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; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#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: + 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..7068d6f --- /dev/null +++ b/src/util/kmodifierkeyinfo.cpp @@ -0,0 +1,80 @@ +/* + Copyright 2009 Michael Leupold + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . +*/ + +#include "kmodifierkeyinfo.h" +#include "kmodifierkeyinfoprovider_p.h" + +KModifierKeyInfo::KModifierKeyInfo(QObject *parent) + : QObject(parent), p(new KModifierKeyInfoProvider) +{ + connect(p, SIGNAL(keyPressed(Qt::Key,bool)), this, SIGNAL(keyPressed(Qt::Key,bool))); + connect(p, SIGNAL(keyLatched(Qt::Key,bool)), this, SIGNAL(keyLatched(Qt::Key,bool))); + connect(p, SIGNAL(keyLocked(Qt::Key,bool)), this, SIGNAL(keyLocked(Qt::Key,bool))); + connect(p, SIGNAL(buttonPressed(Qt::MouseButton,bool)), + this, SIGNAL(buttonPressed(Qt::MouseButton,bool))); + connect(p, SIGNAL(keyAdded(Qt::Key)), this, SIGNAL(keyAdded(Qt::Key))); + connect(p, SIGNAL(keyRemoved(Qt::Key)), this, SIGNAL(keyRemoved(Qt::Key))); +} + +KModifierKeyInfo::~KModifierKeyInfo() +{ + delete p; +} + +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..e999762 --- /dev/null +++ b/src/util/kmodifierkeyinfo.h @@ -0,0 +1,189 @@ +/* + Copyright 2009 Michael Leupold + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . +*/ + +#ifndef KMODIFIERKEYINFO_H +#define KMODIFIERKEYINFO_H + +#include + +#include + +class KModifierKeyInfoProvider; + +/** + * 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: + *
  • locked: eg. caps-locked (aka toggled)
  • + *
  • latched: the key is temporarily locked but will be unlocked upon + * the next keypress.
+ * + * An application can either query the states synchronously (@see isKeyLatched, + * @see isKeyLocked) or connect to KModifierKeyInfo's signals to be notified about + * changes (@see keyLatched, @see keyLocked). + */ +class KGUIADDONS_EXPORT KModifierKeyInfo : public QObject +{ + Q_OBJECT + +public: + /** + * Default constructor + */ + KModifierKeyInfo(QObject *parent = 0); + + /** + * 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) + KModifierKeyInfoProvider *const p; // krazy:exclude=dpointer +}; + +#endif diff --git a/src/util/kmodifierkeyinfoprovider.cpp b/src/util/kmodifierkeyinfoprovider.cpp new file mode 100644 index 0000000..24c9a77 --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider.cpp @@ -0,0 +1,65 @@ +/* + Copyright 2009 Michael Leupold + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . +*/ + +#include "kmodifierkeyinfoprovider_p.h" + +bool KModifierKeyInfoProvider::isKeyPressed(Qt::Key key) const +{ + if (m_modifierStates.contains(key)) { + return m_modifierStates[key] & Pressed; + } + return false; +} + +bool KModifierKeyInfoProvider::isKeyLatched(Qt::Key key) const +{ + if (m_modifierStates.contains(key)) { + return m_modifierStates[key] & Latched; + } + return false; +} + +bool KModifierKeyInfoProvider::isKeyLocked(Qt::Key key) const +{ + if (m_modifierStates.contains(key)) { + return m_modifierStates[key] & 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(); +} + +#include "moc_kmodifierkeyinfoprovider_p.cpp" diff --git a/src/util/kmodifierkeyinfoprovider_dummy.cpp b/src/util/kmodifierkeyinfoprovider_dummy.cpp new file mode 100644 index 0000000..860e769 --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_dummy.cpp @@ -0,0 +1,53 @@ +/* + Copyright 2009 Michael Leupold + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . +*/ + +#include "kmodifierkeyinfo.h" +#include "kmodifierkeyinfoprovider_p.h" + +KModifierKeyInfoProvider::KModifierKeyInfoProvider() + : QObject(0) +{ +} + +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::nativeEventFilter(const QByteArray &eventType, void *message, long int *result) +{ + Q_UNUSED(eventType) + Q_UNUSED(message) + Q_UNUSED(result) + return false; +} diff --git a/src/util/kmodifierkeyinfoprovider_p.h b/src/util/kmodifierkeyinfoprovider_p.h new file mode 100644 index 0000000..096663d --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_p.h @@ -0,0 +1,139 @@ +/* + Copyright 2009 Michael Leupold + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . +*/ + +#ifndef KMODIFIERKEYINFOPROVIDER_P_H +#define KMODIFIERKEYINFOPROVIDER_P_H + +#include +#include +#include + +/** + * Background class that implements the behaviour of KModifierKeyInfo for + * the different supported platforms. + * @internal + */ +class KModifierKeyInfoProvider : public QObject, public QAbstractNativeEventFilter +{ + Q_OBJECT + +public: + enum ModifierState { + Nothing = 0x0, + Pressed = 0x1, + Latched = 0x2, + Locked = 0x4 + }; + Q_DECLARE_FLAGS(ModifierStates, ModifierState) + + KModifierKeyInfoProvider(); + ~KModifierKeyInfoProvider(); + + /** + * 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 + */ + 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 + */ + 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; + + bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) Q_DECL_OVERRIDE; + +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 xkbUpdateModifierMapping(); + void xkbModifierStateChanged(unsigned char mods, unsigned char latched_mods, + unsigned char locked_mods); + void xkbButtonStateChanged(unsigned short ptr_buttons); + +private: + // the state of each known modifier + QHash m_modifierStates; + // the state of each known mouse button + QHash m_buttonStates; + + 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; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(KModifierKeyInfoProvider::ModifierStates) + +#endif diff --git a/src/util/kmodifierkeyinfoprovider_x11.cpp b/src/util/kmodifierkeyinfoprovider_x11.cpp new file mode 100644 index 0000000..9216acf --- /dev/null +++ b/src/util/kmodifierkeyinfoprovider_x11.cpp @@ -0,0 +1,362 @@ +/* + Copyright 2009 Michael Leupold + Copyright 2013 Martin Gräßlin + + 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) version 3, or any + later version 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 defined in Section 6 of version 3 of the license. + + 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, see . +*/ + +#include "kmodifierkeyinfo.h" +#include "kmodifierkeyinfoprovider_p.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 != 0); + + unsigned int mask = 0; + bool nameEqual; + for (int i = 0; i < XkbNumVirtualMods; ++i) { + char *modStr = XGetAtomName(xkb->dpy, xkb->names->vmods[i]); + if (modStr != 0) { + nameEqual = (strcmp(name, modStr) == 0); + XFree(modStr); + if (nameEqual) { + XkbVirtualModsToReal(xkb, 1 << i, &mask); + break; + } + } + } + return mask; +} + +KModifierKeyInfoProvider::KModifierKeyInfoProvider() + : QObject(0) + , QAbstractNativeEventFilter() + , m_xkbEv(0) + , m_xkbAvailable(false) +{ + if (qApp) { + if (qApp->platformName() == QStringLiteral("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::MidButton, 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); + } +} + +KModifierKeyInfoProvider::~KModifierKeyInfoProvider() +{ + if (m_xkbAvailable) { + QCoreApplication::instance()->removeNativeEventFilter(this); + } +} + +bool KModifierKeyInfoProvider::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 KModifierKeyInfoProvider::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 KModifierKeyInfoProvider::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 KModifierKeyInfoProvider::xkbModifierStateChanged(unsigned char mods, + unsigned char latched_mods, + unsigned char locked_mods) +{ + // detect keyboard modifiers + ModifierStates oldState; + 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; + oldState = m_modifierStates[it.key()]; + + // determine the new state + if (mods & it.value()) { + newState |= Pressed; + } + if (latched_mods & it.value()) { + newState |= Latched; + } + if (locked_mods & it.value()) { + newState |= Locked; + } + + if (newState != oldState) { + m_modifierStates[it.key()] = newState; + + if ((newState ^ oldState) & Pressed) { + emit keyPressed(it.key(), newState & Pressed); + } + if ((newState ^ oldState) & Latched) { + emit keyLatched(it.key(), newState & Latched); + } + if ((newState ^ oldState) & Locked) { + emit keyLocked(it.key(), newState & Locked); + } + } + } +} + +void KModifierKeyInfoProvider::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 KModifierKeyInfoProvider::xkbUpdateModifierMapping() +{ + if (!m_xkbAvailable) { + return; + } + m_xkbModifiers.clear(); + + QList srcModifiers; + srcModifiers << ModifierDefinition(Qt::Key_Shift, ShiftMask, 0, 0) + << ModifierDefinition(Qt::Key_Control, ControlMask, 0, 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, 0, 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 != 0) { + // try virtual modifier first + if (it->name != 0) { + 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 != 0) { + XkbFreeKeyboard(xkb, 0, true); + } +} diff --git a/src/util/urlhandler.cpp b/src/util/urlhandler.cpp new file mode 100644 index 0000000..1fd5984 --- /dev/null +++ b/src/util/urlhandler.cpp @@ -0,0 +1,69 @@ +/********************************************************************************* + * * + * Copyright (C) 2013 Aleix Pol Gonzalez * + * * + * 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) version 3, or any * + * later version 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 defined in Section 6 of version 3 of the license. * + * * + * 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, see . * + * * + *********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +class UrlHandler : public QObject +{ + Q_OBJECT +public: + explicit UrlHandler(QObject *parent = 0) : QObject(parent) {} + +public Q_SLOTS: + void openHelp(const QUrl &url) + { + QUrl u(url); + if (u.path() == QLatin1String("/")) { + u.setPath(QCoreApplication::applicationName()); + } + + QString helpcenter = QStandardPaths::findExecutable(QStringLiteral("khelpcenter")); + if (helpcenter.isEmpty()) { + if (QCoreApplication::organizationDomain() == QLatin1String("kde.org")) { + //if khelpcenter is not installed and it's a KDE application, use docs.kde.org + const QUrl httpUrl(QStringLiteral("https://docs.kde.org/index.php?branch=stable5&language=")+QLocale().name()+QStringLiteral("&application=") + + QCoreApplication::applicationName() + QStringLiteral("&path=") + url.path()); + QDesktopServices::openUrl(httpUrl); + } else + QDesktopServices::openUrl(u); + } else { + QProcess::startDetached(helpcenter, QStringList(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..16d52fa --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +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 +) diff --git a/tests/kcolorcollectiontest.cpp b/tests/kcolorcollectiontest.cpp new file mode 100644 index 0000000..0f706c9 --- /dev/null +++ b/tests/kcolorcollectiontest.cpp @@ -0,0 +1,35 @@ + +#include +#include +#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/kmodifierkeyinfotest.cpp b/tests/kmodifierkeyinfotest.cpp new file mode 100644 index 0000000..c2852dd --- /dev/null +++ b/tests/kmodifierkeyinfotest.cpp @@ -0,0 +1,199 @@ +/* + Copyright 2009 Michael Leupold + + 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) version 3 or any later version + 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 + 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#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(0), 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::MidButton, 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, SIGNAL(clicked()), SLOT(latch())); + QPushButton *lock = new QPushButton(QStringLiteral("lock"), this); + lock->setProperty("modifier", it.key()); + connect(lock, SIGNAL(clicked()), SLOT(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); + } + + setLayout(layout); + + connect(&m_lock, SIGNAL(keyPressed(Qt::Key,bool)), SLOT(keyPressed(Qt::Key,bool))); + connect(&m_lock, SIGNAL(keyLatched(Qt::Key,bool)), SLOT(keyLatched(Qt::Key,bool))); + connect(&m_lock, SIGNAL(keyLocked(Qt::Key,bool)), SLOT(keyLocked(Qt::Key,bool))); + connect(&m_lock, SIGNAL(buttonPressed(Qt::MouseButton,bool)), + SLOT(mouseButtonPressed(Qt::MouseButton,bool))); + connect(&m_lock, SIGNAL(keyAdded(Qt::Key)), SLOT(keyAdded(Qt::Key))); + connect(&m_lock, SIGNAL(keyRemoved(Qt::Key)), SLOT(keyRemoved(Qt::Key))); +} + +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..bfa18a3 --- /dev/null +++ b/tests/openurltest.cpp @@ -0,0 +1,37 @@ +/* This file is part of the KDE libraries + * Copyright 2013 Aleix Pol Gonzalez + * + * 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 of the License or ( at + * your option ) version 3 or, at the discretion of KDE e.V. ( which shall + * act as a proxy as in section 14 of the GPLv3 ), 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 Lesser General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + if (app.arguments().count() <= 1 || app.arguments().last() == QStringLiteral("--help")) { + qDebug() << "please use ./openurltest "; + return 0; + } + QDesktopServices::openUrl(QUrl(app.arguments().last())); + + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + return app.exec(); +} -- 2.30.2