Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:mkittler:appimage:vcs
tageditor
_service:appimage:qtutilities.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:appimage:qtutilities.obscpio of Package tageditor
07070100000000000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001400000000qtutilities/.github07070100000001000081A4000000000000000000000001636534C9000002CE000000000000000000000000000000000000001E00000000qtutilities/.github/stale.yml# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - security - feature request - enhancement # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false 07070100000002000081A4000000000000000000000001636534C90000017A000000000000000000000000000000000000001700000000qtutilities/.gitignore# C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es /.qmake.cache /.qmake.stash *.pro.user *.txt.user *.pro.user.* *.qbs.user *.qbs.user.* *.moc moc_*.cpp qrc_*.cpp ui_*.h Makefile* *-build-* # QtCreator *.autosave #QtCtreator Qml *.qmlproject.user *.qmlproject.user.* # Dolphin .directory # documentation /doc # clang-format /.clang-format 07070100000003000081A4000000000000000000000001636534C900001CFC000000000000000000000000000000000000001B00000000qtutilities/CMakeLists.txtcmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # meta data set(META_PROJECT_NAME qtutilities) set(META_PROJECT_VARNAME QT_UTILITIES) set(META_APP_NAME "Qt Utilities") set(META_APP_AUTHOR "Martchus") set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_DESCRIPTION "Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models") set(META_VERSION_MAJOR 6) set(META_VERSION_MINOR 10) set(META_VERSION_PATCH 1) set(META_APP_VERSION ${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}) project(${META_PROJECT_NAME}) # add project files set(HEADER_FILES misc/xmlparsermacros.h misc/undefxmlparsermacros.h misc/trylocker.h misc/adoptlocker.h misc/dialogutils.h misc/desktoputils.h misc/conversion.h misc/compat.h models/checklistmodel.h resources/qtconfigarguments.h resources/resources.h resources/importplugin.h) set(SRC_FILES misc/dialogutils.cpp misc/desktoputils.cpp models/checklistmodel.cpp resources/qtconfigarguments.cpp resources/resources.cpp) set(RES_FILES resources/qtutilsicons.qrc) set(WIDGETS_HEADER_FILES aboutdialog/aboutdialog.h enterpassworddialog/enterpassworddialog.h settingsdialog/optioncategory.h settingsdialog/optioncategoryfiltermodel.h settingsdialog/optioncategorymodel.h settingsdialog/optionpage.h settingsdialog/settingsdialog.h settingsdialog/qtsettings.h widgets/buttonoverlay.h widgets/clearcombobox.h widgets/clearlineedit.h widgets/clearplaintextedit.h widgets/clearspinbox.h widgets/iconbutton.h widgets/pathselection.h paletteeditor/paletteeditor.h paletteeditor/colorbutton.h misc/recentmenumanager.h) set(WIDGETS_SRC_FILES aboutdialog/aboutdialog.cpp enterpassworddialog/enterpassworddialog.cpp settingsdialog/optioncategory.cpp settingsdialog/optioncategoryfiltermodel.cpp settingsdialog/optioncategorymodel.cpp settingsdialog/optionpage.cpp settingsdialog/settingsdialog.cpp settingsdialog/qtsettings.cpp widgets/buttonoverlay.cpp widgets/clearcombobox.cpp widgets/clearlineedit.cpp widgets/clearplaintextedit.cpp widgets/clearspinbox.cpp widgets/iconbutton.cpp widgets/pathselection.cpp paletteeditor/paletteeditor.cpp paletteeditor/colorbutton.cpp misc/recentmenumanager.cpp) set(WIDGETS_UI_FILES aboutdialog/aboutdialog.ui enterpassworddialog/enterpassworddialog.ui settingsdialog/settingsdialog.ui settingsdialog/qtappearanceoptionpage.ui settingsdialog/qtlanguageoptionpage.ui settingsdialog/qtenvoptionpage.ui paletteeditor/paletteeditor.ui) set(QT_TESTS buttonoverlay dialogs) set(CMAKE_MODULE_FILES cmake/modules/AndroidApk.cmake cmake/modules/QtConfig.cmake cmake/modules/QtGuiConfig.cmake cmake/modules/QtLinkage.cmake cmake/modules/QtWebViewProviderConfig.cmake cmake/modules/QtJsProviderConfig.cmake) set(CMAKE_TEMPLATE_FILES cmake/templates/qtconfig.h.in cmake/templates/webviewdefs.h.in cmake/templates/webviewincludes.h.in cmake/templates/jsdefs.h.in cmake/templates/jsincludes.h.in) if (ANDROID) list(APPEND CMAKE_MODULE_FILES) list(APPEND CMAKE_TEMPLATE_FILES cmake/templates/android-deployment.json.in) endif () set(TS_FILES translations/${META_PROJECT_NAME}_zh_CN.ts translations/${META_PROJECT_NAME}_de_DE.ts translations/${META_PROJECT_NAME}_en_US.ts) set(DOC_FILES README.md) set(REQUIRED_ICONS dialog-cancel dialog-ok dialog-ok-apply document-open document-open-recent edit-clear go-next preferences-desktop-icons preferences-desktop-locale qtcreator system-file-manager system-run system-search window-close) set(SCRIPT_FILES scripts/required_icons.sh) # required to include CMake modules from own project directory set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" "${CMAKE_MODULE_PATH}") # configure platform specific capslock detection for enterpassworddialog.cpp if (WIN32 OR (UNIX AND NOT APPLE AND NOT ANDROID)) set(ENABLE_CAPSLOCK_DETECTION_BY_DEFAULT ON) else () set(ENABLE_CAPSLOCK_DETECTION_BY_DEFAULT OFF) endif () option(CAPSLOCK_DETECTION "enables capslock detection" ${ENABLE_CAPSLOCK_DETECTION_BY_DEFAULT}) if (CAPSLOCK_DETECTION) if (WIN32) # WinAPI provides functions to provide capslock detection set(HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION ON) else () # X11 can provide functions for capslock detection under non-Windows environments find_package(X11) if (X11_FOUND) list(APPEND LIBRARIES ${X11_LIBRARIES}) set(HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION ON) endif () endif () if (NOT HAVE_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) message(FATAL_ERROR "No backend for capslock detection found (WinAPI or X11 must be provided)") endif () set_property( SOURCE enterpassworddialog/enterpassworddialog.cpp APPEND PROPERTY COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION) endif () # configure support for D-Bus notifications if (UNIX AND NOT APPLE AND NOT ANDROID) set(ENABLE_DBUS_NOTIFICATIONS_BY_DEFAULT ON) else () set(ENABLE_DBUS_NOTIFICATIONS_BY_DEFAULT OFF) endif () option(DBUS_NOTIFICATIONS "enables support for D-Bus notifications" ${ENABLE_DBUS_NOTIFICATIONS_BY_DEFAULT}) set(DBUS_NOTIFICATIONS_FILE_NAME misc/dbusnotification) if (DBUS_NOTIFICATIONS) list(APPEND HEADER_FILES ${DBUS_NOTIFICATIONS_FILE_NAME}.h) list(APPEND SRC_FILES ${DBUS_NOTIFICATIONS_FILE_NAME}.cpp) list(APPEND DBUS_FILES dbus/org.freedesktop.Notifications.xml) list(APPEND META_PUBLIC_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME}_SUPPORT_DBUS_NOTIFICATIONS) list(APPEND QT_TESTS dbusnotification) message(STATUS "D-Bus notifications enabled") else () list(APPEND EXCLUDED_FILES ${DBUS_NOTIFICATIONS_FILE_NAME}.h ${DBUS_NOTIFICATIONS_FILE_NAME}.cpp) message(STATUS "D-Bus notifications disabled") endif () # find c++utilities set(CONFIGURATION_PACKAGE_SUFFIX "" CACHE STRING "sets the suffix for find_package() calls to packages configured via c++utilities") set(PACKAGE_NAMESPACE "" CACHE STRING "sets the namespace (prefix) for find_package() calls to packages configured via c++utilities") if (PACKAGE_NAMESPACE) set(PACKAGE_NAMESPACE_PREFIX "${PACKAGE_NAMESPACE}-") endif () find_package(${PACKAGE_NAMESPACE_PREFIX}c++utilities${CONFIGURATION_PACKAGE_SUFFIX} 5.5.0 REQUIRED) use_cpp_utilities() # include modules to apply configuration include(BasicConfig) include(QtGuiConfig) # add further Qt modules (which are not automatically detected) set(META_PUBLIC_QT_MODULES Core ${ADDITIONAL_QT_MODULES}) # include further modules to apply configuration include(QtConfig) include(WindowsResources) include(LibraryTarget) include(Doxygen) include(ConfigHeader) # configure test target include(TestUtilities) list(APPEND QT_TEST_LIBRARIES ${CPP_UTILITIES_LIB} ${META_TARGET_NAME}) use_qt_module(LIBRARIES_VARIABLE "QT_TEST_LIBRARIES" PREFIX "${QT_PACKAGE_PREFIX}" MODULE "Test") foreach (TEST ${QT_TESTS}) configure_test_target(TEST_NAME "${TEST}_tests" SRC_FILES "tests/${TEST}.cpp" LIBRARIES "${QT_TEST_LIBRARIES}") endforeach () 07070100000004000081A4000000000000000000000001636534C90000467F000000000000000000000000000000000000001400000000qtutilities/LICENSE GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. 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. {description} Copyright (C) {year} {fullname} This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. {signature of Ty Coon}, 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. 07070100000005000081A4000000000000000000000001636534C9000002A6000000000000000000000000000000000000001600000000qtutilities/README.md# qtutilities Common Qt related C++ classes and routines used by my applications such as dialogs, widgets and models. ## Build instructions Qt utilities depends on c++utilities and is built in the same way. So checkout this repository for further instructions. The documentation for various CMake variables influencing the build is also provided in the c++utilities repository. The library also depends on the following Qt modules (only the latest Qt 5 and Qt 6 version tested): core, gui, widgets Some header files also require further Qt modules. ## Copyright notice and license Copyright © 2015-2022 Marius Kittler All code is licensed under [GPL-2-or-later](LICENSE). 07070100000006000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001800000000qtutilities/aboutdialog07070100000007000081A4000000000000000000000001636534C9000015B5000000000000000000000000000000000000002800000000qtutilities/aboutdialog/aboutdialog.cpp#include "./aboutdialog.h" #include "../misc/dialogutils.h" #include "ui_aboutdialog.h" #include <c++utilities/application/argumentparser.h> #include <QApplication> #include <QGraphicsPixmapItem> #include <QMessageBox> #include <QStringBuilder> #include <QStyle> /*! * \brief The QtUtilities namespace contains all utilities provided by the qtutilities library. */ namespace QtUtilities { /*! * \class AboutDialog * \brief The AboutDialog class provides a simple about dialog. */ /*! * \brief Constructs an about dialog with the provided information. * \param parent Specifies the parent widget. * \param applicationName Specifies the name of the application. If empty, * QApplication::applicationName() will be used. * \param creator Specifies the creator of the application. If empty, * QApplication::organizationName() will be used. * \param version Specifies the version of the application. If empty, * QApplication::applicationVersion() will be used. * \param dependencyVersions Specifies the dependency versions which were present at link-time. If empty, * the application info from c++utilities is used. * \param description Specifies a short description about the application. * \param website Specifies the URL to the website of the application. * \param image Specifies the application icon. If the image is null, the * standard information icon will be used. */ AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const QString &creator, const QString &version, const std::vector<const char *> &dependencyVersions, const QString &website, const QString &description, const QImage &image) : QDialog(parent) , m_ui(new Ui::AboutDialog) { m_ui->setupUi(this); makeHeading(m_ui->productNameLabel); setStyleSheet(dialogStyle()); setWindowFlags((windowFlags()) & ~(Qt::WindowMinMaxButtonsHint | Qt::WindowContextHelpButtonHint | Qt::WindowFullscreenButtonHint)); if (!applicationName.isEmpty()) { m_ui->productNameLabel->setText(applicationName); } else if (!QApplication::applicationDisplayName().isEmpty()) { m_ui->productNameLabel->setText(QApplication::applicationDisplayName()); } else { m_ui->productNameLabel->setText(QApplication::applicationName()); } if (creator.startsWith(QLatin1Char('<'))) { // assign rich text as-is m_ui->creatorLabel->setText(creator); } else { // add "developed by " before creator name m_ui->creatorLabel->setText(tr("developed by %1").arg(creator.isEmpty() ? QApplication::organizationName() : creator)); } m_ui->versionLabel->setText(version.isEmpty() ? QApplication::applicationVersion() : version); const auto &deps(dependencyVersions.size() ? dependencyVersions : CppUtilities::applicationInfo.dependencyVersions); if (!deps.empty()) { QStringList linkedAgainst; linkedAgainst.reserve(static_cast<int>(deps.size())); for (const auto &dependencyVersion : deps) { linkedAgainst << QString::fromUtf8(dependencyVersion); } m_ui->versionLabel->setToolTip(QStringLiteral("<p>") % tr("Linked against:") % QStringLiteral("</p><ul><li>") % linkedAgainst.join(QStringLiteral("</li><li>")) % QStringLiteral("</li></ul>")); } if (!website.isEmpty() || CppUtilities::applicationInfo.url) { m_ui->websiteLabel->setText(tr("For updates and bug reports visit the <a href=\"%1\" " "style=\"text-decoration: underline; color: palette(link);\">project " "website</a>.") .arg(!website.isEmpty() ? website : QString::fromUtf8(CppUtilities::applicationInfo.url))); } else { m_ui->websiteLabel->hide(); } m_ui->descLabel->setText(description.isEmpty() && CppUtilities::applicationInfo.description ? QString::fromUtf8(CppUtilities::applicationInfo.description) : description); m_iconScene = new QGraphicsScene(this); auto *item = image.isNull() ? new QGraphicsPixmapItem(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation, nullptr, this).pixmap(128)) : new QGraphicsPixmapItem(QPixmap::fromImage(image)); m_iconScene->addItem(item); m_ui->graphicsView->setScene(m_iconScene); m_ui->qtVersionLabel->setText(tr("Using <a href=\"qtversion\">Qt %1</a>").arg(QString::fromUtf8(qVersion()))); connect(m_ui->qtVersionLabel, &QLabel::linkActivated, this, &AboutDialog::linkActivated); centerWidget(this, parentWidget()); } /*! * \brief Constructs an about dialog with the specified information. */ AboutDialog::AboutDialog(QWidget *parent, const QString &applicationName, const QString &creator, const QString &version, const QString &website, const QString &description, const QImage &image) : AboutDialog(parent, applicationName, creator, version, {}, website, description, image) { } /*! * \brief Constructs an about dialog with the specified \a parent, \a * description and \a image. */ AboutDialog::AboutDialog(QWidget *parent, const QString &website, const QString &description, const QImage &image) : AboutDialog(parent, QString(), QString(), QString(), website, description, image) { } /*! * \brief Destroys the about dialog. */ AboutDialog::~AboutDialog() { } void AboutDialog::linkActivated(const QString &link) { if (link == QLatin1String("qtversion")) { QMessageBox::aboutQt(nullptr); } } } // namespace QtUtilities 07070100000008000081A4000000000000000000000001636534C9000004D6000000000000000000000000000000000000002600000000qtutilities/aboutdialog/aboutdialog.h#ifndef DIALOGS_ABOUTDIALOG_H #define DIALOGS_ABOUTDIALOG_H #include "../global.h" #include <QDialog> #include <memory> QT_FORWARD_DECLARE_CLASS(QGraphicsScene) namespace QtUtilities { namespace Ui { class AboutDialog; } class QT_UTILITIES_EXPORT AboutDialog : public QDialog { Q_OBJECT public: explicit AboutDialog(QWidget *parent, const QString &applicationName, const QString &creator, const QString &version, const QString &website = QString(), const QString &description = QString(), const QImage &image = QImage()); explicit AboutDialog(QWidget *parent, const QString &applicationName, const QString &creator, const QString &version, const std::vector<const char *> &dependencyVersions, const QString &website = QString(), const QString &description = QString(), const QImage &image = QImage()); explicit AboutDialog(QWidget *parent, const QString &website = QString(), const QString &description = QString(), const QImage &image = QImage()); ~AboutDialog() override; private Q_SLOTS: void linkActivated(const QString &link); private: std::unique_ptr<Ui::AboutDialog> m_ui; QGraphicsScene *m_iconScene; }; } // namespace QtUtilities #endif // DIALOGS_ABOUTDIALOG_H 07070100000009000081A4000000000000000000000001636534C90000229C000000000000000000000000000000000000002700000000qtutilities/aboutdialog/aboutdialog.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::AboutDialog</class> <widget class="QDialog" name="QtUtilities::AboutDialog"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>43</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="minimumSize"> <size> <width>480</width> <height>380</height> </size> </property> <property name="maximumSize"> <size> <width>480</width> <height>380</height> </size> </property> <property name="windowTitle"> <string>About</string> </property> <property name="whatsThis"> <string/> </property> <property name="locale"> <locale language="English" country="UnitedStates"/> </property> <property name="modal"> <bool>false</bool> </property> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> <number>0</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QWidget" name="mainWidget" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QFrame" name="informationFrame"> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Raised</enum> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QGraphicsView" name="graphicsView"> <property name="minimumSize"> <size> <width>32</width> <height>128</height> </size> </property> <property name="maximumSize"> <size> <width>16777215</width> <height>128</height> </size> </property> <property name="styleSheet"> <string notr="true">background: transparent;</string> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="lineWidth"> <number>0</number> </property> <property name="verticalScrollBarPolicy"> <enum>Qt::ScrollBarAlwaysOff</enum> </property> <property name="horizontalScrollBarPolicy"> <enum>Qt::ScrollBarAlwaysOff</enum> </property> <property name="foregroundBrush"> <brush brushstyle="NoBrush"> <color alpha="0"> <red>0</red> <green>0</green> <blue>0</blue> </color> </brush> </property> <property name="interactive"> <bool>false</bool> </property> </widget> </item> <item> <widget class="QLabel" name="productNameLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">font-size: 16pt;</string> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="frameShadow"> <enum>QFrame::Plain</enum> </property> <property name="lineWidth"> <number>1</number> </property> <property name="text"> <string>application name</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> </widget> </item> <item> <widget class="QLabel" name="versionLabel"> <property name="text"> <string>version</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> </widget> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="descLabel"> <property name="styleSheet"> <string notr="true">font-style: italic;</string> </property> <property name="text"> <string>description</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QLabel" name="websiteLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="mouseTracking"> <bool>true</bool> </property> <property name="text"> <string>website link</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="openExternalLinks"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> </property> </widget> </item> <item> <spacer name="verticalSpacer_2"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="creatorLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="styleSheet"> <string notr="true">font-size: 8pt;</string> </property> <property name="text"> <string>creators</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="openExternalLinks"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> </property> </widget> </item> <item> <widget class="QLabel" name="qtVersionLabel"> <property name="font"> <font> <pointsize>8</pointsize> </font> </property> <property name="styleSheet"> <string notr="true">font-size: 8pt;</string> </property> <property name="text"> <string>Using Qt</string> </property> <property name="alignment"> <set>Qt::AlignCenter</set> </property> <property name="wordWrap"> <bool>true</bool> </property> <property name="textInteractionFlags"> <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set> </property> </widget> </item> </layout> </widget> </item> </layout> </widget> </item> </layout> </widget> <resources/> <connections/> </ui> 0707010000000A000041ED000000000000000000000004636534C900000000000000000000000000000000000000000000001200000000qtutilities/cmake0707010000000B000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001A00000000qtutilities/cmake/modules0707010000000C000081A4000000000000000000000001636534C900004DAE000000000000000000000000000000000000002B00000000qtutilities/cmake/modules/AndroidApk.cmakecmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # adds a target to create an Android APK with the help of androiddeployqt if target platform is Android if (NOT ANDROID) return() endif () if (NOT BASIC_PROJECT_CONFIG_DONE OR NOT QT_CONFIGURED) message(FATAL_ERROR "Before including the ApkConfig module, the AppTarget module and QtConfig module must be included.") endif () if (ANDROID_APK_CONFIGURED) message(FATAL_ERROR "The AndroidApk module mustn't be included twice.") endif () # check paths of Android SDK and NDK if (EXISTS "${ANDROID_SDK}") set(ANDROID_APK_SDK "${ANDROID_SDK}") elseif (EXISTS "${CMAKE_ANDROID_SDK}") set(ANDROID_APK_SDK "${CMAKE_ANDROID_SDK}") elseif (EXISTS "$ENV{ANDROID_HOME}") set(ANDROID_APK_SDK "$ENV{ANDROID_HOME}") else () message(FATAL_ERROR "ANDROID_SDK must contain the path of the Android SDK (for passing to androiddeployqt).") endif () if (EXISTS "${ANDROID_NDK}") set(ANDROID_APK_NDK "${ANDROID_NDK}") elseif (EXISTS "${CMAKE_ANDROID_NDK}") set(ANDROID_APK_NDK "${CMAKE_ANDROID_NDK}") elseif (EXISTS "$ENV{ANDROID_NDK_HOME}") set(ANDROID_APK_NDK "$ENV{ANDROID_NDK_HOME}") else () message(FATAL_ERROR "ANDROID_NDK must contain the path of the Android NDK (for passing to androiddeployqt).") endif () # set min/target SDK versions set(ANDROID_MIN_SDK_VERSION "${CMAKE_SYSTEM_VERSION}" CACHE STRING "specifies the minimum SDK version") set(ANDROID_TARGET_SDK_VERSION "30" CACHE STRING "specifies the target SDK version") # determine some variables if (NOT META_ANDROID_PACKAGE_NAME) message(FATAL_ERROR "Attempt to load AndroidApk.cmake without having set ANDROID_PACKAGE_NAME.") endif () set(ANDROID_APK_APPLICATION_ID_SUFFIX "" CACHE STRING "suffix for Android APK ID, use e.g. \".debug\" to produce a co-installable debug version") set(ANDROID_APK_APPLICATION_LABEL "${META_APP_NAME}" CACHE STRING "user visible name for the APK") # find "android" subdirectory in the source directory and check for AndroidManifest.xml set(ANDROID_APK_SUBDIR "${CMAKE_CURRENT_SOURCE_DIR}/android") if (NOT IS_DIRECTORY "${ANDROID_APK_SUBDIR}") message( FATAL_ERROR "The directory containing Android-specific files is expected to be \"${ANDROID_APK_SUBDIR}\" but doesn't exist.") endif () set(ANDROID_APK_MANIFEST_PATH "${ANDROID_APK_SUBDIR}/AndroidManifest.xml") if (NOT EXISTS "${ANDROID_APK_MANIFEST_PATH}" AND NOT EXISTS "${ANDROID_APK_MANIFEST_PATH}.in") message(FATAL_ERROR "The Android manifest doesn't exist at \"${ANDROID_APK_SUBDIR}/AndroidManifest.xml\".") endif () # link Android package directory to binary directory evaluating templates on top-level set(ANDROID_PACKAGE_SOURCE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/android-package-source-directory") file(MAKE_DIRECTORY "${ANDROID_PACKAGE_SOURCE_DIRECTORY}") file( GLOB_RECURSE ANDROID_APK_FILES LIST_DIRECTORIES false RELATIVE "${ANDROID_APK_SUBDIR}" "${ANDROID_APK_SUBDIR}/*") set(ANDROID_SOURCE_DIRECTORY_FILES) foreach (ANDROID_APK_FILE ${ANDROID_APK_FILES}) if (IS_DIRECTORY "${ANDROID_APK_FILE}") continue() endif () get_filename_component(ANDROID_APK_FILE_DIR ${ANDROID_APK_FILE} DIRECTORY) if (ANDROID_APK_FILE_DIR) set(ANDROID_APK_FILE_DESTINATION "${ANDROID_PACKAGE_SOURCE_DIRECTORY}/${ANDROID_APK_FILE_DIR}") file(MAKE_DIRECTORY "${ANDROID_APK_FILE_DESTINATION}") else () set(ANDROID_APK_FILE_DESTINATION "${ANDROID_PACKAGE_SOURCE_DIRECTORY}") endif () get_filename_component(ANDROID_APK_FILE_EXT ${ANDROID_APK_FILE} LAST_EXT) if (ANDROID_APK_FILE_EXT STREQUAL ".in") string(LENGTH "${ANDROID_APK_FILE}" ANDROID_APK_FILE_LENGTH) math(EXPR ANDROID_APK_FILE_LENGTH "${ANDROID_APK_FILE_LENGTH} - 3") string(SUBSTRING "${ANDROID_APK_FILE}" 0 ${ANDROID_APK_FILE_LENGTH} ANDROID_APK_FILE_NAME) configure_file("${ANDROID_APK_SUBDIR}/${ANDROID_APK_FILE}" "${ANDROID_PACKAGE_SOURCE_DIRECTORY}/${ANDROID_APK_FILE_NAME}") set(ANDROID_APK_FILE "${ANDROID_APK_FILE_NAME}") else () file(COPY "${ANDROID_APK_SUBDIR}/${ANDROID_APK_FILE}" DESTINATION "${ANDROID_APK_FILE_DESTINATION}") endif () # FIXME: tracking these deps doesn't work endforeach () # make subdirectory to store build artefacts for APK set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk") file(MAKE_DIRECTORY "${ANDROID_APK_BUILD_DIR}") # find Qt get_filename_component(ANDROID_APK_QT_CMAKE_DIR "${Qt5Core_DIR}" DIRECTORY) get_filename_component(ANDROID_APK_QT_LIBRARY_DIR "${ANDROID_APK_QT_CMAKE_DIR}" DIRECTORY) get_filename_component(ANDROID_APK_QT_INSTALL_PREFIX "${ANDROID_APK_QT_LIBRARY_DIR}" DIRECTORY) # deduce Android toolchain prefix from "CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX" set(ANDROID_APK_USE_LLVM false) if (CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX MATCHES ".*/toolchains/llvm/.*") set(ANDROID_APK_TOOL_PREFIX "llvm") set(ANDROID_APK_USE_LLVM true) elseif (CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX MATCHES ".*/(.+)-") set(ANDROID_APK_TOOL_PREFIX "${CMAKE_MATCH_1}") else () set(ANDROID_APK_TOOL_PREFIX "${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}") endif () message(STATUS "Android toolchain prefix: ${ANDROID_APK_TOOL_PREFIX}") # deduce Android toolchain version from various variables (not required when using LLVM) set(ANDROID_APK_TOOLCHAIN_VERSION "" CACHE STRING "toolchain version for making APK file") if (NOT ANDROID_APK_TOOLCHAIN_VERSION AND NOT ANDROID_APK_TOOL_PREFIX STREQUAL "llvm") if (CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX MATCHES ".*/.+-linux-android-([^/]+)/.*") set(ANDROID_APK_TOOLCHAIN_VERSION "${CMAKE_MATCH_1}") elseif (CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX MATCHES ".*/.+-linux-androideabi-([^/]+)/.*") set(ANDROID_APK_TOOLCHAIN_VERSION "${CMAKE_MATCH_1}") elseif (NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION MATCHES "clang.*") set(ANDROID_APK_TOOLCHAIN_VERSION "${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}") else () message( FATAL_ERROR "Unable to detect the toolchain version (for passing it to androiddeployqt)." "Please set ANDROID_APK_TOOLCHAIN_VERSION manually. Related variables:\n" "CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX: ${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}\n" "CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION: ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION}") endif () message(STATUS "Auto-detected ANDROID_APK_TOOLCHAIN_VERSION: ${ANDROID_APK_TOOLCHAIN_VERSION}") endif () # determine Android build tools version - note: Assuming the build tools are installed under "${ANDROID_APK_SDK}/build-tools" file( GLOB ANDROID_APK_BUILD_TOOLS_VERSIONS LIST_DIRECTORIES TRUE RELATIVE "${ANDROID_APK_SDK}/build-tools" "${ANDROID_APK_SDK}/build-tools/*") if (NOT ANDROID_APK_BUILD_TOOLS_VERSIONS) message(FATAL_ERROR "No build-tools present under \"${ANDROID_APK_SDK}/build-tools\".") endif () list(GET ANDROID_APK_BUILD_TOOLS_VERSIONS 0 ANDROID_APK_BUILD_TOOLS_VERSION) # deduce path of C++ standard library from "CMAKE_CXX_STANDARD_LIBRARIES" - note: Assuming CMAKE_CXX_STANDARD_LIBRARIES # contains a paths or quotes paths with flags appended. set(ANDROID_APK_CXX_STANDARD_LIBRARY "" CACHE STRING "path to standard library for making APK file") if (NOT ANDROID_APK_CXX_STANDARD_LIBRARY AND CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX MATCHES "(.*/toolchains/llvm/.*)/bin/.*") message(STATUS "CMAKE_MATCH_1: ${CMAKE_MATCH_1}") set(ANDROID_APK_CXX_STANDARD_LIBRARY "${CMAKE_MATCH_1}/sysroot/usr/lib") if (NOT EXISTS "${ANDROID_APK_CXX_STANDARD_LIBRARY}") unset(ANDROID_APK_CXX_STANDARD_LIBRARY) endif () endif () if (NOT ANDROID_APK_CXX_STANDARD_LIBRARY) foreach (CMAKE_CXX_STANDARD_LIBRARY ${CMAKE_CXX_STANDARD_LIBRARIES}) if (EXISTS "${CMAKE_CXX_STANDARD_LIBRARY}") set(ANDROID_APK_CXX_STANDARD_LIBRARY "${CMAKE_CXX_STANDARD_LIBRARY}") break() elseif (CMAKE_CXX_STANDARD_LIBRARY MATCHES "\"(.*)\".*") if (EXISTS "${CMAKE_MATCH_1}") set(ANDROID_APK_CXX_STANDARD_LIBRARY "${CMAKE_MATCH_1}") break() endif () elseif (CMAKE_CXX_STANDARD_LIBRARY MATCHES "-l.*") continue() endif () message(WARNING "Library \"${CMAKE_CXX_STANDARD_LIBRARY}\" from CMAKE_CXX_STANDARD_LIBRARIES does not exist.") endforeach () endif () if (NOT ANDROID_APK_CXX_STANDARD_LIBRARY) message( FATAL_ERROR "Unable to detect path of standard library (for passing it to androiddeployqt)." "Please set ANDROID_APK_CXX_STANDARD_LIBRARY manually. Related variables:\n" "CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX: ${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}\n" "CMAKE_CXX_STANDARD_LIBRARIES: ${CMAKE_CXX_STANDARD_LIBRARIES}") endif () # determine extra prefix dirs set(ANDROID_APK_BINARY_DIRS "${RUNTIME_LIBRARY_PATH}") if (NOT CMAKE_CURRENT_BINARY_DIR IN_LIST ANDROID_APK_BINARY_DIRS) list(APPEND ANDROID_APK_BINARY_DIRS "${CMAKE_CURRENT_BINARY_DIR}") endif () set(ANDROID_APK_BINARY_DIRS_DEPENDS "") foreach (PATH ${ANDROID_APK_BINARY_DIRS}) # symlink "lib" subdirectory so androiddeployqt finds the library in the runtime path when specified via # "extraPrefixDirs" list(APPEND ANDROID_APK_BINARY_DIRS_DEPENDS "${PATH}/lib") add_custom_command(OUTPUT "${PATH}/lib" COMMAND "${CMAKE_COMMAND}" -E create_symlink "${PATH}" "${PATH}/lib") endforeach () include(ListToString) list_to_string("" "\n \"" "\"," "${ANDROID_APK_BINARY_DIRS}" ANDROID_APK_BINARY_DIRS) # find dependencies - note: androiddeployqt seems to find only Qt libraries and plugins but misses other # target_link_libraries set(ANDROID_APK_ADDITIONAL_LIBS "" CACHE STRING "additional libraries to be bundled into the Android APK") set(ANDROID_APK_EXTRA_LIBS "${ANDROID_APK_ADDITIONAL_LIBS}") function (add_android_apk_extra_libs TARGET_NAME) get_target_property(ANDROID_APK_EXTRA_LIBS_FOR_TARGET ${TARGET_NAME} LINK_LIBRARIES) if (NOT ANDROID_APK_EXTRA_LIBS_FOR_TARGET) return() endif () foreach (LIBRARY ${ANDROID_APK_EXTRA_LIBS_FOR_TARGET}) if (TARGET "${LIBRARY}") list(APPEND ANDROID_APK_EXTRA_LIBS "\$<TARGET_FILE:${LIBRARY}>") add_android_apk_extra_libs(${LIBRARY}) elseif (EXISTS "${LIBRARY}") list(APPEND ANDROID_APK_EXTRA_LIBS "${LIBRARY}") else () message( STATUS "Unable to find library \"${LIBRARY}\" required by target \"${TARGET_NAME}\". The library is likely " "a private dependency of the target and therefore not visible within the context of creating the " "final application. Relying on androiddeployqt for adding it to the APK.") endif () endforeach () set(ANDROID_APK_EXTRA_LIBS "${ANDROID_APK_EXTRA_LIBS}" PARENT_SCOPE) endfunction () add_android_apk_extra_libs("${META_TARGET_NAME}") list_to_string("," "" "" "${ANDROID_APK_EXTRA_LIBS}" ANDROID_APK_EXTRA_LIBS) # determine host architecture - note: ANDROID_HOST_TAG is set supposed to be set by the NDK toolchain file. If not, fallback # to CMake's CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG variable. if (NOT ANDROID_HOST_TAG) set(ANDROID_HOST_TAG "${CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG}") endif () # determine Android architecture - note: ANDROID_ABI is set supposed to be set by the NDK toolchain file. If not, fallback to # CMake's CMAKE_ANDROID_ARCH_ABI variable. if (NOT ANDROID_ABI) set(ANDROID_ABI "${CMAKE_ANDROID_ARCH_ABI}") endif () set(ANDROID_APK_SYSROOT_NAME "" CACHE STRING "name of the sysroot for making APK file") if (CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX MATCHES ".*/bin/(.*)-") set(ANDROID_APK_SYSROOT_NAME "${CMAKE_MATCH_1}") else () message( FATAL_ERROR "Unable to sysroot name (for passing it to androiddeployqt)." "Please set ANDROID_APK_SYSROOT_NAME manually. Related variables:\n" "CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX: ${CMAKE_CXX_ANDROID_TOOLCHAIN_PREFIX}") endif () # set application binary if (Qt5Core_VERSION VERSION_LESS 5.14.0) set(ANDROID_APK_APP_BINARY "\$<TARGET_FILE:${META_TARGET_NAME}>") else () set(ANDROID_APK_APP_BINARY "${META_TARGET_NAME}") endif () # query certain qmake variables foreach (QMAKE_VARIABLE QT_INSTALL_QML QT_INSTALL_PLUGINS QT_INSTALL_IMPORTS) query_qmake_variable(${QMAKE_VARIABLE}) endforeach () # define function to get a list of (existing) paths function (compose_dirs_for_android_apk) # parse arguments set(OPTIONAL_ARGS) set(ONE_VALUE_ARGS OUTPUT_VARIABLE) set(MULTI_VALUE_ARGS POSSIBLE_DIRS) cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) list(REMOVE_DUPLICATES ARGS_POSSIBLE_DIRS) unset(DIRS) foreach (POSSIBLE_DIR ${ARGS_POSSIBLE_DIRS}) if (IS_DIRECTORY "${POSSIBLE_DIR}") list(APPEND DIRS "${POSSIBLE_DIR}") endif () endforeach () list_to_string("," "" "" "${DIRS}" DIRS) set("${ARGS_OUTPUT_VARIABLE}" "${DIRS}" PARENT_SCOPE) endfunction () # pick QML import paths from install prefix compose_dirs_for_android_apk( OUTPUT_VARIABLE ANDROID_APK_QML_IMPORT_DIRS POSSIBLE_DIRS "${QT_INSTALL_IMPORTS}" "${QT_INSTALL_QML}" "${CMAKE_INSTALL_FULL_LIBDIR}${SELECTED_LIB_SUFFIX}/qt/imports" "${CMAKE_INSTALL_FULL_LIBDIR}${SELECTED_LIB_SUFFIX}/imports" "${CMAKE_INSTALL_FULL_LIBDIR}${SELECTED_LIB_SUFFIX}/qt/qml" "${CMAKE_INSTALL_FULL_LIBDIR}${SELECTED_LIB_SUFFIX}/qml") if (NOT ANDROID_APK_QML_IMPORT_DIRS) message(WARNING "Unable to find QML import directories for making the APK.") endif () # pick extra plugins from install prefix compose_dirs_for_android_apk( OUTPUT_VARIABLE ANDROID_APK_EXTRA_PLUGIN_DIRS POSSIBLE_DIRS "${QT_INSTALL_PLUGINS}" "${CMAKE_INSTALL_FULL_LIBDIR}${SELECTED_LIB_SUFFIX}/qt/plugins" "${CMAKE_INSTALL_FULL_LIBDIR}${SELECTED_LIB_SUFFIX}/plugins") if (NOT ANDROID_APK_EXTRA_PLUGIN_DIRS) message(WARNING "Unable to find extra plugin directories for making the APK.") endif () # find template for deployment JSON find_template_file("android-deployment.json" QT_UTILITIES ANDROID_DEPLOYMENT_JSON_TEMPLATE_FILE) set(ANDROID_DEPLOYMENT_JSON_FILE "${CMAKE_CURRENT_BINARY_DIR}/android-deployment.json") configure_file("${ANDROID_DEPLOYMENT_JSON_TEMPLATE_FILE}" "${ANDROID_DEPLOYMENT_JSON_FILE}.configured") file( GENERATE OUTPUT "${ANDROID_DEPLOYMENT_JSON_FILE}" INPUT "${ANDROID_DEPLOYMENT_JSON_FILE}.configured") # pass make arguments if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") set(MAKE_ARGUMENTS "\\$(ARGS)") endif () # add rules to make APK option(ANDROID_APK_FORCE_DEBUG "specifies whether a debug APK should be created even when building in release mode" OFF) if (Qt5Core_VERSION VERSION_LESS 5.12.0) set(ANDROID_APK_FILE_DIRECTORY "") else () if (ANDROID_APK_FORCE_DEBUG OR CMAKE_BUILD_TYPE STREQUAL "Debug") set(ANDROID_APK_FILE_DIRECTORY "debug/") else () set(ANDROID_APK_FILE_DIRECTORY "release/") endif () endif () if (ANDROID_APK_FORCE_DEBUG OR CMAKE_BUILD_TYPE STREQUAL "Debug") set(ANDROID_APK_FILE_PATH "${ANDROID_APK_BUILD_DIR}/build/outputs/apk/${ANDROID_APK_FILE_DIRECTORY}apk-debug.apk") set(ANDROID_APK_ADDITIONAL_ANDROIDDEPOYQT_OPTIONS) else () set(ANDROID_APK_ADDITIONAL_ANDROIDDEPOYQT_OPTIONS --release) set(ANDROID_APK_KEYSTORE_URL "" CACHE STRING "keystore URL for signing the Android APK") set(ANDROID_APK_KEYSTORE_ALIAS "" CACHE STRING "keystore alias for signing the Android APK") set(ANDROID_APK_KEYSTORE_PASSWORD "" CACHE STRING "keystore password for signing the Android APK") set(ANDROID_APK_KEYSTORE_KEY_PASSWORD "" CACHE STRING "keystore key password for signing the Android APK") if (ANDROID_APK_KEYSTORE_URL AND ANDROID_APK_KEYSTORE_ALIAS) set(ANDROID_APK_FILE_PATH "${ANDROID_APK_BUILD_DIR}/build/outputs/apk/${ANDROID_APK_FILE_DIRECTORY}apk-release-signed.apk") list(APPEND ANDROID_APK_ADDITIONAL_ANDROIDDEPOYQT_OPTIONS --sign "${ANDROID_APK_KEYSTORE_URL}" "${ANDROID_APK_KEYSTORE_ALIAS}") if (ANDROID_APK_KEYSTORE_PASSWORD) list(APPEND ANDROID_APK_ADDITIONAL_ANDROIDDEPOYQT_OPTIONS --storepass "${ANDROID_APK_KEYSTORE_PASSWORD}") endif () if (ANDROID_APK_KEYSTORE_KEY_PASSWORD) list(APPEND ANDROID_APK_ADDITIONAL_ANDROIDDEPOYQT_OPTIONS --keypass "${ANDROID_APK_KEYSTORE_KEY_PASSWORD}") endif () else () set(ANDROID_APK_FILE_PATH "${ANDROID_APK_BUILD_DIR}/build/outputs/apk/${ANDROID_APK_FILE_DIRECTORY}apk-release-unsigned.apk") message(WARNING "Set ANDROID_APK_KEYSTORE_URL/ANDROID_APK_KEYSTORE_ALIAS to sign Android APK release.") endif () endif () if (Qt5Core_VERSION VERSION_LESS 5.14.0) set(ANDROID_APK_BINARY_PATH "${ANDROID_APK_BUILD_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/lib${META_TARGET_NAME}.so") else () # incorporate the ANDROID_ABI into the target name because androiddeployqt > 5.14 forces use to do so set_target_properties(${META_TARGET_NAME} PROPERTIES SUFFIX "_${ANDROID_ABI}.so") set(ANDROID_APK_BINARY_PATH "${ANDROID_APK_BUILD_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI}/lib${META_TARGET_NAME}_${ANDROID_ABI}.so") endif () add_custom_command( OUTPUT "${ANDROID_APK_BINARY_PATH}" COMMAND "${CMAKE_COMMAND}" -E copy "$<TARGET_FILE:${META_TARGET_NAME}>" "${ANDROID_APK_BINARY_PATH}" COMMENT "Preparing build dir for Android APK" DEPENDS "${META_TARGET_NAME}" COMMAND_EXPAND_LISTS VERBATIM) add_custom_command( OUTPUT "${ANDROID_APK_FILE_PATH}" COMMAND rm -r "${ANDROID_APK_BUILD_DIR}/assets/--Added-by-androiddeployqt--/lib" || true COMMAND $<TARGET_FILE_DIR:Qt5::qmake>/androiddeployqt --gradle --input "${ANDROID_DEPLOYMENT_JSON_FILE}" --output "${ANDROID_APK_BUILD_DIR}" --deployment bundled ${MAKE_ARGUMENTS} --verbose ${ANDROID_APK_ADDITIONAL_ANDROIDDEPOYQT_OPTIONS} WORKING_DIRECTORY "${ANDROID_APK_BUILD_DIR}" COMMENT "Creating Android APK ${ANDROID_APK_FILE_PATH} using androiddeployqt" DEPENDS "${ANDROID_DEPLOYMENT_JSON_FILE};${ANDROID_APK_BINARY_PATH};${ANDROID_SOURCE_DIRECTORY_FILES};${ANDROID_APK_BINARY_DIRS_DEPENDS}" COMMAND_EXPAND_LISTS VERBATIM) add_custom_target( "${META_TARGET_NAME}_apk" COMMENT "Android APK" DEPENDS "${ANDROID_APK_FILE_PATH}") if (NOT TARGET apk) add_custom_target(apk) endif () add_dependencies(apk "${META_TARGET_NAME}_apk") # add install target for APK if (CMAKE_BUILD_TYPE STREQUAL "Debug") set(ANDROID_APK_FINAL_NAME "${META_ID}-debug-${META_APP_VERSION}.apk") else () set(ANDROID_APK_FINAL_NAME "${META_ID}-${META_APP_VERSION}.apk") endif () install( FILES "${ANDROID_APK_FILE_PATH}" DESTINATION "share/apk" RENAME "${ANDROID_APK_FINAL_NAME}" COMPONENT apk) add_custom_target("${META_TARGET_NAME}_install_apk" COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=apk -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") add_dependencies("${META_TARGET_NAME}_install_apk" "${META_TARGET_NAME}_apk") if (NOT TARGET install-apk) add_custom_target(install-apk) endif () add_dependencies(install-apk "${META_TARGET_NAME}_install_apk") # add deploy target for APK find_program(ADB_BIN adb) add_custom_target( "${META_TARGET_NAME}_deploy_apk" COMMAND "${ADB_BIN}" install -r "${ANDROID_APK_FILE_PATH}" COMMENT "Deploying Android APK" DEPENDS "${ANDROID_APK_FILE_PATH}") set(ANDROID_APK_CONFIGURED YES) 0707010000000D000081A4000000000000000000000001636534C9000070E7000000000000000000000000000000000000002900000000qtutilities/cmake/modules/QtConfig.cmakecmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # applies Qt specific configuration notes: For GUI applications, QtGuiConfig must be included before. This module must always # be included before AppTarget/LibraryTarget. # ensure generated sources are processed by AUTOMOC and AUTOUIC if (POLICY CMP0071) cmake_policy(SET CMP0071 NEW) endif () # verify inclusion order if (NOT BASIC_PROJECT_CONFIG_DONE) message(FATAL_ERROR "Before including the QtConfig module, the BasicConfig module must be included.") endif () if (QT_CONFIGURED) message(FATAL_ERROR "The QtConfig module can not be included when Qt usage has already been configured.") endif () if (TARGET_CONFIG_DONE) message(FATAL_ERROR "Can not include QtConfig module when targets are already configured.") endif () # include required modules include(ListToString) include(TemplateFinder) include(QtLinkage) # add the Core module as it is always required and also add additional Qt/KF modules which must have been specified before if # required note: The Gui/Widgets/Quick modules should be added by including QtGuiConfig. set(QT_REPOS ${ADDITIONAL_QT_REPOS} base) set(QT_MODULES ${ADDITIONAL_QT_MODULES} Core) set(KF_MODULES ${ADDITIONAL_KF_MODULES}) # disable auto-inclusion of QML plugins because these seem to pull in unwanted dependencies like PostgreSQL (required as of # Qt 6) set(QT_SKIP_AUTO_QML_PLUGIN_INCLUSION ON) # disable deprecated features option(DISABLE_DEPRECATED_QT_FEATURES "specifies whether deprecated Qt features should be disabled" OFF) if (DISABLE_DEPRECATED_QT_FEATURES) list(APPEND META_PRIVATE_COMPILE_DEFINITIONS QT_DEPRECATED_WARNINGS QT_DISABLE_DEPRECATED_BEFORE=0x060000) endif () # allow specifying a custom directory for Qt plugins set(QT_PLUGIN_DIR "" CACHE STRING "specifies the directory to install Qt plugins") # check whether D-Bus interfaces need to be processed if (DBUS_FILES) message(STATUS "Project has D-Bus interface declarations which will be processed.") # the D-Bus Qt module is required list(APPEND QT_MODULES DBus) endif () # remove duplicates list(REMOVE_DUPLICATES QT_REPOS) list(REMOVE_DUPLICATES QT_MODULES) if (IMPORTED_QT_MODULES) list(REMOVE_DUPLICATES IMPORTED_QT_MODULES) endif () if (KF_MODULES) list(REMOVE_DUPLICATES KF_MODULES) endif () if (IMPORTED_KF_MODULES) list(REMOVE_DUPLICATES IMPORTED_KF_MODULES) endif () # find and use the required Qt/KF modules set(QT_PACKAGE_PREFIX "Qt5" CACHE STRING "specifies the prefix for Qt packages") string(TOLOWER "${QT_PACKAGE_PREFIX}" QT_PACKAGE_PREFIX_LOWER) set(QT_LINGUIST_TOOLS_PACKAGE "${QT_PACKAGE_PREFIX}LinguistTools") set(QT_QMAKE_TARGET "${QT_PACKAGE_PREFIX}::qmake") foreach (MODULE ${QT_MODULES}) unset(MODULE_OPTIONS) if ("${MODULE}" IN_LIST META_PUBLIC_QT_MODULES) list(APPEND MODULE_OPTIONS VISIBILITY PUBLIC) endif () use_qt_module(PREFIX "${QT_PACKAGE_PREFIX}" MODULE "${MODULE}" ${MODULE_OPTIONS}) endforeach () set(KF_PACKAGE_PREFIX "KF5" CACHE STRING "specifies the prefix for KDE Frameworks packages") foreach (MODULE ${KF_MODULES}) unset(MODULE_OPTIONS) if ("${MODULE}" IN_LIST META_PUBLIC_KF_MODULES) list(APPEND MODULE_OPTIONS VISIBILITY PUBLIC) endif () use_qt_module(PREFIX "${KF_PACKAGE_PREFIX}" MODULE "${MODULE}" ${MODULE_OPTIONS}) endforeach () # hacks for using static Qt 5 via "StaticQt5" prefix: find regular Qt5Core module as well so Qt version is defined and use # regular Qt5LinguistTools module and regular qmake target if (QT_PACKAGE_PREFIX STREQUAL "StaticQt5") find_package(Qt5Core) set(QT_LINGUIST_TOOLS_PACKAGE Qt5LinguistTools) set(QT_QMAKE_TARGET Qt5::qmake) endif () # find transitively required Qt/KF modules foreach (MODULE ${IMPORTED_QT_MODULES}) if (NOT "${QT_MODULE}" IN_LIST QT_MODULES) find_package("${QT_PACKAGE_PREFIX}${MODULE}" REQUIRED) endif () endforeach () foreach (MODULE ${IMPORTED_KF_MODULES}) if (NOT "${KF_MODULE}" IN_LIST KF_MODULES) find_package("${KF_PACKAGE_PREFIX}${MODULE}" REQUIRED) endif () endforeach () # enable TLS support by default when using Qt Network (relevant for linking against static plugin) if (NOT DEFINED TLS_SUPPORT) if (Network IN_LIST QT_MODULES OR Network IN_LIST IMPORTED_QT_MODULES) set(TLS_SUPPORT ON) else () set(TLS_SUPPORT OFF) endif () endif () # built-in platform, imageformat and iconengine plugins when linking statically against Qt if (TARGET "${QT_PACKAGE_PREFIX}::Core") get_target_property(QT_TARGET_TYPE "${QT_PACKAGE_PREFIX}::Core" TYPE) endif () if (STATIC_LINKAGE OR QT_TARGET_TYPE STREQUAL STATIC_LIBRARY) if (META_PROJECT_IS_APPLICATION) set(QT_PLUGINS_LIBRARIES_VARIABLE PRIVATE_LIBRARIES) else () set(QT_PLUGINS_LIBRARIES_VARIABLE QT_TEST_LIBRARIES) endif () message( STATUS "Linking ${META_PROJECT_NAME} (${QT_PLUGINS_LIBRARIES_VARIABLE}) against Qt plugins because static linkage is enabled or a static Qt build is used." ) if (Gui IN_LIST QT_MODULES OR Widgets IN_LIST QT_MODULES OR Quick IN_LIST QT_MODULES) if (WIN32 AND TARGET "${QT_PACKAGE_PREFIX}::QWindowsIntegrationPlugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Gui PLUGINS WindowsIntegration ONLY_PLUGINS) elseif (APPLE AND TARGET "${QT_PACKAGE_PREFIX}::QCocoaIntegrationPlugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Gui PLUGINS CocoaIntegration ONLY_PLUGINS) endif () if (UNIX AND TARGET "${QT_PACKAGE_PREFIX}::QOffscreenIntegrationPlugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Gui PLUGINS OffscreenIntegration ONLY_PLUGINS) endif () if (TARGET "${QT_PACKAGE_PREFIX}::QXcbIntegrationPlugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Gui PLUGINS XcbIntegration ONLY_PLUGINS) endif () if (TARGET "${QT_PACKAGE_PREFIX}::QWaylandIntegrationPlugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Gui PLUGINS WaylandIntegration ONLY_PLUGINS) use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE WaylandClient PLUGINS WaylandXdgShellIntegration WaylandWlShellIntegration WaylandIviShellIntegration WaylandQtShellIntegration ONLY_PLUGINS PLUGINS_OPTIONAL) endif () endif () # ensure a TLS plugin is built-in when available and when creating an app using Qt Network - required since Qt 6.2.0 # which "pluginized" TLS support if (TLS_SUPPORT) set(KNOWN_TLS_PLUGINS ${META_TLS_PLUGINS} SchannelBackend TlsBackendOpenSSL) set(USED_TLS_PLUGINS) foreach (TLS_PLUGIN ${KNOWN_TLS_PLUGINS}) if (TARGET "${QT_PACKAGE_PREFIX}::Q${TLS_PLUGIN}Plugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Network PLUGINS ${TLS_PLUGIN} ONLY_PLUGINS) list(APPEND USED_TLS_PLUGINS "${TLS_PLUGIN}") break() # one plugin is sufficient endif () endforeach () # allow importing TLS plugins via qtconfig.h if (USED_TLS_PLUGINS) list_to_string(" " "\\\n Q_IMPORT_PLUGIN(Q" ")" "${USED_TLS_PLUGINS}" USED_TLS_PLUGINS_ARRAY) endif () endif () # ensure all available widget style plugins are built-in when creating a Qt Widgets application - required since Qt 5.10 # because the styles have been "pluginized" (see commit 4f3249f) set(KNOWN_WIDGET_STYLE_PLUGINS WindowsVistaStyle MacStyle AndroidStyle) set(USED_WIDGET_STYLE_PLUGINS) if (Widgets IN_LIST QT_MODULES) foreach (WIDGET_STYLE_PLUGIN ${KNOWN_WIDGET_STYLE_PLUGINS}) if (TARGET "${QT_PACKAGE_PREFIX}::Q${WIDGET_STYLE_PLUGIN}Plugin") use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Widgets PLUGINS ${WIDGET_STYLE_PLUGIN} ONLY_PLUGINS) list(APPEND USED_WIDGET_STYLE_PLUGINS "${WIDGET_STYLE_PLUGIN}") endif () endforeach () # allow importing widget style plugins via qtconfig.h if (USED_WIDGET_STYLE_PLUGINS) list_to_string(" " "\\\n Q_IMPORT_PLUGIN(Q" "Plugin)" "${USED_WIDGET_STYLE_PLUGINS}" WIDGET_STYLE_PLUGINS_ARRAY) endif () endif () # ensure image format plugins (beside SVG) are built-in if configured if (IMAGE_FORMAT_SUPPORT) foreach (IMAGE_FORMAT ${IMAGE_FORMAT_SUPPORT}) if (IMAGE_FORMAT EQUAL "Svg") # the image format plugin of the Qt Svg module is handled separately set(SVG_SUPPORT ON) list(REMOVE_ITEM IMAGE_FORMAT_SUPPORT Svg) else () use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Gui PLUGINS ${IMAGE_FORMAT} ONLY_PLUGINS) endif () endforeach () # allow importing image format plugins via qtconfig.h list_to_string(" " "\\\n Q_IMPORT_PLUGIN(Q" "Plugin)" "${IMAGE_FORMAT_SUPPORT}" IMAGE_FORMAT_SUPPORT_ARRAY) endif () # ensure SVG plugins are built-in if configured if ((SVG_SUPPORT OR SVG_ICON_SUPPORT) AND NOT Svg IN_LIST QT_MODULES) use_qt_module(LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Svg) endif () if (SVG_SUPPORT) use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Svg PLUGINS Svg ONLY_PLUGINS) endif () if (SVG_ICON_SUPPORT) use_qt_module( LIBRARIES_VARIABLE "${QT_PLUGINS_LIBRARIES_VARIABLE}" PREFIX "${QT_PACKAGE_PREFIX}" MODULE Svg PLUGINS SvgIcon ONLY_PLUGINS) endif () endif () # option for built-in translations option(BUILTIN_TRANSLATIONS "enables/disables built-in translations when building applications and libraries" OFF) option(BUILTIN_TRANSLATIONS_OF_QT "enables/disables built-in translations of Qt itself when building applications" "${BUILTIN_TRANSLATIONS}") # determine relevant Qt translation files set(QT_TRANSLATION_FILES) set(QT_TRANSLATION_SEARCH_PATHS) query_qmake_variable_path(QT_INSTALL_TRANSLATIONS) if (QT_INSTALL_TRANSLATIONS) list(APPEND QT_TRANSLATION_SEARCH_PATHS "${QT_INSTALL_TRANSLATIONS}") endif () if (CMAKE_FIND_ROOT_PATH) foreach (ROOT_PATH ${CMAKE_FIND_ROOT_PATH}) list(APPEND QT_TRANSLATION_SEARCH_PATHS "${ROOT_PATH}/${CMAKE_INSTALL_DATAROOTDIR}/qt/translations" "${ROOT_PATH}/${CMAKE_INSTALL_DATAROOTDIR}/${QT_PACKAGE_PREFIX_LOWER}/translations") endforeach () endif () list( APPEND QT_TRANSLATION_SEARCH_PATHS "${CMAKE_INSTALL_FULL_DATAROOTDIR}/qt/translations" "${CMAKE_INSTALL_FULL_DATAROOTDIR}/${QT_PACKAGE_PREFIX_LOWER}/translations" "/usr/${CMAKE_INSTALL_DATAROOTDIR}/qt/translations" "/usr/${CMAKE_INSTALL_DATAROOTDIR}/${QT_PACKAGE_PREFIX_LOWER}/translations") list(REMOVE_DUPLICATES QT_TRANSLATION_SEARCH_PATHS) set(QT_TRANSLATIONS_FOUND NO) foreach (QT_TRANSLATION_PATH ${QT_TRANSLATION_SEARCH_PATHS}) if (NOT IS_DIRECTORY "${QT_TRANSLATION_PATH}") continue() endif () foreach (QT_REPO ${QT_REPOS}) file(GLOB QT_QM_FILES "${QT_TRANSLATION_PATH}/qt${QT_REPO}_*.qm") if (NOT QT_QM_FILES) continue() endif () # add files to list of built-in translations but only if that configuration is enabled and if we're building the # final application if (BUILTIN_TRANSLATIONS_OF_QT AND "${META_PROJECT_TYPE}" STREQUAL "application") file(COPY ${QT_QM_FILES} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") list(APPEND EXTERNAL_QM_FILES ${QT_QM_FILES}) endif () list(APPEND QT_TRANSLATION_FILES "qt${QT_REPO}") endforeach () set(QT_TRANSLATIONS_FOUND YES) break() endforeach () # make list of Qt translation files even if translations are not found at build time if (NOT QT_TRANSLATIONS_FOUND) foreach (QT_REPO ${QT_REPOS}) list(APPEND QT_TRANSLATION_FILES "qt${QT_REPO}") endforeach () endif () # emit warning if no Qt translations found but built-in translations are enabled if (BUILTIN_TRANSLATIONS AND NOT QT_TRANSLATION_FILES) message( WARNING "Unable to find translations of Qt itself so Qt's translation files will not be built-in. Be sure Qt translations (https://code.qt.io/cgit/qt/qttranslations.git) are installed. Was looking under: ${QT_TRANSLATION_SEARCH_PATHS}" ) endif () # make relevant Qt translations available as array via qtconfig.h if (QT_TRANSLATION_FILES) list_to_string("," " \\\n QStringLiteral(\"" "\")" "${QT_TRANSLATION_FILES}" QT_TRANSLATION_FILES_ARRAY) endif () # enable lrelease and add install target for localization option(ENABLE_QT_TRANSLATIONS "specifies whether Qt translations should be updated/released" ON) if (ENABLE_QT_TRANSLATIONS AND TS_FILES) message(STATUS "Project has translations which will be released.") set(APP_SPECIFIC_QT_TRANSLATIONS_AVAILABLE YES) # require the LinguistTools module (not adding it to QT_MODULES because we don't link against it) find_package("${QT_LINGUIST_TOOLS_PACKAGE}") if (NOT "${${QT_LINGUIST_TOOLS_PACKAGE}_FOUND}" AND QT_HOST_PATH) # find the module within the host path when set (required for cross compilation with Qt 6 as the module is absent in # the target install tree) find_package("${QT_LINGUIST_TOOLS_PACKAGE}" PATHS "${QT_HOST_PATH}" "${QT_HOST_PATH}/lib/cmake" NO_CMAKE_FIND_ROOT_PATH NO_DEFAULT_PATH) endif () if (NOT "${${QT_LINGUIST_TOOLS_PACKAGE}_FOUND}") message( FATAL_ERROR "Qt translations are enabled but the CMake module \"${QT_LINGUIST_TOOLS_PACKAGE}\" could not be found.") endif () if (NOT COMMAND qt_create_translation) macro (qt_create_translation) qt5_create_translation(${ARGV}) endmacro () endif () set(LUPDATE_OPTIONS "" CACHE STRING "specifies options passed to lupdate") # adds the translations and a target for it qt_create_translation( QM_FILES ${HEADER_FILES} ${SRC_FILES} ${WIDGETS_HEADER_FILES} ${WIDGETS_SRC_FILES} ${WIDGETS_UI_FILES} ${QML_HEADER_FILES} ${QML_SRC_FILES} ${QML_RES_FILES} ${EXCLUDED_FILES} ${TS_FILES} OPTIONS ${LUPDATE_OPTIONS}) add_custom_target(${META_PROJECT_NAME}_translations DEPENDS ${QM_FILES}) if (NOT TARGET translations) add_custom_target(translations DEPENDS ${META_PROJECT_NAME}_translations) else () add_dependencies(translations ${META_PROJECT_NAME}_translations) endif () # add install target for translations if (NOT META_NO_INSTALL_TARGETS AND ENABLE_INSTALL_TARGETS AND NOT BUILTIN_TRANSLATIONS) install( FILES ${QM_FILES} DESTINATION "${META_DATA_DIR}/translations" COMPONENT localization) if (NOT TARGET install-localization) set(LOCALIZATION_TARGET "install-localization") add_custom_target(${LOCALIZATION_TARGET} COMMAND "${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=localization -P "${CMAKE_BINARY_DIR}/cmake_install.cmake") add_dependencies(${LOCALIZATION_TARGET} ${META_PROJECT_NAME}_translations) endif () endif () list(APPEND APP_SPECIFIC_QT_TRANSLATION_FILES "${META_PROJECT_NAME}") endif () # make application specific translation available as array via qtconfig.h (even if this project has no translations, there # might be some from dependencies) if (APP_SPECIFIC_QT_TRANSLATION_FILES) list_to_string("," " \\\n QStringLiteral(\"" "\")" "${APP_SPECIFIC_QT_TRANSLATION_FILES}" APP_SPECIFIC_QT_TRANSLATION_FILES_ARRAY) else () set(APP_SPECIFIC_QT_TRANSLATIONS_AVAILABLE NO) endif () # built-in translations if (BUILTIN_TRANSLATIONS AND (QM_FILES OR EXTERNAL_QM_FILES)) # write a qrc file for the qm files and add it to the resource files set(TRANSLATIONS_QRC_FILE_NAME "${META_PROJECT_VARNAME_LOWER}_translations.qrc") set(TRANSLATIONS_QRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/${TRANSLATIONS_QRC_FILE_NAME}") file(WRITE "${TRANSLATIONS_QRC_FILE}" "<RCC><qresource prefix=\"/translations\">") foreach (QM_FILE ${QM_FILES} ${EXTERNAL_QM_FILES}) get_filename_component(QM_FILE_NAME "${QM_FILE}" NAME) file(APPEND "${TRANSLATIONS_QRC_FILE}" "<file>${QM_FILE_NAME}</file>") endforeach () file(APPEND "${TRANSLATIONS_QRC_FILE}" "</qresource></RCC>") list(APPEND RES_FILES "${TRANSLATIONS_QRC_FILE}") list(APPEND AUTOGEN_DEPS ${QM_FILES} ${EXTERNAL_QM_FILES}) list(APPEND BUILTIN_TRANSLATION_FILES "${TRANSLATIONS_QRC_FILE_NAME}") endif () # generate DBus interfaces if (DBUS_FILES) if (NOT COMMAND qt_add_dbus_interfaces) macro (qt_add_dbus_interfaces) qt5_add_dbus_interfaces(${ARGV}) endmacro () endif () qt_add_dbus_interfaces(GENERATED_DBUS_FILES ${DBUS_FILES}) endif () # add icons to be built-in if (REQUIRED_ICONS) set(BUILTIN_ICON_THEMES "" CACHE STRING "specifies icon themes to be built-in") option(BUILTIN_ICON_THEMES_IN_LIBRARIES "specifies whether icon themes should also be built-in when building libraries" OFF) if (BUILTIN_ICON_THEMES AND (BUILTIN_ICON_THEMES_IN_LIBRARIES OR (NOT "${META_PROJECT_TYPE}" STREQUAL "library"))) set(ICON_THEME_FILES) set(ICON_SEARCH_PATHS) if (CMAKE_FIND_ROOT_PATH) foreach (ROOT_PATH ${CMAKE_FIND_ROOT_PATH}) list(APPEND ICON_SEARCH_PATHS "${ROOT_PATH}/${CMAKE_INSTALL_DATAROOTDIR}/icons") endforeach () endif () list(APPEND ICON_SEARCH_PATHS "${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons") list(APPEND ICON_SEARCH_PATHS "/usr/${CMAKE_INSTALL_DATAROOTDIR}/icons") # find icons from regular prefix when cross- # compiling list(REMOVE_DUPLICATES ICON_SEARCH_PATHS) set(BUILTIN_ICONS_DIR "${CMAKE_CURRENT_BINARY_DIR}/icons") set(DEFAULT_THEME_INDEX_FILE "${BUILTIN_ICONS_DIR}/default/index.theme") file(REMOVE_RECURSE "${BUILTIN_ICONS_DIR}") file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}") foreach (ICON_THEME ${BUILTIN_ICON_THEMES}) string(REGEX MATCHALL "([^:]+|[^:]+$)" ICON_THEME_PARTS "${ICON_THEME}") list(LENGTH ICON_THEME_PARTS ICON_THEME_PARTS_LENGTH) if ("${ICON_THEME_PARTS_LENGTH}" STREQUAL 2) list(GET ICON_THEME_PARTS 0 ICON_THEME) list(GET ICON_THEME_PARTS 1 NEW_ICON_THEME_NAME) else () set(NEW_ICON_THEME_NAME "${ICON_THEME}") endif () foreach (ICON_SEARCH_PATH ${ICON_SEARCH_PATHS}) set(ICON_THEME_PATH "${ICON_SEARCH_PATH}/${ICON_THEME}") set(NEW_ICON_THEME_PATH "${ICON_SEARCH_PATH}/${ICON_THEME}") if (IS_DIRECTORY "${ICON_THEME_PATH}") message( STATUS "The specified icon theme \"${ICON_THEME}\" has been located under \"${ICON_THEME_PATH}\" and will be built-in." ) # find index files if (NOT ICON_THEME STREQUAL FALLBACK_ICON_THEME) file( GLOB GLOBBED_ICON_THEME_INDEX_FILES LIST_DIRECTORIES false "${ICON_THEME_PATH}/index.theme" "${ICON_THEME_PATH}/icon-theme.cache") else () # only index.theme required when icons are provided as fallback anyways file( GLOB GLOBBED_ICON_THEME_INDEX_FILES LIST_DIRECTORIES false "${ICON_THEME_PATH}/index.theme") endif () # make the first specified built-in the default theme if (NOT EXISTS "${DEFAULT_THEME_INDEX_FILE}") file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/default") file(WRITE "${DEFAULT_THEME_INDEX_FILE}" "[Icon Theme]\nInherits=${NEW_ICON_THEME_NAME}") list(APPEND ICON_THEME_FILES "<file>default/index.theme</file>") endif () # find required icons, except the icon theme is provided as fallback anyways if (NOT ICON_THEME STREQUAL FALLBACK_ICON_THEME) set(GLOB_PATTERNS) foreach (REQUIRED_ICON ${REQUIRED_ICONS}) list(APPEND GLOB_PATTERNS "${ICON_THEME_PATH}/${REQUIRED_ICON}" "${ICON_THEME_PATH}/${REQUIRED_ICON}.*" "${ICON_THEME_PATH}/*/${REQUIRED_ICON}" "${ICON_THEME_PATH}/*/${REQUIRED_ICON}.*") endforeach () file( GLOB_RECURSE GLOBBED_ICON_THEME_FILES LIST_DIRECTORIES false ${GLOB_PATTERNS}) else () message( STATUS "Icon files for specified theme \"${ICON_THEME}\" are skipped because these are provided as fallback anyways." ) set(GLOBBED_ICON_THEME_FILES) endif () # make temporary copy of required icons and create resource list for Qt foreach (ICON_THEME_FILE ${GLOBBED_ICON_THEME_INDEX_FILES} ${GLOBBED_ICON_THEME_FILES}) # resolve symlinks if (IS_SYMLINK "${ICON_THEME_FILE}") string(REGEX REPLACE "^${ICON_SEARCH_PATH}/" "" ICON_THEME_FILE_RELATIVE_PATH "${ICON_THEME_FILE}") string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_RELATIVE_PATH "${ICON_THEME_FILE_RELATIVE_PATH}") set(ICON_THEME_FILE_ALIAS " alias=\"${NEW_ICON_THEME_FILE_RELATIVE_PATH}\"") get_filename_component(ICON_THEME_FILE "${ICON_THEME_FILE}" REALPATH) else () unset(ICON_THEME_FILE_ALIAS) endif () string(REGEX REPLACE "^${ICON_SEARCH_PATH}/" "" ICON_THEME_FILE_RELATIVE_PATH "${ICON_THEME_FILE}") string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_RELATIVE_PATH "${ICON_THEME_FILE_RELATIVE_PATH}") get_filename_component(ICON_THEME_FILE_DIR "${ICON_THEME_FILE_RELATIVE_PATH}" DIRECTORY) string(REGEX REPLACE "(^[^/\\]+)" "${NEW_ICON_THEME_NAME}" NEW_ICON_THEME_FILE_DIR "${ICON_THEME_FILE_DIR}") file(MAKE_DIRECTORY "${BUILTIN_ICONS_DIR}/${NEW_ICON_THEME_FILE_DIR}") file(COPY "${ICON_THEME_FILE}" DESTINATION "${BUILTIN_ICONS_DIR}/${NEW_ICON_THEME_FILE_DIR}") list(APPEND ICON_THEME_FILES "<file${ICON_THEME_FILE_ALIAS}>${NEW_ICON_THEME_FILE_RELATIVE_PATH}</file>") endforeach () break() endif () unset(ICON_THEME_PATH) endforeach () if (NOT ICON_THEME_PATH) message(FATAL_ERROR "The specified icon theme \"${ICON_THEME}\" could not be found.") endif () endforeach () set(BUILTIN_ICON_THEMES_QRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/icons/${META_PROJECT_NAME}_builtinicons.qrc") list(REMOVE_DUPLICATES ICON_THEME_FILES) string(CONCAT BUILTIN_ICON_THEMES_QRC_FILE_CONTENT "<RCC><qresource prefix=\"/icons\">" ${ICON_THEME_FILES} "</qresource></RCC>") file(WRITE "${BUILTIN_ICON_THEMES_QRC_FILE}" "${BUILTIN_ICON_THEMES_QRC_FILE_CONTENT}") list(APPEND RES_FILES "${BUILTIN_ICON_THEMES_QRC_FILE}") endif () endif () # export Qt resources from specified RES_FILES foreach (RES_FILE ${RES_FILES}) get_filename_component(RES_EXT ${RES_FILE} EXT) if (RES_EXT STREQUAL ".qrc") get_filename_component(RES_NAME ${RES_FILE} NAME_WE) list(APPEND QT_RESOURCES "${RES_NAME}") endif () endforeach () # export Qt resources required by static libraries the static library depends on if (STATIC_LIBRARIES_QT_RESOURCES) list(REMOVE_DUPLICATES STATIC_LIBRARIES_QT_RESOURCES) list(APPEND QT_RESOURCES ${STATIC_LIBRARIES_QT_RESOURCES}) endif () # enable Qt resources required by libraries the application depends on if (QT_RESOURCES) list(REMOVE_DUPLICATES QT_RESOURCES) # make enabling resources of static dependencies available via qtconfig.h unset(ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES) foreach (QT_RESOURCE ${STATIC_LIBRARIES_QT_RESOURCES}) set(ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES "${ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES} \\\n struct initializer_${QT_RESOURCE} { \\\n initializer_${QT_RESOURCE}() { Q_INIT_RESOURCE(${QT_RESOURCE}); } \\\n ~initializer_${QT_RESOURCE}() { Q_CLEANUP_RESOURCE(${QT_RESOURCE}); } \\\n } dummy_${QT_RESOURCE};" ) endforeach () endif () # enable moc, uic and rcc by default for all targets set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) if (WIDGETS_UI_FILES AND WIDGETS_GUI) set(CMAKE_AUTOUIC ON) if (INSTALL_UI_HEADER) # also add install for header files generated by uic foreach (UI_FILE WIDGETS_UI_FILES) get_filename_component(UI_NAME "${UI_FILE}" NAME_WE) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/ui_${UI_NAME}.h" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${META_PROJECT_NAME}/ui" COMPONENT ui-header) endforeach () endif () endif () # add configuration header for Qt-specific configuration find_template_file("qtconfig.h" QT_UTILITIES QT_CONFIG_H_TEMPLATE_FILE) configure_file("${QT_CONFIG_H_TEMPLATE_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/resources/qtconfig.h") set(QT_CONFIGURED YES) 0707010000000E000081A4000000000000000000000001636534C900000C83000000000000000000000000000000000000002C00000000qtutilities/cmake/modules/QtGuiConfig.cmakecmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) if (NOT BASIC_PROJECT_CONFIG_DONE) message(FATAL_ERROR "Before including the QtGuiConfig module, the BasicConfig module must be included.") endif () if (QT_CONFIGURED) message(FATAL_ERROR "The QtGuiConfig module can not be included when Qt usage has already been configured.") endif () if (TARGET_CONFIG_DONE) message(FATAL_ERROR "Can not include QtGuiConfig module when targets are already configured.") endif () if (NOT WIDGETS_GUI AND NOT QUICK_GUI) message(STATUS "GUI is completely disabled.") return() endif () list(APPEND ADDITIONAL_QT_MODULES Gui) # enable Qt Widgets GUI if (WIDGETS_GUI) list(APPEND META_PRIVATE_COMPILE_DEFINITIONS GUI_QTWIDGETS) list(APPEND META_PUBLIC_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME_UPPER}_GUI_QTWIDGETS) list(APPEND WIDGETS_FILES ${WIDGETS_HEADER_FILES} ${WIDGETS_SRC_FILES} ${WIDGETS_RES_FILES} ${WIDGETS_UI_FILES}) list(APPEND ADDITIONAL_HEADER_FILES ${WIDGETS_HEADER_FILES}) if (WIDGETS_FILES) list(APPEND ADDITIONAL_QT_MODULES Widgets) message(STATUS "Building with Qt Widgets GUI.") else () message(STATUS "Qt Widgets GUI is not available.") endif () else () message(STATUS "Building WITHOUT Qt Widgets GUI.") endif () # enable Qt Quick GUI if (QUICK_GUI) list(APPEND META_PRIVATE_COMPILE_DEFINITIONS GUI_QTQUICK) list(APPEND META_PUBLIC_COMPILE_DEFINITIONS ${META_PROJECT_VARNAME_UPPER}_GUI_QTQUICK) list(APPEND QML_FILES ${QML_HEADER_FILES} ${QML_SRC_FILES} ${QML_RES_FILES}) list(APPEND ADDITIONAL_HEADER_FILES ${QML_HEADER_FILES}) if (QML_FILES) list(APPEND ADDITIONAL_QT_MODULES Qml Quick) list(APPEND ADDITIONAL_QT_REPOS "declarative") message(STATUS "Building with Qt Quick GUI.") # enable QML debugging if (CMAKE_BUILD_TYPE STREQUAL "Debug") list(APPEND META_PRIVATE_COMPILE_DEFINITIONS QT_QML_DEBUG) endif () # enable Qt Quick Controls 2 if (META_USE_QQC2) list(APPEND ADDITIONAL_QT_MODULES QuickControls2) endif () else () message(STATUS "Qt Quick GUI is not available.") endif () else () message(STATUS "Building WITHOUT Qt Quick GUI.") endif () # set platform-specific GUI-type if (WIN32) # set "GUI-type" to WIN32 to hide console under Windows set(GUI_TYPE WIN32) elseif (APPLE) # make the GUI application a "bundle" under MacOSX set(GUI_TYPE MACOSX_BUNDLE) endif () # add source files required by both GUI variants list(APPEND SRC_FILES ${GUI_SRC_FILES}) list(APPEND ADDITIONAL_HEADER_FILES ${GUI_HEADER_FILES}) # add option for enabling/disabling static Qt plugins option(SVG_SUPPORT "whether to link against the SVG image format plugin (only relevant when using static Qt)" ON) option(SVG_ICON_SUPPORT "whether to link against the SVG icon engine (only relevant when using static Qt)" ON) set(IMAGE_FORMAT_SUPPORT "Gif;ICO;Jpeg" CACHE STRING "specifies the image format plugins to link against (only relevant when using static Qt)") # always enable the Svg module under Android if (ANDROID) list(APPEND ADDITIONAL_QT_MODULES Svg) endif () 0707010000000F000081A4000000000000000000000001636534C900000911000000000000000000000000000000000000003300000000qtutilities/cmake/modules/QtJsProviderConfig.cmakecmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # determines the JavaScript provider (either Qt Script or Qt Declarative) if (TARGET_CONFIG_DONE) message(FATAL_ERROR "Can not include QtJsProviderConfig module when targets are already configured.") endif () # configure the specified JavaScript provider set(JS_PROVIDER "qml" CACHE STRING "specifies the JavaScript provider: qml (default), script or none") if (JS_PROVIDER STREQUAL "script") set(JS_PROVIDER Script) set(JS_DEFINITION "${META_PROJECT_VARNAME_UPPER}_USE_SCRIPT") list(APPEND ADDITIONAL_QT_REPOS "script") message(STATUS "Using Qt Script as JavaScript provider.") elseif (JS_PROVIDER STREQUAL "qml") set(JS_PROVIDER Qml) set(JS_DEFINITION "${META_PROJECT_VARNAME_UPPER}_USE_JSENGINE") list(APPEND ADDITIONAL_QT_REPOS "declarative") message(STATUS "Using Qt QML as JavaScript provider.") elseif (JS_PROVIDER STREQUAL "none") set(JS_PROVIDER "") set(JS_DEFINITION "${META_PROJECT_VARNAME_UPPER}_NO_JSENGINE") message(STATUS "JavaScript provider has been disabled.") else () message(FATAL_ERROR "The specified JavaScript provider '${JS_PROVIDER}' is unknown.") endif () # add header files with some defines/includes to conveniently use the selected provider if (JS_PROVIDER) list(APPEND ADDITIONAL_QT_MODULES "${JS_PROVIDER}") if (META_JS_SRC_DIR) set(JS_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${META_JS_SRC_DIR}") else () set(JS_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/gui") endif () include(TemplateFinder) find_template_file("jsdefs.h" QT_UTILITIES JS_DEFS_H_TEMPLATE_FILE) configure_file( "${JS_DEFS_H_TEMPLATE_FILE}" "${JS_HEADER_DIR}/jsdefs.h" # simply add this to source to ease inclusion NEWLINE_STYLE UNIX # since this goes to sources ensure consistency ) find_template_file("jsincludes.h" QT_UTILITIES JS_INCLUDES_H_TEMPLATE_FILE) configure_file( "${JS_INCLUDES_H_TEMPLATE_FILE}" "${JS_HEADER_DIR}/jsincludes.h" # simply add this to source to ease inclusion NEWLINE_STYLE UNIX # since this goes to sources ensure consistency ) list(APPEND WIDGETS_FILES "${JS_HEADER_DIR}/jsdefs.h" "${JS_HEADER_DIR}/jsincludes.h") endif () list(APPEND META_PUBLIC_COMPILE_DEFINITIONS ${JS_DEFINITION}) 07070100000010000081A4000000000000000000000001636534C9000022E3000000000000000000000000000000000000002A00000000qtutilities/cmake/modules/QtLinkage.cmakecmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # defines helper to link against Qt dynamically or statically # prevent multiple inclusion if (DEFINED QT_LINKAGE_DETERMINED) return() endif () set(QT_LINKAGE_DETERMINED ON) # include validate_visibility from c++utilities' 3rdParty module include(3rdParty) # determine the minimum Qt version if (NOT META_QT_VERSION) if (META_QT5_VERSION) # use deprecated META_QT5_VERSION variable (for compatibility) set(META_QT_VERSION "${META_QT5_VERSION}") else () # require Qt 5.6 or higher by default set(META_QT_VERSION 5.6) endif () endif () # avoid "add_custom_target cannot create target "apk" because another target…" errors produced by Qt's Android support module # (which can not cope with Qt CMake modules already pulled in by a dependency) if (ANDROID AND NOT ${PROJECT_NAME}-MultiAbiBuild) set(${PROJECT_NAME}-MultiAbiBuild ON) endif () # define function for using Qt and KDE Frameworks modules and static plugins macro (use_qt_module) # parse arguments set(OPTIONAL_ARGS ONLY_PLUGINS PLUGINS_OPTIONAL) set(ONE_VALUE_ARGS PREFIX MODULE VISIBILITY LIBRARIES_VARIABLE) set(MULTI_VALUE_ARGS TARGETS PLUGINS) cmake_parse_arguments(ARGS "${OPTIONAL_ARGS}" "${ONE_VALUE_ARGS}" "${MULTI_VALUE_ARGS}" ${ARGN}) # validate values if (NOT ARGS_PREFIX) message(FATAL_ERROR "use_qt_module called without PREFIX.") endif () if (NOT ARGS_MODULE) message(FATAL_ERROR "use_qt_module called without MODULE.") endif () if (ARGS_VISIBILITY) validate_visibility(${ARGS_VISIBILITY}) else () set(ARGS_VISIBILITY PRIVATE) endif () if (NOT ARGS_LIBRARIES_VARIABLE) set(ARGS_LIBRARIES_VARIABLE "${ARGS_VISIBILITY}_LIBRARIES") endif () if (NOT ARGS_TARGETS) if (${MODULE}_MODULE_TARGETS) set(ARGS_TARGETS "${${MODULE}_MODULE_TARGETS}") else () set(ARGS_TARGETS "${ARGS_PREFIX}::${ARGS_MODULE}") endif () endif () if (ARGS_ONLY_PLUGINS AND NOT ARGS_PLUGINS) message(FATAL_ERROR "ONLY_PLUGINS specified but no plugins.") endif () # find and use module if (NOT ONLY_PLUGINS) find_package("${ARGS_PREFIX}${ARGS_MODULE}" "${META_QT_VERSION}" REQUIRED) foreach (TARGET ${ARGS_TARGETS}) if (NOT TARGET "${TARGET}") message(FATAL_ERROR "The ${ARGS_PREFIX}${ARGS_MODULE} does not provide the target ${TARGET}.") endif () if ("${TARGET}" IN_LIST "${ARGS_LIBRARIES_VARIABLE}") continue() endif () set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${TARGET}") set("PKG_CONFIG_${ARGS_PREFIX}_${ARGS_MODULE}" "${ARGS_PREFIX}${ARGS_MODULE}") message(STATUS "Linking ${META_TARGET_NAME} against Qt module ${TARGET}.") # hack for "StaticQt5": re-assign INTERFACE_LINK_LIBRARIES_RELEASE to INTERFACE_LINK_LIBRARIES get_target_property("${ARGS_MODULE}_INTERFACE_LINK_LIBRARIES_RELEASE" "${TARGET}" INTERFACE_LINK_LIBRARIES_RELEASE) if ("${ARGS_MODULE}_INTERFACE_LINK_LIBRARIES_RELEASE") set_target_properties("${TARGET}" PROPERTIES INTERFACE_LINK_LIBRARIES "${${ARGS_MODULE}_INTERFACE_LINK_LIBRARIES_RELEASE}") endif () endforeach () endif () # find and use plugins foreach (PLUGIN ${ARGS_PLUGINS}) set(PLUGIN_TARGET "${ARGS_PREFIX}::Q${PLUGIN}Plugin") if (NOT TARGET "${PLUGIN_TARGET}") find_package("${ARGS_PREFIX}${ARGS_MODULE}" "${META_QT_VERSION}" REQUIRED) endif () if (NOT TARGET "${PLUGIN_TARGET}" AND PLUGIN MATCHES "Svg.*" AND ARGS_MODULE STREQUAL "Svg") # look for Svg plugins within the Gui module's directory as well find_package("${ARGS_PREFIX}Gui" "${META_QT_VERSION}" REQUIRED) set(PLUGIN_CONFIG "${${ARGS_PREFIX}Gui_DIR}/${ARGS_PREFIX}Q${PLUGIN}PluginConfig.cmake") if (EXISTS "${PLUGIN_CONFIG}") include("${PLUGIN_CONFIG}") endif () endif () if (NOT TARGET "${PLUGIN_TARGET}") if (NOT PLUGINS_OPTIONAL) continue() endif () message( FATAL_ERROR "The ${ARGS_PREFIX}${ARGS_MODULE} package does not provide the target ${ARGS_PREFIX}::Q${PLUGIN}Plugin.") endif () if ("${PLUGIN_TARGET}" IN_LIST "${ARGS_LIBRARIES_VARIABLE}") continue() endif () set("${ARGS_LIBRARIES_VARIABLE}" "${${ARGS_LIBRARIES_VARIABLE}};${ARGS_PREFIX}::Q${PLUGIN}Plugin") message(STATUS "Linking ${META_TARGET_NAME} against Qt plugin ${ARGS_PREFIX}::Q${PLUGIN}Plugin.") endforeach () # unset variables (can not simply use a function because Qt's variables need to be exported) foreach (ARGUMENT ${OPTIONAL_ARGS} ${ONE_VALUE_ARGS} ${MULTI_VALUE_ARGS}) unset(ARGS_${ARGUMENT}) endforeach () endmacro () # define function to make Qt variable available; queries qmake or uses variables provided by Qt 6 function (query_qmake_variable QMAKE_VARIABLE) # prevent queries for variables already known if (NOT "${${QMAKE_VARIABLE}}" STREQUAL "") return() endif () # check configuration variables provided by Qt 6 if (QMAKE_VARIABLE MATCHES "QT_(.*)") set(VARIABLE_NAME "${CMAKE_MATCH_1}") if (QT6_${VARIABLE_NAME}) set(VARIABLE_NAME QT6_${VARIABLE_NAME}) endif () if (NOT "${${VARIABLE_NAME}}" STREQUAL "") set("${QMAKE_VARIABLE}" "${${VARIABLE_NAME}}" PARENT_SCOPE) message(STATUS "Qt variable ${QMAKE_VARIABLE} taken from ${VARIABLE_NAME}: ${${VARIABLE_NAME}}") return() endif () endif () # execute qmake get_target_property(QMAKE_BIN "${QT_QMAKE_TARGET}" IMPORTED_LOCATION) execute_process( COMMAND "${QMAKE_BIN}" -query "${QMAKE_VARIABLE}" RESULT_VARIABLE "${QMAKE_VARIABLE}_RESULT" OUTPUT_VARIABLE "${QMAKE_VARIABLE}") if (NOT "${${QMAKE_VARIABLE}_RESULT}" STREQUAL 0 OR "${${QMAKE_VARIABLE}}" STREQUAL "") message( FATAL_ERROR "Unable to read qmake variable ${QMAKE_VARIABLE} via \"${QMAKE_BIN} -query ${QMAKE_VARIABLE}\"; output was \"${${QMAKE_VARIABLE}}\"." ) endif () # remove new-line character at the end string(REGEX REPLACE "\n$" "" "${QMAKE_VARIABLE}" "${${QMAKE_VARIABLE}}") # export variable to parent scope set("${QMAKE_VARIABLE}" "${${QMAKE_VARIABLE}}" PARENT_SCOPE) message(STATUS "Qt variable ${QMAKE_VARIABLE} queried from qmake: ${${QMAKE_VARIABLE}}") endfunction () # define function to make Qt variable available, resolving relative paths via CMAKE_FIND_ROOT_PATH function (query_qmake_variable_path QMAKE_VARIABLE) # query the variable itself query_qmake_variable("${QMAKE_VARIABLE}") set(VARIABLE_VALUE "${${QMAKE_VARIABLE}}") if (NOT VARIABLE_VALUE) message(WARNING "Unable to resolve Qt variable ${QMAKE_VARIABLE}: it is empty") return() endif () # pass the variable as-is if it points to an existing dir/file if (EXISTS "${VARIABLE_VALUE}") get_filename_component(VARIABLE_VALUE "${VARIABLE_VALUE}" REALPATH) message(STATUS "Qt variable ${QMAKE_VARIABLE} resolved to path: ${VARIABLE_VALUE}") set("${QMAKE_VARIABLE}" "${VARIABLE_VALUE}" PARENT_SCOPE) return() endif () # assume VARIABLE_VALUE is relative within CMAKE_FIND_ROOT_PATH, e.g. QT_INSTALL_TRANSLATIONS might be set to # "share/qt6/translations" if ("${CMAKE_VERSION}" VERSION_LESS "3.12.0") message(WARNING "Unable to resolve Qt variable ${QMAKE_VARIABLE} to an existing path, try using CMake 3.12 or newer") return() # skip if CMake version too old like on Leap 15.1 endif () foreach (ROOT_PATH ${CMAKE_FIND_ROOT_PATH} "") foreach (PREFIX_PATH ${CMAKE_INSTALL_PREFIX} ${CMAKE_PREFIX_PATH} "") string(JOIN "/" FULL_PATH ${ROOT_PATH} ${PREFIX_PATH} ${VARIABLE_VALUE}) if (EXISTS "${FULL_PATH}") set("${QMAKE_VARIABLE}" "${FULL_PATH}" PARENT_SCOPE) message(STATUS "Qt variable ${QMAKE_VARIABLE} resolved to path: ${FULL_PATH}") return() else () list(APPEND CHECKED_PATHS "${FULL_PATH}") endif () endforeach () endforeach () message( WARNING "Unable to resolve Qt variable ${QMAKE_VARIABLE} to an existing path, was checking for: ${CHECKED_PATHS}") endfunction () 07070100000011000081A4000000000000000000000001636534C9000009F7000000000000000000000000000000000000003800000000qtutilities/cmake/modules/QtWebViewProviderConfig.cmakecmake_minimum_required(VERSION 3.3.0 FATAL_ERROR) # determines the web view provider (either Qt WebKit or Qt WebEngine) if (TARGET_CONFIG_DONE) message(FATAL_ERROR "Can not include QtWebViewProviderConfig module when targets are already configured.") endif () # configure the specified web view provider set(WEBVIEW_PROVIDER "webengine" CACHE STRING "specifies the web view provider: webengine (default), webkit or none") if (WEBVIEW_PROVIDER STREQUAL "webkit") set(WEBVIEW_PROVIDER WebKitWidgets) set(WEBVIEW_DEFINITION "${META_PROJECT_VARNAME_UPPER}_USE_WEBKIT") message(STATUS "Using Qt WebKit as web view provider.") elseif (WEBVIEW_PROVIDER STREQUAL "webengine") set(WEBVIEW_PROVIDER WebEngineWidgets) set(WEBVIEW_DEFINITION "${META_PROJECT_VARNAME_UPPER}_USE_WEBENGINE") list(APPEND ADDITIONAL_QT_REPOS "webengine") message(STATUS "Using Qt WebEngine as web view provider.") elseif (WEBVIEW_PROVIDER STREQUAL "none") set(WEBVIEW_PROVIDER "") set(WEBVIEW_DEFINITION "${META_PROJECT_VARNAME_UPPER}_NO_WEBVIEW") message(STATUS "Built-in web view has been disabled.") else () message(FATAL_ERROR "The specified web view provider '${WEBVIEW_PROVIDER}' is unknown.") endif () # add header files with some defines/includes to conveniently use the selected provider if (WEBVIEW_PROVIDER) list(APPEND ADDITIONAL_QT_MODULES "${WEBVIEW_PROVIDER}") if (META_WEBVIEW_SRC_DIR) set(WEBVIEW_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${META_WEBVIEW_SRC_DIR}") else () set(WEBVIEW_HEADER_DIR "${CMAKE_CURRENT_SOURCE_DIR}/gui") endif () include(TemplateFinder) find_template_file("webviewdefs.h" QT_UTILITIES WEBVIEWDEFS_H_TEMPLATE_FILE) configure_file( "${WEBVIEWDEFS_H_TEMPLATE_FILE}" "${WEBVIEW_HEADER_DIR}/webviewdefs.h" # simply add this to source to ease inclusion NEWLINE_STYLE UNIX # since this goes to sources ensure consistency ) find_template_file("webviewincludes.h" QT_UTILITIES WEBVIEWINCLUDES_H_TEMPLATE_FILE) configure_file( "${WEBVIEWINCLUDES_H_TEMPLATE_FILE}" "${WEBVIEW_HEADER_DIR}/webviewincludes.h" # simply add this to source to ease # inclusion NEWLINE_STYLE UNIX # since this goes to sources ensure consistency ) list(APPEND WIDGETS_FILES "${WEBVIEW_HEADER_DIR}/webviewdefs.h" "${WEBVIEW_HEADER_DIR}/webviewincludes.h") endif () list(APPEND META_PUBLIC_COMPILE_DEFINITIONS ${WEBVIEW_DEFINITION}) 07070100000012000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001C00000000qtutilities/cmake/templates07070100000013000081A4000000000000000000000001636534C9000004E7000000000000000000000000000000000000003700000000qtutilities/cmake/templates/android-deployment.json.in{ "qt": "@ANDROID_APK_QT_INSTALL_PREFIX@", "sdk": "@ANDROID_APK_SDK@", "ndk": "@ANDROID_APK_NDK@", "android-min-sdk-version": "@ANDROID_MIN_SDK_VERSION@", "android-target-sdk-version": "@ANDROID_TARGET_SDK_VERSION@", "toolchain-prefix": "@ANDROID_APK_TOOL_PREFIX@", "tool-prefix": "@ANDROID_APK_TOOL_PREFIX@", "toolchain-version": "@ANDROID_APK_TOOLCHAIN_VERSION@", "ndk-host": "@ANDROID_HOST_TAG@", "target-architecture": "@ANDROID_ABI@", "application-binary": "@ANDROID_APK_APP_BINARY@", "qml-root-path": "@CMAKE_CURRENT_SOURCE_DIR@", "qml-import-paths": "@ANDROID_APK_QML_IMPORT_DIRS@", "android-extra-libs": "@ANDROID_APK_EXTRA_LIBS@", "android-extra-plugins": "@ANDROID_APK_EXTRA_PLUGIN_DIRS@", "android-package-source-directory": "@ANDROID_PACKAGE_SOURCE_DIRECTORY@", "stdcpp-path":"@ANDROID_APK_CXX_STANDARD_LIBRARY@", "sdkBuildToolsRevision": "@ANDROID_APK_BUILD_TOOLS_VERSION@", "useLLVM": @ANDROID_APK_USE_LLVM@, "architectures": { "@ANDROID_ABI@" : "@ANDROID_APK_SYSROOT_NAME@" }, "extraPrefixDirs": [@ANDROID_APK_BINARY_DIRS@ "@ANDROID_APK_NDK@/sysroot", "@CMAKE_INSTALL_PREFIX@", "@ANDROID_APK_QT_INSTALL_PREFIX@" ] } 07070100000014000081A4000000000000000000000001636534C90000072D000000000000000000000000000000000000002800000000qtutilities/cmake/templates/jsdefs.h.in// Created via CMake from template jsdefs.h.in // WARNING! Any changes to this file will be overwritten by the next CMake run! #ifndef @META_PROJECT_VARNAME_UPPER@_JAVA_SCRIPT_DEFINES #define @META_PROJECT_VARNAME_UPPER@_JAVA_SCRIPT_DEFINES #include <QtGlobal> #if defined(@META_PROJECT_VARNAME_UPPER@_USE_JSENGINE) # define @META_PROJECT_VARNAME_UPPER@_JS_ENGINE QJSEngine # define @META_PROJECT_VARNAME_UPPER@_JS_VALUE QJSValue # define @META_PROJECT_VARNAME_UPPER@_JS_READONLY # define @META_PROJECT_VARNAME_UPPER@_JS_UNDELETABLE # define @META_PROJECT_VARNAME_UPPER@_JS_QOBJECT(engine, obj) engine.newQObject(obj) # define @META_PROJECT_VARNAME_UPPER@_JS_INT(value) value.toInt() # define @META_PROJECT_VARNAME_UPPER@_JS_IS_VALID_PROG(program) (!program.isError() && program.isCallable()) #elif defined(@META_PROJECT_VARNAME_UPPER@_USE_SCRIPT) # define @META_PROJECT_VARNAME_UPPER@_JS_ENGINE QScriptEngine # define @META_PROJECT_VARNAME_UPPER@_JS_VALUE QScriptValue # define @META_PROJECT_VARNAME_UPPER@_JS_READONLY ,QScriptValue::ReadOnly # define @META_PROJECT_VARNAME_UPPER@_JS_UNDELETABLE ,QScriptValue::Undeletable # define @META_PROJECT_VARNAME_UPPER@_JS_QOBJECT(engine, obj) engine.newQObject(obj, QScriptEngine::ScriptOwnership) # define @META_PROJECT_VARNAME_UPPER@_JS_INT(value) value.toInt32() # define @META_PROJECT_VARNAME_UPPER@_JS_IS_VALID_PROG(program) (!program.isError() && program.isFunction()) #elif !defined(@META_PROJECT_VARNAME_UPPER@_NO_JSENGINE) # error "No definition for JavaScript provider present." #endif #ifdef @META_PROJECT_VARNAME_UPPER@_JS_ENGINE QT_FORWARD_DECLARE_CLASS(@META_PROJECT_VARNAME_UPPER@_JS_ENGINE) #endif #ifdef @META_PROJECT_VARNAME_UPPER@_JS_VALUE QT_FORWARD_DECLARE_CLASS(@META_PROJECT_VARNAME_UPPER@_JS_VALUE) #endif #endif // @META_PROJECT_VARNAME_UPPER@_JAVA_SCRIPT_DEFINES 07070100000015000081A4000000000000000000000001636534C900000290000000000000000000000000000000000000002C00000000qtutilities/cmake/templates/jsincludes.h.in// Created via CMake from template jsincludes.h.in // WARNING! Any changes to this file will be overwritten by the next CMake run! #ifndef @META_PROJECT_VARNAME_UPPER@_JAVA_SCRIPT_INCLUDES #define @META_PROJECT_VARNAME_UPPER@_JAVA_SCRIPT_INCLUDES #include <QtGlobal> #if defined(@META_PROJECT_VARNAME_UPPER@_USE_JSENGINE) # include <QJSEngine> # include <QJSValue> #elif defined(@META_PROJECT_VARNAME_UPPER@_USE_SCRIPT) # include <QScriptEngine> # include <QScriptValue> #elif !defined(@META_PROJECT_VARNAME_UPPER@_NO_JSENGINE) # error "No definition for JavaScript provider present." #endif #endif // @META_PROJECT_VARNAME_UPPER@_JAVA_SCRIPT_INCLUDES 07070100000016000081A4000000000000000000000001636534C90000032A000000000000000000000000000000000000002A00000000qtutilities/cmake/templates/qtconfig.h.in// Created via CMake from template qtconfig.h.in // WARNING! Any changes to this file will be overwritten by the next CMake run! #ifndef @META_PROJECT_VARNAME_UPPER@_QT_CONFIG #define @META_PROJECT_VARNAME_UPPER@_QT_CONFIG #define QT_TRANSLATION_FILES {@QT_TRANSLATION_FILES_ARRAY@} #define APP_SPECIFIC_QT_TRANSLATION_FILES {@APP_SPECIFIC_QT_TRANSLATION_FILES_ARRAY@} #define ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES@ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES@ #cmakedefine SVG_SUPPORT #cmakedefine SVG_ICON_SUPPORT #cmakedefine IMAGE_FORMAT_SUPPORT #define IMPORT_IMAGE_FORMAT_PLUGINS @IMAGE_FORMAT_SUPPORT_ARRAY@ #define IMPORT_WIDGET_STYLE_PLUGINS @WIDGET_STYLE_PLUGINS_ARRAY@ #define IMPORT_TLS_PLUGINS @USED_TLS_PLUGINS_ARRAY@ @META_CUSTOM_QT_CONFIG@ #endif // @META_PROJECT_VARNAME_UPPER@_QT_CONFIG 07070100000017000081A4000000000000000000000001636534C9000004AA000000000000000000000000000000000000002D00000000qtutilities/cmake/templates/webviewdefs.h.in// Created via CMake from template webviewdefs.h.in // WARNING! Any changes to this file will be overwritten by the next CMake run! #ifndef @META_PROJECT_VARNAME_UPPER@_WEB_VIEW_DEFINES #define @META_PROJECT_VARNAME_UPPER@_WEB_VIEW_DEFINES #include <QtGlobal> #if defined(@META_PROJECT_VARNAME_UPPER@_USE_WEBENGINE) # define @META_PROJECT_VARNAME_UPPER@_WEB_VIEW QWebEngineView # define @META_PROJECT_VARNAME_UPPER@_WEB_PAGE QWebEnginePage #elif defined(@META_PROJECT_VARNAME_UPPER@_USE_WEBKIT) # define @META_PROJECT_VARNAME_UPPER@_WEB_VIEW QWebView # define @META_PROJECT_VARNAME_UPPER@_WEB_PAGE QWebPage # define @META_PROJECT_VARNAME_UPPER@_WEB_FRAME QWebFrame #elif !defined(@META_PROJECT_VARNAME_UPPER@_NO_WEBVIEW) # error "No definition for web view provider present." #endif #ifdef @META_PROJECT_VARNAME_UPPER@_WEB_VIEW QT_FORWARD_DECLARE_CLASS(@META_PROJECT_VARNAME_UPPER@_WEB_VIEW) #endif #ifdef @META_PROJECT_VARNAME_UPPER@_WEB_PAGE QT_FORWARD_DECLARE_CLASS(@META_PROJECT_VARNAME_UPPER@_WEB_PAGE) #endif #ifdef @META_PROJECT_VARNAME_UPPER@_WEB_FRAME QT_FORWARD_DECLARE_CLASS(@META_PROJECT_VARNAME_UPPER@_WEB_FRAME) #endif #endif // @META_PROJECT_VARNAME_UPPER@_WEB_VIEW_DEFINES 07070100000018000081A4000000000000000000000001636534C9000002C8000000000000000000000000000000000000003100000000qtutilities/cmake/templates/webviewincludes.h.in// Created via CMake from template webviewincludes.h.in // WARNING! Any changes to this file will be overwritten by the next CMake run! #ifndef @META_PROJECT_VARNAME_UPPER@_WEB_VIEW_INCLUDES #define @META_PROJECT_VARNAME_UPPER@_WEB_VIEW_INCLUDES #include <QtGlobal> #if defined(@META_PROJECT_VARNAME_UPPER@_USE_WEBENGINE) # include <QWebEngineView> # include <QWebEnginePage> # include <QtWebEngineWidgetsVersion> #elif defined(@META_PROJECT_VARNAME_UPPER@_USE_WEBKIT) # include <QWebView> # include <QWebPage> # include <QWebFrame> #elif !defined(@META_PROJECT_VARNAME_UPPER@_NO_WEBVIEW) # error "No definition for web view provider present." #endif #endif // @META_PROJECT_VARNAME_UPPER@_WEB_VIEW_INCLUDES 07070100000019000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001100000000qtutilities/dbus0707010000001A000081A4000000000000000000000001636534C90000062D000000000000000000000000000000000000003300000000qtutilities/dbus/org.freedesktop.Notifications.xml<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.freedesktop.Notifications"> <signal name="NotificationClosed"> <arg name="id" type="u" direction="out"/> <arg name="reason" type="u" direction="out"/> </signal> <signal name="ActionInvoked"> <arg name="id" type="u" direction="out"/> <arg name="action_key" type="s" direction="out"/> </signal> <method name="Notify"> <annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="QVariantMap"/> <arg type="u" direction="out"/> <arg name="app_name" type="s" direction="in"/> <arg name="replaces_id" type="u" direction="in"/> <arg name="app_icon" type="s" direction="in"/> <arg name="summary" type="s" direction="in"/> <arg name="body" type="s" direction="in"/> <arg name="actions" type="as" direction="in"/> <arg name="hints" type="a{sv}" direction="in"/> <arg name="timeout" type="i" direction="in"/> </method> <method name="CloseNotification"> <arg name="id" type="u" direction="in"/> </method> <method name="GetCapabilities"> <arg type="as" name="caps" direction="out"/> </method> <method name="GetServerInformation"> <arg type="s" name="name" direction="out"/> <arg type="s" name="vendor" direction="out"/> <arg type="s" name="version" direction="out"/> <arg type="s" name="spec_version" direction="out"/> </method> </interface> </node> 0707010000001B000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000002000000000qtutilities/enterpassworddialog0707010000001C000081A4000000000000000000000001636534C900002A04000000000000000000000000000000000000003800000000qtutilities/enterpassworddialog/enterpassworddialog.cpp#include "./enterpassworddialog.h" #include "../misc/dialogutils.h" #include "ui_enterpassworddialog.h" #include <QApplication> #include <QEvent> #include <QGraphicsPixmapItem> #include <QGraphicsScene> #include <QIcon> #include <QKeyEvent> #include <QMessageBox> #include <QStyle> #ifdef QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION #if defined(Q_OS_WIN32) #include <windows.h> #elif defined(X_AVAILABLE) #include <X11/XKBlib.h> #undef KeyPress #undef KeyRelease #undef FocusIn #undef FocusOut #endif #endif namespace QtUtilities { /*! * \class EnterPasswordDialog * \brief The EnterPasswordDialog class provides a simple dialog to ask the user * for a password. */ /*! * \brief Constructs a password dialog. * \param parent Specifies the parent widget. */ EnterPasswordDialog::EnterPasswordDialog(QWidget *parent) : QDialog(parent) , m_ui(new Ui::EnterPasswordDialog) { // setup ui m_ui->setupUi(this); makeHeading(m_ui->instructionLabel); setStyleSheet(dialogStyle()); setDescription(); setPromptForUserName(false); setVerificationRequired(false); setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); installEventFilter(this); m_ui->userNameLineEdit->installEventFilter(this); m_ui->password1LineEdit->installEventFilter(this); m_ui->password2LineEdit->installEventFilter(this); // capslock key detection #ifdef QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION m_capslockPressed = isCapslockPressed(); #else m_capslockPressed = false; #endif m_ui->capslockWarningWidget->setVisible(m_capslockPressed); // draw icon to capslock warning graphics view QIcon icon = QApplication::style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, this); QGraphicsScene *scene = new QGraphicsScene(); QGraphicsPixmapItem *item = new QGraphicsPixmapItem(icon.pixmap(16, 16)); scene->addItem(item); m_ui->capslockWarningGraphicsView->setScene(scene); // connect signals and slots connect(m_ui->showPasswordCheckBox, &QCheckBox::clicked, this, &EnterPasswordDialog::updateShowPassword); connect(m_ui->noPwCheckBox, &QCheckBox::clicked, this, &EnterPasswordDialog::updateShowPassword); connect(m_ui->confirmPushButton, &QPushButton::clicked, this, &EnterPasswordDialog::confirm); connect(m_ui->abortPushButton, &QPushButton::clicked, this, &EnterPasswordDialog::abort); // grab the keyboard grabKeyboard(); } /*! * \brief Destroys the password dialog. */ EnterPasswordDialog::~EnterPasswordDialog() { } /*! * \brief Returns the description. The description is shown under the * instruction text. * \sa setDescription() */ QString EnterPasswordDialog::description() const { return m_ui->descLabel->text(); } /*! * \brief Sets the description. * \sa description() */ void EnterPasswordDialog::setDescription(const QString &description) { m_ui->descLabel->setText(description); m_ui->descLabel->setHidden(description.isEmpty()); adjustSize(); } /*! * \brief Returns whether the dialogs prompts for a user name as well. * * The dialog does not prompt for a user name by default. * * \sa setPromptForUserName() */ bool EnterPasswordDialog::promtForUserName() const { return !m_ui->userNameLineEdit->isHidden(); } /*! * \brief Sets whethere the dialog prompts for a user name as well. * \sa promptForUserName() */ void EnterPasswordDialog::setPromptForUserName(bool prompt) { m_ui->userNameLineEdit->setHidden(!prompt); adjustSize(); } /*! * \brief Returns an indication whether a verification (password has to be * entered twice) is required. * * \sa EnterPasswordDialog::setVerificationRequired() */ bool EnterPasswordDialog::isVerificationRequired() const { return !m_ui->password2LineEdit->isHidden(); } /*! * \brief Returns an indication whether the user is force to enter a password. * * If no password is required, the user is allowed to skip the dialog without * entering * a password. * * \sa EnterPasswordDialog::setPasswordRequired() */ bool EnterPasswordDialog::isPasswordRequired() const { return m_ui->noPwCheckBox->isHidden(); } /*! * \brief Sets whether the user is force to enter a password. * * If no password is required, the user is allowed to skip the dialog without * entering * a password. * * \sa EnterPasswordDialog::isPasswordRequired() */ void EnterPasswordDialog::setPasswordRequired(bool value) { m_ui->noPwCheckBox->setHidden(value); m_ui->noPwCheckBox->setChecked(false); adjustSize(); } /*! * \brief Updates the relevant controls to show entered characters or to mask * them them. * * This private slot is called when m_ui->showPasswordCheckBox is clicked. */ void EnterPasswordDialog::updateShowPassword() { m_ui->password1LineEdit->setEchoMode(m_ui->showPasswordCheckBox->isChecked() ? QLineEdit::Normal : QLineEdit::Password); m_ui->password1LineEdit->setEnabled(!m_ui->noPwCheckBox->isChecked()); m_ui->password2LineEdit->setEnabled(!(m_ui->showPasswordCheckBox->isChecked() || m_ui->noPwCheckBox->isChecked())); } /*! * \brief Sets whether a verification (password has to be entered twice) is * required. * * \sa EnterPasswordDialog::isVerificationRequired() */ void EnterPasswordDialog::setVerificationRequired(bool value) { if (m_instruction.isEmpty()) { m_ui->instructionLabel->setText(value ? tr("Enter the new password") : tr("Enter the password")); } m_ui->password2LineEdit->setHidden(!value); adjustSize(); } /*! * \brief Sets the instruction text. * * \sa EnterPasswordDialog::instruction() */ void EnterPasswordDialog::setInstruction(const QString &value) { m_instruction = value; if (m_instruction.isEmpty()) { m_ui->instructionLabel->setText(isVerificationRequired() ? tr("Enter the new password") : tr("Enter the password")); } else { m_ui->instructionLabel->setText(value); } adjustSize(); } bool EnterPasswordDialog::event(QEvent *event) { switch (event->type()) { case QEvent::KeyPress: { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_CapsLock) { m_capslockPressed = !m_capslockPressed; } m_ui->capslockWarningWidget->setVisible(m_capslockPressed); break; } default:; } return QDialog::event(event); } /*! * \brief Internal method to notice when the capslock key is pressed by the * user. * * Invocation of this method is done by installing the event filter in the * constructor. */ bool EnterPasswordDialog::eventFilter(QObject *sender, QEvent *event) { switch (event->type()) { case QEvent::KeyPress: { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); if (keyEvent->key() == Qt::Key_CapsLock) { m_capslockPressed = !m_capslockPressed; } else { QString text = keyEvent->text(); if (text.length()) { QChar firstChar = text.at(0); bool shiftPressed = (keyEvent->modifiers() & Qt::ShiftModifier) != 0; if ((shiftPressed && firstChar.isLower()) || (!shiftPressed && firstChar.isUpper())) { m_capslockPressed = true; } else if (firstChar.isLetter()) { m_capslockPressed = false; } } } m_ui->capslockWarningWidget->setVisible(m_capslockPressed); } break; case QEvent::FocusIn: if (sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) { releaseKeyboard(); qobject_cast<QWidget *>(sender)->grabKeyboard(); } break; case QEvent::FocusOut: if (sender == m_ui->userNameLineEdit || sender == m_ui->password1LineEdit || sender == m_ui->password2LineEdit) { qobject_cast<QWidget *>(sender)->releaseKeyboard(); grabKeyboard(); } break; default:; } return false; } /*! * \brief Sets the dialog status to QDialog::Accepted if a valid password has * been enterd. * Displays an error message otherwise. * * This private slot is called when m_ui->confirmPushButton is clicked. */ void EnterPasswordDialog::confirm() { if (!isPasswordRequired() && m_ui->noPwCheckBox->isChecked()) { m_password.clear(); done(QDialog::Accepted); } else { QString userName = m_ui->userNameLineEdit->text(); QString password = m_ui->password1LineEdit->text(); QString repeatedPassword = m_ui->password2LineEdit->text(); if (promtForUserName() && userName.isEmpty()) { QMessageBox::warning(this, windowTitle(), tr("You didn't enter a user name.")); } else if (password.isEmpty()) { QMessageBox::warning(this, windowTitle(), tr("You didn't enter a password.")); } else { if (isVerificationRequired() && (password != repeatedPassword) && !m_ui->showPasswordCheckBox->isChecked()) { if (repeatedPassword.isEmpty()) { QMessageBox::warning(this, windowTitle(), tr("You have to enter the new password twice to " "ensure you enterd it correct.")); } else { QMessageBox::warning(this, windowTitle(), tr("You mistyped the password.")); } } else { m_userName = userName; m_password = password; done(QDialog::Accepted); } } } } /*! * \brief Returns an indication whether the capslock key is pressed using * platform specific functions. * * \remarks * - Returns always false for unsupported platforms. * - This method always returns false when the detection is not * supported. It is supported under X11 * and Windows. * - The function requires the application to be linked against X11 on * Linux/Unix. * - This static function will be used internally to detect whether the * capslock key is pressed when initializing the dialog. */ bool EnterPasswordDialog::isCapslockPressed() { #ifdef QT_UTILITIES_PLATFORM_SPECIFIC_CAPSLOCK_DETECTION // platform dependent method of determining if CAPS LOCK is pressed #if defined(Q_OS_WIN32) return GetKeyState(VK_CAPITAL) == 1; #elif defined(X_AVAILABLE) Display *d = XOpenDisplay((char *)0); bool caps_state = false; if (d) { unsigned n; XkbGetIndicatorState(d, XkbUseCoreKbd, &n); caps_state = (n & 0x01) == 1; } return caps_state; #else return false; #endif #else return false; #endif } } // namespace QtUtilities 0707010000001D000081A4000000000000000000000001636534C900000B69000000000000000000000000000000000000003600000000qtutilities/enterpassworddialog/enterpassworddialog.h#ifndef DIALOGS_ENTERPASSWORDDIALOG_H #define DIALOGS_ENTERPASSWORDDIALOG_H #include "../global.h" #include <QDialog> #include <memory> namespace QtUtilities { namespace Ui { class EnterPasswordDialog; } class QT_UTILITIES_EXPORT EnterPasswordDialog : public QDialog { Q_OBJECT Q_PROPERTY(QString userName READ userName) Q_PROPERTY(QString password READ password) Q_PROPERTY(QString description READ description WRITE setDescription) Q_PROPERTY(bool promtForUserName READ promtForUserName WRITE setPromptForUserName) Q_PROPERTY(bool isVerificationRequired READ isVerificationRequired WRITE setVerificationRequired) Q_PROPERTY(bool isPasswordRequired READ isPasswordRequired WRITE setPasswordRequired) Q_PROPERTY(QString instruction READ instruction WRITE setInstruction) Q_PROPERTY(bool isCapslockPressed READ isCapslockPressed) public: explicit EnterPasswordDialog(QWidget *parent = nullptr); ~EnterPasswordDialog() override; const QString &userName() const; const QString &password() const; QString description() const; void setDescription(const QString &description = QString()); bool promtForUserName() const; void setPromptForUserName(bool prompt); bool isVerificationRequired() const; void setVerificationRequired(bool value); bool isPasswordRequired() const; void setPasswordRequired(bool value); const QString &instruction() const; void setInstruction(const QString &value); static bool isCapslockPressed(); protected: bool event(QEvent *event) override; bool eventFilter(QObject *sender, QEvent *event) override; private Q_SLOTS: void updateShowPassword(); void confirm(); void abort(); private: std::unique_ptr<Ui::EnterPasswordDialog> m_ui; QString m_userName; QString m_password; QString m_instruction; bool m_capslockPressed; }; /*! * \brief Returns the entered user name. */ inline const QString &EnterPasswordDialog::userName() const { return m_userName; } /*! * \brief Returns the entered password. */ inline const QString &EnterPasswordDialog::password() const { return m_password; } /*! * \brief Returns the instruction text. * * The instruction text is displayed at the top of the dialog. * If the instruction text is empty the default text "Enter the new password" * or "Enter the password" (depending on whether the verification is required or * not) displayed. * * \sa EnterPasswordDialog::setInstruction() */ inline const QString &EnterPasswordDialog::instruction() const { return m_instruction; } /*! * \brief Clears all results and sets the dialog status to QDialog::Rejected. * * This private slot is called when m_ui->abortPushButton is clicked. */ inline void EnterPasswordDialog::abort() { m_password.clear(); done(QDialog::Rejected); } } // namespace QtUtilities #endif // DIALOGS_ENTERPASSWORDDIALOG_H 0707010000001E000081A4000000000000000000000001636534C90000228B000000000000000000000000000000000000003700000000qtutilities/enterpassworddialog/enterpassworddialog.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::EnterPasswordDialog</class> <widget class="QDialog" name="QtUtilities::EnterPasswordDialog"> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="windowTitle"> <string>Enter the password</string> </property> <property name="modal"> <bool>true</bool> </property> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> <number>0</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QWidget" name="mainWidget" native="true"> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QLabel" name="instructionLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="maximumSize"> <size> <width>500</width> <height>16777215</height> </size> </property> <property name="text"> <string>Enter the password</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> <item> <widget class="QLabel" name="descLabel"/> </item> <item> <widget class="QtUtilities::ClearLineEdit" name="userNameLineEdit"> <property name="placeholderText"> <string>user name</string> </property> </widget> </item> <item> <widget class="QtUtilities::ClearLineEdit" name="password1LineEdit"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> <property name="maxLength"> <number>32</number> </property> <property name="echoMode"> <enum>QLineEdit::Password</enum> </property> <property name="placeholderText"> <string>password</string> </property> </widget> </item> <item> <widget class="QtUtilities::ClearLineEdit" name="password2LineEdit"> <property name="enabled"> <bool>true</bool> </property> <property name="maxLength"> <number>32</number> </property> <property name="echoMode"> <enum>QLineEdit::Password</enum> </property> <property name="placeholderText"> <string>repeat password</string> </property> </widget> </item> <item> <widget class="QWidget" name="checkboxesWidget" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_2"> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QCheckBox" name="showPasswordCheckBox"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>show password</string> </property> </widget> </item> <item> <widget class="QCheckBox" name="noPwCheckBox"> <property name="text"> <string>don't use a password</string> </property> </widget> </item> </layout> </widget> </item> </layout> </widget> </item> <item> <widget class="QWidget" name="bottomWidget" native="true"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QWidget" name="capslockWarningWidget" native="true"> <layout class="QHBoxLayout" name="horizontalLayout_3"> <property name="spacing"> <number>3</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QGraphicsView" name="capslockWarningGraphicsView"> <property name="maximumSize"> <size> <width>16</width> <height>16</height> </size> </property> <property name="styleSheet"> <string notr="true">background-color: transparent;</string> </property> <property name="frameShape"> <enum>QFrame::NoFrame</enum> </property> <property name="backgroundBrush"> <brush brushstyle="NoBrush"> <color alpha="0"> <red>0</red> <green>0</green> <blue>0</blue> </color> </brush> </property> <property name="sceneRect"> <rectf> <x>0.000000000000000</x> <y>0.000000000000000</y> <width>16.000000000000000</width> <height>16.000000000000000</height> </rectf> </property> </widget> </item> <item> <widget class="QLabel" name="capslockWarningLabel"> <property name="styleSheet"> <string notr="true">font-weight: bold;</string> </property> <property name="text"> <string>Capslock is active</string> </property> </widget> </item> </layout> </widget> </item> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>168</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="abortPushButton"> <property name="styleSheet"> <string notr="true">background: none;</string> </property> <property name="text"> <string>Abort</string> </property> <property name="icon"> <iconset theme="window-close"> <normaloff/> </iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="confirmPushButton"> <property name="styleSheet"> <string notr="true">background: none;</string> </property> <property name="text"> <string>Confirm</string> </property> <property name="icon"> <iconset theme="go-next"> <normaloff/> </iconset> </property> <property name="default"> <bool>true</bool> </property> </widget> </item> </layout> </widget> </item> </layout> </widget> <customwidgets> <customwidget> <class>QtUtilities::ClearLineEdit</class> <extends>QLineEdit</extends> <header>widgets/clearlineedit.h</header> </customwidget> </customwidgets> <tabstops> <tabstop>password1LineEdit</tabstop> <tabstop>password2LineEdit</tabstop> <tabstop>confirmPushButton</tabstop> <tabstop>abortPushButton</tabstop> </tabstops> <resources/> <connections/> </ui> 0707010000001F000081A4000000000000000000000001636534C9000002B8000000000000000000000000000000000000001500000000qtutilities/global.h// Created via CMake from template global.h.in // WARNING! Any changes to this file will be overwritten by the next CMake run! #ifndef QT_UTILITIES_GLOBAL #define QT_UTILITIES_GLOBAL #include <c++utilities/application/global.h> #ifdef QT_UTILITIES_STATIC #define QT_UTILITIES_EXPORT #define QT_UTILITIES_IMPORT #else #define QT_UTILITIES_EXPORT CPP_UTILITIES_GENERIC_LIB_EXPORT #define QT_UTILITIES_IMPORT CPP_UTILITIES_GENERIC_LIB_IMPORT #endif /*! * \def QT_UTILITIES_EXPORT * \brief Marks the symbol to be exported by the qtutilities library. */ /*! * \def QT_UTILITIES_IMPORT * \brief Marks the symbol to be imported from the qtutilities library. */ #endif // QT_UTILITIES_GLOBAL 07070100000020000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001100000000qtutilities/misc07070100000021000081A4000000000000000000000001636534C9000002B6000000000000000000000000000000000000001F00000000qtutilities/misc/adoptlocker.h#ifndef THEADING_UTILS_ADOPTLOCKER_H #define THEADING_UTILS_ADOPTLOCKER_H #include <QtGlobal> QT_FORWARD_DECLARE_CLASS(QMutex) namespace QtUtilities { /*! * \brief Like QMutexLocker, but assumes that the mutex has already been locked. */ template <typename Mutex = QMutex> class AdoptLocker { public: /*! * \brief Constructs the locker for the specified \a mutex. */ AdoptLocker(Mutex &mutex) : m_mutex(mutex) { } /*! * \brief Unlocks the mutex specified when constructing the instance. */ ~AdoptLocker() { m_mutex.unlock(); } private: Mutex &m_mutex; }; } // namespace QtUtilities #endif // THEADING_UTILS_ADOPTLOCKER_H 07070100000022000081A4000000000000000000000001636534C9000009B4000000000000000000000000000000000000001A00000000qtutilities/misc/compat.h#ifndef QT_UTILITIES_COMPAT_H #define QT_UTILITIES_COMPAT_H #include "../global.h" #include <c++utilities/misc/traits.h> #include <QtGlobal> #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #define QT_UTILITIES_USE_Q_STRING_VIEW #endif // note: QStringView has been available since Qt 5.10 but for a consistent ABI/API regardless which // Qt 5.x version is used it makes sense to stick to QStringRef when using Qt 5. #ifdef QT_UTILITIES_USE_Q_STRING_VIEW #include <QStringView> #else #include <QStringRef> #endif namespace QtUtilities { using Utf16CharType = #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) char16_t #else ushort #endif ; using StringView = #ifdef QT_UTILITIES_USE_Q_STRING_VIEW QStringView #else QStringRef #endif ; /*! * \brief Makes either a QStringView or a QStringRef depending on the Qt version. */ inline StringView makeStringView(const QString &str) { return StringView( #ifndef QT_UTILITIES_USE_Q_STRING_VIEW & #endif str); } /*! * \brief Makes either a QStringView or a QStringRef depending on the Qt version, applying "mid()" parameters. */ template <typename PosType1, typename PosType2 = PosType1, CppUtilities::Traits::EnableIf<std::is_integral<PosType1>, std::is_signed<PosType1>, std::is_integral<PosType2>, std::is_signed<PosType2>> * = nullptr> inline StringView midRef(const QString &str, PosType1 pos, PosType2 n = -1) { #ifdef QT_UTILITIES_USE_Q_STRING_VIEW return QStringView(str).mid(pos, n); #else return str.midRef(pos, n); #endif } /*! * \brief Splits \a str into QStringViews, QStringRefs or QStrings depending on the Qt version. */ template <class... SplitArgs> inline auto splitRef(const QString &str, SplitArgs &&...args) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) return QStringView(str).split(std::forward<SplitArgs>(args)...); #elif QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) return str.splitRef(std::forward<SplitArgs>(args)...); #else return str.split(std::forward<SplitArgs>(args)...); #endif } } // namespace QtUtilities #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) QT_BEGIN_NAMESPACE /*! * \brief Provides a fallback for qEnvironmentVariable() when using old Qt versions. */ inline QString qEnvironmentVariable(const char *varName, const QString &defaultValue) { const auto val = qgetenv(varName); return !val.isEmpty() ? QString::fromLocal8Bit(val) : defaultValue; } QT_END_NAMESPACE #endif #endif // QT_UTILITIES_COMPAT_H 07070100000023000081A4000000000000000000000001636534C900000503000000000000000000000000000000000000001E00000000qtutilities/misc/conversion.h#ifndef QT_UTILITIES_CONVERSION_H #define QT_UTILITIES_CONVERSION_H #include "../global.h" #include <QString> #include <string> #include <string_view> namespace QtUtilities { inline QByteArray toNativeFileName(const QString &fileName) { #if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) return fileName.toLocal8Bit(); #else return fileName.toUtf8(); #endif } inline QString fromNativeFileName(const char *nativeFileName, int size = -1) { #if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) return QString::fromLocal8Bit(nativeFileName, size); #else return QString::fromUtf8(nativeFileName, size); #endif } inline QString fromNativeFileName(const std::string &nativeFileName) { #if !defined(PLATFORM_MINGW) || !defined(CPP_UTILITIES_USE_NATIVE_FILE_BUFFER) return QString::fromLocal8Bit(nativeFileName.data(), static_cast<int>(nativeFileName.size())); #else return QString::fromUtf8(nativeFileName.data(), static_cast<int>(nativeFileName.size())); #endif } inline QString qstringFromStdStringView(std::string_view stringView) { return QString::fromUtf8(stringView.data(), static_cast<QString::size_type>(stringView.size())); } } // namespace QtUtilities #endif // QT_UTILITIES_CONVERSION_H 07070100000024000081A4000000000000000000000001636534C900003E8E000000000000000000000000000000000000002600000000qtutilities/misc/dbusnotification.cpp#include "./dbusnotification.h" #include "notificationsinterface.h" #include <QCoreApplication> #include <QDBusConnection> #include <QDBusPendingReply> #include <QImage> #include <QMutex> #include <QMutexLocker> #include <limits> #include <map> using namespace std; namespace QtUtilities { /*! * \class DBusNotification * \brief The DBusNotification class emits D-Bus notifications. * * D-Bus notifications are only available if the library has been compiled with * support for it by specifying CMake option `DBUS_NOTIFICATIONS`. If support is * available, the macro `QT_UTILITIES_SUPPORT_DBUS_NOTIFICATIONS` is defined. * * **Usage** * * First create a new instance. The constructor allows to set basic parameters. * To set more parameters, use setter methods. Call show() to actually show the * notification. This method can also be used to update the currently shown notification * (it will not be updated automatically by just using the setter methods). * * Instances of this class share static data. So do not call the member functions * from different threads without proper synchronization - even if using different * instances. The destructor is safe to call, though. * * \sa https://developer.gnome.org/notification-spec */ /// \cond using IDType = uint; static QMutex pendingNotificationsMutex; static std::map<IDType, DBusNotification *> pendingNotifications; OrgFreedesktopNotificationsInterface *DBusNotification::s_dbusInterface = nullptr; constexpr auto initialId = std::numeric_limits<IDType>::min(); constexpr auto pendingId = std::numeric_limits<IDType>::max(); constexpr auto pendingId2 = pendingId - 1; /// \endcond /*! * \brief The SwappedImage struct represents RGB-interved version of the image specified on construction. */ struct SwappedImage : public QImage { SwappedImage(const QImage &image); }; inline SwappedImage::SwappedImage(const QImage &image) : QImage(image.rgbSwapped()) { } /*! * \brief The NotificationImage struct is a raw data image format. * * It describes the width, height, rowstride, has alpha, bits per sample, channels and image data respectively. */ struct NotificationImage : public QDBusArgument { NotificationImage(); NotificationImage(SwappedImage image); QImage toQImage() const; QVariant toDBusArgument() const; static NotificationImage fromDBusArgument(const QVariant &variant); qint32 width; qint32 height; qint32 rowstride; bool hasAlpha; qint32 channels; qint32 bitsPerSample; QByteArray data; bool isValid; private: NotificationImage(const QImage &image); }; QDBusArgument &operator<<(QDBusArgument &argument, const NotificationImage &img) { argument.beginStructure(); argument << img.width << img.height << img.rowstride << img.hasAlpha << img.bitsPerSample << img.channels << img.data; argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, NotificationImage &img) { argument.beginStructure(); argument >> img.width >> img.height >> img.rowstride >> img.hasAlpha >> img.bitsPerSample >> img.channels >> img.data; argument.endStructure(); return argument; } inline NotificationImage::NotificationImage() : isValid(false) { } inline NotificationImage::NotificationImage(SwappedImage image) : NotificationImage(static_cast<const QImage &>(image)) { } inline NotificationImage::NotificationImage(const QImage &image) : width(image.width()) , height(image.height()) , rowstride(static_cast<qint32>(image.bytesPerLine())) , hasAlpha(image.hasAlphaChannel()) , channels(image.isGrayscale() ? 1 : hasAlpha ? 4 : 3) , bitsPerSample(image.depth() / channels) #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) , data(reinterpret_cast<const char *>(image.bits()), static_cast<int>(image.sizeInBytes())) #else , data(reinterpret_cast<const char *>(image.bits()), image.byteCount()) #endif , isValid(!image.isNull()) { if (isValid) { // populate QDBusArgument structure // note: Just use the operator overload which is required for qDBusMarshallHelper(). *this << *this; } } inline QImage NotificationImage::toQImage() const { return isValid ? QImage(reinterpret_cast<const uchar *>(data.constData()), width, height, hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32) .rgbSwapped() : QImage(); } inline QVariant NotificationImage::toDBusArgument() const { return QVariant::fromValue(*this); } inline NotificationImage NotificationImage::fromDBusArgument(const QVariant &variant) { return variant.canConvert<NotificationImage>() ? variant.value<NotificationImage>() : NotificationImage(); } } // namespace QtUtilities Q_DECLARE_METATYPE(QtUtilities::NotificationImage); namespace QtUtilities { /*! * \brief Creates a new notification (which is *not* shown instantly). */ DBusNotification::DBusNotification(const QString &title, NotificationIcon icon, int timeout, QObject *parent) : QObject(parent) , m_id(initialId) , m_watcher(nullptr) , m_title(title) , m_timeout(timeout) { initInterface(); setIcon(icon); } /*! * \brief Creates a new notification (which is *not* shown instantly). */ DBusNotification::DBusNotification(const QString &title, const QString &icon, int timeout, QObject *parent) : QObject(parent) , m_id(initialId) , m_watcher(nullptr) , m_title(title) , m_icon(icon) , m_timeout(timeout) { initInterface(); } /*! * \brief Initializes the static interface object if not done yet. */ void DBusNotification::initInterface() { if (!s_dbusInterface) { qDBusRegisterMetaType<NotificationImage>(); s_dbusInterface = new OrgFreedesktopNotificationsInterface( QStringLiteral("org.freedesktop.Notifications"), QStringLiteral("/org/freedesktop/Notifications"), QDBusConnection::sessionBus()); connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::ActionInvoked, &DBusNotification::handleActionInvoked); connect(s_dbusInterface, &OrgFreedesktopNotificationsInterface::NotificationClosed, &DBusNotification::handleNotificationClosed); } } /*! * \brief Closes the notification if still shown and delete the object. */ DBusNotification::~DBusNotification() { { QMutexLocker lock(&pendingNotificationsMutex); auto i = pendingNotifications.find(m_id); if (i != pendingNotifications.end()) { pendingNotifications.erase(i); } } hide(); } /*! * \brief Returns whether the notification D-Bus daemon is running. */ bool DBusNotification::isAvailable() { initInterface(); return s_dbusInterface->isValid(); } /*! * \brief Sets the icon to one of the pre-defined notification icons. */ void DBusNotification::setIcon(NotificationIcon icon) { switch (icon) { case NotificationIcon::Information: m_icon = QStringLiteral("dialog-information"); break; case NotificationIcon::Warning: m_icon = QStringLiteral("dialog-warning"); break; case NotificationIcon::Critical: m_icon = QStringLiteral("dialog-critical"); break; default:; } } /*! * \brief Returns the image. * \sa setImage() for more details */ const QImage DBusNotification::image() const { return NotificationImage::fromDBusArgument(hint(QStringLiteral("image-data"), QStringLiteral("image_data"))).toQImage(); } /*! * \brief Sets the image. * \remarks * \a image is a raw data image format which describes the width, height, rowstride, * has alpha, bits per sample, channels and image data respectively. */ void DBusNotification::setImage(const QImage &image) { m_hints[QStringLiteral("image-data")] = NotificationImage(SwappedImage(image)).toDBusArgument(); } /*! * \brief * Returns whether the notification is about to be shown after calling show() or update() but has not been shown yet. * \remarks * This is the case when show() or update() has been called but the notification daemon has not responded yet. When * the notification daemon has responded or an error occurred isPending() will return false again. On success, isVisible() * should return true instead. */ bool DBusNotification::isPending() const { return m_id == pendingId || m_id == pendingId2; } /*! * \brief Makes the notification object delete itself when the notification has * been closed or an error occurred. */ void DBusNotification::deleteOnCloseOrError() { connect(this, &DBusNotification::closed, this, &DBusNotification::deleteLater); connect(this, &DBusNotification::error, this, &DBusNotification::deleteLater); } /*! * \brief Shows the notification. * \remarks * - If called when a previous notification is still shown, the previous notification is updated. * - If called when a previous notification is about to be shown (isShowing() returns true) no second notification * is spawned immediately. Instead, the previously started notification will be updated once it has been shown to * apply changes. * \returns Returns false is the D-Bus daemon isn't reachable and true otherwise. */ bool DBusNotification::show() { if (isPending()) { m_id = pendingId2; return true; } if (!s_dbusInterface->isValid()) { emit error(); return false; } delete m_watcher; m_watcher = new QDBusPendingCallWatcher(s_dbusInterface->Notify(m_applicationName.isEmpty() ? QCoreApplication::applicationName() : m_applicationName, m_id, m_icon, m_title, m_msg, m_actions, m_hints, m_timeout), this); connect(m_watcher, &QDBusPendingCallWatcher::finished, this, &DBusNotification::handleNotifyResult); m_id = pendingId; return true; } /*! * \brief Updates the message and shows/updates the notification. * \remarks If called when a previous notification is still shown, the previous * notification is updated. * \returns Returns false is the D-Bus daemon isn't reachable and true * otherwise. The message is updated in any case. */ bool DBusNotification::show(const QString &message) { m_msg = message; return show(); } /*! * \brief Updates the message and shows/updates the notification. * \remarks * - If called when a previous notification is still shown, the previous * notification is updated. In this * case the specified \a line will be appended to the current message. * - If called when no previous notification is still shown, the previous * message is completely replaced * by \a line and shown as a new notification. * \returns Returns false is the D-Bus daemon isn't reachable and true * otherwise. The message is updated in any case. */ bool DBusNotification::update(const QString &line) { if ((!isPending() && !isVisible()) || m_msg.isEmpty()) { m_msg = line; } else { if (!m_msg.startsWith(QStringLiteral("•"))) { m_msg.insert(0, QStringLiteral("• ")); } m_msg.append(QStringLiteral("\n• ")); m_msg.append(line); } return show(); } bool DBusNotification::queryCapabilities(const std::function<void(Capabilities &&capabilities)> &callback) { // ensure DBus-interface is initialized and valid initInterface(); if (!s_dbusInterface->isValid()) { return false; } // invoke GetCapabilities() and pass the return value to the callback when available const auto *const newWatcher = new QDBusPendingCallWatcher(s_dbusInterface->GetCapabilities()); connect(newWatcher, &QDBusPendingCallWatcher::finished, [&callback](QDBusPendingCallWatcher *watcher) { watcher->deleteLater(); const QDBusPendingReply<QStringList> returnValue(*watcher); if (returnValue.isError()) { callback(Capabilities()); } else { callback(Capabilities(std::move(returnValue.value()))); } }); return true; } /*! * \brief Hides the notification (if still visible). * \remarks On success, the signal closed() is emitted with the reason * NotificationCloseReason::Manually. */ bool DBusNotification::hide() { if (m_id) { s_dbusInterface->CloseNotification(m_id); return true; } return false; } /*! * \brief Handles the results of the Notify D-Bus call. */ void DBusNotification::handleNotifyResult(QDBusPendingCallWatcher *watcher) { if (watcher != m_watcher) { return; } watcher->deleteLater(); m_watcher = nullptr; QDBusPendingReply<uint> returnValue = *watcher; if (returnValue.isError()) { m_id = initialId; emit error(); return; } const auto needsUpdate = m_id == pendingId2; { QMutexLocker lock(&pendingNotificationsMutex); pendingNotifications[m_id = returnValue.argumentAt<0>()] = this; } emit shown(); // update the notification again if show() was called before we've got the ID if (needsUpdate) { show(); } } /*! * \brief Handles the NotificationClosed D-Bus signal. */ void DBusNotification::handleNotificationClosed(IDType id, uint reason) { QMutexLocker lock(&pendingNotificationsMutex); auto i = pendingNotifications.find(id); if (i != pendingNotifications.end()) { DBusNotification *notification = i->second; notification->m_id = initialId; emit notification->closed(reason >= 1 && reason <= 3 ? static_cast<NotificationCloseReason>(reason) : NotificationCloseReason::Undefined); pendingNotifications.erase(i); } } /*! * \brief Handles the ActionInvoked D-Bus signal. */ void DBusNotification::handleActionInvoked(IDType id, const QString &action) { QMutexLocker lock(&pendingNotificationsMutex); auto i = pendingNotifications.find(id); if (i != pendingNotifications.end()) { DBusNotification *notification = i->second; emit notification->actionInvoked(action); // Plasma 5 also closes the notification but doesn't emit the // NotificationClose signal // -> just consider the notification closed emit notification->closed(NotificationCloseReason::ActionInvoked); notification->m_id = initialId; pendingNotifications.erase(i); // however, lxqt-notificationd does not close the notification // -> close manually for consistent behaviour s_dbusInterface->CloseNotification(i->first); } } /*! * \fn DBusNotification::message() * \brief Returns the assigned message. * \sa setMessage() for more details. */ /*! * \fn DBusNotification::setMessage() * \brief Sets the message to be shown. * \remarks * - Might also be set via show() and update(). * - Can contain the following HTML tags: `<b>`, `<i>`, `<u>`, `<a href="...">` * and `<img src="..." alt="..."/>` */ /*! * \fn DBusNotification::timeout() * \brief Returns the number of milliseconds the notification will be visible * after calling show(). * \sa setTimeout() for more details. */ /*! * \fn DBusNotification::setTimeout() * \brief Sets the number of milliseconds the notification will be visible after * calling show(). * \remarks * - Set to 0 for non-expiring notifications. * - Set to -1 to let the notification daemon decide. */ /*! * \fn DBusNotification::actions() * \brief Returns the assigned actions. * \sa setActions() for more details. */ /*! * \fn DBusNotification::setActions() * \brief Sets the list of available actions. * \remarks * The list consists of pairs of action IDs and action labels, eg. * `QStringList({QStringLiteral("first_id"), tr("First action"), * QStringLiteral("second_id"), tr("Second action"), ...})` * \sa actionInvoked() signal */ /*! * \fn DBusNotification::isVisible() * \brief Returns whether the notification is (still) visible. */ } // namespace QtUtilities 07070100000025000081A4000000000000000000000001636534C900002A24000000000000000000000000000000000000002400000000qtutilities/misc/dbusnotification.h#ifndef MISC_UTILS_NOTIFICATION_H #define MISC_UTILS_NOTIFICATION_H #include "../global.h" #include <QObject> #include <QSet> #include <QVariantMap> #include <functional> QT_FORWARD_DECLARE_CLASS(QDBusPendingCallWatcher) class OrgFreedesktopNotificationsInterface; namespace QtUtilities { enum class NotificationIcon { NoIcon, Information, Warning, Critical }; enum class NotificationCloseReason { Undefined, Expired, Dismissed, Manually, ActionInvoked }; class QT_UTILITIES_EXPORT DBusNotification : public QObject { Q_OBJECT Q_PROPERTY(QString applicationName READ applicationName WRITE setApplicationName) Q_PROPERTY(QString title READ title WRITE setTitle) Q_PROPERTY(QString message READ message WRITE setMessage) Q_PROPERTY(QString icon READ icon WRITE setIcon) Q_PROPERTY(int timeout READ timeout WRITE setTimeout) Q_PROPERTY(QStringList actions READ actions WRITE setActions) Q_PROPERTY(bool visible READ isVisible) Q_PROPERTY(bool pending READ isPending) public: using IDType = uint; class QT_UTILITIES_EXPORT Capabilities : public QSet<QString> { public: explicit Capabilities(); explicit Capabilities(const QStringList &capabilities); bool isValid() const; bool supportsBody() const; bool supportsLinks() const; bool supportsMarkup() const; bool supportsImages() const; bool supportsIcon() const; bool supportsActions() const; bool supportsAnimatedIcon() const; bool supportsActionIcons() const; bool supportsSound() const; bool supportsPercistence() const; private: bool m_valid; }; explicit DBusNotification( const QString &title, NotificationIcon icon = NotificationIcon::Information, int timeout = 10000, QObject *parent = nullptr); explicit DBusNotification(const QString &title, const QString &icon, int timeout = 10000, QObject *parent = nullptr); ~DBusNotification() override; static bool isAvailable(); const QString &applicationName() const; void setApplicationName(const QString &applicationName); const QString &title() const; void setTitle(const QString &title); const QString &message() const; void setMessage(const QString &message); const QString &icon() const; void setIcon(const QString &icon); void setIcon(NotificationIcon icon); const QImage image() const; void setImage(const QImage &image); const QString imagePath() const; void setImagePath(const QString &imagePath); int timeout() const; void setTimeout(int timeout); int urgency() const; void setUrgency(quint8 urgency); bool isResident() const; void setResident(bool resident); QString category() const; void setCategory(const QString &category); const QStringList &actions() const; void setActions(const QStringList &actions); const QVariantMap &hints() const; QVariantMap &hints(); QVariant hint(const QString &name) const; QVariant hint(const QString &name, const QString &fallbackNames...) const; bool isVisible() const; bool isPending() const; void deleteOnCloseOrError(); static bool queryCapabilities(const std::function<void(Capabilities &&capabilities)> &callback); public Q_SLOTS: bool show(); bool show(const QString &message); bool update(const QString &line); bool hide(); Q_SIGNALS: /// \brief Emitted when the notification could be shown successful. void shown(); /// \brief Emitted when the notification couldn't be shown. void error(); /// \brief Emitted when the notification has been closed. void closed(NotificationCloseReason reason); /// \brief Emitted when \a action has been invoked. void actionInvoked(const QString &action); private Q_SLOTS: void handleNotifyResult(QDBusPendingCallWatcher *); static void handleNotificationClosed(IDType id, uint reason); static void handleActionInvoked(IDType id, const QString &action); private: static void initInterface(); IDType m_id; QDBusPendingCallWatcher *m_watcher; QString m_applicationName; QString m_title; QString m_msg; QString m_icon; int m_timeout; QStringList m_actions; QVariantMap m_hints; static OrgFreedesktopNotificationsInterface *s_dbusInterface; }; inline DBusNotification::Capabilities::Capabilities() : m_valid(false) { } inline DBusNotification::Capabilities::Capabilities(const QStringList &capabilities) #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) : QSet<QString>(capabilities.toSet()) #else : QSet<QString>(capabilities.begin(), capabilities.end()) #endif , m_valid(true) { } inline bool DBusNotification::Capabilities::isValid() const { return m_valid; } inline bool DBusNotification::Capabilities::supportsBody() const { return contains(QStringLiteral("body")); } inline bool DBusNotification::Capabilities::supportsLinks() const { return contains(QStringLiteral("body-hyperlinks")); } inline bool DBusNotification::Capabilities::supportsMarkup() const { return contains(QStringLiteral("body-markup")); } inline bool DBusNotification::Capabilities::supportsImages() const { return contains(QStringLiteral("body-images")); } inline bool DBusNotification::Capabilities::supportsIcon() const { return contains(QStringLiteral("icon-static")) || supportsAnimatedIcon(); } inline bool DBusNotification::Capabilities::supportsActions() const { return contains(QStringLiteral("actions")); } inline bool DBusNotification::Capabilities::supportsAnimatedIcon() const { return contains(QStringLiteral("icon-multi")); } inline bool DBusNotification::Capabilities::supportsActionIcons() const { return contains(QStringLiteral("action-icons")); } inline bool DBusNotification::Capabilities::supportsSound() const { return contains(QStringLiteral("sound")); } inline bool DBusNotification::Capabilities::supportsPercistence() const { return contains(QStringLiteral("persistence")); } /*! * \brief Returns the application name to be used. * \remarks If the application name is empty (which is the default), QCoreApplication::applicationName() is used instead. */ inline const QString &DBusNotification::applicationName() const { return m_applicationName; } /*! * \brief Sets the application name to be used. * \remarks If the application name is empty (which is the default), QCoreApplication::applicationName() is used instead. */ inline void DBusNotification::setApplicationName(const QString &applicationName) { m_applicationName = applicationName; } inline const QString &DBusNotification::title() const { return m_title; } inline void DBusNotification::setTitle(const QString &title) { m_title = title; } inline const QString &DBusNotification::message() const { return m_msg; } inline void DBusNotification::setMessage(const QString &message) { m_msg = message; } /*! * \brief Returns the icon name. * \sa setIcon() for more details */ inline const QString &DBusNotification::icon() const { return m_icon; } /*! * \brief Sets the icon name. * \remarks * The specified \a icon should be either an URI (file:// is the only URI schema * supported right now) or a name in an icon theme. */ inline void DBusNotification::setIcon(const QString &icon) { m_icon = icon; } /*! * \brief Returns the hint with the specified \a name. */ inline QVariant DBusNotification::hint(const QString &name) const { return m_hints[name]; } /*! * \brief Returns the hint with the specified \a name. If no hint is present, the \a fallbackNames are tried in the specified order. */ inline QVariant DBusNotification::hint(const QString &name, const QString &fallbackNames...) const { const auto variant(m_hints[name]); return variant.isNull() ? this->hint(fallbackNames) : variant; } /*! * \brief Returns the image path. * \sa setImagePath() for more details */ inline const QString DBusNotification::imagePath() const { return hint(QStringLiteral("image-data"), QStringLiteral("image_path")).toString(); } /*! * \brief Sets the image path. * \remarks * Alternative way to define the notification image; setImage() precedes. */ inline void DBusNotification::setImagePath(const QString &imagePath) { m_hints[QStringLiteral("image-path")] = imagePath; } inline int DBusNotification::timeout() const { return m_timeout; } inline void DBusNotification::setTimeout(int timeout) { m_timeout = timeout; } /*! * \brief Returns the urgency level (0 = low, 1 = normal, 2 = critical). */ inline int DBusNotification::urgency() const { return m_hints[QStringLiteral("urgency")].toInt(); } /*! * \brief Sets the urgency level (0 = low, 1 = normal, 2 = critical). */ inline void DBusNotification::setUrgency(quint8 urgency) { m_hints[QStringLiteral("urgency")] = urgency; } /*! * \brief Returns whether the notification will remain visible after an action has been clicked. */ inline bool DBusNotification::isResident() const { return m_hints[QStringLiteral("resident")].toBool(); } /*! * \brief Sets whether the notification will remain visible after an action has been clicked. */ inline void DBusNotification::setResident(bool resident) { m_hints[QStringLiteral("resident")] = resident; } /*! * \brief Returns the category. * \sa https://developer.gnome.org/notification-spec/#categories */ inline QString DBusNotification::category() const { return m_hints[QStringLiteral("category")].toString(); } /*! * \brief Sets the category. * \sa https://developer.gnome.org/notification-spec/#categories */ inline void DBusNotification::setCategory(const QString &category) { m_hints[QStringLiteral("category")] = category; } /*! * \brief Returns the actions for the notification. * * The actions are a list of action IDs and action names. The ID is returned by * the actionInvoked() signal if an action is triggered. The action name is the * user-visible name of the notification. * * Example: { QStringLiteral("dismiss"), tr("Dismiss notification"), * QStringLiteral("details"), tr("Show details") } */ inline const QStringList &DBusNotification::actions() const { return m_actions; } /*! * \brief Sets the actions for the notification. * \sa see actions() for details and an example */ inline void DBusNotification::setActions(const QStringList &actions) { m_actions = actions; } inline const QVariantMap &DBusNotification::hints() const { return m_hints; } inline QVariantMap &DBusNotification::hints() { return m_hints; } inline bool DBusNotification::isVisible() const { return m_id != 0; } } // namespace QtUtilities #endif // MISC_UTILS_NOTIFICATION_H 07070100000026000081A4000000000000000000000001636534C900000665000000000000000000000000000000000000002200000000qtutilities/misc/desktoputils.cpp#include "./desktoputils.h" #include <QDesktopServices> #include <QUrl> #ifdef Q_OS_WIN32 #include <QFileInfo> #endif namespace QtUtilities { /*! * \brief Shows the specified file or directory using the default file browser. * \remarks * - The specified \a path must *not* be specified as URL. (The conversion to a URL suitable for * QDesktopServices::openUrl() is the whole purpose of this function). * - The Qt documentation suggests to use * `QDesktopServices::openUrl(QUrl("file:///C:/Documents and Settings/All Users/Desktop", QUrl::TolerantMode));` * under Windows. However, that does not work if the path contains a '#'. It is also better to use * QUrl::DecodedMode to prevent QUrl from interpreting any of the paths characters in a special way. */ bool openLocalFileOrDir(const QString &path) { QUrl url(QStringLiteral("file://")); #ifdef Q_OS_WIN32 // replace backslashes with regular slashes QString tmp(path); tmp.replace(QChar('\\'), QChar('/')); // add a slash before the drive letter of an absolute path if (QFileInfo(path).isAbsolute()) { tmp = QStringLiteral("/") + tmp; } url.setPath(tmp, QUrl::DecodedMode); #else url.setPath(path, QUrl::DecodedMode); #endif return QDesktopServices::openUrl(url); } /*! * \brief Returns whether \a palette is dark. * \remarks Just call with no argument to check for the default palette to see whether "dark mode" is enabled. */ bool isPaletteDark(const QPalette &palette) { return palette.color(QPalette::WindowText).lightness() > palette.color(QPalette::Window).lightness(); } } // namespace QtUtilities 07070100000027000081A4000000000000000000000001636534C900000191000000000000000000000000000000000000002000000000qtutilities/misc/desktoputils.h#ifndef DESKTOP_UTILS_DESKTOPSERVICES_H #define DESKTOP_UTILS_DESKTOPSERVICES_H #include "../global.h" #include <QPalette> QT_FORWARD_DECLARE_CLASS(QString) namespace QtUtilities { QT_UTILITIES_EXPORT bool openLocalFileOrDir(const QString &path); QT_UTILITIES_EXPORT bool isPaletteDark(const QPalette &palette = QPalette()); } // namespace QtUtilities #endif // DESKTOP_UTILS_DESKTOPSERVICES_H 07070100000028000081A4000000000000000000000001636534C9000021CA000000000000000000000000000000000000002100000000qtutilities/misc/dialogutils.cpp#include "./dialogutils.h" #include <QCoreApplication> #include <QDir> #include <QFileInfo> #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) #include <QGuiApplication> #include <QPalette> #endif #if defined(QT_UTILITIES_GUI_QTWIDGETS) #include <QApplication> #include <QCursor> #if (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) #include <QDesktopWidget> #endif #include <QScreen> #include <QStyle> #include <QWidget> #endif namespace QtUtilities { /*! * \brief Generates the window title string for the specified \a documentStatus * and \a documentPath. */ QString generateWindowTitle(DocumentStatus documentStatus, const QString &documentPath) { switch (documentStatus) { case DocumentStatus::Saved: if (documentPath.isEmpty()) { return QCoreApplication::translate("Utilities::windowTitle", "Unsaved - %1").arg(QCoreApplication::applicationName()); } else { const QFileInfo file(documentPath); return QCoreApplication::translate("Utilities::windowTitle", "%1 - %2 - %3") .arg(file.fileName(), file.dir().path(), QCoreApplication::applicationName()); } case DocumentStatus::Unsaved: if (documentPath.isEmpty()) { return QCoreApplication::translate("Utilities::windowTitle", "*Unsaved - %1").arg(QCoreApplication::applicationName()); } else { const QFileInfo file(documentPath); return QCoreApplication::translate("Utilities::windowTitle", "*%1 - %2 - %3") .arg(file.fileName(), file.dir().path(), QCoreApplication::applicationName()); } case DocumentStatus::NoDocument: return QCoreApplication::applicationName(); default: return QString(); // to suppress warning: "control reaches end of non-void // function" } } #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) #ifdef Q_OS_WIN32 /*! * \brief Returns the color used to draw frames. */ QColor windowFrameColor() { return QGuiApplication::palette().window().color().darker(108); } /*! * \brief Returns the color used to draw instructions. */ QColor instructionTextColor() { const auto baseColor = QGuiApplication::palette().base().color(); return (baseColor.value() > 204 && baseColor.saturation() < 63) ? QColor(0x00, 0x33, 0x99) : QGuiApplication::palette().text().color(); } #endif /*! * \brief Returns the stylesheet for dialogs and other windows used in my * applications. */ const QString &dialogStyle() { #ifdef Q_OS_WIN32 static const auto style = QStringLiteral("#mainWidget { color: palette(text); background-color: " "palette(base); border: none; }" "#bottomWidget { background-color: palette(window); " "color: palette(window-text); border-top: 1px solid %1; }" "QMessageBox QLabel, QInputDialog QLabel, " "*[classNames~=\"heading\"] { font-size: 12pt; color: %2; " "}" "*[classNames~=\"input-invalid\"] { color: red; }") .arg(windowFrameColor().name(), instructionTextColor().name()); #else static const auto style = QStringLiteral("*[classNames~=\"heading\"] { font-weight: bold; }" "*[classNames~=\"input-invalid\"] { color: red; }"); #endif return style; } #ifdef QT_UTILITIES_GUI_QTWIDGETS QRect availableScreenGeometryAtPoint(const QPoint &point) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QScreen *const screen = QGuiApplication::screenAt(point); if (!screen) { return QRect(); } return screen->availableGeometry(); #else return QApplication::desktop()->availableGeometry(point); #endif } /// \cond static QRect shrinkRectByMargins(QRect rect, const QMargins &margins) { rect.setLeft(rect.left() + margins.left()); rect.setTop(rect.top() + margins.top()); rect.setRight(rect.right() - margins.right()); rect.setBottom(rect.bottom() - margins.bottom()); return rect; } static QRect limitRect(QRect rect, const QRect &bounds) { if (rect.left() < bounds.left()) { rect.setLeft(bounds.left()); } if (rect.top() < bounds.top()) { rect.setTop(bounds.top()); } if (rect.right() > bounds.right()) { rect.setRight(bounds.right()); } if (rect.bottom() > bounds.bottom()) { rect.setBottom(bounds.bottom()); } return rect; } static QMargins widgetFrame(QWidget *widget, const QMargins &defaultAssumption = QMargins(10, 25, 10, 10)) { if (!widget->isWindow()) { return QMargins(); } const auto widgetGeometry = widget->geometry(); const auto frameGeometry = widget->frameGeometry(); const auto frame = QMargins(widgetGeometry.left() - frameGeometry.left(), widgetGeometry.top() - frameGeometry.top(), frameGeometry.right() - widgetGeometry.right(), frameGeometry.bottom() - widgetGeometry.bottom()); return frame.isNull() ? defaultAssumption : frame; } static bool centerWidgetInternal(QWidget *widget, const QWidget *parent, const QPoint *position, bool avoidOverflow) { const auto availableGeometry = parent ? parent->geometry() : availableScreenGeometryAtPoint(position ? *position : QCursor::pos()); const auto alignedRect = QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, widget->size(), availableGeometry); if (!avoidOverflow) { widget->setGeometry(alignedRect); return false; } const auto limitedRect = limitRect(alignedRect, shrinkRectByMargins(availableGeometry, widgetFrame(widget))); widget->setGeometry(limitedRect); return alignedRect != limitedRect; } /// \endcond /*! * \brief Moves the specified \a widget to be centered within the (available) screen area or \a parent if specified. * \remarks * - The screen containing the current cursor position is used unless \a position is specified. */ void centerWidget(QWidget *widget, const QWidget *parent, const QPoint *position) { centerWidgetInternal(widget, parent, position, false); } /*! * \brief Moves the specified \a widget to be centered within the (available) screen area or \a parent if specified. * \returns Returns whether an overflow occurred. * \remarks * - If the widget overflows it is resized to take the whole available space in the dimension(s) that overflow. * If the widget is a window, its frame is attempted to be taken into account. If the window frame can not be determined * a generous assumption is made. It can nevertheless make sense to simply show \a widget using QWidget::showMaximized() * to make it simply fill the entire screen after all. * - The screen containing the current cursor position is used unless \a position is specified. */ bool centerWidgetAvoidingOverflow(QWidget *widget, const QWidget *parent, const QPoint *position) { return centerWidgetInternal(widget, parent, position, true); } /*! * \brief Moves the specified \a widget to the corner which is closest to the * current cursor position or \a position if specified. * * If there are multiple screens available, the screen where the cursor currently * is located is chosen. */ void cornerWidget(QWidget *widget, const QPoint *position) { const QPoint cursorPos(position ? *position : QCursor::pos()); const QRect availableGeometry(availableScreenGeometryAtPoint(cursorPos)); const Qt::Alignment alignment = (cursorPos.x() - availableGeometry.left() < availableGeometry.right() - cursorPos.x() ? Qt::AlignLeft : Qt::AlignRight) | (cursorPos.y() - availableGeometry.top() < availableGeometry.bottom() - cursorPos.y() ? Qt::AlignTop : Qt::AlignBottom); widget->setGeometry(QStyle::alignedRect(Qt::LeftToRight, alignment, widget->size(), availableGeometry)); } /*! * \brief Makes \a widget a heading. */ void makeHeading(QWidget *widget) { widget->setProperty("classNames", widget->property("classNames").toStringList() << QStringLiteral("heading")); } /*! * \brief Updates the widget style. * \remarks Useful when dynamic properties are used in the stylesheet because * the widget style does not update automatically when a property * changes. */ void updateStyle(QWidget *widget) { widget->style()->unpolish(widget); widget->style()->polish(widget); widget->update(); } #endif #endif } // namespace QtUtilities 07070100000029000081A4000000000000000000000001636534C9000006CF000000000000000000000000000000000000001F00000000qtutilities/misc/dialogutils.h#ifndef DIALOGS_DIALOGUTILS_H #define DIALOGS_DIALOGUTILS_H #include "../global.h" #include <QtGlobal> QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QWidget) QT_FORWARD_DECLARE_CLASS(QColor) QT_FORWARD_DECLARE_CLASS(QPoint) QT_FORWARD_DECLARE_CLASS(QRect) namespace QtUtilities { /*! * \brief The DocumentStatus enum specifies the status of the document in a * window. */ enum class DocumentStatus { NoDocument, /**< There is no document opened. The document path is ignored in this case. */ Saved, /**< There is a document opened. All modifications have been saved yet. */ Unsaved /**< There is a document opened and there are unsaved modifications. */ }; QT_UTILITIES_EXPORT QString generateWindowTitle(DocumentStatus documentStatus, const QString &documentPath); #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) #ifdef Q_OS_WIN32 QT_UTILITIES_EXPORT QColor windowFrameColor(); QT_UTILITIES_EXPORT QColor instructionTextColor(); #endif QT_UTILITIES_EXPORT const QString &dialogStyle(); #ifdef QT_UTILITIES_GUI_QTWIDGETS QT_UTILITIES_EXPORT QRect availableScreenGeometryAtPoint(const QPoint &point); QT_UTILITIES_EXPORT void centerWidget(QWidget *widget, const QWidget *parent = nullptr, const QPoint *position = nullptr); QT_UTILITIES_EXPORT bool centerWidgetAvoidingOverflow(QWidget *widget, const QWidget *parent = nullptr, const QPoint *position = nullptr); QT_UTILITIES_EXPORT void cornerWidget(QWidget *widget, const QPoint *position = nullptr); QT_UTILITIES_EXPORT void makeHeading(QWidget *widget); QT_UTILITIES_EXPORT void updateStyle(QWidget *widget); #endif #endif } // namespace QtUtilities #endif // DIALOGS_DIALOGUTILS_H 0707010000002A000081A4000000000000000000000001636534C900001785000000000000000000000000000000000000002700000000qtutilities/misc/recentmenumanager.cpp#include "./recentmenumanager.h" #include <QAction> #include <QCoreApplication> #include <QFile> #include <QMenu> #include <QMessageBox> #include <QPushButton> #include <QStringList> namespace QtUtilities { /*! * \class RecentMenuManager * \brief The RecentMenuManager class manages the entries for a "recently opened * files" menu. */ /*! * \brief Constructs a new recent menu manager. * \param menu Specifies the QMenu instance to operate with. * \param parent Specifies the parent QObject; might be nullptr. * \remarks * - Menu title and icon are set within the constructor. * - The current menu entries are cleared. * - The menu entries shouldn't be manipulated manually by the caller till the * manager is destructed. * - The manager does not take ownership over \a menu. */ RecentMenuManager::RecentMenuManager(QMenu *menu, QObject *parent) : QObject(parent) , m_menu(menu) { m_menu->clear(); m_menu->setTitle(tr("&Recent")); m_menu->setIcon(QIcon::fromTheme(QStringLiteral("document-open-recent"))); m_sep = m_menu->addSeparator(); m_clearAction = m_menu->addAction(QIcon::fromTheme(QStringLiteral("edit-clear")), tr("&Clear list"), this, &RecentMenuManager::clearEntries); } /*! * \brief Restores the specified entries. */ void RecentMenuManager::restore(const QStringList &savedEntries) { QAction *action = nullptr; for (const QString &path : savedEntries) { if (!path.isEmpty()) { action = new QAction(path, m_menu); action->setProperty("file_path", path); m_menu->insertAction(m_sep, action); connect(action, &QAction::triggered, this, &RecentMenuManager::handleActionTriggered); } } if (action) { m_menu->actions().front()->setShortcut(QKeySequence(Qt::Key_F6)); m_menu->setEnabled(true); } } /*! * \brief Saves the current entries. */ QStringList RecentMenuManager::save() { QStringList existingEntires; QList<QAction *> entryActions = m_menu->actions(); existingEntires.reserve(entryActions.size()); for (const QAction *action : entryActions) { QVariant path = action->property("file_path"); if (!path.isNull()) { existingEntires << path.toString(); } } return existingEntires; } /*! * \brief Ensures an entry for the specified \a path is present and the first * entry in the list. */ void RecentMenuManager::addEntry(const QString &path) { QList<QAction *> existingEntries = m_menu->actions(); QAction *entry = nullptr; // remove shortcut from existing entries for (QAction *existingEntry : existingEntries) { existingEntry->setShortcut(QKeySequence()); // check whether existing entry matches entry to add if (existingEntry->property("file_path").toString() == path) { entry = existingEntry; break; } } if (!entry) { // remove old entries to have never more than 10 entries for (auto i = existingEntries.size() - 1; i > 8; --i) { delete existingEntries[i]; } existingEntries = m_menu->actions(); // create new action entry = new QAction(path, this); entry->setProperty("file_path", path); connect(entry, &QAction::triggered, this, &RecentMenuManager::handleActionTriggered); } else { // remove existing action (will be inserted again as first action) m_menu->removeAction(entry); } // add shortcut for new entry entry->setShortcut(QKeySequence(Qt::Key_F6)); // ensure menu is enabled m_menu->setEnabled(true); // add action as first action in the recent menu m_menu->insertAction(m_menu->isEmpty() ? nullptr : m_menu->actions().front(), entry); } /*! * \brief Clears all entries. */ void RecentMenuManager::clearEntries() { QList<QAction *> entries = m_menu->actions(); for (auto i = entries.begin(), end = entries.end() - 2; i != end; ++i) { if (*i != m_clearAction) { delete *i; } } m_menu->setEnabled(false); } /*! * \brief Internally called to emit fileSelected() after an action has been * triggered. */ void RecentMenuManager::handleActionTriggered() { if (QAction *action = qobject_cast<QAction *>(sender())) { const QString path = action->property("file_path").toString(); if (!path.isEmpty()) { if (QFile::exists(path)) { emit fileSelected(path); } else { QMessageBox msg; msg.setWindowTitle(tr("Recently opened files - ") + QCoreApplication::applicationName()); msg.setText(tr("The selected file can't be found anymore. Do you want " "to delete the obsolete entry from the list?")); msg.setIcon(QMessageBox::Warning); QPushButton *keepEntryButton = msg.addButton(tr("keep entry"), QMessageBox::NoRole); QPushButton *deleteEntryButton = msg.addButton(tr("delete entry"), QMessageBox::YesRole); msg.setEscapeButton(keepEntryButton); msg.exec(); if (msg.clickedButton() == deleteEntryButton) { delete action; QList<QAction *> remainingActions = m_menu->actions(); if (!remainingActions.isEmpty() && remainingActions.front() != m_sep && remainingActions.front() != m_clearAction) { remainingActions.front()->setShortcut(QKeySequence(Qt::Key_F6)); m_menu->setEnabled(true); } else { m_menu->setEnabled(false); } } } } } } /*! * \fn RecentMenuManager::fileSelected() * \brief Emitted after the user selected a file. * \remarks Only emitted when the selected file still existed; otherwise the * user is ask whether to keep or delete the entry. */ } // namespace QtUtilities 0707010000002B000081A4000000000000000000000001636534C900000309000000000000000000000000000000000000002500000000qtutilities/misc/recentmenumanager.h#ifndef MISC_UTILS_RECENTMENUMANAGER_H #define MISC_UTILS_RECENTMENUMANAGER_H #include "../global.h" #include <QObject> QT_FORWARD_DECLARE_CLASS(QMenu) QT_FORWARD_DECLARE_CLASS(QAction) namespace QtUtilities { class QT_UTILITIES_EXPORT RecentMenuManager : public QObject { Q_OBJECT public: RecentMenuManager(QMenu *menu, QObject *parent = nullptr); public Q_SLOTS: void restore(const QStringList &savedEntries); QStringList save(); void addEntry(const QString &path); void clearEntries(); Q_SIGNALS: void fileSelected(const QString &path); private Q_SLOTS: void handleActionTriggered(); private: QMenu *m_menu; QAction *m_sep; QAction *m_clearAction; }; } // namespace QtUtilities #endif // MISC_UTILS_RECENTMENUMANAGER_H 0707010000002C000081A4000000000000000000000001636534C900000432000000000000000000000000000000000000001D00000000qtutilities/misc/trylocker.h#ifndef THREADING_UTILS_TRYLOCKER_H #define THREADING_UTILS_TRYLOCKER_H #include <QtGlobal> QT_FORWARD_DECLARE_CLASS(QMutex) namespace QtUtilities { /*! * \brief Like QMutexLocker, but it just tries to lock the mutex. */ template <typename Mutex = QMutex> class TryLocker { public: /*! * \brief Tries to lock the specified mutex. */ TryLocker(Mutex &mutex) : m_mutex(mutex.tryLock() ? &mutex : nullptr) { } /*! * \brief Unlocks the mutex specified when constructing. * \remarks Does nothing if the mutex couldn't be locked in the first place. */ ~TryLocker() { if (m_mutex) { m_mutex->unlock(); } } /*! * \brief Returns whether the mutex could be locked. */ bool isLocked() const { return m_mutex != nullptr; } /*! * \brief Returns whether the mutex could be locked. */ operator bool() const { return m_mutex != nullptr; } private: Mutex *m_mutex; }; } // namespace QtUtilities #endif // THREADING_UTILS_TRYLOCKER_H 0707010000002D000081A4000000000000000000000001636534C900000192000000000000000000000000000000000000002800000000qtutilities/misc/undefxmlparsermacros.h/*! * \file undefxmlparsermacros.h * \brief Undefines macros to utilize XML parsing using QXmlStreamReader. * \sa For an example, see dbquery.cpp of the tageditor project. */ #ifdef iftag #undef iftag #endif #ifdef eliftag #undef eliftag #endif #ifdef else_skip #undef else_skip #endif #ifdef children #undef children #endif #ifdef text #undef text #endif #ifdef attribute #undef attribute #endif 0707010000002E000081A4000000000000000000000001636534C90000066F000000000000000000000000000000000000002300000000qtutilities/misc/xmlparsermacros.h/*! * \file xmlparsermacros.h * \brief Macros to utilize XML parsing using QXmlStreamReader. * \sa For an example, see dbquery.cpp of the tageditor project. */ // ensure there are no conflicting macros defined #include "./undefxmlparsermacros.h" #define iftag(tagName) if (xmlReader.name() == QLatin1String(tagName)) #define eliftag(tagName) else if (xmlReader.name() == QLatin1String(tagName)) #define else_skip \ else \ { \ xmlReader.skipCurrentElement(); \ } #define children while (xmlReader.readNextStartElement()) #define text (xmlReader.readElementText(QXmlStreamReader::ErrorOnUnexpectedElement)) #define attribute(attributeName) (xmlReader.attributes().value(QLatin1String(attributeName))) #define attributeFlag(attributeName) \ (xmlReader.attributes().hasAttribute(QLatin1String(attributeName)) \ && xmlReader.attributes().value(QLatin1String(attributeName)) != QLatin1String("false")) 0707010000002F000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001300000000qtutilities/models07070100000030000081A4000000000000000000000001636534C9000021A8000000000000000000000000000000000000002600000000qtutilities/models/checklistmodel.cpp#include "./checklistmodel.h" #include <QSettings> /*! \namespace Models \brief Provides common models. */ namespace QtUtilities { /*! * \class Models::ChecklistItem * \brief The ChecklistItem class provides an item for use with the * ChecklistModel class. */ /*! * \class Models::ChecklistModel * \brief The ChecklistModel class provides a generic model for storing * checkable items. */ /*! * \brief Constructs a new checklist model. */ ChecklistModel::ChecklistModel(QObject *parent) : QAbstractListModel(parent) { } int ChecklistModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { return static_cast<int>(m_items.size()); } return 0; } Qt::ItemFlags ChecklistModel::flags(const QModelIndex &index) const { if (!index.isValid() || index.row() >= m_items.count() || index.model() != this) { return Qt::ItemIsDropEnabled; // allows drops outside the items } return QAbstractListModel::flags(index) | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled; } QVariant ChecklistModel::data(const QModelIndex &index, int role) const { if (index.isValid() && index.row() < m_items.size()) { switch (role) { case Qt::DisplayRole: return m_items.at(index.row()).label(); case Qt::CheckStateRole: return m_items.at(index.row()).checkState(); case idRole(): return m_items.at(index.row()).id(); default:; } } return QVariant(); } QMap<int, QVariant> ChecklistModel::itemData(const QModelIndex &index) const { QMap<int, QVariant> roles; roles.insert(Qt::DisplayRole, data(index, Qt::DisplayRole)); roles.insert(Qt::CheckStateRole, data(index, Qt::CheckStateRole)); roles.insert(idRole(), data(index, idRole())); return roles; } bool ChecklistModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool success = false; QVector<int> roles{ role }; if (index.isValid() && index.row() < m_items.size()) { switch (role) { case Qt::DisplayRole: m_items[index.row()].m_label = value.toString(); success = true; break; case Qt::CheckStateRole: if (value.canConvert( #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QMetaType::Int #else QMetaType::fromType<int>() #endif )) { m_items[index.row()].m_checkState = static_cast<Qt::CheckState>(value.toInt()); success = true; } break; case idRole(): { m_items[index.row()].m_id = value; success = true; auto label = labelForId(value); if (!label.isEmpty()) { m_items[index.row()].m_label = std::move(label); roles << Qt::DisplayRole; } break; } default:; } } if (success) { emit dataChanged(index, index, roles); } return success; } bool ChecklistModel::setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) { for (QMap<int, QVariant>::ConstIterator it = roles.constBegin(); it != roles.constEnd(); ++it) { setData(index, it.value(), it.key()); } return true; } /*! * \brief Sets the checked state of the specified item. */ bool ChecklistModel::setChecked(int row, Qt::CheckState checked) { if (row < 0 || row >= m_items.size()) { return false; } m_items[row].m_checkState = checked ? Qt::Checked : Qt::Unchecked; const auto index(this->index(row)); emit dataChanged(index, index, QVector<int>{ Qt::CheckStateRole }); return true; } /*! * \brief Returns the label for the specified \a id. * * This method might be reimplemented when subclassing to provide labels * for the item IDs. * * If an item's ID is set (using setData() with idRole() or setItems()) this method * is called to update or initialize the item's label as well. If this method returns * an empty string (default behaviour) the item's label will not be updated. * * This is useful when items are moved by the view (eg. for Drag & Drop) and to * initialize the ChecklistItem labels more conveniently. */ QString ChecklistModel::labelForId(const QVariant &) const { return QString(); } Qt::DropActions ChecklistModel::supportedDropActions() const { return Qt::MoveAction; } bool ChecklistModel::insertRows(int row, int count, const QModelIndex &parent) { if (count < 1 || row < 0 || row > rowCount() || parent.isValid()) { return false; } beginInsertRows(QModelIndex(), row, row + count - 1); for (int index = row, end = row + count; index < end; ++index) { m_items.insert(index, ChecklistItem()); } endInsertRows(); return true; } bool ChecklistModel::removeRows(int row, int count, const QModelIndex &parent) { if (count < 1 || row < 0 || (row + count) > rowCount() || parent.isValid()) { return false; } beginRemoveRows(QModelIndex(), row, row + count - 1); for (int index = row, end = row + count; index < end; ++index) { m_items.removeAt(index); } endRemoveRows(); return true; } /*! * \brief Sets the items. Resets the model. */ void ChecklistModel::setItems(const QList<ChecklistItem> &items) { beginResetModel(); m_items = items; for (auto &item : m_items) { if (item.m_label.isEmpty()) { item.m_label = labelForId(item.id()); } } endResetModel(); } /*! * \brief Restores the IDs and checkstates read from the specified \a settings * object. * * The items will be read from the array with the specified \a name. * * Resets the model (current items are cleared). * * Does not restore any labels. Labels are meant to be restored from the ID. */ void ChecklistModel::restore(QSettings &settings, const QString &name) { beginResetModel(); auto currentItems = m_items; QList<QVariant> restoredIds; m_items.clear(); int rows = settings.beginReadArray(name); m_items.reserve(rows); for (int i = 0; i < rows; ++i) { settings.setArrayIndex(i); const auto id = settings.value(QStringLiteral("id")); const auto isIdValid = [&] { for (const auto &item : currentItems) { if (item.id() == id) { return true; } } return false; }(); if (!isIdValid) { continue; } const auto selected = settings.value(QStringLiteral("selected")); if (!id.isNull() && !selected.isNull() && selected.canConvert( #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QMetaType::Bool #else QMetaType::fromType<bool>() #endif ) && !restoredIds.contains(id)) { m_items << ChecklistItem(id, labelForId(id), selected.toBool() ? Qt::Checked : Qt::Unchecked); restoredIds << id; } } settings.endArray(); for (const ChecklistItem &item : currentItems) { if (!restoredIds.contains(item.id())) { m_items << item; } } endResetModel(); } /*! * \brief Saves the IDs and checkstates to the specified \a settings object. * * The items will be stored using an array with the specified \a name. * * Does not save any labels. */ void ChecklistModel::save(QSettings &settings, const QString &name) const { settings.beginWriteArray(name, static_cast<int>(m_items.size())); int index = 0; for (const ChecklistItem &item : m_items) { settings.setArrayIndex(index); settings.setValue(QStringLiteral("id"), item.id()); settings.setValue(QStringLiteral("selected"), item.isChecked()); ++index; } settings.endArray(); } /*! * \brief Returns the checked IDs. */ QVariantList ChecklistModel::toVariantList() const { QVariantList checkedIds; checkedIds.reserve(m_items.size()); for (const auto &item : m_items) { if (item.isChecked()) { checkedIds << item.id(); } } return checkedIds; } /*! * \brief Checks all items contained by \a checkedIds and unchecks other items. */ void ChecklistModel::applyVariantList(const QVariantList &checkedIds) { for (auto &item : m_items) { item.m_checkState = checkedIds.contains(item.id()) ? Qt::Checked : Qt::Unchecked; } emit dataChanged(index(0), index(static_cast<int>(m_items.size())), { Qt::CheckStateRole }); } } // namespace QtUtilities 07070100000031000081A4000000000000000000000001636534C900000CD5000000000000000000000000000000000000002400000000qtutilities/models/checklistmodel.h#ifndef MODELS_CHECKLISTMODEL_H #define MODELS_CHECKLISTMODEL_H #include "../global.h" #include <QAbstractListModel> #include <QList> QT_FORWARD_DECLARE_CLASS(QSettings) namespace QtUtilities { class ChecklistModel; class QT_UTILITIES_EXPORT ChecklistItem { friend class ChecklistModel; public: ChecklistItem(const QVariant &id = QVariant(), const QString &label = QString(), Qt::CheckState checked = Qt::Unchecked); const QVariant &id() const; const QString &label() const; Qt::CheckState checkState() const; bool isChecked() const; private: QVariant m_id; QString m_label; Qt::CheckState m_checkState; }; inline ChecklistItem::ChecklistItem(const QVariant &id, const QString &label, Qt::CheckState checkState) : m_id(id) , m_label(label) , m_checkState(checkState) { } /*! * \brief Returns the ID of the item. */ inline const QVariant &ChecklistItem::id() const { return m_id; } /*! * \brief Returns the label. */ inline const QString &ChecklistItem::label() const { return m_label; } /*! * \brief Returns the check state. */ inline Qt::CheckState ChecklistItem::checkState() const { return m_checkState; } /*! * \brief Returns whether the item is checked. */ inline bool ChecklistItem::isChecked() const { return m_checkState == Qt::Checked; } class QT_UTILITIES_EXPORT ChecklistModel : public QAbstractListModel { Q_OBJECT public: explicit ChecklistModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QMap<int, QVariant> itemData(const QModelIndex &index) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override; bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &roles) override; bool setChecked(int row, bool checked); bool setChecked(int row, Qt::CheckState checked); virtual QString labelForId(const QVariant &id) const; Qt::DropActions supportedDropActions() const override; bool insertRows(int row, int count, const QModelIndex &parent) override; bool removeRows(int row, int count, const QModelIndex &parent) override; const QList<ChecklistItem> &items() const; void setItems(const QList<ChecklistItem> &items); void restore(QSettings &settings, const QString &name); void save(QSettings &settings, const QString &name) const; QVariantList toVariantList() const; void applyVariantList(const QVariantList &checkedIds); static constexpr int idRole(); private: QList<ChecklistItem> m_items; }; /*! * \brief Returns the items. */ inline const QList<ChecklistItem> &ChecklistModel::items() const { return m_items; } /*! * \brief Sets the checked state of the specified item. */ inline bool ChecklistModel::setChecked(int row, bool checked) { return setChecked(row, checked ? Qt::Checked : Qt::Unchecked); } /*! * \brief Returns the role used to get or set the item ID. */ constexpr int ChecklistModel::idRole() { return Qt::UserRole + 1; } } // namespace QtUtilities #endif // MODELS_CHECKLISTMODEL_H 07070100000032000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001A00000000qtutilities/paletteeditor07070100000033000081A4000000000000000000000001636534C900001574000000000000000000000000000000000000002A00000000qtutilities/paletteeditor/colorbutton.cpp#include "./colorbutton.h" #include <QApplication> #include <QColorDialog> #include <QDrag> #include <QDragEnterEvent> #include <QMimeData> #include <QPainter> namespace QtUtilities { /*! * \cond */ class ColorButtonPrivate { ColorButton *q_ptr; Q_DECLARE_PUBLIC(ColorButton) public: QColor m_color; #ifndef QT_NO_DRAGANDDROP QColor m_dragColor; QPoint m_dragStart; bool m_dragging; #endif bool m_backgroundCheckered; void slotEditColor(); QColor shownColor() const; QPixmap generatePixmap() const; }; void ColorButtonPrivate::slotEditColor() { const QColor newColor = QColorDialog::getColor(m_color, q_ptr, QString(), QColorDialog::ShowAlphaChannel); if (!newColor.isValid() || newColor == q_ptr->color()) return; q_ptr->setColor(newColor); } QColor ColorButtonPrivate::shownColor() const { #ifndef QT_NO_DRAGANDDROP if (m_dragging) return m_dragColor; #endif return m_color; } QPixmap ColorButtonPrivate::generatePixmap() const { QPixmap pix(24, 24); int pixSize = 20; QBrush br(shownColor()); QPixmap pm(2 * pixSize, 2 * pixSize); QPainter pmp(&pm); pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray); pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray); pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray); pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray); pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, shownColor()); br = QBrush(pm); QPainter p(&pix); int corr = 1; QRect r = pix.rect().adjusted(corr, corr, -corr, -corr); p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); p.fillRect(r, br); p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr, r.width() / 2, r.height() / 2, QColor(shownColor().rgb())); p.drawRect(pix.rect().adjusted(0, 0, -1, -1)); return pix; } /*! * \endcond */ ColorButton::ColorButton(QWidget *parent) : QToolButton(parent) , d_ptr(new ColorButtonPrivate) { d_ptr->q_ptr = this; d_ptr->m_dragging = false; d_ptr->m_backgroundCheckered = true; setAcceptDrops(true); connect(this, SIGNAL(clicked()), this, SLOT(slotEditColor())); setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred)); } ColorButton::~ColorButton() { } void ColorButton::setColor(const QColor &color) { if (d_ptr->m_color == color) return; update(); emit colorChanged(d_ptr->m_color = color); } QColor ColorButton::color() const { return d_ptr->m_color; } void ColorButton::setBackgroundCheckered(bool checkered) { if (d_ptr->m_backgroundCheckered == checkered) return; d_ptr->m_backgroundCheckered = checkered; update(); } bool ColorButton::isBackgroundCheckered() const { return d_ptr->m_backgroundCheckered; } void ColorButton::paintEvent(QPaintEvent *event) { QToolButton::paintEvent(event); if (!isEnabled()) return; const int pixSize = 10; QBrush br(d_ptr->shownColor()); if (d_ptr->m_backgroundCheckered) { QPixmap pm(2 * pixSize, 2 * pixSize); QPainter pmp(&pm); pmp.fillRect(0, 0, pixSize, pixSize, Qt::white); pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::white); pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::black); pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::black); pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, d_ptr->shownColor()); br = QBrush(pm); } QPainter p(this); const int corr = 4; QRect r = rect().adjusted(corr, corr, -corr, -corr); p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr); p.fillRect(r, br); const QColor frameColor1(0, 0, 0, 26); p.setPen(frameColor1); p.drawRect(r.adjusted(1, 1, -2, -2)); const QColor frameColor2(0, 0, 0, 51); p.setPen(frameColor2); p.drawRect(r.adjusted(0, 0, -1, -1)); } void ColorButton::mousePressEvent(QMouseEvent *event) { #ifndef QT_NO_DRAGANDDROP if (event->button() == Qt::LeftButton) d_ptr->m_dragStart = event->pos(); #endif QToolButton::mousePressEvent(event); } void ColorButton::mouseMoveEvent(QMouseEvent *event) { #ifndef QT_NO_DRAGANDDROP if (event->buttons() & Qt::LeftButton && (d_ptr->m_dragStart - event->pos()).manhattanLength() > QApplication::startDragDistance()) { auto *const mime = new QMimeData; mime->setColorData(color()); auto *const drg = new QDrag(this); drg->setMimeData(mime); drg->setPixmap(d_ptr->generatePixmap()); setDown(false); event->accept(); drg->exec(Qt::CopyAction); return; } #endif QToolButton::mouseMoveEvent(event); } #ifndef QT_NO_DRAGANDDROP void ColorButton::dragEnterEvent(QDragEnterEvent *event) { const QMimeData *mime = event->mimeData(); if (!mime->hasColor()) return; event->accept(); d_ptr->m_dragColor = qvariant_cast<QColor>(mime->colorData()); d_ptr->m_dragging = true; update(); } void ColorButton::dragLeaveEvent(QDragLeaveEvent *event) { event->accept(); d_ptr->m_dragging = false; update(); } void ColorButton::dropEvent(QDropEvent *event) { event->accept(); d_ptr->m_dragging = false; if (d_ptr->m_dragColor == color()) return; setColor(d_ptr->m_dragColor); } #endif } // namespace QtUtilities #include "moc_colorbutton.cpp" 07070100000034000081A4000000000000000000000001636534C9000005A6000000000000000000000000000000000000002800000000qtutilities/paletteeditor/colorbutton.h#ifndef WIDGETS_COLORBUTTON_H #define WIDGETS_COLORBUTTON_H #include "../global.h" #include <QToolButton> namespace QtUtilities { /*! * \brief The ColorButton class is used by PaletteEditor. * * This is taken from qttools/src/shared/qtgradienteditor/qtcolorbutton.h. */ class QT_UTILITIES_EXPORT ColorButton : public QToolButton { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(bool backgroundCheckered READ isBackgroundCheckered WRITE setBackgroundCheckered) public: ColorButton(QWidget *parent = nullptr); ~ColorButton() override; bool isBackgroundCheckered() const; void setBackgroundCheckered(bool checkered); QColor color() const; public Q_SLOTS: void setColor(const QColor &color); Q_SIGNALS: void colorChanged(const QColor &color); protected: void paintEvent(QPaintEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent *event) override; #ifndef QT_NO_DRAGANDDROP void dragEnterEvent(QDragEnterEvent *event) override; void dragLeaveEvent(QDragLeaveEvent *event) override; void dropEvent(QDropEvent *event) override; #endif private: QScopedPointer<class ColorButtonPrivate> d_ptr; Q_DECLARE_PRIVATE(ColorButton) Q_DISABLE_COPY(ColorButton) Q_PRIVATE_SLOT(d_func(), void slotEditColor()) }; } // namespace QtUtilities #endif // WIDGETS_COLORBUTTON_H 07070100000035000081A4000000000000000000000001636534C9000052BC000000000000000000000000000000000000002C00000000qtutilities/paletteeditor/paletteeditor.cpp#include "./paletteeditor.h" #include "./colorbutton.h" #include "ui_paletteeditor.h" #include <QFileDialog> #include <QHeaderView> #include <QLabel> #include <QMessageBox> #include <QMetaProperty> #include <QPainter> #include <QPushButton> #include <QSettings> #include <QStyle> #include <QToolButton> namespace QtUtilities { enum { BrushRole = 33 }; PaletteEditor::PaletteEditor(QWidget *parent) : QDialog(parent) , m_ui(new Ui::PaletteEditor) , m_currentColorGroup(QPalette::Active) , m_paletteModel(new PaletteModel(this)) , m_modelUpdated(false) , m_paletteUpdated(false) , m_compute(true) { m_ui->setupUi(this); m_ui->paletteView->setModel(m_paletteModel); updatePreviewPalette(); updateStyledButton(); m_ui->paletteView->setModel(m_paletteModel); auto *const delegate = new ColorDelegate(this); m_ui->paletteView->setItemDelegate(delegate); m_ui->paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers); m_ui->paletteView->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui->paletteView->setDragEnabled(true); m_ui->paletteView->setDropIndicatorShown(true); m_ui->paletteView->setRootIsDecorated(false); m_ui->paletteView->setColumnHidden(2, true); m_ui->paletteView->setColumnHidden(3, true); auto saveButton = m_ui->buttonBox->addButton(tr("Save…"), QDialogButtonBox::ActionRole); connect(saveButton, &QPushButton::clicked, this, &PaletteEditor::save); auto loadButton = m_ui->buttonBox->addButton(tr("Load…"), QDialogButtonBox::ActionRole); connect(loadButton, &QPushButton::clicked, this, &PaletteEditor::load); connect(m_paletteModel, &PaletteModel::paletteChanged, this, &PaletteEditor::paletteChanged); connect(m_ui->buildButton, &ColorButton::colorChanged, this, &PaletteEditor::buildPalette); connect(m_ui->computeRadio, &QRadioButton::clicked, this, &PaletteEditor::handleComputeRadioClicked); connect(m_ui->detailsRadio, &QRadioButton::clicked, this, &PaletteEditor::handleDetailsRadioClicked); } PaletteEditor::~PaletteEditor() { } QPalette PaletteEditor::palette() const { return m_editPalette; } void PaletteEditor::setPalette(const QPalette &palette) { m_editPalette = palette; const auto mask = palette. #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) resolveMask() #else resolve() #endif ; for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) { if (mask & (1 << i)) { continue; } m_editPalette.setBrush( QPalette::Active, static_cast<QPalette::ColorRole>(i), m_parentPalette.brush(QPalette::Active, static_cast<QPalette::ColorRole>(i))); m_editPalette.setBrush( QPalette::Inactive, static_cast<QPalette::ColorRole>(i), m_parentPalette.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i))); m_editPalette.setBrush( QPalette::Disabled, static_cast<QPalette::ColorRole>(i), m_parentPalette.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i))); } m_editPalette. #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) setResolveMask(mask); m_editPalette = m_editPalette.resolve(m_editPalette) #else resolve(mask) #endif ; updatePreviewPalette(); updateStyledButton(); m_paletteUpdated = true; if (!m_modelUpdated) { m_paletteModel->setPalette(m_editPalette, m_parentPalette); } m_paletteUpdated = false; } void PaletteEditor::setPalette(const QPalette &palette, const QPalette &parentPalette) { m_parentPalette = parentPalette; setPalette(palette); } void PaletteEditor::handleComputeRadioClicked() { if (m_compute) { return; } m_ui->paletteView->setColumnHidden(2, true); m_ui->paletteView->setColumnHidden(3, true); m_compute = true; m_paletteModel->setCompute(true); } void PaletteEditor::handleDetailsRadioClicked() { if (!m_compute) { return; } const int w = m_ui->paletteView->columnWidth(1); m_ui->paletteView->setColumnHidden(2, false); m_ui->paletteView->setColumnHidden(3, false); auto *const header = m_ui->paletteView->header(); header->resizeSection(1, w / 3); header->resizeSection(2, w / 3); header->resizeSection(3, w / 3); m_compute = false; m_paletteModel->setCompute(false); } static inline QString paletteSuffix() { return QStringLiteral("ini"); } static inline QString paletteFilter() { return PaletteEditor::tr("Color palette configuration (*.ini)"); } static bool loadPalette(const QString &fileName, QPalette *pal, QString *errorMessage) { const auto settings = QSettings(fileName, QSettings::IniFormat); if (settings.status() != QSettings::NoError) { *errorMessage = PaletteEditor::tr("Unable to load \"%1\".").arg(fileName); return false; } const auto value = settings.value(QStringLiteral("palette")); if (!value.isValid() || !value.canConvert<QPalette>()) { *errorMessage = PaletteEditor::tr("\"%1\" does not contain a valid palette.").arg(fileName); return false; } *pal = settings.value(QStringLiteral("palette")).value<QPalette>(); return true; } static bool savePalette(const QString &fileName, const QPalette &pal, QString *errorMessage) { auto settings = QSettings(fileName, QSettings::IniFormat); settings.setValue(QStringLiteral("palette"), QVariant(pal)); settings.sync(); if (settings.status() != QSettings::NoError) { *errorMessage = PaletteEditor::tr("Unable to write \"%1\".").arg(fileName); return false; } return true; } void PaletteEditor::load() { auto dialog = QFileDialog(this, tr("Load palette"), QString(), paletteFilter()); dialog.setAcceptMode(QFileDialog::AcceptOpen); if (dialog.exec() != QDialog::Accepted) { return; } auto pal = QPalette(); auto errorMessage = QString(); if (loadPalette(dialog.selectedFiles().constFirst(), &pal, &errorMessage)) { setPalette(pal); } else { QMessageBox::warning(this, tr("Error reading palette"), errorMessage); } } void PaletteEditor::save() { auto dialog = QFileDialog(this, tr("Save palette"), QString(), paletteFilter()); dialog.setAcceptMode(QFileDialog::AcceptSave); dialog.setDefaultSuffix(paletteSuffix()); if (dialog.exec() != QDialog::Accepted) { return; } auto errorMessage = QString(); if (!savePalette(dialog.selectedFiles().constFirst(), palette(), &errorMessage)) { QMessageBox::warning(this, tr("Error writing palette"), errorMessage); } } void PaletteEditor::paletteChanged(const QPalette &palette) { m_modelUpdated = true; if (!m_paletteUpdated) { setPalette(palette); } m_modelUpdated = false; } void PaletteEditor::buildPalette() { const QColor btn(m_ui->buildButton->color()); const QPalette temp(btn); setPalette(temp); } void PaletteEditor::updatePreviewPalette() { const QPalette::ColorGroup g = currentColorGroup(); // build the preview palette const QPalette currentPalette = palette(); QPalette previewPalette; for (int i = QPalette::WindowText; i < QPalette::NColorRoles; ++i) { const QPalette::ColorRole r = static_cast<QPalette::ColorRole>(i); const QBrush br = currentPalette.brush(g, r); previewPalette.setBrush(QPalette::Active, r, br); previewPalette.setBrush(QPalette::Inactive, r, br); previewPalette.setBrush(QPalette::Disabled, r, br); } } void PaletteEditor::updateStyledButton() { m_ui->buildButton->setColor(palette().color(QPalette::Active, QPalette::Button)); } QPalette PaletteEditor::getPalette(QWidget *parent, const QPalette &init, const QPalette &parentPal, int *ok) { PaletteEditor dlg(parent); auto parentPalette(parentPal); const auto mask = init. #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) resolveMask() #else resolve() #endif ; for (int i = 0; i < static_cast<int>(QPalette::NColorRoles); ++i) { if (mask & (1 << i)) { continue; } parentPalette.setBrush( QPalette::Active, static_cast<QPalette::ColorRole>(i), init.brush(QPalette::Active, static_cast<QPalette::ColorRole>(i))); parentPalette.setBrush( QPalette::Inactive, static_cast<QPalette::ColorRole>(i), init.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(i))); parentPalette.setBrush( QPalette::Disabled, static_cast<QPalette::ColorRole>(i), init.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(i))); } dlg.setPalette(init, parentPalette); const int result = dlg.exec(); if (ok) { *ok = result; } return result == QDialog::Accepted ? dlg.palette() : init; } PaletteModel::PaletteModel(QObject *parent) : QAbstractTableModel(parent) , m_compute(true) { const QMetaObject *meta = metaObject(); const QMetaProperty property = meta->property(meta->indexOfProperty("colorRole")); const QMetaEnum enumerator = property.enumerator(); for (int r = QPalette::WindowText; r < QPalette::NColorRoles; ++r) { m_roleNames[static_cast<QPalette::ColorRole>(r)] = QLatin1String(enumerator.key(r)); } } int PaletteModel::rowCount(const QModelIndex &) const { return static_cast<int>(m_roleNames.count()); } int PaletteModel::columnCount(const QModelIndex &) const { return 4; } QVariant PaletteModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() < 0 || index.row() >= QPalette::NColorRoles || index.column() < 0 || index.column() >= 4) { return QVariant(); } if (index.column() == 0) { if (role == Qt::DisplayRole) { return m_roleNames[static_cast<QPalette::ColorRole>(index.row())]; } if (role == Qt::EditRole) { const auto mask = m_palette. #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) resolveMask() #else resolve() #endif ; if (mask & (1 << index.row())) return true; return false; } return QVariant(); } if (role == BrushRole) { return m_palette.brush(columnToGroup(index.column()), static_cast<QPalette::ColorRole>(index.row())); } return QVariant(); } bool PaletteModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) { return false; } if (index.column() != 0 && role == BrushRole) { const QBrush br = qvariant_cast<QBrush>(value); const QPalette::ColorRole r = static_cast<QPalette::ColorRole>(index.row()); const QPalette::ColorGroup g = columnToGroup(index.column()); m_palette.setBrush(g, r, br); QModelIndex idxBegin = PaletteModel::index(r, 0); QModelIndex idxEnd = PaletteModel::index(r, 3); if (m_compute) { m_palette.setBrush(QPalette::Inactive, r, br); switch (r) { case QPalette::WindowText: case QPalette::Text: case QPalette::ButtonText: case QPalette::Base: break; case QPalette::Dark: m_palette.setBrush(QPalette::Disabled, QPalette::WindowText, br); m_palette.setBrush(QPalette::Disabled, QPalette::Dark, br); m_palette.setBrush(QPalette::Disabled, QPalette::Text, br); m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, br); idxBegin = PaletteModel::index(0, 0); idxEnd = PaletteModel::index(static_cast<int>(m_roleNames.count()) - 1, 3); break; case QPalette::Window: m_palette.setBrush(QPalette::Disabled, QPalette::Base, br); m_palette.setBrush(QPalette::Disabled, QPalette::Window, br); idxBegin = PaletteModel::index(QPalette::Base, 0); break; case QPalette::Highlight: break; default: m_palette.setBrush(QPalette::Disabled, r, br); break; } } emit paletteChanged(m_palette); emit dataChanged(idxBegin, idxEnd); return true; } if (index.column() == 0 && role == Qt::EditRole) { auto mask = m_palette. #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) resolveMask() #else resolve() #endif ; const bool isMask = qvariant_cast<bool>(value); const int r = index.row(); if (isMask) { mask |= (1 << r); } else { m_palette.setBrush( QPalette::Active, static_cast<QPalette::ColorRole>(r), m_parentPalette.brush(QPalette::Active, static_cast<QPalette::ColorRole>(r))); m_palette.setBrush(QPalette::Inactive, static_cast<QPalette::ColorRole>(r), m_parentPalette.brush(QPalette::Inactive, static_cast<QPalette::ColorRole>(r))); m_palette.setBrush(QPalette::Disabled, static_cast<QPalette::ColorRole>(r), m_parentPalette.brush(QPalette::Disabled, static_cast<QPalette::ColorRole>(r))); mask &= ~static_cast<decltype(mask)>(1 << index.row()); } m_palette. #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) setResolveMask(mask); m_palette = m_palette.resolve(m_palette) #else resolve(mask) #endif ; emit paletteChanged(m_palette); const QModelIndex idxEnd = PaletteModel::index(r, 3); emit dataChanged(index, idxEnd); return true; } return false; } Qt::ItemFlags PaletteModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::ItemIsEnabled; return Qt::ItemIsEditable | Qt::ItemIsEnabled; } QVariant PaletteModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Color Role"); if (section == groupToColumn(QPalette::Active)) return tr("Active"); if (section == groupToColumn(QPalette::Inactive)) return tr("Inactive"); if (section == groupToColumn(QPalette::Disabled)) return tr("Disabled"); } return QVariant(); } QPalette PaletteModel::getPalette() const { return m_palette; } void PaletteModel::setPalette(const QPalette &palette, const QPalette &parentPalette) { m_parentPalette = parentPalette; m_palette = palette; const QModelIndex idxBegin = index(0, 0); const QModelIndex idxEnd = index(static_cast<int>(m_roleNames.count()) - 1, 3); emit dataChanged(idxBegin, idxEnd); } QPalette::ColorGroup PaletteModel::columnToGroup(int index) const { if (index == 1) return QPalette::Active; if (index == 2) return QPalette::Inactive; return QPalette::Disabled; } int PaletteModel::groupToColumn(QPalette::ColorGroup group) const { if (group == QPalette::Active) return 1; if (group == QPalette::Inactive) return 2; return 3; } BrushEditor::BrushEditor(QWidget *parent) : QWidget(parent) , m_button(new ColorButton(this)) , m_changed(false) { auto *const layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_button); connect(m_button, &ColorButton::colorChanged, this, &BrushEditor::brushChanged); setFocusProxy(m_button); } void BrushEditor::setBrush(const QBrush &brush) { m_button->setColor(brush.color()); m_changed = false; } QBrush BrushEditor::brush() const { return QBrush(m_button->color()); } void BrushEditor::brushChanged() { m_changed = true; emit changed(this); } bool BrushEditor::changed() const { return m_changed; } RoleEditor::RoleEditor(QWidget *parent) : QWidget(parent) , m_label(new QLabel(this)) , m_edited(false) { QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(m_label); m_label->setAutoFillBackground(true); m_label->setIndent(3); // same value as textMargin in QItemDelegate setFocusProxy(m_label); auto *const button = new QToolButton(this); button->setToolButtonStyle(Qt::ToolButtonIconOnly); button->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear"))); button->setIconSize(QSize(8, 8)); button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); layout->addWidget(button); connect(button, &QAbstractButton::clicked, this, &RoleEditor::emitResetProperty); } void RoleEditor::setLabel(const QString &label) { m_label->setText(label); } void RoleEditor::setEdited(bool on) { QFont font; if (on == true) { font.setBold(on); } m_label->setFont(font); m_edited = on; } bool RoleEditor::edited() const { return m_edited; } void RoleEditor::emitResetProperty() { setEdited(false); emit changed(this); } ColorDelegate::ColorDelegate(QObject *parent) : QItemDelegate(parent) { } QWidget *ColorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &index) const { if (index.column() == 0) { auto *const editor = new RoleEditor(parent); connect(editor, &RoleEditor::changed, this, &ColorDelegate::commitData); return editor; } using BrushEditorWidgetSignal = void (BrushEditor::*)(QWidget *); auto *const editor = new BrushEditor(parent); connect(editor, static_cast<BrushEditorWidgetSignal>(&BrushEditor::changed), this, &ColorDelegate::commitData); editor->setFocusPolicy(Qt::NoFocus); editor->installEventFilter(const_cast<ColorDelegate *>(this)); return editor; } void ColorDelegate::setEditorData(QWidget *ed, const QModelIndex &index) const { if (index.column() == 0) { const auto mask = qvariant_cast<bool>(index.model()->data(index, Qt::EditRole)); auto *const editor = static_cast<RoleEditor *>(ed); editor->setEdited(mask); const auto colorName = qvariant_cast<QString>(index.model()->data(index, Qt::DisplayRole)); editor->setLabel(colorName); } else { const auto br = qvariant_cast<QBrush>(index.model()->data(index, BrushRole)); auto *const editor = static_cast<BrushEditor *>(ed); editor->setBrush(br); } } void ColorDelegate::setModelData(QWidget *ed, QAbstractItemModel *model, const QModelIndex &index) const { if (index.column() == 0) { const auto *const editor = static_cast<RoleEditor *>(ed); const auto mask = editor->edited(); model->setData(index, mask, Qt::EditRole); } else { const auto *const editor = static_cast<BrushEditor *>(ed); if (editor->changed()) { QBrush br = editor->brush(); model->setData(index, br, BrushRole); } } } void ColorDelegate::updateEditorGeometry(QWidget *ed, const QStyleOptionViewItem &option, const QModelIndex &index) const { QItemDelegate::updateEditorGeometry(ed, option, index); ed->setGeometry(ed->geometry().adjusted(0, 0, -1, -1)); } void ColorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const { QStyleOptionViewItem option = opt; const auto mask = qvariant_cast<bool>(index.model()->data(index, Qt::EditRole)); if (index.column() == 0 && mask) { option.font.setBold(true); } auto br = qvariant_cast<QBrush>(index.model()->data(index, BrushRole)); if (br.style() == Qt::LinearGradientPattern || br.style() == Qt::RadialGradientPattern || br.style() == Qt::ConicalGradientPattern) { painter->save(); painter->translate(option.rect.x(), option.rect.y()); painter->scale(option.rect.width(), option.rect.height()); QGradient gr = *(br.gradient()); gr.setCoordinateMode(QGradient::LogicalMode); br = QBrush(gr); painter->fillRect(0, 0, 1, 1, br); painter->restore(); } else { painter->save(); painter->setBrushOrigin(option.rect.x(), option.rect.y()); painter->fillRect(option.rect, br); painter->restore(); } QItemDelegate::paint(painter, option, index); const QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &option)); const QPen oldPen = painter->pen(); painter->setPen(QPen(color)); painter->drawLine(option.rect.right(), option.rect.y(), option.rect.right(), option.rect.bottom()); painter->drawLine(option.rect.x(), option.rect.bottom(), option.rect.right(), option.rect.bottom()); painter->setPen(oldPen); } QSize ColorDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const { return QItemDelegate::sizeHint(opt, index) + QSize(4, 4); } } // namespace QtUtilities 07070100000036000081A4000000000000000000000001636534C9000012F5000000000000000000000000000000000000002A00000000qtutilities/paletteeditor/paletteeditor.h#ifndef WIDGETS_PALETTEEDITOR_H #define WIDGETS_PALETTEEDITOR_H #include "../global.h" #include <QDialog> #include <QItemDelegate> #include <memory> QT_FORWARD_DECLARE_CLASS(QListView) QT_FORWARD_DECLARE_CLASS(QLabel) namespace QtUtilities { class ColorButton; namespace Ui { class PaletteEditor; } /*! * \brief The PaletteEditor class provides a dialog to customize a QPalette. * * This is taken from * qttools/src/designer/src/components/propertyeditor/paletteeditor.cpp. * In contrast to the original version this version doesn't provide a preview. */ class QT_UTILITIES_EXPORT PaletteEditor : public QDialog { Q_OBJECT Q_PROPERTY(QPalette palette READ palette WRITE setPalette) public: PaletteEditor(QWidget *parent); ~PaletteEditor() override; static QPalette getPalette(QWidget *parent, const QPalette &init = QPalette(), const QPalette &parentPal = QPalette(), int *result = nullptr); QPalette palette() const; void setPalette(const QPalette &palette); void setPalette(const QPalette &palette, const QPalette &parentPalette); private Q_SLOTS: void buildPalette(); void paletteChanged(const QPalette &palette); void handleComputeRadioClicked(); void handleDetailsRadioClicked(); void load(); void save(); private: void updatePreviewPalette(); void updateStyledButton(); QPalette::ColorGroup currentColorGroup() const { return m_currentColorGroup; } std::unique_ptr<Ui::PaletteEditor> m_ui; QPalette m_editPalette; QPalette m_parentPalette; QPalette::ColorGroup m_currentColorGroup; class PaletteModel *m_paletteModel; bool m_modelUpdated; bool m_paletteUpdated; bool m_compute; }; /*! * \brief The PaletteModel class is used by PaletteEditor. */ class QT_UTILITIES_EXPORT PaletteModel : public QAbstractTableModel { Q_OBJECT Q_PROPERTY(QPalette::ColorRole colorRole READ colorRole) public: explicit PaletteModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role) const override; bool setData(const QModelIndex &index, const QVariant &value, int role) override; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; QPalette getPalette() const; void setPalette(const QPalette &palette, const QPalette &parentPalette); QPalette::ColorRole colorRole() const { return QPalette::NoRole; } void setCompute(bool on) { m_compute = on; } Q_SIGNALS: void paletteChanged(const QPalette &palette); private: QPalette::ColorGroup columnToGroup(int index) const; int groupToColumn(QPalette::ColorGroup group) const; QPalette m_palette; QPalette m_parentPalette; QMap<QPalette::ColorRole, QString> m_roleNames; bool m_compute; }; /*! * \brief The BrushEditor class is used by PaletteEditor. */ class QT_UTILITIES_EXPORT BrushEditor : public QWidget { Q_OBJECT public: explicit BrushEditor(QWidget *parent = nullptr); void setBrush(const QBrush &brush); QBrush brush() const; bool changed() const; Q_SIGNALS: void changed(QWidget *widget); private Q_SLOTS: void brushChanged(); private: ColorButton *m_button; bool m_changed; }; /*! * \brief The RoleEditor class is used by PaletteEditor. */ class QT_UTILITIES_EXPORT RoleEditor : public QWidget { Q_OBJECT public: explicit RoleEditor(QWidget *parent = nullptr); void setLabel(const QString &label); void setEdited(bool on); bool edited() const; Q_SIGNALS: void changed(QWidget *widget); private Q_SLOTS: void emitResetProperty(); private: QLabel *m_label; bool m_edited; }; /*! * \brief The ColorDelegate class is used by PaletteEditor. */ class QT_UTILITIES_EXPORT ColorDelegate : public QItemDelegate { Q_OBJECT public: explicit ColorDelegate(QObject *parent = nullptr); QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *ed, const QModelIndex &index) const override; void setModelData(QWidget *ed, QAbstractItemModel *model, const QModelIndex &index) const override; void updateEditorGeometry(QWidget *ed, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const override; }; } // namespace QtUtilities #endif // WIDGETS_PALETTEEDITOR_H 07070100000037000081A4000000000000000000000001636534C90000117C000000000000000000000000000000000000002B00000000qtutilities/paletteeditor/paletteeditor.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::PaletteEditor</class> <widget class="QDialog" name="QtUtilities::PaletteEditor"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>365</width> <height>409</height> </rect> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="windowTitle"> <string>Edit Palette</string> </property> <layout class="QVBoxLayout"> <property name="spacing"> <number>6</number> </property> <property name="leftMargin"> <number>9</number> </property> <property name="topMargin"> <number>9</number> </property> <property name="rightMargin"> <number>9</number> </property> <property name="bottomMargin"> <number>9</number> </property> <item> <widget class="QGroupBox" name="advancedBox"> <property name="minimumSize"> <size> <width>0</width> <height>0</height> </size> </property> <property name="maximumSize"> <size> <width>16777215</width> <height>16777215</height> </size> </property> <property name="title"> <string>Tune Palette</string> </property> <layout class="QGridLayout"> <property name="leftMargin"> <number>9</number> </property> <property name="topMargin"> <number>9</number> </property> <property name="rightMargin"> <number>9</number> </property> <property name="bottomMargin"> <number>9</number> </property> <property name="spacing"> <number>6</number> </property> <item row="1" column="0" colspan="4"> <widget class="QTreeView" name="paletteView"> <property name="minimumSize"> <size> <width>0</width> <height>200</height> </size> </property> </widget> </item> <item row="0" column="3"> <widget class="QRadioButton" name="detailsRadio"> <property name="text"> <string>Show Detai&ls</string> </property> </widget> </item> <item row="0" column="2"> <widget class="QRadioButton" name="computeRadio"> <property name="text"> <string>&Compute Details</string> </property> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="0" column="0"> <widget class="QLabel" name="label"> <property name="text"> <string>Quick</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QtUtilities::ColorButton" name="buildButton"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Ignored"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string/> </property> </widget> </item> </layout> </widget> </item> <item> <widget class="QDialogButtonBox" name="buttonBox"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="standardButtons"> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> </property> </widget> </item> </layout> </widget> <customwidgets> <customwidget> <class>QtUtilities::ColorButton</class> <extends>QToolButton</extends> <header>paletteeditor/colorbutton.h</header> </customwidget> </customwidgets> <resources/> <connections> <connection> <sender>buttonBox</sender> <signal>accepted()</signal> <receiver>QtUtilities::PaletteEditor</receiver> <slot>accept()</slot> <hints> <hint type="sourcelabel"> <x>180</x> <y>331</y> </hint> <hint type="destinationlabel"> <x>134</x> <y>341</y> </hint> </hints> </connection> <connection> <sender>buttonBox</sender> <signal>rejected()</signal> <receiver>QtUtilities::PaletteEditor</receiver> <slot>reject()</slot> <hints> <hint type="sourcelabel"> <x>287</x> <y>329</y> </hint> <hint type="destinationlabel"> <x>302</x> <y>342</y> </hint> </hints> </connection> </connections> </ui> 07070100000038000041ED000000000000000000000003636534C900000000000000000000000000000000000000000000001600000000qtutilities/resources07070100000039000041ED000000000000000000000003636534C900000000000000000000000000000000000000000000001C00000000qtutilities/resources/icons0707010000003A000041ED000000000000000000000003636534C900000000000000000000000000000000000000000000002400000000qtutilities/resources/icons/hicolor0707010000003B000041ED000000000000000000000003636534C900000000000000000000000000000000000000000000002D00000000qtutilities/resources/icons/hicolor/scalable0707010000003C000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000003200000000qtutilities/resources/icons/hicolor/scalable/apps0707010000003D000081A4000000000000000000000001636534C900000C24000000000000000000000000000000000000004000000000qtutilities/resources/icons/hicolor/scalable/apps/qtcreator.svg<svg width="48" xmlns="http://www.w3.org/2000/svg" height="48" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="a" y1="36.39" x1="-2.994" y2="23.839" x2="-19.1" gradientUnits="userSpaceOnUse"> <stop stop-color="#95a5a6"/> <stop offset="1" stop-color="#eff0f1"/> </linearGradient> <linearGradient id="b" y1="9.178" x1="-14.65" y2="17.522" x2="-6.43" gradientUnits="userSpaceOnUse" gradientTransform="translate(1.01-1.022)"> <stop/> <stop offset="1" stop-opacity="0"/> </linearGradient> <linearGradient xlink:href="#b" id="c" y1="9.268" x1="-28.756" y2="27.436" x2="-10.858" gradientUnits="userSpaceOnUse" gradientTransform="translate(0-1.022)"/> <linearGradient id="d" y1="-.883" x1="-41.26" y2="37.956" x2="-2.994" gradientUnits="userSpaceOnUse" gradientTransform="translate(-1.01-1.022)"> <stop stop-color="#66b036"/> <stop offset="1" stop-color="#239161"/> </linearGradient> <linearGradient id="e" y1="37.956" x1="-2.994" y2="-.883" x2="-41.26" gradientUnits="userSpaceOnUse" gradientTransform="translate(0-1.022)"> <stop stop-color="#34495e"/> <stop offset="1" stop-color="#1c7865"/> </linearGradient> </defs> <g transform="matrix(.99317 0 0 .97839 45.973 4.864)"> <path fill="url(#d)" stroke-linejoin="bevel" stroke-width="2" d="m-36.03-.883c-2.894 0-5.137 2.332-5.223 5.224v33.615h33.04c2.894 0 5.223-2.331 5.223-5.224v-33.615z"/> <g fill-rule="evenodd" stroke-width="1.014"> <path opacity=".2" fill="url(#c)" d="m-29.17 6.271l-3.02 4.088 2.458 17.375 3.02 2.044-1.074 2.044 4.332 4.398 13.01-1.912c.487-.085.946-.297 1.363-.603l-5.54-8.686.794-9.199-9.06-9.199z"/> <path opacity=".2" fill="url(#b)" d="m-12.06 7.293l-3.02 20.442 6.751 6.853c1.063-.939 2.311-3.634 2.311-5.304v-15.858z"/> </g> <g stroke-linejoin="bevel" stroke-width="2"> <path fill="#ffffff" d="m-27.16 5.249c-4.03 0-7.05 2.044-7.05 11.754 0 8.01 2.149 10.798 5.086 11.53.524 6.17 6.997 4.312 6.997 4.312v-2.044c0 0-2.611.88-2.975-2.29 2.888-.769 4.989-3.586 4.989-11.508 0-9.71-3.02-11.754-7.05-11.754m0 2.044c2.01 0 3.02 1.689 3.02 9.71 0 8.02-1.01 9.71-3.02 9.71-2.01 0-3.02-1.689-3.02-9.71 0-8.02 1.01-9.71 3.02-9.71"/> <path fill="url(#e)" d="m-2.994 31.71c0 2.894-2.329 5.224-5.223 5.224h-33.04v1.022h33.04c2.894 0 5.223-2.331 5.223-5.224z"/> </g> <g fill-rule="evenodd" stroke-width="1.014"> <path fill="url(#a)" d="m2.041 21.458c0 0-3.344 2.232-6.288 3.934-2.944 1.702-5.488 2.875-7.089 4.993-3.202 4.235-.63 9.603-.63 9.603 0 0 5.773 1.132 8.975-3.103 1.601-2.118 2.058-4.915 2.916-8.243.858-3.327 2.116-7.184 2.116-7.184"/> <path fill="#7f8c8d" d="m2.041 21.459c0 0-.342.221-.403.262-.41 1.295-1.09 3.483-1.713 5.899-.858 3.327-1.316 6.125-2.916 8.243-3.202 4.235-8.973 3.104-8.973 3.104 0 0-.834-1.752-.958-4.04-.132 2.77.958 5.065.958 5.065 0 0 5.772 1.131 8.973-3.104 1.601-2.118 2.059-4.915 2.916-8.243.858-3.327 2.116-7.183 2.116-7.183"/> <path fill="#ffffff" d="m-15.08 7.293h3.02v4.088h2.01v2.044h-2.01v11.243c0 2.044 2.01.511 2.01.511v2.044c0 0-6.04 3.577-6.04-2.555v-11.243h-1.01v-2.044h1.01z"/> </g> </g> </svg> 0707010000003E000081A4000000000000000000000001636534C9000002D0000000000000000000000000000000000000002500000000qtutilities/resources/importplugin.h#ifndef MISC_UTILS_IMPORT_PLUGIN_H #define MISC_UTILS_IMPORT_PLUGIN_H #include "resources/qtconfig.h" #ifdef QT_STATIC #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) #include <QtPlugin> #ifdef Q_OS_WIN32 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) #endif #ifdef Q_OS_MACOS Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) #endif #ifdef SVG_SUPPORT Q_IMPORT_PLUGIN(QSvgPlugin) #endif #ifdef SVG_ICON_SUPPORT Q_IMPORT_PLUGIN(QSvgIconPlugin) #endif #ifdef IMAGE_FORMAT_SUPPORT IMPORT_IMAGE_FORMAT_PLUGINS #endif IMPORT_WIDGET_STYLE_PLUGINS #endif // defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) IMPORT_TLS_PLUGINS #endif // QT_STATIC #endif // MISC_UTILS_IMPORT_PLUGIN_H 0707010000003F000081A4000000000000000000000001636534C900002103000000000000000000000000000000000000002C00000000qtutilities/resources/qtconfigarguments.cpp#include "./qtconfigarguments.h" #include <c++utilities/conversion/stringconversion.h> #include <c++utilities/io/ansiescapecodes.h> #include <QFont> #include <QIcon> #include <QLocale> #include <QString> #ifdef QT_UTILITIES_GUI_QTWIDGETS #include <QApplication> #include <QStyle> #include <QStyleFactory> #else #include <QGuiApplication> #endif #include <initializer_list> #include <iostream> using namespace std; using namespace CppUtilities::EscapeCodes; /*! * \brief The CppUtilities namespace contains addons to the c++utilities library provided by the qtutilities library. */ namespace CppUtilities { /*! * \brief Constructs new Qt config arguments. */ QtConfigArguments::QtConfigArguments() : m_qtWidgetsGuiArg("qt-widgets-gui", 'g', "shows a Qt widgets based graphical user interface") , m_qtQuickGuiArg("qt-quick-gui", 'q', "shows a Qt quick based graphical user interface") , m_lngArg("lang", 'l', "sets the language for the Qt GUI") , m_qmlDebuggerArg("qmljsdebugger", 'q', "enables QML debugging (see " "http://doc.qt.io/qt-5/" "qtquick-debugging.html)") , m_widgetsStyleArg("widgets-style", '\0', "sets the Qt Widgets style") , m_quickControls2StyleArg("qqc2-style", '\0', "sets the Qt Quick Controls 2 style") , m_iconThemeArg("icon-theme", '\0', "sets the icon theme and additional " "theme search paths for the Qt GUI") , m_fontArg("font", '\0', "sets the font family and size (point) for the Qt GUI") , m_libraryPathsArg("library-paths", '\0', "sets the list of directories to search when loading " "libraries (all existing paths will be deleted)") , m_platformThemeArg("platformtheme", '\0', "specifies the Qt platform theme to be used") , m_sceneGraphRenderLoopArg("scene-graph-render-loop", '\0', "sets the loop for the Qt Quick Scene Graph OpenGL Renderer") { // language m_lngArg.setValueNames({ "language" }); m_lngArg.setRequiredValueCount(1); m_lngArg.setRequired(false); m_lngArg.setCombinable(true); // qml debugger (handled by Qt, just to let the parser know of it) m_qmlDebuggerArg.setValueNames({ "port:<port_from>[,port_to][,host:<ip address>][,block]" }); m_qmlDebuggerArg.setRequiredValueCount(1); m_qmlDebuggerArg.setCombinable(true); // appearance m_widgetsStyleArg.setValueNames({ "breeze/cleanlooks/fusion/kvantum/oxygen/adwaita/windows/..." }); m_widgetsStyleArg.setRequiredValueCount(1); m_widgetsStyleArg.setCombinable(true); m_widgetsStyleArg.setEnvironmentVariable("QT_STYLE_OVERRIDE"); m_quickControls2StyleArg.setValueNames({ "default/material/universal/org.kde.desktop/..." }); m_quickControls2StyleArg.setRequiredValueCount(1); m_quickControls2StyleArg.setCombinable(true); m_quickControls2StyleArg.setEnvironmentVariable("QT_QUICK_CONTROLS_STYLE"); m_iconThemeArg.setValueNames({ "theme name", "search path 1", "search path 2" }); m_iconThemeArg.setRequiredValueCount(Argument::varValueCount); m_iconThemeArg.setCombinable(true); m_iconThemeArg.setEnvironmentVariable("ICON_THEME_SEARCH_PATH and ICON_THEME"); m_fontArg.setValueNames({ "name", "size" }); m_fontArg.setRequiredValueCount(2); m_fontArg.setCombinable(true); m_libraryPathsArg.setValueNames({ "path 1", "path 2" }); m_libraryPathsArg.setRequiredValueCount(Argument::varValueCount); m_libraryPathsArg.setCombinable(true); m_platformThemeArg.setRequiredValueCount(1); m_platformThemeArg.setCombinable(true); m_platformThemeArg.setValueNames({ "qt5ct/kde/..." }); m_platformThemeArg.setPreDefinedCompletionValues("kde gnome " #if QT_VERSION_MAJOR == 5 "qt5ct" #elif QT_VERSION_MAJOR == 6 "qt6ct" #endif ); m_platformThemeArg.setEnvironmentVariable("QT_QPA_PLATFORMTHEME"); m_sceneGraphRenderLoopArg.setRequiredValueCount(1); m_sceneGraphRenderLoopArg.setCombinable(true); m_sceneGraphRenderLoopArg.setValueNames({ "basic/windows/threaded" }); m_sceneGraphRenderLoopArg.setPreDefinedCompletionValues("basic windows threaded"); m_sceneGraphRenderLoopArg.setEnvironmentVariable("QSG_RENDER_LOOP"); m_qtWidgetsGuiArg.setSubArguments( { &m_lngArg, &m_qmlDebuggerArg, &m_widgetsStyleArg, &m_iconThemeArg, &m_fontArg, &m_libraryPathsArg, &m_platformThemeArg }); m_qtQuickGuiArg.setSubArguments({ &m_lngArg, &m_qmlDebuggerArg, &m_quickControls2StyleArg, &m_iconThemeArg, &m_fontArg, &m_libraryPathsArg, &m_platformThemeArg, &m_sceneGraphRenderLoopArg }); m_qtWidgetsGuiArg.setDenotesOperation(true); m_qtQuickGuiArg.setDenotesOperation(true); #if defined(QT_UTILITIES_GUI_QTWIDGETS) m_qtWidgetsGuiArg.setImplicit(true); #elif defined(QT_UTILITIES_GUI_QTQUICK) m_qtQuickGuiArg.setImplicit(true); #endif } /*! * \brief Applies the settings from the arguments. * \remarks Also checks environment variables for the icon theme. * \param preventApplyingDefaultFont If true, the font will not be updated to * some default value if no font has been specified explicitly. */ void QtConfigArguments::applySettings(bool preventApplyingDefaultFont) const { if (m_lngArg.isPresent()) { QLocale::setDefault(QLocale(QString::fromLocal8Bit(m_lngArg.values().front()))); } #ifdef QT_UTILITIES_GUI_QTWIDGETS if (m_widgetsStyleArg.isPresent()) { if (QStyle *const style = QStyleFactory::create(QString::fromLocal8Bit(m_widgetsStyleArg.values().front()))) { QApplication::setStyle(style); } else { cerr << Phrases::Warning << "Can not find the specified Qt Widgets style." << Phrases::EndFlush; } } #endif if (m_iconThemeArg.isPresent()) { auto i = m_iconThemeArg.values().cbegin(), end = m_iconThemeArg.values().end(); if (i != end) { QIcon::setThemeName(QString::fromLocal8Bit(*i)); if (++i != end) { QStringList searchPaths; searchPaths.reserve(static_cast<QStringList::size_type>(m_iconThemeArg.values().size() - 1)); for (; i != end; ++i) { searchPaths << QString::fromLocal8Bit(*i); } searchPaths << QStringLiteral(":/icons"); QIcon::setThemeSearchPaths(searchPaths); } } } else { if (qEnvironmentVariableIsSet("ICON_THEME_SEARCH_PATH")) { QString path; path.append(qgetenv("ICON_THEME_SEARCH_PATH")); QIcon::setThemeSearchPaths(QStringList({ path, QStringLiteral(":/icons") })); } else { QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << QStringLiteral("../share/icons") << QStringLiteral(":/icons")); } if (qEnvironmentVariableIsSet("ICON_THEME")) { QString themeName; themeName.append(qgetenv("ICON_THEME")); QIcon::setThemeName(themeName); } } #ifdef Q_OS_WIN32 // default configuration under Windows if (QIcon::themeName().isEmpty()) { QIcon::setThemeName(QStringLiteral("default")); } #endif if (m_fontArg.isPresent()) { QFont font; font.setFamily(QString::fromLocal8Bit(m_fontArg.values().front())); try { font.setPointSize(stringToNumber<int>(m_fontArg.values().back())); } catch (const ConversionException &) { cerr << Phrases::Warning << "The specified font size is no number and will be ignored." << Phrases::EndFlush; } QGuiApplication::setFont(font); } #ifdef Q_OS_WIN32 else if (!preventApplyingDefaultFont) { QGuiApplication::setFont(QFont(QStringLiteral("Segoe UI"), 9)); } #else CPP_UTILITIES_UNUSED(preventApplyingDefaultFont) #endif if (m_libraryPathsArg.isPresent()) { QStringList libraryPaths; libraryPaths.reserve(static_cast<QStringList::size_type>(m_libraryPathsArg.values().size())); for (const auto &path : m_libraryPathsArg.values()) { libraryPaths << QString::fromLocal8Bit(path); } QCoreApplication::setLibraryPaths(libraryPaths); } if (m_sceneGraphRenderLoopArg.isPresent()) { qputenv(m_sceneGraphRenderLoopArg.environmentVariable(), QByteArray(m_sceneGraphRenderLoopArg.firstValue())); } } } // namespace CppUtilities 07070100000040000081A4000000000000000000000001636534C900000A32000000000000000000000000000000000000002A00000000qtutilities/resources/qtconfigarguments.h#ifndef APPLICATION_UTILITIES_QTCONFIGARGUMENTS_H #define APPLICATION_UTILITIES_QTCONFIGARGUMENTS_H #include "../global.h" #include <c++utilities/application/argumentparser.h> #ifdef QT_UTILITIES_GUI_QTQUICK #include <QQuickStyle> #include <QString> #if defined(PLATFORM_ANDROID) #define QT_UTILITIES_DEFAULT_QQC2_STYLE "material" #elif defined(PLATFORM_WINDOWS) #define QT_UTILITIES_DEFAULT_QQC2_STYLE "universal" #endif #endif namespace CppUtilities { class QT_UTILITIES_EXPORT QtConfigArguments { public: QtConfigArguments(); Argument &qtWidgetsGuiArg(); Argument &qtQuickGuiArg(); Argument &languageArg(); bool areQtGuiArgsPresent() const; void applySettings(bool preventApplyingDefaultFont = false) const; #ifdef QT_UTILITIES_GUI_QTQUICK void applySettingsForQuickGui() const; #endif private: Argument m_qtWidgetsGuiArg; Argument m_qtQuickGuiArg; Argument m_lngArg; Argument m_qmlDebuggerArg; Argument m_widgetsStyleArg; Argument m_quickControls2StyleArg; Argument m_iconThemeArg; Argument m_fontArg; Argument m_libraryPathsArg; Argument m_platformThemeArg; Argument m_sceneGraphRenderLoopArg; }; /*! * \brief Returns the argument for the Qt Widgets GUI. */ inline Argument &QtConfigArguments::qtWidgetsGuiArg() { return m_qtWidgetsGuiArg; } /*! * \brief Returns the argument for the Qt Quick GUI. */ inline Argument &QtConfigArguments::qtQuickGuiArg() { return m_qtQuickGuiArg; } /*! * \brief Returns the language argument. */ inline Argument &QtConfigArguments::languageArg() { return m_lngArg; } /*! * \brief Returns whether at least one of the GUI arguments is present. */ inline bool QtConfigArguments::areQtGuiArgsPresent() const { return m_qtWidgetsGuiArg.isPresent() || m_qtQuickGuiArg.isPresent(); } #ifdef QT_UTILITIES_GUI_QTQUICK /*! * \brief Applies settings the for Qt Quick GUI. */ inline void QtConfigArguments::applySettingsForQuickGui() const { if (m_quickControls2StyleArg.isPresent()) { QQuickStyle::setStyle(QString::fromLocal8Bit(m_quickControls2StyleArg.values().front())); } #ifdef QT_UTILITIES_DEFAULT_QQC2_STYLE else if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { QQuickStyle::setStyle(QStringLiteral(QT_UTILITIES_DEFAULT_QQC2_STYLE)); } #endif // QT_UTILITIES_DEFAULT_QQC2_STYLE } #endif // QT_UTILITIES_GUI_QTQUICK } // namespace CppUtilities #endif // APPLICATION_UTILITIES_QTCONFIGARGUMENTS_H #ifdef QT_CONFIG_ARGUMENTS #undef QT_CONFIG_ARGUMENTS #endif #define QT_CONFIG_ARGUMENTS CppUtilities::QtConfigArguments 07070100000041000081A4000000000000000000000001636534C9000000B2000000000000000000000000000000000000002700000000qtutilities/resources/qtutilsicons.qrc<RCC> <qresource prefix="/qtutilities"> <file alias="icons/hicolor/48x48/apps/qtcreator.svg">icons/hicolor/scalable/apps/qtcreator.svg</file> </qresource> </RCC> 07070100000042000081A4000000000000000000000001636534C900003A93000000000000000000000000000000000000002400000000qtutilities/resources/resources.cpp#include "./resources.h" #include "resources/config.h" #include <QDir> #include <QFile> #include <QFont> #include <QIcon> #include <QLibraryInfo> #include <QLocale> #include <QSettings> #include <QString> #include <QStringBuilder> #include <QTranslator> #if defined(QT_UTILITIES_GUI_QTWIDGETS) #include <QApplication> #elif defined(QT_UTILITIES_GUI_QTQUICK) #include <QGuiApplication> #else #include <QCoreApplication> #endif #include <iostream> using namespace std; ///! \cond inline void initResources() { Q_INIT_RESOURCE(qtutilsicons); } inline void cleanupResources() { Q_CLEANUP_RESOURCE(qtutilsicons); } ///! \endcond namespace QtUtilities { /*! * \brief Functions for using the resources provided by this library. * \deprecated Replaced by ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES macro. */ namespace QtUtilitiesResources { /*! * \brief Initiates the resources used and provided by this library. * \deprecated Replaced by ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES macro. */ void init() { initResources(); } /*! * \brief Frees the resources used and provided by this library. * \deprecated Replaced by ENABLE_QT_RESOURCES_OF_STATIC_DEPENDENCIES macro. */ void cleanup() { cleanupResources(); } } // namespace QtUtilitiesResources /*! * \brief Convenience functions to load translations for Qt and the application. */ namespace TranslationFiles { /*! * \brief Allows to set an additional search path for translation files. * \remarks This path is considered *before* the default directories. */ QString &additionalTranslationFilePath() { static QString path; return path; } /*! * \brief Loads and installs the appropriate Qt translation file for the current * locale. * \param repositoryNames Specifies the names of the Qt repositories to load * translations for (eg. qtbase, qtscript, ...). * \remarks * - Translation files have to be placed in one of the following locations: * * QLibraryInfo::location(QLibraryInfo::TranslationsPath) (used in UNIX) * * ../share/qt/translations (used in Windows) * - Translation files can also be built-in using by setting the CMake variable * BUILTIN_TRANSLATIONS. * In this case it is also necessary to load the translations using this * function. */ void loadQtTranslationFile(std::initializer_list<QString> repositoryNames) { loadQtTranslationFile(repositoryNames, QLocale().name()); } /*! * \brief Loads and installs the appropriate Qt translation file for the * specified locale. * \param repositoryNames Specifies the names of the Qt repositories to load * translations for (eg. qtbase, qtscript, ...). * \param localeName Specifies the name of the locale. * \remarks * - Translation files have to be placed in one of the following locations: * * QLibraryInfo::location(QLibraryInfo::TranslationsPath) (used in UNIX) * * ../share/qt/translations (used in Windows) * - Translation files can also be built-in using by setting the CMake variable * BUILTIN_TRANSLATIONS. * In this case it is also necessary to load the translations using this * function. */ void loadQtTranslationFile(initializer_list<QString> repositoryNames, const QString &localeName) { const auto debugTranslations = qEnvironmentVariableIsSet("QT_DEBUG_TRANSLATIONS"); for (const auto &repoName : repositoryNames) { auto *const qtTranslator = new QTranslator(QCoreApplication::instance()); const auto fileName = QString(repoName % QChar('_') % localeName); QString path; if ((!additionalTranslationFilePath().isEmpty() && qtTranslator->load(fileName, path = additionalTranslationFilePath())) || qtTranslator->load(fileName, path = #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QLibraryInfo::location(QLibraryInfo::TranslationsPath) #else QLibraryInfo::path(QLibraryInfo::TranslationsPath) #endif ) || qtTranslator->load(fileName, path = QStringLiteral("../share/qt/translations")) || qtTranslator->load(fileName, path = QStringLiteral(":/translations"))) { QCoreApplication::installTranslator(qtTranslator); if (debugTranslations) { cerr << "Loading translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and the locale \"" << localeName.toLocal8Bit().data() << "\" from \"" << path.toLocal8Bit().data() << "\"." << endl; } } else { delete qtTranslator; if (localeName.startsWith(QLatin1String("en"))) { // the translation file is probably just empty (English is built-in and usually only used for plural forms) continue; } cerr << "Unable to load translation file for Qt repository \"" << repoName.toLocal8Bit().data() << "\" and locale " << localeName.toLocal8Bit().data() << "." << endl; } } } /*! * \brief Loads and installs the appropriate application translation file for * the current locale. * \param applicationName Specifies the name of the application. * \remarks * - Translation files have to be placed in one of the following locations: * * ./ * * ../$application * * ../../$application * * ./translations * * ../share/$application/translations * * $install_prefix/share/$application/translations * - Translation files must be named using the following scheme: * * $application_$language.qm * - Translation files can also be built-in using by setting the CMake variable * BUILTIN_TRANSLATIONS. * In this case it is also necessary to load the translations using this * function. */ void loadApplicationTranslationFile(const QString &configName, const QString &applicationName) { // load English translation files as fallback loadApplicationTranslationFile(configName, applicationName, QStringLiteral("en_US")); // load translation files for current locale const auto defaultLocale(QLocale().name()); if (defaultLocale != QLatin1String("en_US")) { loadApplicationTranslationFile(configName, applicationName, defaultLocale); } } /// \cond void logTranslationEvent( const char *event, const QString &configName, const QString &applicationName, const QString &localeName, const QString &path = QString()) { cerr << event << " translation file for \"" << applicationName.toLocal8Bit().data() << "\""; if (!configName.isEmpty()) { cerr << " (config \"" << configName.toLocal8Bit().data() << "\")"; } cerr << " and locale \"" << localeName.toLocal8Bit().data() << '\"'; if (!path.isEmpty()) { cerr << " from \"" << path.toLocal8Bit().data() << '\"'; } cerr << '.' << endl; } /// \endcond /*! * \brief Loads and installs the appropriate application translation file for * the specified locale. * \param applicationName Specifies the name of the application. * \param localeName Specifies the name of the locale. * \remarks * - Translation files have to be placed in one of the following locations: * * ./ * * ../$application * * ../../$application * * ./translations * * ../share/$application/translations * * $install_prefix/share/$application/translations * - Translation files must be named using the following scheme: * * $application_$language.qm * - Translation files can also be built-in using by setting the CMake variable * BUILTIN_TRANSLATIONS. * In this case it is also necessary to load the translations using this * function. */ void loadApplicationTranslationFile(const QString &configName, const QString &applicationName, const QString &localeName) { auto *const appTranslator = new QTranslator(QCoreApplication::instance()); const auto fileName = QString(applicationName % QChar('_') % localeName); const auto directoryName = configName.isEmpty() ? applicationName : QString(applicationName % QChar('-') % configName); QString path; if ((!additionalTranslationFilePath().isEmpty() && appTranslator->load(fileName, path = additionalTranslationFilePath())) || appTranslator->load(fileName, path = QStringLiteral(".")) || appTranslator->load(fileName, path = QStringLiteral("../") % directoryName) || appTranslator->load(fileName, path = QStringLiteral("../") % directoryName) || appTranslator->load(fileName, path = QStringLiteral("../../") % directoryName) || appTranslator->load(fileName, path = QStringLiteral("./translations")) || appTranslator->load(fileName, path = QStringLiteral("../share/") % directoryName % QStringLiteral("/translations")) || appTranslator->load(fileName, path = QStringLiteral(APP_INSTALL_PREFIX "/share/") % directoryName % QStringLiteral("/translations")) || appTranslator->load(fileName, path = QStringLiteral(":/translations"))) { QCoreApplication::installTranslator(appTranslator); if (qEnvironmentVariableIsSet("QT_DEBUG_TRANSLATIONS")) { logTranslationEvent("Loading", configName, applicationName, localeName, path); } } else { delete appTranslator; if (localeName.startsWith(QLatin1String("en"))) { // the translation file is probably just empty (English is built-in and usually only used for plural forms) return; } logTranslationEvent("Unable to load", configName, applicationName, localeName); } } /*! * \brief Loads and installs the appropriate application translation file for * the current locale. * \param applicationNames Specifies the names of the applications. */ void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames) { for (const QString &applicationName : applicationNames) { loadApplicationTranslationFile(configName, applicationName); } } /*! * \brief Loads and installs the appropriate application translation file for * the specified locale. * \param applicationNames Specifies the names of the applications. * \param localeName Specifies the name of the locale. */ void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames, const QString &localeName) { for (const QString &applicationName : applicationNames) { loadApplicationTranslationFile(configName, applicationName, localeName); } } } // namespace TranslationFiles /*! * \brief Convenience functions to check whether a * QCoreApplication/QGuiApplication/QApplication singleton has been instantiated * yet. */ namespace ApplicationInstances { #if defined(QT_UTILITIES_GUI_QTWIDGETS) /*! * \brief Returns whether a QApplication has been instantiated yet. */ bool hasWidgetsApp() { return qobject_cast<QApplication *>(QCoreApplication::instance()) != nullptr; } #endif #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) /*! * \brief Returns whether a QGuiApplication has been instantiated yet. */ bool hasGuiApp() { return qobject_cast<QGuiApplication *>(QCoreApplication::instance()) != nullptr; } #endif /*! * \brief Returns whether a QCoreApplication has been instantiated yet. */ bool hasCoreApp() { return qobject_cast<QCoreApplication *>(QCoreApplication::instance()) != nullptr; } } // namespace ApplicationInstances /*! * \brief Sets Qt application attributes which are commonly used within my Qt applications. * \remarks * - So far this enables High-DPI support. * - The exact attributes are unspecified and might change to whatever makes sense in the future. */ void setupCommonQtApplicationAttributes() { // enable dark window frame on Windows if the configured color palette is dark (supported as of Qt 6.4) // see https://bugreports.qt.io/browse/QTBUG-72028?focusedCommentId=677819#comment-677819 #if defined(Q_OS_WINDOWS) && (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) if (QLibraryInfo::version() >= QVersionNumber(6, 4, 0) && !qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { qputenv("QT_QPA_PLATFORM", "windows:darkmode=1"); } #endif // ensure FONTCONFIG_PATH is set (mainly required for static GNU/Linux builds) #ifdef QT_FEATURE_fontdialog if (!qEnvironmentVariableIsSet("FONTCONFIG_PATH") && QDir(QStringLiteral("/etc/fonts")).exists()) { qputenv("FONTCONFIG_PATH", "/etc/fonts"); } #endif // enable settings for High-DPI scaling #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) if (!QCoreApplication::instance()) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true); } QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps, true); #endif } /*! * \brief Returns the settings object for the specified \a organization and \a application. * \remarks * - This function always uses INI as that's what I'd like to use in all of my applications consistently, regardless of the platform. * - The parameter \a application might be empty. In fact, most of my applications use just `getSettings(QStringLiteral(PROJECT_NAME))`. * - This function first checks whether a file called `$organization/$application.ini` exists in the current working directory (or just * `$organization.ini` if \a application is empty) and uses that if it exists. That allows having a portable installation. * - Some of my apps where using values from QCoreApplication for \a organization and \a application in the beginning. This function * moves those old config files to their new location if needed. This extra handling will likely removed at some point. Note that * I moved away from using values from QCoreApplication to avoid having spaces and additional config suffixes in the file name. */ std::unique_ptr<QSettings> getSettings(const QString &organization, const QString &application) { auto settings = std::unique_ptr<QSettings>(); if (const auto portableFile = QFile(application.isEmpty() ? organization + QStringLiteral(".ini") : organization % QChar('/') % application % QStringLiteral(".ini")); portableFile.exists()) { settings = std::make_unique<QSettings>(portableFile.fileName(), QSettings::IniFormat); } else { settings = std::make_unique<QSettings>(QSettings::IniFormat, QSettings::UserScope, organization, application); // move config created by older versions to new location if (organization != QCoreApplication::organizationName() || application != QCoreApplication::applicationName()) { const auto oldConfig = QSettings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName()) .fileName(); QFile::rename(oldConfig, settings->fileName()) || QFile::remove(oldConfig); } } settings->sync(); return settings; } // namespace ApplicationInstances } // namespace QtUtilities 07070100000043000081A4000000000000000000000001636534C900000E08000000000000000000000000000000000000002200000000qtutilities/resources/resources.h#ifndef APPLICATION_UTILITIES_RESOURCES_H #define APPLICATION_UTILITIES_RESOURCES_H #include "../global.h" #include <QString> #include <QtContainerFwd> #include <QtGlobal> #include <initializer_list> #include <memory> QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QSettings) #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) QT_FORWARD_DECLARE_CLASS(QStringList) #endif /*! * \brief Sets the application meta data in the QCoreApplication singleton and attributes commonly used * within my applications. * \sa ::QtUtilities::setupCommonQtApplicationAttributes() */ #define SET_QT_APPLICATION_INFO \ QCoreApplication::setOrganizationName(QStringLiteral(APP_AUTHOR)); \ QCoreApplication::setOrganizationDomain(QStringLiteral(APP_DOMAIN)); \ QCoreApplication::setApplicationName(QStringLiteral(APP_NAME)); \ QCoreApplication::setApplicationVersion(QStringLiteral(APP_VERSION)); \ ::QtUtilities::setupCommonQtApplicationAttributes() /*! * \brief Loads translations for Qt, other dependencies and the application. */ #define LOAD_QT_TRANSLATIONS \ QtUtilities::TranslationFiles::loadQtTranslationFile(QT_TRANSLATION_FILES); \ QtUtilities::TranslationFiles::loadApplicationTranslationFile(QStringLiteral(PROJECT_CONFIG_NAME), APP_SPECIFIC_QT_TRANSLATION_FILES) namespace QtUtilities { namespace QtUtilitiesResources { QT_UTILITIES_EXPORT void init(); QT_UTILITIES_EXPORT void cleanup(); } // namespace QtUtilitiesResources namespace TranslationFiles { QT_UTILITIES_EXPORT QString &additionalTranslationFilePath(); QT_UTILITIES_EXPORT void loadQtTranslationFile(std::initializer_list<QString> repositoryNames); QT_UTILITIES_EXPORT void loadQtTranslationFile(std::initializer_list<QString> repositoryNames, const QString &localeName); QT_UTILITIES_EXPORT void loadApplicationTranslationFile(const QString &configName, const QString &applicationName); QT_UTILITIES_EXPORT void loadApplicationTranslationFile(const QString &configName, const QString &applicationName, const QString &localeName); QT_UTILITIES_EXPORT void loadApplicationTranslationFile(const QString &configName, const std::initializer_list<QString> &applicationNames); QT_UTILITIES_EXPORT void loadApplicationTranslationFile( const QString &configName, const std::initializer_list<QString> &applicationNames, const QString &localeName); } // namespace TranslationFiles namespace ApplicationInstances { #if defined(QT_UTILITIES_GUI_QTWIDGETS) QT_UTILITIES_EXPORT bool hasWidgetsApp(); #endif #if defined(QT_UTILITIES_GUI_QTWIDGETS) || defined(QT_UTILITIES_GUI_QTQUICK) QT_UTILITIES_EXPORT bool hasGuiApp(); #endif QT_UTILITIES_EXPORT bool hasCoreApp(); } // namespace ApplicationInstances QT_UTILITIES_EXPORT void setupCommonQtApplicationAttributes(); QT_UTILITIES_EXPORT std::unique_ptr<QSettings> getSettings(const QString &organization, const QString &application = QString()); } // namespace QtUtilities #endif // APPLICATION_UTILITIES_RESOURCES_H 07070100000044000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001400000000qtutilities/scripts07070100000045000081ED000000000000000000000001636534C900000459000000000000000000000000000000000000002200000000qtutilities/scripts/svg_to_png.sh#!/bin/bash # abort on first error set -e if [[ ! $@ ]]; then echo 'Updates the the PNG icons of the projects in the specified directories.' echo 'However, no project directories have been specified.' exit -1 fi # define array for commands to be executed cmds=() # iterate over specified source directories for srcdir in "$@"; do # find SVG icons for svg_icon_full_path in $(find "$srcdir" -iname '*.svg'); do prefix="${svg_icon_full_path%/scalable/*}" svg_icon="${svg_icon_full_path##*/scalable/}" # add inkscape command for each icon and size and ensure output directory exists for size in 16 32 48; do mkdir -p "${prefix}/${size}x${size}/${svg_icon%/*.svg}" cmds+=("inkscape --without-gui \"${svg_icon_full_path}\" --export-png=\"${prefix}/${size}x${size}/${svg_icon%.svg}.png\" --export-width=${size} --export-height=${size}") done done done # run commands function print_cmds { for cmd in "${cmds[@]}"; do echo "$cmd" done } echo "Executing the following commands:" print_cmds print_cmds | parallel 07070100000046000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001B00000000qtutilities/settingsdialog07070100000047000081A4000000000000000000000001636534C900000877000000000000000000000000000000000000002E00000000qtutilities/settingsdialog/optioncategory.cpp#include "./optioncategory.h" #include "./optionpage.h" namespace QtUtilities { /*! * \class OptionCategory * \brief The OptionCategory class wraps associated option pages. */ /*! * \brief Constructs a option category. */ OptionCategory::OptionCategory(QObject *parent) : QObject(parent) , m_currentIndex(0) { } /*! * \brief Destroys the option category. */ OptionCategory::~OptionCategory() { qDeleteAll(m_pages); } /*! * \brief Applies all pages. * \remarks Pages which have not been shown yet must have not been initialized anyways * and hence are skipped. * \sa OptionPage::apply() */ bool OptionCategory::applyAllPages() { for (OptionPage *page : m_pages) { if (!page->hasBeenShown()) { continue; } if (!page->apply()) { return false; } } return true; } /*! * \brief Resets all pages. * \remarks Pages which have not been shown yet must have not been initialized anyways * and hence are skipped. * \sa OptionPage::reset() */ void OptionCategory::resetAllPages() { for (OptionPage *page : m_pages) { if (page->hasBeenShown()) { page->reset(); } } } /*! * \brief Returns whether the option category matches the specified \a * searchKeyWord. */ bool OptionCategory::matches(const QString &searchKeyWord) const { for (OptionPage *page : m_pages) { if (page->matches(searchKeyWord)) { return true; } } return false; } /*! * \brief Assigns the specified \a pages to the category. * * Previously assigned pages get deleted. The pagesChanged() signal is emitted. * The category takes ownership over the given \a pages. */ void OptionCategory::assignPages(const QList<OptionPage *> &pages) { qDeleteAll(m_pages); emit pagesChanged(m_pages = pages); } /*! * \fn OptionCategory::displayNameChanged() * \brief Emitted when the display name changed. */ /*! * \fn OptionCategory::iconChanged() * \brief Emitted when the icon changed. */ /*! * \fn OptionCategory::pagesChanged() * \brief Emitted when the pages changed. */ } // namespace QtUtilities 07070100000048000081A4000000000000000000000001636534C900000A5F000000000000000000000000000000000000002C00000000qtutilities/settingsdialog/optioncategory.h#ifndef DIALOGS_OPTIONSCATEGORY_H #define DIALOGS_OPTIONSCATEGORY_H #include "../global.h" #include <QIcon> #include <QList> #include <QObject> #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) Q_MOC_INCLUDE("settingsdialog/optionpage.h") #endif namespace QtUtilities { class OptionPage; class QT_UTILITIES_EXPORT OptionCategory : public QObject { Q_OBJECT Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged) Q_PROPERTY(QIcon icon READ icon WRITE setIcon NOTIFY iconChanged) Q_PROPERTY(QList<OptionPage *> pages READ pages WRITE assignPages NOTIFY pagesChanged) public: explicit OptionCategory(QObject *parent = nullptr); ~OptionCategory() override; const QString &displayName() const; void setDisplayName(const QString &displayName); const QIcon &icon() const; void setIcon(const QIcon &icon); const QList<OptionPage *> &pages() const; void assignPages(const QList<OptionPage *> &pages); bool applyAllPages(); void resetAllPages(); bool matches(const QString &searchKeyWord) const; int currentIndex() const; void setCurrentIndex(int currentIndex); Q_SIGNALS: void displayNameChanged(const QString &displayName); void iconChanged(const QIcon &icon); void pagesChanged(const QList<OptionPage *> &pages); private: QString m_displayName; QIcon m_icon; QList<OptionPage *> m_pages; int m_currentIndex; }; /*! * \brief Returns the display name of the category. */ inline const QString &OptionCategory::displayName() const { return m_displayName; } /*! * \brief Sets the display name of the category. */ inline void OptionCategory::setDisplayName(const QString &displayName) { emit displayNameChanged(m_displayName = displayName); } /*! * \brief Returns the icon of the category. */ inline const QIcon &OptionCategory::icon() const { return m_icon; } /*! * \brief Sets the icon of the category. */ inline void OptionCategory::setIcon(const QIcon &icon) { emit iconChanged(m_icon = icon); } /*! * \brief Returns the assigned pages. */ inline const QList<OptionPage *> &OptionCategory::pages() const { return m_pages; } /*! * \brief Returns the index of the currently shown page. * \remarks The returned index might be invalid/out of range. * \sa setCurrentIndex() */ inline int OptionCategory::currentIndex() const { return m_currentIndex; } /*! * \brief Sets the current index. * \sa currentIndex() */ inline void OptionCategory::setCurrentIndex(int currentIndex) { m_currentIndex = currentIndex; } } // namespace QtUtilities #endif // DIALOGS_OPTIONSCATEGORY_H 07070100000049000081A4000000000000000000000001636534C9000004E2000000000000000000000000000000000000003900000000qtutilities/settingsdialog/optioncategoryfiltermodel.cpp#include "./optioncategoryfiltermodel.h" #include "./optioncategory.h" #include "./optioncategorymodel.h" namespace QtUtilities { /*! * \class OptionCategoryFilterModel * \brief The OptionCategoryFilterModel class is used by SettingsDialog to * filter option categories. */ /*! * \brief Constructs an option category filter model. */ OptionCategoryFilterModel::OptionCategoryFilterModel(QObject *parent) : QSortFilterProxyModel(parent) { } bool OptionCategoryFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { if (QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) return true; if (auto *const model = qobject_cast<OptionCategoryModel *>(sourceModel())) { if (OptionCategory *category = model->category(sourceRow)) { return category->matches( #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) filterRegularExpression().pattern() #elif (QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)) !filterRegularExpression().pattern().isEmpty() ? filterRegularExpression().pattern() : filterRegExp().pattern() #else filterRegExp().pattern() #endif ); } } return false; } } // namespace QtUtilities 0707010000004A000081A4000000000000000000000001636534C9000001E1000000000000000000000000000000000000003700000000qtutilities/settingsdialog/optioncategoryfiltermodel.h#ifndef DIALOGS_OPTIONCATEGORYFILTERMODEL_H #define DIALOGS_OPTIONCATEGORYFILTERMODEL_H #include <QSortFilterProxyModel> namespace QtUtilities { class OptionCategoryFilterModel : public QSortFilterProxyModel { Q_OBJECT public: explicit OptionCategoryFilterModel(QObject *parent = nullptr); protected: bool filterAcceptsRow(int source_row, const QModelIndex &sourceParent) const override; }; } // namespace QtUtilities #endif // DIALOGS_OPTIONCATEGORYFILTERMODEL_H 0707010000004B000081A4000000000000000000000001636534C900000DF6000000000000000000000000000000000000003300000000qtutilities/settingsdialog/optioncategorymodel.cpp#include "./optioncategorymodel.h" #include "./optioncategory.h" #ifdef QT_UTILITIES_GUI_QTWIDGETS #include <QApplication> #include <QStyle> #endif #include <utility> namespace QtUtilities { /*! * \class OptionCategoryModel * \brief The OptionCategoryModel class is used by SettingsDialog to store and * display option categories. */ /*! * \brief Constructs an option category model. */ OptionCategoryModel::OptionCategoryModel(QObject *parent) : QAbstractListModel(parent) { } /*! * \brief Constructs an option category model with the specified \a categories. * \remarks The model takes ownership over the given categories. */ OptionCategoryModel::OptionCategoryModel(const QList<OptionCategory *> &categories, QObject *parent) : QAbstractListModel(parent) , m_categories(categories) { for (OptionCategory *category : std::as_const(m_categories)) { category->setParent(this); } } /*! * \brief Destroys the option category model. */ OptionCategoryModel::~OptionCategoryModel() { } /*! * \brief Sets the \a categories for the model. * * The model takes ownership over the given \a categories. */ void OptionCategoryModel::setCategories(const QList<OptionCategory *> &categories) { beginResetModel(); qDeleteAll(m_categories); m_categories = categories; for (OptionCategory *const category : std::as_const(m_categories)) { category->setParent(this); connect(category, &OptionCategory::displayNameChanged, this, &OptionCategoryModel::categoryChangedName); connect(category, &OptionCategory::iconChanged, this, &OptionCategoryModel::categoryChangedIcon); } endResetModel(); } int OptionCategoryModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : static_cast<int>(m_categories.size()); } QVariant OptionCategoryModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= m_categories.size()) { return QVariant(); } switch (role) { case Qt::DisplayRole: return m_categories.at(index.row())->displayName(); case Qt::DecorationRole: { const QIcon &icon = m_categories.at(index.row())->icon(); if (!icon.isNull()) { return icon.pixmap( #ifdef QT_UTILITIES_GUI_QTWIDGETS QApplication::style()->pixelMetric(QStyle::PM_LargeIconSize) #else QSize(32, 32) #endif ); } } } return QVariant(); } /*! * \brief Handles the change of name of a category. */ void OptionCategoryModel::categoryChangedName() { const auto *const senderCategory = qobject_cast<const OptionCategory *>(QObject::sender()); if (!senderCategory) { return; } for (int i = 0, end = static_cast<int>(m_categories.size()); i < end; ++i) { if (senderCategory == m_categories.at(i)) { QModelIndex index = this->index(i); emit dataChanged(index, index, QVector<int>({ Qt::DisplayRole })); } } } /*! * \brief Handles the a changed icon of a category. */ void OptionCategoryModel::categoryChangedIcon() { const auto *const senderCategory = qobject_cast<const OptionCategory *>(QObject::sender()); if (!senderCategory) { return; } for (int i = 0, end = static_cast<int>(m_categories.size()); i < end; ++i) { if (senderCategory == m_categories.at(i)) { QModelIndex index = this->index(i); emit dataChanged(index, index, QVector<int>({ Qt::DecorationRole })); } } } } // namespace QtUtilities 0707010000004C000081A4000000000000000000000001636534C9000007F7000000000000000000000000000000000000003100000000qtutilities/settingsdialog/optioncategorymodel.h#ifndef DIALOGS_OPTIONCATEGORYMODEL_H #define DIALOGS_OPTIONCATEGORYMODEL_H #include "../global.h" #include <QAbstractListModel> #include <QList> namespace QtUtilities { class OptionPage; class OptionCategory; class QT_UTILITIES_EXPORT OptionCategoryModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(QList<OptionCategory *> categories READ categories WRITE setCategories) public: explicit OptionCategoryModel(QObject *parent = nullptr); explicit OptionCategoryModel(const QList<OptionCategory *> &categories, QObject *parent = nullptr); ~OptionCategoryModel() override; const QList<OptionCategory *> &categories() const; OptionCategory *category(const QModelIndex &index) const; OptionCategory *category(int row) const; void setCategories(const QList<OptionCategory *> &categories); int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private Q_SLOTS: void categoryChangedName(); void categoryChangedIcon(); private: QList<OptionCategory *> m_categories; }; /*! * \brief Returns the categories. * \sa OptionCategoryModel::category() * \sa OptionCategoryModel::setCategories() */ inline const QList<OptionCategory *> &OptionCategoryModel::categories() const { return m_categories; } /*! * \brief Returns the category for the specified model \a index. * \sa OptionCategoryModel::categories() * \sa OptionCategoryModel::setCategories() */ inline OptionCategory *OptionCategoryModel::category(const QModelIndex &index) const { return (index.isValid()) ? category(index.row()) : nullptr; } /*! * \brief Returns the category for the specified \a row. * \sa OptionCategoryModel::categories() * \sa OptionCategoryModel::setCategories() */ inline OptionCategory *OptionCategoryModel::category(int row) const { return row < m_categories.size() ? m_categories.at(row) : nullptr; } } // namespace QtUtilities #endif // DIALOGS_OPTIONCATEGORYMODEL_H 0707010000004D000081A4000000000000000000000001636534C900000E5F000000000000000000000000000000000000002A00000000qtutilities/settingsdialog/optionpage.cpp#include "./optionpage.h" #include <QCheckBox> #include <QEvent> #include <QGroupBox> #include <QLabel> #include <QPushButton> #include <QRadioButton> #include <utility> namespace QtUtilities { /*! * \class OptionPage * \brief The OptionPage class is the base class for SettingsDialog pages. * * The specified \a parentWindow might be used by some implementations as parent * when showing dialogs. */ /*! * \brief Constructs a option page. */ OptionPage::OptionPage(QWidget *parentWindow) : m_parentWindow(parentWindow) , m_shown(false) , m_keywordsInitialized(false) { } /*! * \brief Destroys the option page. */ OptionPage::~OptionPage() { } /*! * \brief Returns the widget for the option page. * * If the widget has not been constructed yet, a new widget will be * constructed using the OptionPage::setupWidget() method and the * current configuration is applied. * * The option page keeps ownership over the returned widget. */ QWidget *OptionPage::widget() { if (!m_widget) { m_widget.reset(setupWidget()); // ensure widget has been created } if (!m_shown) { m_shown = true; reset(); // show current configuration if not shown yet } return m_widget.get(); } /*! * \brief Returns whether the pages matches the specified * \a searchKeyWord. */ bool OptionPage::matches(const QString &searchKeyWord) { if (searchKeyWord.isEmpty()) { return true; } if (!m_keywordsInitialized) { if (!m_widget) { m_widget.reset(setupWidget()); // ensure widget has been created } m_keywords << m_widget->windowTitle(); // find common subwidgets for (const QLabel *label : m_widget->findChildren<QLabel *>()) m_keywords << label->text(); for (const QCheckBox *checkbox : m_widget->findChildren<QCheckBox *>()) m_keywords << checkbox->text(); for (const QRadioButton *checkbox : m_widget->findChildren<QRadioButton *>()) m_keywords << checkbox->text(); for (const QPushButton *pushButton : m_widget->findChildren<QPushButton *>()) m_keywords << pushButton->text(); for (const QGroupBox *groupBox : m_widget->findChildren<QGroupBox *>()) m_keywords << groupBox->title(); m_keywordsInitialized = true; } for (const QString &keyword : std::as_const(m_keywords)) if (keyword.contains(searchKeyWord, Qt::CaseInsensitive)) return true; return false; } /*! * \brief Emits the paletteChanged() signal. */ bool OptionPageWidget::event(QEvent *event) { switch (event->type()) { case QEvent::PaletteChange: emit paletteChanged(); break; default:; } return QWidget::event(event); } /*! * \fn OptionPage::apply() * \brief Applies altered settings. * \remarks * The SettingsDialog and any other classes/functions of this library will not call * this method if the option page has not been shown yet. Hence it is (no longer) necessary * to use OptionPage::hasBeenShown() to check whether the page has been initialized * yet. */ /*! * \fn OptionPage::reset() * \brief Discards altered settings and resets relevant widgets. * \remarks * The SettingsDialog and any other classes/functions of this library will not call * this method if the option page has not been shown yet. Hence it is (no longer) necessary * to use OptionPage::hasBeenShown() to check whether the page has been initialized * yet. */ /*! * \fn OptionPage::setupWidget() * \brief Creates the widget for the page. Called on the first invocation of * widget(). */ } // namespace QtUtilities 0707010000004E000081A4000000000000000000000001636534C90000416B000000000000000000000000000000000000002800000000qtutilities/settingsdialog/optionpage.h#ifndef DIALOGS_OPTIONSPAGE_H #define DIALOGS_OPTIONSPAGE_H #include "../global.h" #include <QObject> #include <QWidget> #include <memory> namespace QtUtilities { class SettingsDialog; class QT_UTILITIES_EXPORT OptionPageWidget : public QWidget { Q_OBJECT public: explicit OptionPageWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); Q_SIGNALS: void paletteChanged(); protected: bool event(QEvent *) override; }; inline OptionPageWidget::OptionPageWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { } class QT_UTILITIES_EXPORT OptionPage { friend class SettingsDialog; public: explicit OptionPage(QWidget *parentWindow = nullptr); virtual ~OptionPage(); QWidget *parentWindow() const; QWidget *widget(); bool hasBeenShown() const; virtual bool apply() = 0; virtual void reset() = 0; bool matches(const QString &searchKeyWord); const QStringList &errors() const; protected: virtual QWidget *setupWidget() = 0; QStringList &errors(); private: std::unique_ptr<QWidget> m_widget; QWidget *m_parentWindow; bool m_shown; bool m_keywordsInitialized; QStringList m_keywords; QStringList m_errors; }; /*! * \brief Returns the parent window of the option page. */ inline QWidget *OptionPage::parentWindow() const { return m_parentWindow; } /*! * \brief Returns an indication whether the option page has been shown yet. * \remarks If this is true, the method OptionPage::setupWidget() has already * been called. */ inline bool OptionPage::hasBeenShown() const { return m_widget != nullptr && m_shown; } /*! * \brief Returns the errors which haven been occurred when applying the * changes. */ inline const QStringList &OptionPage::errors() const { return m_errors; } /*! * \brief Returns the errors which haven been occurred when applying the * changes. * * Error messages should be added when implementing apply() and something goes * wrong. * In this case, apply() should return false. */ inline QStringList &OptionPage::errors() { return m_errors; } /*! * \class Dialogs::UiFileBasedOptionPage * \brief The UiFileBasedOptionPage class is the base class for SettingsDialog * pages using UI files * to describe the widget tree. * * \tparam UiClass Specifies the UI class generated by uic. */ template <class UiClass> class QT_UTILITIES_EXPORT UiFileBasedOptionPage : public OptionPage { public: explicit UiFileBasedOptionPage(QWidget *parentWindow = nullptr); ~UiFileBasedOptionPage() override; bool apply() override = 0; void reset() override = 0; protected: QWidget *setupWidget() override; UiClass *ui(); private: std::unique_ptr<UiClass> m_ui; }; /*! * \brief Constructs a new UI file based option page. */ template <class UiClass> UiFileBasedOptionPage<UiClass>::UiFileBasedOptionPage(QWidget *parentWindow) : OptionPage(parentWindow) { } /*! * \brief Destroys the option page. */ template <class UiClass> UiFileBasedOptionPage<UiClass>::~UiFileBasedOptionPage() { } /*! * \brief Inflates the widget for the option page using the UI class. */ template <class UiClass> QWidget *UiFileBasedOptionPage<UiClass>::setupWidget() { QWidget *widget = new OptionPageWidget(); if (!m_ui) { m_ui.reset(new UiClass); } m_ui->setupUi(widget); return widget; } /*! * \brief Provides the derived class access to the UI class. */ template <class UiClass> inline UiClass *UiFileBasedOptionPage<UiClass>::ui() { return m_ui.get(); } } // namespace QtUtilities /*! * \brief Declares the base class for a class inheriting from Dialogs::OptionPage. */ #define BEGIN_DECLARE_TYPEDEF_OPTION_PAGE(SomeClass) using SomeClass##Base = ::QtUtilities::OptionPage; /*! * \brief Declares a class inheriting from Dialogs::OptionPage in a convenient * way. * \remarks Must be closed with END_DECLARE_OPTION_PAGE. */ #define BEGIN_DECLARE_OPTION_PAGE(SomeClass) \ BEGIN_DECLARE_TYPEDEF_OPTION_PAGE(SomeClass) \ class QT_UTILITIES_EXPORT SomeClass : public ::QtUtilities::OptionPage { \ public: \ explicit SomeClass(QWidget *parentWidget = nullptr); \ ~SomeClass() override; \ bool apply() override; \ void reset() override; \ \ private: /*! * \brief Declares a class inheriting from Dialogs::OptionPage in a convenient * way. * \remarks Must be closed with END_DECLARE_OPTION_PAGE. */ #define BEGIN_DECLARE_OPTION_PAGE_CUSTOM_CTOR(SomeClass) \ BEGIN_DECLARE_TYPEDEF_OPTION_PAGE(SomeClass) \ class QT_UTILITIES_EXPORT SomeClass : public ::QtUtilities::OptionPage { \ public: \ ~SomeClass() override; \ bool apply() override; \ void reset() override; \ \ private: /*! * \brief Declares the base class for a class inheriting from Dialogs::UiFileBasedOptionPage. */ #define BEGIN_DECLARE_TYPEDEF_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ namespace Ui { \ class SomeClass; \ } \ using SomeClass##Base = ::QtUtilities::UiFileBasedOptionPage<Ui::SomeClass>; /*! * \brief Declares a class inheriting from Dialogs::UiFileBasedOptionPage in a * convenient way. * \remarks Must be closed with END_DECLARE_OPTION_PAGE. */ #define BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(SomeClass) \ BEGIN_DECLARE_TYPEDEF_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ class QT_UTILITIES_EXPORT SomeClass : public ::QtUtilities::UiFileBasedOptionPage<Ui::SomeClass> { \ public: \ ~SomeClass() override; \ bool apply() override; \ void reset() override; \ \ private: /*! * \brief Declares a class inheriting from Dialogs::UiFileBasedOptionPage in a * convenient way. * \remarks Must be closed with END_DECLARE_OPTION_PAGE. */ #define BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(SomeClass) \ public: \ explicit SomeClass(QWidget *parentWidget = nullptr); \ \ private: /*! * \brief Must be used after BEGIN_DECLARE_OPTION_PAGE and * BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE. */ #define END_DECLARE_OPTION_PAGE \ } \ ; /*! * \brief Instantiates a class declared with * BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE in a convenient way. * \remarks Might be required when the class is used by another application. */ #define INSTANTIATE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ namespace QtUtilities { \ template class UiFileBasedOptionPage<Ui::SomeClass>; \ } /*! * \brief Instantiates a class declared with * BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE inside a given namespace in a * convenient way. * \remarks Might be required when the class is used by another application. */ #define INSTANTIATE_UI_FILE_BASED_OPTION_PAGE_NS(SomeNamespace, SomeClass) \ namespace QtUtilities { \ template class UiFileBasedOptionPage<::SomeNamespace::Ui::SomeClass>; \ } /*! * \brief Declares external instantiation of class declared with * BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE in a convenient way. * \remarks Might be required when the class comes from an external library. */ #define DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ namespace QtUtilities { \ namespace Ui { \ class SomeClass; \ } \ extern template class UiFileBasedOptionPage<Ui::SomeClass>; \ } /*! * \brief Declares external instantiation of class declared with * BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE inside a given namespace in a * convenient way. * \remarks Might be required when the class comes from an external library. */ #define DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE_NS(SomeNamespace, SomeClass) \ namespace SomeNamespace { \ namespace Ui { \ class SomeClass; \ } \ } \ namespace QtUtilities { \ extern template class UiFileBasedOptionPage<::SomeNamespace::Ui::SomeClass>; \ } /*! * \brief Declares the method setupWidget() in a convenient way. * \remarks Can be used between BEGIN_DECLARE_OPTION_PAGE and * END_DECLARE_OPTION_PAGE. */ #define DECLARE_SETUP_WIDGETS \ protected: \ QWidget *setupWidget() override; \ \ private: /*! * \brief Declares a class inheriting from Dialogs::OptionPage in a convenient * way. * \remarks Doesn't allow to declare additional class members. */ #define DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ END_DECLARE_OPTION_PAGE /*! * \brief Declares a class inheriting from Dialogs::OptionPage in a convenient * way. * \remarks Doesn't allow to declare additional class members. */ #define DECLARE_OPTION_PAGE(SomeClass) \ BEGIN_DECLARE_OPTION_PAGE(SomeClass) \ DECLARE_SETUP_WIDGETS \ END_DECLARE_OPTION_PAGE /*! * \brief Declares a class inheriting from Dialogs::UiFileBasedOptionPage in a * convenient way. * \remarks Doesn't allow to declare additional class members. */ #define DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_SETUP(SomeClass) \ BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE(SomeClass) \ DECLARE_SETUP_WIDGETS \ END_DECLARE_OPTION_PAGE #endif // DIALOGS_OPTIONSPAGE_H 0707010000004F000081A4000000000000000000000001636534C9000001F5000000000000000000000000000000000000003200000000qtutilities/settingsdialog/optionpage.ui.template<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>NS::Class</class> <widget class="QWidget" name="NS::Class"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>345</width> <height>146</height> </rect> </property> <property name="windowTitle"> <string>Window title</string> </property> <property name="styleSheet"> <string notr="true">QGroupBox { font-weight: bold };</string> </property> </widget> <resources/> <connections/> </ui> 07070100000050000081A4000000000000000000000001636534C9000027B4000000000000000000000000000000000000003500000000qtutilities/settingsdialog/qtappearanceoptionpage.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::QtAppearanceOptionPage</class> <widget class="QWidget" name="QtUtilities::QtAppearanceOptionPage"> <property name="windowTitle"> <string>Appearance</string> </property> <property name="windowIcon"> <iconset theme="preferences-desktop-icons"> <normaloff>.</normaloff>.</iconset> </property> <property name="styleSheet"> <string notr="true">QGroupBox { font-weight: bold };</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="2"> <widget class="QLabel" name="useDefaultLabel"> <property name="text"> <string>Use system default</string> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="fontLabel"> <property name="minimumSize"> <size> <width>60</width> <height>0</height> </size> </property> <property name="text"> <string>Font</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="1" column="1"> <layout class="QHBoxLayout" name="fontHorizontalLayout"> <property name="spacing"> <number>4</number> </property> <item> <widget class="QFontComboBox" name="fontComboBox"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> </widget> </item> <item> <widget class="QPushButton" name="fontPushButton"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>More options ...</string> </property> </widget> </item> </layout> </item> <item row="1" column="2" alignment="Qt::AlignHCenter"> <widget class="QCheckBox" name="fontCheckBox"> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="2" column="0"> <widget class="QLabel" name="widgetStyleLabel"> <property name="minimumSize"> <size> <width>60</width> <height>0</height> </size> </property> <property name="text"> <string>Widget style</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="2" column="1"> <widget class="QComboBox" name="widgetStyleComboBox"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="editable"> <bool>true</bool> </property> </widget> </item> <item row="2" column="2" alignment="Qt::AlignHCenter"> <widget class="QCheckBox" name="widgetStyleCheckBox"> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="3" column="0"> <widget class="QLabel" name="styleSheetLabel"> <property name="minimumSize"> <size> <width>60</width> <height>0</height> </size> </property> <property name="text"> <string>Style sheet</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="3" column="1"> <widget class="QtUtilities::PathSelection" name="styleSheetPathSelection" native="true"> <property name="enabled"> <bool>false</bool> </property> </widget> </item> <item row="3" column="2" alignment="Qt::AlignHCenter"> <widget class="QCheckBox" name="styleSheetCheckBox"> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="4" column="0"> <widget class="QLabel" name="paletteLabel"> <property name="minimumSize"> <size> <width>60</width> <height>0</height> </size> </property> <property name="text"> <string>Palette</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="4" column="1"> <widget class="QToolButton" name="paletteToolButton"> <property name="enabled"> <bool>false</bool> </property> <property name="text"> <string>select ...</string> </property> </widget> </item> <item row="4" column="2" alignment="Qt::AlignHCenter"> <widget class="QCheckBox" name="paletteCheckBox"> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="5" column="0"> <widget class="QLabel" name="iconThemeLabel"> <property name="minimumSize"> <size> <width>60</width> <height>0</height> </size> </property> <property name="text"> <string>Icon theme</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="5" column="1"> <widget class="QComboBox" name="iconThemeComboBox"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="editable"> <bool>true</bool> </property> </widget> </item> <item row="5" column="2" alignment="Qt::AlignHCenter"> <widget class="QCheckBox" name="iconThemeCheckBox"> <property name="checked"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="label"> <property name="font"> <font> <weight>75</weight> <bold>true</bold> </font> </property> <property name="text"> <string>These settings take effect after restarting the application. These settings might be overwritten by your Qt platform integration plugin and hence have no effect.</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> </layout> </widget> <customwidgets> <customwidget> <class>QtUtilities::PathSelection</class> <extends>QWidget</extends> <header>widgets/pathselection.h</header> <container>1</container> </customwidget> </customwidgets> <resources/> <connections> <connection> <sender>fontCheckBox</sender> <signal>toggled(bool)</signal> <receiver>fontComboBox</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>462</x> <y>38</y> </hint> <hint type="destinationlabel"> <x>193</x> <y>39</y> </hint> </hints> </connection> <connection> <sender>fontCheckBox</sender> <signal>toggled(bool)</signal> <receiver>fontPushButton</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>462</x> <y>38</y> </hint> <hint type="destinationlabel"> <x>365</x> <y>39</y> </hint> </hints> </connection> <connection> <sender>widgetStyleCheckBox</sender> <signal>toggled(bool)</signal> <receiver>widgetStyleComboBox</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>462</x> <y>71</y> </hint> <hint type="destinationlabel"> <x>240</x> <y>72</y> </hint> </hints> </connection> <connection> <sender>paletteCheckBox</sender> <signal>toggled(bool)</signal> <receiver>paletteToolButton</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>462</x> <y>137</y> </hint> <hint type="destinationlabel"> <x>99</x> <y>138</y> </hint> </hints> </connection> <connection> <sender>styleSheetCheckBox</sender> <signal>toggled(bool)</signal> <receiver>styleSheetPathSelection</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>378</x> <y>119</y> </hint> <hint type="destinationlabel"> <x>198</x> <y>119</y> </hint> </hints> </connection> <connection> <sender>iconThemeCheckBox</sender> <signal>toggled(bool)</signal> <receiver>iconThemeComboBox</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>412</x> <y>173</y> </hint> <hint type="destinationlabel"> <x>216</x> <y>173</y> </hint> </hints> </connection> </connections> </ui> 07070100000051000081A4000000000000000000000001636534C900000AE9000000000000000000000000000000000000002E00000000qtutilities/settingsdialog/qtenvoptionpage.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::QtEnvOptionPage</class> <widget class="QWidget" name="QtUtilities::QtEnvOptionPage"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>515</width> <height>243</height> </rect> </property> <property name="windowTitle"> <string>Environment/paths</string> </property> <property name="windowIcon"> <iconset theme="system-search"/> </property> <property name="styleSheet"> <string notr="true">QGroupBox { font-weight: bold };</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> <widget class="QLabel" name="pluginDirLabel"> <property name="text"> <string>Additional plugin directory</string> </property> </widget> </item> <item row="0" column="1"> <widget class="QtUtilities::PathSelection" name="pluginPathSelection" native="true"/> </item> <item row="1" column="0"> <widget class="QLabel" name="iconThemePathLabel"> <property name="text"> <string>Additional icon theme search path</string> </property> </widget> </item> <item row="1" column="1"> <widget class="QtUtilities::PathSelection" name="iconThemeSearchPathSelection" native="true"/> </item> <item row="2" column="0"> <widget class="QLabel" name="translationsSearchPathLabel"> <property name="text"> <string>Additional translation search path</string> </property> </widget> </item> <item row="2" column="1"> <widget class="QtUtilities::PathSelection" name="translationPathSelection" native="true"/> </item> </layout> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="label"> <property name="font"> <font> <weight>75</weight> <bold>true</bold> </font> </property> <property name="text"> <string>These settings take effect after restarting the application.</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> </widget> </item> </layout> </widget> <customwidgets> <customwidget> <class>QtUtilities::PathSelection</class> <extends>QWidget</extends> <header>widgets/pathselection.h</header> <container>1</container> </customwidget> </customwidgets> <resources/> <connections/> </ui> 07070100000052000081A4000000000000000000000001636534C900000D40000000000000000000000000000000000000003300000000qtutilities/settingsdialog/qtlanguageoptionpage.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::QtLanguageOptionPage</class> <widget class="QWidget" name="QtUtilities::QtLanguageOptionPage"> <property name="windowTitle"> <string>Localization</string> </property> <property name="windowIcon"> <iconset theme="preferences-desktop-locale"> <normaloff>.</normaloff>.</iconset> </property> <property name="styleSheet"> <string notr="true">QGroupBox { font-weight: bold };</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <layout class="QGridLayout" name="gridLayout"> <item row="1" column="1"> <widget class="QComboBox" name="localeComboBox"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="editable"> <bool>true</bool> </property> </widget> </item> <item row="1" column="0"> <widget class="QLabel" name="localeLabel"> <property name="minimumSize"> <size> <width>60</width> <height>0</height> </size> </property> <property name="text"> <string>Locale</string> </property> <property name="alignment"> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> </property> </widget> </item> <item row="0" column="2"> <widget class="QLabel" name="defaultLabel"> <property name="text"> <string>Use system default</string> </property> </widget> </item> <item row="1" column="2" alignment="Qt::AlignHCenter"> <widget class="QCheckBox" name="localeCheckBox"> <property name="checked"> <bool>true</bool> </property> </widget> </item> <item row="2" column="1"> <widget class="QLabel" name="languageLabel"> <property name="text"> <string/> </property> <property name="textFormat"> <enum>Qt::RichText</enum> </property> </widget> </item> </layout> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> <item> <widget class="QLabel" name="label"> <property name="font"> <font> <weight>75</weight> <bold>true</bold> </font> </property> <property name="text"> <string>These settings take effect after restarting the application.</string> </property> <property name="textFormat"> <enum>Qt::PlainText</enum> </property> </widget> </item> </layout> </widget> <resources/> <connections> <connection> <sender>localeCheckBox</sender> <signal>toggled(bool)</signal> <receiver>localeComboBox</receiver> <slot>setDisabled(bool)</slot> <hints> <hint type="sourcelabel"> <x>159</x> <y>39</y> </hint> <hint type="destinationlabel"> <x>75</x> <y>39</y> </hint> </hints> </connection> </connections> </ui> 07070100000053000081A4000000000000000000000001636534C900003E4E000000000000000000000000000000000000002A00000000qtutilities/settingsdialog/qtsettings.cpp#include "./qtsettings.h" #include "./optioncategory.h" #include "./optioncategoryfiltermodel.h" #include "./optioncategorymodel.h" #include "./optionpage.h" #include "../paletteeditor/paletteeditor.h" #include "../widgets/clearlineedit.h" #include "../resources/resources.h" #include "ui_qtappearanceoptionpage.h" #include "ui_qtenvoptionpage.h" #include "ui_qtlanguageoptionpage.h" #include <QDir> #include <QFileDialog> #include <QFontDialog> #include <QIcon> #include <QSettings> #include <QStringBuilder> #include <QStyleFactory> #include <iostream> #include <memory> using namespace std; namespace QtUtilities { struct QtSettingsData { QtSettingsData(); QFont font; QPalette palette; QString widgetStyle; QString styleSheetPath; QString iconTheme; QLocale defaultLocale; QString localeName; QString additionalPluginDirectory; QString additionalIconThemeSearchPath; bool customFont; bool customPalette; bool customWidgetStyle; bool customStyleSheet; bool customIconTheme; bool customLocale; }; inline QtSettingsData::QtSettingsData() : iconTheme(QIcon::themeName()) , localeName(defaultLocale.name()) , customFont(false) , customPalette(false) , customWidgetStyle(false) , customStyleSheet(false) , customIconTheme(false) , customLocale(false) { } /*! * \brief Creates a new settings object. * \remarks Settings are not restored automatically. Instead, some values (font, * widget style, ...) are initialized * from the current Qt configuration. These values are considered as * system-default. */ QtSettings::QtSettings() : m_d(make_unique<QtSettingsData>()) { } /*! * \brief Destroys the settings object. * \remarks Unlike QSettings not explicitly saved settings are not saved * automatically. */ QtSettings::~QtSettings() { } /*! * \brief Returns whether a custom font is set. */ bool QtSettings::hasCustomFont() const { return m_d->customFont; } /*! * \brief Restores the settings from the specified QSettings object. * \remarks The restored values are not applied automatically (except * translation path). * \sa apply(), save() */ void QtSettings::restore(QSettings &settings) { settings.beginGroup(QStringLiteral("qt")); m_d->font.fromString(settings.value(QStringLiteral("font")).toString()); m_d->customFont = settings.value(QStringLiteral("customfont"), false).toBool(); m_d->palette = settings.value(QStringLiteral("palette")).value<QPalette>(); m_d->customPalette = settings.value(QStringLiteral("custompalette"), false).toBool(); m_d->widgetStyle = settings.value(QStringLiteral("widgetstyle"), m_d->widgetStyle).toString(); m_d->customWidgetStyle = settings.value(QStringLiteral("customwidgetstyle"), false).toBool(); m_d->styleSheetPath = settings.value(QStringLiteral("stylesheetpath"), m_d->styleSheetPath).toString(); m_d->customStyleSheet = settings.value(QStringLiteral("customstylesheet"), false).toBool(); m_d->iconTheme = settings.value(QStringLiteral("icontheme"), m_d->iconTheme).toString(); m_d->customIconTheme = settings.value(QStringLiteral("customicontheme"), false).toBool(); m_d->localeName = settings.value(QStringLiteral("locale"), m_d->localeName).toString(); m_d->customLocale = settings.value(QStringLiteral("customlocale"), false).toBool(); m_d->additionalPluginDirectory = settings.value(QStringLiteral("plugindir")).toString(); m_d->additionalIconThemeSearchPath = settings.value(QStringLiteral("iconthemepath")).toString(); TranslationFiles::additionalTranslationFilePath() = settings.value(QStringLiteral("trpath")).toString(); settings.endGroup(); } /*! * \brief Saves the settings to the specified QSettings object. */ void QtSettings::save(QSettings &settings) const { settings.beginGroup(QStringLiteral("qt")); settings.setValue(QStringLiteral("font"), QVariant(m_d->font.toString())); settings.setValue(QStringLiteral("customfont"), m_d->customFont); settings.setValue(QStringLiteral("palette"), QVariant(m_d->palette)); settings.setValue(QStringLiteral("custompalette"), m_d->customPalette); settings.setValue(QStringLiteral("widgetstyle"), m_d->widgetStyle); settings.setValue(QStringLiteral("customwidgetstyle"), m_d->customWidgetStyle); settings.setValue(QStringLiteral("stylesheetpath"), m_d->styleSheetPath); settings.setValue(QStringLiteral("customstylesheet"), m_d->customStyleSheet); settings.setValue(QStringLiteral("icontheme"), m_d->iconTheme); settings.setValue(QStringLiteral("customicontheme"), m_d->customIconTheme); settings.setValue(QStringLiteral("locale"), m_d->localeName); settings.setValue(QStringLiteral("customlocale"), m_d->customLocale); settings.setValue(QStringLiteral("plugindir"), m_d->additionalPluginDirectory); settings.setValue(QStringLiteral("iconthemepath"), m_d->additionalIconThemeSearchPath); settings.setValue(QStringLiteral("trpath"), QVariant(TranslationFiles::additionalTranslationFilePath())); settings.endGroup(); } /*! * \brief Applies the current configuration. * \remarks * - Some settings take only affect after restarting the application. * - QApplication/QGuiApplication must be instantiated before calling this * method. * - Hence it makes most sense to call this directly after instantiating * QApplication/QGuiApplication. */ void QtSettings::apply() { // read style sheet QString styleSheet; if (m_d->customStyleSheet && !m_d->styleSheetPath.isEmpty()) { QFile file(m_d->styleSheetPath); if (!file.open(QFile::ReadOnly)) { cerr << "Unable to open the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\"." << endl; } styleSheet.append(file.readAll()); if (file.error() != QFile::NoError) { cerr << "Unable to read the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\"." << endl; } } // apply appearance if (m_d->customFont) { QGuiApplication::setFont(m_d->font); } if (m_d->customWidgetStyle) { QApplication::setStyle(m_d->widgetStyle); } if (!styleSheet.isEmpty()) { if (auto *qapp = qobject_cast<QApplication *>(QApplication::instance())) { qapp->setStyleSheet(styleSheet); } else { cerr << "Unable to apply the specified stylesheet \"" << m_d->styleSheetPath.toLocal8Bit().data() << "\" because no QApplication has been instantiated." << endl; } } if (m_d->customPalette) { QGuiApplication::setPalette(m_d->palette); } if (m_d->customIconTheme) { QIcon::setThemeName(m_d->iconTheme); } // apply locale QLocale::setDefault(m_d->customLocale ? QLocale(m_d->localeName) : m_d->defaultLocale); // apply environment if (m_d->additionalPluginDirectory.isEmpty()) { QCoreApplication::addLibraryPath(m_d->additionalPluginDirectory); } if (!m_d->additionalIconThemeSearchPath.isEmpty()) { QIcon::setThemeSearchPaths(QIcon::themeSearchPaths() << m_d->additionalIconThemeSearchPath); } } /*! * \brief Returns a new OptionCatecory containing all Qt related option pages. * \remarks * - The QtSettings instance does not keep the ownership over the returned * category. * - The pages of the returned category require the QtSettings instance which * hence must be present as long as all pages are destroyed. */ OptionCategory *QtSettings::category() { auto *category = new OptionCategory; category->setDisplayName(QCoreApplication::translate("QtGui::QtOptionCategory", "Qt")); category->setIcon(QIcon::fromTheme(QStringLiteral("qtcreator"), QIcon(QStringLiteral(":/qtutilities/icons/hicolor/48x48/apps/qtcreator.svg")))); category->assignPages({ new QtAppearanceOptionPage(*m_d), new QtLanguageOptionPage(*m_d), new QtEnvOptionPage(*m_d) }); return category; } QtAppearanceOptionPage::QtAppearanceOptionPage(QtSettingsData &settings, QWidget *parentWidget) : QtAppearanceOptionPageBase(parentWidget) , m_settings(settings) , m_fontDialog(nullptr) { } QtAppearanceOptionPage::~QtAppearanceOptionPage() { } bool QtAppearanceOptionPage::apply() { m_settings.font = ui()->fontComboBox->currentFont(); m_settings.customFont = !ui()->fontCheckBox->isChecked(); m_settings.widgetStyle = ui()->widgetStyleComboBox->currentText(); m_settings.customWidgetStyle = !ui()->widgetStyleCheckBox->isChecked(); m_settings.styleSheetPath = ui()->styleSheetPathSelection->lineEdit()->text(); m_settings.customStyleSheet = !ui()->styleSheetCheckBox->isChecked(); m_settings.palette = ui()->paletteToolButton->palette(); m_settings.customPalette = !ui()->paletteCheckBox->isChecked(); m_settings.iconTheme = ui()->iconThemeComboBox->currentIndex() != -1 ? ui()->iconThemeComboBox->currentData().toString() : ui()->iconThemeComboBox->currentText(); m_settings.customIconTheme = !ui()->iconThemeCheckBox->isChecked(); return true; } void QtAppearanceOptionPage::reset() { ui()->fontComboBox->setCurrentFont(m_settings.font); ui()->fontCheckBox->setChecked(!m_settings.customFont); ui()->widgetStyleComboBox->setCurrentText( m_settings.widgetStyle.isEmpty() ? (QApplication::style() ? QApplication::style()->objectName() : QString()) : m_settings.widgetStyle); ui()->widgetStyleCheckBox->setChecked(!m_settings.customWidgetStyle); ui()->styleSheetPathSelection->lineEdit()->setText(m_settings.styleSheetPath); ui()->styleSheetCheckBox->setChecked(!m_settings.customStyleSheet); ui()->paletteToolButton->setPalette(m_settings.palette); ui()->paletteCheckBox->setChecked(!m_settings.customPalette); int iconThemeIndex = ui()->iconThemeComboBox->findData(m_settings.iconTheme); if (iconThemeIndex != -1) { ui()->iconThemeComboBox->setCurrentIndex(iconThemeIndex); } else { ui()->iconThemeComboBox->setCurrentText(m_settings.iconTheme); } ui()->iconThemeCheckBox->setChecked(!m_settings.customIconTheme); } QWidget *QtAppearanceOptionPage::setupWidget() { // call base implementation first, so ui() is available auto *widget = QtAppearanceOptionPageBase::setupWidget(); // setup widget style selection ui()->widgetStyleComboBox->addItems(QStyleFactory::keys()); // setup style sheet selection ui()->styleSheetPathSelection->provideCustomFileMode(QFileDialog::ExistingFile); // setup font selection QObject::connect(ui()->fontPushButton, &QPushButton::clicked, [this] { if (!m_fontDialog) { m_fontDialog = new QFontDialog(this->widget()); m_fontDialog->setCurrentFont(ui()->fontComboBox->font()); QObject::connect(m_fontDialog, &QFontDialog::fontSelected, ui()->fontComboBox, &QFontComboBox::setCurrentFont); QObject::connect(ui()->fontComboBox, &QFontComboBox::currentFontChanged, m_fontDialog, &QFontDialog::setCurrentFont); } m_fontDialog->show(); }); // setup palette selection QObject::connect(ui()->paletteToolButton, &QToolButton::clicked, [this] { ui()->paletteToolButton->setPalette(PaletteEditor::getPalette(this->widget(), ui()->paletteToolButton->palette())); }); // setup icon theme selection const QStringList searchPaths = QIcon::themeSearchPaths() << QStringLiteral("/usr/share/icons/"); for (const QString &searchPath : searchPaths) { const auto dir = QDir(searchPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name); for (const QString &iconTheme : dir) { const int existingItemIndex = ui()->iconThemeComboBox->findData(iconTheme); QFile indexFile(searchPath % QChar('/') % iconTheme % QStringLiteral("/index.theme")); QByteArray index; if (indexFile.open(QFile::ReadOnly) && !(index = indexFile.readAll()).isEmpty()) { const auto iconThemeSection = index.indexOf("[Icon Theme]"); const auto nameStart = index.indexOf("Name=", iconThemeSection != -1 ? iconThemeSection : 0); if (nameStart != -1) { auto nameLength = index.indexOf("\n", nameStart) - nameStart - 5; if (nameLength > 0) { QString displayName = index.mid(nameStart + 5, nameLength); if (displayName != iconTheme) { displayName += QChar(' ') % QChar('(') % iconTheme % QChar(')'); } if (existingItemIndex != -1) { ui()->iconThemeComboBox->setItemText(existingItemIndex, displayName); } else { ui()->iconThemeComboBox->addItem(displayName, iconTheme); } continue; } } } if (existingItemIndex == -1) { ui()->iconThemeComboBox->addItem(iconTheme, iconTheme); } } } return widget; } QtLanguageOptionPage::QtLanguageOptionPage(QtSettingsData &settings, QWidget *parentWidget) : QtLanguageOptionPageBase(parentWidget) , m_settings(settings) { } QtLanguageOptionPage::~QtLanguageOptionPage() { } bool QtLanguageOptionPage::apply() { m_settings.localeName = ui()->localeComboBox->currentText(); m_settings.customLocale = !ui()->localeCheckBox->isChecked(); return true; } void QtLanguageOptionPage::reset() { ui()->localeComboBox->setCurrentText(m_settings.localeName); ui()->localeCheckBox->setChecked(!m_settings.customLocale); } QWidget *QtLanguageOptionPage::setupWidget() { // call base implementation first, so ui() is available auto *widget = QtLanguageOptionPageBase::setupWidget(); // add all available locales to combo box auto *localeComboBox = ui()->localeComboBox; const auto locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript, QLocale::AnyCountry); for (const QLocale &locale : locales) { localeComboBox->addItem(locale.name()); } auto *languageLabel = ui()->languageLabel; QObject::connect(ui()->localeComboBox, &QComboBox::currentTextChanged, [languageLabel, localeComboBox] { const QLocale selectedLocale(localeComboBox->currentText()); const QLocale currentLocale; languageLabel->setText(QCoreApplication::translate("QtGui::QtLanguageOptionPage", "recognized by Qt as") % QStringLiteral(" <i>") % currentLocale.languageToString(selectedLocale.language()) % QChar(',') % QChar(' ') % currentLocale.countryToString(selectedLocale.country()) % QStringLiteral("</i>")); }); return widget; } QtEnvOptionPage::QtEnvOptionPage(QtSettingsData &settings, QWidget *parentWidget) : QtEnvOptionPageBase(parentWidget) , m_settings(settings) { } QtEnvOptionPage::~QtEnvOptionPage() { } bool QtEnvOptionPage::apply() { m_settings.additionalPluginDirectory = ui()->pluginPathSelection->lineEdit()->text(); m_settings.additionalIconThemeSearchPath = ui()->iconThemeSearchPathSelection->lineEdit()->text(); TranslationFiles::additionalTranslationFilePath() = ui()->translationPathSelection->lineEdit()->text(); return true; } void QtEnvOptionPage::reset() { ui()->pluginPathSelection->lineEdit()->setText(m_settings.additionalPluginDirectory); ui()->iconThemeSearchPathSelection->lineEdit()->setText(m_settings.additionalIconThemeSearchPath); ui()->translationPathSelection->lineEdit()->setText(TranslationFiles::additionalTranslationFilePath()); } } // namespace QtUtilities INSTANTIATE_UI_FILE_BASED_OPTION_PAGE(QtAppearanceOptionPage) INSTANTIATE_UI_FILE_BASED_OPTION_PAGE(QtLanguageOptionPage) INSTANTIATE_UI_FILE_BASED_OPTION_PAGE(QtEnvOptionPage) 07070100000054000081A4000000000000000000000001636534C900000649000000000000000000000000000000000000002800000000qtutilities/settingsdialog/qtsettings.h#ifndef DIALOGS_QT_UTILITIES_QTSETTINGS_H #define DIALOGS_QT_UTILITIES_QTSETTINGS_H #include "./optionpage.h" #include <memory> QT_FORWARD_DECLARE_CLASS(QFontDialog) QT_FORWARD_DECLARE_CLASS(QSettings) namespace QtUtilities { class OptionCategory; struct QtSettingsData; BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(QtAppearanceOptionPage) public: explicit QtAppearanceOptionPage(QtSettingsData &settings, QWidget *parentWidget = nullptr); private: DECLARE_SETUP_WIDGETS QtSettingsData &m_settings; QFontDialog *m_fontDialog; END_DECLARE_OPTION_PAGE BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(QtLanguageOptionPage) public: explicit QtLanguageOptionPage(QtSettingsData &settings, QWidget *parentWidget = nullptr); private: DECLARE_SETUP_WIDGETS QtSettingsData &m_settings; END_DECLARE_OPTION_PAGE BEGIN_DECLARE_UI_FILE_BASED_OPTION_PAGE_CUSTOM_CTOR(QtEnvOptionPage) public: explicit QtEnvOptionPage(QtSettingsData &settings, QWidget *parentWidget = nullptr); private: QtSettingsData &m_settings; END_DECLARE_OPTION_PAGE class QT_UTILITIES_EXPORT QtSettings { public: QtSettings(); ~QtSettings(); void restore(QSettings &settings); void save(QSettings &settings) const; void apply(); bool hasCustomFont() const; OptionCategory *category(); private: std::unique_ptr<QtSettingsData> m_d; }; } // namespace QtUtilities DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE(QtAppearanceOptionPage) DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE(QtLanguageOptionPage) DECLARE_EXTERN_UI_FILE_BASED_OPTION_PAGE(QtEnvOptionPage) #endif // DIALOGS_QT_UTILITIES_QTSETTINGS_H 07070100000055000081A4000000000000000000000001636534C900002B38000000000000000000000000000000000000002E00000000qtutilities/settingsdialog/settingsdialog.cpp#include "./settingsdialog.h" #include "./optioncategory.h" #include "./optioncategoryfiltermodel.h" #include "./optioncategorymodel.h" #include "./optionpage.h" #include "../misc/dialogutils.h" #include "ui_settingsdialog.h" #include <QItemSelectionModel> #include <QMessageBox> #include <QScrollArea> #include <QShowEvent> #include <QStringBuilder> namespace QtUtilities { /*! * \class SettingsDialog * \brief The SettingsDialog class provides a framework for creating settings * dialogs with different categories and subcategories. */ /*! * \brief Constructs a settings dialog. * \param parent Specifies the parent widget. */ SettingsDialog::SettingsDialog(QWidget *parent) : QDialog(parent) , m_ui(new Ui::SettingsDialog) , m_categoryModel(new OptionCategoryModel(this)) , m_categoryFilterModel(new OptionCategoryFilterModel(this)) , m_currentCategory(nullptr) , m_tabBarAlwaysVisible(true) { m_ui->setupUi(this); makeHeading(m_ui->headingLabel); setStyleSheet(dialogStyle()); // setup models m_categoryFilterModel->setSourceModel(m_categoryModel); m_ui->categoriesListView->setModel(m_categoryFilterModel); // connect signals and slots // selection models connect(m_ui->categoriesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &SettingsDialog::currentCategoryChanged); // buttons connect(m_ui->abortPushButton, &QPushButton::clicked, this, &SettingsDialog::reject); connect(m_ui->applyPushButton, &QPushButton::clicked, this, &SettingsDialog::apply); connect(m_ui->okPushButton, &QPushButton::clicked, this, &SettingsDialog::accept); // dialog connect(this, &SettingsDialog::accepted, this, &SettingsDialog::apply); connect(this, &SettingsDialog::rejected, this, &SettingsDialog::reset); // misc connect(m_ui->filterLineEdit, &QLineEdit::textChanged, m_categoryFilterModel, &OptionCategoryFilterModel::setFilterFixedString); connect(m_ui->filterLineEdit, &QLineEdit::textChanged, this, &SettingsDialog::updateTabWidget); } /*! * \brief Destroys the settings dialog. */ SettingsDialog::~SettingsDialog() { } /*! * \brief Sets whether the tab bar is always visible. * \sa SettingsDialog::isTabBarAlwaysVisible() */ void SettingsDialog::setTabBarAlwaysVisible(bool value) { m_tabBarAlwaysVisible = value; if (m_currentCategory) { m_ui->pagesTabWidget->tabBar()->setHidden(!value && m_currentCategory->pages().size() == 1); } } /*! * \brief Returns the category for the specified \a categoryIndex. * * The settings dialog keeps ownership over the returned category. * If no category exists for the specified index a null pointer is returned. */ OptionCategory *SettingsDialog::category(int categoryIndex) const { return m_categoryModel->category(categoryIndex); } /*! * \brief Returns the page for the specified \a categoryIndex and the specified * \a pageIndex. * * The settings dialog keeps ownership over the returned category. * If no page for the specified indices a null pointer is returned. */ OptionPage *SettingsDialog::page(int categoryIndex, int pageIndex) const { if (OptionCategory *const category = this->category(categoryIndex)) { if (pageIndex < category->pages().length()) { return category->pages()[pageIndex]; } } return nullptr; } /*! * \brief Resets all pages before the dialog is shown by the application. */ void SettingsDialog::showEvent(QShowEvent *event) { if (event->spontaneous()) { return; } for (OptionCategory *const category : m_categoryModel->categories()) { for (OptionPage *const page : category->pages()) { if (page->hasBeenShown()) { page->reset(); } } } } /*! * \brief Shows the selected category specified by its model \a index in the * category filter model. * * This private slot is called when * m_ui->categoriesListView->selectionModel()->currentChanged() is emitted. */ void SettingsDialog::currentCategoryChanged(const QModelIndex &index) { showCategory(m_categoryModel->category(m_categoryFilterModel->mapToSource(index))); } /*! * \brief Sets the current category to the specified \a category and updates the * relevant widgets to show it. */ void SettingsDialog::showCategory(OptionCategory *category) { if (m_currentCategory) { m_currentCategory->setCurrentIndex(m_ui->pagesTabWidget->currentIndex()); } if (category) { if (m_currentCategory != category) { m_currentCategory = category; m_ui->headingLabel->setText(category->displayName()); } } else { m_currentCategory = nullptr; m_ui->headingLabel->setText(tr("No category selected")); } updateTabWidget(); } /*! * \brief Enables *single-category mode* to show only the specified \a * singleCategory. * \remarks * - In *single-category mode* category selection, filter and heading are * hidden. * - The *single-category mode* can be disabled again by setting \a * singleCategory to nullptr. */ void SettingsDialog::setSingleCategory(OptionCategory *singleCategory) { const bool hasSingleCategory = singleCategory != nullptr; m_ui->filterLineEdit->setHidden(hasSingleCategory); m_ui->categoriesListView->setHidden(hasSingleCategory); m_ui->headingLabel->setHidden(hasSingleCategory); if (hasSingleCategory) { m_ui->filterLineEdit->clear(); categoryModel()->setCategories(QList<OptionCategory *>({ singleCategory })); showCategory(singleCategory); } } /*! * \brief Returns the tab-widget's corner widget. */ QWidget *SettingsDialog::cornerWidget(Qt::Corner corner) const { return m_ui->pagesTabWidget->cornerWidget(corner); } /*! * \brief Sets the tab-widget's corner widget. */ void SettingsDialog::setCornerWidget(QWidget *widget, Qt::Corner corner) { m_ui->pagesTabWidget->setCornerWidget(widget, corner); } /*! * \brief Adds a widget next to the heading. */ void SettingsDialog::addHeadingWidget(QWidget *widget) { m_ui->headingLayout->addWidget(widget); } /*! * \brief Selects the specified page within the specified category. */ void SettingsDialog::selectPage(int categoryIndex, int pageIndex) { m_categoryFilterModel->setFilterFixedString(QString()); m_ui->filterLineEdit->clear(); showCategory(m_categoryModel->category(categoryIndex)); m_ui->categoriesListView->selectionModel()->select( m_categoryFilterModel->mapFromSource(m_categoryModel->index(categoryIndex)), QItemSelectionModel::ClearAndSelect); m_ui->pagesTabWidget->setCurrentIndex(pageIndex); } /*! * \brief Updates the tab widget to show the pages for the current category. */ void SettingsDialog::updateTabWidget() { if (!m_currentCategory) { m_ui->pagesTabWidget->clear(); return; } m_ui->pagesTabWidget->setUpdatesEnabled(false); const QString searchKeyWord = m_ui->filterLineEdit->text(); int index = 0, pageIndex = 0; for (OptionPage *const page : m_currentCategory->pages()) { if (page->matches(searchKeyWord)) { QScrollArea *scrollArea; if (index < m_ui->pagesTabWidget->count()) { scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index)); scrollArea->takeWidget(); m_ui->pagesTabWidget->setTabText(index, page->widget()->windowTitle()); m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon()); } else { scrollArea = new QScrollArea(m_ui->pagesTabWidget); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setBackgroundRole(QPalette::Base); scrollArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); scrollArea->setWidgetResizable(true); m_ui->pagesTabWidget->addTab(scrollArea, page->widget()->windowTitle()); m_ui->pagesTabWidget->setTabIcon(index, page->widget()->windowIcon()); } if (page->widget()->layout()) { page->widget()->layout()->setAlignment(Qt::AlignTop | Qt::AlignLeft); } scrollArea->setWidget(page->widget()); ++index; } if (pageIndex == m_currentCategory->currentIndex()) { m_ui->pagesTabWidget->setCurrentIndex(pageIndex); } ++pageIndex; } while (index < m_ui->pagesTabWidget->count()) { QScrollArea *const scrollArea = qobject_cast<QScrollArea *>(m_ui->pagesTabWidget->widget(index)); scrollArea->takeWidget(); m_ui->pagesTabWidget->removeTab(index); delete scrollArea; } m_ui->pagesTabWidget->tabBar()->setHidden(!m_tabBarAlwaysVisible && m_ui->pagesTabWidget->count() == 1); m_ui->pagesTabWidget->setUpdatesEnabled(true); } /*! * \brief Applies all changes. Calls OptionCategory::applyAllPages() for each category. * \remarks Pages which have not been shown yet must have not been initialized anyways * and hence are skipped. */ bool SettingsDialog::apply() { // apply each page in each category and gather error messages QString errorMessage; for (OptionCategory *const category : m_categoryModel->categories()) { for (OptionPage *const page : category->pages()) { if (!page->hasBeenShown() || page->apply()) { // nothing to apply or no error continue; } // add error message if (errorMessage.isEmpty()) { errorMessage = tr("<p><b>Errors occurred when applying changes:</b></p><ul>"); } QStringList &errors = const_cast<OptionPage *>(page)->errors(); if (errors.isEmpty()) { errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle() % QStringLiteral("</i>: ") % tr("unknown error") % QStringLiteral("</li>")); } else { for (const QString &error : errors) { errorMessage.append(QStringLiteral("<li><i>") % category->displayName() % QLatin1Char('/') % page->widget()->windowTitle() % QStringLiteral("</i>: ") % error % QStringLiteral("</li>")); } errors.clear(); } } } // show error messages (if errors occurred) if (!errorMessage.isEmpty()) { errorMessage.append(QStringLiteral("</ul>")); QMessageBox::warning(this, windowTitle(), errorMessage); } // return status emit applied(); return errorMessage.isEmpty(); } /*! * \brief Resets all changes. Calls OptionCategory::resetAllPages() for each * category. */ void SettingsDialog::reset() { for (OptionCategory *const category : m_categoryModel->categories()) { category->resetAllPages(); } emit resetted(); } } // namespace QtUtilities 07070100000056000081A4000000000000000000000001636534C90000088B000000000000000000000000000000000000002C00000000qtutilities/settingsdialog/settingsdialog.h#ifndef DIALOGS_SETTINGSDIALOG_H #define DIALOGS_SETTINGSDIALOG_H #include "../global.h" #include <QDialog> #include <memory> namespace QtUtilities { class OptionCategoryModel; class OptionCategoryFilterModel; class OptionCategory; class OptionPage; namespace Ui { class SettingsDialog; } class QT_UTILITIES_EXPORT SettingsDialog : public QDialog { Q_OBJECT Q_PROPERTY(bool tabBarAlwaysVisible READ isTabBarAlwaysVisible WRITE setTabBarAlwaysVisible) public: explicit SettingsDialog(QWidget *parent = nullptr); ~SettingsDialog() override; bool isTabBarAlwaysVisible() const; void setTabBarAlwaysVisible(bool value); OptionCategoryModel *categoryModel(); OptionCategory *category(int categoryIndex) const; OptionPage *page(int categoryIndex, int pageIndex) const; void showCategory(OptionCategory *category); void setSingleCategory(OptionCategory *singleCategory); QWidget *cornerWidget(Qt::Corner corner = Qt::TopRightCorner) const; void setCornerWidget(QWidget *widget, Qt::Corner corner = Qt::TopRightCorner); void addHeadingWidget(QWidget *widget); void selectPage(int categoryIndex, int pageIndex); public Q_SLOTS: bool apply(); void reset(); Q_SIGNALS: void applied(); void resetted(); protected: void showEvent(QShowEvent *event) override; private Q_SLOTS: void currentCategoryChanged(const QModelIndex &index); void updateTabWidget(); private: std::unique_ptr<Ui::SettingsDialog> m_ui; OptionCategoryModel *m_categoryModel; OptionCategoryFilterModel *m_categoryFilterModel; OptionCategory *m_currentCategory; bool m_tabBarAlwaysVisible; }; /*! * \brief Returns whether the tab bar is always visible. * * The tab bar is always visible by default. * * \sa SettingsDialog::setTabBarAlwaysVisible() */ inline bool SettingsDialog::isTabBarAlwaysVisible() const { return m_tabBarAlwaysVisible; } /*! * \brief Returns the category model used by the settings dialog to manage the * categories. */ inline OptionCategoryModel *SettingsDialog::categoryModel() { return m_categoryModel; } } // namespace QtUtilities #endif // DIALOGS_SETTINGSDIALOG_H 07070100000057000081A4000000000000000000000001636534C9000013F8000000000000000000000000000000000000002D00000000qtutilities/settingsdialog/settingsdialog.ui<?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QtUtilities::SettingsDialog</class> <widget class="QDialog" name="QtUtilities::SettingsDialog"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>515</width> <height>311</height> </rect> </property> <property name="windowTitle"> <string>Settings</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <property name="spacing"> <number>0</number> </property> <property name="leftMargin"> <number>0</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>0</number> </property> <property name="bottomMargin"> <number>0</number> </property> <item> <widget class="QWidget" name="mainWidget" native="true"> <layout class="QGridLayout" name="gridLayout"> <property name="bottomMargin"> <number>3</number> </property> <item row="1" column="1"> <widget class="QTabWidget" name="pagesTabWidget"> <property name="currentIndex"> <number>-1</number> </property> </widget> </item> <item row="1" column="0"> <widget class="QListView" name="categoriesListView"> <property name="maximumSize"> <size> <width>140</width> <height>16777215</height> </size> </property> <property name="selectionBehavior"> <enum>QAbstractItemView::SelectRows</enum> </property> <property name="iconSize"> <size> <width>32</width> <height>32</height> </size> </property> </widget> </item> <item row="0" column="1"> <layout class="QHBoxLayout" name="headingLayout"> <item> <widget class="QLabel" name="headingLabel"> <property name="sizePolicy"> <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="text"> <string>No category selected</string> </property> <property name="wordWrap"> <bool>true</bool> </property> </widget> </item> </layout> </item> <item row="0" column="0"> <widget class="QtUtilities::ClearLineEdit" name="filterLineEdit"> <property name="maximumSize"> <size> <width>140</width> <height>16777215</height> </size> </property> <property name="placeholderText"> <string>Filter</string> </property> </widget> </item> </layout> </widget> </item> <item> <widget class="QWidget" name="bottomWidget" native="true"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <layout class="QHBoxLayout" name="horizontalLayout"> <property name="topMargin"> <number>3</number> </property> <property name="bottomMargin"> <number>6</number> </property> <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>168</width> <height>20</height> </size> </property> </spacer> </item> <item> <widget class="QPushButton" name="abortPushButton"> <property name="styleSheet"> <string notr="true">background: none;</string> </property> <property name="text"> <string>Abort</string> </property> <property name="icon"> <iconset theme="dialog-cancel"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="applyPushButton"> <property name="styleSheet"> <string notr="true">background: none;</string> </property> <property name="text"> <string>Apply</string> </property> <property name="icon"> <iconset theme="dialog-ok-apply"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> <item> <widget class="QPushButton" name="okPushButton"> <property name="text"> <string>OK</string> </property> <property name="icon"> <iconset theme="dialog-ok"> <normaloff>.</normaloff>.</iconset> </property> </widget> </item> </layout> </widget> </item> </layout> </widget> <customwidgets> <customwidget> <class>QtUtilities::ClearLineEdit</class> <extends>QLineEdit</extends> <header>widgets/clearlineedit.h</header> </customwidget> </customwidgets> <resources/> <connections/> </ui> 07070100000058000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001200000000qtutilities/tests07070100000059000081A4000000000000000000000001636534C900001840000000000000000000000000000000000000002400000000qtutilities/tests/buttonoverlay.cpp#include "../widgets/clearcombobox.h" #include "../widgets/clearlineedit.h" #include "../widgets/clearplaintextedit.h" #include "../widgets/clearspinbox.h" #include "../widgets/iconbutton.h" #include <QHBoxLayout> #include <QPushButton> #include <QtTest/QtTest> using namespace QtUtilities; class ButtonOverlayTests : public QObject { Q_OBJECT private: void assertGeneralDefaults(ButtonOverlay &buttonOverlay); void changeBasicConfiguration(ButtonOverlay &buttonOverlay); private Q_SLOTS: void testClearLineEdit(); void testClearComboBox(); void testClearSpinBox(); void testClearPlainTextEdit(); }; void ButtonOverlayTests::assertGeneralDefaults(ButtonOverlay &buttonOverlay) { // assert defaults QVERIFY2(buttonOverlay.isClearButtonEnabled(), "clear button enabled by default"); QVERIFY2(!buttonOverlay.isInfoButtonEnabled(), "info button disabled by default"); QVERIFY2(buttonOverlay.isCleared(), "widget considered cleared by default"); QVERIFY2(!buttonOverlay.isUsingCustomLayout(), "not using custom layout by default"); } void ButtonOverlayTests::changeBasicConfiguration(ButtonOverlay &buttonOverlay) { buttonOverlay.setClearButtonEnabled(false); QVERIFY2(!buttonOverlay.isClearButtonEnabled(), "clear button disabled"); buttonOverlay.enableInfoButton( QIcon::fromTheme(QStringLiteral("data-information")).pixmap(IconButton::defaultPixmapSize), QStringLiteral("Some info")); QVERIFY2(buttonOverlay.isInfoButtonEnabled(), "info button enabled"); } void ButtonOverlayTests::testClearLineEdit() { auto clearWidget = ClearLineEdit(); // assert defaults assertGeneralDefaults(clearWidget); QVERIFY2(static_cast<QLineEdit &>(clearWidget).isClearButtonEnabled(), "clear button enabled via QLineEdit"); // change configuration changeBasicConfiguration(clearWidget); clearWidget.setText(QStringLiteral("not cleared anymore")); QVERIFY2(!clearWidget.isCleared(), "widget not considered cleared anymore"); auto customAction = QAction(QIcon::fromTheme(QStringLiteral("edit-copy")).pixmap(IconButton::defaultPixmapSize), QStringLiteral("Copy")); clearWidget.addCustomAction(&customAction); QVERIFY2(!clearWidget.isUsingCustomLayout(), "not resorted to using custom layout so far"); QCOMPARE(clearWidget.actions().size(), 2); // more an implementation detail but the QLineEdit should have 2 actions right now // test fallback to custom layout static_cast<ButtonOverlay &>(clearWidget).setClearButtonEnabled(true); auto pushButton = QPushButton(QStringLiteral("Custom widget")); clearWidget.addCustomButton(&pushButton); QVERIFY2(clearWidget.isUsingCustomLayout(), "resorted to using custom layout due to addCustomButton()"); QVERIFY2(static_cast<ButtonOverlay &>(clearWidget).isClearButtonEnabled(), "clear button still enabled"); QVERIFY2(!static_cast<QLineEdit &>(clearWidget).isClearButtonEnabled(), "clear button not enabled via QLineEdit"); QCOMPARE(clearWidget.actions().size(), 0); // more an implementation detail but the QLineEdit should have been converted to icon buttons QCOMPARE(clearWidget.buttonLayout()->count(), 4); // change custom action; its fallback icon button should reflect the change const auto *const iconButton = qobject_cast<const IconButton *>(clearWidget.buttonLayout()->itemAt(2)->widget()); QVERIFY2(iconButton, "icon button present"); QCOMPARE(iconButton->toolTip(), QStringLiteral("Copy")); customAction.setText(QStringLiteral("Paste")); QCOMPARE(iconButton->toolTip(), QStringLiteral("Paste")); // remove buttons again static_cast<ButtonOverlay &>(clearWidget).setClearButtonEnabled(false); clearWidget.disableInfoButton(); clearWidget.removeCustomAction(&customAction); clearWidget.removeCustomButton(&pushButton); QCOMPARE(clearWidget.buttonLayout()->count(), 0); } void ButtonOverlayTests::testClearComboBox() { auto clearWidget = ClearComboBox(); // assert defaults assertGeneralDefaults(clearWidget); // change configuration changeBasicConfiguration(clearWidget); clearWidget.setClearButtonEnabled(true); clearWidget.setCurrentText(QStringLiteral("not cleared anymore")); QVERIFY2(!clearWidget.isCleared(), "widget not considered cleared anymore"); auto customAction = QAction(QIcon::fromTheme(QStringLiteral("edit-copy")).pixmap(IconButton::defaultPixmapSize), QStringLiteral("Copy")); clearWidget.addCustomAction(&customAction); QVERIFY2(!clearWidget.isUsingCustomLayout(), "not resorted to using custom layout so far"); // trigger fallback QCOMPARE(clearWidget.buttonLayout()->count(), 3); } void ButtonOverlayTests::testClearSpinBox() { auto clearWidget = ClearSpinBox(); // assert defaults assertGeneralDefaults(clearWidget); // change configuration changeBasicConfiguration(clearWidget); clearWidget.setClearButtonEnabled(true); clearWidget.setValue(1); QVERIFY2(!clearWidget.isCleared(), "widget not considered cleared anymore"); auto customAction = QAction(QIcon::fromTheme(QStringLiteral("edit-copy")).pixmap(IconButton::defaultPixmapSize), QStringLiteral("Copy")); clearWidget.addCustomAction(&customAction); QVERIFY2(!clearWidget.isUsingCustomLayout(), "not resorted to using custom layout so far"); // trigger fallback QCOMPARE(clearWidget.buttonLayout()->count(), 3); } void ButtonOverlayTests::testClearPlainTextEdit() { auto clearWidget = ClearPlainTextEdit(); // assert defaults QVERIFY2(clearWidget.isClearButtonEnabled(), "clear button enabled by default"); QVERIFY2(!clearWidget.isInfoButtonEnabled(), "info button disabled by default"); QVERIFY2(clearWidget.isCleared(), "widget considered cleared by default"); QVERIFY2(clearWidget.isUsingCustomLayout(), "using custom layout by default"); QCOMPARE(clearWidget.buttonLayout()->count(), 1); // change configuration clearWidget.document()->setPlainText(QStringLiteral("not cleared anymore")); QVERIFY2(!clearWidget.isCleared(), "widget not considered cleared anymore"); } QTEST_MAIN(ButtonOverlayTests) #include "buttonoverlay.moc" 0707010000005A000081A4000000000000000000000001636534C900001113000000000000000000000000000000000000002700000000qtutilities/tests/dbusnotification.cpp#include "../misc/dbusnotification.h" #include "resources/config.h" #include <QSignalSpy> #include <QTest> using namespace QtUtilities; /*! * \brief The DBusNotificationTests class tests the DBusNotification class. */ class DBusNotificationTests : public QObject { Q_OBJECT private Q_SLOTS: void smokeTest(); void semiAutomaticTest(); }; static void dummy(DBusNotification::Capabilities &&) { } const static auto callback = std::function<void(DBusNotification::Capabilities &&)>(&dummy); /*! * \brief Runs some basic functions of DBusNotification (c'tor, d'tor, some accessors, ...) but doesn't really check * whether it works. * \remarks This test should pass regardless whether DBus-notifications are actually available. Hence it avoids any checks * which depend on that and just checks whether certain function don't lead to crashes. */ void DBusNotificationTests::smokeTest() { DBusNotification::isAvailable(); DBusNotification::queryCapabilities(callback); DBusNotification n(QStringLiteral("Smoke test"), NotificationIcon::Warning, 100); QVERIFY2(!n.isVisible(), "not immediately visible"); QCOMPARE(n.title(), QStringLiteral("Smoke test")); n.setApplicationName(QStringLiteral(APP_NAME " tests; " APP_VERSION)); QCOMPARE(n.applicationName(), QStringLiteral(APP_NAME " tests; " APP_VERSION)); n.show(QStringLiteral("Some message")); // will emit an error if not available n.isVisible(); n.hide(); QCOMPARE(n.message(), QStringLiteral("Some message")); n.update(QStringLiteral("Another message")); n.hide(); if (n.isVisible()) { QSignalSpy closedSpy(&n, &DBusNotification::closed); closedSpy.wait(); } } /*! * \brief Runs a semi-automatic test to verify whether DBusNotification works for real. * \remarks This test needs a daemon for D-Bus notifications running and requires manual user interaction. It is therefore * skipped unless an environment variable is set. */ void DBusNotificationTests::semiAutomaticTest() { const auto envValue = qgetenv(PROJECT_VARNAME_UPPER "_ENABLE_SEMI_AUTOMATIC_NOTIFICATION_TESTS"); auto envValueIsInt = false; if (envValue.isEmpty() || (envValue.toInt(&envValueIsInt) == 0 && envValueIsInt)) { QSKIP("Set the environment variable " PROJECT_VARNAME_UPPER "_ENABLE_SEMI_AUTOMATIC_NOTIFICATION_TESTS to run " "the semi-automatic D-Bus notification test."); } QVERIFY2(DBusNotification::isAvailable(), "D-Bus notifications are available"); DBusNotification n(QStringLiteral("Semi-automatic test"), NotificationIcon::Information, 10000); QString clickedAction, error; const auto actionConnection = connect(&n, &DBusNotification::actionInvoked, [&clickedAction](const QString &actionName) { clickedAction = actionName; }); const auto errorConnection = connect(&n, &DBusNotification::error, [&error]() { error = QStringLiteral("error occurred (TODO: pass an error message here)"); }); n.setApplicationName(QStringLiteral(APP_NAME " tests; " APP_VERSION)); n.show(QStringLiteral("Some message; will append more lines later")); for (auto i = 1; i <= 10; ++i) { n.update(QStringLiteral("Yet another line, should be displayed in the same notification as previous message (%1)").arg(i)); QTest::qWait(100); } QCOMPARE(error, QString()); n.setImage(QIcon::fromTheme(QStringLiteral("document-open")).pixmap(64).toImage()); n.setTitle(n.title() + QStringLiteral(" - click action to continue")); n.setActions(QStringList({ QStringLiteral("fail"), QStringLiteral("Let test fail"), QStringLiteral("pass"), QStringLiteral("Let test pass") })); QSignalSpy actionInvokedSpy(&n, &DBusNotification::actionInvoked); n.update(QStringLiteral("Click on \"Let test pass\" to continue within 10 seconds")); actionInvokedSpy.wait(10000); QCOMPARE(clickedAction, QStringLiteral("pass")); QSignalSpy closedSpy(&n, &DBusNotification::closed); n.setTimeout(5000); n.setActions(QStringList()); n.update(QStringLiteral("Waiting for message to close (will close automatically in 5 seconds)")); closedSpy.wait(8000); QCOMPARE(error, QString()); disconnect(actionConnection); disconnect(errorConnection); } QTEST_MAIN(DBusNotificationTests) #include "dbusnotification.moc" 0707010000005B000081A4000000000000000000000001636534C9000004A0000000000000000000000000000000000000001E00000000qtutilities/tests/dialogs.cpp#include "../settingsdialog/optioncategory.h" #include "../settingsdialog/optioncategorymodel.h" #include "../settingsdialog/qtsettings.h" #include "../settingsdialog/settingsdialog.h" #include <QtTest/QtTest> using namespace QtUtilities; class DialogsTests : public QObject { Q_OBJECT private Q_SLOTS: void testSettingsDialog(); }; void DialogsTests::testSettingsDialog() { // show single category auto settingsDlg = SettingsDialog(); auto qtSettings = QtSettings(); settingsDlg.setSingleCategory(qtSettings.category()); // add another empty category auto *const testCategory = new OptionCategory(); testCategory->setDisplayName(QStringLiteral("Test category")); testCategory->setIcon(QIcon::fromTheme(QStringLiteral("preferences"))); settingsDlg.showCategory(nullptr); // ensure no current category is shown anymore settingsDlg.setSingleCategory(nullptr); auto *const qtCategory = qtSettings.category(); settingsDlg.categoryModel()->setCategories(QList<OptionCategory *>({ testCategory, qtCategory })); settingsDlg.showCategory(qtCategory); settingsDlg.show(); } QTEST_MAIN(DialogsTests) #include "dialogs.moc" 0707010000005C000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001900000000qtutilities/translations0707010000005D000081A4000000000000000000000001636534C9000054BC000000000000000000000000000000000000002E00000000qtutilities/translations/qtutilities_de_DE.ts<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="de_DE"> <context> <name>QObject</name> <message> <location filename="../widgets/buttonoverlay.cpp" line="143"/> <source>Clear</source> <translation>Text löschen</translation> </message> </context> <context> <name>QtGui::QtLanguageOptionPage</name> <message> <location filename="../settingsdialog/qtsettings.cpp" line="367"/> <source>recognized by Qt as</source> <translation>von Qt erkannt als</translation> </message> </context> <context> <name>QtGui::QtOptionCategory</name> <message> <location filename="../settingsdialog/qtsettings.cpp" line="213"/> <source>Qt</source> <translation></translation> </message> </context> <context> <name>QtUtilities::AboutDialog</name> <message> <location filename="../aboutdialog/aboutdialog.ui" line="24"/> <source>About</source> <translation>Über</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="127"/> <source>application name</source> <translation></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="137"/> <source>version</source> <translation></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="163"/> <source>description</source> <translation></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="185"/> <source>website link</source> <translation></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="226"/> <source>creators</source> <translation></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="253"/> <source>Using Qt</source> <translation>Verwendet Qt</translation> </message> <message> <source>using Qt</source> <translation type="vanished">verwendet Qt</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="61"/> <source>developed by %1</source> <translation>entwickelt von %1</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="71"/> <source>Linked against:</source> <translation>Gegen folgende Bibliotheken gelinkt:</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="75"/> <source>For updates and bug reports visit the <a href="%1" style="text-decoration: underline; color: palette(link);">project website</a>.</source> <translation>Für Aktualisierung und Melden von Fehlern besuche die <a href="%1" style="text-decoration: underline; color: palette(link);">Webseite des Projekts</a>.</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="91"/> <source>Using <a href="qtversion">Qt %1</a></source> <translation>Verwendet <a href="qtversion">Qt %1</a></translation> </message> </context> <context> <name>QtUtilities::EnterPasswordDialog</name> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="12"/> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="51"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/> <source>Enter the password</source> <translation>Passwort eingeben</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="64"/> <source>user name</source> <translation>Benutzername</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="86"/> <source>password</source> <translation>Passwort</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="102"/> <source>repeat password</source> <translation>Password wiederholen</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="130"/> <source>show password</source> <translation>Passwort zeigen</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="137"/> <source>don't use a password</source> <translation>Kein Passwort verwenden</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="213"/> <source>Capslock is active</source> <translation>Feststelltaste ist aktiviert</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="239"/> <source>Abort</source> <translation>Abbrechen</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="254"/> <source>Confirm</source> <translation>Bestätigen</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/> <source>Enter the new password</source> <translation>Neues Passwort festlegen</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="290"/> <source>You didn't enter a user name.</source> <translation>Es wurde kein Benutzername eingegeben.</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="292"/> <source>You didn't enter a password.</source> <translation>Es wurde kein Passwort eingegeben.</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="297"/> <source>You have to enter the new password twice to ensure you enterd it correct.</source> <translation>Um sicher zu stellen, dass das neue Passwort richtig eingegeben wurde, muss es zweimal eingegeben werden.</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="300"/> <source>You mistyped the password.</source> <translation>Erstes und zweites Passwort stimmen nicht überein.</translation> </message> </context> <context> <name>QtUtilities::PaletteEditor</name> <message> <location filename="../paletteeditor/paletteeditor.ui" line="20"/> <source>Edit Palette</source> <translation>Palette ändern</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="53"/> <source>Tune Palette</source> <translation>Palette</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="84"/> <source>Show Detai&ls</source> <translation>Details einblenden</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="91"/> <source>&Compute Details</source> <translation>Details berechnen</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="101"/> <source>Quick</source> <translation>Einfach</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="45"/> <source>Save…</source> <translation>Speichern…</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="47"/> <source>Load…</source> <translation>Laden…</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="143"/> <source>Color palette configuration (*.ini)</source> <translation>Farbpalettenkonfiguration (*.ini)</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="150"/> <source>Unable to load "%1".</source> <translation>Fehler beim Laden von "%1".</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="155"/> <source>"%1" does not contain a valid palette.</source> <translation>"%1" enthält keine gültige Farbpalette.</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="168"/> <source>Unable to write "%1".</source> <translation>Fehler beim Schreiben von "%1".</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="176"/> <source>Load palette</source> <translation>Palette laden</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="186"/> <source>Error reading palette</source> <translation>Fehler beim Einlesen der Palette</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="192"/> <source>Save palette</source> <translation>Palette speichern</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="200"/> <source>Error writing palette</source> <translation>Fehler beim Schreiben der Palette</translation> </message> </context> <context> <name>QtUtilities::PaletteModel</name> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="417"/> <source>Color Role</source> <translation>Farbrolle</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="419"/> <source>Active</source> <translation>Aktiv</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="421"/> <source>Inactive</source> <translation>Inaktiv</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="423"/> <source>Disabled</source> <translation>Deaktiviert</translation> </message> </context> <context> <name>QtUtilities::PathSelection</name> <message> <location filename="../widgets/pathselection.cpp" line="56"/> <location filename="../widgets/pathselection.cpp" line="76"/> <source>Select ...</source> <translation>Wählen ...</translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="81"/> <source>Open</source> <translation>Öffnen</translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="84"/> <source>Explore</source> <translation>Im Dateibrowser öffnen</translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="120"/> <location filename="../widgets/pathselection.cpp" line="122"/> <source>Select path</source> <translation>Pfad auswählen</translation> </message> </context> <context> <name>QtUtilities::QtAppearanceOptionPage</name> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="6"/> <source>Appearance</source> <translation>Erscheinungsbild</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="21"/> <source>Use system default</source> <translation>Verwende Systemvorgabe</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="34"/> <source>Font</source> <translation>Schriftart</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="71"/> <source>More options ...</source> <translation>Weitere Optionen ...</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="93"/> <source>Widget style</source> <translation>Widget-Stil</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="132"/> <source>Style sheet</source> <translation>Stylesheet</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="162"/> <source>Palette</source> <translation></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="175"/> <source>select ...</source> <translation>Bearbeiten ...</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="195"/> <source>Icon theme</source> <translation>Symbole</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="249"/> <source>These settings take effect after restarting the application. These settings might be overwritten by your Qt platform integration plugin and hence have no effect.</source> <translation>Diese Einstellungen wirken sich erst nach einem Neustart der Anwendung aus. Außerdem werden sie vielleicht vom QPA-Plugin überschrieben und funktionieren daher nicht.</translation> </message> <message> <source>These settings take effect after restarting the application. These settings might be overwritten by your Qt platfrom integration plugin and hence have no effect.</source> <translation type="vanished">Diese Einstellungen wirken sich erst nach einem Neustart der Anwendung aus. Außerdem werden sie vielleicht vom QPA-Plugin überschrieben und funktionieren daher nicht.</translation> </message> <message> <source>These settings take effect after restarting the application. These settings might be overwritten by your Qt 5 platfrom integration plugin and hence have no effect.</source> <translation type="vanished">Diese Einstellungen wirken sich erst nach einem Neustart der Anwendung aus. Außerdem werden sie vielleicht vom QPA plugin überschrieben und funktionieren daher nicht.</translation> </message> </context> <context> <name>QtUtilities::QtEnvOptionPage</name> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="14"/> <source>Environment/paths</source> <translation>Umgebung/Pfade</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="28"/> <source>Additional plugin directory</source> <translation>Zusätzliches Plugin-Verzeichnis</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="38"/> <source>Additional icon theme search path</source> <translation>Zusätzliches Verzeichnis für Symbole</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="48"/> <source>Additional translation search path</source> <translation>Zusätzliches Verzeichnis für Übersetzungen</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="79"/> <source>These settings take effect after restarting the application.</source> <translation>Diese Einstellungen wirken sich erst nach einem Neustart der Anwendung aus.</translation> </message> </context> <context> <name>QtUtilities::QtLanguageOptionPage</name> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="6"/> <source>Localization</source> <translation>Lokalisierung</translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="43"/> <source>Locale</source> <translation>Gebietsschema</translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="53"/> <source>Use system default</source> <translation>Verwende Systemvorgabe</translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="98"/> <source>These settings take effect after restarting the application.</source> <translation>Diese Einstellungen wirken sich erst nach einem Neustart der Anwendung aus.</translation> </message> </context> <context> <name>QtUtilities::RecentMenuManager</name> <message> <location filename="../misc/recentmenumanager.cpp" line="35"/> <source>&Recent</source> <translation>&Zuletzt verwendet</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="38"/> <source>&Clear list</source> <translation>&Liste löschen</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="144"/> <source>Recently opened files - </source> <translation>Kürzlich geöffnete Dateien - </translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="145"/> <source>The selected file can't be found anymore. Do you want to delete the obsolete entry from the list?</source> <translation>Die ausgewählte Datei kann nicht mehr gefunden werden. Soll die Datei aus der Liste gelöscht werden?</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="148"/> <source>keep entry</source> <translation>Eintrag behalten</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="149"/> <source>delete entry</source> <translation>Eintrag löschen</translation> </message> </context> <context> <name>QtUtilities::SettingsDialog</name> <message> <location filename="../settingsdialog/settingsdialog.ui" line="14"/> <source>Settings</source> <translation>Einstellungen</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="75"/> <location filename="../settingsdialog/settingsdialog.cpp" line="153"/> <source>No category selected</source> <translation>Keine Kategorie gewählt</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="93"/> <source>Filter</source> <translation></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="134"/> <source>Abort</source> <translation>Abbrechen</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="148"/> <source>Apply</source> <translation>Anwenden</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="159"/> <source>OK</source> <translation></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.cpp" line="285"/> <source><p><b>Errors occurred when applying changes:</b></p><ul></source> <translation><p><b>Beim Anwenden der Einstellungen sind Fehler aufgetreten:</b></p><ul></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.cpp" line="290"/> <source>unknown error</source> <translation>unbekannter Fehler</translation> </message> <message> <source>unknonw error</source> <translation type="vanished">unbekannter Fehler</translation> </message> </context> <context> <name>Utilities::windowTitle</name> <message> <location filename="../misc/dialogutils.cpp" line="34"/> <source>Unsaved - %1</source> <translation>Nicht gespeichert - %1</translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="37"/> <source>%1 - %2 - %3</source> <translation></translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="42"/> <source>*Unsaved - %1</source> <translation>*Nicht gespeichert - %1</translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="45"/> <source>*%1 - %2 - %3</source> <translation></translation> </message> </context> </TS> 0707010000005E000081A4000000000000000000000001636534C900004DDF000000000000000000000000000000000000002E00000000qtutilities/translations/qtutilities_en_US.ts<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="en_US"> <context> <name>QObject</name> <message> <location filename="../widgets/buttonoverlay.cpp" line="143"/> <source>Clear</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtGui::QtLanguageOptionPage</name> <message> <location filename="../settingsdialog/qtsettings.cpp" line="367"/> <source>recognized by Qt as</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtGui::QtOptionCategory</name> <message> <location filename="../settingsdialog/qtsettings.cpp" line="213"/> <source>Qt</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::AboutDialog</name> <message> <location filename="../aboutdialog/aboutdialog.ui" line="24"/> <source>About</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="127"/> <source>application name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="137"/> <source>version</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="163"/> <source>description</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="185"/> <source>website link</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="226"/> <source>creators</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="253"/> <source>Using Qt</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="61"/> <source>developed by %1</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="71"/> <source>Linked against:</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="75"/> <source>For updates and bug reports visit the <a href="%1" style="text-decoration: underline; color: palette(link);">project website</a>.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="91"/> <source>Using <a href="qtversion">Qt %1</a></source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::EnterPasswordDialog</name> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="12"/> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="51"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/> <source>Enter the password</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="64"/> <source>user name</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="86"/> <source>password</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="102"/> <source>repeat password</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="130"/> <source>show password</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="137"/> <source>don't use a password</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="213"/> <source>Capslock is active</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="239"/> <source>Abort</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="254"/> <source>Confirm</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/> <source>Enter the new password</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="290"/> <source>You didn't enter a user name.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="292"/> <source>You didn't enter a password.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="297"/> <source>You have to enter the new password twice to ensure you enterd it correct.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="300"/> <source>You mistyped the password.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::PaletteEditor</name> <message> <location filename="../paletteeditor/paletteeditor.ui" line="20"/> <source>Edit Palette</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="53"/> <source>Tune Palette</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="84"/> <source>Show Detai&ls</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="91"/> <source>&Compute Details</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="101"/> <source>Quick</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="45"/> <source>Save…</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="47"/> <source>Load…</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="143"/> <source>Color palette configuration (*.ini)</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="150"/> <source>Unable to load "%1".</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="155"/> <source>"%1" does not contain a valid palette.</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="168"/> <source>Unable to write "%1".</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="176"/> <source>Load palette</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="186"/> <source>Error reading palette</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="192"/> <source>Save palette</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="200"/> <source>Error writing palette</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::PaletteModel</name> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="417"/> <source>Color Role</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="419"/> <source>Active</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="421"/> <source>Inactive</source> <translation type="unfinished"></translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="423"/> <source>Disabled</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::PathSelection</name> <message> <location filename="../widgets/pathselection.cpp" line="56"/> <location filename="../widgets/pathselection.cpp" line="76"/> <source>Select ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="81"/> <source>Open</source> <translation type="unfinished"></translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="84"/> <source>Explore</source> <translation type="unfinished"></translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="120"/> <location filename="../widgets/pathselection.cpp" line="122"/> <source>Select path</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::QtAppearanceOptionPage</name> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="6"/> <source>Appearance</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="21"/> <source>Use system default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="34"/> <source>Font</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="71"/> <source>More options ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="93"/> <source>Widget style</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="132"/> <source>Style sheet</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="162"/> <source>Palette</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="175"/> <source>select ...</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="195"/> <source>Icon theme</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="249"/> <source>These settings take effect after restarting the application. These settings might be overwritten by your Qt platform integration plugin and hence have no effect.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::QtEnvOptionPage</name> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="14"/> <source>Environment/paths</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="28"/> <source>Additional plugin directory</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="38"/> <source>Additional icon theme search path</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="48"/> <source>Additional translation search path</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="79"/> <source>These settings take effect after restarting the application.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::QtLanguageOptionPage</name> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="6"/> <source>Localization</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="43"/> <source>Locale</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="53"/> <source>Use system default</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="98"/> <source>These settings take effect after restarting the application.</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::RecentMenuManager</name> <message> <location filename="../misc/recentmenumanager.cpp" line="35"/> <source>&Recent</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="38"/> <source>&Clear list</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="144"/> <source>Recently opened files - </source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="145"/> <source>The selected file can't be found anymore. Do you want to delete the obsolete entry from the list?</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="148"/> <source>keep entry</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="149"/> <source>delete entry</source> <translation type="unfinished"></translation> </message> </context> <context> <name>QtUtilities::SettingsDialog</name> <message> <location filename="../settingsdialog/settingsdialog.ui" line="14"/> <source>Settings</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="75"/> <location filename="../settingsdialog/settingsdialog.cpp" line="153"/> <source>No category selected</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="93"/> <source>Filter</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="134"/> <source>Abort</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="148"/> <source>Apply</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="159"/> <source>OK</source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.cpp" line="285"/> <source><p><b>Errors occurred when applying changes:</b></p><ul></source> <translation type="unfinished"></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.cpp" line="290"/> <source>unknown error</source> <translation type="unfinished"></translation> </message> </context> <context> <name>Utilities::windowTitle</name> <message> <location filename="../misc/dialogutils.cpp" line="34"/> <source>Unsaved - %1</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="37"/> <source>%1 - %2 - %3</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="42"/> <source>*Unsaved - %1</source> <translation type="unfinished"></translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="45"/> <source>*%1 - %2 - %3</source> <translation type="unfinished"></translation> </message> </context> </TS> 0707010000005F000081A4000000000000000000000001636534C900004E74000000000000000000000000000000000000002E00000000qtutilities/translations/qtutilities_zh_CN.ts<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> <TS version="2.1" language="zh_CN"> <context> <name>QObject</name> <message> <location filename="../widgets/buttonoverlay.cpp" line="143"/> <source>Clear</source> <translation>清除</translation> </message> </context> <context> <name>QtGui::QtLanguageOptionPage</name> <message> <location filename="../settingsdialog/qtsettings.cpp" line="367"/> <source>recognized by Qt as</source> <translation>被 Qt 识别为</translation> </message> </context> <context> <name>QtGui::QtOptionCategory</name> <message> <location filename="../settingsdialog/qtsettings.cpp" line="213"/> <source>Qt</source> <translation>Qt</translation> </message> </context> <context> <name>QtUtilities::AboutDialog</name> <message> <location filename="../aboutdialog/aboutdialog.ui" line="24"/> <source>About</source> <translation>关于</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="127"/> <source>application name</source> <translation>应用名称</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="137"/> <source>version</source> <translation>版本</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="163"/> <source>description</source> <translation>描述</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="185"/> <source>website link</source> <translation>网站链接</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="226"/> <source>creators</source> <translation>创造者</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.ui" line="253"/> <source>Using Qt</source> <translation>使用 Qt</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="61"/> <source>developed by %1</source> <translation>由 %1 开发</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="71"/> <source>Linked against:</source> <translation>链接到:</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="75"/> <source>For updates and bug reports visit the <a href="%1" style="text-decoration: underline; color: palette(link);">project website</a>.</source> <translation>有关更新和错误报告,请访问<a href="%1" style="text-decoration: underline; color: Palette(link);">项目网站</a>。</translation> </message> <message> <location filename="../aboutdialog/aboutdialog.cpp" line="91"/> <source>Using <a href="qtversion">Qt %1</a></source> <translation>使用 <a href="qtversion">Qt %1</a></translation> </message> </context> <context> <name>QtUtilities::EnterPasswordDialog</name> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="12"/> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="51"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/> <source>Enter the password</source> <translation>输入密码</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="64"/> <source>user name</source> <translation>用户名</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="86"/> <source>password</source> <translation>密码</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="102"/> <source>repeat password</source> <translation>重复密码</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="130"/> <source>show password</source> <translation>显示密码</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="137"/> <source>don't use a password</source> <translation>不要使用密码</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="213"/> <source>Capslock is active</source> <translation>大写锁定激活</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="239"/> <source>Abort</source> <translation>放弃</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.ui" line="254"/> <source>Confirm</source> <translation>确认</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="190"/> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="205"/> <source>Enter the new password</source> <translation>输入新密码</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="290"/> <source>You didn't enter a user name.</source> <translation>您没有输入用户名。</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="292"/> <source>You didn't enter a password.</source> <translation>您没有输入密码。</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="297"/> <source>You have to enter the new password twice to ensure you enterd it correct.</source> <translation>您必须输入两次新密码以确保输入正确。</translation> </message> <message> <location filename="../enterpassworddialog/enterpassworddialog.cpp" line="300"/> <source>You mistyped the password.</source> <translation>您输错了密码。</translation> </message> </context> <context> <name>QtUtilities::PaletteEditor</name> <message> <location filename="../paletteeditor/paletteeditor.ui" line="20"/> <source>Edit Palette</source> <translation>编辑调色板</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="53"/> <source>Tune Palette</source> <translation>微调调色板</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="84"/> <source>Show Detai&ls</source> <translation>显示&详情</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="91"/> <source>&Compute Details</source> <translation>&计算详情</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.ui" line="101"/> <source>Quick</source> <translation>快</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="45"/> <source>Save…</source> <translation>保存…</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="47"/> <source>Load…</source> <translation>加载…</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="143"/> <source>Color palette configuration (*.ini)</source> <translation>调色板配置 (*.ini)</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="150"/> <source>Unable to load "%1".</source> <translation>无法加载 "%1"。</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="155"/> <source>"%1" does not contain a valid palette.</source> <translation>"%1" 不包含有效的调色板。</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="168"/> <source>Unable to write "%1".</source> <translation>无法写入 "%1".。</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="176"/> <source>Load palette</source> <translation>加载调色板</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="186"/> <source>Error reading palette</source> <translation>读取调色板时出错</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="192"/> <source>Save palette</source> <translation>保存调色板</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="200"/> <source>Error writing palette</source> <translation>写入调色板时出错</translation> </message> </context> <context> <name>QtUtilities::PaletteModel</name> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="417"/> <source>Color Role</source> <translation>颜色角色</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="419"/> <source>Active</source> <translation>激活</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="421"/> <source>Inactive</source> <translation>未激活</translation> </message> <message> <location filename="../paletteeditor/paletteeditor.cpp" line="423"/> <source>Disabled</source> <translation>已禁用</translation> </message> </context> <context> <name>QtUtilities::PathSelection</name> <message> <location filename="../widgets/pathselection.cpp" line="56"/> <location filename="../widgets/pathselection.cpp" line="76"/> <source>Select ...</source> <translation>选择 ...</translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="81"/> <source>Open</source> <translation>打开</translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="84"/> <source>Explore</source> <translation>浏览</translation> </message> <message> <location filename="../widgets/pathselection.cpp" line="120"/> <location filename="../widgets/pathselection.cpp" line="122"/> <source>Select path</source> <translation>选择路径</translation> </message> </context> <context> <name>QtUtilities::QtAppearanceOptionPage</name> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="6"/> <source>Appearance</source> <translation>外观</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="21"/> <source>Use system default</source> <translation>使用系统默认值</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="34"/> <source>Font</source> <translation>字体</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="71"/> <source>More options ...</source> <translation>更多选择 ...</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="93"/> <source>Widget style</source> <translation>小部件样式</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="132"/> <source>Style sheet</source> <translation>样式表</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="162"/> <source>Palette</source> <translation>调色板</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="175"/> <source>select ...</source> <translation>选择 ...</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="195"/> <source>Icon theme</source> <translation>图标主题</translation> </message> <message> <location filename="../settingsdialog/qtappearanceoptionpage.ui" line="249"/> <source>These settings take effect after restarting the application. These settings might be overwritten by your Qt platform integration plugin and hence have no effect.</source> <translation>这些设置在重新启动应用程序后生效。 这些设置可能会被 Qt 平台集成插件覆盖,因此无效。</translation> </message> </context> <context> <name>QtUtilities::QtEnvOptionPage</name> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="14"/> <source>Environment/paths</source> <translation>Environment/paths</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="28"/> <source>Additional plugin directory</source> <translation>附加插件目录</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="38"/> <source>Additional icon theme search path</source> <translation>附加图标主题搜索路径</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="48"/> <source>Additional translation search path</source> <translation>附加翻译搜索路径</translation> </message> <message> <location filename="../settingsdialog/qtenvoptionpage.ui" line="79"/> <source>These settings take effect after restarting the application.</source> <translation>这些设置在重新启动应用程序后生效。</translation> </message> </context> <context> <name>QtUtilities::QtLanguageOptionPage</name> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="6"/> <source>Localization</source> <translation>本土化</translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="43"/> <source>Locale</source> <translation>语言环境</translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="53"/> <source>Use system default</source> <translation>使用系统默认值</translation> </message> <message> <location filename="../settingsdialog/qtlanguageoptionpage.ui" line="98"/> <source>These settings take effect after restarting the application.</source> <translation>这些设置在重新启动应用程序后生效。</translation> </message> </context> <context> <name>QtUtilities::RecentMenuManager</name> <message> <location filename="../misc/recentmenumanager.cpp" line="35"/> <source>&Recent</source> <translation>&最近的</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="38"/> <source>&Clear list</source> <translation>&清除列表</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="144"/> <source>Recently opened files - </source> <translation>最近打开的文件 - </translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="145"/> <source>The selected file can't be found anymore. Do you want to delete the obsolete entry from the list?</source> <translation>无法再找到所选文件。是否要从列表中删除过时的条目?</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="148"/> <source>keep entry</source> <translation>保持条目</translation> </message> <message> <location filename="../misc/recentmenumanager.cpp" line="149"/> <source>delete entry</source> <translation>删除条目</translation> </message> </context> <context> <name>QtUtilities::SettingsDialog</name> <message> <location filename="../settingsdialog/settingsdialog.ui" line="14"/> <source>Settings</source> <translation>设置</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="75"/> <location filename="../settingsdialog/settingsdialog.cpp" line="153"/> <source>No category selected</source> <translation>未选择类别</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="93"/> <source>Filter</source> <translation>筛选</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="134"/> <source>Abort</source> <translation>放弃</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="148"/> <source>Apply</source> <translation>应用</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.ui" line="159"/> <source>OK</source> <translation>好的</translation> </message> <message> <location filename="../settingsdialog/settingsdialog.cpp" line="285"/> <source><p><b>Errors occurred when applying changes:</b></p><ul></source> <translation><p><b>应用更改时发生错误:</b></p><ul></translation> </message> <message> <location filename="../settingsdialog/settingsdialog.cpp" line="290"/> <source>unknown error</source> <translation>未知错误</translation> </message> </context> <context> <name>Utilities::windowTitle</name> <message> <location filename="../misc/dialogutils.cpp" line="34"/> <source>Unsaved - %1</source> <translation>未保存 - %1</translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="37"/> <source>%1 - %2 - %3</source> <translation>%1 - %2 - %3</translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="42"/> <source>*Unsaved - %1</source> <translation>*未保存 - %1</translation> </message> <message> <location filename="../misc/dialogutils.cpp" line="45"/> <source>*%1 - %2 - %3</source> <translation>*%1 - %2 - %3</translation> </message> </context> </TS> 07070100000060000041ED000000000000000000000002636534C900000000000000000000000000000000000000000000001400000000qtutilities/widgets07070100000061000081A4000000000000000000000001636534C9000035AC000000000000000000000000000000000000002600000000qtutilities/widgets/buttonoverlay.cpp#include "./buttonoverlay.h" #include "./iconbutton.h" #include <QAction> #include <QComboBox> #include <QCursor> #include <QHBoxLayout> #include <QIcon> #include <QLineEdit> #include <QStyle> #include <QStyleOption> #include <QToolTip> #include <QWidget> #include <functional> namespace QtUtilities { /*! * \class ButtonOverlay * \brief The ButtonOverlay class is used to display buttons on top of other widgets. * * This class had been created before QLineEdit's functions setClearButtonEnabled() and * addAction() have been available. (These functions have been available only since Qt 5.2.) * * The downside of the "custom approach" compared to QLineEdit's own functions is that the * buttons are shown over the text as the text margins are not updated accordingly. Hence * the ButtonOverlay class has been updated to use QLineEdit's functions internally when the * specified widget is QLineEdit-based and its QLineEdit has been passed to the constructor. * However, when using any functions which can not be implemented using QLineEdit's own * functions, the ButtonOverlay has to fallback to its "custom approach". All functions which * cause this have a remark in their documentation. * * When QLineEdit's functions can not be used, the ButtonOverlay class creates a new layout * manager and sets it to the widget specified when constructing an instance. Thus this widget * must not already have a layout manager. * * The class is used to implement widget customization like ClearLineEidt and ClearComboBox * and most of the times it makes sense to use these widgets instead of using ButtonOverlay * directly. */ /*! * \brief Constructs a button overlay for the specified \a widget. * \param widget Specifies the widget to display the buttons on. * \remarks This function enforces the "custom approach" mentioned in the class documentation * and should therefore be avoided. */ ButtonOverlay::ButtonOverlay(QWidget *widget) : m_widget(widget) , m_buttonWidget(nullptr) , m_buttonLayout(nullptr) , m_clearButton(nullptr) , m_infoButtonOrAction(nullptr) { fallbackToUsingCustomLayout(); } /*! * \brief Constructs a button overlay for the specified \a widget. * \param widget Specifies the widget to display the buttons on. * \param lineEdit Specifies the line edit used by \a widget to use the QLineEdit's functions * for adding actions instead of a custom layout. */ ButtonOverlay::ButtonOverlay(QWidget *widget, QLineEdit *lineEdit) : m_widget(widget) , m_buttonWidget(lineEdit) , m_buttonLayout(nullptr) , m_clearButton(nullptr) , m_infoButtonOrAction(nullptr) { if (!m_buttonWidget) { fallbackToUsingCustomLayout(); } } /*! * \brief Destroys the button overlay. */ ButtonOverlay::~ButtonOverlay() { } /*! * \brief Returns whether the "custom approach" mentioned in the class documentation is used. */ bool ButtonOverlay::isUsingCustomLayout() const { return m_buttonLayout != nullptr; } /*! * \brief Returns the layout manager holding the buttons. * \remarks This function enforces the "custom approach" mentioned in the class documentation * and should therefore be avoided. */ QHBoxLayout *ButtonOverlay::buttonLayout() { fallbackToUsingCustomLayout(); return m_buttonLayout; } /*! * \brief Returns whether the clear button is enabled. */ bool ButtonOverlay::isClearButtonEnabled() const { if (isUsingCustomLayout()) { return m_clearButton != nullptr; } return lineEditForWidget()->isClearButtonEnabled(); } /*! * \brief Returns whether the info button is enabled. */ bool ButtonOverlay::isInfoButtonEnabled() const { return m_infoButtonOrAction != nullptr; } /*! * \brief Sets whether the clear button is enabled. */ void ButtonOverlay::setClearButtonEnabled(bool enabled) { if (auto *const le = lineEditForWidget()) { le->setClearButtonEnabled(enabled); return; } const auto clearButtonEnabled = isClearButtonEnabled(); if (clearButtonEnabled && !enabled) { // disable clear button m_buttonLayout->removeWidget(m_clearButton); delete m_clearButton; m_clearButton = nullptr; } else if (!clearButtonEnabled && enabled) { // enable clear button m_clearButton = new IconButton; m_clearButton->setHidden(isCleared()); m_clearButton->setPixmap(QIcon::fromTheme(QStringLiteral("edit-clear")).pixmap(IconButton::defaultPixmapSize)); m_clearButton->setGeometry(QRect(QPoint(), IconButton::defaultPixmapSize)); m_clearButton->setToolTip(QObject::tr("Clear")); QObject::connect(m_clearButton, &IconButton::clicked, std::bind(&ButtonOverlay::handleClearButtonClicked, this)); m_buttonLayout->addWidget(m_clearButton); } } /*! * \brief Shows an info button with the specified \a pixmap and \a infoText. * * If there is already an info button enabled, it gets replaced with the new * button. * * \sa ButtonOverlay::disableInfoButton() */ void ButtonOverlay::enableInfoButton(const QPixmap &pixmap, const QString &infoText) { if (auto *const le = lineEditForWidget()) { disableInfoButton(); auto *const action = le->addAction(QIcon(pixmap), QLineEdit::TrailingPosition); action->setToolTip(infoText); QObject::connect(action, &QAction::triggered, std::bind(&ButtonOverlay::showInfo, this)); m_infoButtonOrAction = action; return; } auto *infoButton = static_cast<IconButton *>(m_infoButtonOrAction); if (!infoButton) { m_infoButtonOrAction = infoButton = new IconButton; infoButton->setGeometry(QRect(QPoint(), IconButton::defaultPixmapSize)); if (m_clearButton) { m_buttonLayout->insertWidget(m_buttonLayout->count() - 2, infoButton); } else { m_buttonLayout->addWidget(infoButton); } } infoButton->setPixmap(pixmap); infoButton->setToolTip(infoText); } /*! * \brief Hides an info button if one is shown. * \sa ButtonOverlay::enableInfoButton() */ void ButtonOverlay::disableInfoButton() { if (auto *const le = lineEditForWidget()) { if (auto *const infoAction = static_cast<QAction *>(m_infoButtonOrAction)) { le->removeAction(infoAction); m_infoButtonOrAction = nullptr; } return; } if (auto *infoButton = static_cast<IconButton *>(m_infoButtonOrAction)) { m_buttonLayout->removeWidget(infoButton); delete infoButton; m_infoButtonOrAction = nullptr; } } /*! * \brief Adds a custom \a button. * * The button overlay takes ownership over the specified \a button. * * \remarks This function enforces the "custom approach" mentioned in the class documentation * and should therefore be avoided. */ void ButtonOverlay::addCustomButton(QWidget *button) { fallbackToUsingCustomLayout(); m_buttonLayout->addWidget(button); } /*! * \brief Inserts a custom \a button at the specified \a index. * * The button overlay takes ownership over the specified \a button. * * \remarks This function enforces the "custom approach" mentioned in the class documentation * and should therefore be avoided. */ void ButtonOverlay::insertCustomButton(int index, QWidget *button) { fallbackToUsingCustomLayout(); m_buttonLayout->insertWidget(index, button); } /*! * \brief Removes the specified custom \a button; does nothing if \a button has not been added. * * The ownership of widget remains the same as when it was added. */ void ButtonOverlay::removeCustomButton(QWidget *button) { if (isUsingCustomLayout()) { m_buttonLayout->removeWidget(button); } } /*! * \brief Adds a custom \a action. */ void ButtonOverlay::addCustomAction(QAction *action) { if (auto *const le = lineEditForWidget()) { le->addAction(action, QLineEdit::TrailingPosition); } else { addCustomButton(IconButton::fromAction(action, reinterpret_cast<std::uintptr_t>(this))); } } /*! * \brief Inserts a custom \a action at the specified \a index. */ void ButtonOverlay::insertCustomAction(int index, QAction *action) { if (auto *const le = lineEditForWidget()) { const auto actions = le->actions(); le->insertAction(index < actions.size() ? actions[index] : nullptr, action); } else { insertCustomButton(index, IconButton::fromAction(action, reinterpret_cast<std::uintptr_t>(this))); } } /*! * \brief Removes the specified custom \a action; does nothing if \a action has not been added. */ void ButtonOverlay::removeCustomAction(QAction *action) { if (auto *const le = lineEditForWidget()) { le->removeAction(action); } else { removeCustomButton(IconButton::fromAction(action, reinterpret_cast<std::uintptr_t>(this))); } } /*! * \brief Updates the visibility of the clear button. * * This function is meant to be called when subclassing. */ void ButtonOverlay::updateClearButtonVisibility(bool visible) { if (m_clearButton) { m_clearButton->setVisible(visible); } } /*! * \brief Clears the related widget. * * This function is meant to be implemented when subclassing to support the clear button. */ void ButtonOverlay::handleClearButtonClicked() { } /*! * \brief Applies additional handling when the button layout has been created. * * This function is meant to be implemented when subclassing when additional handling is * required. */ void ButtonOverlay::handleCustomLayoutCreated() { } /*! * \brief Switches to the "custom approach". * \remarks This function is internally used when any legacy function is called * or when the QLineEdit for the specified widget can not be determined. */ void ButtonOverlay::fallbackToUsingCustomLayout() { // skip if custom layout is already used if (isUsingCustomLayout()) { return; } // disable QLineEdit's clear button and actions; save configuration auto clearButtonEnabled = false; auto *iconAction = static_cast<QAction *>(m_infoButtonOrAction); QPixmap infoPixmap; QString infoText; QList<QAction *> actions; if (auto *const le = lineEditForWidget()) { if ((clearButtonEnabled = le->isClearButtonEnabled())) { setClearButtonEnabled(false); } if ((iconAction = static_cast<QAction *>(m_infoButtonOrAction))) { const auto icon = iconAction->icon(); const auto sizes = icon.availableSizes(); infoPixmap = icon.pixmap(sizes.empty() ? IconButton::defaultPixmapSize : sizes.front()); infoText = iconAction->toolTip(); disableInfoButton(); } actions = le->actions(); for (auto *const action : actions) { le->removeAction(action); } } // initialize custom layout m_buttonLayout = new QHBoxLayout(m_buttonWidget); m_buttonWidget = new QWidget(m_widget); m_buttonLayout->setAlignment(Qt::AlignCenter | Qt::AlignRight); m_widget->setLayout(m_buttonLayout); handleCustomLayoutCreated(); // restore old configuration if (clearButtonEnabled) { setClearButtonEnabled(true); } if (iconAction) { enableInfoButton(infoPixmap, infoText); } for (auto *const action : actions) { addCustomAction(action); } } /*! * \brief Returns the QLineEdit used to implement the button overlay. * \remarks This is always nullptr in case the "custom approach" is used. */ QLineEdit *ButtonOverlay::lineEditForWidget() const { return isUsingCustomLayout() ? nullptr : static_cast<QLineEdit *>(m_buttonWidget); } /*! * \brief Returns whether the related widget is cleared. * * This method is meant to be implemented when subclassing. */ bool ButtonOverlay::isCleared() const { return false; } /*! * \brief Shows the info text using a tool tip. * * This method is called when the info button is clicked. * * \remarks * This function avoids using QCursor::pos() because it is problematic to use under Wayland. For the action case it seems not * possible to avoid it because the position of QLineEditIconButton used by QLineEdit is not exposed. */ void ButtonOverlay::showInfo() { if (auto const *const le = lineEditForWidget()) { if (auto *const infoAction = static_cast<QAction *>(m_infoButtonOrAction)) { const auto pos = QCursor::pos(); if (!pos.isNull()) { QToolTip::showText(pos, infoAction->toolTip(), m_widget); } } return; } if (auto *const infoButton = static_cast<IconButton *>(m_infoButtonOrAction)) { QToolTip::showText(infoButton->mapToGlobal(infoButton->rect().center()), infoButton->toolTip(), infoButton); } } /*! * \brief Sets the contents margins of the button layout so the overlay buttons will only be shown over the \a editFieldRect and * not interfere with e.g. spin box buttons. * \remarks This function enforces the "custom approach" mentioned in the class documentation * and should therefore be avoided. Of course it makes sense to call it within handleCustomLayoutCreated(). */ void ButtonOverlay::setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding) { const auto margins = m_widget->contentsMargins(); const auto buttonWidth = m_widget->width() - editFieldRect.width(); buttonLayout()->setContentsMargins(margins.left() + frameWidth + padding, margins.top() + frameWidth, margins.right() + frameWidth + padding + buttonWidth, margins.bottom() + frameWidth); } } // namespace QtUtilities 07070100000062000081A4000000000000000000000001636534C90000086F000000000000000000000000000000000000002400000000qtutilities/widgets/buttonoverlay.h#ifndef WIDGETS_BUTTONOVERLAY_H #define WIDGETS_BUTTONOVERLAY_H #include "../global.h" #include <QtGlobal> QT_FORWARD_DECLARE_CLASS(QAction) QT_FORWARD_DECLARE_CLASS(QWidget) QT_FORWARD_DECLARE_CLASS(QHBoxLayout) QT_FORWARD_DECLARE_CLASS(QString) QT_FORWARD_DECLARE_CLASS(QPixmap) QT_FORWARD_DECLARE_CLASS(QMargins) QT_FORWARD_DECLARE_CLASS(QRect) QT_FORWARD_DECLARE_CLASS(QLineEdit) namespace QtUtilities { class IconButton; class ClearComboBox; class ClearSpinBox; class ClearPlainTextEdit; class ClearLineEdit; class QT_UTILITIES_EXPORT ButtonOverlay { // allow these derived classes to use private helpers provided by ButtonOverlay friend class ClearComboBox; friend class ClearSpinBox; friend class ClearPlainTextEdit; friend class ClearLineEdit; public: explicit ButtonOverlay(QWidget *widget); explicit ButtonOverlay(QWidget *widget, QLineEdit *lineEdit); virtual ~ButtonOverlay(); bool isUsingCustomLayout() const; QHBoxLayout *buttonLayout(); bool isClearButtonEnabled() const; void setClearButtonEnabled(bool enabled); bool isInfoButtonEnabled() const; void enableInfoButton(const QPixmap &pixmap, const QString &infoText); void disableInfoButton(); void addCustomButton(QWidget *button); void insertCustomButton(int index, QWidget *button); void removeCustomButton(QWidget *button); void addCustomAction(QAction *action); void insertCustomAction(int index, QAction *action); void removeCustomAction(QAction *action); virtual bool isCleared() const; protected: void updateClearButtonVisibility(bool visible); virtual void handleClearButtonClicked(); virtual void handleCustomLayoutCreated(); private: void fallbackToUsingCustomLayout(); QLineEdit *lineEditForWidget() const; void showInfo(); void setContentsMarginsFromEditFieldRectAndFrameWidth(const QRect &editFieldRect, int frameWidth, int padding = 0); QWidget *m_widget; QWidget *m_buttonWidget; QHBoxLayout *m_buttonLayout; IconButton *m_clearButton; void *m_infoButtonOrAction; }; } // namespace QtUtilities #endif // WIDGETS_BUTTONOVERLAY_H 07070100000063000081A4000000000000000000000001636534C900000630000000000000000000000000000000000000002600000000qtutilities/widgets/clearcombobox.cpp#include "./clearcombobox.h" #include <QHBoxLayout> #include <QStyle> #include <QStyleOptionComboBox> namespace QtUtilities { /*! * \class ClearComboBox * \brief A QComboBox with an embedded button for clearing its contents. */ /// \cond static inline auto *getComboBoxLineEdit(QComboBox *comboBox) { comboBox->setEditable(true); return comboBox->lineEdit(); } /// \endcond /*! * \brief Constructs a clear combo box. * \remarks The combo box is initialized to be editable and which must not be changed. */ ClearComboBox::ClearComboBox(QWidget *parent) : QComboBox(parent) , ButtonOverlay(this, getComboBoxLineEdit(this)) { ButtonOverlay::setClearButtonEnabled(true); } /*! * \brief Destroys the clear combo box. */ ClearComboBox::~ClearComboBox() { } /*! * \brief Updates the visibility of the clear button. */ void ClearComboBox::handleTextChanged(const QString &text) { updateClearButtonVisibility(!text.isEmpty()); } void ClearComboBox::handleClearButtonClicked() { clearEditText(); } void ClearComboBox::handleCustomLayoutCreated() { const QStyle *const s = style(); QStyleOptionComboBox opt; opt.initFrom(this); setContentsMarginsFromEditFieldRectAndFrameWidth( s->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this), s->pixelMetric(QStyle::PM_ComboBoxFrameWidth, &opt, this)); connect(this, &ClearComboBox::currentTextChanged, this, &ClearComboBox::handleTextChanged); } bool ClearComboBox::isCleared() const { return currentText().isEmpty(); } } // namespace QtUtilities 07070100000064000081A4000000000000000000000001636534C900000280000000000000000000000000000000000000002400000000qtutilities/widgets/clearcombobox.h#ifndef WIDGETS_CLEARCOMBOBOX_H #define WIDGETS_CLEARCOMBOBOX_H #include "./buttonoverlay.h" #include <QComboBox> namespace QtUtilities { class QT_UTILITIES_EXPORT ClearComboBox : public QComboBox, public ButtonOverlay { Q_OBJECT Q_PROPERTY(bool cleared READ isCleared) public: explicit ClearComboBox(QWidget *parent = nullptr); ~ClearComboBox() override; bool isCleared() const override; private Q_SLOTS: void handleTextChanged(const QString &text); void handleClearButtonClicked() override; void handleCustomLayoutCreated() override; }; } // namespace QtUtilities #endif // WIDGETS_CLEARCOMBOBOX_H 07070100000065000081A4000000000000000000000001636534C900000524000000000000000000000000000000000000002600000000qtutilities/widgets/clearlineedit.cpp#include "./clearlineedit.h" #include <QStyle> #include <QStyleOptionFrame> namespace QtUtilities { /*! * \class ClearLineEdit * \brief A QLineEdit with an embedded button for clearing its contents. */ /*! * \brief Constructs a clear line edit. */ ClearLineEdit::ClearLineEdit(QWidget *parent) : QLineEdit(parent) , ButtonOverlay(this, this) { ButtonOverlay::setClearButtonEnabled(true); } /*! * \brief Destroys the clear combo box. */ ClearLineEdit::~ClearLineEdit() { } /*! * \brief Updates the visibility of the clear button. */ void ClearLineEdit::handleTextChanged(const QString &text) { updateClearButtonVisibility(!text.isEmpty()); } void ClearLineEdit::handleClearButtonClicked() { clear(); } void ClearLineEdit::handleCustomLayoutCreated() { const QStyle *const s = style(); QStyleOptionFrame opt; opt.initFrom(this); setContentsMarginsFromEditFieldRectAndFrameWidth(s->subElementRect(QStyle::SE_LineEditContents, &opt, this), s->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, m_widget), s->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &opt, m_widget)); connect(this, &ClearLineEdit::textChanged, this, &ClearLineEdit::handleTextChanged); } bool ClearLineEdit::isCleared() const { return text().isEmpty(); } } // namespace QtUtilities 07070100000066000081A4000000000000000000000001636534C9000002B9000000000000000000000000000000000000002400000000qtutilities/widgets/clearlineedit.h#ifndef WIDGETS_CLEARLINEEDIT_H #define WIDGETS_CLEARLINEEDIT_H #include "./buttonoverlay.h" #include <QLineEdit> QT_FORWARD_DECLARE_CLASS(QHBoxLayout) namespace QtUtilities { class IconButton; class QT_UTILITIES_EXPORT ClearLineEdit : public QLineEdit, public ButtonOverlay { Q_OBJECT Q_PROPERTY(bool cleared READ isCleared) public: explicit ClearLineEdit(QWidget *parent = nullptr); ~ClearLineEdit() override; bool isCleared() const override; private Q_SLOTS: void handleTextChanged(const QString &text); void handleClearButtonClicked() override; void handleCustomLayoutCreated() override; }; } // namespace QtUtilities #endif // WIDGETS_CLEARLINEEDIT_H 07070100000067000081A4000000000000000000000001636534C900000855000000000000000000000000000000000000002B00000000qtutilities/widgets/clearplaintextedit.cpp#include "./clearplaintextedit.h" #include <QHBoxLayout> #include <QScrollBar> #include <QStyle> #include <QStyleOptionFrame> using namespace std; namespace QtUtilities { /*! * \class ClearPlainTextEdit * \brief A QPlainTextEdit with an embedded button for clearing its contents. */ /*! * \brief Constructs a clear plain text edit. */ ClearPlainTextEdit::ClearPlainTextEdit(QWidget *parent) : QPlainTextEdit(parent) , ButtonOverlay(viewport()) { handleCustomLayoutCreated(); ButtonOverlay::setClearButtonEnabled(true); } /*! * \brief Destroys the clear plain text edit. */ ClearPlainTextEdit::~ClearPlainTextEdit() { } /*! * \brief Updates the visibility of the clear button. */ void ClearPlainTextEdit::handleTextChanged() { updateClearButtonVisibility(!document()->isEmpty()); } void ClearPlainTextEdit::handleClearButtonClicked() { // do no call clear() here to prevent clearing of undo history QTextCursor cursor(document()); cursor.select(QTextCursor::Document); cursor.removeSelectedText(); } void ClearPlainTextEdit::handleCustomLayoutCreated() { // set alignment to show buttons in the bottom right corner ButtonOverlay::buttonLayout()->setAlignment(Qt::AlignBottom | Qt::AlignRight); const QStyle *const s = style(); QStyleOptionFrame opt; opt.initFrom(this); setContentsMarginsFromEditFieldRectAndFrameWidth(s->subElementRect(QStyle::SE_FrameContents, &opt, this), s->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt, m_widget), s->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &opt, m_widget)); connect(this, &QPlainTextEdit::textChanged, this, &ClearPlainTextEdit::handleTextChanged); // ensure button layout is realigned when scrolling connect(verticalScrollBar(), &QScrollBar::actionTriggered, this, &ClearPlainTextEdit::handleScroll); connect(this, &QPlainTextEdit::cursorPositionChanged, this, &ClearPlainTextEdit::handleScroll); } void ClearPlainTextEdit::handleScroll() { buttonLayout()->update(); } bool ClearPlainTextEdit::isCleared() const { return document()->isEmpty(); } } // namespace QtUtilities 07070100000068000081A4000000000000000000000001636534C9000002AE000000000000000000000000000000000000002900000000qtutilities/widgets/clearplaintextedit.h#ifndef WIDGETS_CLEARPLAINTEXTEDIT_H #define WIDGETS_CLEARPLAINTEXTEDIT_H #include "./buttonoverlay.h" #include <QPlainTextEdit> namespace QtUtilities { class QT_UTILITIES_EXPORT ClearPlainTextEdit : public QPlainTextEdit, public ButtonOverlay { Q_OBJECT Q_PROPERTY(bool cleared READ isCleared) public: explicit ClearPlainTextEdit(QWidget *parent = nullptr); ~ClearPlainTextEdit() override; bool isCleared() const override; private Q_SLOTS: void handleTextChanged(); void handleClearButtonClicked() override; void handleCustomLayoutCreated() override; void handleScroll(); }; } // namespace QtUtilities #endif // WIDGETS_CLEARPLAINTEXTEDIT_H 07070100000069000081A4000000000000000000000001636534C900000719000000000000000000000000000000000000002500000000qtutilities/widgets/clearspinbox.cpp#include "./clearspinbox.h" #include <QHBoxLayout> #include <QStyle> #include <QStyleOptionSpinBox> namespace QtUtilities { /*! * \class ClearSpinBox * \brief A QSpinBox with an embedded button for clearing its contents and the * ability to hide * the minimum value. */ /*! * \brief Constructs a clear spin box. */ ClearSpinBox::ClearSpinBox(QWidget *parent) : QSpinBox(parent) , ButtonOverlay(this, lineEdit()) , m_minimumHidden(false) { ButtonOverlay::setClearButtonEnabled(true); } /*! * \brief Destroys the clear spin box. */ ClearSpinBox::~ClearSpinBox() { } /*! * \brief Updates the visibility of the clear button. */ void ClearSpinBox::handleValueChanged(int value) { updateClearButtonVisibility(value != minimum()); } void ClearSpinBox::handleClearButtonClicked() { setValue(minimum()); } void ClearSpinBox::handleCustomLayoutCreated() { const QStyle *const s = style(); QStyleOptionSpinBox opt; opt.initFrom(this); setContentsMarginsFromEditFieldRectAndFrameWidth( s->subControlRect(QStyle::CC_SpinBox, &opt, QStyle::SC_SpinBoxEditField, this), s->pixelMetric(QStyle::PM_SpinBoxFrameWidth, &opt, this)); connect(this, static_cast<void (ClearSpinBox::*)(int)>(&ClearSpinBox::valueChanged), this, &ClearSpinBox::handleValueChanged); } bool ClearSpinBox::isCleared() const { return value() == minimum(); } int ClearSpinBox::valueFromText(const QString &text) const { if (m_minimumHidden && text.isEmpty()) { return minimum(); } else { return QSpinBox::valueFromText(text); } } QString ClearSpinBox::textFromValue(int val) const { if (m_minimumHidden && (val == minimum())) { return QString(); } else { return QSpinBox::textFromValue(val); } } } // namespace QtUtilities 0707010000006A000081A4000000000000000000000001636534C90000076B000000000000000000000000000000000000002300000000qtutilities/widgets/clearspinbox.h#ifndef WIDGETS_CLEARSPINBOX_H #define WIDGETS_CLEARSPINBOX_H #include "./buttonoverlay.h" #include <QLineEdit> #include <QSpinBox> QT_FORWARD_DECLARE_CLASS(QHBoxLayout) namespace QtUtilities { class IconButton; class QT_UTILITIES_EXPORT ClearSpinBox : public QSpinBox, public ButtonOverlay { Q_OBJECT Q_PROPERTY(bool cleared READ isCleared) Q_PROPERTY(bool minimumHidden READ minimumHidden WRITE setMinimumHidden) Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText) public: explicit ClearSpinBox(QWidget *parent = nullptr); ~ClearSpinBox() override; bool minimumHidden() const; void setMinimumHidden(bool value); QString placeholderText() const; void setPlaceholderText(const QString &placeholderText); bool isCleared() const override; protected: int valueFromText(const QString &text) const override; QString textFromValue(int val) const override; private Q_SLOTS: void handleValueChanged(int value); void handleClearButtonClicked() override; void handleCustomLayoutCreated() override; private: bool m_minimumHidden; }; /*! * \brief Returns whether the minimum value will be hidden. */ inline bool ClearSpinBox::minimumHidden() const { return m_minimumHidden; } /*! * \brief Sets whether the minimum value should be hidden. */ inline void ClearSpinBox::setMinimumHidden(bool value) { m_minimumHidden = value; } /*! * \brief Returns the placeholder text. * \sa QLineEdit::placeholderText() */ inline QString ClearSpinBox::placeholderText() const { return lineEdit()->placeholderText(); } /*! * \brief Sets the placeholder text. * \sa QLineEdit::setPlaceholderText() */ inline void ClearSpinBox::setPlaceholderText(const QString &placeholderText) { lineEdit()->setPlaceholderText(placeholderText); } } // namespace QtUtilities #endif // WIDGETS_CLEARSPINBOX_H 0707010000006B000081A4000000000000000000000001636534C900000E0B000000000000000000000000000000000000002300000000qtutilities/widgets/iconbutton.cpp#include "./iconbutton.h" #include <c++utilities/conversion/stringbuilder.h> #include <QKeyEvent> #include <QStyle> #include <QStyleOptionFocusRect> #include <QStylePainter> using namespace CppUtilities; namespace QtUtilities { /*! * \class IconButton * \brief A simple QAbstractButton implementation displaying a QPixmap. */ /*! * \brief Constructs an icon button. */ IconButton::IconButton(QWidget *parent) : QAbstractButton(parent) { setCursor(Qt::ArrowCursor); setFocusPolicy(Qt::NoFocus); } /*! * \brief Destroys the icon button. */ IconButton::~IconButton() { } /*! * \brief Creates an IconButton for the specified \a action. * \remarks Calling this function on the same action twice with the same \a id yields the * same instance. */ IconButton *IconButton::fromAction(QAction *action, std::uintptr_t id) { const auto propertyName = argsToString("iconButton-", id); const auto existingIconButton = action->property(propertyName.data()); if (!existingIconButton.isNull()) { return existingIconButton.value<IconButton *>(); } auto *const iconButton = new IconButton; iconButton->assignDataFromAction(action); action->setProperty(propertyName.data(), QVariant::fromValue(iconButton)); connect(action, &QAction::changed, iconButton, &IconButton::assignDataFromActionChangedSignal); connect(iconButton, &IconButton::clicked, action, &QAction::trigger); return iconButton; } /*! * \brief Internally called to assign data from a QAction to the icon button. */ void IconButton::assignDataFromActionChangedSignal() { assignDataFromAction(qobject_cast<const QAction *>(QObject::sender())); } /*! * \brief Internally called to assign data from a QAction to the icon button. */ void IconButton::assignDataFromAction(const QAction *action) { auto const icon = action->icon(); const auto sizes = icon.availableSizes(); const auto text = action->text(); setPixmap(icon.pixmap(sizes.empty() ? defaultPixmapSize : sizes.front())); setToolTip(text.isEmpty() ? action->toolTip() : text); } QSize IconButton::sizeHint() const { #if QT_VERSION >= 0x050100 const qreal pixmapRatio = m_pixmap.devicePixelRatio(); #else const qreal pixmapRatio = 1.0; #endif return QSize(static_cast<int>(m_pixmap.width() / pixmapRatio), static_cast<int>(m_pixmap.height() / pixmapRatio)); } void IconButton::paintEvent(QPaintEvent *) { #if QT_VERSION >= 0x050100 const qreal pixmapRatio = m_pixmap.devicePixelRatio(); #else const qreal pixmapRatio = 1.0; #endif auto painter = QStylePainter(this); auto pixmapRect = QRect(0, 0, static_cast<int>(m_pixmap.width() / pixmapRatio), static_cast<int>(m_pixmap.height() / pixmapRatio)); pixmapRect.moveCenter(rect().center()); painter.drawPixmap(pixmapRect, m_pixmap); if (hasFocus()) { auto focusOption = QStyleOptionFocusRect(); focusOption.initFrom(this); focusOption.rect = pixmapRect; #ifdef Q_OS_MAC focusOption.rect.adjust(-4, -4, 4, 4); painter.drawControl(QStyle::CE_FocusFrame, focusOption); #else painter.drawPrimitive(QStyle::PE_FrameFocusRect, focusOption); #endif } } void IconButton::keyPressEvent(QKeyEvent *event) { QAbstractButton::keyPressEvent(event); if (!event->modifiers() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) { click(); } event->accept(); } void IconButton::keyReleaseEvent(QKeyEvent *event) { QAbstractButton::keyReleaseEvent(event); event->accept(); } } // namespace QtUtilities 0707010000006C000081A4000000000000000000000001636534C900000522000000000000000000000000000000000000002100000000qtutilities/widgets/iconbutton.h#ifndef WIDGETS_ICONBUTTON_H #define WIDGETS_ICONBUTTON_H #include "../global.h" #include <QAbstractButton> #include <QAction> #include <QPixmap> #include <QSize> #include <cstdint> namespace QtUtilities { class QT_UTILITIES_EXPORT IconButton : public QAbstractButton { Q_OBJECT Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap) public: explicit IconButton(QWidget *parent = nullptr); ~IconButton() override; static IconButton *fromAction(QAction *action, std::uintptr_t id = 0); const QPixmap &pixmap() const; void setPixmap(const QPixmap &pixmap); QSize sizeHint() const override; static constexpr auto defaultPixmapSize = QSize(16, 16); protected: void paintEvent(QPaintEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; private Q_SLOTS: void assignDataFromActionChangedSignal(); void assignDataFromAction(const QAction *action); private: QPixmap m_pixmap; }; /*! * \brief Returns the pixmap. */ inline const QPixmap &IconButton::pixmap() const { return m_pixmap; } /*! * \brief Sets the pixmap. */ inline void IconButton::setPixmap(const QPixmap &pixmap) { m_pixmap = pixmap; update(); } } // namespace QtUtilities #endif // WIDGETS_ICONBUTTON_H 0707010000006D000081A4000000000000000000000001636534C90000109A000000000000000000000000000000000000002600000000qtutilities/widgets/pathselection.cpp#include "./pathselection.h" #include "./clearlineedit.h" #include "../misc/desktoputils.h" #include <c++utilities/io/path.h> #include <QCompleter> #include <QFileDialog> #include <QFileSystemModel> #include <QHBoxLayout> #include <QMenu> #include <QPushButton> #include <QStringBuilder> #ifndef QT_NO_CONTEXTMENU #include <QContextMenuEvent> #endif #include <functional> #include <memory> using namespace std; namespace QtUtilities { /*! * \class PathSelection * \brief A QLineEdit with a QPushButton next to it which allows to select * file/directory via QFileDialog. */ QCompleter *PathSelection::s_completer = nullptr; /*! * \brief Constructs a path selection widget. */ PathSelection::PathSelection(QWidget *parent) : QWidget(parent) , m_lineEdit(new ClearLineEdit(this)) , m_button(new QPushButton(this)) , m_customDialog(nullptr) , m_customMode(QFileDialog::Directory) { if (!s_completer) { s_completer = new QCompleter; s_completer->setCompletionMode(QCompleter::UnfilteredPopupCompletion); auto *const fileSystemModel = new QFileSystemModel(s_completer); fileSystemModel->setRootPath(QString()); s_completer->setModel(fileSystemModel); } m_lineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); m_lineEdit->installEventFilter(this); m_lineEdit->setCompleter(s_completer); m_button->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); m_button->setText(tr("Select ...")); auto *const layout = new QHBoxLayout(this); layout->setSpacing(3); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(m_lineEdit); layout->addWidget(m_button); setLayout(layout); connect(m_button, &QPushButton::clicked, this, &PathSelection::showFileDialog); } bool PathSelection::eventFilter(QObject *obj, QEvent *event) { #ifndef QT_NO_CONTEXTMENU if (obj == m_lineEdit) { switch (event->type()) { case QEvent::ContextMenu: { unique_ptr<QMenu> menu(m_lineEdit->createStandardContextMenu()); menu->addSeparator(); connect(menu->addAction(QIcon::fromTheme(QStringLiteral("document-open")), tr("Select ...")), &QAction::triggered, this, &PathSelection::showFileDialog); QFileInfo fileInfo(m_lineEdit->text()); if (fileInfo.exists()) { if (fileInfo.isFile()) { connect(menu->addAction(QIcon::fromTheme(QStringLiteral("system-run")), tr("Open")), &QAction::triggered, bind(&openLocalFileOrDir, m_lineEdit->text())); } else if (fileInfo.isDir()) { connect(menu->addAction(QIcon::fromTheme(QStringLiteral("system-file-manager")), tr("Explore")), &QAction::triggered, bind(&openLocalFileOrDir, m_lineEdit->text())); } } menu->exec(static_cast<QContextMenuEvent *>(event)->globalPos()); } return true; default:; } } #endif return QWidget::eventFilter(obj, event); } void PathSelection::showFileDialog() { QString directory; QFileInfo fileInfo(m_lineEdit->text()); if (fileInfo.exists()) { if (fileInfo.isFile()) { directory = fileInfo.absoluteDir().absolutePath(); } else { directory = fileInfo.absolutePath(); } } if (m_customDialog) { m_customDialog->setDirectory(directory); if (m_customDialog->exec() == QFileDialog::Accepted) { m_lineEdit->selectAll(); m_lineEdit->insert(m_customDialog->selectedFiles().join(SEARCH_PATH_SEP_CHAR)); } } else { QFileDialog dialog(this); dialog.setDirectory(directory); dialog.setFileMode(m_customMode); if (window()) { dialog.setWindowTitle(tr("Select path") % QStringLiteral(" - ") % window()->windowTitle()); } else { dialog.setWindowTitle(tr("Select path")); } if (dialog.exec() == QFileDialog::Accepted) { m_lineEdit->selectAll(); m_lineEdit->insert(dialog.selectedFiles().join(SEARCH_PATH_SEP_CHAR)); } } } } // namespace QtUtilities 0707010000006E000081A4000000000000000000000001636534C9000006D9000000000000000000000000000000000000002400000000qtutilities/widgets/pathselection.h#ifndef WIDGETS_PATHSELECTION_H #define WIDGETS_PATHSELECTION_H #include "../global.h" #include <QFileDialog> QT_FORWARD_DECLARE_CLASS(QPushButton) QT_FORWARD_DECLARE_CLASS(QCompleter) namespace QtUtilities { class ClearLineEdit; class QT_UTILITIES_EXPORT PathSelection : public QWidget { Q_OBJECT public: explicit PathSelection(QWidget *parent = nullptr); ClearLineEdit *lineEdit(); const ClearLineEdit *lineEdit() const; void provideCustomFileMode(QFileDialog::FileMode customFileMode); void provideCustomFileDialog(QFileDialog *customFileDialog); protected: bool eventFilter(QObject *obj, QEvent *event) override; private Q_SLOTS: void showFileDialog(); private: ClearLineEdit *m_lineEdit; QPushButton *m_button; QFileDialog *m_customDialog; QFileDialog::FileMode m_customMode; static QCompleter *s_completer; }; /*! * \brief Returns the line edit with the selected path. */ inline ClearLineEdit *PathSelection::lineEdit() { return m_lineEdit; } /*! * \brief Returns the line edit with the selected path. */ inline const ClearLineEdit *PathSelection::lineEdit() const { return m_lineEdit; } /*! * \brief Can be used to provide a custom file mode. * * The default file mode is QFileDialog::Directory. */ inline void PathSelection::provideCustomFileMode(QFileDialog::FileMode customFileMode) { m_customMode = customFileMode; } /*! * \brief Can be used to provide a custom file dialog. * * The default file mode is ignored when a custom file dialog has been * specified. */ inline void PathSelection::provideCustomFileDialog(QFileDialog *customFileDialog) { m_customDialog = customFileDialog; } } // namespace QtUtilities #endif // WIDGETS_PATHSELECTION_H 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!959 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor