From 2ea7c4ae2e9c93d698f4d9065ebd5a49904720c6 Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Fri, 24 Mar 2023 14:49:40 +0100 Subject: Use symlink in the build directory for includes Make includes work though a symlink rather than modifying the generated sources. This is necessary so that headers included from the main header work. --- bindings/vala/examples/CMakeLists.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/bindings/vala/examples/CMakeLists.txt b/bindings/vala/examples/CMakeLists.txt index 2751096..cb869d3 100644 --- a/bindings/vala/examples/CMakeLists.txt +++ b/bindings/vala/examples/CMakeLists.txt @@ -4,6 +4,7 @@ add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ayatana-indicator-example.c" DEPENDS "bindings-vala" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E create_symlink "${CMAKE_BINARY_DIR}/src" "${CMAKE_CURRENT_BINARY_DIR}/libayatana-appindicator" COMMAND ${VALA_COMPILER} --pkg ayatana-appindicator3-0.1 @@ -12,16 +13,11 @@ add_custom_command( -C ayatana-indicator-example.vala --metadatadir "${CMAKE_SOURCE_DIR}/src" --directory=${CMAKE_CURRENT_BINARY_DIR} - VERBATIM - COMMAND - sed - -i "s|#include\\s*<\\s*libayatana-appindicator/app-indicator.h\\s*>||g" - "${CMAKE_CURRENT_BINARY_DIR}/ayatana-indicator-example.c" ) # ayatana-indicator-example -set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ayatana-indicator-example.c" PROPERTIES COMPILE_FLAGS " -include ${CMAKE_SOURCE_DIR}/src/app-indicator.h -Wno-incompatible-pointer-types") +set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/ayatana-indicator-example.c" PROPERTIES COMPILE_FLAGS " -Wno-incompatible-pointer-types") add_executable("ayatana-indicator-example" "${CMAKE_CURRENT_BINARY_DIR}/ayatana-indicator-example.c") -target_include_directories("ayatana-indicator-example" PUBLIC ${PROJECT_DEPS_INCLUDE_DIRS}) +target_include_directories("ayatana-indicator-example" PUBLIC ${PROJECT_DEPS_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries("ayatana-indicator-example" "${PROJECT_DEPS_LIBRARIES} -layatana-appindicator3 -L${CMAKE_BINARY_DIR}/src") -- cgit v1.2.3 From 808727b9b49328d8e46097fccdd1614de774ed00 Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Mon, 1 Mar 2021 19:13:17 +0100 Subject: Add macros for compile-time version checking --- CMakeLists.txt | 7 +- .../reference/libayatana-appindicator-sections.txt | 12 +++ src/CMakeLists.txt | 25 ++++- src/app-indicator-version.h.in | 109 +++++++++++++++++++++ src/app-indicator.h | 4 + 5 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 src/app-indicator-version.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 13d31e9..cd98b4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.13) -project(libayatana-appindicator C) +project(libayatana-appindicator + VERSION 0.5.90 + LANGUAGES C +) set(PROJECT_VERSION "0.5.94") set(PROJECT_NAME "libayatana-appindicator") @@ -78,6 +81,8 @@ if (ENABLE_GTKDOC) endif() endif() +add_compile_definitions(APP_INDICATOR_COMPILATION) + # Make everything add_subdirectory(src) diff --git a/docs/reference/libayatana-appindicator-sections.txt b/docs/reference/libayatana-appindicator-sections.txt index ab86216..9664f38 100644 --- a/docs/reference/libayatana-appindicator-sections.txt +++ b/docs/reference/libayatana-appindicator-sections.txt @@ -49,3 +49,15 @@ app_indicator_get_secondary_activate_target app_indicator_get_title app_indicator_build_menu_from_desktop + +
+app-indicator-version +Versioning +AYATANA_APP_INDICATOR_MAJOR_VERSION +AYATANA_APP_INDICATOR_MINOR_VERSION +AYATANA_APP_INDICATOR_MICRO_VERSION +AYATANA_APP_INDICATOR_VERSION +AYATANA_APP_INDICATOR_VERSION_S +AYATANA_APP_INDICATOR_VERSION_HEX +AYATANA_APP_INDICATOR_CHECK_VERSION +
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e5f591..b0990a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,11 @@ elseif (FLAVOUR_GTK2) set (gtk_girver "Gtk-2.0") endif() +set(GIR_SOURCES + app-indicator.c + app-indicator.h +) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/app-indicator.h" DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/lib${ayatana_appindicator_gtkver}-0.1/libayatana-appindicator") # ayatana-appindicator{,3}-0.1.pc @@ -132,16 +137,24 @@ install(TARGETS "${ayatana_appindicator_gtkver}" LIBRARY DESTINATION "${CMAKE_IN # AyatanaAppIndicator{,3}-0.1.gir +add_custom_command( + TARGET "${ayatana_appindicator_gtkver}" PRE_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND + ${CMAKE_COMMAND} + -E copy + ${GIR_SOURCES} + ${CMAKE_CURRENT_BINARY_DIR} +) + find_package(GObjectIntrospection REQUIRED) add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${ayatana_appindicator_girver}-0.1.gir" DEPENDS "${ayatana_appindicator_gtkver}" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND ${INTROSPECTION_SCANNER} - app-indicator.c ${HEADERS} - --add-include-path=${CMAKE_CURRENT_BINARY_DIR} + ${GIR_SOURCES} --c-include=libayatana-appindicator/app-indicator.h --symbol-prefix=app --identifier-prefix=App @@ -173,3 +186,9 @@ add_custom_command( install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${ayatana_appindicator_girver}-0.1.typelib" DESTINATION "${CMAKE_INSTALL_FULL_LIBDIR}/girepository-1.0") add_custom_target(src ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${ayatana_appindicator_girver}-0.1.typelib") + +# app-indicator-version.h + +configure_file(app-indicator-version.h.in app-indicator-version.h @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/app-indicator-version.h" DESTINATION "${CMAKE_INSTALL_FULL_INCLUDEDIR}/lib${ayatana_appindicator_gtkver}-0.1/libayatana-appindicator") diff --git a/src/app-indicator-version.h.in b/src/app-indicator-version.h.in new file mode 100644 index 0000000..7d99c87 --- /dev/null +++ b/src/app-indicator-version.h.in @@ -0,0 +1,109 @@ +/* +Copyright (C) 2021 Guido Berhoerster + +This program is free software: you can redistribute it and/or modify it +under the terms of either or both of the following licenses: + +1) the GNU Lesser General Public License version 3, as published by the + Free Software Foundation; and/or +2) the GNU Lesser General Public License version 2.1, 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 applicable version of the GNU Lesser General Public +License for more details. + +You should have received a copy of both the GNU Lesser General Public +License version 3 and version 2.1 along with this program. If not, see + +*/ + +#ifndef APP_INDICATOR_VERSION_H +#define APP_INDICATOR_VERSION_H + +#if !defined(APP_INDICATOR_INSIDE) && !defined(APP_INDICATOR_COMPILATION) +#error "Only can be included directly." +#endif + +G_BEGIN_DECLS + +/** + * SECTION:app-indicator-version + * @Title: Versioning + * @short_description: Ayatana Application Indicators version checking + * + * ayatana-appindicator provides macros for checking the version of the library + * an application is compiled against. + */ + +/** + * AYATANA_APP_INDICATOR_MAJOR_VERSION: + * + * Ayatana Application Indicators major version component (e.g. 1 if + * %AYATANA_APP_INDICATOR_VERSION is 1.2.3) + */ +#define AYATANA_APP_INDICATOR_MAJOR_VERSION (@PROJECT_VERSION_MAJOR@) + +/** + * AYATANA_APP_INDICATOR_MINOR_VERSION: + * + * Ayatana Application Indicators minor version component (e.g. 2 if + * %AYATANA_APP_INDICATOR_VERSION is 1.2.3) + */ +#define AYATANA_APP_INDICATOR_MINOR_VERSION (@PROJECT_VERSION_MINOR@) + +/** + * AYATANA_APP_INDICATOR_MICRO_VERSION: + * + * Ayatana Application Indicators micro version component (e.g. 3 if + * %AYATANA_APP_INDICATOR_VERSION is 1.2.3) + */ +#define AYATANA_APP_INDICATOR_MICRO_VERSION (@PROJECT_VERSION_PATCH@) + +/** + * AYATANA_APP_INDICATOR_VERSION + * + * Ayatana Application Indicators version, e.g. 1.2.3. + */ +#define AYATANA_APP_INDICATOR_VERSION (@PROJECT_VERSION@) + +/** + * AYATANA_APP_INDICATOR_VERSION_S: + * + * Ayatana Application Indicators version, encoded as a string which may be + * concatenated, e.g. "1.2.3". + */ +#define AYATANA_APP_INDICATOR_VERSION_S "@PROJECT_VERSION@" + +#define AYATANA_APP_INDICATOR_ENCODE_VERSION(major,minor,micro) \ + ((major) << 24 | (minor) << 16 | (micro) << 8) + +/** + * AYATANA_APP_INDICATOR_VERSION_HEX: + * + * Ayatana Application Indicators version, encoded as an hexadecimal number + * which may be compared to an integer number, e.g. 0x010203. + */ +#define AYATANA_APP_INDICATOR_VERSION_HEX \ + (AYATANA_APP_INDICATOR_ENCODE_VERSION (AYATANA_APP_INDICATOR_MAJOR_VERSION, AYATANA_APP_INDICATOR_MINOR_VERSION, AYATANA_APP_INDICATOR_MICRO_VERSION)) + +/** + * AYATANA_APP_INDICATOR_CHECK_VERSION: + * @major: required major version + * @minor: required minor version + * @micro: required micro version + * + * Compile-time version checking. Evaluates to %TRUE if the version of + * ayatana-appindicator is greater than the given @major, @minor, and @micro + * numbers. + */ +#define AYATANA_APP_INDICATOR_CHECK_VERSION(major,minor,micro) \ + (AYATANA_APP_INDICATOR_MAJOR_VERSION > (major) || \ + (AYATANA_APP_INDICATOR_MAJOR_VERSION == (major) && AYATANA_APP_INDICATOR_MINOR_VERSION > (minor)) || \ + (AYATANA_APP_INDICATOR_MAJOR_VERSION == (major) && AYATANA_APP_INDICATOR_MINOR_VERSION == (minor) && AYATANA_APP_INDICATOR_MICRO_VERSION >= (micro))) + +G_END_DECLS + +#endif /* APP_INDICATOR_VERSION_H */ diff --git a/src/app-indicator.h b/src/app-indicator.h index b53b7fd..4ac78f4 100644 --- a/src/app-indicator.h +++ b/src/app-indicator.h @@ -32,6 +32,10 @@ License version 3 and version 2.1 along with this program. If not, see #include +#define APP_INDICATOR_INSIDE +#include "app-indicator-version.h" +#undef APP_INDICATOR_INSIDE + G_BEGIN_DECLS /** -- cgit v1.2.3 From bfef7f60d6af13f8ae5cd733a7d61f47766e9d27 Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Mon, 1 Mar 2021 23:58:34 +0100 Subject: Add support for tooltips An icon, title and text body may be set independently or at once resulting in a single signal. Markup in the text body is not supported for simplicity. --- bindings/vala/examples/CMakeLists.txt | 1 + src/app-indicator.c | 411 +++++++++++++++++++++++++++++++++- src/app-indicator.h | 22 ++ src/notification-item.xml | 3 + 4 files changed, 436 insertions(+), 1 deletion(-) diff --git a/bindings/vala/examples/CMakeLists.txt b/bindings/vala/examples/CMakeLists.txt index cb869d3..8822d27 100644 --- a/bindings/vala/examples/CMakeLists.txt +++ b/bindings/vala/examples/CMakeLists.txt @@ -12,6 +12,7 @@ add_custom_command( --vapidir=${CMAKE_BINARY_DIR}/bindings/vala -C ayatana-indicator-example.vala --metadatadir "${CMAKE_SOURCE_DIR}/src" + --includedir="${CMAKE_BINARY_DIR}/src" --directory=${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/src/app-indicator.c b/src/app-indicator.c index 8f714fe..b20cba4 100644 --- a/src/app-indicator.c +++ b/src/app-indicator.c @@ -83,6 +83,10 @@ typedef struct { gchar *absolute_attention_icon_name; gchar *icon_theme_path; gchar *absolute_icon_theme_path; + gchar *tooltip_icon_name; + gchar *absolute_tooltip_icon_name; + gchar *tooltip_title; + gchar *tooltip_body; DbusmenuServer *menuservice; GtkWidget *menu; GtkWidget *sec_activate_target; @@ -117,6 +121,7 @@ enum { NEW_ATTENTION_ICON, NEW_STATUS, NEW_LABEL, + NEW_TOOLTIP, CONNECTION_CHANGED, NEW_ICON_THEME_PATH, SCROLL_EVENT, @@ -144,7 +149,10 @@ enum { PROP_ORDERING_INDEX, PROP_DBUS_MENU_SERVER, PROP_TITLE, - PROP_MENU + PROP_MENU, + PROP_TOOLTIP_ICON_NAME, + PROP_TOOLTIP_TITLE, + PROP_TOOLTIP_BODY }; /* The strings so that they can be slowly looked up. */ @@ -163,6 +171,9 @@ enum { #define PROP_DBUS_MENU_SERVER_S "dbus-menu-server" #define PROP_TITLE_S "title" #define PROP_MENU_S "menu" +#define PROP_TOOLTIP_ICON_NAME_S "tooltip-icon-name" +#define PROP_TOOLTIP_TITLE_S "tooltip-title" +#define PROP_TOOLTIP_BODY_S "tooltip-body" /* Default Path */ #define DEFAULT_ITEM_PATH "/org/ayatana/NotificationItem" @@ -195,6 +206,7 @@ static void status_icon_status_wrapper (AppIndicator * self, const gchar * statu static gboolean scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer user_data); static gboolean middle_click_wrapper(GtkWidget *status_icon, GdkEventButton *event, gpointer user_data); static void status_icon_changes (AppIndicator * self, gpointer data); +static void tooltip_changes (AppIndicator * self, gpointer data); static void status_icon_activate (GtkStatusIcon * icon, gpointer data); static void status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data); static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon); @@ -515,6 +527,56 @@ app_indicator_class_init (AppIndicatorClass *klass) "The menu that should be shown when the Application Indicator is clicked on in the panel.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * AppIndicator:tooltip-icon-name: + * + * Specifies the name of an icon which may be shown in a tooltip + * associated with the application indicator. + * + * Since: 0.6 + * + */ + g_object_class_install_property (object_class, + PROP_TOOLTIP_ICON_NAME, + g_param_spec_string (PROP_TOOLTIP_ICON_NAME_S, + "Name of the icon shown in the tooltip", + "An icon which may be shown in a tooltip associated with the application indicator.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * AppIndicator:tooltip-title: + * + * Specifies the title of a tooltip associated with the application + * indicator. + * + * Since: 0.6 + * + */ + g_object_class_install_property (object_class, + PROP_TOOLTIP_TITLE, + g_param_spec_string (PROP_TOOLTIP_TITLE_S, + "Title of the tooltip", + "The title part of a tooltip associated with the application indicator.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * AppIndicator:tooltip-body: + * + * Specifies the body part of a tooltip associated with the application + * indicator. + * + * Since: 0.6 + * + */ + g_object_class_install_property (object_class, + PROP_TOOLTIP_BODY, + g_param_spec_string (PROP_TOOLTIP_BODY_S, + "Text body of the tooltip", + "The text body of a tooltip assocaited with the application indicator.", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* Signals */ @@ -579,6 +641,23 @@ app_indicator_class_init (AppIndicatorClass *klass) _application_service_marshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); + /** + * AppIndicator::new-tooltip: + * @arg0: The #AppIndicator object + * + * Emitted when #AppIndicator:tooltip is changed. + * + * Since: 0.6 + * + */ + signals[NEW_TOOLTIP] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_TOOLTIP, + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (AppIndicatorClass, new_tooltip), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0, G_TYPE_NONE); + /** * AppIndicator::connection-changed: * @arg0: The #AppIndicator object @@ -703,6 +782,10 @@ app_indicator_init (AppIndicator *self) priv->label = NULL; priv->label_guide = NULL; priv->label_change_idle = 0; + priv->tooltip_icon_name = NULL; + priv->absolute_tooltip_icon_name = NULL; + priv->tooltip_title = NULL; + priv->tooltip_body = NULL; priv->connection = NULL; priv->dbus_registration = 0; @@ -890,6 +973,26 @@ app_indicator_finalize (GObject *object) priv->att_accessible_desc = NULL; } + if (priv->tooltip_icon_name != NULL) { + g_free(priv->tooltip_icon_name); + priv->tooltip_icon_name = NULL; + } + + if (priv->absolute_tooltip_icon_name != NULL) { + g_free (priv->absolute_tooltip_icon_name); + priv->absolute_tooltip_icon_name = NULL; + } + + if (priv->tooltip_title != NULL) { + g_free (priv->tooltip_title); + priv->tooltip_title = NULL; + } + + if (priv->tooltip_body != NULL) { + g_free (priv->tooltip_body); + priv->tooltip_body = NULL; + } + if (priv->path != NULL) { g_free(priv->path); priv->path = NULL; @@ -1035,6 +1138,30 @@ G_GNUC_END_IGNORE_DEPRECATIONS } break; } + case PROP_TOOLTIP_ICON_NAME: +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + app_indicator_set_tooltip_full (APP_INDICATOR (object), + g_value_get_string (value), + priv->tooltip_title, + priv->tooltip_body); +G_GNUC_END_IGNORE_DEPRECATIONS + break; + case PROP_TOOLTIP_TITLE: +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + app_indicator_set_tooltip_full (APP_INDICATOR (object), + priv->tooltip_icon_name, + g_value_get_string (value), + priv->tooltip_body); +G_GNUC_END_IGNORE_DEPRECATIONS + break; + case PROP_TOOLTIP_BODY: +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + app_indicator_set_tooltip_full (APP_INDICATOR (object), + priv->tooltip_icon_name, + priv->tooltip_title, + g_value_get_string (value)); +G_GNUC_END_IGNORE_DEPRECATIONS + break; case PROP_LABEL_GUIDE: { gchar * oldguide = priv->label_guide; priv->label_guide = g_value_dup_string(value); @@ -1162,6 +1289,18 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa g_value_set_object(value, priv->menu); break; + case PROP_TOOLTIP_ICON_NAME: + g_value_set_string(value, priv->tooltip_icon_name); + break; + + case PROP_TOOLTIP_TITLE: + g_value_set_string(value, priv->tooltip_title); + break; + + case PROP_TOOLTIP_BODY: + g_value_set_string(value, priv->tooltip_body); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -1309,6 +1448,22 @@ bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * return g_variant_new_string(priv->absolute_icon_theme_path); } return g_variant_new_string(priv->icon_theme_path ? priv->icon_theme_path : ""); + } else if (g_strcmp0(property, "ToolTip") == 0) { + const gchar * icon = ""; + if (priv->absolute_tooltip_icon_name != NULL) { + icon = priv->absolute_tooltip_icon_name; + } else if (priv->tooltip_icon_name != NULL) { + icon = priv->tooltip_icon_name; + } + const gchar * title = ""; + if (priv->tooltip_title != NULL) { + title = priv->tooltip_title; + } else if (priv->title != NULL) { + title = priv->title; + } + GVariantBuilder builder; + g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(iiay)")); + return g_variant_new ("(sa(iiay)ss)", icon, &builder, title, priv->tooltip_body ? priv->tooltip_body : ""); } else if (g_strcmp0(property, "Menu") == 0) { if (priv->menuservice != NULL) { GValue strval = { 0 }; @@ -1611,8 +1766,11 @@ G_GNUC_END_IGNORE_DEPRECATIONS G_CALLBACK(status_icon_changes), icon); g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON, G_CALLBACK(status_icon_changes), icon); + g_signal_connect (G_OBJECT (self), APP_INDICATOR_SIGNAL_NEW_TOOLTIP, + G_CALLBACK (tooltip_changes), icon); status_icon_changes(self, icon); + tooltip_changes (self, icon); g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self); g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(status_icon_menu_activate), self); @@ -1759,6 +1917,60 @@ G_GNUC_END_IGNORE_DEPRECATIONS return; } +static void +extract_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) +{ + GString * strbuf = user_data; + g_string_append_len (strbuf, text, (gssize)text_len); +} + +/* Strip all XML markup tags and only preserve text nodes */ +static gchar * +strip_markup (const gchar *markup) +{ + const GMarkupParser parser = { .text = extract_text }; + GString * strbuf = g_string_new (NULL); + GMarkupParseContext * context = g_markup_parse_context_new (&parser, G_MARKUP_TREAT_CDATA_AS_TEXT, strbuf, NULL); + g_markup_parse_context_parse (context, markup, strlen (markup), NULL); + g_markup_parse_context_end_parse (context, NULL); + g_markup_parse_context_free (context); + return g_string_free (strbuf, FALSE); +} + +/* This tracks changes to the tooltip associated with the app indicator */ +static void +tooltip_changes (AppIndicator * self, gpointer data) +{ + GtkStatusIcon * icon = GTK_STATUS_ICON (data); + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + const gchar * title = (priv->tooltip_title != NULL) ? priv->tooltip_title : priv->title; + if (title == NULL) { +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gtk_status_icon_set_tooltip_markup (icon, NULL); +G_GNUC_END_IGNORE_DEPRECATIONS + return; + } + gchar * title_escaped = g_markup_escape_text (title, -1); + + gchar * body_text = NULL; + if (priv->tooltip_body != NULL) { + body_text = strip_markup (priv->tooltip_body); + } + + gchar * tooltip = g_strdup_printf ("%s%s%s", + title_escaped, + body_text != NULL ? "\n" : "", + body_text != NULL ? body_text : ""); +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gtk_status_icon_set_tooltip_markup (icon, tooltip); +G_GNUC_END_IGNORE_DEPRECATIONS + + g_free (tooltip); + g_free (body_text); + g_free (title_escaped); +} + /* Handles the activate action by the status icon by showing the menu in a popup. */ static void @@ -2132,6 +2344,137 @@ app_indicator_set_icon_full (AppIndicator *self, const gchar *icon_name, const g return; } +/** + * app_indicator_set_tooltip_full: + * @self: The #AppIndicator object to use + * @icon_name: The name of the icon shown in a tooltip. Maps to AppIndicator:tooltip-icon-name. + * @title: The title shown in a tooltip. Maps to AppIndicator:tooltip-title. + * @body: The text body shown in a tooltip. Maps to AppIndicator:tooltip-body. + * + * This is a wrapper function for the #AppIndicator:tooltip-icon-name, + * #AppIndicator:tooltip-title and #AppIndicator:tooltip-body properties. + * + * Since: 0.6 + * + */ +void +app_indicator_set_tooltip_full (AppIndicator *self, const gchar *icon_name, const gchar *title, const gchar *body) +{ + g_return_if_fail (APP_IS_INDICATOR (self)); + gboolean changed = FALSE; + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + if (g_strcmp0 (priv->icon_name, icon_name) != 0) { + g_free (priv->tooltip_icon_name); + priv->tooltip_icon_name = icon_name ? g_strdup (icon_name) : NULL; + + g_free (priv->absolute_tooltip_icon_name); + priv->absolute_tooltip_icon_name = NULL; + + if (icon_name && icon_name[0] == '/') { + priv->absolute_tooltip_icon_name = append_snap_prefix (icon_name); + } + + changed = TRUE; + } + + if (g_strcmp0 (priv->tooltip_title, title) != 0) { + g_free (priv->tooltip_title); + priv->tooltip_title = title ? g_strdup (title) : NULL; + + changed = TRUE; + } + + if (g_strcmp0 (priv->tooltip_body, body) != 0) { + g_free (priv->tooltip_body); + priv->tooltip_body = body ? g_strdup (body) : NULL; + + changed = TRUE; + } + + if (changed) { + g_signal_emit (self, signals[NEW_TOOLTIP], 0, TRUE); + + if (priv->dbus_registration != 0 && priv->connection != NULL) { + GError * error = NULL; + + g_dbus_connection_emit_signal (priv->connection, + NULL, + priv->path, + NOTIFICATION_ITEM_DBUS_IFACE, + "NewToolTip", + NULL, + &error); + + if (error != NULL) { + g_warning ("Unable to send signal for NewToolTip: %s", error->message); + g_error_free (error); + } + } + } +} + +/** + * app_indicator_set_tooltip_icon: + * @self: The #AppIndicator object to use + * @icon_name: The name of the icon shown in the tooltip. Maps to AppIndicator:tooltip-icon-name. + * + * Sets the icon which may be shown in the tooltip of the application indicator. + * + * Since: 0.6 + * + */ +void +app_indicator_set_tooltip_icon (AppIndicator *self, const gchar *icon_name) +{ + g_return_if_fail (APP_IS_INDICATOR (self)); + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + app_indicator_set_tooltip_full (self, icon_name, priv->tooltip_title, priv->tooltip_body); +} + +/** + * app_indicator_set_tooltip_title: + * @self: The #AppIndicator object to use + * @title: The title shown in a tooltip. Maps to AppIndicator:tooltip-title. + * + * Sets the title part of a tooltip of the application indicator. + * + * Since: 0.6 + * + */ +void +app_indicator_set_tooltip_title (AppIndicator *self, const gchar *title) +{ + g_return_if_fail (APP_IS_INDICATOR (self)); + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + app_indicator_set_tooltip_full (self, priv->tooltip_icon_name, title, priv->tooltip_body); +} + +/** + * app_indicator_set_tooltip_body: + * @self: The #AppIndicator object to use + * @body: The text body shown in a tooltip. Maps to AppIndicator:tooltip-body. + * + * Sets the body part of a tooltip of the application indicator. + * + * Since: 0.6 + * + */ +void +app_indicator_set_tooltip_body (AppIndicator *self, const gchar *body) +{ + g_return_if_fail (APP_IS_INDICATOR (self)); + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + app_indicator_set_tooltip_full (self, priv->tooltip_icon_name, priv->tooltip_title, body); +} + /** * app_indicator_set_label: * @self: The #AppIndicator object to use @@ -2601,6 +2944,72 @@ app_indicator_get_menu (AppIndicator *self) return GTK_MENU(priv->menu); } +/** + * app_indicator_get_tooltip_icon: + * @self: The #AppIndicator object to use + * + * Gets the tooltip icon name of the application indicator. See the function + * app_indicator_set_tooltip_icon() for information on the tooltip icon name. + * + * Return value: The current tooltip icon name. + * + * Since: 0.6 + * + */ +const gchar * +app_indicator_get_tooltip_icon (AppIndicator *self) +{ + g_return_val_if_fail (APP_IS_INDICATOR (self), NULL); + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + return priv->tooltip_icon_name; +} + +/** + * app_indicator_get_tooltip_title: + * @self: The #AppIndicator object to use + * + * Gets the tooltip title of the application indicator. See the function + * app_indicator_set_tooltip_title() for information on the tooltip title. + * + * Return value: The current tooltip title. + * + * Since: 0.6 + * + */ +const gchar * +app_indicator_get_tooltip_title (AppIndicator *self) +{ + g_return_val_if_fail (APP_IS_INDICATOR (self), NULL); + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + return priv->tooltip_title; +} + +/** + * app_indicator_get_tooltip_body: + * @self: The #AppIndicator object to use + * + * Gets the tooltip body of the application indicator. See the function + * app_indicator_set_tooltip_body() for information on the tooltip body. + * + * Return value: The current tooltip text body. + * + * Since: 0.6 + * + */ +const gchar * +app_indicator_get_tooltip_body (AppIndicator *self) +{ + g_return_val_if_fail (APP_IS_INDICATOR (self), NULL); + + AppIndicatorPrivate * priv = app_indicator_get_instance_private (self); + + return priv->tooltip_body; +} + /** * app_indicator_get_label: * @self: The #AppIndicator object to use diff --git a/src/app-indicator.h b/src/app-indicator.h index 4ac78f4..7709f3c 100644 --- a/src/app-indicator.h +++ b/src/app-indicator.h @@ -101,6 +101,11 @@ G_BEGIN_DECLS * * String identifier for the #AppIndicator::new-label signal. */ +/** + * APP_INDICATOR_SIGNAL_NEW_TOOLTIP: + * + * String identifier for the #AppIndicator::new-tooltip signal. + */ /** * APP_INDICATOR_SIGNAL_CONNECTION_CHANGED: * @@ -120,6 +125,7 @@ G_BEGIN_DECLS #define APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON "new-attention-icon" #define APP_INDICATOR_SIGNAL_NEW_STATUS "new-status" #define APP_INDICATOR_SIGNAL_NEW_LABEL "new-label" +#define APP_INDICATOR_SIGNAL_NEW_TOOLTIP "new-tooltip" #define APP_INDICATOR_SIGNAL_CONNECTION_CHANGED "connection-changed" #define APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH "new-icon-theme-path" #define APP_INDICATOR_SIGNAL_SCROLL_EVENT "scroll-event" @@ -180,6 +186,7 @@ typedef struct _AppIndicatorClass AppIndicatorClass; * there is no Application Indicator area available. * @unfallback: The function that gets called if an Application * Indicator area appears after the fallback has been created. + * @new_tooltip: Slot for #AppIndicator::new-tooltip. * @app_indicator_reserved_1: Reserved for future use. * @app_indicator_reserved_2: Reserved for future use. * @app_indicator_reserved_3: Reserved for future use. @@ -209,6 +216,8 @@ struct _AppIndicatorClass { const gchar *label, const gchar *guide, gpointer user_data); + void (* new_tooltip) (AppIndicator *indicator, + gpointer user_data); /* Local Signals */ void (* connection_changed) (AppIndicator * indicator, @@ -282,6 +291,16 @@ void app_indicator_set_icon (AppIndicator void app_indicator_set_icon_full (AppIndicator *self, const gchar *icon_name, const gchar *icon_desc); +void app_indicator_set_tooltip_icon (AppIndicator *self, + const gchar *icon_name); +void app_indicator_set_tooltip_title (AppIndicator *self, + const gchar *title); +void app_indicator_set_tooltip_body (AppIndicator *self, + const gchar *body); +void app_indicator_set_tooltip_full (AppIndicator *self, + const gchar *icon_name, + const gchar *title, + const gchar *body); void app_indicator_set_label (AppIndicator *self, const gchar *label, const gchar *guide); @@ -306,6 +325,9 @@ const gchar * app_indicator_get_attention_icon_desc (AppIndic const gchar * app_indicator_get_title (AppIndicator *self); GtkMenu * app_indicator_get_menu (AppIndicator *self); +const gchar * app_indicator_get_tooltip_icon (AppIndicator *self); +const gchar * app_indicator_get_tooltip_title (AppIndicator *self); +const gchar * app_indicator_get_tooltip_body (AppIndicator *self); const gchar * app_indicator_get_label (AppIndicator *self); const gchar * app_indicator_get_label_guide (AppIndicator *self); guint32 app_indicator_get_ordering_index (AppIndicator *self); diff --git a/src/notification-item.xml b/src/notification-item.xml index a223ea4..94f3df8 100644 --- a/src/notification-item.xml +++ b/src/notification-item.xml @@ -11,6 +11,7 @@ + @@ -53,6 +54,8 @@ + + -- cgit v1.2.3