/* * Copyright (C) 2016 Alberts Muktupāvels * * 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, see . */ #include #include "sn-dbus-menu.h" #include "sn-item.h" #include "na-item.h" struct _SnItemPrivate { gchar *bus_name; gchar *object_path; GtkOrientation orientation; GtkMenu *menu; }; enum { PROP_0, PROP_BUS_NAME, PROP_OBJECT_PATH, PROP_ORIENTATION, LAST_PROP }; enum { SIGNAL_READY, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static void na_item_init (NaItemInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (SnItem, sn_item, SN_TYPE_FLAT_BUTTON, G_ADD_PRIVATE (SnItem) G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL) G_IMPLEMENT_INTERFACE (NA_TYPE_ITEM, na_item_init)) static void sn_item_dispose (GObject *object) { SnItem *item; SnItemPrivate *priv; item = SN_ITEM (object); priv = SN_ITEM (item)->priv; g_clear_object (&priv->menu); G_OBJECT_CLASS (sn_item_parent_class)->dispose (object); } static void sn_item_finalize (GObject *object) { SnItem *item; SnItemPrivate *priv; item = SN_ITEM (object); priv = SN_ITEM (item)->priv; g_clear_pointer (&priv->bus_name, g_free); g_clear_pointer (&priv->object_path, g_free); G_OBJECT_CLASS (sn_item_parent_class)->finalize (object); } static void sn_item_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { SnItem *item; SnItemPrivate *priv; item = SN_ITEM (object); priv = SN_ITEM (item)->priv; switch (property_id) { case PROP_BUS_NAME: g_value_set_string (value, priv->bus_name); break; case PROP_OBJECT_PATH: g_value_set_string (value, priv->object_path); break; case PROP_ORIENTATION: g_value_set_enum (value, priv->orientation); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void sn_item_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { SnItem *item; SnItemPrivate *priv; item = SN_ITEM (object); priv = SN_ITEM (item)->priv; switch (property_id) { case PROP_BUS_NAME: priv->bus_name = g_value_dup_string (value); break; case PROP_OBJECT_PATH: priv->object_path = g_value_dup_string (value); break; case PROP_ORIENTATION: priv->orientation = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void sn_item_get_action_coordinates (SnItem *item, gint *x, gint *y) { GtkWidget *widget; SnItemPrivate *priv; GdkWindow *window; GtkWidget *toplevel; gint width; gint height; priv = SN_ITEM (item)->priv; widget = GTK_WIDGET (item); window = gtk_widget_get_window (widget); toplevel = gtk_widget_get_toplevel (widget); gdk_window_get_geometry (window, x, y, &width, &height); gtk_widget_translate_coordinates (widget, toplevel, *x, *y, x, y); if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) *y += height; else *x += width; } static gboolean sn_item_button_press_event (GtkWidget *widget, GdkEventButton *event) { SnItem *item; SnItemPrivate *priv; gint x; gint y; if (event->button < 2 || event->button > 3) return GTK_WIDGET_CLASS (sn_item_parent_class)->button_press_event (widget, event); item = SN_ITEM (widget); priv = SN_ITEM (item)->priv; sn_item_get_action_coordinates (item, &x, &y); if (event->button == 2) { gdk_seat_ungrab (gdk_device_get_seat (event->device)); SN_ITEM_GET_CLASS (item)->secondary_activate (item, x, y); } else if (event->button == 3) { if (priv->menu != NULL) { gtk_menu_popup_at_widget (priv->menu, widget, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, (GdkEvent *) event); /*Fix positioning if size changed since last shown*/ gtk_menu_reposition(priv->menu); } else { gdk_seat_ungrab (gdk_device_get_seat (event->device)); SN_ITEM_GET_CLASS (item)->context_menu (item, x, y); } } else { g_assert_not_reached (); } return GTK_WIDGET_CLASS (sn_item_parent_class)->button_press_event (widget, event); } static gboolean sn_item_popup_menu (GtkWidget *widget) { SnItem *item; SnItemPrivate *priv; item = SN_ITEM (widget); priv = SN_ITEM (item)->priv; if (priv->menu != NULL) { gtk_menu_popup_at_widget (priv->menu, widget, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL); /*Fix positioning if size changed since last shown*/ gtk_menu_reposition(priv->menu); } else { gint x; gint y; sn_item_get_action_coordinates (item, &x, &y); SN_ITEM_GET_CLASS (item)->context_menu (item, x, y); } return TRUE; } static void sn_item_clicked (GtkButton *button) { SnItem *item; gint x; gint y; item = SN_ITEM (button); sn_item_get_action_coordinates (item, &x, &y); SN_ITEM_GET_CLASS (item)->activate (item, x, y); } static gboolean sn_item_scroll_event (GtkWidget *widget, GdkEventScroll *event) { SnItem *item; GdkScrollDirection direction; SnItemOrientation orientation; gdouble dx; gdouble dy; gint delta; item = SN_ITEM (widget); if (!gdk_event_get_scroll_direction ((GdkEvent *) event, &direction)) { g_assert_not_reached (); } else { switch (direction) { case GDK_SCROLL_UP: case GDK_SCROLL_DOWN: orientation = SN_ITEM_ORIENTATION_VERTICAL; break; case GDK_SCROLL_LEFT: case GDK_SCROLL_RIGHT: orientation = SN_ITEM_ORIENTATION_HORIZONTAL; break; case GDK_SCROLL_SMOOTH: default: g_assert_not_reached (); break; } } if (!gdk_event_get_scroll_deltas ((GdkEvent *) event, &dx, &dy)) { switch (direction) { case GDK_SCROLL_UP: case GDK_SCROLL_LEFT: delta = 1; break; case GDK_SCROLL_DOWN: case GDK_SCROLL_RIGHT: delta = -1; break; case GDK_SCROLL_SMOOTH: default: g_assert_not_reached (); break; } } else { if (dy != 0) delta = (gint) dy; else delta = (gint) dx; } SN_ITEM_GET_CLASS (item)->scroll (item, delta, orientation); return GDK_EVENT_STOP; } static void sn_item_ready (SnItem *item) { const gchar *menu; SnItemPrivate *priv; menu = SN_ITEM_GET_CLASS (item)->get_menu (item); if (menu == NULL || *menu == '\0' || g_strcmp0 (menu, "/") == 0) return; priv = SN_ITEM (item)->priv; priv->menu = sn_dbus_menu_new (priv->bus_name, menu); g_object_ref_sink (priv->menu); gtk_button_set_always_show_image (GTK_BUTTON (item),TRUE); } static const gchar * sn_item_get_id (NaItem *item) { return SN_ITEM_GET_CLASS (item)->get_id (SN_ITEM (item)); } static NaItemCategory sn_item_get_category (NaItem *item) { const gchar *string; NaItemCategory category; string = SN_ITEM_GET_CLASS (item)->get_category (SN_ITEM (item)); if (g_strcmp0 (string, "Hardware") == 0) category = NA_ITEM_CATEGORY_HARDWARE; else if (g_strcmp0 (string, "SystemServices") == 0) category = NA_ITEM_CATEGORY_SYSTEM_SERVICES; else if (g_strcmp0 (string, "Communications") == 0) category = NA_ITEM_CATEGORY_COMMUNICATIONS; else category = NA_ITEM_CATEGORY_APPLICATION_STATUS; return category; } static void na_item_init (NaItemInterface *iface) { iface->get_id = sn_item_get_id; iface->get_category = sn_item_get_category; } static void install_properties (GObjectClass *object_class) { g_object_class_install_property (object_class, PROP_BUS_NAME, g_param_spec_string ("bus-name", "bus-name", "bus-name", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_OBJECT_PATH, g_param_spec_string ("object-path", "object-path", "object-path", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_override_property (object_class, PROP_ORIENTATION, "orientation"); } static void install_signals (SnItemClass *item_class) { signals[SIGNAL_READY] = g_signal_new ("ready", G_TYPE_FROM_CLASS (item_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SnItemClass, ready), NULL, NULL, NULL, G_TYPE_NONE, 0); } static void sn_item_class_init (SnItemClass *item_class) { GObjectClass *object_class; GtkWidgetClass *widget_class; GtkButtonClass *button_class; object_class = G_OBJECT_CLASS (item_class); widget_class = GTK_WIDGET_CLASS (item_class); button_class = GTK_BUTTON_CLASS (item_class); object_class->dispose = sn_item_dispose; object_class->finalize = sn_item_finalize; object_class->get_property = sn_item_get_property; object_class->set_property = sn_item_set_property; widget_class->button_press_event = sn_item_button_press_event; widget_class->popup_menu = sn_item_popup_menu; widget_class->scroll_event = sn_item_scroll_event; button_class->clicked = sn_item_clicked; item_class->ready = sn_item_ready; install_properties (object_class); install_signals (item_class); } static void sn_item_init (SnItem *item) { item->priv = sn_item_get_instance_private (item); gtk_widget_add_events (GTK_WIDGET (item), GDK_SCROLL_MASK); } const gchar * sn_item_get_bus_name (SnItem *item) { SnItemPrivate *priv; priv = SN_ITEM (item)->priv; return priv->bus_name; } const gchar * sn_item_get_object_path (SnItem *item) { SnItemPrivate *priv; priv = SN_ITEM (item)->priv; return priv->object_path; } void sn_item_emit_ready (SnItem *item) { g_signal_emit (item, signals[SIGNAL_READY], 0); }