diff options
author | Javier <dev.git@javispedro.com> | 2014-01-28 17:21:16 +0100 |
---|---|---|
committer | Javier <dev.git@javispedro.com> | 2014-01-28 17:21:16 +0100 |
commit | 982ebc96f2c77c1f9dbddd6a4c0a776c74b425de (patch) | |
tree | 6800326ba4da9c84dc14f0b442f088b1c2fb63bf /module/menuproxy.cc | |
download | topmenu-qt-982ebc96f2c77c1f9dbddd6a4c0a776c74b425de.tar.gz topmenu-qt-982ebc96f2c77c1f9dbddd6a4c0a776c74b425de.zip |
initial import
Diffstat (limited to 'module/menuproxy.cc')
-rw-r--r-- | module/menuproxy.cc | 286 |
1 files changed, 286 insertions, 0 deletions
diff --git a/module/menuproxy.cc b/module/menuproxy.cc new file mode 100644 index 0000000..5536bf5 --- /dev/null +++ b/module/menuproxy.cc @@ -0,0 +1,286 @@ +#include <gtk/gtk.h> +#include <QtCore/QDebug> +#include <QtGui/QActionEvent> + +#include "menuproxy.h" +#include "qtkeysyms.h" + +static inline int index_of_menu_item(GtkMenuShell *shell, GtkMenuItem *item) +{ + return g_list_index(shell->children, item); +} + +static inline GtkMenuItem * get_nth_menu_item(GtkMenuShell *shell, int i) +{ + return GTK_MENU_ITEM(g_list_nth_data(shell->children, i)); +} + +static bool transform_key_sequence(const QKeySequence &keys, guint *keyp, GdkModifierType *modsp) +{ + uint count = keys.count(); + if (count != 1) return false; // TODO + + guint mods = 0; + guint key = 0; + + for (uint i = 0; i < count; i++) { + const uint qt_key = keys[i]; + if (qt_key & Qt::CTRL) mods |= GDK_CONTROL_MASK; + if (qt_key & Qt::ALT) mods |= GDK_MOD1_MASK; + if (qt_key & Qt::SHIFT) mods |= GDK_SHIFT_MASK; + + key = qt_to_gdk_key(qt_key & ~Qt::MODIFIER_MASK); + } + + *keyp = key; + *modsp = GdkModifierType(mods); + + return true; +} + +static void destroy_image_data(guchar *pixels, gpointer data) +{ + Q_UNUSED(data); + g_free(pixels); +} + +MenuProxy::MenuProxy(QObject *parent) + : QObject(parent), m_target(0), m_accel(0) +{ +} + +MenuProxy::~MenuProxy() +{ + Q_FOREACH(GtkMenu *menu, m_menus) { + gtk_widget_destroy(GTK_WIDGET(menu)); + } + Q_FOREACH(GtkMenuItem *item, m_items) { + gtk_widget_destroy(GTK_WIDGET(item)); + } + if (m_accel) { + g_object_unref(m_accel); + m_accel = 0; + } +} + +void MenuProxy::setTargetMenu(GtkMenuShell *shell) +{ + m_target = shell; + if (!m_accel) { + m_accel = gtk_accel_group_new(); + } +} + +GtkMenuItem * MenuProxy::addAction(QAction* action, QAction* before, QMenu* parent) +{ + GtkMenuShell *g_parent; + if (parent) { + g_parent = GTK_MENU_SHELL(m_menus.value(parent)); + } else { + g_parent = m_target; + } + + GtkMenuItem *item; + if (action->isSeparator()) { + item = GTK_MENU_ITEM(gtk_separator_menu_item_new()); + } else if (action->isCheckable()) { + QString label = transformMnemonic(action->text()); + item = GTK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(label.toUtf8().constData())); + if (action->actionGroup() && action->actionGroup()->isExclusive()) { + gtk_check_menu_item_set_draw_as_radio(GTK_CHECK_MENU_ITEM(item), TRUE); + } + } else { + QString label = transformMnemonic(action->text()); + QIcon icon = action->icon(); + if (!icon.isNull()) { + QImage image = icon.pixmap(16, 16).toImage().convertToFormat(QImage::Format_ARGB32).rgbSwapped(); + gsize size = image.byteCount(); + guchar *data = (guchar*) g_malloc(size); + memcpy(data, image.constBits(), size); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, + image.hasAlphaChannel(), 8, + image.width(), image.height(), + image.bytesPerLine(), + destroy_image_data, NULL); + item = GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(label.toUtf8().constData())); + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), + gtk_image_new_from_pixbuf(pixbuf)); + g_object_unref(pixbuf); + } else { + item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(label.toUtf8().constData())); + } + } + + m_items.insert(action, item); + g_object_ref_sink(item); + + if (before) { + GtkMenuItem *g_before = m_items.value(before); + Q_ASSERT(g_before); + gint position = index_of_menu_item(g_parent, g_before); + gtk_menu_shell_insert(g_parent, GTK_WIDGET(item), position); + } else { + gtk_menu_shell_append(g_parent, GTK_WIDGET(item)); + } + + QMenu *menu = action->menu(); + if (menu) { + GtkMenu * g_menu = addMenu(menu); + gtk_menu_item_set_submenu(item, GTK_WIDGET(g_menu)); + } + + updateAction(action); + + return item; +} + +GtkMenu * MenuProxy::addMenu(QMenu *menu) +{ + menu->installEventFilter(this); + + GtkMenu *g_menu = GTK_MENU(gtk_menu_new()); + + m_menus.insert(menu, g_menu); + g_object_ref_sink(g_menu); + + // Items might have been added already + foreach (QAction *action, menu->actions()) { + addAction(action, 0, menu); + } + + return g_menu; +} + +void MenuProxy::removeAction(QAction *action) +{ + QMenu* menu = action->menu(); + + if (menu) { + removeMenu(menu); + } + + GtkMenuItem *item = m_items.value(action); + if (!item) { + return; + } + + gtk_widget_destroy(GTK_WIDGET(item)); + m_items.remove(action); +} + +void MenuProxy::removeMenu(QMenu *menu) +{ + GtkMenu *g_menu = m_menus.value(menu); + Q_ASSERT(g_menu); + GtkWidget *g_item = gtk_menu_get_attach_widget(g_menu); + Q_ASSERT(g_item); + GtkMenu *g_parent = GTK_MENU(gtk_widget_get_parent(g_item)); + Q_ASSERT(g_parent), + + menu->removeEventFilter(this); + + gtk_widget_destroy(GTK_WIDGET(g_menu)); + m_menus.remove(menu); +} + +void MenuProxy::updateAction(QAction *action) +{ + GtkMenuItem *item = m_items.value(action); + Q_ASSERT(item); + + g_signal_handlers_disconnect_by_data(item, action); + + if (action->isSeparator()) { + // TODO Decide visibility of separators using Qt rules. + gtk_widget_set_visible(GTK_WIDGET(item), action->isVisible()); + } else { + QString label = action->text().replace(QChar('&'), QChar('_')); + gtk_menu_item_set_label(item, label.toUtf8().constData()); + + if (GTK_IS_CHECK_MENU_ITEM(item)) { + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), + action->isChecked()); + } + + GtkWidget *child = gtk_bin_get_child(GTK_BIN(item)); + if (GTK_IS_ACCEL_LABEL(child)) { + Q_FOREACH(const QKeySequence& shortcut, action->shortcuts()) { + guint key; + GdkModifierType mods; + if (transform_key_sequence(shortcut, &key, &mods)) { + gtk_widget_add_accelerator(GTK_WIDGET(item), "activate", m_accel, + key, mods, + GTK_ACCEL_VISIBLE); + } + } + } + + gtk_widget_set_sensitive(GTK_WIDGET(item), action->isEnabled()); + gtk_widget_set_visible(GTK_WIDGET(item), action->isVisible()); + } + + g_signal_connect(item, "activate", + G_CALLBACK(handleMenuItemActivated), action); + g_signal_connect(item, "select", + G_CALLBACK(handleMenuItemSelected), action); + g_signal_connect(item, "deselect", + G_CALLBACK(handleMenuItemDeselected), action); +} + +GtkMenuItem *MenuProxy::getItemForAction(QAction *action) +{ + return m_items.value(action, 0); +} + +QString MenuProxy::transformMnemonic(const QString &text) +{ + QString s(text); + return s.replace(QChar('&'), QChar('_')); +} + +bool MenuProxy::eventFilter(QObject* src, QEvent* event) +{ + QActionEvent *actionEvent; + switch (event->type()) { + case QEvent::ActionAdded: + actionEvent = static_cast<QActionEvent *>(event); + addAction(actionEvent->action(), actionEvent->before(), static_cast<QMenu*>(src)); + break; + case QEvent::ActionRemoved: + actionEvent = static_cast<QActionEvent *>(event); + removeAction(actionEvent->action()); + break; + case QEvent::ActionChanged: + actionEvent = static_cast<QActionEvent *>(event); + updateAction(actionEvent->action()); + break; + default: + break; + } + return false; +} + +void MenuProxy::handleMenuItemActivated(GtkMenuItem *item, QAction *action) +{ + Q_UNUSED(item); + action->activate(QAction::Trigger); +} + +void MenuProxy::handleMenuItemSelected(GtkMenuItem *item, QAction *action) +{ + Q_UNUSED(item); + action->activate(QAction::Hover); + QMenu *submenu = action->menu(); + if (submenu) { + QMetaObject::invokeMethod(submenu, "aboutToShow"); + } +} + +void MenuProxy::handleMenuItemDeselected(GtkMenuItem *item, QAction *action) +{ + Q_UNUSED(item); + QMenu *submenu = action->menu(); + if (submenu) { + QMetaObject::invokeMethod(submenu, "aboutToHide"); + } +} |