diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | example/menus.c | 11 | ||||
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/idolevelmenuitem.c | 309 | ||||
-rw-r--r-- | src/idolevelmenuitem.h | 52 | ||||
-rw-r--r-- | src/idomenuitemfactory.c | 6 |
6 files changed, 385 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ceccb83..df0b8bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,11 @@ else() add_definitions("-Wall") endif() +# Thread fix for old CMake + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") + # Check for prerequisites set(DEPS glib-2.0>=2.58 gtk+-3.0>=3.24) diff --git a/example/menus.c b/example/menus.c index 1786697..eff6a9f 100644 --- a/example/menus.c +++ b/example/menus.c @@ -7,6 +7,7 @@ #include "idoswitchmenuitem.h" #include "idousermenuitem.h" #include "idoremovablemenuitem.h" +#include "idolevelmenuitem.h" static void slider_grabbed (GtkWidget *widget, gpointer user_data) @@ -150,6 +151,16 @@ main (int argc, char *argv[]) idoRemovableMenuItemUseMarkup(IDO_REMOVABLE_MENU_ITEM(menuitem), TRUE); gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new ()); + + /* Level */ + menuitem = ido_level_menu_item_new(); + idoLevelMenuItemSetText (IDO_LEVEL_MENU_ITEM (menuitem), "Some battery level"); + GIcon *pIcon = g_themed_icon_new_with_default_fallbacks ("battery"); + idoLevelMenuItemSetIcon (IDO_LEVEL_MENU_ITEM (menuitem), pIcon); + idoLevelMenuItemSetLevel (IDO_LEVEL_MENU_ITEM (menuitem), 50); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); + /* Add the menubar */ gtk_menu_shell_append (GTK_MENU_SHELL (menubar), root); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1f03954..670cc71 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,7 @@ set(HEADERS idoscalemenuitem.h idodetaillabel.h idoentrymenuitem.h + idolevelmenuitem.h ) set(SOURCES @@ -65,6 +66,7 @@ set(SOURCES idousermenuitem.c idosourcemenuitem.c idotimeline.c + idolevelmenuitem.c ${CMAKE_CURRENT_BINARY_DIR}/idotypebuiltins.c ) diff --git a/src/idolevelmenuitem.c b/src/idolevelmenuitem.c new file mode 100644 index 0000000..b7ce543 --- /dev/null +++ b/src/idolevelmenuitem.c @@ -0,0 +1,309 @@ +/* + * Copyright 2023 Robert Tari + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <http://www.gnu.org/licenses/>. + * + * Author: Robert Tari <robert@tari.in> + */ + +#include "idolevelmenuitem.h" +#include "idoactionhelper.h" + +enum +{ + PROP_0, + PROP_ICON, + PROP_TEXT, + PROP_LEVEL, + PROP_LAST +}; + +static GParamSpec *lProperties[PROP_LAST]; + +typedef struct +{ + GIcon *pIcon; + gchar *sText; + GtkWidget *pLabel; + GtkWidget *pImage; + GtkWidget *pLevelBar; + IdoActionHelper *pHelper; + +} IdoLevelMenuItemPrivate; + +static GParamSpec *lProperties[PROP_LAST]; + +G_DEFINE_TYPE_WITH_PRIVATE (IdoLevelMenuItem, ido_level_menu_item, GTK_TYPE_MENU_ITEM) + +static void onGetProperty (GObject *pObject, guint nProperty, GValue *pValue, GParamSpec *pParamSpec) +{ + IdoLevelMenuItem *self = IDO_LEVEL_MENU_ITEM (pObject); + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + switch (nProperty) + { + case PROP_ICON: + { + g_value_set_object (pValue, pPrivate->pIcon); + + break; + } + case PROP_TEXT: + { + g_value_set_string (pValue, pPrivate->sText); + + break; + } + case PROP_LEVEL: + { + guint16 nLevel = (guint16) gtk_level_bar_get_value (GTK_LEVEL_BAR (pPrivate->pLevelBar)); + g_value_set_uint (pValue, (guint16) nLevel); + + break; + } + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (pObject, nProperty, pParamSpec); + + break; + } + } +} + +static void onSetProperty (GObject *pObject, guint nProperty, const GValue *pValue, GParamSpec *pParamSpec) +{ + IdoLevelMenuItem *self = IDO_LEVEL_MENU_ITEM(pObject); + + switch (nProperty) + { + case PROP_ICON: + { + idoLevelMenuItemSetIcon (self, g_value_get_object (pValue)); + + break; + } + case PROP_TEXT: + { + idoLevelMenuItemSetText (self, g_value_get_string (pValue)); + + break; + } + case PROP_LEVEL: + { + idoLevelMenuItemSetLevel (self, g_value_get_uint (pValue)); + + break; + } + default: + { + G_OBJECT_WARN_INVALID_PROPERTY_ID (pObject, nProperty, pParamSpec); + + break; + } + } +} + +static void onDispose (GObject *pObject) +{ + IdoLevelMenuItem *self = IDO_LEVEL_MENU_ITEM (pObject); + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + g_clear_object (&pPrivate->pIcon); + G_OBJECT_CLASS (ido_level_menu_item_parent_class)->dispose(pObject); +} + +static void onFinalize(GObject *pObject) +{ + IdoLevelMenuItem *self = IDO_LEVEL_MENU_ITEM (pObject); + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + g_free (pPrivate->sText); + G_OBJECT_CLASS (ido_level_menu_item_parent_class)->finalize (pObject); +} + +static void idoLevelMenuItemStyleUpdateImage (IdoLevelMenuItem *self) +{ + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + gtk_image_clear (GTK_IMAGE (pPrivate->pImage)); + + if (pPrivate->pIcon == NULL) + { + gtk_widget_set_visible (pPrivate->pImage, FALSE); + } + else + { + GtkIconInfo *pInfo; + const gchar *sFilename; + + pInfo = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default(), pPrivate->pIcon, 16, 0); + sFilename = gtk_icon_info_get_filename (pInfo); + + if (sFilename) + { + GdkPixbuf *pPixbuf; + + pPixbuf = gdk_pixbuf_new_from_file_at_scale (sFilename, -1, 16, TRUE, NULL); + gtk_image_set_from_pixbuf (GTK_IMAGE (pPrivate->pImage), pPixbuf); + g_object_unref (pPixbuf); + } + + gtk_widget_set_visible (pPrivate->pImage, sFilename != NULL); + g_object_unref (pInfo); + } +} + +static void onStyleUpdated (GtkWidget *pWidget) +{ + GTK_WIDGET_CLASS (ido_level_menu_item_parent_class)->style_updated (pWidget); + idoLevelMenuItemStyleUpdateImage (IDO_LEVEL_MENU_ITEM (pWidget)); + gtk_widget_queue_draw (pWidget); +} + +static void onActivate (GtkMenuItem *item, gpointer pData) +{ + IdoActionHelper *pHelper = pData; + ido_action_helper_activate (pHelper); +} + +static void ido_level_menu_item_class_init (IdoLevelMenuItemClass *klass) +{ + GObjectClass *pObject = G_OBJECT_CLASS (klass); + GtkWidgetClass *pWidget = GTK_WIDGET_CLASS (klass); + pObject->get_property = onGetProperty; + pObject->set_property = onSetProperty; + pObject->dispose = onDispose; + pObject->finalize = onFinalize; + pWidget->style_updated = onStyleUpdated; + + GParamFlags nParamFlags = G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS; + lProperties[PROP_ICON] = g_param_spec_object ("icon", "Icon", "The menuitem's GIcon", G_TYPE_OBJECT, nParamFlags); + lProperties[PROP_TEXT] = g_param_spec_string ("text", "Text", "The menuitem's text", "", nParamFlags); + lProperties[PROP_LEVEL] = g_param_spec_uint ("level", "Level", "The menuitem's level bar value", 0, 100, 0, nParamFlags); + + g_object_class_install_properties(pObject, PROP_LAST, lProperties); +} + +static void ido_level_menu_item_init (IdoLevelMenuItem *self) +{ + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + pPrivate->pHelper = NULL; + pPrivate->pImage = gtk_image_new(); + pPrivate->pLabel = gtk_label_new(""); + pPrivate->pLevelBar = gtk_level_bar_new_for_interval (0.0, 100.0); + gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (pPrivate->pLevelBar), GTK_LEVEL_BAR_OFFSET_LOW, 20.0); + gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (pPrivate->pLevelBar), GTK_LEVEL_BAR_OFFSET_HIGH, 90.0); + gtk_level_bar_add_offset_value (GTK_LEVEL_BAR (pPrivate->pLevelBar), GTK_LEVEL_BAR_OFFSET_FULL, 100.0); + + GtkWidget *pGrid = gtk_grid_new (); + gtk_grid_attach (GTK_GRID (pGrid), pPrivate->pImage, 0, 0, 1, 2); + gtk_grid_attach (GTK_GRID (pGrid), pPrivate->pLabel, 1, 0, 1, 1); + gtk_grid_attach (GTK_GRID (pGrid), pPrivate->pLevelBar, 1, 1, 1, 1); + + g_object_set (pPrivate->pImage, "halign", GTK_ALIGN_START, "hexpand", FALSE, "valign", GTK_ALIGN_START, "margin-right", 6, NULL); + g_object_set (pPrivate->pLabel, "halign", GTK_ALIGN_START, "hexpand", TRUE, "valign", GTK_ALIGN_CENTER, NULL); + + gtk_widget_show_all (pGrid); + gtk_container_add (GTK_CONTAINER(self), pGrid); +} + +GtkWidget* ido_level_menu_item_new () +{ + return GTK_WIDGET (g_object_new (IDO_TYPE_LEVEL_MENU_ITEM, NULL)); +} + +void idoLevelMenuItemSetIcon (IdoLevelMenuItem *self, GIcon *pIcon) +{ + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + if (pPrivate->pIcon != pIcon) + { + if (pPrivate->pIcon) + { + g_object_unref (pPrivate->pIcon); + } + + pPrivate->pIcon = pIcon ? g_object_ref (pIcon) : NULL; + idoLevelMenuItemStyleUpdateImage (self); + } +} + +void idoLevelMenuItemSetText (IdoLevelMenuItem *self, const char *sText) +{ + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + if (g_strcmp0(pPrivate->sText, sText)) + { + g_free (pPrivate->sText); + pPrivate->sText = g_strdup (sText); + + g_object_set (G_OBJECT (pPrivate->pLabel), "label", pPrivate->sText, "visible", (gboolean)(pPrivate->sText && *pPrivate->sText), NULL); + } +} + +void idoLevelMenuItemSetLevel (IdoLevelMenuItem *self, guint16 nLevel) +{ + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (self); + + gtk_level_bar_set_value (GTK_LEVEL_BAR (pPrivate->pLevelBar), (gdouble)nLevel); +} + +GtkMenuItem* ido_level_menu_item_new_from_model (GMenuItem *pMenuItem, GActionGroup *pActionGroup) +{ + GtkWidget *pItem = ido_level_menu_item_new (); + IdoLevelMenuItemPrivate *pPrivate = ido_level_menu_item_get_instance_private (IDO_LEVEL_MENU_ITEM (pItem)); + + gchar *sLabel; + + if (g_menu_item_get_attribute (pMenuItem, "label", "s", &sLabel)) + { + idoLevelMenuItemSetText (IDO_LEVEL_MENU_ITEM (pItem), sLabel); + g_free (sLabel); + } + + GVariant *sIcon = g_menu_item_get_attribute_value (pMenuItem, "icon", NULL); + + if (sIcon) + { + GIcon *pIcon = g_icon_deserialize (sIcon); + idoLevelMenuItemSetIcon (IDO_LEVEL_MENU_ITEM (pItem), pIcon); + g_object_unref (pIcon); + g_variant_unref (sIcon); + } + + guint16 nProgress = 0; + + if (g_menu_item_get_attribute (pMenuItem, "x-ayatana-level", "q", &nProgress)) + { + idoLevelMenuItemSetLevel (IDO_LEVEL_MENU_ITEM (pItem), nProgress); + } + + gchar *sAction; + + if (g_menu_item_get_attribute (pMenuItem, "action", "s", &sAction)) + { + GVariant *sTarget = g_menu_item_get_attribute_value (pMenuItem, "target", NULL); + pPrivate->pHelper = ido_action_helper_new (pItem, pActionGroup, sAction, sTarget); + g_signal_connect_object (pItem, "activate", G_CALLBACK (onActivate), pPrivate->pHelper, 0); + g_signal_connect_swapped (pItem, "destroy", G_CALLBACK (g_object_unref), pPrivate->pHelper); + + if (sTarget) + { + g_variant_unref (sTarget); + } + + g_free (sAction); + } + + return GTK_MENU_ITEM (pItem); +} diff --git a/src/idolevelmenuitem.h b/src/idolevelmenuitem.h new file mode 100644 index 0000000..bba5bc9 --- /dev/null +++ b/src/idolevelmenuitem.h @@ -0,0 +1,52 @@ +/* + * Copyright 2023 Robert Tari + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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 <http://www.gnu.org/licenses/>. + * + * Author: Robert Tari <robert@tari.in> + */ + +#ifndef __IDO_LEVEL_MENU_ITEM_H__ +#define __IDO_LEVEL_MENU_ITEM_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define IDO_TYPE_LEVEL_MENU_ITEM (ido_level_menu_item_get_type ()) +#define IDO_LEVEL_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_LEVEL_MENU_ITEM, IdoLevelMenuItem)) +#define IDO_IS_LEVEL_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_LEVEL_MENU_ITEM)) + +typedef struct _IdoLevelMenuItem IdoLevelMenuItem; +typedef struct _IdoLevelMenuItemClass IdoLevelMenuItemClass; + +struct _IdoLevelMenuItemClass +{ + GtkMenuItemClass parent_class; +}; + +struct _IdoLevelMenuItem +{ + GtkMenuItem parent; +}; + +GType ido_level_menu_item_get_type (void) G_GNUC_CONST; +GtkWidget* ido_level_menu_item_new (); +GtkMenuItem* ido_level_menu_item_new_from_model (GMenuItem *pMenuItem, GActionGroup *pActionGroup); +void idoLevelMenuItemSetIcon (IdoLevelMenuItem *self, GIcon *pIcon); +void idoLevelMenuItemSetText (IdoLevelMenuItem *self, const char *sText); +void idoLevelMenuItemSetLevel (IdoLevelMenuItem *self, guint16 nLevel); + +G_END_DECLS + +#endif diff --git a/src/idomenuitemfactory.c b/src/idomenuitemfactory.c index 7f9978c..85318d5 100644 --- a/src/idomenuitemfactory.c +++ b/src/idomenuitemfactory.c @@ -1,5 +1,6 @@ /* * Copyright 2013 Canonical Ltd. + * Copyright 2023 Robert Tari * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published @@ -15,6 +16,7 @@ * * Authors: * Lars Uebernickel <lars.uebernickel@canonical.com> + * Robert Tari <robert@tari.in> */ #include <gtk/gtk.h> @@ -33,6 +35,7 @@ #include "idoswitchmenuitem.h" #include "idoprogressmenuitem.h" #include "idoremovablemenuitem.h" +#include "idolevelmenuitem.h" #define IDO_TYPE_MENU_ITEM_FACTORY (ido_menu_item_factory_get_type ()) #define IDO_MENU_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), IDO_TYPE_MENU_ITEM_FACTORY, IdoMenuItemFactory)) @@ -102,6 +105,9 @@ ido_menu_item_factory_create_menu_item (AyatanaMenuItemFactory *factory, else if (g_str_equal (type, "org.ayatana.indicator.removable")) item = ido_removable_menu_item_new_from_model (menuitem, actions); + else if (g_str_equal (type, "org.ayatana.indicator.level")) + item = ido_level_menu_item_new_from_model (menuitem, actions); + return item; } |