File indexing completed on 2024-04-21 05:46:58

0001 /*****************************************************************************
0002  *   Copyright 2003 - 2010 Craig Drummond <craig.p.drummond@gmail.com>       *
0003  *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
0004  *                                                                           *
0005  *   This program is free software; you can redistribute it and/or modify    *
0006  *   it under the terms of the GNU Lesser General Public License as          *
0007  *   published by the Free Software Foundation; either version 2.1 of the    *
0008  *   License, or (at your option) version 3, or any later version accepted   *
0009  *   by the membership of KDE e.V. (or its successor approved by the         *
0010  *   membership of KDE e.V.), which shall act as a proxy defined in          *
0011  *   Section 6 of version 3 of the license.                                  *
0012  *                                                                           *
0013  *   This program is distributed in the hope that it will be useful,         *
0014  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0015  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0016  *   Lesser General Public License for more details.                         *
0017  *                                                                           *
0018  *   You should have received a copy of the GNU Lesser General Public        *
0019  *   License along with this library. If not,                                *
0020  *   see <http://www.gnu.org/licenses/>.                                     *
0021  *****************************************************************************/
0022 
0023 #include "menu.h"
0024 
0025 #include <qtcurve-utils/gtkprops.h>
0026 #include <qtcurve-utils/x11qtc.h>
0027 #include <qtcurve-cairo/utils.h>
0028 
0029 #include <gdk/gdkx.h>
0030 #include <common/common.h>
0031 
0032 namespace QtCurve {
0033 namespace Menu {
0034 
0035 #define EXTEND_MENUBAR_ITEM_HACK
0036 
0037 #ifdef EXTEND_MENUBAR_ITEM_HACK
0038 static bool
0039 isSelectable(GtkWidget *menu)
0040 {
0041     return !((!gtk_bin_get_child(GTK_BIN(menu)) &&
0042               G_OBJECT_TYPE(menu) == GTK_TYPE_MENU_ITEM) ||
0043              GTK_IS_SEPARATOR_MENU_ITEM(menu) ||
0044              !gtk_widget_is_sensitive(menu) ||
0045              !gtk_widget_get_visible(menu));
0046 }
0047 
0048 static gboolean
0049 shellButtonPress(GtkWidget *widget, GdkEventButton *event, void*)
0050 {
0051     if (GTK_IS_MENU_BAR(widget)) {
0052         // QtCurve's menubars have a 2 pixel border ->
0053         // but want the left/top to be 'active'...
0054         int nx, ny;
0055         gdk_window_get_origin(gtk_widget_get_window(widget), &nx, &ny);
0056         if ((event->x_root - nx) <= 2.0 || (event->y_root - ny) <= 2.0) {
0057             if ((event->x_root - nx) <= 2.0) {
0058                 event->x_root += 2.0;
0059             }
0060             if ((event->y_root - ny) <= 2.0) {
0061                 event->y_root += 2.0;
0062             }
0063 
0064             GtkMenuShell *menuShell = GTK_MENU_SHELL(widget);
0065             GList *children =
0066                 gtk_container_get_children(GTK_CONTAINER(menuShell));
0067             bool rv = false;
0068             for (GList *child = children;child;child = child->next) {
0069                 GtkWidget *item = (GtkWidget*)child->data;
0070                 QtcRect alloc = Widget::getAllocation(item);
0071                 int cx = alloc.x + nx;
0072                 int cy = alloc.y + ny;
0073                 int cw = alloc.width;
0074                 int ch = alloc.height;
0075                 if (cx <= event->x_root && cy <= event->y_root &&
0076                     (cx + cw) > event->x_root && (cy + ch) > event->y_root) {
0077                     if (isSelectable(item)) {
0078                         if (event->type == GDK_BUTTON_PRESS) {
0079                             if (item != menuShell->active_menu_item) {
0080                                 menuShell->active = false;
0081                                 gtk_menu_shell_select_item(menuShell, item);
0082                                 menuShell->active = true;
0083                             } else {
0084                                 menuShell->active = true;
0085                                 gtk_menu_shell_deselect(menuShell);
0086                                 menuShell->active = false;
0087                             }
0088                         }
0089                         rv = true;
0090                     }
0091                     break;
0092                 }
0093             }
0094             if (children) {
0095                 g_list_free(children);
0096             }
0097             return rv;
0098         }
0099     }
0100     return false;
0101 }
0102 #endif
0103 
0104 static void
0105 shellCleanup(GtkWidget *widget)
0106 {
0107     if (GTK_IS_MENU_BAR(widget)) {
0108         GtkWidgetProps props(widget);
0109         props->menuShellMotion.disconn();
0110         props->menuShellLeave.disconn();
0111         props->menuShellDestroy.disconn();
0112         props->menuShellStyleSet.disconn();
0113 #ifdef EXTEND_MENUBAR_ITEM_HACK
0114         props->menuShellButtonPress.disconn();
0115         props->menuShellButtonRelease.disconn();
0116 #endif
0117         props->menuShellHacked = true;
0118     }
0119 }
0120 
0121 static gboolean
0122 shellStyleSet(GtkWidget *widget, GtkStyle*, void*)
0123 {
0124     shellCleanup(widget);
0125     return false;
0126 }
0127 
0128 static gboolean
0129 shellDestroy(GtkWidget *widget, GdkEvent*, void*)
0130 {
0131     shellCleanup(widget);
0132     return false;
0133 }
0134 
0135 static gboolean
0136 shellMotion(GtkWidget *widget, GdkEventMotion*, void*)
0137 {
0138     if (GTK_IS_MENU_SHELL(widget)) {
0139         int pointer_x, pointer_y;
0140         GdkModifierType pointer_mask;
0141 
0142         gdk_window_get_pointer(gtk_widget_get_window(widget), &pointer_x,
0143                                &pointer_y, &pointer_mask);
0144 
0145         if (GTK_IS_CONTAINER(widget)) {
0146             GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
0147             for (GList *child = children;child;child = g_list_next(child)) {
0148                 if ((child->data) && GTK_IS_WIDGET(child->data) &&
0149                     (gtk_widget_get_state(GTK_WIDGET(child->data)) !=
0150                      GTK_STATE_INSENSITIVE)) {
0151                     QtcRect alloc =
0152                         Widget::getAllocation(GTK_WIDGET(child->data));
0153 
0154                     if ((pointer_x >= alloc.x) && (pointer_y >= alloc.y) &&
0155                         (pointer_x < (alloc.x + alloc.width)) &&
0156                         (pointer_y < (alloc.y + alloc.height))) {
0157                         gtk_widget_set_state(GTK_WIDGET(child->data),
0158                                              GTK_STATE_PRELIGHT);
0159                     } else {
0160                         gtk_widget_set_state(GTK_WIDGET(child->data),
0161                                              GTK_STATE_NORMAL);
0162                     }
0163                 }
0164             }
0165             if (children) {
0166                 g_list_free(children);
0167             }
0168         }
0169     }
0170 
0171     return false;
0172 }
0173 
0174 static gboolean
0175 shellLeave(GtkWidget *widget, GdkEventCrossing*, void*)
0176 {
0177     if (GTK_IS_MENU_SHELL(widget) && GTK_IS_CONTAINER(widget)) {
0178         GList *children = gtk_container_get_children(GTK_CONTAINER(widget));
0179         for (GList *child = children;child;child = g_list_next(child)) {
0180             if ((child->data) && GTK_IS_MENU_ITEM(child->data) &&
0181                 (gtk_widget_get_state(GTK_WIDGET(child->data)) !=
0182                  GTK_STATE_INSENSITIVE)) {
0183                 GtkWidget *submenu =
0184                     gtk_menu_item_get_submenu(GTK_MENU_ITEM(child->data));
0185                 GtkWidget *topLevel =
0186                     submenu ? gtk_widget_get_toplevel(submenu) : nullptr;
0187 
0188                 if (submenu &&
0189                     ((!GTK_IS_MENU(submenu)) ||
0190                      (!(gtk_widget_get_realized(submenu) &&
0191                         gtk_widget_get_visible(submenu) &&
0192                         gtk_widget_get_realized(topLevel) &&
0193                         gtk_widget_get_visible(topLevel))))) {
0194                     gtk_widget_set_state(GTK_WIDGET(child->data),
0195                                          GTK_STATE_NORMAL);
0196                 }
0197             }
0198         }
0199         if (children) {
0200             g_list_free(children);
0201         }
0202     }
0203     return false;
0204 }
0205 
0206 void
0207 shellSetup(GtkWidget *widget)
0208 {
0209     GtkWidgetProps props(widget);
0210     if (GTK_IS_MENU_BAR(widget) && !props->menuShellHacked) {
0211         props->menuShellHacked = true;
0212         props->menuShellMotion.conn("motion-notify-event", shellMotion);
0213         props->menuShellLeave.conn("leave-notify-event", shellLeave);
0214         props->menuShellDestroy.conn("destroy-event", shellDestroy);
0215         props->menuShellStyleSet.conn("style-set", shellStyleSet);
0216 #ifdef EXTEND_MENUBAR_ITEM_HACK
0217         props->menuShellButtonPress.conn("button-press-event",
0218                                          shellButtonPress);
0219         props->menuShellButtonRelease.conn("button-release-event",
0220                                            shellButtonPress);
0221 #endif
0222     }
0223 }
0224 
0225 bool
0226 emitSize(GtkWidget *w, unsigned size)
0227 {
0228     if (w) {
0229         GtkWidgetProps props(w);
0230         unsigned oldSize = props->menuBarSize;
0231 
0232         if (oldSize != size) {
0233             GtkWidget *topLevel = gtk_widget_get_toplevel(w);
0234             xcb_window_t wid =
0235                 GDK_WINDOW_XID(gtk_widget_get_window(GTK_WIDGET(topLevel)));
0236 
0237             if (size == 0xFFFF) {
0238                 size = 0;
0239             }
0240             props->menuBarSize = size;
0241             qtcX11SetMenubarSize(wid, size);
0242             return true;
0243         }
0244     }
0245     return false;
0246 }
0247 
0248 }
0249 }