diff options
27 files changed, 8009 insertions, 0 deletions
@@ -0,0 +1,2 @@ +The Qt Company Ltd. +Ilya Kotov <forkorov02@ya.ru>, <iokotov@astralinux.ru>, <trialuser02@gmail.com> @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..a8ac813 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,33 @@ +Changelog of Qt5Gtk2 + +---------- +Version 0.1 +* inital release + +Version 0.2 +* removed gconf dependency +* removed warning about missing GtkRange::activate-slider property +* updated copyright and documentation + +Version 0.3 +* using gtk2 library directly +* removed unused code + +Version 0.4 +* added Qt 5.8 support + +Version 0.5 +* fixed documentation +* fixed 'distclean' target +* fixed gcc warnings +* fixed QDockWidget icons (Qt 5.10+) + +Version 0.6 +* show shortcuts in context menu (see QTBUG-69452) +* replaced deprecated functions + +Version 0.7 +* fixed Qt 5.15 support + +Version 0.8 +* Added preview support to the file dialog (Ilya Fedin) @@ -0,0 +1,37 @@ +Qt5Gtk2 - GTK+2.0 integration plugins for Qt5 + +Official home page: https://bitbucket.org/trialuser02/qt5gtk2 + +Requirements: + +- GNU Linux or FreeBSD +- qtbase >= 5.7.0 (with private headers) +- GTK+ 2.0 +- libX11 + +Installation: + +qmake +make +make install (under root) + +To change default installation root you should run the following +command: + +make install INSTALL_ROOT="custom root" + +Add line 'export QT_QPA_PLATFORMTHEME=qt5gtk2' to ~/.profile and re-login. +Alternatively, create the file /etc/X11/Xsession.d/100-qt5gtk2 with +the following line: + +export QT_QPA_PLATFORMTHEME=qt5gtk2 + +Now restart X11 server to take the changes effect. + +Files and directories: + +libqt5gtk2.so - GTK+2.0 platform plugin +libqt5gtk2-style.so - GTK+2.0 style plugin + +Attention! +Environment variable QT_STYLE_OVERRIDE should be removed before usage.
\ No newline at end of file diff --git a/qt5gtk2.pri b/qt5gtk2.pri new file mode 100644 index 0000000..7e3e516 --- /dev/null +++ b/qt5gtk2.pri @@ -0,0 +1,14 @@ +#Some conf to redirect intermediate stuff in separate dirs +UI_DIR=./.build/ui/ +MOC_DIR=./.build/moc/ +OBJECTS_DIR=./.build/obj +RCC_DIR=./.build/rcc + +CONFIG += hide_symbols +DEFINES += QT_DEPRECATED_WARNINGS +QMAKE_DISTCLEAN += -r .build + +#Install paths +unix { + isEmpty(PLUGINDIR):PLUGINDIR = $$[QT_INSTALL_PLUGINS] +} diff --git a/qt5gtk2.pro b/qt5gtk2.pro new file mode 100644 index 0000000..ce96778 --- /dev/null +++ b/qt5gtk2.pro @@ -0,0 +1,36 @@ +#Qt version check (taken from QtCreator) +defineTest(minQtVersion) { + maj = $$1 + min = $$2 + patch = $$3 + isEqual(QT_MAJOR_VERSION, $$maj) { + isEqual(QT_MINOR_VERSION, $$min) { + isEqual(QT_PATCH_VERSION, $$patch) { + return(true) + } + greaterThan(QT_PATCH_VERSION, $$patch) { + return(true) + } + } + greaterThan(QT_MINOR_VERSION, $$min) { + return(true) + } + } + greaterThan(QT_MAJOR_VERSION, $$maj) { + return(true) + } + return(false) +} + +!minQtVersion(5, 7, 0) { + message("Cannot build Qt5Gtk2 with Qt version $${QT_VERSION}.") + error("Use at least Qt 5.7.0.") +} + +TEMPLATE = subdirs + +SUBDIRS += src/qt5gtk2-qtplugin src/qt5gtk2-style + +include(qt5gtk2.pri) + +message (PLUGINDIR=$$PLUGINDIR) diff --git a/src/qt5gtk2-qtplugin/main.cpp b/src/qt5gtk2-qtplugin/main.cpp new file mode 100644 index 0000000..096a226 --- /dev/null +++ b/src/qt5gtk2-qtplugin/main.cpp @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include <qpa/qplatformthemeplugin.h> +#include "qt5gtk2theme.h" + +QT_BEGIN_NAMESPACE + +class Qt5Gtk2ThemePlugin : public QPlatformThemePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QPlatformThemeFactoryInterface_iid FILE "qt5gtk2.json") + +public: + QPlatformTheme *create(const QString &key, const QStringList ¶ms) Q_DECL_OVERRIDE; +}; + +QPlatformTheme *Qt5Gtk2ThemePlugin::create(const QString &key, const QStringList ¶ms) +{ + Q_UNUSED(params); + if (!key.compare(QLatin1String(Qt5Gtk2Theme::name), Qt::CaseInsensitive)) + return new Qt5Gtk2Theme; + + return 0; +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/qt5gtk2-qtplugin/qt5gtk2-qtplugin.pro b/src/qt5gtk2-qtplugin/qt5gtk2-qtplugin.pro new file mode 100644 index 0000000..830550a --- /dev/null +++ b/src/qt5gtk2-qtplugin/qt5gtk2-qtplugin.pro @@ -0,0 +1,30 @@ +include(../../qt5gtk2.pri) + +TARGET = qt5gtk2 + +QT += core-private gui-private + +greaterThan(QT_MINOR_VERSION, 7) { + QT += theme_support-private +} else { + QT += platformsupport-private +} + +HEADERS += \ + qt5gtk2theme.h \ + qt5gtk2dialoghelpers.h + +SOURCES += \ + main.cpp \ + qt5gtk2theme.cpp \ + qt5gtk2dialoghelpers.cpp + +TEMPLATE = lib +TARGET = qt5gtk2 +CONFIG += plugin \ + link_pkgconfig \ + +PKGCONFIG += gtk+-2.0 x11 + +target.path = $$PLUGINDIR/platformthemes +INSTALLS += target diff --git a/src/qt5gtk2-qtplugin/qt5gtk2.json b/src/qt5gtk2-qtplugin/qt5gtk2.json new file mode 100644 index 0000000..cb1b4b1 --- /dev/null +++ b/src/qt5gtk2-qtplugin/qt5gtk2.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "qt5gtk2" ] +} diff --git a/src/qt5gtk2-qtplugin/qt5gtk2dialoghelpers.cpp b/src/qt5gtk2-qtplugin/qt5gtk2dialoghelpers.cpp new file mode 100644 index 0000000..76cb732 --- /dev/null +++ b/src/qt5gtk2-qtplugin/qt5gtk2dialoghelpers.cpp @@ -0,0 +1,649 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include "qt5gtk2dialoghelpers.h" + +#include <qeventloop.h> +#include <qwindow.h> +#include <qcolor.h> +#include <qdebug.h> +#include <qfont.h> +#include <qfileinfo.h> + +#include <private/qguiapplication_p.h> +#include <qpa/qplatformfontdatabase.h> + +#undef signals +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#include <pango/pango.h> + +// The size of the preview we display for selected image files. We set height +// larger than width because generally there is more free space vertically +// than horiztonally (setting the preview image will alway expand the width of +// the dialog, but usually not the height). The image's aspect ratio will always +// be preserved. +#define PREVIEW_WIDTH 256 +#define PREVIEW_HEIGHT 512 + +QT_BEGIN_NAMESPACE + +class QGtk2Dialog : public QWindow +{ + Q_OBJECT + +public: + QGtk2Dialog(GtkWidget *gtkWidget); + ~QGtk2Dialog(); + + GtkDialog *gtkDialog() const; + + void exec(); + bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent); + void hide(); + +Q_SIGNALS: + void accept(); + void reject(); + +protected: + static void onResponse(QGtk2Dialog *dialog, int response); + +private slots: + void onParentWindowDestroyed(); + +private: + GtkWidget *gtkWidget; +}; + +QGtk2Dialog::QGtk2Dialog(GtkWidget *gtkWidget) : gtkWidget(gtkWidget) +{ + g_signal_connect_swapped(G_OBJECT(gtkWidget), "response", G_CALLBACK(onResponse), this); + g_signal_connect(G_OBJECT(gtkWidget), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL); +} + +QGtk2Dialog::~QGtk2Dialog() +{ + gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); + gtk_widget_destroy(gtkWidget); +} + +GtkDialog *QGtk2Dialog::gtkDialog() const +{ + return GTK_DIALOG(gtkWidget); +} + +void QGtk2Dialog::exec() +{ + if (modality() == Qt::ApplicationModal) { + // block input to the whole app, including other GTK dialogs + gtk_dialog_run(gtkDialog()); + } else { + // block input to the window, allow input to other GTK dialogs + QEventLoop loop; + connect(this, SIGNAL(accept()), &loop, SLOT(quit())); + connect(this, SIGNAL(reject()), &loop, SLOT(quit())); + loop.exec(); + } +} + +bool QGtk2Dialog::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + connect(parent, &QWindow::destroyed, this, &QGtk2Dialog::onParentWindowDestroyed, + Qt::UniqueConnection); + setParent(parent); + setFlags(flags); + setModality(modality); + + gtk_widget_realize(gtkWidget); // creates X window + + if (parent) { + XSetTransientForHint(gdk_x11_drawable_get_xdisplay(gtkWidget->window), + gdk_x11_drawable_get_xid(gtkWidget->window), + parent->winId()); + } + + if (modality != Qt::NonModal) { + gdk_window_set_modal_hint(gtkWidget->window, true); + QGuiApplicationPrivate::showModalWindow(this); + } + + gtk_widget_show(gtkWidget); + gdk_window_focus(gtkWidget->window, 0); + return true; +} + +void QGtk2Dialog::hide() +{ + QGuiApplicationPrivate::hideModalWindow(this); + gtk_widget_hide(gtkWidget); +} + +void QGtk2Dialog::onResponse(QGtk2Dialog *dialog, int response) +{ + if (response == GTK_RESPONSE_OK) + emit dialog->accept(); + else + emit dialog->reject(); +} + +void QGtk2Dialog::onParentWindowDestroyed() +{ + // The QGtk2*DialogHelper classes own this object. Make sure the parent doesn't delete it. + setParent(0); +} + +Qt5Gtk2ColorDialogHelper::Qt5Gtk2ColorDialogHelper() +{ + d.reset(new QGtk2Dialog(gtk_color_selection_dialog_new(""))); + connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); + connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); + + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(d->gtkDialog())); + g_signal_connect_swapped(gtkColorSelection, "color-changed", G_CALLBACK(onColorChanged), this); +} + +Qt5Gtk2ColorDialogHelper::~Qt5Gtk2ColorDialogHelper() +{ +} + +bool Qt5Gtk2ColorDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + applyOptions(); + return d->show(flags, modality, parent); +} + +void Qt5Gtk2ColorDialogHelper::exec() +{ + d->exec(); +} + +void Qt5Gtk2ColorDialogHelper::hide() +{ + d->hide(); +} + +void Qt5Gtk2ColorDialogHelper::setCurrentColor(const QColor &color) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); + GdkColor gdkColor; + gdkColor.red = color.red() << 8; + gdkColor.green = color.green() << 8; + gdkColor.blue = color.blue() << 8; + gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); + if (color.alpha() < 255) { + gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), true); + gtk_color_selection_set_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection), color.alpha() << 8); + } +} + +QColor Qt5Gtk2ColorDialogHelper::currentColor() const +{ + GtkDialog *gtkDialog = d->gtkDialog(); + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); + GdkColor gdkColor; + gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtkColorSelection), &gdkColor); + guint16 alpha = gtk_color_selection_get_current_alpha(GTK_COLOR_SELECTION(gtkColorSelection)); + return QColor(gdkColor.red >> 8, gdkColor.green >> 8, gdkColor.blue >> 8, alpha >> 8); +} + +void Qt5Gtk2ColorDialogHelper::onAccepted() +{ + emit accept(); + emit colorSelected(currentColor()); +} + +void Qt5Gtk2ColorDialogHelper::onColorChanged(Qt5Gtk2ColorDialogHelper *dialog) +{ + emit dialog->currentColorChanged(dialog->currentColor()); +} + +void Qt5Gtk2ColorDialogHelper::applyOptions() +{ + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_window_set_title(GTK_WINDOW(gtkDialog), options()->windowTitle().toUtf8()); + + GtkWidget *gtkColorSelection = gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(gtkDialog)); + gtk_color_selection_set_has_opacity_control(GTK_COLOR_SELECTION(gtkColorSelection), options()->testOption(QColorDialogOptions::ShowAlphaChannel)); + + GtkWidget *okButton = 0; + GtkWidget *cancelButton = 0; + GtkWidget *helpButton = 0; + g_object_get(G_OBJECT(gtkDialog), "ok-button", &okButton, "cancel-button", &cancelButton, "help-button", &helpButton, NULL); + if (okButton) + g_object_set(G_OBJECT(okButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), NULL); + if (cancelButton) + g_object_set(G_OBJECT(cancelButton), "visible", !options()->testOption(QColorDialogOptions::NoButtons), NULL); + if (helpButton) + gtk_widget_hide(helpButton); +} + +Qt5Gtk2FileDialogHelper::Qt5Gtk2FileDialogHelper() +{ + d.reset(new QGtk2Dialog(gtk_file_chooser_dialog_new("", 0, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL))); + connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); + connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); + + g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this); + g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this); + + previewWidget = gtk_image_new(); + g_signal_connect(G_OBJECT(d->gtkDialog()), "update-preview", G_CALLBACK(onUpdatePreview), this); + gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(d->gtkDialog()), previewWidget); +} + +Qt5Gtk2FileDialogHelper::~Qt5Gtk2FileDialogHelper() +{ +} + +bool Qt5Gtk2FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + _dir.clear(); + _selection.clear(); + + applyOptions(); + return d->show(flags, modality, parent); +} + +void Qt5Gtk2FileDialogHelper::exec() +{ + d->exec(); +} + +void Qt5Gtk2FileDialogHelper::hide() +{ + // After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder() + // & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual + // values before hiding the dialog + _dir = directory(); + _selection = selectedFiles(); + + d->hide(); +} + +bool Qt5Gtk2FileDialogHelper::defaultNameFilterDisables() const +{ + return false; +} + +void Qt5Gtk2FileDialogHelper::setDirectory(const QUrl &directory) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toLocalFile().toUtf8()); +} + +QUrl Qt5Gtk2FileDialogHelper::directory() const +{ + // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder() + // returns a bogus value -> return the cached value before hiding + if (!_dir.isEmpty()) + return _dir; + + QString ret; + GtkDialog *gtkDialog = d->gtkDialog(); + gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog)); + if (folder) { + ret = QString::fromUtf8(folder); + g_free(folder); + } + return QUrl::fromLocalFile(ret); +} + +void Qt5Gtk2FileDialogHelper::selectFile(const QUrl &filename) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { + QFileInfo fi(filename.toLocalFile()); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8()); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8()); + } else { + gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toLocalFile().toUtf8()); + } +} + +QList<QUrl> Qt5Gtk2FileDialogHelper::selectedFiles() const +{ + // While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames() + // returns a bogus value -> return the cached value before hiding + if (!_selection.isEmpty()) + return _selection; + + QList<QUrl> selection; + GtkDialog *gtkDialog = d->gtkDialog(); + GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog)); + for (GSList *it = filenames; it; it = it->next) + selection += QUrl::fromLocalFile(QString::fromUtf8((const char*)it->data)); + g_slist_free(filenames); + return selection; +} + +void Qt5Gtk2FileDialogHelper::setFilter() +{ + applyOptions(); +} + +void Qt5Gtk2FileDialogHelper::selectNameFilter(const QString &filter) +{ + GtkFileFilter *gtkFilter = _filters.value(filter); + if (gtkFilter) { + GtkDialog *gtkDialog = d->gtkDialog(); + gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); + } +} + +QString Qt5Gtk2FileDialogHelper::selectedNameFilter() const +{ + GtkDialog *gtkDialog = d->gtkDialog(); + GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog)); + return _filterNames.value(gtkFilter); +} + +void Qt5Gtk2FileDialogHelper::onAccepted() +{ + emit accept(); + + QString filter = selectedNameFilter(); + if (filter.isEmpty()) + emit filterSelected(filter); + + QList<QUrl> files = selectedFiles(); + emit filesSelected(files); + if (files.count() == 1) + emit fileSelected(files.first()); +} + +void Qt5Gtk2FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, Qt5Gtk2FileDialogHelper *helper) +{ + QString selection; + gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog)); + if (filename) { + selection = QString::fromUtf8(filename); + g_free(filename); + } + emit helper->currentChanged(QUrl::fromLocalFile(selection)); +} + +void Qt5Gtk2FileDialogHelper::onCurrentFolderChanged(Qt5Gtk2FileDialogHelper *dialog) +{ + emit dialog->directoryEntered(dialog->directory()); +} + +void Qt5Gtk2FileDialogHelper::onUpdatePreview(GtkDialog *gtkDialog, Qt5Gtk2FileDialogHelper *helper) +{ + gchar *filename = gtk_file_chooser_get_preview_filename(GTK_FILE_CHOOSER(gtkDialog)); + if (!filename) { + gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false); + return; + } + + // Don't attempt to open anything which isn't a regular file. If a named pipe, + // this may hang. + QFileInfo fileinfo(filename); + if (!fileinfo.exists() || !fileinfo.isFile()) { + g_free(filename); + gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), false); + return; + } + + // This will preserve the image's aspect ratio. + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(filename, PREVIEW_WIDTH, PREVIEW_HEIGHT, 0); + g_free(filename); + if (pixbuf) { + gtk_image_set_from_pixbuf(GTK_IMAGE(helper->previewWidget), pixbuf); + g_object_unref(pixbuf); + } + gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(gtkDialog), pixbuf ? true : false); +} + +static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialogOptions> &options) +{ + switch (options->fileMode()) { + case QFileDialogOptions::AnyFile: + case QFileDialogOptions::ExistingFile: + case QFileDialogOptions::ExistingFiles: + if (options->acceptMode() == QFileDialogOptions::AcceptOpen) + return GTK_FILE_CHOOSER_ACTION_OPEN; + else + return GTK_FILE_CHOOSER_ACTION_SAVE; + case QFileDialogOptions::Directory: + case QFileDialogOptions::DirectoryOnly: + default: + if (options->acceptMode() == QFileDialogOptions::AcceptOpen) + return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + else + return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; + } +} + +void Qt5Gtk2FileDialogHelper::applyOptions() +{ + GtkDialog *gtkDialog = d->gtkDialog(); + const QSharedPointer<QFileDialogOptions> &opts = options(); + + gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); + gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true); + + const GtkFileChooserAction action = gtkFileChooserAction(opts); + gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action); + + const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles; + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple); + + const bool confirmOverwrite = !opts->testOption(QFileDialogOptions::DontConfirmOverwrite); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite); + + const QStringList nameFilters = opts->nameFilters(); + if (!nameFilters.isEmpty()) + setNameFilters(nameFilters); + + if (opts->initialDirectory().isLocalFile()) + setDirectory(opts->initialDirectory()); + + foreach (const QUrl &filename, opts->initiallySelectedFiles()) + selectFile(filename); + + const QString initialNameFilter = opts->initiallySelectedNameFilter(); + if (!initialNameFilter.isEmpty()) + selectNameFilter(initialNameFilter); + +#if GTK_CHECK_VERSION(2, 20, 0) + GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK); + if (acceptButton) { + if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept)) + gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8()); + else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen) + gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_OPEN); + else + gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_SAVE); + } + + GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL); + if (rejectButton) { + if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject)) + gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8()); + else + gtk_button_set_label(GTK_BUTTON(rejectButton), GTK_STOCK_CANCEL); + } +#endif +} + +void Qt5Gtk2FileDialogHelper::setNameFilters(const QStringList &filters) +{ + GtkDialog *gtkDialog = d->gtkDialog(); + foreach (GtkFileFilter *filter, _filters) + gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter); + + _filters.clear(); + _filterNames.clear(); + + foreach (const QString &filter, filters) { + GtkFileFilter *gtkFilter = gtk_file_filter_new(); + const QString name = filter.left(filter.indexOf(QLatin1Char('('))); + const QStringList extensions = cleanFilterList(filter); + + gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8()); + foreach (const QString &ext, extensions) + gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8()); + + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter); + + _filters.insert(filter, gtkFilter); + _filterNames.insert(gtkFilter, filter); + } +} + +Qt5Gtk2FontDialogHelper::Qt5Gtk2FontDialogHelper() +{ + d.reset(new QGtk2Dialog(gtk_font_selection_dialog_new(""))); + connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted())); + connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject())); +} + +Qt5Gtk2FontDialogHelper::~Qt5Gtk2FontDialogHelper() +{ +} + +bool Qt5Gtk2FontDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) +{ + applyOptions(); + return d->show(flags, modality, parent); +} + +void Qt5Gtk2FontDialogHelper::exec() +{ + d->exec(); +} + +void Qt5Gtk2FontDialogHelper::hide() +{ + d->hide(); +} + +static QString qt_fontToString(const QFont &font) +{ + PangoFontDescription *desc = pango_font_description_new(); + pango_font_description_set_size(desc, (font.pointSizeF() > 0.0 ? font.pointSizeF() : QFontInfo(font).pointSizeF()) * PANGO_SCALE); + pango_font_description_set_family(desc, QFontInfo(font).family().toUtf8()); + + int weight = font.weight(); + if (weight >= QFont::Black) + pango_font_description_set_weight(desc, PANGO_WEIGHT_HEAVY); + else if (weight >= QFont::ExtraBold) + pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRABOLD); + else if (weight >= QFont::Bold) + pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); + else if (weight >= QFont::DemiBold) + pango_font_description_set_weight(desc, PANGO_WEIGHT_SEMIBOLD); + else if (weight >= QFont::Medium) + pango_font_description_set_weight(desc, PANGO_WEIGHT_MEDIUM); + else if (weight >= QFont::Normal) + pango_font_description_set_weight(desc, PANGO_WEIGHT_NORMAL); + else if (weight >= QFont::Light) + pango_font_description_set_weight(desc, PANGO_WEIGHT_LIGHT); + else if (weight >= QFont::ExtraLight) + pango_font_description_set_weight(desc, PANGO_WEIGHT_ULTRALIGHT); + else + pango_font_description_set_weight(desc, PANGO_WEIGHT_THIN); + + int style = font.style(); + if (style == QFont::StyleItalic) + pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); + else if (style == QFont::StyleOblique) + pango_font_description_set_style(desc, PANGO_STYLE_OBLIQUE); + else + pango_font_description_set_style(desc, PANGO_STYLE_NORMAL); + + char *str = pango_font_description_to_string(desc); + QString name = QString::fromUtf8(str); + pango_font_description_free(desc); + g_free(str); + return name; +} + +static QFont qt_fontFromString(const QString &name) +{ + QFont font; + PangoFontDescription *desc = pango_font_description_from_string(name.toUtf8()); + font.setPointSizeF(static_cast<float>(pango_font_description_get_size(desc)) / PANGO_SCALE); + + QString family = QString::fromUtf8(pango_font_description_get_family(desc)); + if (!family.isEmpty()) + font.setFamily(family); + + const int weight = pango_font_description_get_weight(desc); + font.setWeight(QPlatformFontDatabase::weightFromInteger(weight)); + + PangoStyle style = pango_font_description_get_style(desc); + if (style == PANGO_STYLE_ITALIC) + font.setStyle(QFont::StyleItalic); + else if (style == PANGO_STYLE_OBLIQUE) + font.setStyle(QFont::StyleOblique); + else + font.setStyle(QFont::StyleNormal); + + pango_font_description_free(desc); + return font; +} + +void Qt5Gtk2FontDialogHelper::setCurrentFont(const QFont &font) +{ + GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); + gtk_font_selection_dialog_set_font_name(gtkDialog, qt_fontToString(font).toUtf8()); +} + +QFont Qt5Gtk2FontDialogHelper::currentFont() const +{ + GtkFontSelectionDialog *gtkDialog = GTK_FONT_SELECTION_DIALOG(d->gtkDialog()); + gchar *name = gtk_font_selection_dialog_get_font_name(gtkDialog); + QFont font = qt_fontFromString(QString::fromUtf8(name)); + g_free(name); + return font; +} + +void Qt5Gtk2FontDialogHelper::onAccepted() +{ + emit currentFontChanged(currentFont()); + emit accept(); + emit fontSelected(currentFont()); +} + +void Qt5Gtk2FontDialogHelper::applyOptions() +{ + GtkDialog *gtkDialog = d->gtkDialog(); + const QSharedPointer<QFontDialogOptions> &opts = options(); + + gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8()); + + GtkWidget *okButton = gtk_font_selection_dialog_get_ok_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); + GtkWidget *cancelButton = gtk_font_selection_dialog_get_cancel_button(GTK_FONT_SELECTION_DIALOG(gtkDialog)); + if (okButton) + gtk_widget_set_visible(okButton, !options()->testOption(QFontDialogOptions::NoButtons)); + if (cancelButton) + gtk_widget_set_visible(cancelButton, !options()->testOption(QFontDialogOptions::NoButtons)); +} + +QT_END_NAMESPACE + +#include "qt5gtk2dialoghelpers.moc" diff --git a/src/qt5gtk2-qtplugin/qt5gtk2dialoghelpers.h b/src/qt5gtk2-qtplugin/qt5gtk2dialoghelpers.h new file mode 100644 index 0000000..58c0351 --- /dev/null +++ b/src/qt5gtk2-qtplugin/qt5gtk2dialoghelpers.h @@ -0,0 +1,130 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QGTK2DIALOGHELPERS_P_H +#define QGTK2DIALOGHELPERS_P_H + +#include <QHash> +#include <QList> +#include <QUrl> +#include <QScopedPointer> +#include <QString> +#include <qpa/qplatformdialoghelper.h> + +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkDialog GtkDialog; +typedef struct _GtkFileFilter GtkFileFilter; + +QT_BEGIN_NAMESPACE + +class QGtk2Dialog; +class QColor; + +class Qt5Gtk2ColorDialogHelper : public QPlatformColorDialogHelper +{ + Q_OBJECT + +public: + Qt5Gtk2ColorDialogHelper(); + ~Qt5Gtk2ColorDialogHelper(); + + bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; + void exec() Q_DECL_OVERRIDE; + void hide() Q_DECL_OVERRIDE; + + void setCurrentColor(const QColor &color) Q_DECL_OVERRIDE; + QColor currentColor() const Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onAccepted(); + +private: + static void onColorChanged(Qt5Gtk2ColorDialogHelper *helper); + void applyOptions(); + + QScopedPointer<QGtk2Dialog> d; +}; + +class Qt5Gtk2FileDialogHelper : public QPlatformFileDialogHelper +{ + Q_OBJECT + +public: + Qt5Gtk2FileDialogHelper(); + ~Qt5Gtk2FileDialogHelper(); + + bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; + void exec() Q_DECL_OVERRIDE; + void hide() Q_DECL_OVERRIDE; + + bool defaultNameFilterDisables() const Q_DECL_OVERRIDE; + void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE; + QUrl directory() const Q_DECL_OVERRIDE; + void selectFile(const QUrl &filename) Q_DECL_OVERRIDE; + QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE; + void setFilter() Q_DECL_OVERRIDE; + void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE; + QString selectedNameFilter() const Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onAccepted(); + +private: + static void onSelectionChanged(GtkDialog *dialog, Qt5Gtk2FileDialogHelper *helper); + static void onCurrentFolderChanged(Qt5Gtk2FileDialogHelper *helper); + static void onUpdatePreview(GtkDialog *dialog, Qt5Gtk2FileDialogHelper *helper); + void applyOptions(); + void setNameFilters(const QStringList &filters); + + QUrl _dir; + QList<QUrl> _selection; + QHash<QString, GtkFileFilter*> _filters; + QHash<GtkFileFilter*, QString> _filterNames; + QScopedPointer<QGtk2Dialog> d; + GtkWidget *previewWidget; +}; + +class Qt5Gtk2FontDialogHelper : public QPlatformFontDialogHelper +{ + Q_OBJECT + +public: + Qt5Gtk2FontDialogHelper(); + ~Qt5Gtk2FontDialogHelper(); + + bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent) Q_DECL_OVERRIDE; + void exec() Q_DECL_OVERRIDE; + void hide() Q_DECL_OVERRIDE; + + void setCurrentFont(const QFont &font) Q_DECL_OVERRIDE; + QFont currentFont() const Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onAccepted(); + +private: + void applyOptions(); + + QScopedPointer<QGtk2Dialog> d; +}; + +QT_END_NAMESPACE + +#endif // QT5GTK2DIALOGHELPERS_P_H diff --git a/src/qt5gtk2-qtplugin/qt5gtk2theme.cpp b/src/qt5gtk2-qtplugin/qt5gtk2theme.cpp new file mode 100644 index 0000000..a99d108 --- /dev/null +++ b/src/qt5gtk2-qtplugin/qt5gtk2theme.cpp @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include "qt5gtk2theme.h" +#include "qt5gtk2dialoghelpers.h" +#include <QVariant> + +#undef signals +#include <gtk/gtk.h> + +#include <X11/Xlib.h> + +QT_BEGIN_NAMESPACE + +const char *Qt5Gtk2Theme::name = "qt5gtk2"; + +static QString gtkSetting(const gchar *propertyName) +{ + GtkSettings *settings = gtk_settings_get_default(); + gchararray value; + g_object_get(settings, propertyName, &value, NULL); + QString str = QString::fromUtf8(value); + g_free(value); + return str; +} + +Qt5Gtk2Theme::Qt5Gtk2Theme() +{ + // gtk_init will reset the Xlib error handler, and that causes + // Qt applications to quit on X errors. Therefore, we need to manually restore it. + int (*oldErrorHandler)(Display *, XErrorEvent *) = XSetErrorHandler(NULL); + + gtk_init(0, 0); + + XSetErrorHandler(oldErrorHandler); +} + +QVariant Qt5Gtk2Theme::themeHint(QPlatformTheme::ThemeHint hint) const +{ + switch (hint) { + case QPlatformTheme::SystemIconThemeName: + return QVariant(gtkSetting("gtk-icon-theme-name")); + case QPlatformTheme::SystemIconFallbackThemeName: + return QVariant(gtkSetting("gtk-fallback-icon-theme")); + case QPlatformTheme::StyleNames: + { + QStringList styleNames; + styleNames << "qt5gtk2"; + //styleNames << QGnomeTheme::themeHint(hint).toStringList(); + return styleNames; + } +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + case QPlatformTheme::ShowShortcutsInContextMenus: + return true; +#endif + default: + return QGnomeTheme::themeHint(hint); + } +} + +QString Qt5Gtk2Theme::gtkFontName() const +{ + QString cfgFontName = gtkSetting("gtk-font-name"); + if (!cfgFontName.isEmpty()) + return cfgFontName; + return QGnomeTheme::gtkFontName(); +} + +bool Qt5Gtk2Theme::usePlatformNativeDialog(DialogType type) const +{ + switch (type) { + case ColorDialog: + return true; + case FileDialog: + return true; + case FontDialog: + return true; + default: + return false; + } +} + +QPlatformDialogHelper *Qt5Gtk2Theme::createPlatformDialogHelper(DialogType type) const +{ + switch (type) { + case ColorDialog: + return new Qt5Gtk2ColorDialogHelper; + case FileDialog: + return new Qt5Gtk2FileDialogHelper; + case FontDialog: + return new Qt5Gtk2FontDialogHelper; + default: + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/qt5gtk2-qtplugin/qt5gtk2theme.h b/src/qt5gtk2-qtplugin/qt5gtk2theme.h new file mode 100644 index 0000000..09370bb --- /dev/null +++ b/src/qt5gtk2-qtplugin/qt5gtk2theme.h @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QT5GTK2THEME_H +#define QT5GTK2THEME_H + +#include <private/qgenericunixthemes_p.h> + +QT_BEGIN_NAMESPACE + +class Qt5Gtk2Theme : public QGnomeTheme +{ +public: + Qt5Gtk2Theme(); + + virtual QVariant themeHint(ThemeHint hint) const Q_DECL_OVERRIDE; + virtual QString gtkFontName() const Q_DECL_OVERRIDE; + + bool usePlatformNativeDialog(DialogType type) const Q_DECL_OVERRIDE; + QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const Q_DECL_OVERRIDE; + + static const char *name; +}; + +QT_END_NAMESPACE + +#endif // QT5GTK2THEME_H diff --git a/src/qt5gtk2-style/plugin.cpp b/src/qt5gtk2-style/plugin.cpp new file mode 100644 index 0000000..4ab0bb5 --- /dev/null +++ b/src/qt5gtk2-style/plugin.cpp @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include <QStylePlugin> +#include "qgtkstyle_p.h" + +QT_BEGIN_NAMESPACE + +class Qt5Gtk2StylePlugin : public QStylePlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QStyleFactoryInterface" FILE "qt5gtk2.json") + +public: + QStyle *create(const QString &key); +}; + +QStyle *Qt5Gtk2StylePlugin::create(const QString &key) +{ + if (key == "qt5gtk2") + return new QGtkStyle; + return 0; +} + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/qt5gtk2-style/qgtk2painter.cpp b/src/qt5gtk2-style/qgtk2painter.cpp new file mode 100644 index 0000000..fc7ebca --- /dev/null +++ b/src/qt5gtk2-style/qgtk2painter.cpp @@ -0,0 +1,603 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include "qgtk2painter_p.h" + +#include <QtGlobal> +#if !defined(QT_NO_STYLE_GTK) + +// This class is primarily a wrapper around the gtk painter functions +// and takes care of converting all such calls into cached Qt pixmaps. + +#include "qgtkstyle_p_p.h" +#include <private/qhexstring_p.h> +#include <QWidget> +#include <QPixmapCache> + +QT_BEGIN_NAMESPACE + +// To recover alpha we apply the gtk painting function two times to +// white, and black window backgrounds. This can be used to +// recover the premultiplied alpha channel +QPixmap QGtk2Painter::renderTheme(uchar *bdata, uchar *wdata, const QRect &rect) const +{ + const int bytecount = rect.width() * rect.height() * 4; + for (int index = 0; index < bytecount ; index += 4) { + uchar val = bdata[index + GTK_BLUE]; + if (m_alpha) { + int alphaval = qMax(bdata[index + GTK_BLUE] - wdata[index + GTK_BLUE], + bdata[index + GTK_GREEN] - wdata[index + GTK_GREEN]); + alphaval = qMax(alphaval, bdata[index + GTK_RED] - wdata[index + GTK_RED]) + 255; + bdata[index + QT_ALPHA] = alphaval; + } + bdata[index + QT_RED] = bdata[index + GTK_RED]; + bdata[index + QT_GREEN] = bdata[index + GTK_GREEN]; + bdata[index + QT_BLUE] = val; + } + QImage converted((const uchar*)bdata, rect.width(), rect.height(), m_alpha ? + QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32); + + if (m_hflipped || m_vflipped) { + return QPixmap::fromImage(converted.mirrored(m_hflipped, m_vflipped)); + } else { + // on raster graphicssystem we need to do a copy here, because + // we intend to deallocate the qimage bits shortly after... + return QPixmap::fromImage(converted.copy()); + } +} + +// This macro is responsible for painting any GtkStyle painting function onto a QPixmap +#define DRAW_TO_CACHE(draw_func) \ + if (rect.width() > QWIDGETSIZE_MAX || rect.height() > QWIDGETSIZE_MAX) \ + return; \ + QRect pixmapRect(0, 0, rect.width(), rect.height()); \ + { \ + GdkPixmap *pixmap = gdk_pixmap_new((GdkDrawable*)(m_window->window), \ + rect.width(), rect.height(), -1); \ + if (!pixmap) \ + return; \ + style = gtk_style_attach (style, m_window->window); \ + gdk_draw_rectangle(pixmap, m_alpha ? style->black_gc : *style->bg_gc, \ + true, 0, 0, rect.width(), rect.height()); \ + draw_func; \ + GdkPixbuf *imgb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, \ + rect.width(), rect.height()); \ + if (!imgb) \ + return; \ + imgb = gdk_pixbuf_get_from_drawable(imgb, pixmap, NULL, 0, 0, 0, 0, \ + rect.width(), rect.height()); \ + uchar* bdata = (uchar*)gdk_pixbuf_get_pixels(imgb); \ + if (m_alpha) { \ + gdk_draw_rectangle(pixmap, style->white_gc, true, 0, 0, \ + rect.width(), rect.height()); \ + draw_func; \ + GdkPixbuf *imgw = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, \ + rect.width(), rect.height()); \ + if (!imgw) \ + return; \ + imgw = gdk_pixbuf_get_from_drawable(imgw, pixmap, NULL, 0, 0, 0, 0,\ + rect.width(), rect.height()); \ + uchar* wdata = (uchar*)gdk_pixbuf_get_pixels(imgw); \ + cache = renderTheme(bdata, wdata, rect); \ + g_object_unref(imgw); \ + } else { \ + cache = renderTheme(bdata, 0, rect); \ + } \ + gdk_drawable_unref(pixmap); \ + g_object_unref(imgb); \ + } + +QGtk2Painter::QGtk2Painter() : QGtkPainter(), m_window(QGtkStylePrivate::gtkWidget("GtkWindow")) +{} + +// Note currently painted without alpha for performance reasons +void QGtk2Painter::paintBoxGap(GtkWidget *gtkWidget, const gchar* part, + const QRect &paintRect, GtkStateType state, + GtkShadowType shadow, GtkPositionType gap_side, + gint x, gint width, + GtkStyle *style) +{ + if (!paintRect.isValid()) + return; + + QPixmap cache; + QRect rect = paintRect; + + // To avoid exhausting cache on large tabframes we cheat a bit by + // tiling the center part. + + const int maxHeight = 256; + const int border = 16; + if (rect.height() > maxHeight && (gap_side == GTK_POS_TOP || gap_side == GTK_POS_BOTTOM)) + rect.setHeight(2 * border + 1); + + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) + % HexString<uchar>(gap_side) + % HexString<gint>(width) + % HexString<gint>(x); + + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_box_gap (style, + pixmap, + state, + shadow, + NULL, + gtkWidget, + (const gchar*)part, + 0, 0, + rect.width(), + rect.height(), + gap_side, + x, + width)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + if (rect.size() != paintRect.size()) { + // We assume we can stretch the middle tab part + // Note: the side effect of this is that pinstripe patterns will get fuzzy + const QSize size = cache.size(); + // top part + m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(), + paintRect.width(), border), cache, + QRect(0, 0, size.width(), border)); + + // tiled center part + QPixmap tilePart(cache.width(), 1); + QPainter scanLinePainter(&tilePart); + scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1)); + scanLinePainter.end(); + m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border, + paintRect.width(), paintRect.height() - 2*border), tilePart); + + // bottom part + m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border, + paintRect.width(), border), cache, + QRect(0, size.height() - border, size.width(), border)); + } else + m_painter->drawPixmap(paintRect.topLeft(), cache); +} + +void QGtk2Painter::paintBox(GtkWidget *gtkWidget, const gchar* part, + const QRect &paintRect, GtkStateType state, + GtkShadowType shadow, GtkStyle *style, + const QString &pmKey) +{ + if (!paintRect.isValid()) + return; + + QPixmap cache; + QRect rect = paintRect; + + // To avoid exhausting cache on large tabframes we cheat a bit by + // tiling the center part. + + const int maxHeight = 256; + const int maxArea = 256*512; + const int border = 32; + if (rect.height() > maxHeight && (rect.width()*rect.height() > maxArea)) + rect.setHeight(2 * border + 1); + + QString pixmapName = uniqueName(QLS(part), state, shadow, + rect.size(), gtkWidget) % pmKey; + + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_box (style, + pixmap, + state, + shadow, + NULL, + gtkWidget, + part, + 0, 0, + rect.width(), + rect.height())); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + if (rect.size() != paintRect.size()) { + // We assume we can stretch the middle tab part + // Note: the side effect of this is that pinstripe patterns will get fuzzy + const QSize size = cache.size(); + // top part + m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top(), + paintRect.width(), border), cache, + QRect(0, 0, size.width(), border)); + + // tiled center part + QPixmap tilePart(cache.width(), 1); + QPainter scanLinePainter(&tilePart); + scanLinePainter.drawPixmap(QRect(0, 0, tilePart.width(), tilePart.height()), cache, QRect(0, border, size.width(), 1)); + scanLinePainter.end(); + m_painter->drawTiledPixmap(QRect(paintRect.left(), paintRect.top() + border, + paintRect.width(), paintRect.height() - 2*border), tilePart); + + // bottom part + m_painter->drawPixmap(QRect(paintRect.left(), paintRect.top() + paintRect.height() - border, + paintRect.width(), border), cache, + QRect(0, size.height() - border, size.width(), border)); + } else + m_painter->drawPixmap(paintRect.topLeft(), cache); +} + +void QGtk2Painter::paintHline(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, + GtkStyle *style, int x1, int x2, int y, + const QString &pmKey) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) + % HexString<int>(x1) + % HexString<int>(x2) + % HexString<int>(y) + % pmKey; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_hline (style, + pixmap, + state, + NULL, + gtkWidget, + part, + x1, x2, y)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + +void QGtk2Painter::paintVline(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, + GtkStyle *style, int y1, int y2, int x, + const QString &pmKey) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) + % HexString<int>(y1) + % HexString<int>(y2) + % HexString<int>(x) + % pmKey; + + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_vline (style, + pixmap, + state, + NULL, + gtkWidget, + part, + y1, y2, + x)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + m_painter->drawPixmap(rect.topLeft(), cache); +} + + +void QGtk2Painter::paintExpander(GtkWidget *gtkWidget, + const gchar* part, const QRect &rect, + GtkStateType state, GtkExpanderStyle expander_state, + GtkStyle *style, const QString &pmKey) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) + % HexString<uchar>(expander_state) + % pmKey; + + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_expander (style, pixmap, + state, NULL, + gtkWidget, part, + rect.width()/2, + rect.height()/2, + expander_state)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + +void QGtk2Painter::paintFocus(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, + GtkStyle *style, const QString &pmKey) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, GTK_SHADOW_NONE, rect.size(), gtkWidget) % pmKey; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_focus (style, pixmap, state, NULL, + gtkWidget, + part, + 0, 0, + rect.width(), + rect.height())); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + + +void QGtk2Painter::paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, + GtkShadowType shadow, GdkWindowEdge edge, + GtkStyle *style, const QString &pmKey) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % pmKey; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_resize_grip (style, pixmap, state, + NULL, gtkWidget, + part, edge, 0, 0, + rect.width(), + rect.height())); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + + +void QGtk2Painter::paintArrow(GtkWidget *gtkWidget, const gchar* part, + const QRect &arrowrect, GtkArrowType arrow_type, + GtkStateType state, GtkShadowType shadow, + gboolean fill, GtkStyle *style, const QString &pmKey) +{ + QRect rect = m_cliprect.isValid() ? m_cliprect : arrowrect; + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) + % HexString<uchar>(arrow_type) + % pmKey; + + GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; + int xOffset = m_cliprect.isValid() ? arrowrect.x() - m_cliprect.x() : 0; + int yOffset = m_cliprect.isValid() ? arrowrect.y() - m_cliprect.y() : 0; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_arrow (style, pixmap, state, shadow, + >kCliprect, + gtkWidget, + part, + arrow_type, fill, + xOffset, yOffset, + arrowrect.width(), + arrowrect.height())) + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + + +void QGtk2Painter::paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, + GtkOrientation orientation, GtkStyle *style) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) + % HexString<uchar>(orientation); + + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_handle (style, + pixmap, + state, + shadow, + NULL, + gtkWidget, + part, 0, 0, + rect.width(), + rect.height(), + orientation)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + m_painter->drawPixmap(rect.topLeft(), cache); +} + + +void QGtk2Painter::paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, + GtkStyle *style, GtkOrientation orientation, + const QString &pmKey) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) % pmKey; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_slider (style, + pixmap, + state, + shadow, + NULL, + gtkWidget, + part, + 0, 0, + rect.width(), + rect.height(), + orientation)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + m_painter->drawPixmap(rect.topLeft(), cache); +} + + +void QGtk2Painter::paintShadow(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, + GtkShadowType shadow, GtkStyle *style, + const QString &pmKey) + +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_shadow(style, pixmap, state, shadow, NULL, + gtkWidget, part, 0, 0, rect.width(), rect.height())); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + m_painter->drawPixmap(rect.topLeft(), cache); +} + +void QGtk2Painter::paintFlatBox(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, + GtkShadowType shadow, GtkStyle *style, + const QString &pmKey) +{ + if (!rect.isValid()) + return; + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size()) % pmKey; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_flat_box (style, + pixmap, + state, + shadow, + NULL, + gtkWidget, + part, 0, 0, + rect.width(), + rect.height())); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + m_painter->drawPixmap(rect.topLeft(), cache); +} + +void QGtk2Painter::paintExtention(GtkWidget *gtkWidget, + const gchar *part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, + GtkPositionType gap_pos, GtkStyle *style) +{ + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(QLS(part), state, shadow, rect.size(), gtkWidget) + % HexString<uchar>(gap_pos); + + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_extension (style, pixmap, state, shadow, + NULL, gtkWidget, + (const gchar*)part, 0, 0, + rect.width(), + rect.height(), + gap_pos)); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + +void QGtk2Painter::paintOption(GtkWidget *gtkWidget, const QRect &radiorect, + GtkStateType state, GtkShadowType shadow, + GtkStyle *style, const QString &detail) + +{ + QRect rect = m_cliprect.isValid() ? m_cliprect : radiorect; + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(detail, state, shadow, rect.size()); + GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; + int xOffset = m_cliprect.isValid() ? radiorect.x() - m_cliprect.x() : 0; + int yOffset = m_cliprect.isValid() ? radiorect.y() - m_cliprect.y() : 0; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_option(style, pixmap, + state, shadow, + >kCliprect, + gtkWidget, + detail.toLatin1(), + xOffset, yOffset, + radiorect.width(), + radiorect.height())); + + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + +void QGtk2Painter::paintCheckbox(GtkWidget *gtkWidget, const QRect &checkrect, + GtkStateType state, GtkShadowType shadow, + GtkStyle *style, const QString &detail) + +{ + QRect rect = m_cliprect.isValid() ? m_cliprect : checkrect; + if (!rect.isValid()) + return; + + QPixmap cache; + QString pixmapName = uniqueName(detail, state, shadow, rect.size()); + GdkRectangle gtkCliprect = {0, 0, rect.width(), rect.height()}; + int xOffset = m_cliprect.isValid() ? checkrect.x() - m_cliprect.x() : 0; + int yOffset = m_cliprect.isValid() ? checkrect.y() - m_cliprect.y() : 0; + if (!m_usePixmapCache || !QPixmapCache::find(pixmapName, &cache)) { + DRAW_TO_CACHE(gtk_paint_check (style, + pixmap, + state, + shadow, + >kCliprect, + gtkWidget, + detail.toLatin1(), + xOffset, yOffset, + checkrect.width(), + checkrect.height())); + if (m_usePixmapCache) + QPixmapCache::insert(pixmapName, cache); + } + + m_painter->drawPixmap(rect.topLeft(), cache); +} + +QT_END_NAMESPACE + +#endif //!defined(QT_NO_STYLE_GTK) diff --git a/src/qt5gtk2-style/qgtk2painter_p.h b/src/qt5gtk2-style/qgtk2painter_p.h new file mode 100644 index 0000000..05f6490 --- /dev/null +++ b/src/qt5gtk2-style/qgtk2painter_p.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QGTK2PAINTER_P_H +#define QGTK2PAINTER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGlobal> +#if !defined(QT_NO_STYLE_GTK) + +#include "qgtkpainter_p.h" + +QT_BEGIN_NAMESPACE + +class QGtk2Painter : public QGtkPainter +{ +public: + QGtk2Painter(); + + void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x, + gint width, GtkStyle *style) Q_DECL_OVERRIDE; + void paintBox(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, + const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, + int x1, int x2, int y, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, + int y1, int y2, int x, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, + GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, + const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow, + gboolean fill, GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style) Q_DECL_OVERRIDE; + void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GtkStyle *style, const QString &pmKey = QString()) Q_DECL_OVERRIDE; + void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString()) Q_DECL_OVERRIDE; + void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GtkPositionType gap_pos, GtkStyle *style) Q_DECL_OVERRIDE; + void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) Q_DECL_OVERRIDE; + void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) Q_DECL_OVERRIDE; + +private: + QPixmap renderTheme(uchar *bdata, uchar *wdata, const QRect &rect) const; + + GtkWidget *m_window; +}; + +QT_END_NAMESPACE + +#endif //!defined(QT_NO_STYLE_QGTK) + +#endif // QGTK2PAINTER_P_H diff --git a/src/qt5gtk2-style/qgtkglobal_p.h b/src/qt5gtk2-style/qgtkglobal_p.h new file mode 100644 index 0000000..ff3d612 --- /dev/null +++ b/src/qt5gtk2-style/qgtkglobal_p.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QGTKGLOBAL_P_H +#define QGTKGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGlobal> +#if !defined(QT_NO_STYLE_GTK) + +#undef signals // Collides with GTK symbols +#include <gtk/gtk.h> + +typedef unsigned long XID; + +#undef GTK_OBJECT_FLAGS +#define GTK_OBJECT_FLAGS(obj)(((GtkObject*)(obj))->flags) + +#define QLS(x) QLatin1String(x) + +QT_BEGIN_NAMESPACE + +#if Q_BYTE_ORDER == Q_BIG_ENDIAN +# define QT_RED 3 +# define QT_GREEN 2 +# define QT_BLUE 1 +# define QT_ALPHA 0 +#else +# define QT_RED 0 +# define QT_GREEN 1 +# define QT_BLUE 2 +# define QT_ALPHA 3 +#endif +# define GTK_RED 2 +# define GTK_GREEN 1 +# define GTK_BLUE 0 +# define GTK_ALPHA 3 + +QT_END_NAMESPACE + +#endif // !QT_NO_STYLE_GTK +#endif // QGTKGLOBAL_P_H diff --git a/src/qt5gtk2-style/qgtkpainter.cpp b/src/qt5gtk2-style/qgtkpainter.cpp new file mode 100644 index 0000000..ca19f7c --- /dev/null +++ b/src/qt5gtk2-style/qgtkpainter.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include "qgtkpainter_p.h" + +#if !defined(QT_NO_STYLE_GTK) + +#include <private/qhexstring_p.h> + +QT_BEGIN_NAMESPACE + +QGtkPainter::QGtkPainter() +{ + reset(0); +} + +QGtkPainter::~QGtkPainter() +{ +} + +void QGtkPainter::reset(QPainter *painter) +{ + m_painter = painter; + m_alpha = true; + m_hflipped = false; + m_vflipped = false; + m_usePixmapCache = true; + m_cliprect = QRect(); +} + +QString QGtkPainter::uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow, + const QSize &size, GtkWidget *widget) +{ + // Note the widget arg should ideally use the widget path, though would compromise performance + QString tmp = key + % HexString<uint>(state) + % HexString<uint>(shadow) + % HexString<uint>(size.width()) + % HexString<uint>(size.height()) + % HexString<quint64>(quint64(widget)); + return tmp; +} + +QT_END_NAMESPACE + +#endif //!defined(QT_NO_STYLE_GTK) diff --git a/src/qt5gtk2-style/qgtkpainter_p.h b/src/qt5gtk2-style/qgtkpainter_p.h new file mode 100644 index 0000000..bcaae0b --- /dev/null +++ b/src/qt5gtk2-style/qgtkpainter_p.h @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QGTKPAINTER_H +#define QGTKPAINTER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGlobal> +#if !defined(QT_NO_STYLE_GTK) + +#include "qgtkglobal_p.h" +#include <QSize> +#include <QRect> +#include <QPoint> +#include <QPixmap> +#include <QPainter> + +QT_BEGIN_NAMESPACE + +class QGtkPainter +{ +public: + QGtkPainter(); + virtual ~QGtkPainter(); + + void reset(QPainter *painter = 0); + + void setAlphaSupport(bool value) { m_alpha = value; } + void setClipRect(const QRect &rect) { m_cliprect = rect; } + void setFlipHorizontal(bool value) { m_hflipped = value; } + void setFlipVertical(bool value) { m_vflipped = value; } + void setUsePixmapCache(bool value) { m_usePixmapCache = value; } + + virtual void paintBoxGap(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, GtkPositionType gap_side, gint x, + gint width, GtkStyle *style) = 0; + virtual void paintBox(GtkWidget *gtkWidget, const gchar* part, + const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, + const QString &pmKey = QString()) = 0; + virtual void paintHline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, + int x1, int x2, int y, const QString &pmKey = QString()) = 0; + virtual void paintVline(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, + int y1, int y2, int x, const QString &pmKey = QString()) = 0; + virtual void paintExpander(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, + GtkExpanderStyle expander_state, GtkStyle *style, const QString &pmKey = QString()) = 0; + virtual void paintFocus(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkStyle *style, + const QString &pmKey = QString()) = 0; + virtual void paintResizeGrip(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GdkWindowEdge edge, GtkStyle *style, const QString &pmKey = QString()) = 0; + virtual void paintArrow(GtkWidget *gtkWidget, const gchar* part, const QRect &arrowrect, GtkArrowType arrow_type, GtkStateType state, GtkShadowType shadow, + gboolean fill, GtkStyle *style, const QString &pmKey = QString()) = 0; + virtual void paintHandle(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, + GtkStateType state, GtkShadowType shadow, GtkOrientation orientation, GtkStyle *style) = 0; + virtual void paintSlider(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GtkStyle *style, GtkOrientation orientation, const QString &pmKey = QString()) = 0; + virtual void paintShadow(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GtkStyle *style, const QString &pmKey = QString()) = 0; + virtual void paintFlatBox(GtkWidget *gtkWidget, const gchar* part, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString & = QString()) = 0; + virtual void paintExtention(GtkWidget *gtkWidget, const gchar *part, const QRect &rect, GtkStateType state, GtkShadowType shadow, + GtkPositionType gap_pos, GtkStyle *style) = 0; + virtual void paintOption(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) = 0; + virtual void paintCheckbox(GtkWidget *gtkWidget, const QRect &rect, GtkStateType state, GtkShadowType shadow, GtkStyle *style, const QString &detail) = 0; + +protected: + static QString uniqueName(const QString &key, GtkStateType state, GtkShadowType shadow, const QSize &size, GtkWidget *widget = 0); + + QPainter *m_painter; + bool m_alpha; + bool m_hflipped; + bool m_vflipped; + bool m_usePixmapCache; + QRect m_cliprect; +}; + +QT_END_NAMESPACE + +#endif //!defined(QT_NO_STYLE_QGTK) + +#endif // QGTKPAINTER_H diff --git a/src/qt5gtk2-style/qgtkstyle.cpp b/src/qt5gtk2-style/qgtkstyle.cpp new file mode 100644 index 0000000..024fe5f --- /dev/null +++ b/src/qt5gtk2-style/qgtkstyle.cpp @@ -0,0 +1,4254 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include "qgtkstyle_p.h" + +#if !defined(QT_NO_STYLE_GTK) + +#include <private/qapplication_p.h> +#include <QSettings> +#include <QDialogButtonBox> +#include <QStatusBar> +#include <QLineEdit> +#include <QWidget> +#include <QListView> +#include <QApplication> +#include <QStyleOption> +#include <QPushButton> +#include <QPainter> +#include <QMainWindow> +#include <QToolBar> +#include <QHeaderView> +#include <QMenuBar> +#include <QComboBox> +#include <QSpinBox> +#include <QScrollBar> +#include <QAbstractButton> +#include <QToolButton> +#include <QGroupBox> +#include <QRadioButton> +#include <QCheckBox> +#include <QTreeView> +#include <QStyledItemDelegate> +#include <QWizard> + +#include <QPixmapCache> +#include <private/qstyleanimation_p.h> +#undef signals // Collides with GTK stymbols +#include "qgtkpainter_p.h" +#include "qstylehelper_p.h" +#include "qgtkstyle_p_p.h" + +#ifndef Q_FALLTHROUGH +#define Q_FALLTHROUGH() (void)0 +#endif + + +QT_BEGIN_NAMESPACE + +static GtkStateType qt_gtk_state(const QStyleOption *option) +{ + GtkStateType state = GTK_STATE_NORMAL; + if (!(option->state & QStyle::State_Enabled)) + state = GTK_STATE_INSENSITIVE; + else if (option->state & QStyle::State_MouseOver) + state = GTK_STATE_PRELIGHT; + + return state; +} + +static QPixmap qt_gtk_get_icon(const char* iconName, GtkIconSize size = GTK_ICON_SIZE_BUTTON) +{ + GtkStyle *style = QGtkStylePrivate::gtkStyle(); + GtkIconSet* iconSet = gtk_icon_factory_lookup_default (iconName); + GdkPixbuf* icon = gtk_icon_set_render_icon(iconSet, + style, + GTK_TEXT_DIR_LTR, + GTK_STATE_NORMAL, + size, + NULL, + "button"); + uchar* data = (uchar*)gdk_pixbuf_get_pixels(icon); + int width = gdk_pixbuf_get_width(icon); + int height = gdk_pixbuf_get_height(icon); + QImage converted(width, height, QImage::Format_ARGB32); + uchar* tdata = (uchar*)converted.bits(); + + for ( int index = 0 ; index < height * width*4 ; index +=4 ) { + //int index = y * rowstride + x; + tdata[index + QT_RED] = data[index + GTK_RED]; + tdata[index + QT_GREEN] = data[index + GTK_GREEN]; + tdata[index + QT_BLUE] = data[index + GTK_BLUE]; + tdata[index + QT_ALPHA] = data[index + GTK_ALPHA]; + } + + g_object_unref(icon); + + // should we free iconset? + return QPixmap::fromImage(converted); +} + +static void qt_gtk_draw_mdibutton(QPainter *painter, const QStyleOptionTitleBar *option, const QRect &tmp, bool hover, bool sunken) +{ + QColor dark; + dark.setHsv(option->palette.button().color().hue(), + qMin(255, (int)(option->palette.button().color().saturation()*1.9)), + qMin(255, (int)(option->palette.button().color().value()*0.7))); + + QColor highlight = option->palette.highlight().color(); + + bool active = (option->titleBarState & QStyle::State_Active); + QColor titleBarHighlight(255, 255, 255, 60); + + if (sunken) + painter->fillRect(tmp.adjusted(1, 1, -1, -1), option->palette.highlight().color().darker(120)); + else if (hover) + painter->fillRect(tmp.adjusted(1, 1, -1, -1), QColor(255, 255, 255, 20)); + + QColor mdiButtonGradientStartColor; + QColor mdiButtonGradientStopColor; + + mdiButtonGradientStartColor = QColor(0, 0, 0, 40); + mdiButtonGradientStopColor = QColor(255, 255, 255, 60); + + if (sunken) + titleBarHighlight = highlight.darker(130); + + QLinearGradient gradient(tmp.center().x(), tmp.top(), tmp.center().x(), tmp.bottom()); + gradient.setColorAt(0, mdiButtonGradientStartColor); + gradient.setColorAt(1, mdiButtonGradientStopColor); + QColor mdiButtonBorderColor(active ? option->palette.highlight().color().darker(180): dark.darker(110)); + + painter->setPen(QPen(mdiButtonBorderColor, 1)); + const QLine lines[4] = { + QLine(tmp.left() + 2, tmp.top(), tmp.right() - 2, tmp.top()), + QLine(tmp.left() + 2, tmp.bottom(), tmp.right() - 2, tmp.bottom()), + QLine(tmp.left(), tmp.top() + 2, tmp.left(), tmp.bottom() - 2), + QLine(tmp.right(), tmp.top() + 2, tmp.right(), tmp.bottom() - 2) + }; + painter->drawLines(lines, 4); + const QPoint points[4] = { + QPoint(tmp.left() + 1, tmp.top() + 1), + QPoint(tmp.right() - 1, tmp.top() + 1), + QPoint(tmp.left() + 1, tmp.bottom() - 1), + QPoint(tmp.right() - 1, tmp.bottom() - 1) + }; + painter->drawPoints(points, 4); + + painter->setPen(titleBarHighlight); + painter->drawLine(tmp.left() + 2, tmp.top() + 1, tmp.right() - 2, tmp.top() + 1); + painter->drawLine(tmp.left() + 1, tmp.top() + 2, tmp.left() + 1, tmp.bottom() - 2); + + painter->setPen(QPen(gradient, 1)); + painter->drawLine(tmp.right() + 1, tmp.top() + 2, tmp.right() + 1, tmp.bottom() - 2); + painter->drawPoint(tmp.right() , tmp.top() + 1); + + painter->drawLine(tmp.left() + 2, tmp.bottom() + 1, tmp.right() - 2, tmp.bottom() + 1); + painter->drawPoint(tmp.left() + 1, tmp.bottom()); + painter->drawPoint(tmp.right() - 1, tmp.bottom()); + painter->drawPoint(tmp.right() , tmp.bottom() - 1); +} + +static const char * const dock_widget_close_xpm[] = + { + "11 13 5 1", + " c None", + ". c #D5CFCB", + "+ c #6C6A67", + "@ c #6C6A67", + "$ c #B5B0AC", + " ", + " @@@@@@@@@ ", + "@+ +@", + "@ +@ @+ @", + "@ @@@ @@@ @", + "@ @@@@@ @", + "@ @@@ @", + "@ @@@@@ @", + "@ @@@ @@@ @", + "@ +@ @+ @", + "@+ +@", + " @@@@@@@@@ ", + " " + }; + +static const char * const dock_widget_restore_xpm[] = + { + "11 13 5 1", + " c None", + ". c #D5CFCB", + "+ c #6C6A67", + "@ c #6C6A67", + "# c #6C6A67", + " ", + " @@@@@@@@@ ", + "@+ +@", + "@ #@@@# @", + "@ @ @ @", + "@ #@@@# @ @", + "@ @ @ @ @", + "@ @ @@@ @", + "@ @ @ @", + "@ #@@@@ @", + "@+ +@", + " @@@@@@@@@ ", + " " + }; + +static const char * const qt_titlebar_context_help[] = { + "10 10 3 1", + " c None", + "# c #000000", + "+ c #444444", + " +####+ ", + " ### ### ", + " ## ## ", + " +##+ ", + " +## ", + " ## ", + " ## ", + " ", + " ## ", + " ## "}; + +static const char * const qt_scrollbar_button_arrow_up[] = { + "7 4 2 1", + " c None", + "* c #BFBFBF", + " * ", + " *** ", + " ***** ", + "*******"}; + +static const char * const qt_scrollbar_button_arrow_down[] = { + "7 4 2 1", + " c None", + "* c #BFBFBF", + "*******", + " ***** ", + " *** ", + " * "}; + +static const int groupBoxBottomMargin = 2; // space below the groupbox +static const int groupBoxTitleMargin = 6; // space between contents and title +static const int groupBoxTopMargin = 2; + +static QColor mergedColors(const QColor &colorA, const QColor &colorB, int factor = 50) +{ + const int maxFactor = 100; + QColor tmp = colorA; + tmp.setRed((tmp.red() * factor) / maxFactor + (colorB.red() * (maxFactor - factor)) / maxFactor); + tmp.setGreen((tmp.green() * factor) / maxFactor + (colorB.green() * (maxFactor - factor)) / maxFactor); + tmp.setBlue((tmp.blue() * factor) / maxFactor + (colorB.blue() * (maxFactor - factor)) / maxFactor); + return tmp; +} + +static GdkColor fromQColor(const QColor &color) +{ + GdkColor retval; + retval.pixel = 0; + retval.red = color.red() * 255; + retval.green = color.green() * 255; + retval.blue = color.blue() * 255; + return retval; +} + +/*! + \class QGtkStyle + \brief The QGtkStyle class provides a widget style rendered by GTK+ + \since 4.5 + + \internal + \inmodule QtWidgets + + The QGtkStyle style provides a look and feel that integrates well + into GTK-based desktop environments such as the XFCe and GNOME. + + It does this by making use of the GTK+ theme engine, ensuring + that Qt applications look and feel native on these platforms. + + Note: The style requires GTK+ version 2.18 or later. + The Qt3-based "Qt" GTK+ theme engine will not work with QGtkStyle. + + \sa QWindowsXPStyle, QMacStyle, QWindowsStyle, QFusionStyle +*/ + +/*! + Constructs a QGtkStyle object. +*/ +QGtkStyle::QGtkStyle() + : QCommonStyle(*new QGtkStylePrivate) +{ + Q_D(QGtkStyle); + d->init(); +} + +/*! + \internal + + Constructs a QGtkStyle object. +*/ +QGtkStyle::QGtkStyle(QGtkStylePrivate &dd) + : QCommonStyle(dd) +{ + Q_D(QGtkStyle); + d->init(); +} + + +/*! + Destroys the QGtkStyle object. +*/ +QGtkStyle::~QGtkStyle() +{ +} + +/*! + \reimp +*/ +QPalette QGtkStyle::standardPalette() const +{ + Q_D(const QGtkStyle); + + QPalette palette = QCommonStyle::standardPalette(); + if (d->isThemeAvailable()) { + GtkStyle *style = d->gtkStyle(); + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + GtkWidget *gtkEntry = d->getTextColorWidget(); + GdkColor gdkBg, gdkBase, gdkText, gdkForeground, gdkSbg, gdkSfg, gdkaSbg, gdkaSfg; + QColor bg, base, text, fg, highlight, highlightText, inactiveHighlight, inactiveHighlightedTExt; + gdkBg = style->bg[GTK_STATE_NORMAL]; + gdkForeground = gtk_widget_get_style(gtkButton)->fg[GTK_STATE_NORMAL]; + + // Our base and selected color is primarily used for text + // so we assume a gtkEntry will have the most correct value + GtkStyle *gtkEntryStyle = gtk_widget_get_style(gtkEntry); + gdkBase = gtkEntryStyle->base[GTK_STATE_NORMAL]; + gdkText = gtkEntryStyle->text[GTK_STATE_NORMAL]; + gdkSbg = gtkEntryStyle->base[GTK_STATE_SELECTED]; + gdkSfg = gtkEntryStyle->text[GTK_STATE_SELECTED]; + + // The ACTIVE base color is really used for inactive windows + gdkaSbg = gtkEntryStyle->base[GTK_STATE_ACTIVE]; + gdkaSfg = gtkEntryStyle->text[GTK_STATE_ACTIVE]; + + bg = QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); + text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + fg = QColor(gdkForeground.red>>8, gdkForeground.green>>8, gdkForeground.blue>>8); + base = QColor(gdkBase.red>>8, gdkBase.green>>8, gdkBase.blue>>8); + highlight = QColor(gdkSbg.red>>8, gdkSbg.green>>8, gdkSbg.blue>>8); + highlightText = QColor(gdkSfg.red>>8, gdkSfg.green>>8, gdkSfg.blue>>8); + inactiveHighlight = QColor(gdkaSbg.red>>8, gdkaSbg.green>>8, gdkaSbg.blue>>8); + inactiveHighlightedTExt = QColor(gdkaSfg.red>>8, gdkaSfg.green>>8, gdkaSfg.blue>>8); + + palette.setColor(QPalette::HighlightedText, highlightText); + + + palette.setColor(QPalette::Light, bg.lighter(125)); + palette.setColor(QPalette::Shadow, bg.darker(130)); + palette.setColor(QPalette::Dark, bg.darker(120)); + palette.setColor(QPalette::Text, text); + palette.setColor(QPalette::WindowText, fg); + palette.setColor(QPalette::ButtonText, fg); + palette.setColor(QPalette::Base, base); + + QColor alternateRowColor = palette.base().color().lighter(93); // ref gtkstyle.c draw_flat_box + GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView"); + GdkColor *gtkAltBase = NULL; + gtk_widget_style_get(gtkTreeView, "odd-row-color", >kAltBase, NULL); + if (gtkAltBase) { + alternateRowColor = QColor(gtkAltBase->red>>8, gtkAltBase->green>>8, gtkAltBase->blue>>8); + gdk_color_free(gtkAltBase); + } + palette.setColor(QPalette::AlternateBase, alternateRowColor); + + palette.setColor(QPalette::Window, bg); + palette.setColor(QPalette::Button, bg); + palette.setColor(QPalette::Background, bg); + QColor disabled((fg.red() + bg.red()) / 2, + (fg.green() + bg.green())/ 2, + (fg.blue() + bg.blue()) / 2); + palette.setColor(QPalette::Disabled, QPalette::Text, disabled); + palette.setColor(QPalette::Disabled, QPalette::WindowText, disabled); + palette.setColor(QPalette::Disabled, QPalette::Foreground, disabled); + palette.setColor(QPalette::Disabled, QPalette::ButtonText, disabled); + palette.setColor(QPalette::Highlight, highlight); + // calculate disabled colors by removing saturation + highlight.setHsv(highlight.hue(), 0, highlight.value(), highlight.alpha()); + highlightText.setHsv(highlightText.hue(), 0, highlightText.value(), highlightText.alpha()); + palette.setColor(QPalette::Disabled, QPalette::Highlight, highlight); + palette.setColor(QPalette::Disabled, QPalette::HighlightedText, highlightText); + + palette.setColor(QPalette::Inactive, QPalette::HighlightedText, inactiveHighlightedTExt); + palette.setColor(QPalette::Inactive, QPalette::Highlight, inactiveHighlight); + + style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), "gtk-tooltips", "GtkWindow", + gtk_window_get_type()); + if (style) { + gdkText = style->fg[GTK_STATE_NORMAL]; + text = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + palette.setColor(QPalette::ToolTipText, text); + } + } + return palette; +} + +/*! + \reimp +*/ +void QGtkStyle::polish(QPalette &palette) +{ + Q_D(QGtkStyle); + + if (!d->isThemeAvailable()) + QCommonStyle::polish(palette); + else + palette = palette.resolve(standardPalette()); +} + +/*! + \reimp +*/ +void QGtkStyle::polish(QApplication *app) +{ + Q_D(QGtkStyle); + + QCommonStyle::polish(app); + // Custom fonts and palettes with QtConfig are intentionally + // not supported as these should be entirely determined by + // current Gtk settings + if (app->desktopSettingsAware() && d->isThemeAvailable()) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + QApplication::setPalette(standardPalette()); +#else + QApplicationPrivate::setSystemPalette(standardPalette()); +#endif + QApplicationPrivate::setSystemFont(d->getThemeFont()); + d->applyCustomPaletteHash(); + if (!d->isKDE4Session()) + qApp->installEventFilter(&d->filter); + } +} + +/*! + \reimp +*/ +void QGtkStyle::unpolish(QApplication *app) +{ + Q_D(QGtkStyle); + + QCommonStyle::unpolish(app); + QPixmapCache::clear(); + + if (app->desktopSettingsAware() && d->isThemeAvailable() && !d->isKDE4Session()) + qApp->removeEventFilter(&d->filter); +} + +/*! + \reimp +*/ + +void QGtkStyle::polish(QWidget *widget) +{ + Q_D(QGtkStyle); + + QCommonStyle::polish(widget); + if (!d->isThemeAvailable()) + return; + if (qobject_cast<QAbstractButton*>(widget) + || qobject_cast<QToolButton*>(widget) + || qobject_cast<QComboBox*>(widget) + || qobject_cast<QGroupBox*>(widget) + || qobject_cast<QScrollBar*>(widget) + || qobject_cast<QSlider*>(widget) + || qobject_cast<QAbstractSpinBox*>(widget) + || qobject_cast<QSpinBox*>(widget) + || qobject_cast<QHeaderView*>(widget)) + widget->setAttribute(Qt::WA_Hover); +#ifndef QT_NO_TREEVIEW + else if (QTreeView *tree = qobject_cast<QTreeView *> (widget)) + tree->viewport()->setAttribute(Qt::WA_Hover); +#endif +} + +/*! + \reimp +*/ +void QGtkStyle::unpolish(QWidget *widget) +{ + QCommonStyle::unpolish(widget); +} + +/*! + \reimp +*/ +int QGtkStyle::pixelMetric(PixelMetric metric, + const QStyleOption *option, + const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) + return QCommonStyle::pixelMetric(metric, option, widget); + + switch (metric) { + case PM_DefaultFrameWidth: + if (qobject_cast<const QFrame*>(widget)) { + if (GtkStyle *style = + gtk_rc_get_style_by_paths(gtk_settings_get_default(), + "*.GtkScrolledWindow", + "*.GtkScrolledWindow", + gtk_window_get_type())) + return qMax(style->xthickness, style->ythickness); + } + return 2; + + case PM_MenuButtonIndicator: + return 20; + + case PM_TabBarBaseOverlap: + return 1; + + case PM_ToolBarSeparatorExtent: + return 11; + + case PM_ToolBarFrameWidth: + return 1; + + case PM_ToolBarItemSpacing: + return 0; + + case PM_ButtonShiftHorizontal: { + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + guint horizontal_shift; + gtk_widget_style_get(gtkButton, "child-displacement-x", &horizontal_shift, NULL); + return horizontal_shift; + } + + case PM_ButtonShiftVertical: { + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + guint vertical_shift; + gtk_widget_style_get(gtkButton, "child-displacement-y", &vertical_shift, NULL); + return vertical_shift; + } + + case PM_MenuBarPanelWidth: + return 0; + + case PM_MenuPanelWidth: { + GtkWidget *gtkMenu = d->gtkWidget("GtkMenu"); + guint horizontal_padding = 0; + // horizontal-padding is used by Maemo to get thicker borders + if (!gtk_check_version(2, 10, 0)) + gtk_widget_style_get(gtkMenu, "horizontal-padding", &horizontal_padding, NULL); + int padding = qMax<int>(gtk_widget_get_style(gtkMenu)->xthickness, horizontal_padding); + return padding; + } + + case PM_ButtonIconSize: { + int retVal = 24; + GtkSettings *settings = gtk_settings_get_default(); + gchararray icon_sizes; + g_object_get(settings, "gtk-icon-sizes", &icon_sizes, NULL); + QStringList values = QString(QLS(icon_sizes)).split(QLatin1Char(':')); + g_free(icon_sizes); + QChar splitChar(QLatin1Char(',')); + foreach (const QString &value, values) { + if (value.startsWith(QLS("gtk-button="))) { + QString iconSize = value.right(value.size() - 11); + + if (iconSize.contains(splitChar)) + retVal = iconSize.split(splitChar)[0].toInt(); + break; + } + } + return retVal; + } + + case PM_MenuVMargin: + + case PM_MenuHMargin: + return 0; + + case PM_DockWidgetTitleMargin: + return 0; + + case PM_DockWidgetTitleBarButtonMargin: + return 5; + + case PM_TabBarTabVSpace: + return 12; + + case PM_TabBarTabHSpace: + return 14; + + case PM_TabBarTabShiftVertical: + return 2; + + case PM_ToolBarHandleExtent: + return 9; + + case PM_SplitterWidth: + return 6; + + case PM_SliderThickness: + case PM_SliderControlThickness: { + GtkWidget *gtkScale = d->gtkWidget("GtkHScale"); + gint val; + gtk_widget_style_get(gtkScale, "slider-width", &val, NULL); + if (metric == PM_SliderControlThickness) + return val + 2*gtk_widget_get_style(gtkScale)->ythickness; + return val; + } + + case PM_ScrollBarExtent: { + gint sliderLength; + gint trough_border; + GtkWidget *hScrollbar = d->gtkWidget("GtkHScrollbar"); + gtk_widget_style_get(hScrollbar, + "trough-border", &trough_border, + "slider-width", &sliderLength, + NULL); + return sliderLength + trough_border*2; + } + + case PM_ScrollBarSliderMin: + return 34; + + case PM_SliderLength: + gint val; + gtk_widget_style_get(d->gtkWidget("GtkHScale"), "slider-length", &val, NULL); + return val; + + case PM_ExclusiveIndicatorWidth: + case PM_ExclusiveIndicatorHeight: + case PM_IndicatorWidth: + case PM_IndicatorHeight: { + GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); + gint size, spacing; + gtk_widget_style_get(gtkCheckButton, "indicator-spacing", &spacing, "indicator-size", &size, NULL); + return size + 2 * spacing; + } + + case PM_MenuBarVMargin: { + GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); + return qMax(0, gtk_widget_get_style(gtkMenubar)->ythickness); + } + case PM_ScrollView_ScrollBarSpacing: + { + gint spacing = 3; + GtkWidget *gtkScrollWindow = d->gtkWidget("GtkScrolledWindow"); + Q_ASSERT(gtkScrollWindow); + gtk_widget_style_get(gtkScrollWindow, "scrollbar-spacing", &spacing, NULL); + return spacing; + } + case PM_SubMenuOverlap: { + gint offset = 0; + GtkWidget *gtkMenu = d->gtkWidget("GtkMenu"); + gtk_widget_style_get(gtkMenu, "horizontal-offset", &offset, NULL); + return offset; + } + case PM_ToolTipLabelFrameWidth: + return 2; + case PM_ButtonDefaultIndicator: + return 0; + case PM_ListViewIconSize: + return 24; + case PM_DialogButtonsSeparator: + return 6; + case PM_TitleBarHeight: + return 24; + case PM_SpinBoxFrameWidth: + return 3; + case PM_MenuBarItemSpacing: + return 6; + case PM_MenuBarHMargin: + return 0; + case PM_ToolBarItemMargin: + return 1; + case PM_SmallIconSize: + return 16; + case PM_MaximumDragDistance: + return -1; + case PM_TabCloseIndicatorWidth: + case PM_TabCloseIndicatorHeight: + return 20; + default: + return QCommonStyle::pixelMetric(metric, option, widget); + } +} + +/*! + \reimp +*/ +int QGtkStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, + + QStyleHintReturn *returnData = 0) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) + return QCommonStyle::styleHint(hint, option, widget, returnData); + + switch (hint) { + case SH_ItemView_ChangeHighlightOnFocus: + return true; + case SH_ScrollBar_MiddleClickAbsolutePosition: + return true; + case SH_Menu_AllowActiveAndDisabled: + return false; + case SH_MainWindow_SpaceBelowMenuBar: + return false; + case SH_MenuBar_MouseTracking: + return true; + case SH_Menu_MouseTracking: + return true; + case SH_TitleBar_AutoRaise: + return true; + case SH_TitleBar_NoBorder: + return true; + case SH_ItemView_ShowDecorationSelected: + return true; + case SH_Table_GridLineColor: + if (option) + return option->palette.window().color().darker(120).rgb(); + break; + case SH_WindowFrame_Mask: + if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(returnData)) { + //left rounded corner + mask->region = option->rect; + mask->region -= QRect(option->rect.left(), option->rect.top(), 5, 1); + mask->region -= QRect(option->rect.left(), option->rect.top() + 1, 3, 1); + mask->region -= QRect(option->rect.left(), option->rect.top() + 2, 2, 1); + mask->region -= QRect(option->rect.left(), option->rect.top() + 3, 1, 2); + + //right rounded corner + mask->region -= QRect(option->rect.right() - 4, option->rect.top(), 5, 1); + mask->region -= QRect(option->rect.right() - 2, option->rect.top() + 1, 3, 1); + mask->region -= QRect(option->rect.right() - 1, option->rect.top() + 2, 2, 1); + mask->region -= QRect(option->rect.right() , option->rect.top() + 3, 1, 2); + } + return QCommonStyle::styleHint(hint, option, widget, returnData); + case SH_MessageBox_TextInteractionFlags: + return Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse; + case SH_MessageBox_CenterButtons: + return false; +#ifndef QT_NO_WIZARD + case SH_WizardStyle: + return QWizard::ClassicStyle; +#endif + case SH_ItemView_ArrowKeysNavigateIntoChildren: + return false; + case SH_DialogButtonLayout: { + int ret = QDialogButtonBox::GnomeLayout; + gboolean alternateOrder = 0; + GtkSettings *settings = gtk_settings_get_default(); + g_object_get(settings, "gtk-alternative-button-order", &alternateOrder, NULL); + + if (alternateOrder) + ret = QDialogButtonBox::WinLayout; + + return ret; + } + break; + + case SH_ToolButtonStyle: + { + if (d->isKDE4Session()) + return QCommonStyle::styleHint(hint, option, widget, returnData); + GtkWidget *gtkToolbar = d->gtkWidget("GtkToolbar"); + GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; + g_object_get(gtkToolbar, "toolbar-style", &toolbar_style, NULL); + switch (toolbar_style) { + case GTK_TOOLBAR_TEXT: + return Qt::ToolButtonTextOnly; + case GTK_TOOLBAR_BOTH: + return Qt::ToolButtonTextUnderIcon; + case GTK_TOOLBAR_BOTH_HORIZ: + return Qt::ToolButtonTextBesideIcon; + case GTK_TOOLBAR_ICONS: + default: + return Qt::ToolButtonIconOnly; + } + } + break; + case SH_SpinControls_DisableOnBounds: + return int(true); + + case SH_DitherDisabledText: + return int(false); + + case SH_ComboBox_Popup: { + GtkWidget *gtkComboBox = d->gtkWidget("GtkComboBox"); + gboolean appears_as_list; + gtk_widget_style_get((GtkWidget*)gtkComboBox, "appears-as-list", &appears_as_list, NULL); + return appears_as_list ? 0 : 1; + } + + case SH_MenuBar_AltKeyNavigation: + return int(false); + + case SH_EtchDisabledText: + return int(false); + + case SH_Menu_SubMenuPopupDelay: { + gint delay = 225; + GtkSettings *settings = gtk_settings_get_default(); + g_object_get(settings, "gtk-menu-popup-delay", &delay, NULL); + return delay; + } + + case SH_ScrollView_FrameOnlyAroundContents: { + gboolean scrollbars_within_bevel = false; + if (widget && widget->isWindow()) + scrollbars_within_bevel = true; + else if (!gtk_check_version(2, 12, 0)) { + GtkWidget *gtkScrollWindow = d->gtkWidget("GtkScrolledWindow"); + gtk_widget_style_get(gtkScrollWindow, "scrollbars-within-bevel", &scrollbars_within_bevel, NULL); + } + return !scrollbars_within_bevel; + } + + case SH_DialogButtonBox_ButtonsHaveIcons: { + gboolean buttonsHaveIcons = true; + GtkSettings *settings = gtk_settings_get_default(); + g_object_get(settings, "gtk-button-images", &buttonsHaveIcons, NULL); + return buttonsHaveIcons; + } + + case SH_UnderlineShortcut: { + gboolean underlineShortcut = true; + if (!gtk_check_version(2, 12, 0)) { + GtkSettings *settings = gtk_settings_get_default(); + g_object_get(settings, "gtk-enable-mnemonics", &underlineShortcut, NULL); + } + return underlineShortcut; + } + + default: + break; + } + return QCommonStyle::styleHint(hint, option, widget, returnData); +} + +/*! + \reimp +*/ +void QGtkStyle::drawPrimitive(PrimitiveElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) { + QCommonStyle::drawPrimitive(element, option, painter, widget); + return; + } + + GtkStyle* style = d->gtkStyle(); + QGtkPainter* gtkPainter = d->gtkPainter(painter); + + switch (element) { + case PE_Frame: { + if (widget && widget->inherits("QComboBoxPrivateContainer")){ + QStyleOption copy = *option; + copy.state |= State_Raised; + proxy()->drawPrimitive(PE_PanelMenu, ©, painter, widget); + break; + } + // Drawing the entire itemview frame is very expensive, especially on the native X11 engine + // Instead we cheat a bit and draw a border image without the center part, hence only scaling + // thin rectangular images + const int pmSize = 64; + const int border = proxy()->pixelMetric(PM_DefaultFrameWidth, option, widget); + const QString pmKey = QLatin1String("windowframe") % HexString<uint>(option->state); + + QPixmap pixmap; + QRect pmRect(QPoint(0,0), QSize(pmSize, pmSize)); + + // Only draw through style once + if (!QPixmapCache::find(pmKey, &pixmap)) { + pixmap = QPixmap(pmSize, pmSize); + pixmap.fill(Qt::transparent); + QPainter pmPainter(&pixmap); + gtkPainter->reset(&pmPainter); + gtkPainter->setUsePixmapCache(false); // Don't cache twice + + GtkShadowType shadow_type = GTK_SHADOW_NONE; + if (option->state & State_Sunken) + shadow_type = GTK_SHADOW_IN; + else if (option->state & State_Raised) + shadow_type = GTK_SHADOW_OUT; + + GtkStyle *style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), + "*.GtkScrolledWindow", "*.GtkScrolledWindow", gtk_window_get_type()); + if (style) + gtkPainter->paintShadow(d->gtkWidget("GtkFrame"), "viewport", pmRect, + option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, + shadow_type, style); + QPixmapCache::insert(pmKey, pixmap); + gtkPainter->reset(painter); + } + + QRect rect = option->rect; + const int rw = rect.width() - border; + const int rh = rect.height() - border; + const int pw = pmRect.width() - border; + const int ph = pmRect.height() - border; + + // Sidelines + painter->drawPixmap(rect.adjusted(border, 0, -border, -rh), pixmap, pmRect.adjusted(border, 0, -border,-ph)); + painter->drawPixmap(rect.adjusted(border, rh, -border, 0), pixmap, pmRect.adjusted(border, ph,-border,0)); + painter->drawPixmap(rect.adjusted(0, border, -rw, -border), pixmap, pmRect.adjusted(0, border, -pw, -border)); + painter->drawPixmap(rect.adjusted(rw, border, 0, -border), pixmap, pmRect.adjusted(pw, border, 0, -border)); + + // Corners + painter->drawPixmap(rect.adjusted(0, 0, -rw, -rh), pixmap, pmRect.adjusted(0, 0, -pw,-ph)); + painter->drawPixmap(rect.adjusted(rw, 0, 0, -rh), pixmap, pmRect.adjusted(pw, 0, 0,-ph)); + painter->drawPixmap(rect.adjusted(0, rh, -rw, 0), pixmap, pmRect.adjusted(0, ph, -pw,0)); + painter->drawPixmap(rect.adjusted(rw, rh, 0, 0), pixmap, pmRect.adjusted(pw, ph, 0,0)); + } + break; + case PE_FrameWindow: + painter->save(); + { + QRect rect= option->rect; + painter->setPen(QPen(option->palette.dark().color().darker(150), 0)); + painter->drawRect(option->rect.adjusted(0, 0, -1, -1)); + painter->setPen(QPen(option->palette.light(), 0)); + painter->drawLine(QPoint(rect.left() + 1, rect.top() + 1), + QPoint(rect.left() + 1, rect.bottom() - 1)); + painter->setPen(QPen(option->palette.window().color().darker(120), 0)); + painter->drawLine(QPoint(rect.left() + 1, rect.bottom() - 1), + QPoint(rect.right() - 2, rect.bottom() - 1)); + painter->drawLine(QPoint(rect.right() - 1, rect.top() + 1), + QPoint(rect.right() - 1, rect.bottom() - 1)); + } + painter->restore(); + break; + + case PE_PanelTipLabel: { + GtkWidget *gtkWindow = d->gtkWidget("GtkWindow"); // The Murrine Engine currently assumes a widget is passed + style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), "gtk-tooltips", "GtkWindow", + gtk_window_get_type()); + gtkPainter->paintFlatBox(gtkWindow, "tooltip", option->rect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, style); + } + break; + + case PE_PanelStatusBar: { + if (widget && widget->testAttribute(Qt::WA_SetPalette) && + option->palette.resolve() & (1 << QPalette::Window)) { + // Respect custom palette + painter->fillRect(option->rect, option->palette.window()); + break; + } + GtkShadowType shadow_type; + GtkWidget *gtkStatusbarFrame = d->gtkWidget("GtkStatusbar.GtkFrame"); + gtk_widget_style_get(gtk_widget_get_parent(gtkStatusbarFrame), "shadow-type", &shadow_type, NULL); + gtkPainter->paintShadow(gtkStatusbarFrame, "frame", option->rect, GTK_STATE_NORMAL, + shadow_type, gtk_widget_get_style(gtkStatusbarFrame)); + } + break; + + case PE_IndicatorHeaderArrow: + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + GtkWidget *gtkTreeHeader = d->gtkWidget("GtkTreeView.GtkButton"); + GtkStateType state = qt_gtk_state(option); + style = gtk_widget_get_style(gtkTreeHeader); + GtkArrowType type = GTK_ARROW_UP; + // This sorting indicator inversion is intentional, and follows the GNOME HIG. + // See http://library.gnome.org/devel/hig-book/stable/controls-lists.html.en#controls-lists-sortable + if (header->sortIndicator & QStyleOptionHeader::SortUp) + type = GTK_ARROW_UP; + else if (header->sortIndicator & QStyleOptionHeader::SortDown) + type = GTK_ARROW_DOWN; + + gtkPainter->paintArrow(gtkTreeHeader, "button", option->rect.adjusted(1, 1, -1, -1), type, state, + GTK_SHADOW_NONE, false, style); + } + break; + + case PE_FrameDefaultButton: // fall through + case PE_FrameFocusRect: { + QRect frameRect = option->rect.adjusted(1, 1, -2, -2); // ### this mess should move to subcontrolrect + if (qobject_cast<const QAbstractItemView*>(widget)) { + // Don't draw anything + } else if (qobject_cast<const QTabBar*>(widget)) { + GtkWidget *gtkNotebook = d->gtkWidget("GtkNotebook"); + style = gtk_widget_get_style(gtkNotebook); + gtkPainter->paintFocus(gtkNotebook, "tab", frameRect.adjusted(-1, 1, 1, 1), GTK_STATE_ACTIVE, style); + } else { + GtkWidget *gtkRadioButton = d->gtkWidget("GtkRadioButton"); + gtkPainter->paintFocus(gtkRadioButton, "radiobutton", frameRect, GTK_STATE_ACTIVE, style); + } + } + break; + + case PE_IndicatorBranch: + if (option->state & State_Children) { + QRect rect = option->rect; + rect = QRect(0, 0, 12, 12); + rect.moveCenter(option->rect.center()); + rect.translate(2, 0); + GtkExpanderStyle openState = GTK_EXPANDER_EXPANDED; + GtkExpanderStyle closedState = GTK_EXPANDER_COLLAPSED; + GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView"); + + GtkStateType state = GTK_STATE_NORMAL; + if (!(option->state & State_Enabled)) + state = GTK_STATE_INSENSITIVE; + else if (option->state & State_MouseOver) + state = GTK_STATE_PRELIGHT; + + gtkPainter->paintExpander(gtkTreeView, "treeview", rect, state, + option->state & State_Open ? openState : closedState , gtk_widget_get_style(gtkTreeView)); + } + break; + + case PE_PanelItemViewRow: + // This primitive is only used to draw selection behind selected expander arrows. + // We try not to decorate the tree branch background unless you inherit from StyledItemDelegate + // The reason for this is that a lot of code that relies on custom item delegates will look odd having + // a gradient on the branch but a flat shaded color on the item itself. + QCommonStyle::drawPrimitive(element, option, painter, widget); + if (!(option->state & State_Selected)) { + break; + } else { + if (const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(widget)) { + if (!qobject_cast<QStyledItemDelegate*>(view->itemDelegate())) + break; + } + } // fall through + + case PE_PanelItemViewItem: + if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option)) { + uint resolve_mask = vopt->palette.resolve(); + if (vopt->backgroundBrush.style() != Qt::NoBrush + || (resolve_mask & (1 << QPalette::Base))) + { + QPointF oldBO = painter->brushOrigin(); + painter->setBrushOrigin(vopt->rect.topLeft()); + painter->fillRect(vopt->rect, vopt->backgroundBrush); + painter->setBrushOrigin(oldBO); + if (!(option->state & State_Selected)) + break; + } + if (GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView")) { + const char *detail = "cell_even_ruled"; + if (vopt && vopt->features & QStyleOptionViewItem::Alternate) + detail = "cell_odd_ruled"; + bool isActive = option->state & State_Active; + QString key; + if (isActive ) { + // Required for active/non-active window appearance + key = QLS("a"); + QGtkStylePrivate::QGtkStylePrivate::gtkWidgetSetFocus(gtkTreeView, true); + } + bool isEnabled = (widget ? widget->isEnabled() : (vopt->state & QStyle::State_Enabled)); + gtkPainter->paintFlatBox(gtkTreeView, detail, option->rect, + option->state & State_Selected ? GTK_STATE_SELECTED : + isEnabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, + GTK_SHADOW_OUT, gtk_widget_get_style(gtkTreeView), key); + if (isActive ) + QGtkStylePrivate::QGtkStylePrivate::gtkWidgetSetFocus(gtkTreeView, false); + } + } + break; + case PE_IndicatorToolBarSeparator: + { + const int margin = 6; + GtkWidget *gtkSeparator = d->gtkWidget("GtkToolbar.GtkSeparatorToolItem"); + if (option->state & State_Horizontal) { + const int offset = option->rect.width()/2; + QRect rect = option->rect.adjusted(offset, margin, 0, -margin); + painter->setPen(QPen(option->palette.window().color().darker(110))); + gtkPainter->paintVline(gtkSeparator, "vseparator", + rect, GTK_STATE_NORMAL, gtk_widget_get_style(gtkSeparator), + 0, rect.height(), 0); + } else { //Draw vertical separator + const int offset = option->rect.height()/2; + QRect rect = option->rect.adjusted(margin, offset, -margin, 0); + painter->setPen(QPen(option->palette.window().color().darker(110))); + gtkPainter->paintHline(gtkSeparator, "hseparator", + rect, GTK_STATE_NORMAL, gtk_widget_get_style(gtkSeparator), + 0, rect.width(), 0); + } + } + break; + + case PE_IndicatorToolBarHandle: { + GtkWidget *gtkToolbar = d->gtkWidget("GtkToolbar"); + GtkShadowType shadow_type; + gtk_widget_style_get(gtkToolbar, "shadow-type", &shadow_type, NULL); + //Note when the toolbar is horizontal, the handle is vertical + painter->setClipRect(option->rect); + gtkPainter->paintHandle(gtkToolbar, "toolbar", option->rect.adjusted(-1, -1 ,0 ,1), + GTK_STATE_NORMAL, shadow_type, !(option->state & State_Horizontal) ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, gtk_widget_get_style(gtkToolbar)); + } + break; + + case PE_IndicatorArrowUp: + case PE_IndicatorArrowDown: + case PE_IndicatorArrowLeft: + case PE_IndicatorArrowRight: { + + + GtkArrowType type = GTK_ARROW_UP; + + switch (element) { + + case PE_IndicatorArrowDown: + type = GTK_ARROW_DOWN; + break; + + case PE_IndicatorArrowLeft: + type = GTK_ARROW_LEFT; + break; + + case PE_IndicatorArrowRight: + type = GTK_ARROW_RIGHT; + break; + + default: + break; + } + int size = qMin(option->rect.height(), option->rect.width()); + int border = (size > 9) ? (size/4) : 0; //Allow small arrows to have exact dimensions + int bsx = 0, bsy = 0; + if (option->state & State_Sunken) { + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + } + QRect arrowRect = option->rect.adjusted(border + bsx, border + bsy, -border + bsx, -border + bsy); + GtkShadowType shadow = option->state & State_Sunken ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + GtkStateType state = qt_gtk_state(option); + + QColor arrowColor = option->palette.buttonText().color(); + GtkWidget *gtkArrow = d->gtkWidget("GtkArrow"); + GdkColor color = fromQColor(arrowColor); + gtk_widget_modify_fg (gtkArrow, state, &color); + gtkPainter->paintArrow(gtkArrow, "button", arrowRect, + type, state, shadow, false, gtk_widget_get_style(gtkArrow), + QString::number(arrowColor.rgba(), 16)); + // Passing NULL will revert the color change + gtk_widget_modify_fg (gtkArrow, state, NULL); + } + break; + + case PE_FrameGroupBox: + // Do nothing here, the GNOME groupboxes are flat + break; + + case PE_PanelMenu: { + GtkWidget *gtkMenu = d->gtkWidget("GtkMenu"); + gtkPainter->setAlphaSupport(false); // Note, alpha disabled for performance reasons + gtkPainter->paintBox(gtkMenu, "menu", option->rect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, gtk_widget_get_style(gtkMenu), QString()); + } + break; + + case PE_FrameMenu: + //This is actually done by PE_Widget due to a clipping issue + //Otherwise Menu items will not be able to span the entire menu width + + // This is only used by floating tool bars + if (qobject_cast<const QToolBar *>(widget)) { + GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); + gtkPainter->paintBox(gtkMenubar, "toolbar", option->rect, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, style); + gtkPainter->paintBox(gtkMenubar, "menu", option->rect, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, style); + } + break; + + case PE_FrameLineEdit: { + GtkWidget *gtkEntry = d->gtkWidget("GtkEntry"); + + + gboolean interior_focus; + gint focus_line_width; + QRect rect = option->rect; + gtk_widget_style_get(gtkEntry, + "interior-focus", &interior_focus, + "focus-line-width", &focus_line_width, NULL); + + // See https://bugzilla.mozilla.org/show_bug.cgi?id=405421 for info about this hack + g_object_set_data(G_OBJECT(gtkEntry), "transparent-bg-hint", GINT_TO_POINTER(true)); + + if (!interior_focus && option->state & State_HasFocus) + rect.adjust(focus_line_width, focus_line_width, -focus_line_width, -focus_line_width); + + if (option->state & State_HasFocus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, true); + gtkPainter->paintShadow(gtkEntry, "entry", rect, option->state & State_Enabled ? + GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, + GTK_SHADOW_IN, gtk_widget_get_style(gtkEntry), + option->state & State_HasFocus ? QLS("focus") : QString()); + if (!interior_focus && option->state & State_HasFocus) + gtkPainter->paintShadow(gtkEntry, "entry", option->rect, option->state & State_Enabled ? + GTK_STATE_ACTIVE : GTK_STATE_INSENSITIVE, + GTK_SHADOW_IN, gtk_widget_get_style(gtkEntry), QLS("GtkEntryShadowIn")); + + if (option->state & State_HasFocus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, false); + } + break; + + case PE_PanelLineEdit: + if (const QStyleOptionFrame *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) { + GtkWidget *gtkEntry = d->gtkWidget("GtkEntry"); + if (panel->lineWidth > 0) + proxy()->drawPrimitive(PE_FrameLineEdit, option, painter, widget); + uint resolve_mask = option->palette.resolve(); + GtkStyle *gtkEntryStyle = gtk_widget_get_style(gtkEntry); + QRect textRect = option->rect.adjusted(gtkEntryStyle->xthickness, gtkEntryStyle->ythickness, + -gtkEntryStyle->xthickness, -gtkEntryStyle->ythickness); + + if (widget && widget->testAttribute(Qt::WA_SetPalette) && + resolve_mask & (1 << QPalette::Base)) // Palette overridden by user + painter->fillRect(textRect, option->palette.base()); + else + gtkPainter->paintFlatBox(gtkEntry, "entry_bg", textRect, + option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, gtkEntryStyle); + } + break; + + case PE_FrameTabWidget: + if (const QStyleOptionTabWidgetFrame *frame = qstyleoption_cast<const QStyleOptionTabWidgetFrame*>(option)) { + GtkWidget *gtkNotebook = d->gtkWidget("GtkNotebook"); + style = gtk_widget_get_style(gtkNotebook); + gtkPainter->setAlphaSupport(false); + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = GTK_STATE_NORMAL; // Only state supported by gtknotebook + bool reverse = (option->direction == Qt::RightToLeft); + gtk_widget_set_direction(gtkNotebook, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); + if (const QStyleOptionTabWidgetFrame *tabframe = qstyleoption_cast<const QStyleOptionTabWidgetFrame*>(option)) { + GtkPositionType frameType = GTK_POS_TOP; + QTabBar::Shape shape = frame->shape; + int gapStart = 0; + int gapSize = 0; + if (shape == QTabBar::RoundedNorth || shape == QTabBar::RoundedSouth) { + frameType = (shape == QTabBar::RoundedNorth) ? GTK_POS_TOP : GTK_POS_BOTTOM; + gapStart = tabframe->selectedTabRect.left(); + gapSize = tabframe->selectedTabRect.width(); + } else { + frameType = (shape == QTabBar::RoundedWest) ? GTK_POS_LEFT : GTK_POS_RIGHT; + gapStart = tabframe->selectedTabRect.y(); + gapSize = tabframe->selectedTabRect.height(); + } + gtkPainter->paintBoxGap(gtkNotebook, "notebook", option->rect, state, shadow, frameType, + gapStart, gapSize, style); + break; // done + } + + // Note this is only the fallback option + gtkPainter->paintBox(gtkNotebook, "notebook", option->rect, state, shadow, style); + } + break; + + case PE_PanelButtonCommand: + case PE_PanelButtonTool: { + bool isDefault = false; + bool isTool = (element == PE_PanelButtonTool); + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton*>(option)) + isDefault = btn->features & QStyleOptionButton::DefaultButton; + + // don't draw a frame for tool buttons that have the autoRaise flag and are not enabled or on + if (isTool && !(option->state & State_Enabled || option->state & State_On) && (option->state & State_AutoRaise)) + break; + // don't draw a frame for dock widget buttons, unless we are hovering + if (widget && widget->inherits("QDockWidgetTitleButton") && !(option->state & State_MouseOver)) + break; + + GtkStateType state = qt_gtk_state(option); + if (option->state & State_On || option->state & State_Sunken) + state = GTK_STATE_ACTIVE; + GtkWidget *gtkButton = isTool ? d->gtkWidget("GtkToolButton.GtkButton") : d->gtkWidget("GtkButton"); + gint focusWidth, focusPad; + gboolean interiorFocus = false; + gtk_widget_style_get (gtkButton, + "focus-line-width", &focusWidth, + "focus-padding", &focusPad, + "interior-focus", &interiorFocus, NULL); + + style = gtk_widget_get_style(gtkButton); + + QRect buttonRect = option->rect; + + QString key; + if (isDefault) { + key += QLS("def"); + gtk_widget_set_can_default(gtkButton, true); + gtk_window_set_default((GtkWindow*)gtk_widget_get_toplevel(gtkButton), gtkButton); + gtkPainter->paintBox(gtkButton, "buttondefault", buttonRect, state, GTK_SHADOW_IN, + style, isDefault ? QLS("d") : QString()); + } + + bool hasFocus = option->state & State_HasFocus; + + if (hasFocus) { + key += QLS("def"); + QGtkStylePrivate::gtkWidgetSetFocus(gtkButton, true); + } + + if (!interiorFocus) + buttonRect = buttonRect.adjusted(focusWidth, focusWidth, -focusWidth, -focusWidth); + + GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ? + GTK_SHADOW_IN : GTK_SHADOW_OUT; + + gtkPainter->paintBox(gtkButton, "button", buttonRect, state, shadow, + style, key); + if (isDefault) + gtk_window_set_default((GtkWindow*)gtk_widget_get_toplevel(gtkButton), 0); + if (hasFocus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkButton, false); + } + break; + + case PE_IndicatorRadioButton: { + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = qt_gtk_state(option); + + if (option->state & State_Sunken) + state = GTK_STATE_ACTIVE; + + if (option->state & State_NoChange) + shadow = GTK_SHADOW_ETCHED_IN; + else if (option->state & State_On) + shadow = GTK_SHADOW_IN; + else + shadow = GTK_SHADOW_OUT; + + GtkWidget *gtkRadioButton = d->gtkWidget("GtkRadioButton"); + gint spacing; + gtk_widget_style_get(gtkRadioButton, "indicator-spacing", &spacing, NULL); + QRect buttonRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing); + gtkPainter->setClipRect(option->rect); + // ### Note: Ubuntulooks breaks when the proper widget is passed + // Murrine engine requires a widget not to get RGBA check - warnings + GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); + QString key(QLS("radiobutton")); + if (option->state & State_HasFocus) { // Themes such as Nodoka check this flag + key += QLatin1Char('f'); + QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, true); + } + gtkPainter->paintOption(gtkCheckButton , buttonRect, state, shadow, gtk_widget_get_style(gtkRadioButton), key); + if (option->state & State_HasFocus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, false); + } + break; + + case PE_IndicatorCheckBox: { + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = qt_gtk_state(option); + + if (option->state & State_Sunken) + state = GTK_STATE_ACTIVE; + + if (option->state & State_NoChange) + shadow = GTK_SHADOW_ETCHED_IN; + else if (option->state & State_On) + shadow = GTK_SHADOW_IN; + else + shadow = GTK_SHADOW_OUT; + + int spacing; + + GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); + QString key(QLS("checkbutton")); + if (option->state & State_HasFocus) { // Themes such as Nodoka checks this flag + key += QLatin1Char('f'); + QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, true); + } + + // Some styles such as aero-clone assume they can paint in the spacing area + gtkPainter->setClipRect(option->rect); + + gtk_widget_style_get(gtkCheckButton, "indicator-spacing", &spacing, NULL); + + QRect checkRect = option->rect.adjusted(spacing, spacing, -spacing, -spacing); + + gtkPainter->paintCheckbox(gtkCheckButton, checkRect, state, shadow, gtk_widget_get_style(gtkCheckButton), + key); + if (option->state & State_HasFocus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkCheckButton, false); + + } + break; + +#ifndef QT_NO_TABBAR + + case PE_FrameTabBarBase: + if (const QStyleOptionTabBarBase *tbb + = qstyleoption_cast<const QStyleOptionTabBarBase *>(option)) { + QRect tabRect = tbb->rect; + painter->save(); + painter->setPen(QPen(option->palette.dark().color().darker(110), 0)); + switch (tbb->shape) { + + case QTabBar::RoundedNorth: + painter->drawLine(tabRect.topLeft(), tabRect.topRight()); + break; + + case QTabBar::RoundedWest: + painter->drawLine(tabRect.left(), tabRect.top(), tabRect.left(), tabRect.bottom()); + break; + + case QTabBar::RoundedSouth: + painter->drawLine(tbb->rect.left(), tbb->rect.bottom(), + tabRect.right(), tabRect.bottom()); + break; + + case QTabBar::RoundedEast: + painter->drawLine(tabRect.topRight(), tabRect.bottomRight()); + break; + + case QTabBar::TriangularNorth: + case QTabBar::TriangularEast: + case QTabBar::TriangularWest: + case QTabBar::TriangularSouth: + painter->restore(); + QCommonStyle::drawPrimitive(element, option, painter, widget); + return; + } + + painter->restore(); + } + return; + +#endif // QT_NO_TABBAR + + case PE_Widget: + break; + + default: + QCommonStyle::drawPrimitive(element, option, painter, widget); + } +} + +/*! + \reimp +*/ +void QGtkStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + + QPainter *painter, const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) { + QCommonStyle::drawComplexControl(control, option, painter, widget); + return; + } + + GtkStyle* style = d->gtkStyle(); + QGtkPainter* gtkPainter = d->gtkPainter(painter); + QColor button = option->palette.button().color(); + QColor dark; + QColor grooveColor; + QColor darkOutline; + dark.setHsv(button.hue(), + qMin(255, (int)(button.saturation()*1.9)), + qMin(255, (int)(button.value()*0.7))); + grooveColor.setHsv(button.hue(), + qMin(255, (int)(button.saturation()*2.6)), + qMin(255, (int)(button.value()*0.9))); + darkOutline.setHsv(button.hue(), + qMin(255, (int)(button.saturation()*3.0)), + qMin(255, (int)(button.value()*0.6))); + + QColor alphaCornerColor; + + if (widget) + alphaCornerColor = mergedColors(option->palette.color(widget->backgroundRole()), darkOutline); + else + alphaCornerColor = mergedColors(option->palette.window().color(), darkOutline); + + switch (control) { + + case CC_TitleBar: + painter->save(); + if (const QStyleOptionTitleBar *titleBar = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { + // Since this is drawn by metacity and not Gtk we + // have to do custom drawing + + GdkColor gdkBg = style->bg[GTK_STATE_SELECTED]; + QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); + + const int buttonMargin = 5; + bool active = (titleBar->titleBarState & State_Active); + QRect fullRect = titleBar->rect; + QPalette palette = option->palette; + QColor highlight = bgColor; + + QColor titleBarFrameBorder(active ? highlight.darker(180): dark.darker(110)); + QColor titleBarHighlight(active ? highlight.lighter(120): palette.window().color().lighter(120)); + QColor textColor(active ? 0xffffff : 0xff000000); + QColor textAlphaColor(active ? 0xffffff : 0xff000000 ); + + { + // Fill title bar gradient + QColor titlebarColor = QColor(active ? highlight: palette.window().color()); + QLinearGradient gradient(option->rect.center().x(), option->rect.top(), + option->rect.center().x(), option->rect.bottom()); + + gradient.setColorAt(0, titlebarColor.lighter(114)); + gradient.setColorAt(0.5, titlebarColor.lighter(102)); + gradient.setColorAt(0.51, titlebarColor.darker(104)); + gradient.setColorAt(1, titlebarColor); + painter->fillRect(option->rect.adjusted(1, 1, -1, 0), gradient); + + // Frame and rounded corners + painter->setPen(titleBarFrameBorder); + + // top outline + painter->drawLine(fullRect.left() + 5, fullRect.top(), fullRect.right() - 5, fullRect.top()); + painter->drawLine(fullRect.left(), fullRect.top() + 4, fullRect.left(), fullRect.bottom()); + const QPoint points[5] = { + QPoint(fullRect.left() + 4, fullRect.top() + 1), + QPoint(fullRect.left() + 3, fullRect.top() + 1), + QPoint(fullRect.left() + 2, fullRect.top() + 2), + QPoint(fullRect.left() + 1, fullRect.top() + 3), + QPoint(fullRect.left() + 1, fullRect.top() + 4) + }; + painter->drawPoints(points, 5); + + painter->drawLine(fullRect.right(), fullRect.top() + 4, fullRect.right(), fullRect.bottom()); + const QPoint points2[5] = { + QPoint(fullRect.right() - 3, fullRect.top() + 1), + QPoint(fullRect.right() - 4, fullRect.top() + 1), + QPoint(fullRect.right() - 2, fullRect.top() + 2), + QPoint(fullRect.right() - 1, fullRect.top() + 3), + QPoint(fullRect.right() - 1, fullRect.top() + 4) + }; + painter->drawPoints(points2, 5); + + // draw bottomline + painter->drawLine(fullRect.right(), fullRect.bottom(), fullRect.left(), fullRect.bottom()); + + // top highlight + painter->setPen(titleBarHighlight); + painter->drawLine(fullRect.left() + 6, fullRect.top() + 1, fullRect.right() - 6, fullRect.top() + 1); + } + // draw title + QRect textRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarLabel, widget); + QFont font = painter->font(); + font.setBold(true); + painter->setFont(font); + painter->setPen(active? (titleBar->palette.text().color().lighter(120)) : + titleBar->palette.text().color() ); + // Note workspace also does elliding but it does not use the correct font + QString title = QFontMetrics(font).elidedText(titleBar->text, Qt::ElideRight, textRect.width() - 14); + painter->drawText(textRect.adjusted(1, 1, 1, 1), title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); + painter->setPen(Qt::white); + if (active) + painter->drawText(textRect, title, QTextOption(Qt::AlignHCenter | Qt::AlignVCenter)); + // min button + if ((titleBar->subControls & SC_TitleBarMinButton) && (titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) && + !(titleBar->titleBarState& Qt::WindowMinimized)) { + QRect minButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarMinButton, widget); + if (minButtonRect.isValid()) { + bool hover = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarMinButton) && (titleBar->state & State_Sunken); + qt_gtk_draw_mdibutton(painter, titleBar, minButtonRect, hover, sunken); + QRect minButtonIconRect = minButtonRect.adjusted(buttonMargin ,buttonMargin , -buttonMargin, -buttonMargin); + painter->setPen(textColor); + painter->drawLine(minButtonIconRect.center().x() - 2, minButtonIconRect.center().y() + 3, + minButtonIconRect.center().x() + 3, minButtonIconRect.center().y() + 3); + painter->drawLine(minButtonIconRect.center().x() - 2, minButtonIconRect.center().y() + 4, + minButtonIconRect.center().x() + 3, minButtonIconRect.center().y() + 4); + painter->setPen(textAlphaColor); + painter->drawLine(minButtonIconRect.center().x() - 3, minButtonIconRect.center().y() + 3, + minButtonIconRect.center().x() - 3, minButtonIconRect.center().y() + 4); + painter->drawLine(minButtonIconRect.center().x() + 4, minButtonIconRect.center().y() + 3, + minButtonIconRect.center().x() + 4, minButtonIconRect.center().y() + 4); + } + } + // max button + if ((titleBar->subControls & SC_TitleBarMaxButton) && (titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) && + !(titleBar->titleBarState & Qt::WindowMaximized)) { + QRect maxButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarMaxButton, widget); + if (maxButtonRect.isValid()) { + bool hover = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarMaxButton) && (titleBar->state & State_Sunken); + qt_gtk_draw_mdibutton(painter, titleBar, maxButtonRect, hover, sunken); + + QRect maxButtonIconRect = maxButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin); + + painter->setPen(textColor); + painter->drawRect(maxButtonIconRect.adjusted(0, 0, -1, -1)); + painter->drawLine(maxButtonIconRect.left() + 1, maxButtonIconRect.top() + 1, + maxButtonIconRect.right() - 1, maxButtonIconRect.top() + 1); + painter->setPen(textAlphaColor); + const QPoint points[4] = { + maxButtonIconRect.topLeft(), + maxButtonIconRect.topRight(), + maxButtonIconRect.bottomLeft(), + maxButtonIconRect.bottomRight() + }; + painter->drawPoints(points, 4); + } + } + + // close button + if ((titleBar->subControls & SC_TitleBarCloseButton) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) { + QRect closeButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarCloseButton, widget); + if (closeButtonRect.isValid()) { + bool hover = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarCloseButton) && (titleBar->state & State_Sunken); + qt_gtk_draw_mdibutton(painter, titleBar, closeButtonRect, hover, sunken); + QRect closeIconRect = closeButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin); + painter->setPen(textAlphaColor); + const QLine lines[4] = { + QLine(closeIconRect.left() + 1, closeIconRect.top(), + closeIconRect.right(), closeIconRect.bottom() - 1), + QLine(closeIconRect.left(), closeIconRect.top() + 1, + closeIconRect.right() - 1, closeIconRect.bottom()), + QLine(closeIconRect.right() - 1, closeIconRect.top(), + closeIconRect.left(), closeIconRect.bottom() - 1), + QLine(closeIconRect.right(), closeIconRect.top() + 1, + closeIconRect.left() + 1, closeIconRect.bottom()) + }; + painter->drawLines(lines, 4); + const QPoint points[4] = { + closeIconRect.topLeft(), + closeIconRect.topRight(), + closeIconRect.bottomLeft(), + closeIconRect.bottomRight() + }; + painter->drawPoints(points, 4); + + painter->setPen(textColor); + painter->drawLine(closeIconRect.left() + 1, closeIconRect.top() + 1, + closeIconRect.right() - 1, closeIconRect.bottom() - 1); + painter->drawLine(closeIconRect.left() + 1, closeIconRect.bottom() - 1, + closeIconRect.right() - 1, closeIconRect.top() + 1); + } + } + + // normalize button + if ((titleBar->subControls & SC_TitleBarNormalButton) && + (((titleBar->titleBarFlags & Qt::WindowMinimizeButtonHint) && + (titleBar->titleBarState & Qt::WindowMinimized)) || + ((titleBar->titleBarFlags & Qt::WindowMaximizeButtonHint) && + (titleBar->titleBarState & Qt::WindowMaximized)))) { + QRect normalButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarNormalButton, widget); + if (normalButtonRect.isValid()) { + + bool hover = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarNormalButton) && (titleBar->state & State_Sunken); + QRect normalButtonIconRect = normalButtonRect.adjusted(buttonMargin, buttonMargin, -buttonMargin, -buttonMargin); + qt_gtk_draw_mdibutton(painter, titleBar, normalButtonRect, hover, sunken); + + QRect frontWindowRect = normalButtonIconRect.adjusted(0, 3, -3, 0); + painter->setPen(textColor); + painter->drawRect(frontWindowRect.adjusted(0, 0, -1, -1)); + painter->drawLine(frontWindowRect.left() + 1, frontWindowRect.top() + 1, + frontWindowRect.right() - 1, frontWindowRect.top() + 1); + painter->setPen(textAlphaColor); + const QPoint points[4] = { + frontWindowRect.topLeft(), + frontWindowRect.topRight(), + frontWindowRect.bottomLeft(), + frontWindowRect.bottomRight() + }; + painter->drawPoints(points, 4); + + QRect backWindowRect = normalButtonIconRect.adjusted(3, 0, 0, -3); + QRegion clipRegion = backWindowRect; + clipRegion -= frontWindowRect; + painter->save(); + painter->setClipRegion(clipRegion); + painter->setPen(textColor); + painter->drawRect(backWindowRect.adjusted(0, 0, -1, -1)); + painter->drawLine(backWindowRect.left() + 1, backWindowRect.top() + 1, + backWindowRect.right() - 1, backWindowRect.top() + 1); + painter->setPen(textAlphaColor); + const QPoint points2[4] = { + backWindowRect.topLeft(), + backWindowRect.topRight(), + backWindowRect.bottomLeft(), + backWindowRect.bottomRight() + }; + painter->drawPoints(points2, 4); + painter->restore(); + } + } + + // context help button + if (titleBar->subControls & SC_TitleBarContextHelpButton + && (titleBar->titleBarFlags & Qt::WindowContextHelpButtonHint)) { + QRect contextHelpButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarContextHelpButton, widget); + if (contextHelpButtonRect.isValid()) { + bool hover = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarContextHelpButton) && (titleBar->state & State_Sunken); + qt_gtk_draw_mdibutton(painter, titleBar, contextHelpButtonRect, hover, sunken); + + QColor blend; + QImage image(qt_titlebar_context_help); + QColor alpha = textColor; + alpha.setAlpha(128); + image.setColor(1, textColor.rgba()); + image.setColor(2, alpha.rgba()); + painter->setRenderHint(QPainter::SmoothPixmapTransform); + painter->drawImage(contextHelpButtonRect.adjusted(4, 4, -4, -4), image); + } + } + + // shade button + if (titleBar->subControls & SC_TitleBarShadeButton && (titleBar->titleBarFlags & Qt::WindowShadeButtonHint)) { + QRect shadeButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarShadeButton, widget); + if (shadeButtonRect.isValid()) { + bool hover = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarShadeButton) && (titleBar->state & State_Sunken); + qt_gtk_draw_mdibutton(painter, titleBar, shadeButtonRect, hover, sunken); + QImage image(qt_scrollbar_button_arrow_up); + image.setColor(1, textColor.rgba()); + painter->drawImage(shadeButtonRect.adjusted(5, 7, -5, -7), image); + } + } + + // unshade button + if (titleBar->subControls & SC_TitleBarUnshadeButton && (titleBar->titleBarFlags & Qt::WindowShadeButtonHint)) { + QRect unshadeButtonRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarUnshadeButton, widget); + if (unshadeButtonRect.isValid()) { + bool hover = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_MouseOver); + bool sunken = (titleBar->activeSubControls & SC_TitleBarUnshadeButton) && (titleBar->state & State_Sunken); + qt_gtk_draw_mdibutton(painter, titleBar, unshadeButtonRect, hover, sunken); + QImage image(qt_scrollbar_button_arrow_down); + image.setColor(1, textColor.rgba()); + painter->drawImage(unshadeButtonRect.adjusted(5, 7, -5, -7), image); + } + } + + if ((titleBar->subControls & SC_TitleBarSysMenu) && (titleBar->titleBarFlags & Qt::WindowSystemMenuHint)) { + QRect iconRect = proxy()->subControlRect(CC_TitleBar, titleBar, SC_TitleBarSysMenu, widget); + if (iconRect.isValid()) { + if (!titleBar->icon.isNull()) { + titleBar->icon.paint(painter, iconRect); + } else { + QStyleOption tool = *titleBar; + QPixmap pm = proxy()->standardIcon(SP_TitleBarMenuButton, &tool, widget).pixmap(16, 16); + tool.rect = iconRect; + painter->save(); + proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pm); + painter->restore(); + } + } + } + } + painter->restore(); + break; + +#ifndef QT_NO_GROUPBOX + + case CC_GroupBox: + painter->save(); + + if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) { + QRect textRect = proxy()->subControlRect(CC_GroupBox, groupBox, SC_GroupBoxLabel, widget); + QRect checkBoxRect = proxy()->subControlRect(CC_GroupBox, groupBox, SC_GroupBoxCheckBox, widget); + // Draw title + + if ((groupBox->subControls & QStyle::SC_GroupBoxLabel) && !groupBox->text.isEmpty()) { + // Draw prelight background + GtkWidget *gtkCheckButton = d->gtkWidget("GtkCheckButton"); + + if (option->state & State_MouseOver) { + QRect bgRect = textRect | checkBoxRect; + gtkPainter->paintFlatBox(gtkCheckButton, "checkbutton", bgRect.adjusted(0, 0, 0, -2), + GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT, gtk_widget_get_style(gtkCheckButton)); + } + + if (!groupBox->text.isEmpty()) { + int alignment = int(groupBox->textAlignment); + if (!proxy()->styleHint(QStyle::SH_UnderlineShortcut, option, widget)) + alignment |= Qt::TextHideMnemonic; + QColor textColor = groupBox->textColor; // Note: custom textColor is currently ignored + int labelState = GTK_STATE_INSENSITIVE; + + if (option->state & State_Enabled) + labelState = (option->state & State_MouseOver) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + + GdkColor gdkText = gtk_widget_get_style(gtkCheckButton)->fg[labelState]; + textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + painter->setPen(textColor); + QFont font = painter->font(); + font.setBold(true); + painter->setFont(font); + painter->drawText(textRect, Qt::TextShowMnemonic | Qt::AlignLeft| alignment, groupBox->text); + + if (option->state & State_HasFocus) + gtkPainter->paintFocus(gtkCheckButton, "checkbutton", textRect.adjusted(-4, -1, 0, -3), GTK_STATE_ACTIVE, style); + } + } + + if (groupBox->subControls & SC_GroupBoxCheckBox) { + QStyleOptionButton box; + box.QStyleOption::operator=(*groupBox); + box.rect = checkBoxRect; + proxy()->drawPrimitive(PE_IndicatorCheckBox, &box, painter, widget); + } + } + + painter->restore(); + break; +#endif // QT_NO_GROUPBOX + +#ifndef QT_NO_COMBOBOX + + case CC_ComboBox: + // See: http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GtkComboBox + // and http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GtkComboBoxEntry + if (const QStyleOptionComboBox *comboBox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + bool sunken = comboBox->state & State_On; // play dead, if combobox has no items + BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("cb-%0-%1").arg(sunken).arg(comboBox->editable)); + gtkPainter->reset(p); + gtkPainter->setUsePixmapCache(false); // cached externally + + bool isEnabled = (comboBox->state & State_Enabled); + bool focus = isEnabled && (comboBox->state & State_HasFocus); + GtkStateType state = qt_gtk_state(option); + int appears_as_list = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, comboBox, widget); + QStyleOptionComboBox comboBoxCopy = *comboBox; + comboBoxCopy.rect = option->rect; + + bool reverse = (option->direction == Qt::RightToLeft); + QRect rect = option->rect; + QRect arrowButtonRect = proxy()->subControlRect(CC_ComboBox, &comboBoxCopy, + SC_ComboBoxArrow, widget); + + GtkShadowType shadow = (option->state & State_Sunken || option->state & State_On ) ? + GTK_SHADOW_IN : GTK_SHADOW_OUT; + const QHashableLatin1Literal comboBoxPath = comboBox->editable ? QHashableLatin1Literal("GtkComboBoxEntry") : QHashableLatin1Literal("GtkComboBox"); + + // We use the gtk widget to position arrows and separators for us + GtkWidget *gtkCombo = d->gtkWidget(comboBoxPath); + GtkAllocation geometry = {0, 0, option->rect.width(), option->rect.height()}; + gtk_widget_set_direction(gtkCombo, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); + gtk_widget_size_allocate(gtkCombo, &geometry); + + QHashableLatin1Literal buttonPath = comboBox->editable ? QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton") + : QHashableLatin1Literal("GtkComboBox.GtkToggleButton"); + GtkWidget *gtkToggleButton = d->gtkWidget(buttonPath); + gtk_widget_set_direction(gtkToggleButton, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); + if (gtkToggleButton && (appears_as_list || comboBox->editable)) { + if (focus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, true); + // Draw the combo box as a line edit with a button next to it + if (comboBox->editable || appears_as_list) { + GtkStateType frameState = (state == GTK_STATE_PRELIGHT) ? GTK_STATE_NORMAL : state; + QHashableLatin1Literal entryPath = comboBox->editable ? QHashableLatin1Literal("GtkComboBoxEntry.GtkEntry") : QHashableLatin1Literal("GtkComboBox.GtkFrame"); + GtkWidget *gtkEntry = d->gtkWidget(entryPath); + gtk_widget_set_direction(gtkEntry, reverse ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); + QRect frameRect = option->rect; + + if (reverse) + frameRect.setLeft(arrowButtonRect.right()); + else + frameRect.setRight(arrowButtonRect.left()); + + // Fill the line edit background + // We could have used flat_box with "entry_bg" but that is probably not worth the overhead + uint resolve_mask = option->palette.resolve(); + GtkStyle *gtkEntryStyle = gtk_widget_get_style(gtkEntry); + QRect contentRect = frameRect.adjusted(gtkEntryStyle->xthickness, gtkEntryStyle->ythickness, + -gtkEntryStyle->xthickness, -gtkEntryStyle->ythickness); + // Required for inner blue highlight with clearlooks + if (focus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, true); + + if (widget && widget->testAttribute(Qt::WA_SetPalette) && + resolve_mask & (1 << QPalette::Base)) // Palette overridden by user + p->fillRect(contentRect, option->palette.base().color()); + else { + gtkPainter->paintFlatBox(gtkEntry, "entry_bg", contentRect, + option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, + GTK_SHADOW_NONE, gtkEntryStyle, entryPath.toString() + QString::number(focus)); + } + + gtkPainter->paintShadow(gtkEntry, comboBox->editable ? "entry" : "frame", frameRect, frameState, + GTK_SHADOW_IN, gtkEntryStyle, entryPath.toString() + + QString::number(focus) + QString::number(comboBox->editable) + + QString::number(option->direction)); + if (focus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkEntry, false); + } + + GtkStateType buttonState = GTK_STATE_NORMAL; + + if (!(option->state & State_Enabled)) + buttonState = GTK_STATE_INSENSITIVE; + else if (option->state & State_Sunken || option->state & State_On) + buttonState = GTK_STATE_ACTIVE; + else if (option->state & State_MouseOver && comboBox->activeSubControls & SC_ComboBoxArrow) + buttonState = GTK_STATE_PRELIGHT; + + Q_ASSERT(gtkToggleButton); + gtkPainter->paintBox(gtkToggleButton, "button", arrowButtonRect, buttonState, + shadow, gtk_widget_get_style(gtkToggleButton), buttonPath.toString() + + QString::number(focus) + QString::number(option->direction)); + if (focus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, false); + } else { + // Draw combo box as a button + QRect buttonRect = option->rect; + GtkStyle *gtkToggleButtonStyle = gtk_widget_get_style(gtkToggleButton); + + if (focus) // Clearlooks actually check the widget for the default state + QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, true); + gtkPainter->paintBox(gtkToggleButton, "button", + buttonRect, state, + shadow, gtkToggleButtonStyle, + buttonPath.toString() + QString::number(focus)); + if (focus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkToggleButton, false); + + + // Draw the separator between label and arrows + QHashableLatin1Literal vSeparatorPath = comboBox->editable + ? QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton.GtkHBox.GtkVSeparator") + : QHashableLatin1Literal("GtkComboBox.GtkToggleButton.GtkHBox.GtkVSeparator"); + + if (GtkWidget *gtkVSeparator = d->gtkWidget(vSeparatorPath)) { + GtkAllocation allocation; + gtk_widget_get_allocation(gtkVSeparator, &allocation); + QRect vLineRect(allocation.x, allocation.y, allocation.width, allocation.height); + + gtkPainter->paintVline(gtkVSeparator, "vseparator", + vLineRect, state, gtk_widget_get_style(gtkVSeparator), + 0, vLineRect.height(), 0, vSeparatorPath.toString()); + + + gint interiorFocus = true; + gtk_widget_style_get(gtkToggleButton, "interior-focus", &interiorFocus, NULL); + int xt = interiorFocus ? gtkToggleButtonStyle->xthickness : 0; + int yt = interiorFocus ? gtkToggleButtonStyle->ythickness : 0; + if (focus && ((option->state & State_KeyboardFocusChange) || styleHint(SH_UnderlineShortcut, option, widget))) + gtkPainter->paintFocus(gtkToggleButton, "button", + option->rect.adjusted(xt, yt, -xt, -yt), + option->state & State_Sunken ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL, + gtkToggleButtonStyle); + } + } + + if (comboBox->subControls & SC_ComboBoxArrow) { + if (!isEnabled) + state = GTK_STATE_INSENSITIVE; + else if (sunken) + state = GTK_STATE_ACTIVE; + else if (option->state & State_MouseOver) + state = GTK_STATE_PRELIGHT; + else + state = GTK_STATE_NORMAL; + + QHashableLatin1Literal arrowPath(""); + if (comboBox->editable) { + if (appears_as_list) + arrowPath = QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton.GtkArrow"); + else + arrowPath = QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton.GtkHBox.GtkArrow"); + } else { + if (appears_as_list) + arrowPath = QHashableLatin1Literal("GtkComboBox.GtkToggleButton.GtkArrow"); + else + arrowPath = QHashableLatin1Literal("GtkComboBox.GtkToggleButton.GtkHBox.GtkArrow"); + } + + GtkWidget *gtkArrow = d->gtkWidget(arrowPath); + gfloat scale = 0.7; + gint minSize = 15; + QRect arrowWidgetRect; + + if (gtkArrow && !gtk_check_version(2, 12, 0)) { + gtk_widget_style_get(gtkArrow, "arrow-scaling", &scale, NULL); + gtk_widget_style_get(gtkCombo, "arrow-size", &minSize, NULL); + } + if (gtkArrow) { + GtkAllocation allocation; + gtk_widget_get_allocation(gtkArrow, &allocation); + arrowWidgetRect = QRect(allocation.x, allocation.y, allocation.width, allocation.height); + style = gtk_widget_get_style(gtkArrow); + } + + // Note that for some reason the arrow-size is not properly respected with Hildon + // Hence we enforce the minimum "arrow-size" ourselves + int arrowSize = qMax(qMin(rect.height() - gtk_widget_get_style(gtkCombo)->ythickness * 2, minSize), + qMin(arrowWidgetRect.width(), arrowWidgetRect.height())); + QRect arrowRect(0, 0, static_cast<int>(arrowSize * scale), static_cast<int>(arrowSize * scale)); + + arrowRect.moveCenter(arrowWidgetRect.center()); + + if (sunken) { + int xoff, yoff; + const QHashableLatin1Literal toggleButtonPath = comboBox->editable + ? QHashableLatin1Literal("GtkComboBoxEntry.GtkToggleButton") + : QHashableLatin1Literal("GtkComboBox.GtkToggleButton"); + + GtkWidget *gtkButton = d->gtkWidget(toggleButtonPath); + gtk_widget_style_get(gtkButton, "child-displacement-x", &xoff, NULL); + gtk_widget_style_get(gtkButton, "child-displacement-y", &yoff, NULL); + arrowRect = arrowRect.adjusted(xoff, yoff, xoff, yoff); + } + + // Some styles such as Nimbus paint outside the arrowRect + // hence we have provide the whole widget as the cliprect + if (gtkArrow) { + gtkPainter->setClipRect(option->rect); + gtkPainter->paintArrow(gtkArrow, "arrow", arrowRect, + GTK_ARROW_DOWN, state, GTK_SHADOW_NONE, true, + style, arrowPath.toString() + QString::number(option->direction)); + } + } + END_STYLE_PIXMAPCACHE; + } + break; +#endif // QT_NO_COMBOBOX +#ifndef QT_NO_TOOLBUTTON + + case CC_ToolButton: + if (const QStyleOptionToolButton *toolbutton + = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { + QRect button, menuarea; + button = proxy()->subControlRect(control, toolbutton, SC_ToolButton, widget); + menuarea = proxy()->subControlRect(control, toolbutton, SC_ToolButtonMenu, widget); + State bflags = toolbutton->state & ~(State_Sunken | State_MouseOver); + + if (bflags & State_AutoRaise) + if (!(bflags & State_MouseOver)) + bflags &= ~State_Raised; + + State mflags = bflags; + + if (toolbutton->state & State_Sunken) { + if (toolbutton->activeSubControls & SC_ToolButton) + bflags |= State_Sunken; + if (toolbutton->activeSubControls & SC_ToolButtonMenu) + mflags |= State_Sunken; + } else if (toolbutton->state & State_MouseOver) { + if (toolbutton->activeSubControls & SC_ToolButton) + bflags |= State_MouseOver; + if (toolbutton->activeSubControls & SC_ToolButtonMenu) + mflags |= State_MouseOver; + } + + QStyleOption tool = *toolbutton; + + if (toolbutton->subControls & SC_ToolButton) { + if (bflags & (State_Sunken | State_On | State_Raised | State_MouseOver)) { + tool.rect = button; + tool.state = bflags; + proxy()->drawPrimitive(PE_PanelButtonTool, &tool, painter, widget); + } + } + + bool drawMenuArrow = toolbutton->features & QStyleOptionToolButton::HasMenu && + !(toolbutton->features & QStyleOptionToolButton::MenuButtonPopup); + int popupArrowSize = drawMenuArrow ? 7 : 0; + + if (toolbutton->state & State_HasFocus) { + QStyleOptionFocusRect fr; + fr.QStyleOption::operator=(*toolbutton); + fr.rect = proxy()->subControlRect(CC_ToolButton, toolbutton, SC_ToolButton, widget); + fr.rect.adjust(1, 1, -1, -1); + proxy()->drawPrimitive(PE_FrameFocusRect, &fr, painter, widget); + } + + QStyleOptionToolButton label = *toolbutton; + label.state = bflags; + GtkWidget *gtkButton = d->gtkWidget("GtkToolButton.GtkButton"); + QPalette pal = toolbutton->palette; + if (option->state & State_Enabled && + option->state & State_MouseOver && !(widget && widget->testAttribute(Qt::WA_SetPalette))) { + GdkColor gdkText = gtk_widget_get_style(gtkButton)->fg[GTK_STATE_PRELIGHT]; + QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + pal.setBrush(QPalette::All, QPalette::ButtonText, textColor); + label.palette = pal; + } + label.rect = button.adjusted(style->xthickness, style->ythickness, + -style->xthickness - popupArrowSize, -style->ythickness); + proxy()->drawControl(CE_ToolButtonLabel, &label, painter, widget); + + if (toolbutton->subControls & SC_ToolButtonMenu) { + tool.rect = menuarea; + tool.state = mflags; + if ((mflags & State_Enabled && (mflags & (State_Sunken | State_Raised | State_MouseOver))) || !(mflags & State_AutoRaise)) + proxy()->drawPrimitive(PE_IndicatorButtonDropDown, &tool, painter, widget); + + proxy()->drawPrimitive(PE_IndicatorArrowDown, &tool, painter, widget); + + } else if (drawMenuArrow) { + QRect ir = toolbutton->rect; + QStyleOptionToolButton newBtn = *toolbutton; + newBtn.rect = QRect(ir.right() - popupArrowSize - style->xthickness - 3, ir.height()/2 - 1, popupArrowSize, popupArrowSize); + proxy()->drawPrimitive(PE_IndicatorArrowDown, &newBtn, painter, widget); + } + } + break; + +#endif // QT_NO_TOOLBUTTON +#ifndef QT_NO_SCROLLBAR + + case CC_ScrollBar: + if (const QStyleOptionSlider *scrollBar = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + GtkWidget *gtkHScrollBar = d->gtkWidget("GtkHScrollbar"); + GtkWidget *gtkVScrollBar = d->gtkWidget("GtkVScrollbar"); + + // Fill background in case the scrollbar is partially transparent + painter->fillRect(option->rect, option->palette.window()); + + QRect rect = scrollBar->rect; + QRect scrollBarSubLine = proxy()->subControlRect(control, scrollBar, SC_ScrollBarSubLine, widget); + QRect scrollBarAddLine = proxy()->subControlRect(control, scrollBar, SC_ScrollBarAddLine, widget); + QRect scrollBarSlider = proxy()->subControlRect(control, scrollBar, SC_ScrollBarSlider, widget); + QRect grooveRect = proxy()->subControlRect(control, scrollBar, SC_ScrollBarGroove, widget); + bool horizontal = scrollBar->orientation == Qt::Horizontal; + GtkWidget * scrollbarWidget = horizontal ? gtkHScrollBar : gtkVScrollBar; + style = gtk_widget_get_style(scrollbarWidget); + gboolean trough_under_steppers = true; + gboolean trough_side_details = false; + gboolean activate_slider = true; + gboolean stepper_size = 14; + gint trough_border = 1; + if (!gtk_check_version(2, 10, 0)) { + gtk_widget_style_get((GtkWidget*)(scrollbarWidget), + "trough-border", &trough_border, + "trough-side-details", &trough_side_details, + "trough-under-steppers", &trough_under_steppers, + /*"activate-slider", &activate_slider,*/ //deprecated + "stepper-size", &stepper_size, NULL); + } + if (trough_under_steppers) { + scrollBarAddLine.adjust(trough_border, trough_border, -trough_border, -trough_border); + scrollBarSubLine.adjust(trough_border, trough_border, -trough_border, -trough_border); + scrollBarSlider.adjust(horizontal ? -trough_border : 0, horizontal ? 0 : -trough_border, + horizontal ? trough_border : 0, horizontal ? 0 : trough_border); + } + + // Some styles check the position of scrollbars in order to determine + // if lines should be painted when the scrollbar is in max or min positions. + int maximum = 2; + int fakePos = 0; + bool reverse = (option->direction == Qt::RightToLeft); + if (scrollBar->minimum == scrollBar->maximum) + maximum = 0; + if (scrollBar->sliderPosition == scrollBar->maximum) + fakePos = maximum; + else if (scrollBar->sliderPosition > scrollBar->minimum) + fakePos = maximum - 1; + + + GtkRange *range = (GtkRange*)(horizontal ? gtkHScrollBar : gtkVScrollBar); + GtkAdjustment *adjustment = 0; + + adjustment = gtk_range_get_adjustment(range); + + if (adjustment) { + gtk_adjustment_configure(adjustment, fakePos, 0, maximum, 0, 0, 0); + } else { + adjustment = (GtkAdjustment*)gtk_adjustment_new(fakePos, 0, maximum, 0, 0, 0); + gtk_range_set_adjustment(range, adjustment); + } + + if (scrollBar->subControls & SC_ScrollBarGroove) { + GtkStateType state = GTK_STATE_ACTIVE; + + if (!(option->state & State_Enabled)) + state = GTK_STATE_INSENSITIVE; + + if (trough_under_steppers) + grooveRect = option->rect; + + gtkPainter->paintBox(scrollbarWidget, "trough", grooveRect, state, GTK_SHADOW_IN, style); + } + + //paint slider + if (scrollBar->subControls & SC_ScrollBarSlider) { + GtkStateType state = GTK_STATE_NORMAL; + + if (!(option->state & State_Enabled)) + state = GTK_STATE_INSENSITIVE; + else if (activate_slider && + option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarSlider)) + state = GTK_STATE_ACTIVE; + else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSlider)) + state = GTK_STATE_PRELIGHT; + + GtkShadowType shadow = GTK_SHADOW_OUT; + + if (trough_under_steppers) { + if (!horizontal) + scrollBarSlider.adjust(trough_border, 0, -trough_border, 0); + else + scrollBarSlider.adjust(0, trough_border, 0, -trough_border); + } + + gtkPainter->paintSlider(scrollbarWidget, "slider", scrollBarSlider, state, shadow, style, + horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, QString(QLS("%0%1")).arg(fakePos).arg(maximum)); + } + + if (scrollBar->subControls & SC_ScrollBarAddLine) { + GtkAllocation vAllocation; + vAllocation.y = scrollBarAddLine.top(); + vAllocation.height = scrollBarAddLine.height() - rect.height() + 6; + gtk_widget_set_allocation(gtkVScrollBar, &vAllocation); + + GtkAllocation hAllocation; + hAllocation.x = scrollBarAddLine.right(); + hAllocation.width = scrollBarAddLine.width() - rect.width(); + gtk_widget_set_allocation(gtkHScrollBar, &hAllocation); + + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = GTK_STATE_NORMAL; + + if (!(option->state & State_Enabled) || (fakePos == maximum)) + state = GTK_STATE_INSENSITIVE; + else if (option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarAddLine)) { + state = GTK_STATE_ACTIVE; + shadow = GTK_SHADOW_IN; + + } else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarAddLine)) + state = GTK_STATE_PRELIGHT; + + gtkPainter->paintBox(scrollbarWidget, + horizontal ? "hscrollbar" : "vscrollbar", scrollBarAddLine, + state, shadow, style, QLS("add")); + + gtkPainter->paintArrow(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarAddLine.adjusted(4, 4, -4, -4), + horizontal ? (reverse ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT) : + GTK_ARROW_DOWN, state, GTK_SHADOW_NONE, false, style); + } + + if (scrollBar->subControls & SC_ScrollBarSubLine) { + GtkAllocation vAllocation; + vAllocation.y = 0; + vAllocation.height = scrollBarSubLine.height(); + gtk_widget_set_allocation(gtkVScrollBar, &vAllocation); + + GtkAllocation hAllocation; + hAllocation.x = 0; + hAllocation.width = scrollBarSubLine.width(); + gtk_widget_set_allocation(gtkHScrollBar, &hAllocation); + + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = GTK_STATE_NORMAL; + + if (!(option->state & State_Enabled) || (fakePos == 0)) + state = GTK_STATE_INSENSITIVE; + else if (option->state & State_Sunken && (scrollBar->activeSubControls & SC_ScrollBarSubLine)) { + shadow = GTK_SHADOW_IN; + state = GTK_STATE_ACTIVE; + + } else if (option->state & State_MouseOver && (scrollBar->activeSubControls & SC_ScrollBarSubLine)) + state = GTK_STATE_PRELIGHT; + + gtkPainter->paintBox(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarSubLine, + state, shadow, style, QLS("sub")); + + gtkPainter->paintArrow(scrollbarWidget, horizontal ? "hscrollbar" : "vscrollbar", scrollBarSubLine.adjusted(4, 4, -4, -4), + horizontal ? (reverse ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT) : + GTK_ARROW_UP, state, GTK_SHADOW_NONE, false, style); + } + } + break; + +#endif //QT_NO_SCROLLBAR +#ifndef QT_NO_SPINBOX + + case CC_SpinBox: + if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + + GtkWidget *gtkSpinButton = spinBox->buttonSymbols == QAbstractSpinBox::NoButtons + ? d->gtkWidget("GtkEntry") + : d->gtkWidget("GtkSpinButton"); + bool isEnabled = (spinBox->state & State_Enabled); + bool hover = isEnabled && (spinBox->state & State_MouseOver); + bool sunken = (spinBox->state & State_Sunken); + bool upIsActive = (spinBox->activeSubControls == SC_SpinBoxUp); + bool downIsActive = (spinBox->activeSubControls == SC_SpinBoxDown); + bool reverse = (spinBox->direction == Qt::RightToLeft); + + QRect editArea = option->rect; + QRect editRect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxEditField, widget); + QRect upRect, downRect, buttonRect; + if (spinBox->buttonSymbols != QAbstractSpinBox::NoButtons) { + upRect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxUp, widget); + downRect = proxy()->subControlRect(CC_SpinBox, option, SC_SpinBoxDown, widget); + + //### Move this to subControlRect + upRect.setTop(option->rect.top()); + + if (reverse) + upRect.setLeft(option->rect.left()); + else + upRect.setRight(option->rect.right()); + + downRect.setBottom(option->rect.bottom()); + + if (reverse) + downRect.setLeft(option->rect.left()); + else + downRect.setRight(option->rect.right()); + + buttonRect = upRect | downRect; + + if (reverse) + editArea.setLeft(upRect.right()); + else + editArea.setRight(upRect.left()); + } + if (spinBox->frame) { + GtkStateType state = qt_gtk_state(option); + + if (!(option->state & State_Enabled)) + state = GTK_STATE_INSENSITIVE; + else if (option->state & State_HasFocus) + state = GTK_STATE_NORMAL; + else if (state == GTK_STATE_PRELIGHT) + state = GTK_STATE_NORMAL; + + style = gtk_widget_get_style(gtkSpinButton); + + + QString key; + + if (option->state & State_HasFocus) { + key += QLatin1Char('f'); + QGtkStylePrivate::gtkWidgetSetFocus(gtkSpinButton, true); + } + + uint resolve_mask = option->palette.resolve(); + + if (resolve_mask & (1 << QPalette::Base)) // Palette overridden by user + painter->fillRect(editRect, option->palette.base().color()); + else + gtkPainter->paintFlatBox(gtkSpinButton, "entry_bg", editArea.adjusted(style->xthickness, style->ythickness, + -style->xthickness, -style->ythickness), + option->state & State_Enabled ? + GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE, GTK_SHADOW_NONE, style, key); + + gtkPainter->paintShadow(gtkSpinButton, "entry", editArea, state, GTK_SHADOW_IN, gtk_widget_get_style(gtkSpinButton), key); + if (spinBox->buttonSymbols != QAbstractSpinBox::NoButtons) { + gtkPainter->paintBox(gtkSpinButton, "spinbutton", buttonRect, state, GTK_SHADOW_IN, style, key); + + upRect.setSize(downRect.size()); + if (!(option->state & State_Enabled)) + gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_INSENSITIVE, GTK_SHADOW_IN, style, key); + else if (upIsActive && sunken) + gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_ACTIVE, GTK_SHADOW_IN, style, key); + else if (upIsActive && hover) + gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style, key); + else + gtkPainter->paintBox(gtkSpinButton, "spinbutton_up", upRect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, style, key); + + if (!(option->state & State_Enabled)) + gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_INSENSITIVE, GTK_SHADOW_IN, style, key); + else if (downIsActive && sunken) + gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_ACTIVE, GTK_SHADOW_IN, style, key); + else if (downIsActive && hover) + gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style, key); + else + gtkPainter->paintBox(gtkSpinButton, "spinbutton_down", downRect, GTK_STATE_NORMAL, GTK_SHADOW_OUT, style, key); + + if (option->state & State_HasFocus) + QGtkStylePrivate::gtkWidgetSetFocus(gtkSpinButton, false); + } + } + + if (spinBox->buttonSymbols == QAbstractSpinBox::PlusMinus) { + int centerX = upRect.center().x(); + int centerY = upRect.center().y(); + // plus/minus + + if (spinBox->activeSubControls == SC_SpinBoxUp && sunken) { + painter->drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY); + painter->drawLine(1 + centerX, 1 + centerY - 2, 1 + centerX, 1 + centerY + 2); + + } else { + painter->drawLine(centerX - 2, centerY, centerX + 2, centerY); + painter->drawLine(centerX, centerY - 2, centerX, centerY + 2); + } + centerX = downRect.center().x(); + centerY = downRect.center().y(); + + if (spinBox->activeSubControls == SC_SpinBoxDown && sunken) { + painter->drawLine(1 + centerX - 2, 1 + centerY, 1 + centerX + 2, 1 + centerY); + } else { + painter->drawLine(centerX - 2, centerY, centerX + 2, centerY); + } + + } else if (spinBox->buttonSymbols == QAbstractSpinBox::UpDownArrows) { + int size = d->getSpinboxArrowSize(); + int w = size / 2 - 1; + w -= w % 2 - 1; // force odd + int h = (w + 1)/2; + QRect arrowRect(0, 0, w, h); + arrowRect.moveCenter(upRect.center()); + // arrows + GtkStateType state = GTK_STATE_NORMAL; + + if (!(option->state & State_Enabled) || !(spinBox->stepEnabled & QAbstractSpinBox::StepUpEnabled)) + state = GTK_STATE_INSENSITIVE; + + gtkPainter->paintArrow(gtkSpinButton, "spinbutton", arrowRect, GTK_ARROW_UP, state, + GTK_SHADOW_NONE, false, style); + + arrowRect.moveCenter(downRect.center()); + + if (!(option->state & State_Enabled) || !(spinBox->stepEnabled & QAbstractSpinBox::StepDownEnabled)) + state = GTK_STATE_INSENSITIVE; + + gtkPainter->paintArrow(gtkSpinButton, "spinbutton", arrowRect, GTK_ARROW_DOWN, state, + GTK_SHADOW_NONE, false, style); + } + } + break; + +#endif // QT_NO_SPINBOX + +#ifndef QT_NO_SLIDER + + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + GtkWidget *hScaleWidget = d->gtkWidget("GtkHScale"); + GtkWidget *vScaleWidget = d->gtkWidget("GtkVScale"); + + QRect groove = proxy()->subControlRect(CC_Slider, option, SC_SliderGroove, widget); + QRect handle = proxy()->subControlRect(CC_Slider, option, SC_SliderHandle, widget); + + bool horizontal = slider->orientation == Qt::Horizontal; + bool ticksAbove = slider->tickPosition & QSlider::TicksAbove; + bool ticksBelow = slider->tickPosition & QSlider::TicksBelow; + + QBrush oldBrush = painter->brush(); + QPen oldPen = painter->pen(); + + QColor shadowAlpha(Qt::black); + shadowAlpha.setAlpha(10); + QColor highlightAlpha(Qt::white); + highlightAlpha.setAlpha(80); + + gtk_widget_set_direction(hScaleWidget, slider->upsideDown ? + GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); + GtkWidget *scaleWidget = horizontal ? hScaleWidget : vScaleWidget; + style = gtk_widget_get_style(scaleWidget); + + if ((option->subControls & SC_SliderGroove) && groove.isValid()) { + + GtkRange *range = (GtkRange*)scaleWidget; + GtkAdjustment *adjustment = gtk_range_get_adjustment(range); + if (adjustment) { + gtk_adjustment_configure(adjustment, + slider->sliderPosition, + slider->minimum, + slider->maximum, + slider->singleStep, + slider->singleStep, + slider->pageStep); + } else { + adjustment = (GtkAdjustment*)gtk_adjustment_new(slider->sliderPosition, + slider->minimum, + slider->maximum, + slider->singleStep, + slider->singleStep, + slider->pageStep); + gtk_range_set_adjustment(range, adjustment); + } + + int outerSize; + gtk_range_set_inverted(range, !horizontal); + gtk_widget_style_get(scaleWidget, "trough-border", &outerSize, NULL); + outerSize++; + + GtkStateType state = qt_gtk_state(option); + int focusFrameMargin = 2; + QRect grooveRect = option->rect.adjusted(focusFrameMargin, outerSize + focusFrameMargin, + -focusFrameMargin, -outerSize - focusFrameMargin); + + gboolean trough_side_details = false; // Indicates if the upper or lower scale background differs + if (!gtk_check_version(2, 10, 0)) + gtk_widget_style_get((GtkWidget*)(scaleWidget), "trough-side-details", &trough_side_details, NULL); + + if (!trough_side_details) { + gtkPainter->paintBox(scaleWidget, "trough", grooveRect, state, + GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition)); + } else { + QRect upperGroove = grooveRect; + QRect lowerGroove = grooveRect; + + if (horizontal) { + if (slider->upsideDown) { + lowerGroove.setLeft(handle.center().x()); + upperGroove.setRight(handle.center().x()); + } else { + upperGroove.setLeft(handle.center().x()); + lowerGroove.setRight(handle.center().x()); + } + } else { + if (!slider->upsideDown) { + lowerGroove.setBottom(handle.center().y()); + upperGroove.setTop(handle.center().y()); + } else { + upperGroove.setBottom(handle.center().y()); + lowerGroove.setTop(handle.center().y()); + } + } + + gtkPainter->paintBox(scaleWidget, "trough-upper", upperGroove, state, + GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition)); + gtkPainter->paintBox(scaleWidget, "trough-lower", lowerGroove, state, + GTK_SHADOW_IN, style, QString(QLS("p%0")).arg(slider->sliderPosition)); + } + } + + if (option->subControls & SC_SliderTickmarks) { + painter->setPen(darkOutline); + int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); + int available = proxy()->pixelMetric(PM_SliderSpaceAvailable, slider, widget); + int interval = slider->tickInterval; + + if (interval <= 0) { + interval = slider->singleStep; + + if (QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, interval, + available) + - QStyle::sliderPositionFromValue(slider->minimum, slider->maximum, + 0, available) < 3) + interval = slider->pageStep; + } + + if (interval <= 0) + interval = 1; + + int v = slider->minimum; + int len = proxy()->pixelMetric(PM_SliderLength, slider, widget); + while (v <= slider->maximum + 1) { + if (v == slider->maximum + 1 && interval == 1) + break; + const int v_ = qMin(v, slider->maximum); + int pos = sliderPositionFromValue(slider->minimum, slider->maximum, + v_, (horizontal + ? slider->rect.width() + : slider->rect.height()) - len, + slider->upsideDown) + len / 2; + int extra = 2 - ((v_ == slider->minimum || v_ == slider->maximum) ? 1 : 0); + if (horizontal) { + if (ticksAbove) + painter->drawLine(pos, slider->rect.top() + extra, + pos, slider->rect.top() + tickSize); + if (ticksBelow) + painter->drawLine(pos, slider->rect.bottom() - extra, + pos, slider->rect.bottom() - tickSize); + + } else { + if (ticksAbove) + painter->drawLine(slider->rect.left() + extra, pos, + slider->rect.left() + tickSize, pos); + if (ticksBelow) + painter->drawLine(slider->rect.right() - extra, pos, + slider->rect.right() - tickSize, pos); + } + + // In the case where maximum is max int + int nextInterval = v + interval; + if (nextInterval < v) + break; + v = nextInterval; + } + } + + // Draw slider handle + if (option->subControls & SC_SliderHandle) { + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = GTK_STATE_NORMAL; + + if (!(option->state & State_Enabled)) + state = GTK_STATE_INSENSITIVE; + else if (option->state & State_MouseOver && option->activeSubControls & SC_SliderHandle) + state = GTK_STATE_PRELIGHT; + + bool horizontal = option->state & State_Horizontal; + + if (slider->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*slider); + fropt.rect = slider->rect.adjusted(-1, -1 ,1, 1); + + if (horizontal) { + fropt.rect.setTop(handle.top() - 3); + fropt.rect.setBottom(handle.bottom() + 4); + + } else { + fropt.rect.setLeft(handle.left() - 3); + fropt.rect.setRight(handle.right() + 3); + } + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); + } + gtkPainter->paintSlider(scaleWidget, horizontal ? "hscale" : "vscale", handle, state, shadow, style, + horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + } + painter->setBrush(oldBrush); + painter->setPen(oldPen); + } + break; + case CC_Dial: + if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(option)) + QStyleHelper::drawDial(dial, painter); + break; + +#endif // QT_NO_SLIDER + + default: + QCommonStyle::drawComplexControl(control, option, painter, widget); + + break; + } +} + + +/*! + \reimp +*/ +void QGtkStyle::drawControl(ControlElement element, + const QStyleOption *option, + QPainter *painter, + const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) { + QCommonStyle::drawControl(element, option, painter, widget); + return; + } + + GtkStyle* style = d->gtkStyle(); + QGtkPainter* gtkPainter = d->gtkPainter(painter); + + switch (element) { + case CE_ProgressBarLabel: + if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar"); + if (!gtkProgressBar) + return; + + QRect leftRect; + QRect rect = bar->rect; + GtkStyle *gtkProgressBarStyle = gtk_widget_get_style(gtkProgressBar); + GdkColor gdkText = gtkProgressBarStyle->fg[GTK_STATE_NORMAL]; + QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + gdkText = gtkProgressBarStyle->fg[GTK_STATE_PRELIGHT]; + QColor alternateTextColor= QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + + painter->save(); + bool vertical = false, inverted = false; + if (const QStyleOptionProgressBar *bar2 = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + vertical = (bar2->orientation == Qt::Vertical); + inverted = bar2->invertedAppearance; + } + if (vertical) + rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // flip width and height + const int progressIndicatorPos = (bar->progress - qreal(bar->minimum)) * rect.width() / + qMax(qreal(1.0), qreal(bar->maximum) - bar->minimum); + if (progressIndicatorPos >= 0 && progressIndicatorPos <= rect.width()) + leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height()); + if (vertical) + leftRect.translate(rect.width() - progressIndicatorPos, 0); + + bool flip = (!vertical && (((bar->direction == Qt::RightToLeft) && !inverted) || + ((bar->direction == Qt::LeftToRight) && inverted))); + + QRegion rightRect = rect; + rightRect = rightRect.subtracted(leftRect); + painter->setClipRegion(rightRect); + painter->setPen(flip ? alternateTextColor : textColor); + painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter)); + if (!leftRect.isNull()) { + painter->setPen(flip ? textColor : alternateTextColor); + painter->setClipRect(leftRect); + painter->drawText(rect, bar->text, QTextOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter)); + } + painter->restore(); + } + break; + case CE_PushButtonLabel: + if (const QStyleOptionButton *button = qstyleoption_cast<const QStyleOptionButton *>(option)) { + QRect ir = button->rect; + uint tf = Qt::AlignVCenter | Qt::TextShowMnemonic; + QPoint buttonShift; + + if (option->state & State_Sunken) + buttonShift = QPoint(pixelMetric(PM_ButtonShiftHorizontal, option, widget), + proxy()->pixelMetric(PM_ButtonShiftVertical, option, widget)); + + if (proxy()->styleHint(SH_UnderlineShortcut, button, widget)) + tf |= Qt::TextShowMnemonic; + else + tf |= Qt::TextHideMnemonic; + + if (!button->icon.isNull()) { + //Center both icon and text + QPoint point; + + QIcon::Mode mode = button->state & State_Enabled ? QIcon::Normal : QIcon::Disabled; + if (mode == QIcon::Normal && button->state & State_HasFocus) + mode = QIcon::Active; + + QIcon::State state = QIcon::Off; + + if (button->state & State_On) + state = QIcon::On; + + QPixmap pixmap = button->icon.pixmap(button->iconSize, mode, state); + int w = pixmap.width(); + int h = pixmap.height(); + + if (!button->text.isEmpty()) + w += button->fontMetrics.boundingRect(option->rect, tf, button->text).width() + 4; + + point = QPoint(ir.x() + ir.width() / 2 - w / 2, + ir.y() + ir.height() / 2 - h / 2); + + if (button->direction == Qt::RightToLeft) + point.rx() += pixmap.width(); + + painter->drawPixmap(visualPos(button->direction, button->rect, point + buttonShift), pixmap); + + if (button->direction == Qt::RightToLeft) + ir.translate(-point.x() - 2, 0); + else + ir.translate(point.x() + pixmap.width() + 2, 0); + + // left-align text if there is + if (!button->text.isEmpty()) + tf |= Qt::AlignLeft; + + } else { + tf |= Qt::AlignHCenter; + } + + ir.translate(buttonShift); + + if (button->features & QStyleOptionButton::HasMenu) + ir = ir.adjusted(0, 0, -pixelMetric(PM_MenuButtonIndicator, button, widget), 0); + + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + QPalette pal = button->palette; + int labelState = GTK_STATE_INSENSITIVE; + if (option->state & State_Enabled) + labelState = (option->state & State_MouseOver && !(option->state & State_Sunken)) ? + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + + GdkColor gdkText = gtk_widget_get_style(gtkButton)->fg[labelState]; + QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + pal.setBrush(QPalette::ButtonText, textColor); + proxy()->drawItemText(painter, ir, tf, pal, (button->state & State_Enabled), + button->text, QPalette::ButtonText); + } + break; + + case CE_RadioButton: // Fall through + case CE_CheckBox: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + bool isRadio = (element == CE_RadioButton); + + // Draw prelight background + GtkWidget *gtkRadioButton = d->gtkWidget("GtkRadioButton"); + + if (option->state & State_MouseOver) { + gtkPainter->paintFlatBox(gtkRadioButton, "checkbutton", option->rect, + GTK_STATE_PRELIGHT, GTK_SHADOW_ETCHED_OUT, gtk_widget_get_style(gtkRadioButton)); + } + + QStyleOptionButton subopt = *btn; + subopt.rect = subElementRect(isRadio ? SE_RadioButtonIndicator + : SE_CheckBoxIndicator, btn, widget); + proxy()->drawPrimitive(isRadio ? PE_IndicatorRadioButton : PE_IndicatorCheckBox, + &subopt, painter, widget); + subopt.rect = subElementRect(isRadio ? SE_RadioButtonContents + : SE_CheckBoxContents, btn, widget); + // Get label text color + QPalette pal = subopt.palette; + int labelState = GTK_STATE_INSENSITIVE; + if (option->state & State_Enabled) + labelState = (option->state & State_MouseOver) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + + GdkColor gdkText = gtk_widget_get_style(gtkRadioButton)->fg[labelState]; + QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + pal.setBrush(QPalette::WindowText, textColor); + subopt.palette = pal; + proxy()->drawControl(isRadio ? CE_RadioButtonLabel : CE_CheckBoxLabel, &subopt, painter, widget); + + if (btn->state & State_HasFocus) { + QStyleOptionFocusRect fropt; + fropt.QStyleOption::operator=(*btn); + fropt.rect = subElementRect(isRadio ? SE_RadioButtonFocusRect + : SE_CheckBoxFocusRect, btn, widget); + proxy()->drawPrimitive(PE_FrameFocusRect, &fropt, painter, widget); + } + } + break; + +#ifndef QT_NO_COMBOBOX + + case CE_ComboBoxLabel: + if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + QRect editRect = proxy()->subControlRect(CC_ComboBox, cb, SC_ComboBoxEditField, widget); + bool appearsAsList = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, cb, widget); + painter->save(); + painter->setClipRect(editRect); + + if (!cb->currentIcon.isNull()) { + QIcon::Mode mode = cb->state & State_Enabled ? QIcon::Normal + : QIcon::Disabled; + QPixmap pixmap = cb->currentIcon.pixmap(cb->iconSize, mode); + QRect iconRect(editRect); + iconRect.setWidth(cb->iconSize.width() + 4); + + iconRect = alignedRect(cb->direction, + Qt::AlignLeft | Qt::AlignVCenter, + iconRect.size(), editRect); + + if (cb->editable) + painter->fillRect(iconRect, option->palette.brush(QPalette::Base)); + + proxy()->drawItemPixmap(painter, iconRect, Qt::AlignCenter, pixmap); + + if (cb->direction == Qt::RightToLeft) + editRect.translate(-4 - cb->iconSize.width(), 0); + else + editRect.translate(cb->iconSize.width() + 4, 0); + } + + if (!cb->currentText.isEmpty() && !cb->editable) { + GtkWidget *gtkCombo = d->gtkWidget("GtkComboBox"); + QPalette pal = cb->palette; + int labelState = GTK_STATE_INSENSITIVE; + + if (option->state & State_Enabled) + labelState = (option->state & State_MouseOver && !appearsAsList) ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; + + GdkColor gdkText = gtk_widget_get_style(gtkCombo)->fg[labelState]; + + QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + + pal.setBrush(QPalette::ButtonText, textColor); + + proxy()->drawItemText(painter, editRect.adjusted(1, 0, -1, 0), + visualAlignment(cb->direction, Qt::AlignLeft | Qt::AlignVCenter), + pal, cb->state & State_Enabled, cb->currentText, QPalette::ButtonText); + } + + painter->restore(); + } + break; + +#endif // QT_NO_COMBOBOX + + case CE_DockWidgetTitle: + painter->save(); + if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(option)) { + const QStyleOptionDockWidget *v2 + = qstyleoption_cast<const QStyleOptionDockWidget*>(dwOpt); + bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar; + + QRect rect = dwOpt->rect; + QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, option, widget).adjusted(-2, 0, -2, 0); + QRect r = rect.adjusted(0, 0, -1, -1); + if (verticalTitleBar) + r.adjust(0, 0, 0, -1); + + if (verticalTitleBar) { + QRect r = rect; + r.setSize(r.size().transposed()); + + titleRect = QRect(r.left() + rect.bottom() + - titleRect.bottom(), + r.top() + titleRect.left() - rect.left(), + titleRect.height(), titleRect.width()); + + painter->translate(r.left(), r.top() + r.width()); + painter->rotate(-90); + painter->translate(-r.left(), -r.top()); + + rect = r; + } + + if (!dwOpt->title.isEmpty()) { + QString titleText + = painter->fontMetrics().elidedText(dwOpt->title, + Qt::ElideRight, titleRect.width()); + proxy()->drawItemText(painter, + titleRect, + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, dwOpt->palette, + dwOpt->state & State_Enabled, titleText, + QPalette::WindowText); + } + } + painter->restore(); + break; + + + + case CE_HeaderSection: + painter->save(); + + // Draws the header in tables. + if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) { + Q_UNUSED(header); + GtkWidget *gtkTreeView = d->gtkWidget("GtkTreeView"); + // Get the middle column + GtkTreeViewColumn *column = gtk_tree_view_get_column((GtkTreeView*)gtkTreeView, 1); + Q_ASSERT(column); + + GtkWidget *gtkTreeHeader = column->button; + GtkStateType state = qt_gtk_state(option); + GtkShadowType shadow = GTK_SHADOW_OUT; + + if (option->state & State_Sunken) + shadow = GTK_SHADOW_IN; + + gtkPainter->paintBox(gtkTreeHeader, "button", option->rect.adjusted(-1, 0, 0, 0), state, shadow, gtk_widget_get_style(gtkTreeHeader)); + } + + painter->restore(); + break; + +#ifndef QT_NO_SIZEGRIP + + case CE_SizeGrip: { + GtkWidget *gtkStatusbar = d->gtkWidget("GtkStatusbar.GtkFrame"); + GtkStyle *gtkStatusbarStyle = gtk_widget_get_style(gtkStatusbar); + QRect gripRect = option->rect.adjusted(0, 0, -gtkStatusbarStyle->xthickness, -gtkStatusbarStyle->ythickness); + gtkPainter->paintResizeGrip(gtkStatusbar, "statusbar", gripRect, GTK_STATE_NORMAL, + GTK_SHADOW_OUT, option->direction == Qt::RightToLeft ? + GDK_WINDOW_EDGE_SOUTH_WEST : GDK_WINDOW_EDGE_SOUTH_EAST, + gtkStatusbarStyle); + } + break; + +#endif // QT_NO_SIZEGRIP + + case CE_MenuBarEmptyArea: { + GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); + GdkColor gdkBg = gtk_widget_get_style(gtkMenubar)->bg[GTK_STATE_NORMAL]; // Theme can depend on transparency + painter->fillRect(option->rect, QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8)); + if (widget) { // See CE_MenuBarItem + QRect menuBarRect = widget->rect(); + QPixmap pixmap(menuBarRect.size()); + pixmap.fill(Qt::transparent); + QPainter pmPainter(&pixmap); + gtkPainter->reset(&pmPainter); + GtkShadowType shadow_type; + gtk_widget_style_get(gtkMenubar, "shadow-type", &shadow_type, NULL); + gtkPainter->paintBox(gtkMenubar, "menubar", menuBarRect, + GTK_STATE_NORMAL, shadow_type, gtk_widget_get_style(gtkMenubar)); + pmPainter.end(); + painter->drawPixmap(option->rect, pixmap, option->rect); + gtkPainter->reset(painter); + } + } + break; + + case CE_MenuBarItem: + painter->save(); + + if (const QStyleOptionMenuItem *mbi = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + GtkWidget *gtkMenubarItem = d->gtkWidget("GtkMenuBar.GtkMenuItem"); + GtkWidget *gtkMenubar = d->gtkWidget("GtkMenuBar"); + + style = gtk_widget_get_style(gtkMenubarItem); + + if (widget) { + // Since Qt does not currently allow filling the entire background + // we use a hack for this by making a complete menubar each time and + // paint with the correct offset inside it. Pixmap caching should resolve + // most of the performance penalty. + QRect menuBarRect = widget->rect(); + QPixmap pixmap(menuBarRect.size()); + pixmap.fill(Qt::transparent); + QPainter pmPainter(&pixmap); + gtkPainter->reset(&pmPainter); + GtkShadowType shadow_type; + gtk_widget_style_get(gtkMenubar, "shadow-type", &shadow_type, NULL); + GdkColor gdkBg = gtk_widget_get_style(gtkMenubar)->bg[GTK_STATE_NORMAL]; // Theme can depend on transparency + painter->fillRect(option->rect, QColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8)); + gtkPainter->paintBox(gtkMenubar, "menubar", menuBarRect, + GTK_STATE_NORMAL, shadow_type, gtk_widget_get_style(gtkMenubar)); + pmPainter.end(); + painter->drawPixmap(option->rect, pixmap, option->rect); + gtkPainter->reset(painter); + } + + QStyleOptionMenuItem item = *mbi; + bool act = mbi->state & State_Selected && mbi->state & State_Sunken; + bool dis = !(mbi->state & State_Enabled); + item.rect = mbi->rect; + GdkColor gdkText = style->fg[dis ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL]; + GdkColor gdkHText = style->fg[GTK_STATE_PRELIGHT]; + QColor normalTextColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + QColor highlightedTextColor = QColor(gdkHText.red>>8, gdkHText.green>>8, gdkHText.blue>>8); + item.palette.setBrush(QPalette::HighlightedText, highlightedTextColor); + item.palette.setBrush(QPalette::Text, normalTextColor); + item.palette.setBrush(QPalette::ButtonText, normalTextColor); + QCommonStyle::drawControl(element, &item, painter, widget); + + if (act) { + GtkShadowType shadowType = GTK_SHADOW_NONE; + gtk_widget_style_get (gtkMenubarItem, "selected-shadow-type", &shadowType, NULL); + gtkPainter->paintBox(gtkMenubarItem, "menuitem", option->rect.adjusted(0, 0, 0, 3), + GTK_STATE_PRELIGHT, shadowType, style); + //draw text + QPalette::ColorRole textRole = dis ? QPalette::Text : QPalette::HighlightedText; + uint alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + + if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) + alignment |= Qt::TextHideMnemonic; + + proxy()->drawItemText(painter, item.rect, alignment, item.palette, mbi->state & State_Enabled, mbi->text, textRole); + } + } + painter->restore(); + break; + + case CE_Splitter: { + GtkWidget *gtkWindow = d->gtkWidget("GtkWindow"); // The Murrine Engine currently assumes a widget is passed + gtkPainter->paintHandle(gtkWindow, "splitter", option->rect, qt_gtk_state(option), GTK_SHADOW_NONE, + !(option->state & State_Horizontal) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL, + style); + } + break; + +#ifndef QT_NO_TOOLBAR + + case CE_ToolBar: + if (const QStyleOptionToolBar *toolbar = qstyleoption_cast<const QStyleOptionToolBar *>(option)) { + // Reserve the beveled appearance only for mainwindow toolbars + if (!(widget && qobject_cast<const QMainWindow*> (widget->parentWidget()))) + break; + + QRect rect = option->rect; + // There is a 1 pixel gap between toolbar lines in some styles (i.e Human) + if (toolbar->positionWithinLine != QStyleOptionToolBar::End) + rect.adjust(0, 0, 1, 0); + + GtkWidget *gtkToolbar = d->gtkWidget("GtkToolbar"); + GtkShadowType shadow_type = GTK_SHADOW_NONE; + gtk_widget_style_get(gtkToolbar, "shadow-type", &shadow_type, NULL); + gtkPainter->paintBox(gtkToolbar, "toolbar", rect, + GTK_STATE_NORMAL, shadow_type, gtk_widget_get_style(gtkToolbar)); + } + break; + +#endif // QT_NO_TOOLBAR + + case CE_MenuItem: + painter->save(); + + // Draws one item in a popup menu. + if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + const int windowsItemHMargin = 3; // menu item hor text margin + const int windowsItemVMargin = 26; // menu item ver text margin + GtkWidget *gtkMenuItem = menuItem->checked ? d->gtkWidget("GtkMenu.GtkCheckMenuItem") : + d->gtkWidget("GtkMenu.GtkMenuItem"); + + style = gtk_widget_get_style(gtkMenuItem); + QColor shadow = option->palette.dark().color(); + + if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { + GtkWidget *gtkMenuSeparator = d->gtkWidget("GtkMenu.GtkSeparatorMenuItem"); + painter->setPen(shadow.lighter(106)); + gboolean wide_separators = 0; + gint separator_height = 0; + guint horizontal_padding = 3; + QRect separatorRect = option->rect; + if (!gtk_check_version(2, 10, 0)) { + gtk_widget_style_get(gtkMenuSeparator, + "wide-separators", &wide_separators, + "separator-height", &separator_height, + "horizontal-padding", &horizontal_padding, + NULL); + } + GtkStyle *gtkMenuSeparatorStyle = gtk_widget_get_style(gtkMenuSeparator); + separatorRect.setHeight(option->rect.height() - 2 * gtkMenuSeparatorStyle->ythickness); + separatorRect.setWidth(option->rect.width() - 2 * (horizontal_padding + gtkMenuSeparatorStyle->xthickness)); + separatorRect.moveCenter(option->rect.center()); + if (wide_separators) + gtkPainter->paintBox(gtkMenuSeparator, "hseparator", + separatorRect, GTK_STATE_NORMAL, GTK_SHADOW_NONE, gtkMenuSeparatorStyle); + else + gtkPainter->paintHline(gtkMenuSeparator, "hseparator", + separatorRect, GTK_STATE_NORMAL, gtkMenuSeparatorStyle, + 0, option->rect.right() - 1, 1); + painter->restore(); + break; + } + + bool selected = menuItem->state & State_Selected && menuItem->state & State_Enabled; + + if (selected) { + QRect rect = option->rect; +#ifndef QT_NO_COMBOBOX + if (qobject_cast<const QComboBox*>(widget)) + rect = option->rect; +#endif + gtkPainter->paintBox(gtkMenuItem, "menuitem", rect, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, style); + } + + bool checkable = menuItem->checkType != QStyleOptionMenuItem::NotCheckable; + bool checked = menuItem->checked; + bool enabled = menuItem->state & State_Enabled; + bool ignoreCheckMark = false; + + gint checkSize; + gtk_widget_style_get(d->gtkWidget("GtkMenu.GtkCheckMenuItem"), "indicator-size", &checkSize, NULL); + + int checkcol = qMax(menuItem->maxIconWidth, qMax(20, checkSize)); + +#ifndef QT_NO_COMBOBOX + + if (qobject_cast<const QComboBox*>(widget) || + (option->styleObject && option->styleObject->property("_q_isComboBoxPopupItem").toBool())) + ignoreCheckMark = true; // Ignore the checkmarks provided by the QComboMenuDelegate + +#endif + if (!ignoreCheckMark) { + // Check + QRect checkRect(option->rect.left() + 7, option->rect.center().y() - checkSize/2 + 1, checkSize, checkSize); + checkRect = visualRect(menuItem->direction, menuItem->rect, checkRect); + + if (checkable && menuItem->icon.isNull()) { + // Some themes such as aero-clone draw slightly outside the paint rect + int spacing = 1; // ### Consider using gtkCheckBox : "indicator-spacing" instead + + if (menuItem->checkType & QStyleOptionMenuItem::Exclusive) { + // Radio button + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = qt_gtk_state(option); + + if (selected) + state = GTK_STATE_PRELIGHT; + if (checked) + shadow = GTK_SHADOW_IN; + + gtkPainter->setClipRect(checkRect.adjusted(-spacing, -spacing, spacing, spacing)); + gtkPainter->paintOption(gtkMenuItem, checkRect.translated(-spacing, -spacing), state, shadow, + style, QLS("option")); + gtkPainter->setClipRect(QRect()); + + } else { + // Check box + if (menuItem->icon.isNull()) { + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = qt_gtk_state(option); + + if (selected) + state = GTK_STATE_PRELIGHT; + if (checked) + shadow = GTK_SHADOW_IN; + + gtkPainter->setClipRect(checkRect.adjusted(-spacing, -spacing, -spacing, -spacing)); + gtkPainter->paintCheckbox(gtkMenuItem, checkRect.translated(-spacing, -spacing), state, shadow, + style, QLS("check")); + gtkPainter->setClipRect(QRect()); + } + } + } + + } else { + // Ignore checkmark + if (menuItem->icon.isNull()) + checkcol = 0; + else + checkcol = menuItem->maxIconWidth; + } + + bool dis = !(menuItem->state & State_Enabled); + bool act = menuItem->state & State_Selected; + const QStyleOption *opt = option; + const QStyleOptionMenuItem *menuitem = menuItem; + QPainter *p = painter; + QRect vCheckRect = visualRect(opt->direction, menuitem->rect, + QRect(menuitem->rect.x() + 3, menuitem->rect.y(), + checkcol, menuitem->rect.height())); + + if (!menuItem->icon.isNull()) { + QIcon::Mode mode = dis ? QIcon::Disabled : QIcon::Normal; + + if (act && !dis) + mode = QIcon::Active; + + QPixmap pixmap; + int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize, option, widget); + QSize iconSize(smallIconSize, smallIconSize); + +#ifndef QT_NO_COMBOBOX + if (const QComboBox *combo = qobject_cast<const QComboBox*>(widget)) + iconSize = combo->iconSize(); + +#endif // QT_NO_COMBOBOX + if (checked) + pixmap = menuItem->icon.pixmap(iconSize, mode, QIcon::On); + else + pixmap = menuItem->icon.pixmap(iconSize, mode); + + const int pixw = pixmap.width() / pixmap.devicePixelRatio(); + const int pixh = pixmap.height() / pixmap.devicePixelRatio(); + QRect pmr(0, 0, pixw, pixh); + pmr.moveCenter(vCheckRect.center() - QPoint(0, 1)); + painter->setPen(menuItem->palette.text().color()); + if (!ignoreCheckMark && checkable && checked) { + QStyleOption opt = *option; + + if (act) { + QColor activeColor = mergedColors(option->palette.window().color(), + option->palette.highlight().color()); + opt.palette.setBrush(QPalette::Button, activeColor); + } + opt.state |= State_Sunken; + opt.rect = vCheckRect; + proxy()->drawPrimitive(PE_PanelButtonCommand, &opt, painter, widget); + } + painter->drawPixmap(pmr.topLeft(), pixmap); + } + + GdkColor gdkText = style->fg[GTK_STATE_NORMAL]; + GdkColor gdkDText = style->fg[GTK_STATE_INSENSITIVE]; + GdkColor gdkHText = style->fg[GTK_STATE_PRELIGHT]; + uint resolve_mask = option->palette.resolve(); + QColor textColor = QColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + QColor disabledTextColor = QColor(gdkDText.red>>8, gdkDText.green>>8, gdkDText.blue>>8); + if (resolve_mask & (1 << QPalette::ButtonText)) { + textColor = option->palette.buttonText().color(); + disabledTextColor = option->palette.brush(QPalette::Disabled, QPalette::ButtonText).color(); + } + + QColor highlightedTextColor = QColor(gdkHText.red>>8, gdkHText.green>>8, gdkHText.blue>>8); + if (resolve_mask & (1 << QPalette::HighlightedText)) { + highlightedTextColor = option->palette.highlightedText().color(); + } + + if (selected) + painter->setPen(highlightedTextColor); + else + painter->setPen(textColor); + + int x, y, w, h; + menuitem->rect.getRect(&x, &y, &w, &h); + int tab = menuitem->tabWidth; + int xm = QGtkStylePrivate::menuItemFrame + checkcol + windowsItemHMargin; + int xpos = menuitem->rect.x() + xm + 1; + QRect textRect(xpos, y + windowsItemVMargin, w - xm - QGtkStylePrivate::menuRightBorder - tab + 1, h - 2 * windowsItemVMargin); + QRect vTextRect = visualRect(opt->direction, menuitem->rect, textRect); + QString s = menuitem->text; + + if (!s.isEmpty()) { // Draw text + p->save(); + int t = s.indexOf(QLatin1Char('\t')); + int text_flags = Qt::AlignVCenter | Qt::TextShowMnemonic | Qt::TextDontClip | Qt::TextSingleLine; + + if (!proxy()->styleHint(SH_UnderlineShortcut, menuitem, widget)) + text_flags |= Qt::TextHideMnemonic; + + // Draw shortcut right aligned + text_flags |= Qt::AlignRight; + + if (t >= 0) { + int rightMargin = 12; // Hardcode for now + QRect vShortcutRect = visualRect(opt->direction, menuitem->rect, + QRect(textRect.topRight(), QPoint(menuitem->rect.right() - rightMargin, textRect.bottom()))); + + if (dis) + p->setPen(disabledTextColor); + p->drawText(vShortcutRect, text_flags , s.mid(t + 1)); + s = s.left(t); + } + + text_flags &= ~Qt::AlignRight; + text_flags |= Qt::AlignLeft; + QFont font = menuitem->font; + if (menuitem->menuItemType == QStyleOptionMenuItem::DefaultItem) + font.setBold(true); + p->setFont(font); + + if (dis) + p->setPen(disabledTextColor); + p->drawText(vTextRect, text_flags, s.left(t)); + p->restore(); + } + + // Arrow + if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) {// draw sub menu arrow + + QFontMetrics fm(menuitem->font); + int arrow_size = fm.ascent() + fm.descent() - 2 * style->ythickness; + gfloat arrow_scaling = 0.8; + int extra = 0; + if (!gtk_check_version(2, 16, 0)) { + // "arrow-scaling" is actually hardcoded and fails on hardy (see gtk+-2.12/gtkmenuitem.c) + // though the current documentation states otherwise + gtk_widget_style_get(gtkMenuItem, "arrow-scaling", &arrow_scaling, NULL); + // in versions < 2.16 ythickness was previously subtracted from the arrow_size + extra = 2 * style->ythickness; + } + + int horizontal_padding; + gtk_widget_style_get(gtkMenuItem, "horizontal-padding", &horizontal_padding, NULL); + + const int dim = static_cast<int>(arrow_size * arrow_scaling) + extra; + int xpos = menuItem->rect.left() + menuItem->rect.width() - horizontal_padding - dim; + QRect vSubMenuRect = visualRect(option->direction, menuItem->rect, + QRect(xpos, menuItem->rect.top() + + menuItem->rect.height() / 2 - dim / 2, dim, dim)); + GtkStateType state = enabled ? (act ? GTK_STATE_PRELIGHT: GTK_STATE_NORMAL) : GTK_STATE_INSENSITIVE; + GtkShadowType shadowType = (state == GTK_STATE_PRELIGHT) ? GTK_SHADOW_OUT : GTK_SHADOW_IN; + gtkPainter->paintArrow(gtkMenuItem, "menuitem", vSubMenuRect, option->direction == Qt::RightToLeft ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT, state, + shadowType, false, style); + } + } + painter->restore(); + break; + + case CE_PushButton: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + proxy()->drawControl(CE_PushButtonBevel, btn, painter, widget); + QStyleOptionButton subopt = *btn; + subopt.rect = subElementRect(SE_PushButtonContents, btn, widget); + gint interiorFocus = true; + gtk_widget_style_get(gtkButton, "interior-focus", &interiorFocus, NULL); + GtkStyle *gtkButtonStyle = gtk_widget_get_style(gtkButton); + int xt = interiorFocus ? gtkButtonStyle->xthickness : 0; + int yt = interiorFocus ? gtkButtonStyle->ythickness : 0; + + if (btn->features & QStyleOptionButton::Flat && btn->state & State_HasFocus) + // The normal button focus rect does not work well for flat buttons in Clearlooks + proxy()->drawPrimitive(PE_FrameFocusRect, option, painter, widget); + else if (btn->state & State_HasFocus) + gtkPainter->paintFocus(gtkButton, "button", + option->rect.adjusted(xt, yt, -xt, -yt), + btn->state & State_Sunken ? GTK_STATE_ACTIVE : GTK_STATE_NORMAL, + gtkButtonStyle); + + proxy()->drawControl(CE_PushButtonLabel, &subopt, painter, widget); + } + break; + +#ifndef QT_NO_TABBAR + + case CE_TabBarTabShape: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { + GtkWidget *gtkNotebook = d->gtkWidget("GtkNotebook"); + style = gtk_widget_get_style(gtkNotebook); + + QRect rect = option->rect; + GtkShadowType shadow = GTK_SHADOW_OUT; + GtkStateType state = GTK_STATE_ACTIVE; + if (tab->state & State_Selected) + state = GTK_STATE_NORMAL; + + bool selected = (tab->state & State_Selected); + bool first = false, last = false; + if (widget) { + // This is most accurate and avoids resizing tabs while moving + first = tab->rect.left() == widget->rect().left(); + last = tab->rect.right() == widget->rect().right(); + } else if (option->direction == Qt::RightToLeft) { + bool tmp = first; + first = last; + last = tmp; + } + int topIndent = 3; + int bottomIndent = 1; + int tabOverlap = 1; + painter->save(); + + switch (tab->shape) { + case QTabBar::RoundedNorth: + if (!selected) + rect.adjust(first ? 0 : -tabOverlap, topIndent, last ? 0 : tabOverlap, -bottomIndent); + gtkPainter->paintExtention(gtkNotebook, "tab", rect, + state, shadow, GTK_POS_BOTTOM, style); + break; + + case QTabBar::RoundedSouth: + if (!selected) + rect.adjust(first ? 0 : -tabOverlap, 0, last ? 0 : tabOverlap, -topIndent); + gtkPainter->paintExtention(gtkNotebook, "tab", rect.adjusted(0, 1, 0, 0), + state, shadow, GTK_POS_TOP, style); + break; + + case QTabBar::RoundedWest: + if (!selected) + rect.adjust(topIndent, 0, -bottomIndent, 0); + gtkPainter->paintExtention(gtkNotebook, "tab", rect, state, shadow, GTK_POS_RIGHT, style); + break; + + case QTabBar::RoundedEast: + if (!selected) + rect.adjust(bottomIndent, 0, -topIndent, 0); + gtkPainter->paintExtention(gtkNotebook, "tab", rect, state, shadow, GTK_POS_LEFT, style); + break; + + default: + QCommonStyle::drawControl(element, option, painter, widget); + break; + } + + painter->restore(); + } + + break; + +#endif //QT_NO_TABBAR + + case CE_ProgressBarGroove: + if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + Q_UNUSED(bar); + GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar"); + GtkStateType state = qt_gtk_state(option); + gtkPainter->paintBox(gtkProgressBar, "trough", option->rect, state, GTK_SHADOW_IN, gtk_widget_get_style(gtkProgressBar)); + } + + break; + + case CE_ProgressBarContents: + if (const QStyleOptionProgressBar *bar = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + GtkStateType state = option->state & State_Enabled ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE; + GtkWidget *gtkProgressBar = d->gtkWidget("GtkProgressBar"); + style = gtk_widget_get_style(gtkProgressBar); + gtkPainter->paintBox(gtkProgressBar, "trough", option->rect, state, GTK_SHADOW_IN, style); + int xt = style->xthickness; + int yt = style->ythickness; + QRect rect = bar->rect.adjusted(xt, yt, -xt, -yt); + bool vertical = false; + bool inverted = false; + bool indeterminate = (bar->minimum == 0 && bar->maximum == 0); + // Get extra style options if version 2 + + if (const QStyleOptionProgressBar *bar2 = qstyleoption_cast<const QStyleOptionProgressBar *>(option)) { + vertical = (bar2->orientation == Qt::Vertical); + inverted = bar2->invertedAppearance; + } + + // If the orientation is vertical, we use a transform to rotate + // the progress bar 90 degrees clockwise. This way we can use the + // same rendering code for both orientations. + if (vertical) { + rect.translate(xt, -yt * 2); + rect = QRect(rect.left(), rect.top(), rect.height(), rect.width()); // Flip width and height + QTransform m = QTransform::fromTranslate(rect.height(), 0); + m.rotate(90.0); + painter->setTransform(m); + } + + int maxWidth = rect.width(); + int minWidth = 4; + + qint64 progress = (qint64)qMax(bar->progress, bar->minimum); // Workaround for bug in QProgressBar + double vc6_workaround = ((progress - qint64(bar->minimum)) / double(qint64(bar->maximum) - qint64(bar->minimum))) * maxWidth; + int progressBarWidth = (int(vc6_workaround) > minWidth ) ? int(vc6_workaround) : minWidth; + int width = indeterminate ? maxWidth : progressBarWidth; + bool reverse = (!vertical && (bar->direction == Qt::RightToLeft)) || vertical; + + if (inverted) + reverse = !reverse; + + int maximum = 2; + int fakePos = 0; + if (bar->minimum == bar->maximum) + maximum = 0; + if (bar->progress == bar->maximum) + fakePos = maximum; + else if (bar->progress > bar->minimum) + fakePos = maximum - 1; + + QRect progressBar; + + if (!indeterminate) { + if (!reverse) + progressBar.setRect(rect.left(), rect.top(), width, rect.height()); + else + progressBar.setRect(rect.right() - width, rect.top(), width, rect.height()); +#ifndef QT_NO_ANIMATION + d->stopAnimation(option->styleObject); +#endif + } else { +#ifndef QT_NO_ANIMATION + Q_D(const QGtkStyle); +#endif + int slideWidth = ((rect.width() - 4) * 2) / 3; + int step = 0; +#ifndef QT_NO_ANIMATION + if (QProgressStyleAnimation *animation = qobject_cast<QProgressStyleAnimation*>(d->animation(option->styleObject))) + step = animation->progressStep(slideWidth); + else + d->startAnimation(new QProgressStyleAnimation(d->animationFps, option->styleObject)); +#endif + progressBar.setRect(rect.left() + step, rect.top(), slideWidth / 2, rect.height()); + } + + QString key = QString(QLS("%0")).arg(fakePos); + if (inverted) { + key += QLatin1String("inv"); + gtkPainter->setFlipHorizontal(true); + } + gtkPainter->paintBox(gtkProgressBar, "bar", progressBar, GTK_STATE_SELECTED, GTK_SHADOW_OUT, style, key); + } + + break; + + default: + QCommonStyle::drawControl(element, option, painter, widget); + } +} + +/*! + \reimp +*/ +QRect QGtkStyle::subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + QRect rect = QCommonStyle::subControlRect(control, option, subControl, widget); + if (!d->isThemeAvailable()) + return QCommonStyle::subControlRect(control, option, subControl, widget); + + switch (control) { + case CC_ScrollBar: + break; + case CC_Slider: + if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(option)) { + int tickSize = proxy()->pixelMetric(PM_SliderTickmarkOffset, option, widget); + switch (subControl) { + case SC_SliderHandle: { + if (slider->orientation == Qt::Horizontal) { + rect.setHeight(proxy()->pixelMetric(PM_SliderThickness)); + rect.setWidth(proxy()->pixelMetric(PM_SliderLength)); + int centerY = slider->rect.center().y() - rect.height() / 2; + if (slider->tickPosition & QSlider::TicksAbove) + centerY += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + centerY -= tickSize; + rect.moveTop(centerY); + } else { + rect.setWidth(proxy()->pixelMetric(PM_SliderThickness)); + rect.setHeight(proxy()->pixelMetric(PM_SliderLength)); + int centerX = slider->rect.center().x() - rect.width() / 2; + if (slider->tickPosition & QSlider::TicksAbove) + centerX += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + centerX -= tickSize; + rect.moveLeft(centerX); + } + } + break; + case SC_SliderGroove: { + QPoint grooveCenter = slider->rect.center(); + if (slider->orientation == Qt::Horizontal) { + rect.setHeight(7); + if (slider->tickPosition & QSlider::TicksAbove) + grooveCenter.ry() += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + grooveCenter.ry() -= tickSize; + } else { + rect.setWidth(7); + if (slider->tickPosition & QSlider::TicksAbove) + grooveCenter.rx() += tickSize; + if (slider->tickPosition & QSlider::TicksBelow) + grooveCenter.rx() -= tickSize; + } + rect.moveCenter(grooveCenter); + break; + } + default: + break; + } + } + break; + +#ifndef QT_NO_GROUPBOX + + case CC_GroupBox: + if (const QStyleOptionGroupBox * groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(option)) { + rect = option->rect.adjusted(0, groupBoxTopMargin, 0, -groupBoxBottomMargin); + int topMargin = 0; + int topHeight = 0; + topHeight = 10; + QRect frameRect = rect; + frameRect.setTop(topMargin); + + if (subControl == SC_GroupBoxFrame) + return rect; + else if (subControl == SC_GroupBoxContents) { + int margin = 0; + int leftMarginExtension = 8; + return frameRect.adjusted(leftMarginExtension + margin, margin + topHeight + groupBoxTitleMargin, -margin, -margin); + } + + QFontMetrics fontMetrics = option->fontMetrics; + if (qobject_cast<const QGroupBox *>(widget)) { + //Prepare metrics for a bold font + QFont font = widget->font(); + font.setBold(true); + fontMetrics = QFontMetrics(font); +#ifndef QT_NO_ACCESSIBILITY + } else if (QStyleHelper::isInstanceOf(groupBox->styleObject, QAccessible::Grouping)) { + QVariant var = groupBox->styleObject->property("font"); + if (var.isValid() && var.canConvert<QFont>()) { + QFont font = var.value<QFont>(); + font.setBold(true); + fontMetrics = QFontMetrics(font); + } +#endif // QT_NO_ACCESSIBILITY + } + + QSize textRect = fontMetrics.boundingRect(groupBox->text).size() + QSize(4, 4); + int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, option, widget); + int indicatorHeight = proxy()->pixelMetric(PM_IndicatorHeight, option, widget); + + if (subControl == SC_GroupBoxCheckBox) { + rect.setWidth(indicatorWidth); + rect.setHeight(indicatorHeight); + rect.moveTop((textRect.height() - indicatorHeight) / 2); + + } else if (subControl == SC_GroupBoxLabel) { + if (groupBox->subControls & SC_GroupBoxCheckBox) + rect.adjust(indicatorWidth + 4, 0, 0, 0); + rect.setSize(textRect); + } + rect = visualRect(option->direction, option->rect, rect); + } + + return rect; + +#endif +#ifndef QT_NO_SPINBOX + + case CC_SpinBox: + if (const QStyleOptionSpinBox *spinbox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + GtkWidget *gtkSpinButton = d->gtkWidget("GtkSpinButton"); + int center = spinbox->rect.height() / 2; + GtkStyle *gtkSpinButtonStyle = gtk_widget_get_style(gtkSpinButton); + int xt = spinbox->frame ? gtkSpinButtonStyle->xthickness : 0; + int yt = spinbox->frame ? gtkSpinButtonStyle->ythickness : 0; + int y = yt; + + QSize bs; + bs.setHeight(qMax(8, spinbox->rect.height()/2 - y)); + bs.setWidth(d->getSpinboxArrowSize()); + int x, lx, rx; + x = spinbox->rect.width() - y - bs.width() + 2; + lx = xt; + rx = x - xt; + + switch (subControl) { + + case SC_SpinBoxUp: + if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) + return QRect(); + rect = QRect(x, xt, bs.width(), center - yt); + break; + + case SC_SpinBoxDown: + if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) + return QRect(); + rect = QRect(x, center, bs.width(), spinbox->rect.bottom() - center - yt + 1); + break; + + case SC_SpinBoxEditField: + if (spinbox->buttonSymbols == QAbstractSpinBox::NoButtons) + rect = QRect(lx, yt, spinbox->rect.width() - 2*xt, spinbox->rect.height() - 2*yt); + else + rect = QRect(lx, yt, rx - qMax(xt - 1, 0), spinbox->rect.height() - 2*yt); + break; + + case SC_SpinBoxFrame: + rect = spinbox->rect; + + default: + break; + } + + rect = visualRect(spinbox->direction, spinbox->rect, rect); + } + + break; + +#endif // Qt_NO_SPINBOX +#ifndef QT_NO_COMBOBOX + + case CC_TitleBar: + if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(option)) { + SubControl sc = subControl; + QRect &ret = rect; + const int indent = 3; + const int controlTopMargin = 3; + const int controlBottomMargin = 3; + const int controlWidthMargin = 2; + const int controlHeight = tb->rect.height() - controlTopMargin - controlBottomMargin ; + const int delta = controlHeight + controlWidthMargin; + int offset = 0; + + bool isMinimized = tb->titleBarState & Qt::WindowMinimized; + bool isMaximized = tb->titleBarState & Qt::WindowMaximized; + + switch (sc) { + case SC_TitleBarLabel: + if (tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)) { + ret = tb->rect; + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) + ret.adjust(delta, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowMinimizeButtonHint) + ret.adjust(0, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowMaximizeButtonHint) + ret.adjust(0, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowShadeButtonHint) + ret.adjust(0, 0, -delta, 0); + if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) + ret.adjust(0, 0, -delta, 0); + } + break; + case SC_TitleBarContextHelpButton: + if (tb->titleBarFlags & Qt::WindowContextHelpButtonHint) + offset += delta; + Q_FALLTHROUGH(); + case SC_TitleBarMinButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarMinButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarNormalButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowMinimizeButtonHint)) + offset += delta; + else if (isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarNormalButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarMaxButton: + if (!isMaximized && (tb->titleBarFlags & Qt::WindowMaximizeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarMaxButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarShadeButton: + if (!isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarShadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarUnshadeButton: + if (isMinimized && (tb->titleBarFlags & Qt::WindowShadeButtonHint)) + offset += delta; + else if (sc == SC_TitleBarUnshadeButton) + break; + Q_FALLTHROUGH(); + case SC_TitleBarCloseButton: + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) + offset += delta; + else if (sc == SC_TitleBarCloseButton) + break; + ret.setRect(tb->rect.right() - indent - offset, tb->rect.top() + controlTopMargin, + controlHeight, controlHeight); + break; + case SC_TitleBarSysMenu: + if (tb->titleBarFlags & Qt::WindowSystemMenuHint) { + ret.setRect(tb->rect.left() + controlWidthMargin + indent, tb->rect.top() + controlTopMargin, + controlHeight, controlHeight); + } + break; + default: + break; + } + ret = visualRect(tb->direction, tb->rect, ret); + } + break; + case CC_ComboBox: + if (const QStyleOptionComboBox *box = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + // We employ the gtk widget to position arrows and separators for us + GtkWidget *gtkCombo = box->editable ? d->gtkWidget("GtkComboBoxEntry") + : d->gtkWidget("GtkComboBox"); + gtk_widget_set_direction(gtkCombo, (option->direction == Qt::RightToLeft) ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR); + GtkAllocation geometry = {0, 0, qMax(0, option->rect.width()), qMax(0, option->rect.height())}; + gtk_widget_size_allocate(gtkCombo, &geometry); + int appears_as_list = !proxy()->styleHint(QStyle::SH_ComboBox_Popup, option, widget); + QHashableLatin1Literal arrowPath("GtkComboBoxEntry.GtkToggleButton"); + if (!box->editable) { + if (appears_as_list) + arrowPath = "GtkComboBox.GtkToggleButton"; + else + arrowPath = "GtkComboBox.GtkToggleButton.GtkHBox.GtkArrow"; + } + + GtkWidget *arrowWidget = d->gtkWidget(arrowPath); + if (!arrowWidget) + return QCommonStyle::subControlRect(control, option, subControl, widget); + + GtkAllocation allocation; + gtk_widget_get_allocation(arrowWidget, &allocation); + QRect buttonRect(option->rect.left() + allocation.x, + option->rect.top() + allocation.y, + allocation.width, allocation.height); + + switch (subControl) { + + case SC_ComboBoxArrow: // Note: this indicates the arrowbutton for editable combos + rect = buttonRect; + break; + + case SC_ComboBoxEditField: { + rect = visualRect(option->direction, option->rect, rect); + int xMargin = box->editable ? 1 : 4, yMargin = 2; + GtkStyle *gtkComboStyle = gtk_widget_get_style(gtkCombo); + rect.setRect(option->rect.left() + gtkComboStyle->xthickness + xMargin, + option->rect.top() + gtkComboStyle->ythickness + yMargin, + option->rect.width() - buttonRect.width() - 2*(gtkComboStyle->xthickness + xMargin), + option->rect.height() - 2*(gtkComboStyle->ythickness + yMargin)); + rect = visualRect(option->direction, option->rect, rect); + break; + } + + default: + break; + } + } + + break; +#endif // QT_NO_COMBOBOX + + default: + break; + } + + return rect; +} + +/*! + \reimp +*/ +QSize QGtkStyle::sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + QSize newSize = QCommonStyle::sizeFromContents(type, option, size, widget); + if (!d->isThemeAvailable()) + return newSize; + + switch (type) { + case CT_GroupBox: + // Since we use a bold font we have to recalculate base width + if (const QGroupBox *gb = qobject_cast<const QGroupBox*>(widget)) { + QFont font = gb->font(); + font.setBold(true); + QFontMetrics metrics(font); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + int baseWidth = metrics.horizontalAdvance(gb->title()) + metrics.horizontalAdvance(QLatin1Char(' ')); +#else + int baseWidth = metrics.width(gb->title()) + metrics.width(QLatin1Char(' ')); +#endif + if (gb->isCheckable()) { + baseWidth += proxy()->pixelMetric(QStyle::PM_IndicatorWidth, option, widget); + baseWidth += proxy()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, option, widget); + } + newSize.setWidth(qMax(baseWidth, newSize.width())); + } + newSize += QSize(4, 1 + groupBoxBottomMargin + groupBoxTopMargin + groupBoxTitleMargin); // Add some space below the groupbox + break; + case CT_ToolButton: + if (const QStyleOptionToolButton *toolbutton = qstyleoption_cast<const QStyleOptionToolButton *>(option)) { + GtkWidget *gtkButton = d->gtkWidget("GtkToolButton.GtkButton"); + GtkStyle *gtkButtonStyle = gtk_widget_get_style(gtkButton); + newSize = size + QSize(2 * gtkButtonStyle->xthickness, 2 + 2 * gtkButtonStyle->ythickness); + if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) { + QSize minSize(0, 25); + if (toolbutton->toolButtonStyle != Qt::ToolButtonTextOnly) + minSize = toolbutton->iconSize + QSize(12, 12); + newSize = newSize.expandedTo(minSize); + } + + if (toolbutton->features & QStyleOptionToolButton::HasMenu) + newSize += QSize(6, 0); + } + break; + case CT_SpinBox: + // QSpinBox does some nasty things that depends on CT_LineEdit + newSize = newSize + QSize(0, -gtk_widget_get_style(d->gtkWidget("GtkSpinButton"))->ythickness * 2); + break; + case CT_RadioButton: + case CT_CheckBox: + newSize += QSize(0, 1); + break; + case CT_PushButton: + if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(option)) { + if (!btn->icon.isNull() && btn->iconSize.height() > 16) + newSize -= QSize(0, 2); // From cleanlooksstyle + newSize += QSize(0, 1); + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + gint focusPadding, focusWidth; + gtk_widget_style_get(gtkButton, "focus-padding", &focusPadding, NULL); + gtk_widget_style_get(gtkButton, "focus-line-width", &focusWidth, NULL); + newSize = size; + GtkStyle *gtkButtonStyle = gtk_widget_get_style(gtkButton); + newSize += QSize(2*gtkButtonStyle->xthickness + 4, 2*gtkButtonStyle->ythickness); + newSize += QSize(2*(focusWidth + focusPadding + 2), 2*(focusWidth + focusPadding)); + + GtkWidget *gtkButtonBox = d->gtkWidget("GtkHButtonBox"); + gint minWidth = 85, minHeight = 0; + gtk_widget_style_get(gtkButtonBox, "child-min-width", &minWidth, + "child-min-height", &minHeight, NULL); + if (!btn->text.isEmpty() && newSize.width() < minWidth) + newSize.setWidth(minWidth); + if (newSize.height() < minHeight) + newSize.setHeight(minHeight); + } + break; + case CT_Slider: { + GtkWidget *gtkSlider = d->gtkWidget("GtkHScale"); + GtkStyle *gtkSliderStyle = gtk_widget_get_style(gtkSlider); + newSize = size + QSize(2*gtkSliderStyle->xthickness, 2*gtkSliderStyle->ythickness); } + break; + case CT_LineEdit: { + GtkWidget *gtkEntry = d->gtkWidget("GtkEntry"); + GtkStyle *gtkEntryStyle = gtk_widget_get_style(gtkEntry); + newSize = size + QSize(2*gtkEntryStyle->xthickness, 2 + 2*gtkEntryStyle->ythickness); } + break; + case CT_ItemViewItem: + newSize += QSize(0, 2); + break; + case CT_ComboBox: + if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { + GtkWidget *gtkCombo = d->gtkWidget("GtkComboBox"); + QRect arrowButtonRect = proxy()->subControlRect(CC_ComboBox, combo, SC_ComboBoxArrow, widget); + GtkStyle *gtkComboStyle = gtk_widget_get_style(gtkCombo); + newSize = size + QSize(12 + arrowButtonRect.width() + 2*gtkComboStyle->xthickness, 4 + 2*gtkComboStyle->ythickness); + + if (!(widget && qobject_cast<QToolBar *>(widget->parentWidget()))) + newSize += QSize(0, 2); + } + break; + case CT_TabBarTab: + if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(option)) { + if (!tab->icon.isNull()) + newSize += QSize(6, 0); + } + newSize += QSize(1, 1); + break; + case CT_MenuBarItem: + newSize += QSize(QGtkStylePrivate::menuItemHMargin * 4, QGtkStylePrivate::menuItemVMargin * 2 + 2); + break; + case CT_SizeGrip: + newSize += QSize(4, 4); + break; + case CT_MdiControls: + if (const QStyleOptionComplex *styleOpt = qstyleoption_cast<const QStyleOptionComplex *>(option)) { + int width = 0; + if (styleOpt->subControls & SC_MdiMinButton) + width += 19 + 1; + if (styleOpt->subControls & SC_MdiNormalButton) + width += 19 + 1; + if (styleOpt->subControls & SC_MdiCloseButton) + width += 19 + 1; + newSize = QSize(width, 19); + } else { + newSize = QSize(60, 19); + } + break; + case CT_MenuItem: + if (const QStyleOptionMenuItem *menuItem = qstyleoption_cast<const QStyleOptionMenuItem *>(option)) { + int w = newSize.width(); + int maxpmw = menuItem->maxIconWidth; + int tabSpacing = 20; + if (menuItem->text.contains(QLatin1Char('\t'))) + w += tabSpacing; + else if (menuItem->menuItemType == QStyleOptionMenuItem::SubMenu) + w += 2 * QGtkStylePrivate::menuArrowHMargin; + else if (menuItem->menuItemType == QStyleOptionMenuItem::DefaultItem) { + // adjust the font and add the difference in size. + // it would be better if the font could be adjusted in the initStyleOption qmenu func!! + QFontMetrics fm(menuItem->font); + QFont fontBold = menuItem->font; + fontBold.setBold(true); + QFontMetrics fmBold(fontBold); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + w += fmBold.horizontalAdvance(menuItem->text) - fm.horizontalAdvance(menuItem->text); +#else + w += fmBold.width(menuItem->text) - fm.width(menuItem->text); +#endif + } + + int checkcol = qMax<int>(maxpmw, QGtkStylePrivate::menuCheckMarkWidth); // Windows always shows a check column + w += checkcol; + w += int(QGtkStylePrivate::menuRightBorder) + 10; + + newSize.setWidth(w); + + int textMargin = 8; + if (menuItem->menuItemType == QStyleOptionMenuItem::Separator) { + GtkWidget *gtkMenuSeparator = d->gtkWidget("GtkMenu.GtkSeparatorMenuItem"); + GtkRequisition sizeReq = {0, 0}; + gtk_widget_size_request(gtkMenuSeparator, &sizeReq); + newSize = QSize(newSize.width(), sizeReq.height); + break; + } + + GtkWidget *gtkMenuItem = d->gtkWidget("GtkMenu.GtkCheckMenuItem"); + GtkStyle* style = gtk_widget_get_style(gtkMenuItem); + + // Note we get the perfect height for the default font since we + // set a fake text label on the gtkMenuItem + // But if custom fonts are used on the widget we need a minimum size + GtkRequisition sizeReq = {0, 0}; + gtk_widget_size_request(gtkMenuItem, &sizeReq); + newSize.setHeight(qMax(newSize.height() - 4, sizeReq.height)); + newSize += QSize(textMargin + style->xthickness - 1, 0); + + gint checkSize; + gtk_widget_style_get(gtkMenuItem, "indicator-size", &checkSize, NULL); + newSize.setWidth(newSize.width() + qMax(0, checkSize - 20)); + } + break; + default: + break; + } + + return newSize; +} + + +/*! \reimp */ +QPixmap QGtkStyle::standardPixmap(StandardPixmap sp, const QStyleOption *option, + const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) + return QCommonStyle::standardPixmap(sp, option, widget); + + QPixmap pixmap; + switch (sp) { + + case SP_TitleBarNormalButton: { + QImage restoreButton(dock_widget_restore_xpm); + QColor alphaCorner = restoreButton.color(2); + alphaCorner.setAlpha(80); + restoreButton.setColor(2, alphaCorner.rgba()); + alphaCorner.setAlpha(180); + restoreButton.setColor(4, alphaCorner.rgba()); + return QPixmap::fromImage(restoreButton); + } + break; + + case SP_TitleBarCloseButton: // Fall through + case SP_DockWidgetCloseButton: { + + QImage closeButton(dock_widget_close_xpm); + QColor alphaCorner = closeButton.color(2); + alphaCorner.setAlpha(80); + closeButton.setColor(2, alphaCorner.rgba()); + return QPixmap::fromImage(closeButton); + } + break; + + case SP_DialogDiscardButton: + return qt_gtk_get_icon(GTK_STOCK_DELETE); + case SP_DialogOkButton: + return qt_gtk_get_icon(GTK_STOCK_OK); + case SP_DialogCancelButton: + return qt_gtk_get_icon(GTK_STOCK_CANCEL); + case SP_DialogYesButton: + return qt_gtk_get_icon(GTK_STOCK_YES); + case SP_DialogNoButton: + return qt_gtk_get_icon(GTK_STOCK_NO); + case SP_DialogOpenButton: + return qt_gtk_get_icon(GTK_STOCK_OPEN); + case SP_DialogCloseButton: + return qt_gtk_get_icon(GTK_STOCK_CLOSE); + case SP_DialogApplyButton: + return qt_gtk_get_icon(GTK_STOCK_APPLY); + case SP_DialogSaveButton: + return qt_gtk_get_icon(GTK_STOCK_SAVE); + case SP_MessageBoxWarning: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + case SP_MessageBoxQuestion: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); + case SP_MessageBoxInformation: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); + case SP_MessageBoxCritical: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); + default: + return QCommonStyle::standardPixmap(sp, option, widget); + } + return pixmap; +} + +/*! + \reimp +*/ +QIcon QGtkStyle::standardIcon(StandardPixmap standardIcon, + const QStyleOption *option, + const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + if (!d->isThemeAvailable()) + return QCommonStyle::standardIcon(standardIcon, option, widget); + switch (standardIcon) { +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + case SP_TitleBarNormalButton: + case SP_TitleBarCloseButton: + case SP_DockWidgetCloseButton: + return QIcon(QGtkStyle::standardPixmap(standardIcon, option, widget)); +#endif + case SP_DialogDiscardButton: + return qt_gtk_get_icon(GTK_STOCK_DELETE); + case SP_DialogOkButton: + return qt_gtk_get_icon(GTK_STOCK_OK); + case SP_DialogCancelButton: + return qt_gtk_get_icon(GTK_STOCK_CANCEL); + case SP_DialogYesButton: + return qt_gtk_get_icon(GTK_STOCK_YES); + case SP_DialogNoButton: + return qt_gtk_get_icon(GTK_STOCK_NO); + case SP_DialogOpenButton: + return qt_gtk_get_icon(GTK_STOCK_OPEN); + case SP_DialogCloseButton: + return qt_gtk_get_icon(GTK_STOCK_CLOSE); + case SP_DialogApplyButton: + return qt_gtk_get_icon(GTK_STOCK_APPLY); + case SP_DialogSaveButton: + return qt_gtk_get_icon(GTK_STOCK_SAVE); + case SP_MessageBoxWarning: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG); + case SP_MessageBoxQuestion: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG); + case SP_MessageBoxInformation: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG); + case SP_MessageBoxCritical: + return qt_gtk_get_icon(GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG); + default: + return QCommonStyle::standardIcon(standardIcon, option, widget); + } +} + + +/*! \reimp */ +QRect QGtkStyle::subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const +{ + Q_D(const QGtkStyle); + + QRect r = QCommonStyle::subElementRect(element, option, widget); + if (!d->isThemeAvailable()) + return r; + + switch (element) { + case SE_PushButtonFocusRect: + r.adjust(0, 1, 0, -1); + break; + case SE_DockWidgetTitleBarText: { + const QStyleOptionDockWidget *v2 + = qstyleoption_cast<const QStyleOptionDockWidget*>(option); + bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar; + if (verticalTitleBar) { + r.adjust(0, 0, 0, -4); + } else { + if (option->direction == Qt::LeftToRight) + r.adjust(4, 0, 0, 0); + else + r.adjust(0, 0, -4, 0); + } + + break; + } + case SE_ProgressBarLabel: + case SE_ProgressBarContents: + case SE_ProgressBarGroove: + return option->rect; + case SE_PushButtonContents: + if (!gtk_check_version(2, 10, 0)) { + GtkWidget *gtkButton = d->gtkWidget("GtkButton"); + GtkBorder *border = 0; + gtk_widget_style_get(gtkButton, "inner-border", &border, NULL); + if (border) { + r = option->rect.adjusted(border->left, border->top, -border->right, -border->bottom); + gtk_border_free(border); + } else { + r = option->rect.adjusted(1, 1, -1, -1); + } + r = visualRect(option->direction, option->rect, r); + } + break; + default: + break; + } + + return r; +} + +/*! + \reimp +*/ +QRect QGtkStyle::itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const +{ + return QCommonStyle::itemPixmapRect(r, flags, pixmap); +} + +/*! + \reimp +*/ +void QGtkStyle::drawItemPixmap(QPainter *painter, const QRect &rect, + int alignment, const QPixmap &pixmap) const +{ + QCommonStyle::drawItemPixmap(painter, rect, alignment, pixmap); +} + +/*! + \reimp +*/ +QStyle::SubControl QGtkStyle::hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + const QPoint &pt, const QWidget *w) const +{ + return QCommonStyle::hitTestComplexControl(cc, opt, pt, w); +} + +/*! + \reimp +*/ +QPixmap QGtkStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, + const QStyleOption *opt) const +{ + return QCommonStyle::generatedIconPixmap(iconMode, pixmap, opt); +} + +/*! + \reimp +*/ +void QGtkStyle::drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal, + bool enabled, const QString& text, QPalette::ColorRole textRole) const +{ + return QCommonStyle::drawItemText(painter, rect, alignment, pal, enabled, text, textRole); +} + +QT_END_NAMESPACE + +#endif //!defined(QT_NO_STYLE_QGTK) diff --git a/src/qt5gtk2-style/qgtkstyle_p.cpp b/src/qt5gtk2-style/qgtkstyle_p.cpp new file mode 100644 index 0000000..4ac2a2b --- /dev/null +++ b/src/qt5gtk2-style/qgtkstyle_p.cpp @@ -0,0 +1,585 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include "qgtkstyle_p_p.h" + +// This file is responsible for resolving all GTK functions we use +// dynamically. This is done to avoid link-time dependancy on GTK +// as well as crashes occurring due to usage of the GTK_QT engines +// +// Additionally we create a map of common GTK widgets that we can pass +// to the GTK theme engine as many engines resort to querying the +// actual widget pointers for details that are not covered by the +// state flags + +#include <qglobal.h> +#if !defined(QT_NO_STYLE_GTK) + +#include <QEvent> +#include <QFile> +#include <QStringList> +#include <QTextStream> +#include <QHash> +#include <QUrl> +#include <QDebug> + +#include "qgtk2painter_p.h" +#include <private/qapplication_p.h> +#include <private/qiconloader_p.h> +#include <qpa/qplatformfontdatabase.h> + +#include <QMenu> +#include <QStyle> +#include <QApplication> +#include <QPixmapCache> +#include <QStatusBar> +#include <QMenuBar> +#include <QToolBar> +#include <QToolButton> + +#ifndef Q_OS_MAC +// X11 Includes: + +// the following is necessary to work around breakage in many versions +// of XFree86's Xlib.h still in use +// ### which versions? +#if defined(_XLIB_H_) // crude hack, but... +#error "cannot include <X11/Xlib.h> before this file" +#endif +#define XRegisterIMInstantiateCallback qt_XRegisterIMInstantiateCallback +#define XUnregisterIMInstantiateCallback qt_XUnregisterIMInstantiateCallback +#define XSetIMValues qt_XSetIMValues +#include <X11/Xlib.h> +#undef XRegisterIMInstantiateCallback +#undef XUnregisterIMInstantiateCallback +#undef XSetIMValues +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC(QGtkStyleUpdateScheduler, styleScheduler) + +#ifndef Q_OS_MAC +typedef int (*x11ErrorHandler)(Display*, XErrorEvent*); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGtkStylePrivate*) + +QT_BEGIN_NAMESPACE + +static void gtkStyleSetCallback(GtkWidget*) +{ + qRegisterMetaType<QGtkStylePrivate *>(); + + // We have to let this function return and complete the event + // loop to ensure that all gtk widgets have been styled before + // updating + QMetaObject::invokeMethod(styleScheduler(), "updateTheme", Qt::QueuedConnection); +} + +static void update_toolbar_style(GtkWidget *gtkToolBar, GParamSpec *, gpointer) +{ + GtkToolbarStyle toolbar_style = GTK_TOOLBAR_ICONS; + g_object_get(gtkToolBar, "toolbar-style", &toolbar_style, NULL); + QWidgetList widgets = QApplication::allWidgets(); + for (int i = 0; i < widgets.size(); ++i) { + QWidget *widget = widgets.at(i); + if (qobject_cast<QToolButton*>(widget)) { + QEvent event(QEvent::StyleChange); + QApplication::sendEvent(widget, &event); + } + } +} + +static QHashableLatin1Literal classPath(GtkWidget *widget) +{ + char *class_path; + gtk_widget_path (widget, NULL, &class_path, NULL); + + char *copy = class_path; + if (strncmp(copy, "GtkWindow.", 10) == 0) + copy += 10; + if (strncmp(copy, "GtkFixed.", 9) == 0) + copy += 9; + + copy = strdup(copy); + + g_free(class_path); + + return QHashableLatin1Literal::fromData(copy); +} + + + +bool QGtkStyleFilter::eventFilter(QObject *obj, QEvent *e) +{ + if (e->type() == QEvent::ApplicationPaletteChange) { + // Only do this the first time since this will also + // generate applicationPaletteChange events + //if (!qt_app_palettes_hash() || qt_app_palettes_hash()->isEmpty()) { + // stylePrivate->applyCustomPaletteHash(); + //} + } + return QObject::eventFilter(obj, e); +} + +QList<QGtkStylePrivate *> QGtkStylePrivate::instances; +QGtkStylePrivate::WidgetMap *QGtkStylePrivate::widgetMap = 0; + +QGtkStylePrivate::QGtkStylePrivate() + : QCommonStylePrivate() + , filter(this) +{ + instances.append(this); + animationFps = 60; +} + +QGtkStylePrivate::~QGtkStylePrivate() +{ + instances.removeOne(this); +} + +void QGtkStylePrivate::init() +{ + initGtkWidgets(); +} + +QGtkPainter* QGtkStylePrivate::gtkPainter(QPainter *painter) +{ + // TODO: choose between gtk2 and gtk3 + static QGtk2Painter instance; + instance.reset(painter); + return &instance; +} + +GtkWidget* QGtkStylePrivate::gtkWidget(const QHashableLatin1Literal &path) +{ + GtkWidget *widget = gtkWidgetMap()->value(path); + if (!widget) { + // Theme might have rearranged widget internals + widget = gtkWidgetMap()->value(path); + } + return widget; +} + +GtkStyle* QGtkStylePrivate::gtkStyle(const QHashableLatin1Literal &path) +{ + if (GtkWidget *w = gtkWidgetMap()->value(path)) + return gtk_widget_get_style(w); + return 0; +} + +void QGtkStylePrivate::gtkWidgetSetFocus(GtkWidget *widget, bool focus) +{ + GdkEvent *event = gdk_event_new(GDK_FOCUS_CHANGE); + event->focus_change.type = GDK_FOCUS_CHANGE; + event->focus_change.in = focus; + gtk_widget_send_focus_change(widget, event); + gdk_event_free(event); +} + +/* \internal + * Initializes a number of gtk menu widgets. + * The widgets are cached. + */ +void QGtkStylePrivate::initGtkMenu() const +{ + // Create menubar + GtkWidget *gtkMenuBar = gtk_menu_bar_new(); + setupGtkWidget(gtkMenuBar); + + GtkWidget *gtkMenuBarItem = gtk_menu_item_new_with_label("X"); + gtk_menu_shell_append((GtkMenuShell*)(gtkMenuBar), gtkMenuBarItem); + gtk_widget_realize(gtkMenuBarItem); + + // Create menu + GtkWidget *gtkMenu = gtk_menu_new(); + gtk_menu_item_set_submenu((GtkMenuItem*)(gtkMenuBarItem), gtkMenu); + gtk_widget_realize(gtkMenu); + + GtkWidget *gtkMenuItem = gtk_menu_item_new_with_label("X"); + gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuItem); + gtk_widget_realize(gtkMenuItem); + + GtkWidget *gtkCheckMenuItem = gtk_check_menu_item_new_with_label("X"); + gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkCheckMenuItem); + gtk_widget_realize(gtkCheckMenuItem); + + GtkWidget *gtkMenuSeparator = gtk_separator_menu_item_new(); + gtk_menu_shell_append((GtkMenuShell*)gtkMenu, gtkMenuSeparator); + + addAllSubWidgets(gtkMenuBar); + addAllSubWidgets(gtkMenu); +} + + +void QGtkStylePrivate::initGtkTreeview() const +{ + GtkWidget *gtkTreeView = gtk_tree_view_new(); + gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); + gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); + gtk_tree_view_append_column((GtkTreeView*)gtkTreeView, gtk_tree_view_column_new()); + addWidget(gtkTreeView); +} + + +/* \internal + * Initializes a number of gtk widgets that we can later on use to determine some of our styles. + * The widgets are cached. + */ +void QGtkStylePrivate::initGtkWidgets() const +{ + // From gtkmain.c + uid_t ruid = getuid (); + uid_t rgid = getgid (); + uid_t euid = geteuid (); + uid_t egid = getegid (); + if (ruid != euid || rgid != egid) { + qWarning("\nThis process is currently running setuid or setgid.\nGTK+ does not allow this " + "therefore Qt cannot use the GTK+ integration.\nTry launching your app using \'gksudo\', " + "\'kdesudo\' or a similar tool.\n\n" + "See http://www.gtk.org/setuid.html for more information.\n"); + return; + } + + +#ifndef Q_OS_MAC + // Gtk will set the Qt error handler so we have to reset it afterwards + x11ErrorHandler qt_x_errhandler = XSetErrorHandler(0); +#endif + gtk_init (NULL, NULL); +#ifndef Q_OS_MAC + XSetErrorHandler(qt_x_errhandler); +#endif + + // make a window + GtkWidget* gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_realize(gtkWindow); + QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkWindow")); + removeWidgetFromMap(widgetPath); + gtkWidgetMap()->insert(widgetPath, gtkWindow); + + + // Make all other widgets. respect the text direction + if (qApp->layoutDirection() == Qt::RightToLeft) + gtk_widget_set_default_direction(GTK_TEXT_DIR_RTL); + + if (!gtkWidgetMap()->contains("GtkButton")) { + GtkWidget *gtkButton = gtk_button_new(); + addWidget(gtkButton); + g_signal_connect(gtkButton, "style-set", G_CALLBACK(gtkStyleSetCallback), 0); + addWidget((GtkWidget*)gtk_tool_button_new(NULL, "Qt")); + addWidget(gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE)); + addWidget(gtk_hbutton_box_new()); + addWidget(gtk_check_button_new()); + addWidget(gtk_radio_button_new(NULL)); + addWidget(gtk_combo_box_new()); + addWidget(gtk_combo_box_entry_new()); + GtkWidget *entry = gtk_entry_new(); + // gtk-im-context-none is supported in gtk+ since 2.19.5 + // and also exists in gtk3 + // http://git.gnome.org/browse/gtk+/tree/gtk/gtkimmulticontext.c?id=2.19.5#n33 + // reason that we don't use gtk-im-context-simple here is, + // gtk-im-context-none has less overhead, and 2.19.5 is + // relatively old. and even for older gtk+, it will fallback + // to gtk-im-context-simple if gtk-im-context-none doesn't + // exists. + g_object_set(entry, "im-module", "gtk-im-context-none", NULL); + addWidget(entry); + addWidget(gtk_frame_new(NULL)); + addWidget(gtk_expander_new("")); + addWidget(gtk_statusbar_new()); + addWidget(gtk_hscale_new((GtkAdjustment*)gtk_adjustment_new(1, 0, 1, 0, 0, 0))); + addWidget(gtk_hscrollbar_new(NULL)); + addWidget(gtk_scrolled_window_new(NULL, NULL)); + + initGtkMenu(); + addWidget(gtk_notebook_new()); + addWidget(gtk_progress_bar_new()); + addWidget(gtk_spin_button_new((GtkAdjustment*)gtk_adjustment_new(1, 0, 1, 0, 0, 0), 0.1, 3)); + GtkWidget *toolbar = gtk_toolbar_new(); + g_signal_connect (toolbar, "notify::toolbar-style", G_CALLBACK (update_toolbar_style), toolbar); + gtk_toolbar_insert((GtkToolbar*)toolbar, gtk_separator_tool_item_new(), -1); + addWidget(toolbar); + initGtkTreeview(); + addWidget(gtk_vscale_new((GtkAdjustment*)gtk_adjustment_new(1, 0, 1, 0, 0, 0))); + addWidget(gtk_vscrollbar_new(NULL)); + } + else // Rebuild map + { + // When styles change subwidgets can get rearranged + // as with the combo box. We need to update the widget map + // to reflect this; + QHash<QHashableLatin1Literal, GtkWidget*> oldMap = *gtkWidgetMap(); + gtkWidgetMap()->clear(); + QHashIterator<QHashableLatin1Literal, GtkWidget*> it(oldMap); + while (it.hasNext()) { + it.next(); + if (!strchr(it.key().data(), '.')) { + addAllSubWidgets(it.value()); + } + free(const_cast<char *>(it.key().data())); + } + } +} + +/*! \internal + * destroys all previously buffered widgets. + */ +void QGtkStylePrivate::cleanupGtkWidgets() +{ + if (!widgetMap) + return; + if (widgetMap->contains("GtkWindow")) // Gtk will destroy all children + gtk_widget_destroy(widgetMap->value("GtkWindow")); + for (QHash<QHashableLatin1Literal, GtkWidget *>::const_iterator it = widgetMap->constBegin(); + it != widgetMap->constEnd(); ++it) + free(const_cast<char *>(it.key().data())); +} + +QString QGtkStylePrivate::getThemeName() +{ + QString themeName; + // Read the theme name from GtkSettings + GtkSettings *settings = gtk_settings_get_default(); + gchararray value; + g_object_get(settings, "gtk-theme-name", &value, NULL); + themeName = QString::fromUtf8(value); + g_free(value); + return themeName; +} + +// Get size of the arrow controls in a GtkSpinButton +int QGtkStylePrivate::getSpinboxArrowSize() const +{ + const int MIN_ARROW_WIDTH = 6; + GtkWidget *spinButton = gtkWidget("GtkSpinButton"); + GtkStyle *style = gtk_widget_get_style(spinButton); + gint size = pango_font_description_get_size (style->font_desc); + gint arrow_size; + arrow_size = qMax(PANGO_PIXELS (size), MIN_ARROW_WIDTH) + style->xthickness; + arrow_size += arrow_size%2 + 1; + return arrow_size; +} + + +bool QGtkStylePrivate::isKDE4Session() +{ + static int version = -1; + if (version == -1) + version = qgetenv("KDE_SESSION_VERSION").toInt(); + return (version == 4); +} + +void QGtkStylePrivate::applyCustomPaletteHash() +{ + QPalette menuPal = gtkWidgetPalette("GtkMenu"); + GdkColor gdkBg = gtk_widget_get_style(gtkWidget("GtkMenu"))->bg[GTK_STATE_NORMAL]; + QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); + menuPal.setBrush(QPalette::Base, bgColor); + menuPal.setBrush(QPalette::Window, bgColor); + qApp->setPalette(menuPal, "QMenu"); + + QPalette toolbarPal = gtkWidgetPalette("GtkToolbar"); + qApp->setPalette(toolbarPal, "QToolBar"); + + QPalette menuBarPal = gtkWidgetPalette("GtkMenuBar"); + qApp->setPalette(menuBarPal, "QMenuBar"); +} + +/*! \internal + * Returns the gtk Widget that should be used to determine text foreground and background colors. +*/ +GtkWidget* QGtkStylePrivate::getTextColorWidget() const +{ + return gtkWidget("GtkEntry"); +} + +void QGtkStylePrivate::setupGtkWidget(GtkWidget* widget) +{ + if (Q_GTK_IS_WIDGET(widget)) { + GtkWidget *protoLayout = gtkWidgetMap()->value("GtkContainer"); + if (!protoLayout) { + protoLayout = gtk_fixed_new(); + gtk_container_add((GtkContainer*)(gtkWidgetMap()->value("GtkWindow")), protoLayout); + QHashableLatin1Literal widgetPath = QHashableLatin1Literal::fromData(strdup("GtkContainer")); + gtkWidgetMap()->insert(widgetPath, protoLayout); + } + Q_ASSERT(protoLayout); + + if (!gtk_widget_get_parent(widget) && !gtk_widget_is_toplevel(widget)) + gtk_container_add((GtkContainer*)(protoLayout), widget); + gtk_widget_realize(widget); + } +} + +void QGtkStylePrivate::removeWidgetFromMap(const QHashableLatin1Literal &path) +{ + WidgetMap *map = gtkWidgetMap(); + WidgetMap::iterator it = map->find(path); + if (it != map->end()) { + char* keyData = const_cast<char *>(it.key().data()); + map->erase(it); + free(keyData); + } +} + +void QGtkStylePrivate::addWidgetToMap(GtkWidget *widget) +{ + if (Q_GTK_IS_WIDGET(widget)) { + gtk_widget_realize(widget); + QHashableLatin1Literal widgetPath = classPath(widget); + + removeWidgetFromMap(widgetPath); + gtkWidgetMap()->insert(widgetPath, widget); +#ifdef DUMP_GTK_WIDGET_TREE + qWarning("Inserted Gtk Widget: %s", widgetPath.data()); +#endif + } + } + +void QGtkStylePrivate::addAllSubWidgets(GtkWidget *widget, gpointer v) +{ + Q_UNUSED(v); + addWidgetToMap(widget); + if (G_TYPE_CHECK_INSTANCE_TYPE ((widget), gtk_container_get_type())) + gtk_container_forall((GtkContainer*)widget, addAllSubWidgets, NULL); +} + +// Updates window/windowtext palette based on the indicated gtk widget +QPalette QGtkStylePrivate::gtkWidgetPalette(const QHashableLatin1Literal >kWidgetName) const +{ + GtkWidget *gtkWidget = QGtkStylePrivate::gtkWidget(gtkWidgetName); + Q_ASSERT(gtkWidget); + QPalette pal = QApplication::palette(); + GdkColor gdkBg = gtk_widget_get_style(gtkWidget)->bg[GTK_STATE_NORMAL]; + GdkColor gdkText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_NORMAL]; + GdkColor gdkDisabledText = gtk_widget_get_style(gtkWidget)->fg[GTK_STATE_INSENSITIVE]; + QColor bgColor(gdkBg.red>>8, gdkBg.green>>8, gdkBg.blue>>8); + QColor textColor(gdkText.red>>8, gdkText.green>>8, gdkText.blue>>8); + QColor disabledTextColor(gdkDisabledText.red>>8, gdkDisabledText.green>>8, gdkDisabledText.blue>>8); + pal.setBrush(QPalette::Window, bgColor); + pal.setBrush(QPalette::Button, bgColor); + pal.setBrush(QPalette::All, QPalette::WindowText, textColor); + pal.setBrush(QPalette::Disabled, QPalette::WindowText, disabledTextColor); + pal.setBrush(QPalette::All, QPalette::ButtonText, textColor); + pal.setBrush(QPalette::Disabled, QPalette::ButtonText, disabledTextColor); + return pal; +} + + +void QGtkStyleUpdateScheduler::updateTheme() +{ + static QString oldTheme(QLS("qt_not_set")); + QPixmapCache::clear(); + + QFont font = QGtkStylePrivate::getThemeFont(); + if (QApplication::font() != font) + qApp->setFont(font); + + if (oldTheme != QGtkStylePrivate::getThemeName()) { + oldTheme = QGtkStylePrivate::getThemeName(); + QPalette newPalette = qApp->style()->standardPalette(); +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + QApplicationPrivate::setSystemPalette(newPalette); +#endif + QApplication::setPalette(newPalette); + if (!QGtkStylePrivate::instances.isEmpty()) { + QGtkStylePrivate::instances.last()->initGtkWidgets(); + QGtkStylePrivate::instances.last()->applyCustomPaletteHash(); + } + QList<QWidget*> widgets = QApplication::allWidgets(); + // Notify all widgets that size metrics might have changed + foreach (QWidget *widget, widgets) { + QEvent e(QEvent::StyleChange); + QApplication::sendEvent(widget, &e); + } + } + QIconLoader::instance()->updateSystemTheme(); +} + +void QGtkStylePrivate::addWidget(GtkWidget *widget) +{ + if (widget) { + setupGtkWidget(widget); + addAllSubWidgets(widget); + } +} + + +// Fetch the application font from the pango font description +// contained in the theme. +QFont QGtkStylePrivate::getThemeFont() +{ + QFont font; + GtkStyle *style = gtkStyle(); + if (style && qApp->desktopSettingsAware()) + { + PangoFontDescription *gtk_font = style->font_desc; + font.setPointSizeF((float)(pango_font_description_get_size(gtk_font))/PANGO_SCALE); + + QString family = QString::fromLatin1(pango_font_description_get_family(gtk_font)); + if (!family.isEmpty()) + font.setFamily(family); + + const int weight = pango_font_description_get_weight(gtk_font); + font.setWeight(QPlatformFontDatabase::weightFromInteger(weight)); + + PangoStyle fontstyle = pango_font_description_get_style(gtk_font); + if (fontstyle == PANGO_STYLE_ITALIC) + font.setStyle(QFont::StyleItalic); + else if (fontstyle == PANGO_STYLE_OBLIQUE) + font.setStyle(QFont::StyleOblique); + else + font.setStyle(QFont::StyleNormal); + } + return font; +} + +bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2) +{ + return l1.size() == l2.size() || qstrcmp(l1.data(), l2.data()) == 0; +} + +// copied from qHash.cpp +uint qHash(const QHashableLatin1Literal &key) +{ + int n = key.size(); + const uchar *p = reinterpret_cast<const uchar *>(key.data()); + uint h = 0; + uint g; + + while (n--) { + h = (h << 4) + *p++; + if ((g = (h & 0xf0000000)) != 0) + h ^= g >> 23; + h &= ~g; + } + return h; +} + +QT_END_NAMESPACE + +#include "moc_qgtkstyle_p.cpp" +#include "moc_qgtkstyle_p_p.cpp" + +#endif // !defined(QT_NO_STYLE_GTK) diff --git a/src/qt5gtk2-style/qgtkstyle_p.h b/src/qt5gtk2-style/qgtkstyle_p.h new file mode 100644 index 0000000..456f372 --- /dev/null +++ b/src/qt5gtk2-style/qgtkstyle_p.h @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QGTKSTYLE_P_H +#define QGTKSTYLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +//#include <private/qwindowsstyle_p.h> +#include <QPalette> +#include <QFont> +#include <QFileDialog> +#include <QCommonStyle> + +QT_BEGIN_NAMESPACE + + +#if !defined(QT_NO_STYLE_GTK) + +class QPainterPath; +class QGtkStylePrivate; + +class QGtkStyle : public QCommonStyle +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGtkStyle) + +public: + QGtkStyle(); + QGtkStyle(QGtkStylePrivate &dd); + + ~QGtkStyle(); + + QPalette standardPalette() const Q_DECL_OVERRIDE; + + void drawPrimitive(PrimitiveElement element, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const Q_DECL_OVERRIDE; + void drawControl(ControlElement control, const QStyleOption *option, + QPainter *painter, const QWidget *widget) const Q_DECL_OVERRIDE; + void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, + QPainter *painter, const QWidget *widget) const Q_DECL_OVERRIDE; + void drawItemPixmap(QPainter *painter, const QRect &rect, int alignment, + const QPixmap &pixmap) const Q_DECL_OVERRIDE; + void drawItemText(QPainter *painter, const QRect &rect, int alignment, const QPalette &pal, + bool enabled, const QString& text, QPalette::ColorRole textRole) const Q_DECL_OVERRIDE; + + int pixelMetric(PixelMetric metric, const QStyleOption *option = 0, + const QWidget *widget = 0) const Q_DECL_OVERRIDE; + int styleHint(StyleHint hint, const QStyleOption *option, + const QWidget *widget, QStyleHintReturn *returnData) const Q_DECL_OVERRIDE; + + QStyle::SubControl hitTestComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, + const QPoint &pt, const QWidget *w) const Q_DECL_OVERRIDE; + + QRect subControlRect(ComplexControl control, const QStyleOptionComplex *option, + SubControl subControl, const QWidget *widget) const Q_DECL_OVERRIDE; + QRect subElementRect(SubElement sr, const QStyleOption *opt, const QWidget *w) const Q_DECL_OVERRIDE; + QRect itemPixmapRect(const QRect &r, int flags, const QPixmap &pixmap) const Q_DECL_OVERRIDE; + + + QSize sizeFromContents(ContentsType type, const QStyleOption *option, + const QSize &size, const QWidget *widget) const Q_DECL_OVERRIDE; + QIcon standardIcon(StandardPixmap standardIcon, const QStyleOption *option = 0, + const QWidget *widget = 0) const Q_DECL_OVERRIDE; + QPixmap standardPixmap(StandardPixmap sp, const QStyleOption *option, + const QWidget *widget) const Q_DECL_OVERRIDE; + QPixmap generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap, + const QStyleOption *opt) const Q_DECL_OVERRIDE; + + void polish(QWidget *widget) Q_DECL_OVERRIDE; + void polish(QApplication *app) Q_DECL_OVERRIDE; + void polish(QPalette &palette) Q_DECL_OVERRIDE; + + void unpolish(QWidget *widget) Q_DECL_OVERRIDE; + void unpolish(QApplication *app) Q_DECL_OVERRIDE; +}; + +#endif //!defined(QT_NO_STYLE_QGTK) + +QT_END_NAMESPACE + +#endif //QGTKSTYLE_P_H diff --git a/src/qt5gtk2-style/qgtkstyle_p_p.h b/src/qt5gtk2-style/qgtkstyle_p_p.h new file mode 100644 index 0000000..d03d977 --- /dev/null +++ b/src/qt5gtk2-style/qgtkstyle_p_p.h @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#ifndef QGTKSTYLE_P_P_H +#define QGTKSTYLE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtGlobal> +#if !defined(QT_NO_STYLE_GTK) + +#include <QString> +#include <QStringBuilder> +#include <QCoreApplication> +#include <QFileDialog> +#include <QCommonStyle> + +#include <private/qcommonstyle_p.h> +#include "qgtkstyle_p.h" +#include "qgtkglobal_p.h" + +#define Q_GTK_IS_WIDGET(widget) widget && G_TYPE_CHECK_INSTANCE_TYPE ((widget), gtk_widget_get_type()) + +QT_BEGIN_NAMESPACE + +class QHashableLatin1Literal +{ +public: + int size() const { return m_size; } + const char *data() const { return m_data; } + +#ifdef __SUNPRO_CC + QHashableLatin1Literal(const char* str) + : m_size(strlen(str)), m_data(str) {} +#else + template <int N> + QHashableLatin1Literal(const char (&str)[N]) + : m_size(N - 1), m_data(str) {} +#endif + + QHashableLatin1Literal(const QHashableLatin1Literal &other) + : m_size(other.m_size), m_data(other.m_data) + {} + + QHashableLatin1Literal &operator=(const QHashableLatin1Literal &other) + { + if (this == &other) + return *this; + *const_cast<int *>(&m_size) = other.m_size; + *const_cast<char **>(&m_data) = const_cast<char *>(other.m_data); + return *this; + } + + QString toString() const { return QString::fromLatin1(m_data, m_size); } + + static QHashableLatin1Literal fromData(const char *str) + { + return QHashableLatin1Literal(str, qstrlen(str)); + } + +private: + QHashableLatin1Literal(const char *str, int length) + : m_size(length), m_data(str) + {} + + const int m_size; + const char *m_data; +}; + +bool operator==(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2); +inline bool operator!=(const QHashableLatin1Literal &l1, const QHashableLatin1Literal &l2) { return !operator==(l1, l2); } +uint qHash(const QHashableLatin1Literal &key); + +QT_END_NAMESPACE + +typedef struct _XDisplay Display; + +QT_BEGIN_NAMESPACE + +class QGtkPainter; +class QGtkStylePrivate; + +class QGtkStyleFilter : public QObject +{ +public: + QGtkStyleFilter(QGtkStylePrivate* sp) + : stylePrivate(sp) + {} +private: + QGtkStylePrivate* stylePrivate; + bool eventFilter(QObject *obj, QEvent *e) Q_DECL_OVERRIDE; +}; + +class QGtkStylePrivate : public QCommonStylePrivate +{ + Q_DECLARE_PUBLIC(QGtkStyle) +public: + QGtkStylePrivate(); + ~QGtkStylePrivate(); + + QGtkStyleFilter filter; + + static QGtkPainter* gtkPainter(QPainter *painter = 0); + static GtkWidget* gtkWidget(const QHashableLatin1Literal &path); + static GtkStyle* gtkStyle(const QHashableLatin1Literal &path = QHashableLatin1Literal("GtkWindow")); + static void gtkWidgetSetFocus(GtkWidget *widget, bool focus); + + virtual void initGtkMenu() const; + virtual void initGtkTreeview() const; + virtual void initGtkWidgets() const; + + static void cleanupGtkWidgets(); + + static bool isKDE4Session(); + void applyCustomPaletteHash(); + static QFont getThemeFont(); + static bool isThemeAvailable() { return gtkStyle() != 0; } + + static QString getThemeName(); + virtual int getSpinboxArrowSize() const; + + virtual QPalette gtkWidgetPalette(const QHashableLatin1Literal >kWidgetName) const; + +protected: + typedef QHash<QHashableLatin1Literal, GtkWidget*> WidgetMap; + + static inline void destroyWidgetMap() + { + cleanupGtkWidgets(); + delete widgetMap; + widgetMap = 0; + } + + static inline WidgetMap *gtkWidgetMap() + { + if (!widgetMap) { + widgetMap = new WidgetMap(); + qAddPostRoutine(destroyWidgetMap); + } + return widgetMap; + } + + static QStringList extract_filter(const QString &rawFilter); + + virtual GtkWidget* getTextColorWidget() const; + static void setupGtkWidget(GtkWidget* widget); + static void addWidgetToMap(GtkWidget* widget); + static void addAllSubWidgets(GtkWidget *widget, gpointer v = 0); + static void addWidget(GtkWidget *widget); + static void removeWidgetFromMap(const QHashableLatin1Literal &path); + + virtual void init(); + + enum { + menuItemFrame = 2, // menu item frame width + menuItemHMargin = 3, // menu item hor text margin + menuArrowHMargin = 6, // menu arrow horizontal margin + menuItemVMargin = 2, // menu item ver text margin + menuRightBorder = 15, // right border on menus + menuCheckMarkWidth = 12 // checkmarks width on menus + }; + +private: + static QList<QGtkStylePrivate *> instances; + static WidgetMap *widgetMap; + friend class QGtkStyleUpdateScheduler; +}; + +// Helper to ensure that we have polished all our gtk widgets +// before updating our own palettes +class QGtkStyleUpdateScheduler : public QObject +{ + Q_OBJECT +public slots: + void updateTheme(); +}; + +QT_END_NAMESPACE + +#endif // !QT_NO_STYLE_GTK +#endif // QGTKSTYLE_P_P_H diff --git a/src/qt5gtk2-style/qstylehelper.cpp b/src/qt5gtk2-style/qstylehelper.cpp new file mode 100644 index 0000000..00249ec --- /dev/null +++ b/src/qt5gtk2-style/qstylehelper.cpp @@ -0,0 +1,304 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include <QStyleOption> +#include <QPainter> +#include <QPixmapCache> +#include <private/qmath_p.h> +#include <private/qstyle_p.h> +#include <QtMath> +#include <QScrollBar> +#include <QAbstractScrollArea> +#include <QWindow> + +#include "qstylehelper_p.h" +#include <QStringBuilder> + +QT_BEGIN_NAMESPACE + +namespace QStyleHelper { + +QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size) +{ + const QStyleOptionComplex *complexOption = qstyleoption_cast<const QStyleOptionComplex *>(option); + QString tmp = key % HexString<uint>(option->state) + % HexString<uint>(option->direction) + % HexString<uint>(complexOption ? uint(complexOption->activeSubControls) : 0u) + % HexString<quint64>(option->palette.cacheKey()) + % HexString<uint>(size.width()) + % HexString<uint>(size.height()); + +#ifndef QT_NO_SPINBOX + if (const QStyleOptionSpinBox *spinBox = qstyleoption_cast<const QStyleOptionSpinBox *>(option)) { + tmp = tmp % HexString<uint>(spinBox->buttonSymbols) + % HexString<uint>(spinBox->stepEnabled) + % QLatin1Char(spinBox->frame ? '1' : '0'); ; + } +#endif // QT_NO_SPINBOX + return tmp; +} + +#ifndef QT_NO_ACCESSIBILITY +bool isInstanceOf(QObject *obj, QAccessible::Role role) +{ + bool match = false; + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(obj); + match = iface && iface->role() == role; + return match; +} + +// Searches for an ancestor of a particular accessible role +bool hasAncestor(QObject *obj, QAccessible::Role role) +{ + bool found = false; + QObject *parent = obj ? obj->parent() : 0; + while (parent && !found) { + if (isInstanceOf(parent, role)) + found = true; + parent = parent->parent(); + } + return found; +} +#endif // QT_NO_ACCESSIBILITY + + +#ifndef QT_NO_DIAL + +int calcBigLineSize(int radius) +{ + int bigLineSize = radius / 6; + if (bigLineSize < 4) + bigLineSize = 4; + if (bigLineSize > radius / 2) + bigLineSize = radius / 2; + return bigLineSize; +} + +static QPointF calcRadialPos(const QStyleOptionSlider *dial, qreal offset) +{ + const int width = dial->rect.width(); + const int height = dial->rect.height(); + const int r = qMin(width, height) / 2; + const int currentSliderPosition = dial->upsideDown ? dial->sliderPosition : (dial->maximum - dial->sliderPosition); + qreal a = 0; + if (dial->maximum == dial->minimum) + a = Q_PI / 2; + else if (dial->dialWrapping) + a = Q_PI * 3 / 2 - (currentSliderPosition - dial->minimum) * 2 * Q_PI + / (dial->maximum - dial->minimum); + else + a = (Q_PI * 8 - (currentSliderPosition - dial->minimum) * 10 * Q_PI + / (dial->maximum - dial->minimum)) / 6; + qreal xc = width / 2.0; + qreal yc = height / 2.0; + qreal len = r - QStyleHelper::calcBigLineSize(r) - 3; + qreal back = offset * len; + QPointF pos(QPointF(xc + back * qCos(a), yc - back * qSin(a))); + return pos; +} + +qreal angle(const QPointF &p1, const QPointF &p2) +{ + static const qreal rad_factor = 180 / Q_PI; + qreal _angle = 0; + + if (p1.x() == p2.x()) { + if (p1.y() < p2.y()) + _angle = 270; + else + _angle = 90; + } else { + qreal x1, x2, y1, y2; + + if (p1.x() <= p2.x()) { + x1 = p1.x(); y1 = p1.y(); + x2 = p2.x(); y2 = p2.y(); + } else { + x2 = p1.x(); y2 = p1.y(); + x1 = p2.x(); y1 = p2.y(); + } + + qreal m = -(y2 - y1) / (x2 - x1); + _angle = qAtan(m) * rad_factor; + + if (p1.x() < p2.x()) + _angle = 180 - _angle; + else + _angle = -_angle; + } + return _angle; +} + +QPolygonF calcLines(const QStyleOptionSlider *dial) +{ + QPolygonF poly; + int width = dial->rect.width(); + int height = dial->rect.height(); + qreal r = qMin(width, height) / 2; + int bigLineSize = calcBigLineSize(int(r)); + + qreal xc = width / 2 + 0.5; + qreal yc = height / 2 + 0.5; + const int ns = dial->tickInterval; + if (!ns) // Invalid values may be set by Qt Designer. + return poly; + int notches = (dial->maximum + ns - 1 - dial->minimum) / ns; + if (notches <= 0) + return poly; + if (dial->maximum < dial->minimum || dial->maximum - dial->minimum > 1000) { + int maximum = dial->minimum + 1000; + notches = (maximum + ns - 1 - dial->minimum) / ns; + } + + poly.resize(2 + 2 * notches); + int smallLineSize = bigLineSize / 2; + for (int i = 0; i <= notches; ++i) { + qreal angle = dial->dialWrapping ? Q_PI * 3 / 2 - i * 2 * Q_PI / notches + : (Q_PI * 8 - i * 10 * Q_PI / notches) / 6; + qreal s = qSin(angle); + qreal c = qCos(angle); + if (i == 0 || (((ns * i) % (dial->pageStep ? dial->pageStep : 1)) == 0)) { + poly[2 * i] = QPointF(xc + (r - bigLineSize) * c, + yc - (r - bigLineSize) * s); + poly[2 * i + 1] = QPointF(xc + r * c, yc - r * s); + } else { + poly[2 * i] = QPointF(xc + (r - 1 - smallLineSize) * c, + yc - (r - 1 - smallLineSize) * s); + poly[2 * i + 1] = QPointF(xc + (r - 1) * c, yc -(r - 1) * s); + } + } + return poly; +} + +// This will draw a nice and shiny QDial for us. We don't want +// all the shinyness in QWindowsStyle, hence we place it here + +void drawDial(const QStyleOptionSlider *option, QPainter *painter) +{ + QPalette pal = option->palette; + QColor buttonColor = pal.button().color(); + const int width = option->rect.width(); + const int height = option->rect.height(); + const bool enabled = option->state & QStyle::State_Enabled; + qreal r = qMin(width, height) / 2; + r -= r/50; + const qreal penSize = r/20.0; + + painter->save(); + painter->setRenderHint(QPainter::Antialiasing); + + // Draw notches + if (option->subControls & QStyle::SC_DialTickmarks) { + painter->setPen(option->palette.dark().color().darker(120)); + painter->drawLines(QStyleHelper::calcLines(option)); + } + + // Cache dial background + BEGIN_STYLE_PIXMAPCACHE(QString::fromLatin1("qdial")); + p->setRenderHint(QPainter::Antialiasing); + + const qreal d_ = r / 6; + const qreal dx = option->rect.x() + d_ + (width - 2 * r) / 2 + 1; + const qreal dy = option->rect.y() + d_ + (height - 2 * r) / 2 + 1; + + QRectF br = QRectF(dx + 0.5, dy + 0.5, + int(r * 2 - 2 * d_ - 2), + int(r * 2 - 2 * d_ - 2)); + buttonColor.setHsv(buttonColor .hue(), + qMin(140, buttonColor .saturation()), + qMax(180, buttonColor.value())); + QColor shadowColor(0, 0, 0, 20); + + if (enabled) { + // Drop shadow + qreal shadowSize = qMax(1.0, penSize/2.0); + QRectF shadowRect= br.adjusted(-2*shadowSize, -2*shadowSize, + 2*shadowSize, 2*shadowSize); + QRadialGradient shadowGradient(shadowRect.center().x(), + shadowRect.center().y(), shadowRect.width()/2.0, + shadowRect.center().x(), shadowRect.center().y()); + shadowGradient.setColorAt(qreal(0.91), QColor(0, 0, 0, 40)); + shadowGradient.setColorAt(qreal(1.0), Qt::transparent); + p->setBrush(shadowGradient); + p->setPen(Qt::NoPen); + p->translate(shadowSize, shadowSize); + p->drawEllipse(shadowRect); + p->translate(-shadowSize, -shadowSize); + + // Main gradient + QRadialGradient gradient(br.center().x() - br.width()/3, dy, + br.width()*1.3, br.center().x(), + br.center().y() - br.height()/2); + gradient.setColorAt(0, buttonColor.lighter(110)); + gradient.setColorAt(qreal(0.5), buttonColor); + gradient.setColorAt(qreal(0.501), buttonColor.darker(102)); + gradient.setColorAt(1, buttonColor.darker(115)); + p->setBrush(gradient); + } else { + p->setBrush(Qt::NoBrush); + } + + p->setPen(QPen(buttonColor.darker(280))); + p->drawEllipse(br); + p->setBrush(Qt::NoBrush); + p->setPen(buttonColor.lighter(110)); + p->drawEllipse(br.adjusted(1, 1, -1, -1)); + + if (option->state & QStyle::State_HasFocus) { + QColor highlight = pal.highlight().color(); + highlight.setHsv(highlight.hue(), + qMin(160, highlight.saturation()), + qMax(230, highlight.value())); + highlight.setAlpha(127); + p->setPen(QPen(highlight, 2.0)); + p->setBrush(Qt::NoBrush); + p->drawEllipse(br.adjusted(-1, -1, 1, 1)); + } + + END_STYLE_PIXMAPCACHE + + QPointF dp = calcRadialPos(option, qreal(0.70)); + buttonColor = buttonColor.lighter(104); + buttonColor.setAlphaF(qreal(0.8)); + const qreal ds = r/qreal(7.0); + QRectF dialRect(dp.x() - ds, dp.y() - ds, 2*ds, 2*ds); + QRadialGradient dialGradient(dialRect.center().x() + dialRect.width()/2, + dialRect.center().y() + dialRect.width(), + dialRect.width()*2, + dialRect.center().x(), dialRect.center().y()); + dialGradient.setColorAt(1, buttonColor.darker(140)); + dialGradient.setColorAt(qreal(0.4), buttonColor.darker(120)); + dialGradient.setColorAt(0, buttonColor.darker(110)); + if (penSize > 3.0) { + painter->setPen(QPen(QColor(0, 0, 0, 25), penSize)); + painter->drawLine(calcRadialPos(option, qreal(0.90)), calcRadialPos(option, qreal(0.96))); + } + + painter->setBrush(dialGradient); + painter->setPen(QColor(255, 255, 255, 150)); + painter->drawEllipse(dialRect.adjusted(-1, -1, 1, 1)); + painter->setPen(QColor(0, 0, 0, 80)); + painter->drawEllipse(dialRect); + painter->restore(); +} +#endif //QT_NO_DIAL + +} +QT_END_NAMESPACE diff --git a/src/qt5gtk2-style/qstylehelper_p.h b/src/qt5gtk2-style/qstylehelper_p.h new file mode 100644 index 0000000..d5453cb --- /dev/null +++ b/src/qt5gtk2-style/qstylehelper_p.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright (C) 2015 The Qt Company Ltd. * + * Copyright (C) 2016-2022 Ilya Kotov, forkotov02@ya.ru * + * * + * 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. * + ***************************************************************************/ + +#include <QtGlobal> +#include <QPoint> +#include <QString> +#include <QPolygon> +#include <QStringBuilder> +#include <QAccessible> + +#ifndef QSTYLEHELPER_P_H +#define QSTYLEHELPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qhexstring_p.h> + +QT_BEGIN_NAMESPACE + +class QPainter; +class QPixmap; +class QStyleOptionSlider; +class QStyleOption; +class QWindow; + +namespace QStyleHelper +{ + QString uniqueName(const QString &key, const QStyleOption *option, const QSize &size); +#ifndef QT_NO_DIAL + qreal angle(const QPointF &p1, const QPointF &p2); + QPolygonF calcLines(const QStyleOptionSlider *dial); + int calcBigLineSize(int radius); + void drawDial(const QStyleOptionSlider *dial, QPainter *painter); +#endif //QT_NO_DIAL + void drawBorderPixmap(const QPixmap &pixmap, QPainter *painter, const QRect &rect, + int left = 0, int top = 0, int right = 0, + int bottom = 0); +#ifndef QT_NO_ACCESSIBILITY + bool isInstanceOf(QObject *obj, QAccessible::Role role); + bool hasAncestor(QObject *obj, QAccessible::Role role); +#endif +} + + +QT_END_NAMESPACE + +#endif // QSTYLEHELPER_P_H diff --git a/src/qt5gtk2-style/qt5gtk2-style.pro b/src/qt5gtk2-style/qt5gtk2-style.pro new file mode 100644 index 0000000..a529880 --- /dev/null +++ b/src/qt5gtk2-style/qt5gtk2-style.pro @@ -0,0 +1,26 @@ +include(../../qt5gtk2.pri) + +TEMPLATE = lib +TARGET = qt5gtk2-style +QT += core-private gui-private widgets-private + +DEFINES += QT_NO_ANIMATION + +# Input +HEADERS += qgtk2painter_p.h \ + qgtkglobal_p.h \ + qgtkpainter_p.h \ + qgtkstyle_p.h \ + qgtkstyle_p_p.h \ + qstylehelper_p.h +SOURCES += qgtk2painter.cpp qgtkpainter.cpp qgtkstyle.cpp qgtkstyle_p.cpp \ + plugin.cpp \ + qstylehelper.cpp + +CONFIG += plugin \ + link_pkgconfig \ + +PKGCONFIG += gtk+-2.0 x11 + +target.path = $$PLUGINDIR/styles +INSTALLS += target diff --git a/src/qt5gtk2-style/qt5gtk2.json b/src/qt5gtk2-style/qt5gtk2.json new file mode 100644 index 0000000..cb1b4b1 --- /dev/null +++ b/src/qt5gtk2-style/qt5gtk2.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "qt5gtk2" ] +} |
