aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Tari <robert@tari.in>2020-11-08 13:50:13 +0100
committerRobert Tari <robert@tari.in>2020-11-10 12:10:27 +0100
commitf9a39b44803e6347c72c1edd8ba46f633e99dd57 (patch)
tree558d0879ddc9318f7e9c0bb7362fd7f086fa29f0
parentf872e9097ff373a172119bd3f7e9d0ef84b31ce1 (diff)
downloadayatana-indicator-notifications-f9a39b44803e6347c72c1edd8ba46f633e99dd57.tar.gz
ayatana-indicator-notifications-f9a39b44803e6347c72c1edd8ba46f633e99dd57.tar.bz2
ayatana-indicator-notifications-f9a39b44803e6347c72c1edd8ba46f633e99dd57.zip
Rewrite to indicator-ng, simplify, drop all GTK references, move to CMake
fixes #9, fixes #12
-rw-r--r--CMakeLists.txt79
-rw-r--r--Makefile.am24
-rwxr-xr-xautogen.sh11
-rw-r--r--cmake/GdbusCodegen.cmake36
-rw-r--r--cmake/UseGSettings.cmake23
-rw-r--r--configure.ac86
-rw-r--r--data/CMakeLists.txt83
-rw-r--r--data/Makefile.am21
-rw-r--r--data/ayatana-indicator-notifications.desktop.in8
-rw-r--r--data/ayatana-indicator-notifications.service.in11
-rw-r--r--data/icons/Makefile.am11
-rw-r--r--data/icons/ayatana-indicator-notification-close-deselect.svg79
-rw-r--r--data/icons/ayatana-indicator-notification-close-select.svg61
-rw-r--r--data/icons/scalable/status/ayatana-indicator-notification-read-dnd.svg (renamed from data/icons/ayatana-indicator-notification-read-dnd.svg)0
-rw-r--r--data/icons/scalable/status/ayatana-indicator-notification-read.svg (renamed from data/icons/ayatana-indicator-notification-read.svg)0
-rw-r--r--data/icons/scalable/status/ayatana-indicator-notification-unread-dnd.svg (renamed from data/icons/ayatana-indicator-notification-unread-dnd.svg)0
-rw-r--r--data/icons/scalable/status/ayatana-indicator-notification-unread.svg (renamed from data/icons/ayatana-indicator-notification-unread.svg)0
-rw-r--r--data/org.ayatana.indicator.notifications11
-rw-r--r--data/org.ayatana.indicator.notifications.gschema.xml.in25
-rw-r--r--debian/control24
-rwxr-xr-xdebian/rules32
-rw-r--r--po/CMakeLists.txt6
-rw-r--r--src/CMakeLists.txt41
-rw-r--r--src/Makefile.am36
-rw-r--r--src/indicator-notifications-settings.c438
-rw-r--r--src/indicator-notifications.c897
-rw-r--r--src/main.c52
-rw-r--r--src/notification-menuitem.c407
-rw-r--r--src/notification-menuitem.h52
-rw-r--r--src/service.c867
-rw-r--r--src/service.h65
-rw-r--r--src/settings.h20
-rw-r--r--tests/Makefile.am0
33 files changed, 1331 insertions, 2175 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..7802cfd
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,79 @@
+project(ayatana-indicator-notifications C CXX)
+cmake_minimum_required(VERSION 2.8.9)
+
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
+
+set(PROJECT_VERSION "0.8.1")
+set(PACKAGE ${CMAKE_PROJECT_NAME})
+
+##
+## GNU standard installation directories
+##
+
+include (GNUInstallDirs)
+set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
+set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
+
+##
+## Gettext
+##
+
+set(GETTEXT_PACKAGE "ayatana-indicator-notifications")
+add_definitions (-DGETTEXT_PACKAGE="${GETTEXT_PACKAGE}"
+ -DLOCALEDIR="${CMAKE_INSTALL_FULL_LOCALEDIR}")
+
+##
+## Check for prerequisites
+##
+
+find_package (PkgConfig REQUIRED)
+include (CheckIncludeFile)
+include (FindPkgConfig)
+
+pkg_check_modules(SERVICE_DEPS REQUIRED
+ glib-2.0>=2.36
+ gio-2.0>=2.36
+ gio-unix-2.0>=2.36)
+
+include_directories (SYSTEM ${SERVICE_DEPS_INCLUDE_DIRS})
+
+set(URL_DISPATCHER_1_REQUIRED_VERSION 1)
+pkg_check_modules(
+ URLDISPATCHER
+ url-dispatcher-1>=${URL_DISPATCHER_1_REQUIRED_VERSION}
+)
+include_directories(${URLDISPATCHER_INCLUDE_DIRS})
+
+##
+## custom targets
+##
+
+set (ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${PROJECT_VERSION})
+add_custom_target (dist
+ COMMAND bzr export --root=${ARCHIVE_NAME} ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.gz
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+
+add_custom_target (cppcheck COMMAND cppcheck --enable=all -q --error-exitcode=2 --inline-suppr
+ ${CMAKE_SOURCE_DIR}/src)
+
+##
+## Actual building
+##
+
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Weverything")
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-c++98-compat -Wno-padded") # these are annoying
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-documentation") # gtk-doc != doxygen
+else()
+ set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wall -Wformat=2") # Use -Wextra and -Wpedantic explicitly if needed
+endif()
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-missing-field-initializers") # GActionEntry
+
+
+include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
+include_directories (${CMAKE_CURRENT_BINARY_DIR}/include)
+
+# actually build things
+add_subdirectory(src)
+add_subdirectory(data)
+add_subdirectory(po)
diff --git a/Makefile.am b/Makefile.am
deleted file mode 100644
index 77cce5a..0000000
--- a/Makefile.am
+++ /dev/null
@@ -1,24 +0,0 @@
-SUBDIRS = \
- src \
- data \
- po \
- tests
-
-EXTRA_DIST = autogen.sh
-
-DISTCLEANFILES = \
- INSTALL \
- Makefile.in \
- aclocal.m4 \
- compile \
- configure \
- config.h.in \
- config.h \
- config.sub \
- config.guess \
- depcomp \
- install-sh \
- ltmain.sh \
- missing \
- po/Makefile.in.in
-
diff --git a/autogen.sh b/autogen.sh
deleted file mode 100755
index 8004621..0000000
--- a/autogen.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-PKG_NAME="ayatana-indicator-notifications"
-
-which mate-autogen || {
- echo "You need mate-common from https://git.mate-desktop.org"
- exit 1
-}
-
-USE_GNOME2_MACROS=1 \
-. mate-autogen $@
diff --git a/cmake/GdbusCodegen.cmake b/cmake/GdbusCodegen.cmake
new file mode 100644
index 0000000..ddb2995
--- /dev/null
+++ b/cmake/GdbusCodegen.cmake
@@ -0,0 +1,36 @@
+cmake_minimum_required(VERSION 2.6)
+if(POLICY CMP0011)
+ cmake_policy(SET CMP0011 NEW)
+endif(POLICY CMP0011)
+
+find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable")
+if(NOT GDBUS_CODEGEN)
+ message(FATAL_ERROR "Excutable gdbus-codegen not found")
+endif()
+
+macro(add_gdbus_codegen outfiles name prefix service_xml)
+ add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
+ COMMAND "${GDBUS_CODEGEN}"
+ --interface-prefix "${prefix}"
+ --generate-c-code "${name}"
+ "${service_xml}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${ARGN} "${service_xml}"
+ )
+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
+endmacro(add_gdbus_codegen)
+
+macro(add_gdbus_codegen_with_namespace outfiles name prefix namespace service_xml)
+ add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h" "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
+ COMMAND "${GDBUS_CODEGEN}"
+ --interface-prefix "${prefix}"
+ --generate-c-code "${name}"
+ --c-namespace "${namespace}"
+ "${service_xml}"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${ARGN} "${service_xml}"
+ )
+ list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
+endmacro(add_gdbus_codegen_with_namespace)
diff --git a/cmake/UseGSettings.cmake b/cmake/UseGSettings.cmake
new file mode 100644
index 0000000..3b61523
--- /dev/null
+++ b/cmake/UseGSettings.cmake
@@ -0,0 +1,23 @@
+# GSettings.cmake, CMake macros written for Marlin, feel free to re-use them.
+
+macro(add_schema SCHEMA_NAME)
+
+ set(PKG_CONFIG_EXECUTABLE pkg-config)
+ set(GSETTINGS_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/glib-2.0/schemas")
+
+ # Run the validator and error if it fails
+ execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE _glib_compile_schemas OUTPUT_STRIP_TRAILING_WHITESPACE)
+ execute_process (COMMAND ${_glib_compile_schemas} --dry-run --schema-file=${SCHEMA_NAME} ERROR_VARIABLE _schemas_invalid OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if (_schemas_invalid)
+ message (SEND_ERROR "Schema validation error: ${_schemas_invalid}")
+ endif (_schemas_invalid)
+
+ # Actually install and recomple schemas
+ message (STATUS "${GSETTINGS_DIR} is the GSettings install dir")
+ install (FILES ${SCHEMA_NAME} DESTINATION ${GSETTINGS_DIR} OPTIONAL)
+
+ install (CODE "message (STATUS \"Compiling GSettings schemas\")")
+ install (CODE "execute_process (COMMAND ${_glib_compile_schemas} ${GSETTINGS_DIR})")
+endmacro()
+
diff --git a/configure.ac b/configure.ac
deleted file mode 100644
index 9433de0..0000000
--- a/configure.ac
+++ /dev/null
@@ -1,86 +0,0 @@
-AC_INIT([ayatana-indicator-notifications], [0.8.2], [jason.conti@gmail.com])
-
-AM_INIT_AUTOMAKE([-Wall -Werror])
-
-AM_SILENT_RULES([yes])
-
-# Required compilers
-
-AC_ISC_POSIX
-AC_PROG_CC
-AM_PROG_CC_C_O
-AM_PROG_AR
-AC_STDC_HEADERS
-AC_DISABLE_STATIC
-AC_PROG_LIBTOOL
-
-# Setup gettext with intltool
-
-AC_DEFUN([AC_DEFINE_PATH], [
- test "x$prefix" = xNONE && prefix="$ac_default_prefix"
- test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
- ac_define_path=`eval echo [$]$2`
- ac_define_path=`eval echo [$]ac_define_path`
- $1="$ac_define_path"
- AC_SUBST($1)
- ifelse($3, ,
- AC_DEFINE_UNQUOTED($1, "$ac_define_path"),
- AC_DEFINE_UNQUOTED($1, "$ac_define_path", $3))
-])
-
-IT_PROG_INTLTOOL
-GETTEXT_PACKAGE=ayatana-indicator-notifications
-AC_SUBST(GETTEXT_PACKAGE)
-AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Gettext package])
-AC_DEFINE_PATH([LOCALEDIR], "${datadir}/locale", [locale directory])
-AM_GLIB_GNU_GETTEXT
-
-# Helpers for GSettings
-
-GLIB_GSETTINGS
-
-# Required libraries from pkg-config
-
-INDICATOR_REQUIRED_VERSION=0.3.19
-GTK3_REQUIRED_VERSION=3.0
-
-INDICATOR3_PKG_NAME=ayatana-indicator3-0.4
-
-PKG_CHECK_MODULES(INDICATOR, $INDICATOR3_PKG_NAME >= $INDICATOR_REQUIRED_VERSION
- gtk+-3.0 >= GTK3_REQUIRED_VERSION)
-
-AC_SUBST(INDICATOR_CFLAGS)
-AC_SUBST(INDICATOR_LIBS)
-
-# Library directories from pkg-config
-
-INDICATORDIR=`$PKG_CONFIG --variable=indicatordir $INDICATOR3_PKG_NAME`
-INDICATORICONSDIR="${datadir}/pixmaps/"
-
-AC_SUBST(INDICATORDIR)
-AC_SUBST(INDICATORICONSDIR)
-
-PKG_CHECK_MODULES(SETTINGS, gtk+-3.0 >= GTK3_REQUIRED_VERSION)
-
-AC_SUBST(SETTINGS_CFLAGS)
-AC_SUBST(SETTINGS_LIBS)
-
-AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([
- Makefile
- src/Makefile
- data/Makefile
- data/icons/Makefile
- po/Makefile.in
- tests/Makefile
-])
-
-AC_OUTPUT
-
-AC_MSG_NOTICE([
-
-Recent Notifications Indicator Configuration:
-
- Prefix: $prefix
- Indicator Dir: $INDICATORDIR
-])
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
new file mode 100644
index 0000000..b806dd1
--- /dev/null
+++ b/data/CMakeLists.txt
@@ -0,0 +1,83 @@
+##
+## GSettings schema
+##
+
+include (UseGSettings)
+set (SCHEMA_NAME "org.ayatana.indicator.notifications.gschema.xml")
+set (SCHEMA_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SCHEMA_NAME}")
+set (SCHEMA_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME}.in")
+
+# generate the .xml file using intltool
+set (ENV{LC_ALL} "C")
+execute_process (COMMAND intltool-merge -quiet --xml-style --utf8 --no-translations "${SCHEMA_FILE_IN}" "${SCHEMA_FILE}")
+
+# let UseGSettings do the rest
+add_schema (${SCHEMA_FILE})
+
+##
+## Systemd Unit File
+##
+
+pkg_check_modules(SYSTEMD systemd)
+if (${SYSTEMD_FOUND})
+ pkg_get_variable(SYSTEMD_USER_DIR systemd systemduserunitdir)
+ message (STATUS "${SYSTEMD_USER_DIR} is the systemd user unit file install dir")
+
+ set (SYSTEMD_USER_NAME "${CMAKE_PROJECT_NAME}.service")
+ set (SYSTEMD_USER_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SYSTEMD_USER_NAME}")
+ set (SYSTEMD_USER_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SYSTEMD_USER_NAME}.in")
+
+ # build it
+ set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
+ configure_file ("${SYSTEMD_USER_FILE_IN}" "${SYSTEMD_USER_FILE}")
+
+ # install it
+ install (FILES "${SYSTEMD_USER_FILE}"
+ DESTINATION "${SYSTEMD_USER_DIR}")
+endif()
+
+##
+## XDG Autostart File
+##
+
+# where to install
+set (XDG_AUTOSTART_DIR "/etc/xdg/autostart")
+message (STATUS "${XDG_AUTOSTART_DIR} is the DBus Service File install dir")
+
+set (XDG_AUTOSTART_NAME "${CMAKE_PROJECT_NAME}.desktop")
+set (XDG_AUTOSTART_FILE "${CMAKE_CURRENT_BINARY_DIR}/${XDG_AUTOSTART_NAME}")
+set (XDG_AUTOSTART_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${XDG_AUTOSTART_NAME}.in")
+
+# build it
+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
+configure_file ("${XDG_AUTOSTART_FILE_IN}" "${XDG_AUTOSTART_FILE}")
+
+# install it
+install (FILES "${XDG_AUTOSTART_FILE}"
+ DESTINATION "${XDG_AUTOSTART_DIR}")
+
+## Ayatana Indicators File
+##
+
+# where to install
+set (AYATANA_INDICATOR_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/ayatana/indicators")
+message (STATUS "${AYATANA_INDICATOR_DIR} is the Ayatana Indicators install dir")
+
+set (AYATANA_INDICATOR_NAME "org.ayatana.indicator.notifications")
+set (AYATANA_INDICATOR_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${AYATANA_INDICATOR_NAME}")
+
+install (FILES "${AYATANA_INDICATOR_FILE}"
+ DESTINATION "${AYATANA_INDICATOR_DIR}")
+
+##
+## Icons
+##
+
+# where to install
+set (ICON_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/icons/hicolor")
+message (STATUS "${ICON_DIR} is the Icon install dir")
+
+install (DIRECTORY icons/scalable DESTINATION "${ICON_DIR}" FILES_MATCHING PATTERN "*.svg")
+
+install(CODE "execute_process (COMMAND gtk-update-icon-cache -t -f ${ICON_DIR})"
+ CODE "message (STATUS \"Updating icon cache\")")
diff --git a/data/Makefile.am b/data/Makefile.am
deleted file mode 100644
index 1960ad7..0000000
--- a/data/Makefile.am
+++ /dev/null
@@ -1,21 +0,0 @@
-SUBDIRS = icons
-
-gsettings_file = org.ayatana.indicator.notifications.gschema.xml
-gsettings_SCHEMAS = $(gsettings_file)
-
-@INTLTOOL_XML_NOMERGE_RULE@
-
-$(gsettings_file).in: $(gsettings_file).in.in
- sed \
- -e "s|\@GETTEXT_PACKAGE\@|$(GETTEXT_PACKAGE)|" \
- $< > $@
-
-@GSETTINGS_RULES@
-
-EXTRA_DIST = \
- $(gsettings_file).in.in
-
-CLEANFILES = \
- $(gsettings_file) \
- $(gsettings_file).in \
- *.gschema.valid
diff --git a/data/ayatana-indicator-notifications.desktop.in b/data/ayatana-indicator-notifications.desktop.in
new file mode 100644
index 0000000..7dac4fd
--- /dev/null
+++ b/data/ayatana-indicator-notifications.desktop.in
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Application
+Name=Ayatana Indicator Notifications
+Exec=@pkglibexecdir@/ayatana-indicator-notifications-service
+OnlyShowIn=Unity;MATE;XFCE;
+NoDisplay=true
+StartupNotify=false
+Terminal=false
diff --git a/data/ayatana-indicator-notifications.service.in b/data/ayatana-indicator-notifications.service.in
new file mode 100644
index 0000000..223cea9
--- /dev/null
+++ b/data/ayatana-indicator-notifications.service.in
@@ -0,0 +1,11 @@
+[Unit]
+Description=Ayatana Indicator Notifications
+PartOf=graphical-session.target
+PartOf=ayatana-indicators.target
+
+[Service]
+ExecStart=@pkglibexecdir@/ayatana-indicator-notifications-service
+Restart=on-failure
+
+[Install]
+WantedBy=ayatana-indicators.target
diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am
deleted file mode 100644
index 47d1ac1..0000000
--- a/data/icons/Makefile.am
+++ /dev/null
@@ -1,11 +0,0 @@
-iconsdir = $(INDICATORICONSDIR)
-
-icons_DATA = \
- ayatana-indicator-notification-close-select.svg \
- ayatana-indicator-notification-close-deselect.svg \
- ayatana-indicator-notification-read.svg \
- ayatana-indicator-notification-read-dnd.svg \
- ayatana-indicator-notification-unread.svg \
- ayatana-indicator-notification-unread-dnd.svg
-
-EXTRA_DIST = $(icons_DATA)
diff --git a/data/icons/ayatana-indicator-notification-close-deselect.svg b/data/icons/ayatana-indicator-notification-close-deselect.svg
deleted file mode 100644
index 3e92220..0000000
--- a/data/icons/ayatana-indicator-notification-close-deselect.svg
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="24"
- height="24"
- id="svg3920"
- version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="New document 5">
- <defs
- id="defs3922" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="11.2"
- inkscape:cx="4.1773985"
- inkscape:cy="15.060459"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="959"
- inkscape:window-height="576"
- inkscape:window-x="65"
- inkscape:window-y="24"
- inkscape:window-maximized="1" />
- <metadata
- id="metadata3925">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(0,-1028.3622)">
- <path
- style="fill:#e6ddd5;fill-opacity:1;stroke:#333333;stroke-width:1;stroke-opacity:1"
- d="M 6.34375 2.84375 C 5.8871312 2.84375 5.4436349 3.0251151 5.09375 3.375 L 3.375 5.09375 C 2.6752302 5.7935198 2.6752302 6.8939802 3.375 7.59375 L 7.78125 12 L 3.375 16.40625 C 2.6752302 17.10602 2.6752302 18.20648 3.375 18.90625 L 5.09375 20.625 C 5.7935198 21.32477 6.8939802 21.32477 7.59375 20.625 L 12 16.21875 L 16.40625 20.625 C 17.10602 21.32477 18.20648 21.32477 18.90625 20.625 L 20.625 18.90625 C 21.32477 18.20648 21.32477 17.10602 20.625 16.40625 L 16.21875 12 L 20.625 7.59375 C 21.32477 6.8939802 21.32477 5.7935198 20.625 5.09375 L 18.90625 3.375 C 18.20648 2.6752302 17.10602 2.6752302 16.40625 3.375 L 12 7.78125 L 7.59375 3.375 C 7.2438651 3.0251151 6.8003688 2.84375 6.34375 2.84375 z "
- transform="translate(0,1028.3622)"
- id="rect3928" />
- <rect
- style="fill:#333333;fill-opacity:1;stroke:none"
- id="rect4479"
- width="3"
- height="19"
- x="-728.66187"
- y="734.63245"
- ry="0.91624337"
- transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)" />
- <rect
- ry="0.91624337"
- y="717.66187"
- x="742.63245"
- height="19"
- width="3"
- id="rect4487"
- style="fill:#333333;fill-opacity:1;stroke:none"
- transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
- </g>
-</svg>
diff --git a/data/icons/ayatana-indicator-notification-close-select.svg b/data/icons/ayatana-indicator-notification-close-select.svg
deleted file mode 100644
index 21f5026..0000000
--- a/data/icons/ayatana-indicator-notification-close-select.svg
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="24"
- height="24"
- id="svg3920"
- version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="indicator-notification-close-deselect.svg">
- <defs
- id="defs3922" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="11.2"
- inkscape:cx="4.1773985"
- inkscape:cy="15.060459"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="false"
- inkscape:window-width="959"
- inkscape:window-height="576"
- inkscape:window-x="65"
- inkscape:window-y="24"
- inkscape:window-maximized="1" />
- <metadata
- id="metadata3925">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1"
- transform="translate(0,-1028.3622)">
- <path
- style="fill:#e6ddd5;fill-opacity:1;stroke:#333333;stroke-width:1;stroke-opacity:1"
- d="M 6.34375 2.84375 C 5.8871312 2.84375 5.4436349 3.0251151 5.09375 3.375 L 3.375 5.09375 C 2.6752302 5.7935198 2.6752302 6.8939802 3.375 7.59375 L 7.78125 12 L 3.375 16.40625 C 2.6752302 17.10602 2.6752302 18.20648 3.375 18.90625 L 5.09375 20.625 C 5.7935198 21.32477 6.8939802 21.32477 7.59375 20.625 L 12 16.21875 L 16.40625 20.625 C 17.10602 21.32477 18.20648 21.32477 18.90625 20.625 L 20.625 18.90625 C 21.32477 18.20648 21.32477 17.10602 20.625 16.40625 L 16.21875 12 L 20.625 7.59375 C 21.32477 6.8939802 21.32477 5.7935198 20.625 5.09375 L 18.90625 3.375 C 18.20648 2.6752302 17.10602 2.6752302 16.40625 3.375 L 12 7.78125 L 7.59375 3.375 C 7.2438651 3.0251151 6.8003688 2.84375 6.34375 2.84375 z "
- transform="translate(0,1028.3622)"
- id="rect3928" />
- </g>
-</svg>
diff --git a/data/icons/ayatana-indicator-notification-read-dnd.svg b/data/icons/scalable/status/ayatana-indicator-notification-read-dnd.svg
index d262e90..d262e90 100644
--- a/data/icons/ayatana-indicator-notification-read-dnd.svg
+++ b/data/icons/scalable/status/ayatana-indicator-notification-read-dnd.svg
diff --git a/data/icons/ayatana-indicator-notification-read.svg b/data/icons/scalable/status/ayatana-indicator-notification-read.svg
index 97e1f1e..97e1f1e 100644
--- a/data/icons/ayatana-indicator-notification-read.svg
+++ b/data/icons/scalable/status/ayatana-indicator-notification-read.svg
diff --git a/data/icons/ayatana-indicator-notification-unread-dnd.svg b/data/icons/scalable/status/ayatana-indicator-notification-unread-dnd.svg
index 23b859e..23b859e 100644
--- a/data/icons/ayatana-indicator-notification-unread-dnd.svg
+++ b/data/icons/scalable/status/ayatana-indicator-notification-unread-dnd.svg
diff --git a/data/icons/ayatana-indicator-notification-unread.svg b/data/icons/scalable/status/ayatana-indicator-notification-unread.svg
index beb2292..beb2292 100644
--- a/data/icons/ayatana-indicator-notification-unread.svg
+++ b/data/icons/scalable/status/ayatana-indicator-notification-unread.svg
diff --git a/data/org.ayatana.indicator.notifications b/data/org.ayatana.indicator.notifications
new file mode 100644
index 0000000..ec72e17
--- /dev/null
+++ b/data/org.ayatana.indicator.notifications
@@ -0,0 +1,11 @@
+[Indicator Service]
+Name=ayatana-indicator-notifications
+ObjectPath=/org/ayatana/indicator/notifications
+Position=100
+
+[phone]
+ObjectPath=/org/ayatana/indicator/notifications/phone
+Position=25
+
+[desktop]
+ObjectPath=/org/ayatana/indicator/notifications/desktop
diff --git a/data/org.ayatana.indicator.notifications.gschema.xml.in b/data/org.ayatana.indicator.notifications.gschema.xml.in
new file mode 100644
index 0000000..b85f14b
--- /dev/null
+++ b/data/org.ayatana.indicator.notifications.gschema.xml.in
@@ -0,0 +1,25 @@
+<schemalist gettext-domain="@GETTEXT_PACKAGE@">
+ <schema id="org.ayatana.indicator.notifications" path="/org/ayatana/indicator/notifications/">
+ <key name="filter-list" type="as">
+ <default>[]</default>
+ <_summary>Discard notifications by application name</_summary>
+ <_description>If an application name is in the filter list, all notifications matching the application name will be discarded.</_description>
+ </key>
+ <key name="filter-list-hints" type="as">
+ <default>[]</default>
+ <summary>Recent application names to suggest for the filter list</summary>
+ <description>Keeps track of recent application names so we can suggest them in the settings.</description>
+ </key>
+ <key name="do-not-disturb" type="b">
+ <default>false</default>
+ <summary>Enable do-not-disturb mode</summary>
+ <description>On supported desktops enables do-not-disturb mode on the notification daemon.</description>
+ </key>
+ <key name="max-items" type="i">
+ <range min="1" max="10"/>
+ <default>5</default>
+ <_summary>Maximum number of visible items</_summary>
+ <_description>The indicator will only display at most the number of notifications indicated by this value.</_description>
+ </key>
+ </schema>
+</schemalist>
diff --git a/debian/control b/debian/control
index f5db253..b35a063 100644
--- a/debian/control
+++ b/debian/control
@@ -4,17 +4,19 @@ Priority: optional
Maintainer: Jason Conti <jason.conti@gmail.com>
Uploaders:
Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
-Build-Depends: debhelper (>= 9),
- dh-autoreconf | debhelper (>= 9.20160403~),
- dh-systemd | debhelper (>= 10.2~),
+Build-Depends: cmake,
+ cmake-extras (>= 0.10),
+ gcovr,
+ lcov,
+ libnotify-dev (>= 0.7.6),
+ libglib2.0-dev (>= 2.36),
+ liburl-dispatcher1-dev | hello,
+# for packaging
+ debhelper (>= 9),
+ dpkg-dev (>= 1.16.1.1),
intltool,
- libayatana-indicator-dev (>= 0.3.93),
- libayatana-indicator3-dev (>= 0.3.93),
- libglib2.0-dev (>= 2.29),
- libgdk-pixbuf2.0-dev (>= 2.22),
- libgtk-3-dev (>= 3.1),
- mate-common,
- pkg-config,
+
+# for systemd unit
systemd [linux-any],
Standards-Version: 4.1.3
Homepage: https://github.com/AyatanaIndicators/ayatana-indicator-notifications
@@ -25,7 +27,7 @@ Package: ayatana-indicator-notifications
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
- libglib2.0-bin,
+ ayatana-indicator-common,
Description: Ayatana Indicator for viewing recent notifications
An Ayatana Indicator applet to display recent notifications sent to a
notification daemon such as notify-osd.
diff --git a/debian/rules b/debian/rules
index 7382e4d..90fda29 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,35 +1,25 @@
#!/usr/bin/make -f
+# -*- makefile -*-
-DPKG_GENSYMBOLS_CHECK_LEVEL=4
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+export DPKG_GENSYMBOLS_CHECK_LEVEL=4
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/buildflags.mk
%:
- dh $@ --with autoreconf,systemd
-
-override_dh_autoreconf:
- if [ ! -e po/ayatana-indicator-notifications.pot.bak ]; then \
- cp po/ayatana-indicator-notifications.pot po/ayatana-indicator-notifications.pot.bak; \
- fi
- NOCONFIGURE=1 dh_autoreconf ./autogen.sh
-
-override_dh_auto_configure:
- dh_auto_configure -- \
- --disable-static
+ dh $@ --with systemd
-override_dh_auto_install:
- dh_auto_install
+override_dh_install:
find debian/ayatana-indicator-notifications -name \*.la -delete
+ find debian/ayatana-indicator-notifications -name \*.a -delete
+ dh_install --fail-missing
override_dh_installchangelogs:
dh_installchangelogs NEWS
-override_dh_auto_clean:
- dh_auto_clean
- if [ -e po/ayatana-indicator-notifications.pot.bak ]; then \
- mv -f po/ayatana-indicator-notifications.pot.bak po/ayatana-indicator-notifications.pot; \
- fi
- rm -f config.guess
- rm -f config.sub
+get-orig-source:
+ uscan --noconf --force-download --rename --download-current-version --destdir=..
diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt
new file mode 100644
index 0000000..0b2b2a1
--- /dev/null
+++ b/po/CMakeLists.txt
@@ -0,0 +1,6 @@
+find_package(Intltool REQUIRED)
+
+intltool_install_translations(
+ ALL
+ GETTEXT_PACKAGE ${GETTEXT_PACKAGE}
+)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..eaebc58
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,41 @@
+set (SERVICE_LIB "ayatanaindicatornotificationsservice")
+set (SERVICE_EXEC "ayatana-indicator-notifications-service")
+
+add_definitions(-DG_LOG_DOMAIN="ayatana-indicator-notifications")
+
+if(URLDISPATCHER_FOUND)
+ add_definitions( -DHAS_URLDISPATCHER )
+endif()
+
+# handwritten sources
+set(SERVICE_MANUAL_SOURCES
+ urlregex.c
+ notification.c
+ dbus-spy.c
+ service.c)
+
+# generated sources
+include(GdbusCodegen)
+set(SERVICE_GENERATED_SOURCES)
+
+# add the bin dir to our include path so the code can find the generated header files
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+
+# add warnings/coverage info on handwritten files
+# but not the autogenerated ones...
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-bad-function-cast") # g_clear_object()
+set(C_WARNING_ARGS "${C_WARNING_ARGS} -Wno-switch-enum")
+set_source_files_properties(${SERVICE_MANUAL_SOURCES}
+ PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -g -std=c99")
+
+# the service library for tests to link against (basically, everything except main())
+add_library(${SERVICE_LIB} STATIC ${SERVICE_MANUAL_SOURCES} ${SERVICE_GENERATED_SOURCES})
+include_directories(${CMAKE_SOURCE_DIR})
+link_directories(${SERVICE_DEPS_LIBRARY_DIRS})
+
+# the executable: lib + main()
+add_executable (${SERVICE_EXEC} main.c)
+set_source_files_properties(${SERVICE_SOURCES} main.c PROPERTIES COMPILE_FLAGS "${C_WARNING_ARGS} -std=c99")
+target_link_libraries (${SERVICE_EXEC} ${SERVICE_LIB} ${SERVICE_DEPS_LIBRARIES} ${URLDISPATCHER_LIBRARIES})
+install (TARGETS ${SERVICE_EXEC} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR})
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index e89ade2..0000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,36 +0,0 @@
-ayatananotificationslibdir = $(INDICATORDIR)
-ayatananotificationslib_LTLIBRARIES = libayatana-notifications.la
-libayatana_notifications_la_SOURCES = \
- dbus-spy.c \
- dbus-spy.h \
- urlregex.c \
- urlregex.h \
- notification-menuitem.c \
- notification-menuitem.h \
- settings.h \
- indicator-notifications.c \
- notification.c \
- notification.h
-libayatana_notifications_la_CFLAGS = \
- -DSETTINGS_PATH=\""$(libexecdir)/$(PACKAGE)/indicator-notifications-settings"\" \
- $(INDICATOR_CFLAGS) \
- -Wall \
- -DG_LOG_DOMAIN=\"ayatana-indicator-notifications\"
-libayatana_notifications_la_LIBADD = \
- $(INDICATOR_LIBS)
-libayatana_notifications_la_LDFLAGS = \
- -module \
- -avoid-version
-
-pkglibexec_PROGRAMS = indicator-notifications-settings
-
-indicator_notifications_settings_SOURCES = \
- settings.h \
- indicator-notifications-settings.c
-
-indicator_notifications_settings_CFLAGS = \
- $(SETTINGS_CFLAGS) \
- -Wall
-
-indicator_notifications_settings_LDADD = \
- $(SETTINGS_LIBS)
diff --git a/src/indicator-notifications-settings.c b/src/indicator-notifications-settings.c
deleted file mode 100644
index 510a0ec..0000000
--- a/src/indicator-notifications-settings.c
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * indicator-notifications-settings.c - UI for indicator settings
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <glib/gi18n.h>
-#include <gtk/gtk.h>
-
-#include "settings.h"
-
-#define SCHEMA_KEY "schema-key"
-
-#define COLUMN_APPNAME 0
-
-typedef struct
-{
- GtkApplication parent_instance;
-
- GSettings *settings;
-
- GtkWidget *filter_list_treeview;
- GtkWidget *filter_list_entry;
-
- /* GtkTreeModel foreach variables */
- gboolean result;
- gchar *text;
- GPtrArray *array;
-} IndicatorNotificationsSettings;
-
-typedef GtkApplicationClass IndicatorNotificationsSettingsClass;
-
-G_DEFINE_TYPE(IndicatorNotificationsSettings, indicator_notifications_settings, GTK_TYPE_APPLICATION)
-
-/* Class Functions */
-static void indicator_notifications_settings_class_init(IndicatorNotificationsSettingsClass *klass);
-static void indicator_notifications_settings_init(IndicatorNotificationsSettings *self);
-static void indicator_notifications_settings_dispose(GObject *object);
-
-/* GtkApplication Signals */
-static void indicator_notifications_settings_activate(GApplication *app);
-
-/* Utility Functions */
-static void load_filter_list(IndicatorNotificationsSettings *self);
-static void load_filter_list_hints(IndicatorNotificationsSettings *self);
-static void save_filter_list(IndicatorNotificationsSettings *self);
-static gboolean foreach_check_duplicates(GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, gpointer user_data);
-static gboolean foreach_build_array(GtkTreeModel *model, GtkTreePath *path,
- GtkTreeIter *iter, gpointer user_data);
-
-/* Callbacks */
-static void filter_list_add_clicked_cb(GtkButton *button, gpointer user_data);
-static void filter_list_remove_clicked_cb(GtkButton *button, gpointer user_data);
-static void button_toggled_cb(GtkToggleButton *button, gpointer user_data);
-static void max_items_changed_cb(GtkSpinButton *button, gpointer user_data);
-static gboolean filter_list_entry_focus_in_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data);
-
-static void
-load_filter_list(IndicatorNotificationsSettings *self)
-{
- GtkListStore *list = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(self->filter_list_treeview)));
- GtkTreeIter iter;
- gchar **items;
-
- gtk_list_store_clear(list);
-
- items = g_settings_get_strv(self->settings, NOTIFICATIONS_KEY_FILTER_LIST);
-
- for (int i = 0; items[i] != NULL; i++) {
- gtk_list_store_append(list, &iter);
- gtk_list_store_set(list, &iter, COLUMN_APPNAME, items[i], -1);
- }
-
- g_strfreev(items);
-}
-
-static void
-load_filter_list_hints(IndicatorNotificationsSettings *self)
-{
- GtkEntryCompletion *completion = gtk_entry_get_completion(GTK_ENTRY(self->filter_list_entry));
- GtkListStore *list = GTK_LIST_STORE(gtk_entry_completion_get_model(completion));
- GtkTreeIter iter;
- gchar **items;
-
- gtk_list_store_clear(list);
-
- items = g_settings_get_strv(self->settings, NOTIFICATIONS_KEY_FILTER_LIST_HINTS);
-
- for (int i = 0; items[i] != NULL; i++) {
- gtk_list_store_append(list, &iter);
- gtk_list_store_set(list, &iter, 0, items[i], -1);
- }
-
- g_strfreev(items);
-}
-
-static void
-save_filter_list(IndicatorNotificationsSettings *self)
-{
- GtkListStore *list = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(self->filter_list_treeview)));
- gchar **items;
-
- /* build an array of the filter list items */
- self->array = g_ptr_array_new();
- gtk_tree_model_foreach(GTK_TREE_MODEL(list), foreach_build_array, self);
- g_ptr_array_add(self->array, NULL);
- items = (gchar **) g_ptr_array_free(self->array, FALSE);
- self->array = NULL;
-
- g_settings_set_strv(self->settings, NOTIFICATIONS_KEY_FILTER_LIST, (const gchar **) items);
-
- g_strfreev(items);
-}
-
-static gboolean
-foreach_check_duplicates(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
-{
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) user_data;
- gchar *appname;
- gboolean result = FALSE;
-
- gtk_tree_model_get(model, iter, COLUMN_APPNAME, &appname, -1);
-
- if (g_strcmp0(appname, self->text) == 0) {
- result = TRUE;
- self->result = TRUE;
- }
-
- g_free(appname);
-
- return result;
-}
-
-static gboolean
-foreach_build_array(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
-{
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) user_data;
- gchar *appname;
-
- gtk_tree_model_get(model, iter, COLUMN_APPNAME, &appname, -1);
-
- g_ptr_array_add(self->array, appname);
-
- return FALSE;
-}
-
-static void
-filter_list_add_clicked_cb(GtkButton *button, gpointer user_data)
-{
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) user_data;
- GtkListStore *list = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(self->filter_list_treeview)));
- GtkTreeIter iter;
-
- /* strip off the leading and trailing whitespace in case of user error */
- self->text = g_strdup(gtk_entry_get_text(GTK_ENTRY(self->filter_list_entry)));
- g_strstrip(self->text);
-
- if (strlen(self->text) > 0) {
- /* check for duplicates first */
- self->result = FALSE;
- gtk_tree_model_foreach(GTK_TREE_MODEL(list), foreach_check_duplicates, self);
-
- if (self->result == FALSE) {
- gtk_list_store_append(list, &iter);
- gtk_list_store_set(list, &iter, COLUMN_APPNAME, self->text, -1);
- save_filter_list(self);
- }
- }
-
- /* clear the entry */
- gtk_entry_set_text(GTK_ENTRY(self->filter_list_entry), "");
-
- /* cleanup text */
- g_free(self->text);
- self->text = "";
-}
-
-static void
-filter_list_remove_clicked_cb(GtkButton *button, gpointer user_data)
-{
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) user_data;
- GtkListStore *list = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(self->filter_list_treeview)));
- GtkTreeIter iter;
- GtkTreeSelection *selection;
-
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(self->filter_list_treeview));
-
- if (gtk_tree_selection_get_selected(selection, NULL, &iter) == TRUE) {
- gtk_list_store_remove(list, &iter);
-
- save_filter_list(self);
- }
-}
-
-static void
-button_toggled_cb(GtkToggleButton *button, gpointer user_data)
-{
- GSettings *settings = G_SETTINGS(user_data);
- char *schema_key = (char *) g_object_get_data(G_OBJECT(button), SCHEMA_KEY);
-
- g_settings_set_boolean(settings, schema_key, gtk_toggle_button_get_active(button));
-}
-
-static void
-max_items_changed_cb(GtkSpinButton *button, gpointer user_data)
-{
- GSettings *settings = G_SETTINGS(user_data);
-
- int value = gtk_spin_button_get_value_as_int(button);
- g_settings_set_int(settings, NOTIFICATIONS_KEY_MAX_ITEMS, value);
-}
-
-static gboolean
-filter_list_entry_focus_in_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
-{
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) user_data;
- load_filter_list_hints(self);
- g_signal_emit_by_name(widget, "changed", NULL);
- return FALSE;
-}
-
-static void
-indicator_notifications_settings_activate(GApplication *app)
-{
- GtkWidget *window;
- GtkWidget *frame;
- GtkWidget *vbox;
- GtkWidget *button_cmc;
- GtkWidget *button_hide_ind;
- GtkWidget *button_dnd;
- GtkWidget *button_swap_clr_s;
- GtkWidget *spin;
- GtkWidget *spin_label;
- GtkWidget *filter_list_label;
- GtkListStore *filter_list_list;
- GtkWidget *filter_list_scroll;
- GtkTreeViewColumn *column;
- GtkCellRenderer *renderer;
- GtkWidget *hbox;
- GtkWidget *button_filter_list_rem;
- GtkWidget *button_filter_list_add;
- GtkEntryCompletion *entry_completion;
- GtkListStore *entry_list;
-
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) app;
-
- /* Check for a pre-existing window */
- GtkWindow *old_window = gtk_application_get_active_window(GTK_APPLICATION(app));
- if (old_window != NULL) {
- gtk_window_present_with_time(old_window, GDK_CURRENT_TIME);
- return;
- }
-
- /* GSettings */
- self->settings = g_settings_new(NOTIFICATIONS_SCHEMA);
-
- /* Main Window */
- window = gtk_application_window_new(GTK_APPLICATION(app));
- gtk_window_set_title(GTK_WINDOW(window), _("Indicator Notifications Settings"));
- gtk_window_set_default_size(GTK_WINDOW(window), 400, 400);
- gtk_container_set_border_width(GTK_CONTAINER(window), 10);
- gtk_widget_show(window);
-
- /* Window Frame */
- frame = gtk_frame_new(_("Indicator Notifications Settings"));
- gtk_container_add(GTK_CONTAINER(window), frame);
- gtk_widget_show(frame);
-
- /* Main Vertical Box */
- vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_set_border_width(GTK_CONTAINER(vbox), 10);
- gtk_container_add(GTK_CONTAINER(frame), vbox);
- gtk_widget_show(vbox);
-
- /* clear-on-middle-click */
- button_cmc = gtk_check_button_new_with_label(_("Clear notifications on middle click"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_cmc),
- g_settings_get_boolean(self->settings, NOTIFICATIONS_KEY_CLEAR_MC));
- g_object_set_data(G_OBJECT(button_cmc), SCHEMA_KEY, NOTIFICATIONS_KEY_CLEAR_MC);
- g_signal_connect(button_cmc, "toggled", G_CALLBACK(button_toggled_cb), self->settings);
- gtk_box_pack_start(GTK_BOX(vbox), button_cmc, FALSE, FALSE, 4);
- gtk_widget_show(button_cmc);
-
- /* hide-indicator */
- button_hide_ind = gtk_check_button_new_with_label(_("Hide indicator"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_hide_ind),
- g_settings_get_boolean(self->settings, NOTIFICATIONS_KEY_HIDE_INDICATOR));
- g_object_set_data(G_OBJECT(button_hide_ind), SCHEMA_KEY, NOTIFICATIONS_KEY_HIDE_INDICATOR);
- g_signal_connect(button_hide_ind, "toggled", G_CALLBACK(button_toggled_cb), self->settings);
- gtk_box_pack_start(GTK_BOX(vbox), button_hide_ind, FALSE, FALSE, 4);
- gtk_widget_show(button_hide_ind);
-
- /* do-not-disturb */
- button_dnd = gtk_check_button_new_with_label(_("Do not disturb"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_dnd),
- g_settings_get_boolean(self->settings, NOTIFICATIONS_KEY_DND));
- g_object_set_data(G_OBJECT(button_dnd), SCHEMA_KEY, NOTIFICATIONS_KEY_DND);
- g_signal_connect(button_dnd, "toggled", G_CALLBACK(button_toggled_cb), self->settings);
- gtk_box_pack_start(GTK_BOX(vbox), button_dnd, FALSE, FALSE, 4);
- gtk_widget_show(button_dnd);
-
- /* swap-clear-settings */
- button_swap_clr_s = gtk_check_button_new_with_label(_("Swap \"Clear\" and \"Settings\" items"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_swap_clr_s),
- g_settings_get_boolean(self->settings, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS));
- g_object_set_data(G_OBJECT(button_swap_clr_s), SCHEMA_KEY, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS);
- g_signal_connect(button_swap_clr_s, "toggled", G_CALLBACK(button_toggled_cb), self->settings);
- gtk_box_pack_start(GTK_BOX(vbox), button_swap_clr_s, FALSE, FALSE, 4);
- gtk_widget_show(button_swap_clr_s);
-
- /* max-items */
- /* FIXME: indicator does not change max items until restart... */
- spin_label = gtk_label_new(_("Maximum number of visible notifications"));
- gtk_box_pack_start(GTK_BOX(vbox), spin_label, FALSE, FALSE, 4);
- gtk_widget_show(spin_label);
-
- spin = gtk_spin_button_new_with_range(1, 10, 1);
- gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), g_settings_get_int(self->settings, NOTIFICATIONS_KEY_MAX_ITEMS));
- g_signal_connect(spin, "value-changed", G_CALLBACK(max_items_changed_cb), self->settings);
- gtk_box_pack_start(GTK_BOX(vbox), spin, FALSE, FALSE, 4);
- gtk_widget_show(spin);
-
- /* filter-list */
- filter_list_label = gtk_label_new(_("Discard notifications by application name"));
- gtk_box_pack_start(GTK_BOX(vbox), filter_list_label, FALSE, FALSE, 4);
- gtk_widget_show(filter_list_label);
-
- filter_list_scroll = gtk_scrolled_window_new(NULL, NULL);
- gtk_box_pack_start(GTK_BOX(vbox), filter_list_scroll, TRUE, TRUE, 4);
- gtk_widget_show(filter_list_scroll);
-
- filter_list_list = gtk_list_store_new(1, G_TYPE_STRING);
-
- renderer = gtk_cell_renderer_text_new();
- column = gtk_tree_view_column_new_with_attributes("appname", renderer, "text", COLUMN_APPNAME, NULL);
-
- self->filter_list_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(filter_list_list));
- g_object_unref(filter_list_list);
- gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self->filter_list_treeview), FALSE);
- gtk_tree_view_append_column(GTK_TREE_VIEW(self->filter_list_treeview), column);
- load_filter_list(self);
- gtk_container_add(GTK_CONTAINER(filter_list_scroll), self->filter_list_treeview);
- gtk_widget_show(self->filter_list_treeview);
-
- hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
- gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
- gtk_widget_show(hbox);
-
- button_filter_list_rem = gtk_button_new_with_label(_("Remove"));
- g_signal_connect(button_filter_list_rem, "clicked", G_CALLBACK(filter_list_remove_clicked_cb), self);
- gtk_box_pack_start(GTK_BOX(hbox), button_filter_list_rem, FALSE, FALSE, 2);
- gtk_widget_show(button_filter_list_rem);
-
- button_filter_list_add = gtk_button_new_with_label(_("Add"));
- g_signal_connect(button_filter_list_add, "clicked", G_CALLBACK(filter_list_add_clicked_cb), self);
- gtk_box_pack_start(GTK_BOX(hbox), button_filter_list_add, FALSE, FALSE, 2);
- gtk_widget_show(button_filter_list_add);
-
- self->filter_list_entry = gtk_entry_new();
- gtk_box_pack_start(GTK_BOX(hbox), self->filter_list_entry, TRUE, TRUE, 0);
- gtk_widget_show(self->filter_list_entry);
-
- entry_completion = gtk_entry_completion_new();
- entry_list = gtk_list_store_new(1, G_TYPE_STRING);
- gtk_entry_completion_set_model(entry_completion, GTK_TREE_MODEL(entry_list));
- gtk_entry_completion_set_text_column(entry_completion, 0);
- gtk_entry_completion_set_minimum_key_length(entry_completion, 0);
- gtk_entry_set_completion(GTK_ENTRY(self->filter_list_entry), entry_completion);
- /* When we focus the entry, emit the changed signal so we get the hints immediately */
- /* also update the filter list hints from gsettings */
- g_signal_connect(self->filter_list_entry, "focus-in-event", G_CALLBACK(filter_list_entry_focus_in_cb), self);
-}
-
-static void
-indicator_notifications_settings_init (IndicatorNotificationsSettings *self)
-{
-}
-
-static void
-indicator_notifications_settings_class_init (IndicatorNotificationsSettingsClass *class)
-{
- GApplicationClass *application_class = G_APPLICATION_CLASS (class);
- GObjectClass *object_class = G_OBJECT_CLASS (class);
-
- application_class->activate = indicator_notifications_settings_activate;
-
- object_class->dispose = indicator_notifications_settings_dispose;
-}
-
-static void
-indicator_notifications_settings_dispose(GObject *object)
-{
- IndicatorNotificationsSettings *self = (IndicatorNotificationsSettings *) object;
-
- if(self->settings != NULL) {
- g_object_unref(G_OBJECT(self->settings));
- self->settings = NULL;
- }
-
- G_OBJECT_CLASS(indicator_notifications_settings_parent_class)->dispose(object);
-}
-
-IndicatorNotificationsSettings *
-indicator_notifications_settings_new (void)
-{
- IndicatorNotificationsSettings *self;
-
- g_set_application_name(_("Indicator Notifications Settings"));
-
- self = g_object_new(indicator_notifications_settings_get_type(),
- "application-id", NOTIFICATIONS_SCHEMA ".settings",
- "flags", G_APPLICATION_FLAGS_NONE,
- NULL);
-
- return self;
-}
-
-int
-main(int argc, char **argv)
-{
- IndicatorNotificationsSettings *self;
- int status;
-
- setlocale(LC_ALL, "");
- bindtextdomain( GETTEXT_PACKAGE, LOCALEDIR );
- textdomain( GETTEXT_PACKAGE );
-
- self = indicator_notifications_settings_new();
-
- status = g_application_run(G_APPLICATION(self), argc, argv);
-
- g_object_unref(self);
-
- return status;
-}
diff --git a/src/indicator-notifications.c b/src/indicator-notifications.c
deleted file mode 100644
index dffa57c..0000000
--- a/src/indicator-notifications.c
+++ /dev/null
@@ -1,897 +0,0 @@
-/*
-An indicator to display recent notifications.
-
-Adapted from: indicator-datetime/src/indicator-datetime.c by
- Ted Gould <ted@canonical.com>
-
-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/>.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-/* GStuff */
-#include <glib.h>
-#include <glib/gi18n-lib.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-
-/* Indicator Stuff */
-#include <libayatana-indicator/indicator.h>
-#include <libayatana-indicator/indicator-object.h>
-#include <libayatana-indicator/indicator-service-manager.h>
-
-#include "dbus-spy.h"
-#include "notification-menuitem.h"
-
-#define INDICATOR_NOTIFICATIONS_TYPE (indicator_notifications_get_type ())
-#define INDICATOR_NOTIFICATIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_NOTIFICATIONS_TYPE, IndicatorNotifications))
-#define INDICATOR_NOTIFICATIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_NOTIFICATIONS_TYPE, IndicatorNotificationsClass))
-#define IS_INDICATOR_NOTIFICATIONS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_NOTIFICATIONS_TYPE))
-#define IS_INDICATOR_NOTIFICATIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_NOTIFICATIONS_TYPE))
-#define INDICATOR_NOTIFICATIONS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_NOTIFICATIONS_TYPE, IndicatorNotificationsClass))
-
-typedef struct _IndicatorNotifications IndicatorNotifications;
-typedef struct _IndicatorNotificationsClass IndicatorNotificationsClass;
-typedef struct _IndicatorNotificationsPrivate IndicatorNotificationsPrivate;
-
-struct _IndicatorNotificationsClass {
- IndicatorObjectClass parent_class;
-};
-
-struct _IndicatorNotifications {
- IndicatorObject parent;
- IndicatorNotificationsPrivate *priv;
-};
-
-struct _IndicatorNotificationsPrivate {
- GtkImage *image;
-
- GList *visible_items;
- GList *hidden_items;
-
- gboolean clear_on_middle_click;
- gboolean do_not_disturb;
- gboolean have_unread;
- gboolean hide_indicator;
- gboolean swap_clear_settings;
-
- gint max_items;
-
- GtkMenu *menu;
- GtkWidget *clear_item;
- GtkWidget *clear_item_label;
- GtkWidget *settings_item;
- GtkWidget *pSeparator;
-
- gchar *accessible_desc;
-
- DBusSpy *spy;
-
- GHashTable *filter_list;
-
- GList *filter_list_hints;
-
- GSettings *settings;
-};
-
-#include "settings.h"
-
-#define INDICATOR_ICON_SIZE 22
-#define INDICATOR_ICON_READ "ayatana-indicator-notification-read"
-#define INDICATOR_ICON_UNREAD "ayatana-indicator-notification-unread"
-#define INDICATOR_ICON_READ_DND "ayatana-indicator-notification-read-dnd"
-#define INDICATOR_ICON_UNREAD_DND "ayatana-indicator-notification-unread-dnd"
-
-#define HINT_MAX 10
-
-GType indicator_notifications_get_type(void);
-
-/* Indicator Class Functions */
-static void indicator_notifications_class_init(IndicatorNotificationsClass *klass);
-static void indicator_notifications_init(IndicatorNotifications *self);
-static void indicator_notifications_dispose(GObject *object);
-static void indicator_notifications_finalize(GObject *object);
-
-/* Indicator Standard Methods */
-static GtkImage *get_image(IndicatorObject *io);
-static GtkMenu *get_menu(IndicatorObject *io);
-static const gchar *get_accessible_desc(IndicatorObject *io);
-static void indicator_notifications_middle_click(IndicatorObject *io,
- IndicatorObjectEntry *entry,
- guint time,
- gpointer user_data);
-
-/* Utility Functions */
-static void clear_menuitems(IndicatorNotifications *self);
-static void insert_menuitem(IndicatorNotifications *self, GtkWidget *item);
-static void remove_menuitem(IndicatorNotifications *self, GtkWidget *item);
-static void set_unread(IndicatorNotifications *self, gboolean unread);
-static void update_unread(IndicatorNotifications *self);
-static void update_filter_list(IndicatorNotifications *self);
-static void update_clear_item_markup(IndicatorNotifications *self);
-static void update_indicator_visibility(IndicatorNotifications *self);
-static void load_filter_list_hints(IndicatorNotifications *self);
-static void save_filter_list_hints(IndicatorNotifications *self);
-static void update_filter_list_hints(IndicatorNotifications *self, Notification *notification);
-static void update_do_not_disturb(IndicatorNotifications *self);
-static void settings_try_set_boolean(const gchar *schema, const gchar *key, gboolean value);
-static void swap_clear_settings_items(IndicatorNotifications *self);
-
-/* Callbacks */
-static void clear_item_activated_cb(GtkMenuItem *menuitem, gpointer user_data);
-static void menu_visible_notify_cb(GtkWidget *menu, GParamSpec *pspec, gpointer user_data);
-static void message_received_cb(DBusSpy *spy, Notification *note, gpointer user_data);
-static void notification_clicked_cb(NotificationMenuItem *menuitem, guint button, gpointer user_data);
-static void setting_changed_cb(GSettings *settings, gchar *key, gpointer user_data);
-static void settings_item_activated_cb(GtkMenuItem *menuitem, gpointer user_data);
-
-/* Indicator Module Config */
-INDICATOR_SET_VERSION
-INDICATOR_SET_TYPE(INDICATOR_NOTIFICATIONS_TYPE)
-
-G_DEFINE_TYPE_WITH_PRIVATE(IndicatorNotifications, indicator_notifications, INDICATOR_OBJECT_TYPE);
-
-static void
-indicator_notifications_class_init(IndicatorNotificationsClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- setlocale(LC_ALL, "");
- bindtextdomain( GETTEXT_PACKAGE, LOCALEDIR );
- textdomain( GETTEXT_PACKAGE );
-
- object_class->dispose = indicator_notifications_dispose;
- object_class->finalize = indicator_notifications_finalize;
-
- IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS(klass);
-
- io_class->get_image = get_image;
- io_class->get_menu = get_menu;
- io_class->get_accessible_desc = get_accessible_desc;
- io_class->secondary_activate = indicator_notifications_middle_click;
-
- return;
-}
-
-static void
-indicator_notifications_init(IndicatorNotifications *self)
-{
- self->priv = indicator_notifications_get_instance_private(self);
-
- self->priv->menu = NULL;
-
- self->priv->image = NULL;
-
- self->priv->have_unread = FALSE;
-
- self->priv->accessible_desc = _("Notifications");
-
- self->priv->visible_items = NULL;
- self->priv->hidden_items = NULL;
-
- self->priv->menu = GTK_MENU(gtk_menu_new());
- g_signal_connect(self->priv->menu, "notify::visible", G_CALLBACK(menu_visible_notify_cb), self);
-
- /* Create the settings menuitem */
- self->priv->settings_item = gtk_menu_item_new_with_label(_("Settingsā€¦"));
- g_signal_connect(self->priv->settings_item, "activate", G_CALLBACK(settings_item_activated_cb), NULL);
- gtk_widget_show(self->priv->settings_item);
-
- gtk_menu_shell_prepend(GTK_MENU_SHELL(self->priv->menu), self->priv->settings_item);
-
- /* Create the clear menuitem */
- self->priv->clear_item_label = gtk_label_new(NULL);
- gtk_label_set_xalign(GTK_LABEL(self->priv->clear_item_label), 0);
- gtk_label_set_yalign(GTK_LABEL(self->priv->clear_item_label), 0);
- gtk_label_set_use_markup(GTK_LABEL(self->priv->clear_item_label), TRUE);
- update_clear_item_markup(self);
- gtk_widget_show(self->priv->clear_item_label);
-
- self->priv->clear_item = gtk_menu_item_new();
- g_signal_connect(self->priv->clear_item, "activate", G_CALLBACK(clear_item_activated_cb), self);
- gtk_container_add(GTK_CONTAINER(self->priv->clear_item), self->priv->clear_item_label);
- gtk_widget_show(self->priv->clear_item);
-
- gtk_menu_shell_prepend(GTK_MENU_SHELL(self->priv->menu), self->priv->clear_item);
-
- self->priv->pSeparator = gtk_separator_menu_item_new();
- gtk_menu_shell_prepend(GTK_MENU_SHELL(self->priv->menu), self->priv->pSeparator);
-
- /* Watch for notifications from dbus */
- self->priv->spy = dbus_spy_new();
- g_signal_connect(self->priv->spy, DBUS_SPY_SIGNAL_MESSAGE_RECEIVED, G_CALLBACK(message_received_cb), self);
-
- /* Initialize an empty filter list */
- self->priv->filter_list = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-
- /* Connect to GSettings */
- self->priv->settings = g_settings_new(NOTIFICATIONS_SCHEMA);
- self->priv->clear_on_middle_click = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_CLEAR_MC);
- self->priv->do_not_disturb = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_DND);
- self->priv->hide_indicator = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_HIDE_INDICATOR);
- self->priv->max_items = g_settings_get_int(self->priv->settings, NOTIFICATIONS_KEY_MAX_ITEMS);
- update_filter_list(self);
- self->priv->swap_clear_settings = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS);
- if(self->priv->swap_clear_settings)
- swap_clear_settings_items(self);
- g_signal_connect(self->priv->settings, "changed", G_CALLBACK(setting_changed_cb), self);
-
- /* Set up filter-list hints */
- self->priv->filter_list_hints = NULL;
- load_filter_list_hints(self);
-}
-
-static void
-indicator_notifications_dispose(GObject *object)
-{
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(object);
-
- if(self->priv->image != NULL) {
- g_object_unref(G_OBJECT(self->priv->image));
- self->priv->image = NULL;
- }
-
- if(self->priv->visible_items != NULL) {
- g_list_free_full(self->priv->visible_items, g_object_unref);
- self->priv->visible_items = NULL;
- }
-
- if(self->priv->hidden_items != NULL) {
- g_list_free_full(self->priv->hidden_items, g_object_unref);
- self->priv->hidden_items = NULL;
- }
-
- if(self->priv->menu != NULL) {
- g_object_unref(G_OBJECT(self->priv->menu));
- self->priv->menu = NULL;
- }
-
- if(self->priv->spy != NULL) {
- g_object_unref(G_OBJECT(self->priv->spy));
- self->priv->spy = NULL;
- }
-
- if(self->priv->settings != NULL) {
- g_object_unref(G_OBJECT(self->priv->settings));
- self->priv->settings = NULL;
- }
-
- if(self->priv->filter_list != NULL) {
- g_hash_table_unref(self->priv->filter_list);
- self->priv->filter_list = NULL;
- }
-
- if(self->priv->filter_list_hints != NULL) {
- g_list_free_full(self->priv->filter_list_hints, g_free);
- self->priv->filter_list_hints = NULL;
- }
-
- G_OBJECT_CLASS (indicator_notifications_parent_class)->dispose (object);
- return;
-}
-
-static void
-indicator_notifications_finalize(GObject *object)
-{
- G_OBJECT_CLASS (indicator_notifications_parent_class)->finalize (object);
- return;
-}
-
-static GtkImage *
-get_image(IndicatorObject *io)
-{
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(io);
-
- if(self->priv->image == NULL) {
- self->priv->image = GTK_IMAGE(gtk_image_new());
- /* We have to wait until the image is created to update do-not-disturb the first time */
- update_do_not_disturb(self);
- update_indicator_visibility(self);
- }
-
- return self->priv->image;
-}
-
-static GtkMenu *
-get_menu(IndicatorObject *io)
-{
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(io);
-
- return GTK_MENU(self->priv->menu);
-}
-
-static const gchar *
-get_accessible_desc(IndicatorObject *io)
-{
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(io);
-
- return self->priv->accessible_desc;
-}
-
-static void
-indicator_notifications_middle_click(IndicatorObject *io, IndicatorObjectEntry *entry,
- guint time, gpointer user_data)
-{
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(io);
-
- /* Clear the notifications */
- if(self->priv->clear_on_middle_click) {
- clear_menuitems(self);
- set_unread(self, FALSE);
- }
- /* Otherwise toggle unread status */
- else {
- if(g_list_length(self->priv->visible_items) > 0)
- set_unread(self, !self->priv->have_unread);
- }
-}
-
-/**
- * clear_menuitems:
- * @self: the indicator
- *
- * Clear all notification menuitems from the menu and the visible/hidden lists.
- **/
-static void
-clear_menuitems(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- GList *item;
-
- /* Remove each visible item from the menu */
- for(item = self->priv->visible_items; item; item = item->next) {
- gtk_container_remove(GTK_CONTAINER(self->priv->menu), GTK_WIDGET(item->data));
- }
-
- /* Clear the lists */
- g_list_free_full(self->priv->visible_items, g_object_unref);
- self->priv->visible_items = NULL;
-
- g_list_free_full(self->priv->hidden_items, g_object_unref);
- self->priv->hidden_items = NULL;
-
- update_clear_item_markup(self);
-}
-
-/**
- * insert_menuitem:
- * @self: the indicator
- * @item: the menuitem to insert
- *
- * Inserts a menuitem into the indicator's menu and updates the visible and
- * hidden lists.
- **/
-static void
-insert_menuitem(IndicatorNotifications *self, GtkWidget *item)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- g_return_if_fail(GTK_IS_MENU_ITEM(item));
- GList *last_item;
- GtkWidget *last_widget;
-
- /* List holds a ref to the menuitem */
- self->priv->visible_items = g_list_prepend(self->priv->visible_items, g_object_ref(item));
- gtk_menu_shell_prepend(GTK_MENU_SHELL(self->priv->menu), item);
-
- /* Move items that overflow to the hidden list */
- while(g_list_length(self->priv->visible_items) > self->priv->max_items) {
- last_item = g_list_last(self->priv->visible_items);
- last_widget = GTK_WIDGET(last_item->data);
- /* Steal the ref from the visible list */
- self->priv->visible_items = g_list_delete_link(self->priv->visible_items, last_item);
- self->priv->hidden_items = g_list_prepend(self->priv->hidden_items, last_widget);
- gtk_container_remove(GTK_CONTAINER(self->priv->menu), last_widget);
- last_item = NULL;
- last_widget = NULL;
- }
-
- update_clear_item_markup(self);
-}
-
-/**
- * remove_menuitem:
- * @self: the indicator object
- * @item: the menuitem
- *
- * Removes a menuitem from the indicator menu and the visible list.
- **/
-static void
-remove_menuitem(IndicatorNotifications *self, GtkWidget *item)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- g_return_if_fail(GTK_IS_MENU_ITEM(item));
-
- GList *list_item = g_list_find(self->priv->visible_items, item);
-
- if(list_item == NULL) {
- g_warning("Attempt to remove menuitem not in visible list");
- return;
- }
-
- /* Remove the item */
- gtk_container_remove(GTK_CONTAINER(self->priv->menu), item);
- self->priv->visible_items = g_list_delete_link(self->priv->visible_items, list_item);
- g_object_unref(item);
-
- /* Add an item from the hidden list, if available */
- if(g_list_length(self->priv->hidden_items) > 0) {
- list_item = g_list_first(self->priv->hidden_items);
- GtkWidget *list_widget = GTK_WIDGET(list_item->data);
- self->priv->hidden_items = g_list_delete_link(self->priv->hidden_items, list_item);
- gtk_menu_shell_insert(GTK_MENU_SHELL(self->priv->menu), list_widget,
- g_list_length(self->priv->visible_items));
- /* Steal the ref back from the hidden list */
- self->priv->visible_items = g_list_append(self->priv->visible_items, list_widget);
- }
-
- update_clear_item_markup(self);
-}
-
-/**
- * set_unread:
- * @self: the indicator object
- * @unread: the unread status
- *
- * Sets the unread status of the indicator and updates the icons.
- **/
-static void
-set_unread(IndicatorNotifications *self, gboolean unread)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
-
- self->priv->have_unread = unread;
- update_unread(self);
-}
-
-/**
- * update_unread:
- * @self: the indicator object
- *
- * Updates the indicator icons.
- **/
-static void
-update_unread(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
-
- if(self->priv->have_unread) {
- if (self->priv->do_not_disturb) {
- gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_UNREAD_DND, GTK_ICON_SIZE_MENU);
- }
- else {
- gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_UNREAD, GTK_ICON_SIZE_MENU);
- }
- }
- else {
- if (self->priv->do_not_disturb) {
- gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_READ_DND, GTK_ICON_SIZE_MENU);
- }
- else {
- gtk_image_set_from_icon_name(self->priv->image, INDICATOR_ICON_READ, GTK_ICON_SIZE_MENU);
- }
- }
-}
-
-/**
- * update_filter_list:
- * @self: the indicator object
- *
- * Updates the filter list from GSettings. This currently does not filter already
- * allowed messages. It only applies to messages received in the future.
- **/
-static void
-update_filter_list(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- g_return_if_fail(self->priv->filter_list != NULL);
-
- g_hash_table_remove_all(self->priv->filter_list);
- gchar **items = g_settings_get_strv(self->priv->settings, NOTIFICATIONS_KEY_FILTER_LIST);
- int i;
-
- for(i = 0; items[i] != NULL; i++) {
- g_hash_table_insert(self->priv->filter_list, g_strdup(items[i]), NULL);
- }
-
- g_strfreev(items);
-}
-
-/**
- * update_clear_item_markup:
- * @self: the indicator object
- *
- * Updates the clear menuitem's label markup based on the number of
- * notifications available.
- **/
-static void
-update_clear_item_markup(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- guint visible_length = g_list_length(self->priv->visible_items);
- guint hidden_length = g_list_length(self->priv->hidden_items);
- guint total_length = visible_length + hidden_length;
-
- gchar *markup = g_strdup_printf(ngettext(
- "Clear <small>(%d Notification)</small>",
- "Clear <small>(%d Notifications)</small>",
- total_length),
- total_length);
-
- gtk_label_set_markup(GTK_LABEL(self->priv->clear_item_label), markup);
- g_free(markup);
-
- if (total_length == 0)
- {
- gtk_widget_hide(self->priv->pSeparator);
- gtk_menu_shell_deactivate(GTK_MENU_SHELL(self->priv->menu));
- }
- else
- {
- gtk_widget_show(self->priv->pSeparator);
- }
-}
-
-/**
- * update_indicator_visibility:
- * @self: the indicator object
- *
- * Changes the visibility of the indicator image based on the value
- * of hide_indicator.
- **/
-static void
-update_indicator_visibility(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
-
- if(self->priv->image != NULL) {
- if(self->priv->hide_indicator)
- gtk_widget_hide(GTK_WIDGET(self->priv->image));
- else
- gtk_widget_show(GTK_WIDGET(self->priv->image));
- }
-}
-
-/**
- * load_filter_list_hints:
- * @self: the indicator object
- *
- * Loads the filter list hints from gsettings
- **/
-static void
-load_filter_list_hints(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- g_return_if_fail(self->priv->filter_list_hints == NULL);
-
- gchar **items = g_settings_get_strv(self->priv->settings, NOTIFICATIONS_KEY_FILTER_LIST_HINTS);
- int i;
-
- for (i = 0; items[i] != NULL; i++) {
- self->priv->filter_list_hints = g_list_prepend(self->priv->filter_list_hints, items[i]);
- }
-
- g_free(items);
-}
-
-/**
- * save_filter_list_hints:
- * @self: the indicator object
- *
- * Saves the filter list hints to gsettings
- **/
-static void
-save_filter_list_hints(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
-
- gchar *hints[HINT_MAX + 1];
- int i = 0;
-
- GList *l;
- for (l = self->priv->filter_list_hints; (l != NULL) && (i < HINT_MAX); l = l->next, i++) {
- hints[i] = (gchar *) l->data;
- }
-
- hints[i] = NULL;
-
- g_settings_set_strv(self->priv->settings, NOTIFICATIONS_KEY_FILTER_LIST_HINTS, (const gchar **) hints);
-}
-
-/**
- * update_filter_list_hints:
- * @self: the indicator object
- *
- * Adds an application name to the hints
- **/
-static void
-update_filter_list_hints(IndicatorNotifications *self, Notification *notification)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
- g_return_if_fail(IS_NOTIFICATION(notification));
-
- const gchar *appname = notification_get_app_name(notification);
-
- /* Avoid duplicates */
- GList *l;
- for (l = self->priv->filter_list_hints; l != NULL; l = l->next) {
- if (g_strcmp0(appname, (const gchar *) l->data) == 0)
- return;
- }
-
- /* Add the appname */
- self->priv->filter_list_hints = g_list_prepend(self->priv->filter_list_hints, g_strdup(appname));
-
- /* Keep only a reasonable number */
- while (g_list_length(self->priv->filter_list_hints) > HINT_MAX) {
- GList *last = g_list_last(self->priv->filter_list_hints);
- g_free(last->data);
- self->priv->filter_list_hints = g_list_delete_link(self->priv->filter_list_hints, last);
- }
-
- /* Save the hints */
- /* FIXME: maybe don't do this every update */
- save_filter_list_hints(self);
-}
-
-/**
- * update_do_not_disturb:
- * @self: the indicator object
- *
- * Updates the icon with the do-not-disturb version and sets do-not-disturb options
- * on external notification daemons that are supported.
- **/
-static void
-update_do_not_disturb(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
-
- update_unread(self);
-
- /* Mate do-not-disturb support */
- settings_try_set_boolean(MATE_SCHEMA, MATE_KEY_DND, self->priv->do_not_disturb);
-}
-
-/**
- * settings_try_set_boolean:
- * @schema: the GSettings schema
- * @key: the GSettings key
- * @value: the boolean value
- *
- * Checks to see if the schema and key exist before setting the value.
- */
-static void
-settings_try_set_boolean(const gchar *schema, const gchar *key, gboolean value)
-{
- /* Check if we can access the schema */
- GSettingsSchemaSource *source = g_settings_schema_source_get_default();
- if (source == NULL) {
- return;
- }
-
- /* Lookup the schema */
- GSettingsSchema *source_schema = g_settings_schema_source_lookup(source, schema, TRUE);
-
- /* Couldn't find the schema */
- if (source_schema == NULL) {
- return;
- }
-
- /* Found the schema, make sure we have the key */
- if (g_settings_schema_has_key(source_schema, key)) {
- /* Make sure the key is of boolean type */
- GSettingsSchemaKey *source_key = g_settings_schema_get_key(source_schema, key);
-
- if (g_variant_type_equal(g_settings_schema_key_get_value_type(source_key), G_VARIANT_TYPE_BOOLEAN)) {
- /* Set the value */
- GSettings *settings = g_settings_new(schema);
- g_settings_set_boolean(settings, key, value);
- g_object_unref(settings);
- }
-
- g_settings_schema_key_unref(source_key);
- }
- g_settings_schema_unref(source_schema);
-}
-
-/**
- * swap_clear_settings_items:
- * @self: the indicator object
- *
- * Swaps the position of the clear and settings items.
- **/
-static void
-swap_clear_settings_items(IndicatorNotifications *self)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(self));
-
- // Pick which widget to move
- GtkWidget *widget = self->priv->settings_item;
- if(self->priv->swap_clear_settings)
- widget = self->priv->clear_item;
-
- gtk_container_remove(GTK_CONTAINER(self->priv->menu), g_object_ref(widget));
- gtk_menu_shell_append(GTK_MENU_SHELL(self->priv->menu), widget);
- g_object_unref(widget);
-}
-
-/**
- * clear_item_activated_cb:
- * @menuitem: the clear menuitem
- * @user_data: the indicator object
- *
- * Called when the clear menuitem is activated.
- **/
-static void
-clear_item_activated_cb(GtkMenuItem *menuitem, gpointer user_data)
-{
- g_return_if_fail(GTK_IS_MENU_ITEM(menuitem));
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(user_data));
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(user_data);
-
- clear_menuitems(self);
-}
-
-/**
- * settings_item_activated_cb:
- * @menuitem: the settings menuitem
- * @user_data: the indicator object
- *
- * Called when the settings menuitem is activated.
- **/
-static void
-settings_item_activated_cb(GtkMenuItem *menuitem, gpointer user_data)
-{
- g_return_if_fail(GTK_IS_MENU_ITEM(menuitem));
-
- GError *error = NULL;
-
- gchar *argv[] = { SETTINGS_PATH, NULL };
-
- GPid pid;
-
- g_spawn_async(NULL, argv, NULL, G_SPAWN_DEFAULT, NULL, NULL, &pid, &error);
-
- if (error != NULL) {
- g_message("%s", error->message);
- g_error_free(error);
- }
- else {
- g_spawn_close_pid(pid);
- }
-}
-
-/**
- * setting_changed_cb:
- * @settings: the GSettings object
- * @key: the GSettings key
- * @user_data: the indicator object
- *
- * Called when a GSettings key is changed.
- **/
-static void
-setting_changed_cb(GSettings *settings, gchar *key, gpointer user_data)
-{
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(user_data));
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(user_data);
-
- if(g_strcmp0(key, NOTIFICATIONS_KEY_HIDE_INDICATOR) == 0) {
- self->priv->hide_indicator = g_settings_get_boolean(settings, NOTIFICATIONS_KEY_HIDE_INDICATOR);
- update_indicator_visibility(self);
- }
- else if(g_strcmp0(key, NOTIFICATIONS_KEY_DND) == 0) {
- self->priv->do_not_disturb = g_settings_get_boolean(settings, NOTIFICATIONS_KEY_DND);
- update_do_not_disturb(self);
- }
- else if(g_strcmp0(key, NOTIFICATIONS_KEY_CLEAR_MC) == 0) {
- self->priv->clear_on_middle_click = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_CLEAR_MC);
- }
- else if(g_strcmp0(key, NOTIFICATIONS_KEY_FILTER_LIST) == 0) {
- update_filter_list(self);
- }
- else if(g_strcmp0(key, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS) == 0) {
- self->priv->swap_clear_settings = g_settings_get_boolean(self->priv->settings, NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS);
- swap_clear_settings_items(self);
- }
- /* TODO: Trim or extend the notifications list based on "max-items" key
- * (Currently requires a restart) */
-}
-
-/**
- * menu_visible_notify_cb:
- * @menu: the menu
- * @pspec: unused
- * @user_data: the indicator object
- *
- * Called when the indicator's menu is shown or hidden.
- **/
-static void
-menu_visible_notify_cb(GtkWidget *menu, G_GNUC_UNUSED GParamSpec *pspec, gpointer user_data)
-{
- g_return_if_fail(GTK_IS_MENU(menu));
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(user_data));
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(user_data);
-
- gboolean visible;
- g_object_get(G_OBJECT(menu), "visible", &visible, NULL);
- if(!visible) {
- set_unread(self, FALSE);
- }
-}
-
-/**
- * message_received_cb:
- * @spy: the dbus notification monitor
- * @note: the notification received
- * @user_data: the indicator object
- *
- * Called when a notification arrives on dbus.
- **/
-static void
-message_received_cb(DBusSpy *spy, Notification *note, gpointer user_data)
-{
- g_return_if_fail(IS_DBUS_SPY(spy));
- g_return_if_fail(IS_NOTIFICATION(note));
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(user_data));
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(user_data);
-
- /* Discard notifications if we are hidden */
- if(self->priv->hide_indicator) {
- g_object_unref(note);
- return;
- }
-
- /* Discard useless notifications */
- if(notification_is_private(note) || notification_is_empty(note)) {
- g_object_unref(note);
- return;
- }
-
- /* Discard notifications on the filter list */
- if(self->priv->filter_list != NULL && g_hash_table_contains(self->priv->filter_list,
- notification_get_app_name(note))) {
- g_object_unref(note);
- return;
- }
-
- /* Save a hint for the appname */
- update_filter_list_hints(self, note);
-
- /* Create the menuitem */
- GtkWidget *item = notification_menuitem_new();
- notification_menuitem_set_from_notification(NOTIFICATION_MENUITEM(item), note);
- g_signal_connect(item, NOTIFICATION_MENUITEM_SIGNAL_CLICKED, G_CALLBACK(notification_clicked_cb), self);
- gtk_widget_show(item);
- g_object_unref(note);
-
- insert_menuitem(self, item);
-
- set_unread(self, TRUE);
-}
-
-/**
- * notification_clicked_cb:
- * @widget: the menuitem
- * @user_data: the indicator object
- *
- * Remove the menuitem when clicked.
- **/
-static void
-notification_clicked_cb(NotificationMenuItem *menuitem, guint button, gpointer user_data)
-{
- g_return_if_fail(IS_NOTIFICATION_MENUITEM(menuitem));
- g_return_if_fail(IS_INDICATOR_NOTIFICATIONS(user_data));
- IndicatorNotifications *self = INDICATOR_NOTIFICATIONS(user_data);
-
- remove_menuitem(self, GTK_WIDGET(menuitem));
-}
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..f57237a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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/>.
+ */
+
+#include <locale.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include "service.h"
+
+static void on_name_lost (gpointer instance G_GNUC_UNUSED, gpointer loop)
+{
+ g_message ("exiting: service couldn't acquire or lost ownership of busname");
+ g_main_loop_quit ((GMainLoop*)loop);
+}
+
+int main (int argc G_GNUC_UNUSED, char ** argv G_GNUC_UNUSED)
+{
+ IndicatorNotificationsService * service;
+ GMainLoop * loop;
+
+ /* boilerplate i18n */
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+
+ /* run */
+ service = indicator_notifications_service_new (NULL);
+ loop = g_main_loop_new (NULL, FALSE);
+ g_signal_connect (service, INDICATOR_NOTIFICATIONS_SERVICE_SIGNAL_NAME_LOST, G_CALLBACK(on_name_lost), loop);
+ g_main_loop_run (loop);
+
+ /* cleanup */
+ g_main_loop_unref (loop);
+ g_clear_object (&service);
+
+ return 0;
+}
diff --git a/src/notification-menuitem.c b/src/notification-menuitem.c
deleted file mode 100644
index 392d9d3..0000000
--- a/src/notification-menuitem.c
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * notification-menuitem.h - A menuitem to display notifications.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <glib/gi18n-lib.h>
-#include "notification-menuitem.h"
-#include "urlregex.h"
-
-#define NOTIFICATION_MENUITEM_MAX_CHARS 42
-#define NOTIFICATION_MENUITEM_CLOSE_SELECT "ayatana-indicator-notification-close-select"
-#define NOTIFICATION_MENUITEM_CLOSE_DESELECT "ayatana-indicator-notification-close-deselect"
-
-enum {
- CLICKED,
- LAST_SIGNAL
-};
-
-static void notification_menuitem_class_init(NotificationMenuItemClass *klass);
-static void notification_menuitem_init(NotificationMenuItem *self);
-
-static void notification_menuitem_activate(GtkMenuItem *menuitem);
-static gboolean notification_menuitem_motion(GtkWidget *widget, GdkEventMotion *event);
-static gboolean notification_menuitem_leave(GtkWidget *widget, GdkEventCrossing *event);
-static gboolean notification_menuitem_button_press(GtkWidget *widget, GdkEventButton *event);
-static gboolean notification_menuitem_button_release(GtkWidget *widget, GdkEventButton *event);
-static void notification_menuitem_select(GtkMenuItem *item);
-static void notification_menuitem_deselect(GtkMenuItem *item);
-
-static gboolean notification_menuitem_activate_link_cb(GtkLabel *label, gchar *uri, gpointer user_data);
-static gchar *notification_menuitem_markup_body(const gchar *body);
-
-static gboolean widget_contains_event(GtkWidget *widget, GdkEventButton *event);
-
-static guint notification_menuitem_signals[LAST_SIGNAL] = { 0 };
-
-G_DEFINE_TYPE_WITH_PRIVATE(NotificationMenuItem, notification_menuitem, GTK_TYPE_MENU_ITEM);
-
-static void
-notification_menuitem_class_init(NotificationMenuItemClass *klass)
-{
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- GtkMenuItemClass *menu_item_class = GTK_MENU_ITEM_CLASS(klass);
-
- widget_class->leave_notify_event = notification_menuitem_leave;
- widget_class->motion_notify_event = notification_menuitem_motion;
- widget_class->button_press_event = notification_menuitem_button_press;
- widget_class->button_release_event = notification_menuitem_button_release;
-
- menu_item_class->hide_on_activate = FALSE;
- menu_item_class->activate = notification_menuitem_activate;
- menu_item_class->select = notification_menuitem_select;
- menu_item_class->deselect = notification_menuitem_deselect;
-
- /* Compile the urlregex patterns */
- urlregex_init();
-
- notification_menuitem_signals[CLICKED] =
- g_signal_new(NOTIFICATION_MENUITEM_SIGNAL_CLICKED,
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET(NotificationMenuItemClass, clicked),
- NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
-}
-
-static void
-notification_menuitem_init(NotificationMenuItem *self)
-{
- self->priv = notification_menuitem_get_instance_private(self);
-
- self->priv->pressed_close_image = FALSE;
-
- self->priv->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-
- self->priv->label = gtk_label_new(NULL);
- gtk_widget_set_halign(self->priv->label, GTK_ALIGN_START);
- gtk_widget_set_valign(self->priv->label, GTK_ALIGN_START);
- gtk_label_set_use_markup(GTK_LABEL(self->priv->label), TRUE);
- gtk_label_set_line_wrap(GTK_LABEL(self->priv->label), TRUE);
- gtk_label_set_line_wrap_mode(GTK_LABEL(self->priv->label), PANGO_WRAP_WORD_CHAR);
- gtk_label_set_max_width_chars(GTK_LABEL(self->priv->label), NOTIFICATION_MENUITEM_MAX_CHARS);
- gtk_label_set_track_visited_links(GTK_LABEL(self->priv->label), TRUE);
-
- g_signal_connect(self->priv->label, "activate-link", G_CALLBACK(notification_menuitem_activate_link_cb), self);
-
- gtk_box_pack_start(GTK_BOX(self->priv->hbox), self->priv->label, TRUE, TRUE, 0);
- gtk_widget_show(self->priv->label);
-
- self->priv->close_image = gtk_image_new_from_icon_name(NOTIFICATION_MENUITEM_CLOSE_DESELECT, GTK_ICON_SIZE_MENU);
- gtk_widget_show(self->priv->close_image);
- gtk_box_pack_start(GTK_BOX(self->priv->hbox), self->priv->close_image, FALSE, FALSE, 0);
-
- gtk_container_add(GTK_CONTAINER(self), self->priv->hbox);
- gtk_widget_show(self->priv->hbox);
-}
-
-GtkWidget *
-notification_menuitem_new(void)
-{
- return g_object_new(NOTIFICATION_MENUITEM_TYPE, NULL);
-}
-
-/**
- * notification_menuitem_set_from_notification:
- * @self - the notification menuitem
- * @note - the notification object
- *
- * Sets the markup in the notification menuitem to display information about
- * the notification, as well as marking any links within the message body.
- **/
-void
-notification_menuitem_set_from_notification(NotificationMenuItem *self, Notification *note)
-{
- g_return_if_fail(IS_NOTIFICATION(note));
- gchar *unescaped_timestamp_string = notification_timestamp_for_locale(note);
-
- gchar *app_name = g_markup_escape_text(notification_get_app_name(note), -1);
- gchar *summary = g_markup_escape_text(notification_get_summary(note), -1);
- gchar *body = notification_menuitem_markup_body(notification_get_body(note));
- gchar *timestamp_string = g_markup_escape_text(unescaped_timestamp_string, -1);
-
- gchar *markup = g_strdup_printf("<b>%s</b>\n%s\n<small><i>%s %s <b>%s</b></i></small>",
- summary, body, timestamp_string, _("from"), app_name);
-
- g_free(app_name);
- g_free(summary);
- g_free(body);
- g_free(unescaped_timestamp_string);
- g_free(timestamp_string);
-
- gtk_label_set_markup(GTK_LABEL(self->priv->label), markup);
-
- g_free(markup);
-}
-
-/**
- * notification_menuitem_activate:
- * @menuitem: the menuitem
- *
- * Emit a clicked event for the case where a keyboard activates a menuitem.
- **/
-static void
-notification_menuitem_activate(GtkMenuItem *menuitem)
-{
- g_return_if_fail(IS_NOTIFICATION_MENUITEM(menuitem));
-
- g_signal_emit(NOTIFICATION_MENUITEM(menuitem), notification_menuitem_signals[CLICKED], 0);
-}
-
-/**
- * notification_menuitem_leave:
- * @widget - the widget
- * @event - the event
- *
- * Handle the leave-notify-event, by simply passing it on to the GtkLabel of
- * this menuitem.
- **/
-static gboolean
-notification_menuitem_leave(GtkWidget *widget, GdkEventCrossing *event)
-{
- g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
-
- gtk_widget_event(self->priv->label, (GdkEvent *)event);
- return FALSE;
-}
-
-/**
- * notification_menuitem_motion:
- * @widget - the widget
- * @event - the event
- *
- * Handle the motion-notify-event. It is passed on to the GtkLabel, but the
- * event (x, y) is mapped to the whole menuitem not the label so we have to
- * shift it over a bit into the label's allocation.
- **/
-static gboolean
-notification_menuitem_motion(GtkWidget *widget, GdkEventMotion *event)
-{
- g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
-
- GtkAllocation self_alloc;
- GtkAllocation label_alloc;
-
- gtk_widget_get_allocation(GTK_WIDGET(self), &self_alloc);
- gtk_widget_get_allocation(self->priv->label, &label_alloc);
-
- /* The event is mapped to the menu item's allocation, so we need to shift it
- * to the label's allocation so that links are probably selected.
- */
- GdkEventMotion *e = (GdkEventMotion *)gdk_event_copy((GdkEvent *)event);
- e->x = event->x - (label_alloc.x - self_alloc.x);
- e->y = event->y - (label_alloc.y - self_alloc.y);
-
- gtk_widget_event(self->priv->label, (GdkEvent *)e);
-
- gdk_event_free((GdkEvent *)e);
- return FALSE;
-}
-
-/**
- * notification_menuitem_button_press:
- * @widget: the menuitem
- * @event: the button press event
- *
- * Override the menuitem button-press-event.
- **/
-static gboolean
-notification_menuitem_button_press(GtkWidget *widget, GdkEventButton *event)
-{
- g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
-
- /* The context menu breaks everything so disable it for now */
- if (event->button == GDK_BUTTON_PRIMARY && widget_contains_event(self->priv->label, event)) {
- gtk_widget_event(self->priv->label, (GdkEvent *)event);
- }
- else if (widget_contains_event(self->priv->close_image, event)) {
- self->priv->pressed_close_image = TRUE;
- }
- return TRUE;
-}
-
-/**
- * notification_menuitem_button_release:
- * @widget: the menuitem
- * @event: the button release event
- *
- * Override the menuitem button-release-event so that the menu isn't hidden
- * when the item is removed. Also the event is passed on to the label so we can
- * get an activate-link signal when a link is clicked.
- **/
-static gboolean
-notification_menuitem_button_release(GtkWidget *widget, GdkEventButton *event)
-{
- g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(widget), FALSE);
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(widget);
-
- if (widget_contains_event(self->priv->close_image, event)) {
- if (self->priv->pressed_close_image)
- g_signal_emit(NOTIFICATION_MENUITEM(widget), notification_menuitem_signals[CLICKED], 0, event->button);
- }
- else {
- /* The context menu breaks everything so disable it for now */
- if (event->button == GDK_BUTTON_PRIMARY) {
- gtk_widget_event(self->priv->label, (GdkEvent *)event);
- }
- }
- self->priv->pressed_close_image = FALSE;
- return TRUE;
-}
-
-/**
- * notification_menuitem_select:
- * @menuitem - the menuitem
- *
- * Handle the menuitem select signal. We don't want to set PRELIGHT on the
- * menuitem because in various themes it becomes very hard to see the links.
- * Instead we set a special close image to show that the notification is
- * selected for keyboard navigation.
- **/
-static void
-notification_menuitem_select(GtkMenuItem *menuitem)
-{
- g_return_if_fail(IS_NOTIFICATION_MENUITEM(menuitem));
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(menuitem);
-
- gtk_image_set_from_icon_name(GTK_IMAGE(self->priv->close_image),
- NOTIFICATION_MENUITEM_CLOSE_SELECT,
- GTK_ICON_SIZE_MENU);
-}
-
-/**
- * notification_menuitem_deselect:
- * @menuitem - the menuitem
- *
- * Same as notification_menuitem_select, but sets the opposite close image.
- **/
-static void
-notification_menuitem_deselect(GtkMenuItem *menuitem)
-{
- g_return_if_fail(IS_NOTIFICATION_MENUITEM(menuitem));
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(menuitem);
-
- gtk_image_set_from_icon_name(GTK_IMAGE(self->priv->close_image),
- NOTIFICATION_MENUITEM_CLOSE_DESELECT,
- GTK_ICON_SIZE_MENU);
-}
-
-/**
- * notification_menuitem_activate_link_cb:
- * @label - the label
- * @uri - the link that was activated
- * @user_data - the notification menuitem
- *
- * We override the activate-link signal of the GtkLabel because we need to
- * deactivate the menu shell when it is clicked, otherwise it stays stuck to
- * the screen as the browser loads.
- **/
-static gboolean
-notification_menuitem_activate_link_cb(GtkLabel *label, gchar *uri, gpointer user_data)
-{
- g_return_val_if_fail(IS_NOTIFICATION_MENUITEM(user_data), FALSE);
-
- NotificationMenuItem *self = NOTIFICATION_MENUITEM(user_data);
-
- /* Show the link */
- GError *error = NULL;
-
- if (!gtk_show_uri_on_window(NULL, uri, gtk_get_current_event_time(), &error))
- {
- g_warning("Unable to show '%s': %s", uri, error->message);
- g_error_free(error);
- }
-
- /* Deactivate the menu shell so it doesn't block the screen */
- GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(self));
- if (GTK_IS_MENU_SHELL(parent)) {
- gtk_menu_shell_deactivate(GTK_MENU_SHELL(parent));
- }
-
- return TRUE;
-}
-
-/**
- * notification_menuitem_markup_body:
- * @body - the body of a notification
- *
- * Scans through the body text escaping everything that isn't a link. The links
- * are marked up as anchors with hrefs.
- **/
-static gchar *
-notification_menuitem_markup_body(const gchar *body)
-{
- GList *list = urlregex_split_all(body);
- guint len = g_list_length(list);
- gchar **str_array = g_new0(gchar *, len + 1);
- guint i = 0;
- GList *item;
- gchar *escaped_text;
- gchar *escaped_expanded;
-
- for (item = list; item; item = item->next, i++) {
- MatchGroup *group = (MatchGroup *)item->data;
- if (group->type == MATCHED) {
- escaped_text = g_markup_escape_text(group->text, -1);
- escaped_expanded = g_markup_escape_text(group->expanded, -1);
- str_array[i] = g_strdup_printf("<a href=\"%s\">%s</a>", escaped_expanded, escaped_text);
- g_free(escaped_text);
- g_free(escaped_expanded);
- }
- else {
- str_array[i] = g_markup_escape_text(group->text, -1);
- }
- }
-
- urlregex_matchgroup_list_free(list);
- gchar *result = g_strjoinv(NULL, str_array);
- g_strfreev(str_array);
- return result;
-}
-
-/**
- * widget_contains_event:
- * @widget - the widget
- * @event - the event
- *
- * Determines whether the (x, y) coordinates of the event fall inside the
- * widget.
- **/
-static gboolean
-widget_contains_event(GtkWidget *widget, GdkEventButton *event)
-{
- if (gtk_widget_get_window(widget) == NULL)
- return FALSE;
-
- GtkAllocation allocation;
-
- gtk_widget_get_allocation(widget, &allocation);
-
- GdkWindow *window = gtk_widget_get_window(widget);
-
- int xwin, ywin;
-
- gdk_window_get_origin(window, &xwin, &ywin);
-
- int xmin = allocation.x;
- int xmax = allocation.x + allocation.width;
- int ymin = allocation.y;
- int ymax = allocation.y + allocation.height;
- int x = event->x_root - xwin;
- int y = event->y_root - ywin;
-
- return x >= xmin && x <= xmax && y >= ymin && y <= ymax;
-}
diff --git a/src/notification-menuitem.h b/src/notification-menuitem.h
deleted file mode 100644
index afb2b98..0000000
--- a/src/notification-menuitem.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * notification-menuitem.h - A menuitem to display notifications.
- */
-
-#ifndef __NOTIFICATION_MENUITEM_H__
-#define __NOTIFICATION_MENUITEM_H__
-
-#include <gtk/gtk.h>
-#include "notification.h"
-
-G_BEGIN_DECLS
-
-#define NOTIFICATION_MENUITEM_TYPE (notification_menuitem_get_type ())
-#define NOTIFICATION_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NOTIFICATION_MENUITEM_TYPE, NotificationMenuItem))
-#define NOTIFICATION_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NOTIFICATION_MENUITEM_TYPE, NotificationMenuItemClass))
-#define IS_NOTIFICATION_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NOTIFICATION_MENUITEM_TYPE))
-#define IS_NOTIFICATION_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NOTIFICATION_MENUITEM_TYPE))
-
-typedef struct _NotificationMenuItem NotificationMenuItem;
-typedef struct _NotificationMenuItemClass NotificationMenuItemClass;
-typedef struct _NotificationMenuItemPrivate NotificationMenuItemPrivate;
-
-struct _NotificationMenuItem
-{
- GtkMenuItem parent_instance;
- NotificationMenuItemPrivate *priv;
-};
-
-struct _NotificationMenuItemClass
-{
- GtkMenuItemClass parent_class;
-
- void (* clicked) (NotificationMenuItem *menuitem, guint button);
-};
-
-struct _NotificationMenuItemPrivate {
- GtkWidget *close_image;
- GtkWidget *hbox;
- GtkWidget *label;
-
- gboolean pressed_close_image;
-};
-
-#define NOTIFICATION_MENUITEM_SIGNAL_CLICKED "clicked"
-
-GType notification_menuitem_get_type(void);
-GtkWidget *notification_menuitem_new(void);
-void notification_menuitem_set_from_notification(NotificationMenuItem *self, Notification *note);
-
-G_END_DECLS
-
-#endif /* __NOTIFICATION_MENUITEM_H__ */
diff --git a/src/service.c b/src/service.c
new file mode 100644
index 0000000..2de2613
--- /dev/null
+++ b/src/service.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ * Ted Gould <ted@canonical.com>
+ *
+ * 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/>.
+ */
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "service.h"
+#include "dbus-spy.h"
+#include "urlregex.h"
+
+#define BUS_NAME "org.ayatana.indicator.notifications"
+#define BUS_PATH "/org/ayatana/indicator/notifications"
+#define HINT_MAX 10
+
+static guint m_nSignal = 0;
+
+enum
+{
+ SECTION_HEADER = (1<<0),
+ SECTION_NOTIFICATIONS = (1<<1),
+ SECTION_DO_NOT_DISTURB = (1<<2),
+ SECTION_CLEAR = (1<<3)
+};
+
+enum
+{
+ PROFILE_PHONE,
+ PROFILE_DESKTOP,
+ N_PROFILES
+};
+
+static const char * const menu_names[N_PROFILES] =
+{
+ "phone",
+ "desktop"
+};
+
+struct ProfileMenuInfo
+{
+ GMenu *pMenu;
+ GMenu *pSubmenu;
+ guint nExportId;
+};
+
+struct _IndicatorNotificationsServicePrivate
+{
+ GCancellable *pCancellable;
+ GSettings *pSettings;
+ guint nOwnId;
+ guint nActionsId;
+ GDBusConnection *pConnection;
+ gboolean bMenusBuilt;
+ struct ProfileMenuInfo lMenus[N_PROFILES];
+ GSimpleActionGroup *pActionGroup;
+ GSimpleAction *pHeaderAction;
+ GSimpleAction *pClearAction;
+ GSimpleAction *pRemoveAction;
+ GSimpleAction *pDoNotDisturbAction;
+ GList *lVisibleItems;
+ GList *lHiddenItems;
+ gboolean bDoNotDisturb;
+ gboolean bHasUnread;
+ gint nMaxItems;
+ DBusSpy *pBusSpy;
+ GHashTable *lFilters;
+ GList *lHints;
+ GMenu *pNotificationsSection;
+ gboolean bHasDoNotDisturb;
+};
+
+typedef IndicatorNotificationsServicePrivate priv_t;
+
+G_DEFINE_TYPE_WITH_PRIVATE(IndicatorNotificationsService, indicator_notifications_service, G_TYPE_OBJECT)
+
+static void rebuildNow(IndicatorNotificationsService *self, guint nSections);
+static void updateFilters(IndicatorNotificationsService *self);
+
+static void saveHints(IndicatorNotificationsService *self)
+{
+ gchar *hints[HINT_MAX + 1];
+ int i = 0;
+ GList *l;
+
+ for (l = self->priv->lHints; (l != NULL) && (i < HINT_MAX); l = l->next, i++)
+ {
+ hints[i] = (gchar *) l->data;
+ }
+
+ hints[i] = NULL;
+
+ g_settings_set_strv(self->priv->pSettings, "filter-list-hints", (const gchar **) hints);
+}
+
+static void updateHints(IndicatorNotificationsService *self, Notification *notification)
+{
+ g_return_if_fail(IS_NOTIFICATION(notification));
+
+ const gchar *appname = notification_get_app_name(notification);
+
+ // Avoid duplicates
+ GList *l;
+ for (l = self->priv->lHints; l != NULL; l = l->next)
+ {
+ if (g_strcmp0(appname, (const gchar *) l->data) == 0)
+ {
+ return;
+ }
+ }
+
+ // Add the appname
+ self->priv->lHints = g_list_prepend(self->priv->lHints, g_strdup(appname));
+
+ // Keep only a reasonable number
+ while (g_list_length(self->priv->lHints) > HINT_MAX)
+ {
+ GList *last = g_list_last(self->priv->lHints);
+ g_free(last->data);
+ self->priv->lHints = g_list_delete_link(self->priv->lHints, last);
+ }
+
+ // Save the hints
+ saveHints(self);
+}
+
+static gchar *createMarkup(const gchar *body)
+{
+ GList *list = urlregex_split_all(body);
+ guint len = g_list_length(list);
+ gchar **str_array = g_new0(gchar *, len + 1);
+ guint i = 0;
+ GList *item;
+ gchar *escaped_text;
+ gchar *escaped_expanded;
+
+ for (item = list; item; item = item->next, i++)
+ {
+ MatchGroup *group = (MatchGroup *)item->data;
+
+ if (group->type == MATCHED)
+ {
+ escaped_text = g_markup_escape_text(group->text, -1);
+ escaped_expanded = g_markup_escape_text(group->expanded, -1);
+ str_array[i] = g_strdup_printf("<a href=\"%s\">%s</a>", escaped_expanded, escaped_text);
+ g_free(escaped_text);
+ g_free(escaped_expanded);
+ }
+ else
+ {
+ str_array[i] = g_markup_escape_text(group->text, -1);
+ }
+ }
+
+ urlregex_matchgroup_list_free(list);
+ gchar *result = g_strjoinv(NULL, str_array);
+ g_strfreev(str_array);
+
+ return result;
+}
+
+static void updateClearItem(IndicatorNotificationsService *self)
+{
+ guint visible_length = g_list_length(self->priv->lVisibleItems);
+ guint hidden_length = g_list_length(self->priv->lHiddenItems);
+ guint total_length = visible_length + hidden_length;
+
+ g_simple_action_set_enabled(self->priv->pClearAction, total_length != 0);
+}
+
+static void setUnread(IndicatorNotificationsService *self, gboolean unread)
+{
+ self->priv->bHasUnread = unread;
+ rebuildNow(self, SECTION_HEADER);
+}
+
+static void onMessageReceived(DBusSpy *pBusSpy, Notification *note, gpointer user_data)
+{
+ g_return_if_fail(IS_DBUS_SPY(pBusSpy));
+ g_return_if_fail(IS_NOTIFICATION(note));
+ IndicatorNotificationsService *self = INDICATOR_NOTIFICATIONS_SERVICE(user_data);
+
+ // Discard useless notifications
+ if(notification_is_private(note) || notification_is_empty(note))
+ {
+ g_object_unref(note);
+
+ return;
+ }
+
+ // Discard notifications on the filter list
+ if(self->priv->lFilters != NULL && g_hash_table_contains(self->priv->lFilters, notification_get_app_name(note)))
+ {
+ g_object_unref(note);
+
+ return;
+ }
+
+ updateHints(self, note);
+
+ gchar *unescaped_timestamp_string = notification_timestamp_for_locale(note);
+ gchar *app_name = g_markup_escape_text(notification_get_app_name(note), -1);
+ gchar *summary = g_markup_escape_text(notification_get_summary(note), -1);
+ gchar *body = createMarkup(notification_get_body(note));
+ g_object_unref(note);
+ gchar *timestamp_string = g_markup_escape_text(unescaped_timestamp_string, -1);
+ gchar *markup = g_strdup_printf("<b>%s</b>\n%s\n<small><i>%s %s <b>%s</b></i></small>", summary, body, timestamp_string, _("from"), app_name);
+ g_free(app_name);
+ g_free(summary);
+ g_free(body);
+ g_free(unescaped_timestamp_string);
+ g_free(timestamp_string);
+ GMenuItem * item = g_menu_item_new(markup, NULL);
+ g_free(markup);
+ gint64 nTimestamp = notification_get_timestamp(note);
+ g_menu_item_set_action_and_target_value(item, "indicator.remove-notification", g_variant_new_int64(nTimestamp));
+ g_menu_item_set_attribute_value(item, "x-ayatana-timestamp", g_variant_new_int64(nTimestamp));
+ g_menu_item_set_attribute_value(item, "x-ayatana-use-markup", g_variant_new_boolean(TRUE));
+ g_menu_item_set_attribute(item, "x-ayatana-type", "s", "org.ayatana.indicator.basic");
+
+ GList *last_item;
+ GMenuItem *last_menu_item;
+
+ // List holds a ref to the menuitem
+ self->priv->lVisibleItems = g_list_prepend(self->priv->lVisibleItems, g_object_ref(item));
+ g_menu_prepend_item (self->priv->pNotificationsSection, item);
+ g_object_unref(item);
+
+ // Move items that overflow to the hidden list
+ while (g_list_length(self->priv->lVisibleItems) > self->priv->nMaxItems)
+ {
+ last_item = g_list_last(self->priv->lVisibleItems);
+ last_menu_item = G_MENU_ITEM(last_item->data);
+
+ // Steal the ref from the visible list
+ self->priv->lVisibleItems = g_list_delete_link(self->priv->lVisibleItems, last_item);
+ self->priv->lHiddenItems = g_list_prepend(self->priv->lHiddenItems, last_menu_item);
+ last_item = NULL;
+ last_menu_item = NULL;
+ }
+
+ while (g_menu_model_get_n_items(G_MENU_MODEL(self->priv->pNotificationsSection)) > self->priv->nMaxItems)
+ {
+ g_menu_remove(self->priv->pNotificationsSection, self->priv->nMaxItems);
+ }
+
+ updateClearItem(self);
+ setUnread(self, TRUE);
+}
+
+static GVariant *createHeaderState(IndicatorNotificationsService *self)
+{
+ GVariantBuilder b;
+
+ g_variant_builder_init (&b, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add (&b, "{sv}", "title", g_variant_new_string (_("Notifications")));
+ g_variant_builder_add (&b, "{sv}", "visible", g_variant_new_boolean (TRUE));
+
+ gchar *sIcon = NULL;
+
+ if (self->priv->bHasUnread)
+ {
+ if (self->priv->bHasDoNotDisturb && self->priv->bDoNotDisturb)
+ {
+ sIcon = "ayatana-indicator-notification-unread-dnd";
+ }
+ else
+ {
+ sIcon = "ayatana-indicator-notification-unread";
+ }
+ }
+ else
+ {
+ if (self->priv->bHasDoNotDisturb && self->priv->bDoNotDisturb)
+ {
+ sIcon = "ayatana-indicator-notification-read-dnd";
+ }
+ else
+ {
+ sIcon = "ayatana-indicator-notification-read";
+ }
+ }
+
+ GIcon * icon = g_themed_icon_new_with_default_fallbacks(sIcon);
+ g_variant_builder_add (&b, "{sv}", "accessible-desc", g_variant_new_string (_("Notifications")));
+
+ if (icon)
+ {
+ GVariant * serialized_icon = g_icon_serialize (icon);
+
+ if (serialized_icon != NULL)
+ {
+ g_variant_builder_add (&b, "{sv}", "icon", serialized_icon);
+ g_variant_unref (serialized_icon);
+ }
+
+ g_object_unref (icon);
+ }
+
+ return g_variant_builder_end (&b);
+}
+
+static GMenuModel *createDesktopNotificationsSection(IndicatorNotificationsService *self, int profile)
+{
+ self->priv->pNotificationsSection = g_menu_new();
+
+ return G_MENU_MODEL(self->priv->pNotificationsSection);
+}
+
+static GMenuModel *createDesktopClearSection(IndicatorNotificationsService *self)
+{
+ GMenu * pMenu = g_menu_new ();
+ g_menu_append(pMenu, _("Clear"), "indicator.clear-notifications");
+
+ return G_MENU_MODEL(pMenu);
+}
+
+static GMenuModel *createDesktopDoNotDisturbSection(IndicatorNotificationsService *self)
+{
+ GMenu *pMenu = g_menu_new();
+
+ if (self->priv->bHasDoNotDisturb)
+ {
+ GMenuItem *item = g_menu_item_new(_("Do not disturb"), NULL);
+ g_menu_item_set_attribute(item, "x-ayatana-type", "s", "org.ayatana.indicator.switch");
+ g_menu_item_set_action_and_target(item, "indicator.do-not-disturb", NULL);
+ g_menu_append_item(pMenu, item);
+ g_object_unref(item);
+ }
+
+ return G_MENU_MODEL(pMenu);
+}
+
+static void rebuildSection(GMenu *parent, int pos, GMenuModel *new_section)
+{
+ g_menu_remove (parent, pos);
+ g_menu_insert_section (parent, pos, NULL, new_section);
+ g_object_unref (new_section);
+}
+
+static void rebuildNow(IndicatorNotificationsService *self, guint sections)
+{
+ priv_t *p = self->priv;
+ struct ProfileMenuInfo *desktop = &p->lMenus[PROFILE_DESKTOP];
+
+ if (sections & SECTION_HEADER)
+ {
+ g_simple_action_set_state (p->pHeaderAction, createHeaderState (self));
+ }
+
+ if (!p->bMenusBuilt)
+ {
+ return;
+ }
+
+ if (sections & SECTION_NOTIFICATIONS)
+ {
+ rebuildSection (desktop->pSubmenu, 0, createDesktopNotificationsSection (self, PROFILE_DESKTOP));
+ }
+
+ if (sections & SECTION_DO_NOT_DISTURB)
+ {
+ rebuildSection (desktop->pSubmenu, 1, createDesktopDoNotDisturbSection (self));
+ }
+
+ if (sections & SECTION_CLEAR)
+ {
+ rebuildSection (desktop->pSubmenu, 2, createDesktopClearSection (self));
+ }
+}
+
+static void createMenu(IndicatorNotificationsService *self, int profile)
+{
+ GMenu * pMenu;
+ GMenu * pSubmenu;
+ GMenuItem * header;
+ GMenuModel * sections[16];
+ guint i;
+ guint n = 0;
+
+ g_assert (0 <= profile && profile < N_PROFILES);
+ g_assert (self->priv->lMenus[profile].pMenu == NULL);
+
+ // Build the sections
+ switch (profile)
+ {
+ case PROFILE_PHONE:
+ case PROFILE_DESKTOP:
+ {
+ sections[n++] = createDesktopNotificationsSection(self, PROFILE_DESKTOP);
+ sections[n++] = createDesktopDoNotDisturbSection(self);
+ sections[n++] = createDesktopClearSection(self);
+
+ break;
+ }
+
+ break;
+ }
+
+ // Add sections to the submenu
+ pSubmenu = g_menu_new();
+
+ for (i=0; i<n; ++i)
+ {
+ g_menu_append_section (pSubmenu, NULL, sections[i]);
+ g_object_unref (sections[i]);
+ }
+
+ // Add submenu to the header
+ header = g_menu_item_new (NULL, "indicator._header");
+ g_menu_item_set_attribute (header, "x-ayatana-type", "s", "org.ayatana.indicator.root");
+ g_menu_item_set_submenu(header, G_MENU_MODEL (pSubmenu));
+ g_object_unref(pSubmenu);
+
+ // Add header to the menu
+ pMenu = g_menu_new ();
+ g_menu_append_item (pMenu, header);
+ g_object_unref (header);
+
+ self->priv->lMenus[profile].pMenu = pMenu;
+ self->priv->lMenus[profile].pSubmenu = pSubmenu;
+}
+
+static void clearMenuItems(IndicatorNotificationsService *self)
+{
+ // Remove each visible item from the menu
+ while (g_menu_model_get_n_items(G_MENU_MODEL(self->priv->pNotificationsSection)) > 0)
+ {
+ g_menu_remove(self->priv->pNotificationsSection, 0);
+ }
+
+ // Clear the lists
+ g_list_free_full(self->priv->lVisibleItems, g_object_unref);
+ self->priv->lVisibleItems = NULL;
+
+ g_list_free_full(self->priv->lHiddenItems, g_object_unref);
+ self->priv->lHiddenItems = NULL;
+
+ updateClearItem(self);
+}
+
+static void onRemoveNotification(GSimpleAction *a, GVariant *param, gpointer user_data)
+{
+ IndicatorNotificationsService *self = INDICATOR_NOTIFICATIONS_SERVICE(user_data);
+ guint nItems = g_menu_model_get_n_items(G_MENU_MODEL(self->priv->pNotificationsSection));
+ gint64 nTimestampIn = g_variant_get_int64(param);
+
+ for (guint nItem = 0; nItem < nItems; nItem++)
+ {
+ gint64 nTimestamp;
+ g_menu_model_get_item_attribute(G_MENU_MODEL(self->priv->pNotificationsSection), nItem, "x-ayatana-timestamp", "x", &nTimestamp);
+
+ if (nTimestamp == nTimestampIn)
+ {
+ g_menu_remove(self->priv->pNotificationsSection, nItem);
+
+ break;
+ }
+ }
+
+ for (GList *pItem = self->priv->lVisibleItems; pItem; pItem = pItem->next)
+ {
+ gint64 nTimestamp;
+ g_menu_item_get_attribute(G_MENU_ITEM(pItem->data), "x-ayatana-timestamp", "x", &nTimestamp);
+
+ if (nTimestamp == nTimestampIn)
+ {
+ // Remove the item
+ self->priv->lVisibleItems = g_list_delete_link(self->priv->lVisibleItems, pItem);
+
+ // Add an item from the hidden list, if available
+ if (g_list_length(self->priv->lHiddenItems) > 0)
+ {
+ pItem = g_list_first(self->priv->lHiddenItems);
+ GMenuItem *pMenuItem = G_MENU_ITEM(pItem->data);
+ self->priv->lHiddenItems = g_list_delete_link(self->priv->lHiddenItems, pItem);
+ g_menu_append_item(self->priv->pNotificationsSection, pMenuItem);
+
+ // Steal the ref back from the hidden list
+ self->priv->lVisibleItems = g_list_append(self->priv->lVisibleItems, pMenuItem);
+ }
+
+ updateClearItem(self);
+
+ break;
+ }
+ }
+}
+
+static void onClear(GSimpleAction *a, GVariant *param, gpointer user_data)
+{
+ IndicatorNotificationsService *self = INDICATOR_NOTIFICATIONS_SERVICE(user_data);
+
+ clearMenuItems(self);
+ setUnread(self, FALSE);
+}
+
+static void onDoNotDisturb(GSimpleAction *a, GVariant *param, gpointer user_data)
+{
+ IndicatorNotificationsService *self = INDICATOR_NOTIFICATIONS_SERVICE(user_data);
+
+ if (self->priv->bHasDoNotDisturb)
+ {
+ self->priv->bDoNotDisturb = g_variant_get_boolean(param);
+ GSettings *pSettings = g_settings_new("org.mate.NotificationDaemon");
+ g_settings_set_boolean(pSettings, "do-not-disturb", self->priv->bDoNotDisturb);
+ g_object_unref(pSettings);
+ g_settings_set_boolean(self->priv->pSettings, "do-not-disturb", self->priv->bDoNotDisturb);
+ rebuildNow(self, SECTION_HEADER);
+ }
+}
+
+static void onSettingsChanged(GSettings *pSettings, gchar *key, gpointer user_data)
+{
+ IndicatorNotificationsService *self = INDICATOR_NOTIFICATIONS_SERVICE(user_data);
+
+ if (g_str_equal(key, "filter-list"))
+ {
+ updateFilters(self);
+ }
+ else if (g_str_equal(key, "do-not-disturb"))
+ {
+ if (self->priv->bHasDoNotDisturb)
+ {
+ self->priv->bDoNotDisturb = g_settings_get_boolean(self->priv->pSettings, key);
+ GSettings *pSettings = g_settings_new("org.mate.NotificationDaemon");
+ g_settings_set_boolean(pSettings, key, self->priv->bDoNotDisturb);
+ g_object_unref(pSettings);
+ g_action_change_state(G_ACTION(self->priv->pDoNotDisturbAction), g_variant_new_boolean(self->priv->bDoNotDisturb));
+ rebuildNow(self, SECTION_HEADER);
+ }
+ else if (g_settings_get_boolean(self->priv->pSettings, key))
+ {
+ g_settings_set_boolean(self->priv->pSettings, key, FALSE);
+ }
+ }
+}
+
+static void initActions(IndicatorNotificationsService *self)
+{
+ GSimpleAction * a;
+ GAction *max_items_action;
+ self->priv->pActionGroup = g_simple_action_group_new();
+
+ // Add the header action
+ a = g_simple_action_new_stateful ("_header", NULL, createHeaderState(self));
+ g_action_map_add_action(G_ACTION_MAP(self->priv->pActionGroup), G_ACTION(a));
+ self->priv->pHeaderAction = a;
+
+ // Add the remove action
+ a = g_simple_action_new("remove-notification", G_VARIANT_TYPE_INT64);
+ g_action_map_add_action(G_ACTION_MAP(self->priv->pActionGroup), G_ACTION(a));
+ self->priv->pRemoveAction = a;
+ g_signal_connect(a, "activate", G_CALLBACK(onRemoveNotification), self);
+
+ a = g_simple_action_new("clear-notifications", NULL);
+ g_simple_action_set_enabled (a, FALSE);
+ g_action_map_add_action(G_ACTION_MAP(self->priv->pActionGroup), G_ACTION(a));
+ self->priv->pClearAction = a;
+ g_signal_connect(a, "activate", G_CALLBACK(onClear), self);
+
+ if (self->priv->bHasDoNotDisturb)
+ {
+ a = g_simple_action_new_stateful("do-not-disturb", G_VARIANT_TYPE_BOOLEAN, g_variant_new_boolean(self->priv->bDoNotDisturb));
+ g_action_map_add_action(G_ACTION_MAP(self->priv->pActionGroup), G_ACTION(a));
+ self->priv->pDoNotDisturbAction = a;
+ g_signal_connect(a, "activate", G_CALLBACK(onDoNotDisturb), self);
+ }
+
+ // Add the max-items action
+ max_items_action = g_settings_create_action(self->priv->pSettings, "max-items");
+ g_action_map_add_action(G_ACTION_MAP(self->priv->pActionGroup), max_items_action);
+
+ g_signal_connect(self->priv->pSettings, "changed", G_CALLBACK(onSettingsChanged), self);
+
+ rebuildNow(self, SECTION_HEADER);
+
+ g_object_unref(max_items_action);
+}
+
+static void onBusAcquired(GDBusConnection *connection, const gchar *name, gpointer gself)
+{
+ int i;
+ guint id;
+ GError * err = NULL;
+ IndicatorNotificationsService * self = INDICATOR_NOTIFICATIONS_SERVICE(gself);
+ priv_t * p = self->priv;
+ GString * path = g_string_new (NULL);
+
+ g_debug ("bus acquired: %s", name);
+
+ p->pConnection = (GDBusConnection*)g_object_ref(G_OBJECT (connection));
+
+ // Export the actions
+ if ((id = g_dbus_connection_export_action_group (connection, BUS_PATH, G_ACTION_GROUP (p->pActionGroup), &err)))
+ {
+ p->nActionsId = id;
+ }
+ else
+ {
+ g_warning ("cannot export action group: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ // Export the menus
+ for (i=0; i<N_PROFILES; ++i)
+ {
+ struct ProfileMenuInfo * menu = &p->lMenus[i];
+
+ g_string_printf (path, "%s/%s", BUS_PATH, menu_names[i]);
+
+ if ((id = g_dbus_connection_export_menu_model (connection, path->str, G_MENU_MODEL (menu->pMenu), &err)))
+ {
+ menu->nExportId = id;
+ }
+ else
+ {
+ g_warning ("cannot export %s menu: %s", path->str, err->message);
+ g_clear_error (&err);
+ }
+ }
+
+ g_string_free (path, TRUE);
+}
+
+static void unexport(IndicatorNotificationsService *self)
+{
+ int i;
+ priv_t * p = self->priv;
+
+ // Unexport the menus
+ for (i=0; i<N_PROFILES; ++i)
+ {
+ guint * id = &self->priv->lMenus[i].nExportId;
+
+ if (*id)
+ {
+ g_dbus_connection_unexport_menu_model (p->pConnection, *id);
+ *id = 0;
+ }
+ }
+
+ // Unexport the actions
+ if (p->nActionsId)
+ {
+ g_dbus_connection_unexport_action_group (p->pConnection, p->nActionsId);
+ p->nActionsId = 0;
+ }
+}
+
+static void onNameLost(GDBusConnection *connection, const gchar *name, gpointer gself)
+{
+ IndicatorNotificationsService * self = INDICATOR_NOTIFICATIONS_SERVICE (gself);
+
+ g_debug("%s %s name lost %s", G_STRLOC, G_STRFUNC, name);
+
+ unexport(self);
+}
+
+static void onDispose(GObject *o)
+{
+ IndicatorNotificationsService * self = INDICATOR_NOTIFICATIONS_SERVICE(o);
+ priv_t * p = self->priv;
+
+ if (self->priv->lVisibleItems != NULL)
+ {
+ g_list_free_full(self->priv->lVisibleItems, g_object_unref);
+ self->priv->lVisibleItems = NULL;
+ }
+
+ if (self->priv->lHiddenItems != NULL)
+ {
+ g_list_free_full(self->priv->lHiddenItems, g_object_unref);
+ self->priv->lHiddenItems = NULL;
+ }
+
+ if (self->priv->pBusSpy != NULL)
+ {
+ g_object_unref(G_OBJECT(self->priv->pBusSpy));
+ self->priv->pBusSpy = NULL;
+ }
+
+ if(self->priv->lFilters != NULL)
+ {
+ g_hash_table_unref(self->priv->lFilters);
+ self->priv->lFilters = NULL;
+ }
+
+ if(self->priv->lHints != NULL)
+ {
+ g_list_free_full(self->priv->lHints, g_free);
+ self->priv->lHints = NULL;
+ }
+
+ if (p->nOwnId)
+ {
+ g_bus_unown_name (p->nOwnId);
+ p->nOwnId = 0;
+ }
+
+ unexport (self);
+
+ if (p->pCancellable != NULL)
+ {
+ g_cancellable_cancel (p->pCancellable);
+ g_clear_object (&p->pCancellable);
+ }
+
+ if (p->pSettings != NULL)
+ {
+ g_signal_handlers_disconnect_by_data (p->pSettings, self);
+ g_clear_object (&p->pSettings);
+ }
+
+ if (self->priv->bHasDoNotDisturb)
+ {
+ g_clear_object(&p->pDoNotDisturbAction);
+ }
+
+ g_clear_object (&p->pClearAction);
+ g_clear_object (&p->pRemoveAction);
+ g_clear_object (&p->pHeaderAction);
+ g_clear_object (&p->pActionGroup);
+ g_clear_object (&p->pConnection);
+
+ G_OBJECT_CLASS (indicator_notifications_service_parent_class)->dispose (o);
+}
+
+static void updateFilters(IndicatorNotificationsService *self)
+{
+ g_return_if_fail(self->priv->lFilters != NULL);
+
+ g_hash_table_remove_all(self->priv->lFilters);
+ gchar **items = g_settings_get_strv(self->priv->pSettings, "filter-list");
+ int i;
+
+ for(i = 0; items[i] != NULL; i++)
+ {
+ g_hash_table_insert(self->priv->lFilters, g_strdup(items[i]), NULL);
+ }
+
+ g_strfreev(items);
+}
+
+static void loadHints(IndicatorNotificationsService *self)
+{
+ g_return_if_fail(self->priv->lHints == NULL);
+
+ gchar **items = g_settings_get_strv(self->priv->pSettings, "filter-list-hints");
+ int i;
+
+ for (i = 0; items[i] != NULL; i++)
+ {
+ self->priv->lHints = g_list_prepend(self->priv->lHints, items[i]);
+ }
+
+ g_free(items);
+}
+
+static gboolean getDoNotDisturb()
+{
+ // Check if we can access the schema
+ GSettingsSchemaSource *source = g_settings_schema_source_get_default();
+
+ if (source == NULL)
+ {
+ return FALSE;
+ }
+
+ // Lookup the schema
+ GSettingsSchema *source_schema = g_settings_schema_source_lookup(source, "org.mate.NotificationDaemon", TRUE);
+
+ // Couldn't find the schema
+ if (source_schema == NULL)
+ {
+ return FALSE;
+ }
+
+ gboolean bResult = FALSE;
+
+ // Found the schema, make sure we have the key
+ if (g_settings_schema_has_key(source_schema, "do-not-disturb"))
+ {
+ // Make sure the key is of boolean type
+ GSettingsSchemaKey *source_key = g_settings_schema_get_key(source_schema, "do-not-disturb");
+
+ if (g_variant_type_equal(g_settings_schema_key_get_value_type(source_key), G_VARIANT_TYPE_BOOLEAN))
+ {
+ bResult = TRUE;
+ }
+
+ g_settings_schema_key_unref(source_key);
+ }
+
+ g_settings_schema_unref(source_schema);
+
+ return bResult;
+}
+
+static void indicator_notifications_service_init(IndicatorNotificationsService *self)
+{
+ int i;
+ self->priv = indicator_notifications_service_get_instance_private(self);
+ self->priv->pCancellable = g_cancellable_new();
+ self->priv->bHasDoNotDisturb = getDoNotDisturb();
+ self->priv->pSettings = g_settings_new("org.ayatana.indicator.notifications");
+ self->priv->bHasUnread = FALSE;
+ self->priv->lVisibleItems = NULL;
+ self->priv->lHiddenItems = NULL;
+
+ // Watch for notifications from dbus
+ self->priv->pBusSpy = dbus_spy_new();
+ g_signal_connect(self->priv->pBusSpy, DBUS_SPY_SIGNAL_MESSAGE_RECEIVED, G_CALLBACK(onMessageReceived), self);
+
+ // Initialize an empty filter list
+ self->priv->lFilters = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ self->priv->nMaxItems = g_settings_get_int(self->priv->pSettings, "max-items");
+
+ if (self->priv->bHasDoNotDisturb)
+ {
+ self->priv->bDoNotDisturb = g_settings_get_boolean(self->priv->pSettings, "do-not-disturb");
+ }
+
+ updateFilters(self);
+
+ // Set up filter-list hints
+ self->priv->lHints = NULL;
+ loadHints(self);
+
+ initActions(self);
+
+ for (i=0; i<N_PROFILES; ++i)
+ {
+ createMenu(self, i);
+ }
+
+ self->priv->bMenusBuilt = TRUE;
+ self->priv->nOwnId = g_bus_own_name(G_BUS_TYPE_SESSION, BUS_NAME, G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT, onBusAcquired, NULL, onNameLost, self, NULL);
+}
+
+static void indicator_notifications_service_class_init(IndicatorNotificationsServiceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+ object_class->dispose = onDispose;
+ m_nSignal = g_signal_new(INDICATOR_NOTIFICATIONS_SERVICE_SIGNAL_NAME_LOST, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (IndicatorNotificationsServiceClass, name_lost), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+IndicatorNotificationsService *indicator_notifications_service_new()
+{
+ GObject *o = g_object_new(INDICATOR_TYPE_NOTIFICATIONS_SERVICE, NULL);
+
+ return INDICATOR_NOTIFICATIONS_SERVICE(o);
+}
diff --git a/src/service.h b/src/service.h
new file mode 100644
index 0000000..45621ca
--- /dev/null
+++ b/src/service.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2013 Canonical Ltd.
+ *
+ * Authors:
+ * Charles Kerr <charles.kerr@canonical.com>
+ *
+ * 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/>.
+ */
+
+#ifndef __INDICATOR_NOTIFICATIONS_SERVICE_H__
+#define __INDICATOR_NOTIFICATIONS_SERVICE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* standard GObject macros */
+#define INDICATOR_NOTIFICATIONS_SERVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_NOTIFICATIONS_SERVICE, IndicatorNotificationsService))
+#define INDICATOR_TYPE_NOTIFICATIONS_SERVICE (indicator_notifications_service_get_type())
+#define INDICATOR_IS_NOTIFICATIONS_SERVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_NOTIFICATIONS_SERVICE))
+
+typedef struct _IndicatorNotificationsService IndicatorNotificationsService;
+typedef struct _IndicatorNotificationsServiceClass IndicatorNotificationsServiceClass;
+typedef struct _IndicatorNotificationsServicePrivate IndicatorNotificationsServicePrivate;
+
+/* signal keys */
+#define INDICATOR_NOTIFICATIONS_SERVICE_SIGNAL_NAME_LOST "name-lost"
+
+/**
+ * The Indicator Notifications Service.
+ */
+struct _IndicatorNotificationsService
+{
+ /*< private >*/
+ GObject parent;
+ IndicatorNotificationsServicePrivate * priv;
+};
+
+struct _IndicatorNotificationsServiceClass
+{
+ GObjectClass parent_class;
+
+ /* signals */
+
+ void (* name_lost)(IndicatorNotificationsService * self);
+};
+
+GType indicator_notifications_service_get_type (void);
+
+IndicatorNotificationsService *indicator_notifications_service_new();
+
+G_END_DECLS
+
+#endif /* __INDICATOR_NOTIFICATIONS_SERVICE_H__ */
diff --git a/src/settings.h b/src/settings.h
deleted file mode 100644
index 9663c8d..0000000
--- a/src/settings.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * settings.h - Definitions of settings keys and schema.
- */
-
-#ifndef __SETTINGS_H__
-#define __SETTINGS_H__
-
-#define NOTIFICATIONS_SCHEMA "org.ayatana.indicator.notifications"
-#define NOTIFICATIONS_KEY_FILTER_LIST "filter-list"
-#define NOTIFICATIONS_KEY_FILTER_LIST_HINTS "filter-list-hints"
-#define NOTIFICATIONS_KEY_CLEAR_MC "clear-on-middle-click"
-#define NOTIFICATIONS_KEY_DND "do-not-disturb"
-#define NOTIFICATIONS_KEY_HIDE_INDICATOR "hide-indicator"
-#define NOTIFICATIONS_KEY_MAX_ITEMS "max-items"
-#define NOTIFICATIONS_KEY_SWAP_CLEAR_SETTINGS "swap-clear-settings"
-
-#define MATE_SCHEMA "org.mate.NotificationDaemon"
-#define MATE_KEY_DND "do-not-disturb"
-
-#endif
diff --git a/tests/Makefile.am b/tests/Makefile.am
deleted file mode 100644
index e69de29..0000000
--- a/tests/Makefile.am
+++ /dev/null