aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore9
-rw-r--r--Makefile.am12
-rw-r--r--bindings/mono/ApplicationIndicator.custom8
-rw-r--r--bindings/mono/AssemblyInfo.cs2
-rw-r--r--bindings/mono/Makefile.am81
-rw-r--r--bindings/mono/app-indicator.sources.xml4
-rw-r--r--bindings/mono/appindicator-sharp.dll.config.in2
-rw-r--r--bindings/mono/libappindicator-api.metadata21
-rw-r--r--bindings/mono/policy.0.0.appindicator-sharp.config.in10
-rw-r--r--bindings/mono/policy.0.1.appindicator-sharp.config.in10
-rw-r--r--bindings/python/appindicator.defs53
-rw-r--r--configure.ac4
-rw-r--r--data/Makefile.am10
-rw-r--r--data/ordering-override.keyfile5
-rw-r--r--docs/reference/Makefile.am1
-rw-r--r--docs/reference/libappindicator-sections.txt9
-rw-r--r--example/simple-client.c41
-rw-r--r--src/Makefile.am17
-rw-r--r--src/app-indicator.c487
-rw-r--r--src/app-indicator.h55
-rw-r--r--src/application-service-appstore.c780
-rw-r--r--src/application-service-appstore.h20
-rw-r--r--src/application-service-lru-file.c473
-rw-r--r--src/application-service-lru-file.h59
-rw-r--r--src/application-service-marshal.list5
-rw-r--r--src/application-service-watcher.c137
-rw-r--r--src/application-service-watcher.h7
-rw-r--r--src/application-service.c9
-rw-r--r--src/application-service.xml14
-rw-r--r--src/dbus-properties-client.h139
-rw-r--r--src/dbus-shared.h2
-rw-r--r--src/generate-id.c69
-rw-r--r--src/generate-id.h30
-rw-r--r--src/indicator-application.c274
-rw-r--r--src/notification-approver.xml29
-rw-r--r--src/notification-item.xml10
-rw-r--r--src/notification-watcher.xml28
-rw-r--r--tests/Makefile.am28
-rw-r--r--tests/test-approver.c179
-rw-r--r--tests/test-libappindicator.c133
40 files changed, 2321 insertions, 945 deletions
diff --git a/.bzrignore b/.bzrignore
index df407b6..6f66217 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -106,3 +106,12 @@ src/AppIndicator-0.1.typelib
docs/reference/libappindicator-decl.txt.bak
docs/reference/libappindicator-decl-list.txt.bak
src/AppIndicator-0.1.vapi
+bindings/mono/policy.appindicator-sharp.dll
+src/notification-approver-client.h
+src/notification-approver-server.h
+tests/test-approver
+tests/test-approver-tester
+bindings/mono/policy.0.0.appindicator-sharp.config
+bindings/mono/policy.0.0.appindicator-sharp.dll
+src/libapplication_la-generate-id.lo
+src/libappindicator_la-generate-id.lo
diff --git a/Makefile.am b/Makefile.am
index ba772b0..98e4a88 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,3 +30,15 @@ dist-hook:
else \
echo Failed to generate ChangeLog: not a branch >&2; \
fi
+ @if test -d "$(top_srcdir)/.bzr"; \
+ then \
+ echo Creating AUTHORS && \
+ ( cd "$(top_srcdir)" && \
+ echo '# Generated by Makefile. Do not edit.'; echo; \
+ $(top_srcdir)/missing --run bzr log --long --levels=0 | grep -e "^\s*author:" -e "^\s*committer:" | cut -d ":" -f 2 | cut -d "<" -f 1 | sort -u) > AUTHORS.tmp \
+ && mv -f AUTHORS.tmp $(top_distdir)/AUTHORS \
+ || (rm -f AUTHORS.tmp; \
+ echo Failed to generate AUTHORS >&2 ); \
+ else \
+ echo Failed to generate AUTHORS: not a branch >&2; \
+ fi
diff --git a/bindings/mono/ApplicationIndicator.custom b/bindings/mono/ApplicationIndicator.custom
index 11d16a8..fcc52d7 100644
--- a/bindings/mono/ApplicationIndicator.custom
+++ b/bindings/mono/ApplicationIndicator.custom
@@ -1,10 +1,10 @@
-[DllImport ("libappindicator.so.0")]
+[DllImport ("appindicator.dll")]
static extern int app_indicator_get_status (IntPtr i);
-[DllImport ("libappindicator.so.0")]
+[DllImport ("appindicator.dll")]
static extern int app_indicator_get_category (IntPtr i);
-[DllImport ("libappindicator.so.0")]
+[DllImport ("appindicator.dll")]
static extern void app_indicator_set_status (IntPtr i, int s);
[GLib.Property ("status")]
@@ -23,4 +23,4 @@ static extern void app_indicator_set_status (IntPtr i, int s);
get {
return (Category) app_indicator_get_category (Handle);
}
- } \ No newline at end of file
+ }
diff --git a/bindings/mono/AssemblyInfo.cs b/bindings/mono/AssemblyInfo.cs
index 269981a..0417db6 100644
--- a/bindings/mono/AssemblyInfo.cs
+++ b/bindings/mono/AssemblyInfo.cs
@@ -27,4 +27,4 @@ using System.Reflection;
[assembly: AssemblyCopyright ("© 2010 Canonical, Ltd.")]
[assembly: AssemblyTrademark ("")]
[assembly: AssemblyCulture ("")]
-[assembly: AssemblyVersion ("0.1")]
+[assembly: AssemblyVersion ("0.2")]
diff --git a/bindings/mono/Makefile.am b/bindings/mono/Makefile.am
index 4d2d8b4..dfaa41f 100644
--- a/bindings/mono/Makefile.am
+++ b/bindings/mono/Makefile.am
@@ -10,11 +10,43 @@ MIDDLE_API = libappindicator-api.middle
RAW_API = libappindicator-api.raw
METADATA = libappindicator-api.metadata
ASSEMBLY_NAME = appindicator-sharp
-ASSEMBLY = appindicator-sharp.dll
-TARGET = $(ASSEMBLY) $(ASSEMBLY).config
+ASSEMBLY_VERSION = 0.2.0.0
+ASSEMBLY = $(ASSEMBLY_NAME).dll
+POLICY = policy.$(POLICY_VERSION).$(ASSEMBLY_NAME)
+POLICY_VERSION = 0.0
+DLLPOLICY = $(POLICY).dll
+POLICY1 = policy.$(POLICY1_VERSION).$(ASSEMBLY_NAME)
+POLICY1_VERSION = 0.1
+DLLPOLICY1 = $(POLICY1).dll
+WRAPPER_FREE_BINDING_SRC = $(top_srcdir)/src/app-indicator.c
+WRAPPER_FREE_BINDING = app-indicator.c
+
+TARGET = \
+ $(ASSEMBLY) \
+ $(ASSEMBLY).config \
+ $(DLLPOLICY) \
+ $(POLICY).config \
+ $(DLLPOLICY1) \
+ $(POLICY1).config
+
assemblydir = $(libdir)/cli/appindicator-sharp-0.1
assembly_DATA = $(TARGET)
-CLEANFILES = $(ASSEMBLY) $(ASSEMBLY).mdb generated-stamp generated/*.cs $(API) $(MIDDLE_API) $(RAW_API) $(TEST)
+
+CLEANFILES = \
+ $(ASSEMBLY) \
+ $(ASSEMBLY).mdb \
+ generated-stamp \
+ generated/*.cs \
+ $(API) \
+ $(MIDDLE_API) \
+ $(RAW_API) \
+ $(TEST) \
+ $(DLLPOLICY) \
+ $(DLLPOLICY1) \
+ $(WRAPPER_FREE_BINDING) \
+ $(POLICY).config \
+ $(POLICY1).config
+
DISTCLEANFILES = $(ASSEMBLY).config
TEST_SOURCES = TestIndicator.cs
customs = ApplicationIndicator.custom
@@ -26,16 +58,21 @@ EXTRA_DIST = \
appindicator-sharp.dll.config.in \
app-indicator.sources.xml \
$(ASSEMBLY_NAME).snk \
+ $(POLICY).config.in \
+ $(POLICY1).config.in \
$(customs) \
$(TEST_SOURCES)
-GACUTIL_FLAGS="-package $(ASSEMBLY_NAME) -root $(DESTDIR)$(prefix)/lib"
+GACUTIL_FLAGS=-package $(ASSEMBLY_NAME) -root $(DESTDIR)$(prefix)/lib
references = $(GTK_SHARP_LIBS)
test_references = $(GTK_SHARP_LIBS) $(NUNIT_LIBS) -r:$(ASSEMBLY)
-$(RAW_API): app-indicator.sources.xml
- $(GAPI_PARSER) app-indicator.sources.xml
+$(RAW_API): app-indicator.sources.xml $(WRAPPER_FREE_BINDING)
+ $(GAPI_PARSER) $(srcdir)/app-indicator.sources.xml
+
+$(WRAPPER_FREE_BINDING): $(WRAPPER_FREE_BINDING_SRC)
+ sed '/signals\[X_NEW_LABEL\] /,+6d' $(WRAPPER_FREE_BINDING_SRC) > $(WRAPPER_FREE_BINDING)
$(MIDDLE_API): $(METADATA) $(RAW_API)
cp $(srcdir)/$(RAW_API) $(MIDDLE_API)
@@ -54,6 +91,9 @@ $(API): $(MIDDLE_API) Makefile.am
-e "s|PROP_ICON_THEME_PATH_S|icon-theme-path|" \
-e "s|PROP_MENU_S|menu|" \
-e "s|PROP_CONNECTED_S|connected|" \
+ -e "s|PROP_LABEL_S|label|" \
+ -e "s|PROP_LABEL_GUIDE_S|label-guide|" \
+ -e "s|PROP_ORDERING_INDEX_S|ordering-index|" \
$< > $@
api_includes = $(GTK_SHARP_CFLAGS)
@@ -72,14 +112,35 @@ $(ASSEMBLY): generated-stamp $(srcdir)/AssemblyInfo.cs
$(CSC) $(CSFLAGS) -keyfile:$(srcdir)/$(ASSEMBLY_NAME).snk -nowarn:0169,0612,0618 -unsafe -out:$(ASSEMBLY) -target:library $(references) $(builddir)/$(GENERATED_SOURCES) $(srcdir)/AssemblyInfo.cs
install-data-local:
- echo "$(GACUTIL) -i $(ASSEMBLY_NAME).dll -package $(ASSEMBLY_NAME) -root $(DESTDIR)$(prefix)/lib"; \
- $(GACUTIL) -i $(ASSEMBLY_NAME).dll -package $(ASSEMBLY_NAME) -root $(DESTDIR)$(prefix)/lib || exit 1;
+ echo "$(GACUTIL) -i $(ASSEMBLY_NAME).dll $(GACUTIL_FLAGS)"; \
+ $(GACUTIL) -i $(ASSEMBLY_NAME).dll $(GACUTIL_FLAGS) || exit 1;
+ echo "$(GACUTIL) -i $(DLLPOLICY) $(GACUTIL_FLAGS)"; \
+ $(GACUTIL) -i $(DLLPOLICY) $(GACUTIL_FLAGS) || exit 1;
uninstall-local:
- echo "$(GACUTIL) -u $(ASSEMBLY_NAME) -package $(ASSEMBLY_NAME) -root $(DESTDIR)$(prefix)/lib"; \
- $(GACUTIL) -u $(ASSEMBLY_NAME) -package $(ASSEMBLY_NAME) -root $(DESTDIR)$(prefix)/lib || exit 1;
+ echo "$(GACUTIL) -u $(ASSEMBLY_NAME) $(GACUTIL_FLAGS)"; \
+ $(GACUTIL) -u $(ASSEMBLY_NAME) $(GACUTIL_FLAGS) || exit 1;
+ echo "$(GACUTIL) -u $(DLLPOLICY) $(GACUTIL_FLAGS)"; \
+ $(GACUTIL) -u $(POLICY) $(GACUTIL_FLAGS) || exit 1;
$(TEST): $(ASSEMBLY) $(TEST_SOURCES)
$(CSC) -out:$(TEST) -target:library $(test_references) $(srcdir)/$(TEST_SOURCES)
all: $(TEST)
+
+$(POLICY).config: $(POLICY).config.in Makefile
+ sed -e "s|@ASSEMBLY_NAME@|$(ASSEMBLY_NAME)|" \
+ -e "s|@ASSEMBLY_VERSION@|$(ASSEMBLY_VERSION)|g" \
+ $< > $@
+
+$(DLLPOLICY): $(POLICY).config $(ASSEMBLY_NAME).snk Makefile
+ $(AL) -link:$(POLICY).config -out:$(DLLPOLICY) -keyfile:$(srcdir)/$(ASSEMBLY_NAME).snk
+
+
+$(POLICY1).config: $(POLICY1).config.in Makefile
+ sed -e "s|@ASSEMBLY_NAME@|$(ASSEMBLY_NAME)|" \
+ -e "s|@ASSEMBLY_VERSION@|$(ASSEMBLY_VERSION)|g" \
+ $< > $@
+
+$(DLLPOLICY1): $(POLICY1).config $(ASSEMBLY_NAME).snk Makefile
+ $(AL) -link:$(POLICY1).config -out:$(DLLPOLICY1) -keyfile:$(srcdir)/$(ASSEMBLY_NAME).snk
diff --git a/bindings/mono/app-indicator.sources.xml b/bindings/mono/app-indicator.sources.xml
index 2649eb4..6cf28e9 100644
--- a/bindings/mono/app-indicator.sources.xml
+++ b/bindings/mono/app-indicator.sources.xml
@@ -1,8 +1,8 @@
<gapi-parser-input>
<api filename="libappindicator-api.raw">
- <library name="libappindicator.so.0">
+ <library name="appindicator.dll">
<namespace name="AppIndicator">
- <file>../../src/app-indicator.c</file>
+ <file>app-indicator.c</file>
<file>../../src/app-indicator-enum-types.c</file>
<file>../../src/app-indicator-enum-types.h</file>
<file>../../src/app-indicator.h</file>
diff --git a/bindings/mono/appindicator-sharp.dll.config.in b/bindings/mono/appindicator-sharp.dll.config.in
index d0e9e1e..2d21d00 100644
--- a/bindings/mono/appindicator-sharp.dll.config.in
+++ b/bindings/mono/appindicator-sharp.dll.config.in
@@ -1,5 +1,5 @@
<configuration>
- <dllmap dll="appindicator-sharp.dll" target="libappindicator@LIB_PREFIX@.0@LIB_SUFFIX@"/>
+ <dllmap dll="appindicator.dll" target="libappindicator@LIB_PREFIX@.1@LIB_SUFFIX@"/>
<dllmap dll="libgtk-2.0-0.dll" target="libgtk-2.0@LIB_PREFIX@.0@LIB_SUFFIX@"/>
<dllmap dll="libglib-2.0-0.dll" target="libglib-2.0@LIB_PREFIX@.0@LIB_SUFFIX@"/>
<dllmap dll="libgobject-2.0-0.dll" target="libgobject-2.0@LIB_PREFIX@.0@LIB_SUFFIX@"/>
diff --git a/bindings/mono/libappindicator-api.metadata b/bindings/mono/libappindicator-api.metadata
index 8f9df53..ccf58f1 100644
--- a/bindings/mono/libappindicator-api.metadata
+++ b/bindings/mono/libappindicator-api.metadata
@@ -5,10 +5,14 @@
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_attention_icon']" name="cname">new-attention-icon</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_status']" name="name">NewStatus</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_status']" name="cname">new-status</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_label']" name="name">NewLabel</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_label']" name="cname">new-label</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='connection_changed']" name="name">ConnectionChanged</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='connection_changed']" name="cname">connection-changed</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_icon']" name="name">NewIcon</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_icon']" name="cname">new-icon</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_icon_theme_path']" name="name">NewIconThemePath</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/signal[@field_name='new_icon_theme_path']" name="cname">new-icon-theme-path</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_ID_S']" name="name">ID</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_CATEGORY_S']" name="hidden">true</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_STATUS_S']" name="hidden">true</attr>
@@ -18,6 +22,9 @@
<attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_MENU_S']" name="name">Menu</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_MENU_S']" name="type">GtkMenu*</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_CONNECTED_S']" name="name">Connected</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_LABEL_S']" name="name">Label</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_LABEL_GUIDE_S']" name="name">LabelGuide</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_ORDERING_INDEX_S']" name="name">OrderingIndex</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/method[@name='SetMenu']" name="name">SetMenu</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/constructor[@cname='app_indicator_new']/*/*[@name='id']" name="property_name">id</attr>
@@ -26,16 +33,26 @@
<attr path="/api/namespace/object[@cname='AppIndicator']/constructor[@cname='app_indicator_new_with_path']/*/*[@name='id']" name="property_name">id</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/constructor[@cname='app_indicator_new_with_path']/*/*[@name='icon_name']" name="property_name">icon-name</attr>
<attr path="/api/namespace/object[@cname='AppIndicator']/constructor[@cname='app_indicator_new_with_path']/*/*[@name='category']" name="property_name">category</attr>
- <attr path="/api/namespace/object[@cname='AppIndicator']/constructor[@cname='app_indicator_new_with_path']/*/*[@name='icon_path']" name="property_name">icon-theme-path</attr>
+ <attr path="/api/namespace/object[@cname='AppIndicator']/constructor[@cname='app_indicator_new_with_path']/*/*[@name='icon_theme_path']" name="property_name">icon-theme-path</attr>
+
+ <remove-node path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_X_LABEL_S']" />
+ <remove-node path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_X_LABEL_GUIDE_S']" />
+ <remove-node path="/api/namespace/object[@cname='AppIndicator']/property[@cname='PROP_X_ORDERING_INDEX_S']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_get_id']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_get_status']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_get_icon']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_get_icon_theme_path']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_get_category']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_get_attention_icon']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_get_label']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_get_label_guide']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_get_ordering_index']" />
- <remove-node path="/api/namespace/object/method[@cname='app_indicator_set_id']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_set_status']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_set_icon']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_set_icon_theme_path']" />
<remove-node path="/api/namespace/object/method[@cname='app_indicator_set_attention_icon']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_set_label']" />
+ <remove-node path="/api/namespace/object/method[@cname='app_indicator_set_ordering_index']" />
</metadata>
diff --git a/bindings/mono/policy.0.0.appindicator-sharp.config.in b/bindings/mono/policy.0.0.appindicator-sharp.config.in
new file mode 100644
index 0000000..e1fe0db
--- /dev/null
+++ b/bindings/mono/policy.0.0.appindicator-sharp.config.in
@@ -0,0 +1,10 @@
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <assemblyIdentity name="@ASSEMBLY_NAME@" publicKeyToken="bcae265d1c7ab4c2" />
+ <bindingRedirect oldVersion="0.0.0.0-0.1.0.0" newVersion="@ASSEMBLY_VERSION@"/>
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration>
diff --git a/bindings/mono/policy.0.1.appindicator-sharp.config.in b/bindings/mono/policy.0.1.appindicator-sharp.config.in
new file mode 100644
index 0000000..0a8f9b1
--- /dev/null
+++ b/bindings/mono/policy.0.1.appindicator-sharp.config.in
@@ -0,0 +1,10 @@
+<configuration>
+ <runtime>
+ <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+ <dependentAssembly>
+ <assemblyIdentity name="@ASSEMBLY_NAME@" publicKeyToken="bcae265d1c7ab4c2" />
+ <bindingRedirect oldVersion="0.1.0.0-@ASSEMBLY_VERSION@" newVersion="@ASSEMBLY_VERSION@"/>
+ </dependentAssembly>
+ </assemblyBinding>
+ </runtime>
+</configuration>
diff --git a/bindings/python/appindicator.defs b/bindings/python/appindicator.defs
index 98abe4c..5027a6d 100644
--- a/bindings/python/appindicator.defs
+++ b/bindings/python/appindicator.defs
@@ -49,7 +49,7 @@
'("const-gchar*" "id")
'("const-gchar*" "icon_name")
'("AppIndicatorCategory" "category")
- '("const-gchar*" "icon_path" (null-ok) (default "NULL"))
+ '("const-gchar*" "icon_theme_path" (null-ok) (default "NULL"))
)
)
@@ -62,6 +62,24 @@
)
)
+(define-method set_label
+ (of-object "AppIndicator")
+ (c-name "app_indicator_set_label")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "label" (null-ok))
+ '("const-gchar*" "guide" (null-ok) (default "NULL"))
+ )
+)
+
+(define-method set_ordering_index
+ (of-object "AppIndicator")
+ (c-name "app_indicator_set_ordering_index")
+ (parameters
+ '("guint32" "ordering_index")
+ )
+)
+
(define-method set_attention_icon
(of-object "AppIndicator")
(c-name "app_indicator_set_attention_icon")
@@ -89,6 +107,15 @@
)
)
+(define-method set_icon_theme_path
+ (of-object "AppIndicator")
+ (c-name "app_indicator_set_icon_theme_path")
+ (return-type "none")
+ (parameters
+ '("const-gchar*" "icon_theme_path" (null-ok))
+ )
+)
+
(define-method get_id
(of-object "AppIndicator")
(c-name "app_indicator_get_id")
@@ -107,12 +134,36 @@
(return-type "AppIndicatorStatus")
)
+(define-method get_label
+ (of-object "AppIndicator")
+ (c-name "app_indicator_get_label")
+ (return-type "const-gchar*")
+)
+
+(define-method get_label_guide
+ (of-object "AppIndicator")
+ (c-name "app_indicator_get_label_guide")
+ (return-type "const-gchar*")
+)
+
+(define-method get_ordering_index
+ (of-object "AppIndicator")
+ (c-name "app_indicator_get_ordering_index")
+ (return-type "guint32")
+)
+
(define-method get_icon
(of-object "AppIndicator")
(c-name "app_indicator_get_icon")
(return-type "const-gchar*")
)
+(define-method get_icon_theme_path
+ (of-object "AppIndicator")
+ (c-name "app_indicator_get_icon_theme_path")
+ (return-type "const-gchar*")
+)
+
(define-method get_attention_icon
(of-object "AppIndicator")
(c-name "app_indicator_get_attention_icon")
diff --git a/configure.ac b/configure.ac
index 4abdd9d..3f0a3de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
-AC_INIT(indicator-application, 0.2.0, ted@canonical.com)
+AC_INIT(indicator-application, 0.2.9, ted@canonical.com)
AC_COPYRIGHT([Copyright 2009, 2010 Canonical])
AC_PREREQ(2.53)
AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE(indicator-application, 0.2.0)
+AM_INIT_AUTOMAKE(indicator-application, 0.2.9)
AM_MAINTAINER_MODE
diff --git a/data/Makefile.am b/data/Makefile.am
index cc1961b..c142c4e 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -5,7 +5,13 @@ dbus_services_DATA = indicator-application.service
%.service: %.service.in
sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
-EXTRA_DIST = indicator-application.service.in
+overridedir = $(pkgdatadir)
+override_DATA = ordering-override.keyfile
-CLEANFILES = indicator-application.service
+EXTRA_DIST = \
+ indicator-application.service.in \
+ ordering-override.keyfile
+
+CLEANFILES = \
+ indicator-application.service
diff --git a/data/ordering-override.keyfile b/data/ordering-override.keyfile
new file mode 100644
index 0000000..71d7c1a
--- /dev/null
+++ b/data/ordering-override.keyfile
@@ -0,0 +1,5 @@
+[Ordering Index Overrides]
+gnome-power-manager=1
+gst-keyboard-xkb=2
+gsd-keyboard-xkb=3
+ibus=4
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 10d8b2a..d2ef3bb 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -66,6 +66,7 @@ IGNORE_HFILES= \
dbus-properties-client.h \
dbus-properties-server.h \
dbus-shared.h \
+ generate-id.h \
notification-item-client.h \
notification-item-server.h \
notification-watcher-client.h \
diff --git a/docs/reference/libappindicator-sections.txt b/docs/reference/libappindicator-sections.txt
index 70df0b8..e0effd1 100644
--- a/docs/reference/libappindicator-sections.txt
+++ b/docs/reference/libappindicator-sections.txt
@@ -9,6 +9,8 @@ APP_INDICATOR_GET_CLASS
APP_INDICATOR_SIGNAL_NEW_ICON
APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON
APP_INDICATOR_SIGNAL_NEW_STATUS
+APP_INDICATOR_SIGNAL_NEW_LABEL
+APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH
APP_INDICATOR_SIGNAL_CONNECTION_CHANGED
AppIndicatorCategory
AppIndicatorStatus
@@ -23,11 +25,18 @@ app_indicator_set_status
app_indicator_set_attention_icon
app_indicator_set_menu
app_indicator_set_icon
+app_indicator_set_icon_theme_path
+app_indicator_set_label
+app_indicator_set_ordering_index
app_indicator_get_id
app_indicator_get_category
app_indicator_get_status
app_indicator_get_icon
+app_indicator_get_icon_theme_path
app_indicator_get_attention_icon
app_indicator_get_menu
+app_indicator_get_label
+app_indicator_get_label_guide
+app_indicator_get_ordering_index
</SECTION>
diff --git a/example/simple-client.c b/example/simple-client.c
index fbcaaaa..f2fac6f 100644
--- a/example/simple-client.c
+++ b/example/simple-client.c
@@ -26,6 +26,21 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
GMainLoop * mainloop = NULL;
static gboolean active = TRUE;
+static gboolean can_haz_label = TRUE;
+
+static void
+label_toggle_cb (GtkWidget * widget, gpointer data)
+{
+ can_haz_label = !can_haz_label;
+
+ if (can_haz_label) {
+ gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Hide label");
+ } else {
+ gtk_menu_item_set_label(GTK_MENU_ITEM(widget), "Show label");
+ }
+
+ return;
+}
static void
activate_clicked_cb (GtkWidget *widget, gpointer data)
@@ -97,6 +112,22 @@ append_submenu (GtkWidget *item)
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
}
+guint percentage = 0;
+
+static gboolean
+percent_change (gpointer user_data)
+{
+ percentage = (percentage + 1) % 100;
+ if (can_haz_label) {
+ gchar * percentstr = g_strdup_printf("%d%%", percentage + 1);
+ app_indicator_set_label (APP_INDICATOR(user_data), percentstr, "100%");
+ g_free(percentstr);
+ } else {
+ app_indicator_set_label (APP_INDICATOR(user_data), NULL, NULL);
+ }
+ return TRUE;
+}
+
int
main (int argc, char ** argv)
{
@@ -114,6 +145,9 @@ main (int argc, char ** argv)
app_indicator_set_status (ci, APP_INDICATOR_STATUS_ACTIVE);
app_indicator_set_attention_icon(ci, "indicator-messages-new");
+ app_indicator_set_label (ci, "1%", "100%");
+
+ g_timeout_add_seconds(1, percent_change, ci);
menu = gtk_menu_new ();
GtkWidget *item = gtk_check_menu_item_new_with_label ("1");
@@ -151,6 +185,13 @@ main (int argc, char ** argv)
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
gtk_widget_show(item);
+ item = gtk_menu_item_new_with_label ("Show label");
+ label_toggle_cb(item, ci);
+ g_signal_connect (item, "activate",
+ G_CALLBACK (label_toggle_cb), ci);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ gtk_widget_show(item);
+
app_indicator_set_menu (ci, GTK_MENU (menu));
mainloop = g_main_loop_new(NULL, FALSE);
diff --git a/src/Makefile.am b/src/Makefile.am
index 7704caf..fed0318 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -56,17 +56,20 @@ indicator_application_service_SOURCES = \
application-service.c \
application-service-appstore.h \
application-service-appstore.c \
- application-service-lru-file.h \
- application-service-lru-file.c \
application-service-marshal.c \
application-service-watcher.h \
application-service-watcher.c \
app-indicator-enum-types.c \
- dbus-shared.h
+ dbus-shared.h \
+ generate-id.h \
+ generate-id.c
+
indicator_application_service_CFLAGS = \
$(INDICATOR_CFLAGS) \
+ -DDATADIR="\"$(pkgdatadir)\"" \
-Wall -Werror \
-DG_LOG_DOMAIN=\"indicator-application-service\"
+
indicator_application_service_LDADD = \
$(INDICATOR_LIBS) \
libappindicator$(VER).la
@@ -103,10 +106,13 @@ BUILT_SOURCES += \
libappindicator_la_SOURCES = \
$(libappindicator_headers) \
app-indicator-enum-types.c \
- app-indicator.c
+ app-indicator.c \
+ application-service-marshal.c \
+ generate-id.h \
+ generate-id.c
libappindicator_la_LDFLAGS = \
- -version-info 0:0:0 \
+ -version-info 1:0:0 \
-no-undefined \
-export-symbols-regex "^[^_d].*"
@@ -136,6 +142,7 @@ libappindicator3_la_LIBADD = $(libappindicator_la_LIBADD)
DBUS_SPECS = \
dbus-properties.xml \
application-service.xml \
+ notification-approver.xml \
notification-item.xml \
notification-watcher.xml
diff --git a/src/app-indicator.c b/src/app-indicator.c
index a52a063..98663c1 100644
--- a/src/app-indicator.c
+++ b/src/app-indicator.c
@@ -32,6 +32,8 @@ License version 3 and version 2.1 along with this program. If not, see
#endif
#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-bindings.h>
+
#include <libdbusmenu-glib/server.h>
#ifdef HAVE_GTK3
#include <libdbusmenu-gtk3/client.h>
@@ -41,11 +43,13 @@ License version 3 and version 2.1 along with this program. If not, see
#include "app-indicator.h"
#include "app-indicator-enum-types.h"
+#include "application-service-marshal.h"
#include "notification-item-server.h"
#include "notification-watcher-client.h"
#include "dbus-shared.h"
+#include "generate-id.h"
#define PANEL_ICON_SUFFIX "panel"
@@ -73,9 +77,13 @@ struct _AppIndicatorPrivate {
AppIndicatorStatus status;
gchar *icon_name;
gchar *attention_icon_name;
- gchar * icon_path;
+ gchar *icon_theme_path;
DbusmenuServer *menuservice;
GtkWidget *menu;
+ guint32 ordering_index;
+ gchar * label;
+ gchar * label_guide;
+ guint label_change_idle;
GtkStatusIcon * status_icon;
gint fallback_timer;
@@ -91,7 +99,10 @@ enum {
NEW_ICON,
NEW_ATTENTION_ICON,
NEW_STATUS,
+ NEW_LABEL,
+ X_NEW_LABEL,
CONNECTION_CHANGED,
+ NEW_ICON_THEME_PATH,
LAST_SIGNAL
};
@@ -108,7 +119,13 @@ enum {
PROP_ATTENTION_ICON_NAME,
PROP_ICON_THEME_PATH,
PROP_MENU,
- PROP_CONNECTED
+ PROP_CONNECTED,
+ PROP_LABEL,
+ PROP_LABEL_GUIDE,
+ PROP_X_LABEL,
+ PROP_X_LABEL_GUIDE,
+ PROP_ORDERING_INDEX,
+ PROP_X_ORDERING_INDEX
};
/* The strings so that they can be slowly looked up. */
@@ -120,11 +137,20 @@ enum {
#define PROP_ICON_THEME_PATH_S "icon-theme-path"
#define PROP_MENU_S "menu"
#define PROP_CONNECTED_S "connected"
+#define PROP_LABEL_S "label"
+#define PROP_LABEL_GUIDE_S "label-guide"
+#define PROP_X_LABEL_S ("x-ayatana-" PROP_LABEL_S)
+#define PROP_X_LABEL_GUIDE_S ("x-ayatana-" PROP_LABEL_GUIDE_S)
+#define PROP_ORDERING_INDEX_S "ordering-index"
+#define PROP_X_ORDERING_INDEX_S ("x-ayatana-" PROP_ORDERING_INDEX_S)
/* Private macro, shhhh! */
#define APP_INDICATOR_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_INDICATOR_TYPE, AppIndicatorPrivate))
+/* Signal wrapper */
+#define APP_INDICATOR_SIGNAL_X_NEW_LABEL ("x-ayatana-" APP_INDICATOR_SIGNAL_NEW_LABEL)
+
/* Default Path */
#define DEFAULT_ITEM_PATH "/org/ayatana/NotificationItem"
@@ -140,6 +166,7 @@ static void app_indicator_finalize (GObject *object);
static void app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
static void app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
/* Other stuff */
+static void signal_label_change (AppIndicator * self);
static void check_connect (AppIndicator * self);
static void register_service_cb (DBusGProxy * proxy, GError * error, gpointer data);
static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout);
@@ -233,7 +260,7 @@ app_indicator_class_init (AppIndicatorClass *klass)
"An icon for the indicator",
"The default icon that is shown for the indicator.",
NULL,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
AppIndicator:attention-icon-name:
@@ -260,7 +287,7 @@ app_indicator_class_init (AppIndicatorClass *klass)
"An additional path for custom icons.",
"An additional place to look for icon names that may be installed by the application.",
NULL,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
/**
AppIndicator:menu:
@@ -288,7 +315,101 @@ app_indicator_class_init (AppIndicatorClass *klass)
"Pretty simple, true if we have a reasonable expectation of being displayed through this object. You should hide your TrayIcon if so.",
FALSE,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ /**
+ AppIndicator:label:
+
+ A label that can be shown next to the string in the application
+ indicator. The label will not be shown unless there is an icon
+ as well. The label is useful for numerical and other frequently
+ updated information. In general, it shouldn't be shown unless a
+ user requests it as it can take up a significant amount of space
+ on the user's panel. This may not be shown in all visualizations.
+ */
+ g_object_class_install_property(object_class,
+ PROP_LABEL,
+ g_param_spec_string (PROP_LABEL_S,
+ "A label next to the icon",
+ "A label to provide dynamic information.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ AppIndicator:label-guide:
+
+ An optional string to provide guidance to the panel on how big
+ the #AppIndicator:label string could get. If this is set correctly
+ then the panel should never 'jiggle' as the string adjusts through
+ out the range of options. For instance, if you were providing a
+ percentage like "54% thrust" in #AppIndicator:label you'd want to
+ set this string to "100% thrust" to ensure space when Scotty can
+ get you enough power.
+ */
+ g_object_class_install_property(object_class,
+ PROP_LABEL_GUIDE,
+ g_param_spec_string (PROP_LABEL_GUIDE_S,
+ "A string to size the space available for the label.",
+ "To ensure that the label does not cause the panel to 'jiggle' this string should provide information on how much space it could take.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ AppIndicator:ordering-index:
+
+ The ordering index is an odd parameter, and if you think you don't need
+ it you're probably right. In general, the application indicator try
+ to place the applications in a recreatable place taking into account
+ which category they're in to try and group them. But, there are some
+ cases where you'd want to ensure indicators are next to each other.
+ To do that you can override the generated ordering index and replace it
+ with a new one. Again, you probably don't want to be doing this, but
+ in case you do, this is the way.
+ */
+ g_object_class_install_property(object_class,
+ PROP_ORDERING_INDEX,
+ g_param_spec_uint (PROP_ORDERING_INDEX_S,
+ "The location that this app indicator should be in the list.",
+ "A way to override the default ordering of the applications by providing a very specific idea of where this entry should be placed.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ AppIndicator:x-ayatana-ordering-index:
+
+ A wrapper for #AppIndicator:ordering-index so that it can match the
+ dbus interface currently. It will hopefully be retired, please don't
+ use it anywhere.
+ */
+ g_object_class_install_property(object_class,
+ PROP_X_ORDERING_INDEX,
+ g_param_spec_uint (PROP_X_ORDERING_INDEX_S,
+ "A wrapper, please don't use.",
+ "A wrapper, please don't use.",
+ 0, G_MAXUINT32, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ AppIndicator:x-ayatana-label:
+
+ Wrapper for #AppIndicator:label. Please use that in all of your
+ code.
+ */
+ g_object_class_install_property(object_class,
+ PROP_X_LABEL,
+ g_param_spec_string (PROP_X_LABEL_S,
+ "A wrapper, please don't use.",
+ "A wrapper, please don't use.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ AppIndicator:x-ayatana-label-guide:
+
+ Wrapper for #AppIndicator:label-guide. Please use that in all of your
+ code.
+ */
+ g_object_class_install_property(object_class,
+ PROP_X_LABEL_GUIDE,
+ g_param_spec_string (PROP_X_LABEL_GUIDE_S,
+ "A wrapper, please don't use.",
+ "A wrapper, please don't use.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/* Signals */
@@ -337,6 +458,40 @@ app_indicator_class_init (AppIndicatorClass *klass)
G_TYPE_STRING);
/**
+ AppIndicator::new-label:
+ @arg0: The #AppIndicator object
+ @arg1: The string for the label
+ @arg1: The string for the guide
+
+ Emitted when either #AppIndicator:label or #AppIndicator:label-guide are
+ changed.
+ */
+ signals[NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_LABEL,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (AppIndicatorClass, new_label),
+ NULL, NULL,
+ _application_service_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+
+ /**
+ AppIndicator::x-ayatana-new-label:
+ @arg0: The #AppIndicator object
+ @arg1: The string for the label
+ @arg1: The string for the guide
+
+ Wrapper for #AppIndicator::new-label, please don't use this signal
+ use the other one.
+ */
+ signals[X_NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_X_NEW_LABEL,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (AppIndicatorClass, new_label),
+ NULL, NULL,
+ _application_service_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+
+ /**
AppIndicator::connection-changed:
@arg0: The #AppIndicator object
@arg1: Whether we're connected or not
@@ -351,6 +506,21 @@ app_indicator_class_init (AppIndicatorClass *klass)
g_cclosure_marshal_VOID__BOOLEAN,
G_TYPE_NONE, 1, G_TYPE_BOOLEAN, G_TYPE_NONE);
+ /**
+ AppIndicator::new-icon-theme-path:
+ @arg0: The #AppIndicator object
+
+ Signaled when there is a new icon set for the
+ object.
+ */
+ signals[NEW_ICON_THEME_PATH] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH,
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (AppIndicatorClass, new_icon_theme_path),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
/* Initialize the object as a DBus type */
dbus_g_object_type_install_info(APP_INDICATOR_TYPE,
&dbus_glib__notification_item_server_object_info);
@@ -369,9 +539,13 @@ app_indicator_init (AppIndicator *self)
priv->status = APP_INDICATOR_STATUS_PASSIVE;
priv->icon_name = NULL;
priv->attention_icon_name = NULL;
- priv->icon_path = NULL;
+ priv->icon_theme_path = NULL;
priv->menu = NULL;
priv->menuservice = NULL;
+ priv->ordering_index = 0;
+ priv->label = NULL;
+ priv->label_guide = NULL;
+ priv->label_change_idle = 0;
priv->watcher_proxy = NULL;
priv->connection = NULL;
@@ -423,6 +597,11 @@ app_indicator_dispose (GObject *object)
priv->fallback_timer = 0;
}
+ if (priv->label_change_idle != 0) {
+ g_source_remove(priv->label_change_idle);
+ priv->label_change_idle = 0;
+ }
+
if (priv->menu != NULL) {
g_signal_handlers_disconnect_by_func (G_OBJECT (priv->menu),
client_menu_changed,
@@ -491,9 +670,19 @@ app_indicator_finalize (GObject *object)
priv->attention_icon_name = NULL;
}
- if (priv->icon_path != NULL) {
- g_free(priv->icon_path);
- priv->icon_path = NULL;
+ if (priv->icon_theme_path != NULL) {
+ g_free(priv->icon_theme_path);
+ priv->icon_theme_path = NULL;
+ }
+
+ if (priv->label != NULL) {
+ g_free(priv->label);
+ priv->label = NULL;
+ }
+
+ if (priv->label_guide != NULL) {
+ g_free(priv->label_guide);
+ priv->label_guide = NULL;
}
G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object);
@@ -561,12 +750,54 @@ app_indicator_set_property (GObject * object, guint prop_id, const GValue * valu
break;
case PROP_ICON_THEME_PATH:
- if (priv->icon_path != NULL) {
- g_free(priv->icon_path);
- }
- priv->icon_path = g_value_dup_string(value);
+ app_indicator_set_icon_theme_path (APP_INDICATOR (object),
+ g_value_get_string (value));
+ check_connect (self);
break;
+ case PROP_X_LABEL:
+ case PROP_LABEL: {
+ gchar * oldlabel = priv->label;
+ priv->label = g_value_dup_string(value);
+
+ if (g_strcmp0(oldlabel, priv->label) != 0) {
+ signal_label_change(APP_INDICATOR(object));
+ }
+
+ if (priv->label != NULL && priv->label[0] == '\0') {
+ g_free(priv->label);
+ priv->label = NULL;
+ }
+
+ if (oldlabel != NULL) {
+ g_free(oldlabel);
+ }
+ break;
+ }
+ case PROP_X_LABEL_GUIDE:
+ case PROP_LABEL_GUIDE: {
+ gchar * oldguide = priv->label_guide;
+ priv->label_guide = g_value_dup_string(value);
+
+ if (g_strcmp0(oldguide, priv->label_guide) != 0) {
+ signal_label_change(APP_INDICATOR(object));
+ }
+
+ if (priv->label_guide != NULL && priv->label_guide[0] == '\0') {
+ g_free(priv->label_guide);
+ priv->label_guide = NULL;
+ }
+
+ if (oldguide != NULL) {
+ g_free(oldguide);
+ }
+ break;
+ }
+ case PROP_X_ORDERING_INDEX:
+ case PROP_ORDERING_INDEX:
+ priv->ordering_index = g_value_get_uint(value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -607,7 +838,7 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa
break;
case PROP_ICON_THEME_PATH:
- g_value_set_string (value, priv->icon_path);
+ g_value_set_string (value, priv->icon_theme_path);
break;
case PROP_MENU:
@@ -624,6 +855,21 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa
g_value_set_boolean (value, priv->watcher_proxy != NULL ? TRUE : FALSE);
break;
+ case PROP_X_LABEL:
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+
+ case PROP_X_LABEL_GUIDE:
+ case PROP_LABEL_GUIDE:
+ g_value_set_string (value, priv->label_guide);
+ break;
+
+ case PROP_X_ORDERING_INDEX:
+ case PROP_ORDERING_INDEX:
+ g_value_set_uint(value, priv->ordering_index);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -632,6 +878,43 @@ app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GPa
return;
}
+/* Sends the label changed signal and resets the source ID */
+static gboolean
+signal_label_change_idle (gpointer user_data)
+{
+ AppIndicator * self = (AppIndicator *)user_data;
+ AppIndicatorPrivate *priv = self->priv;
+
+ g_signal_emit(G_OBJECT(self), signals[NEW_LABEL], 0,
+ priv->label != NULL ? priv->label : "",
+ priv->label_guide != NULL ? priv->label_guide : "",
+ TRUE);
+ g_signal_emit(G_OBJECT(self), signals[X_NEW_LABEL], 0,
+ priv->label != NULL ? priv->label : "",
+ priv->label_guide != NULL ? priv->label_guide : "",
+ TRUE);
+
+ priv->label_change_idle = 0;
+
+ return FALSE;
+}
+
+/* Sets up an idle function to send the label changed signal
+ so that we don't send it too many times. */
+static void
+signal_label_change (AppIndicator * self)
+{
+ AppIndicatorPrivate *priv = self->priv;
+
+ /* don't set it twice */
+ if (priv->label_change_idle != 0) {
+ return;
+ }
+
+ priv->label_change_idle = g_idle_add(signal_label_change_idle, self);
+ return;
+}
+
/* This function is used to see if we have enough information to
connect to things. If we do, and we're not connected, it
connects for us. */
@@ -775,6 +1058,23 @@ dbus_owner_change (DBusGProxy * proxy, const gchar * name, const gchar * prev, c
return;
}
+/* Checking to see if someone already has the name we're looking for */
+static void
+check_owner_cb (DBusGProxy *proxy, gboolean exists, GError *error, gpointer userdata)
+{
+ if (error != NULL) {
+ g_warning("Unable to check for '" NOTIFICATION_WATCHER_DBUS_ADDR "' on DBus. No worries, but concerning.");
+ return;
+ }
+
+ if (exists) {
+ g_debug("Woah, we actually has a race condition with dbus");
+ dbus_owner_change(proxy, NOTIFICATION_WATCHER_DBUS_ADDR, NULL, "Non NULL", userdata);
+ }
+
+ return;
+}
+
/* This is an idle function to create the proxy. This is mostly
because start_fallback_timer can get called in the distruction
of a proxy and thus the proxy manager gets confused when creating
@@ -797,6 +1097,11 @@ setup_name_owner_proxy (gpointer data)
G_TYPE_INVALID);
dbus_g_proxy_connect_signal(priv->dbus_proxy, "NameOwnerChanged",
G_CALLBACK(dbus_owner_change), data, NULL);
+
+ /* Check to see if anyone has the name we're looking for
+ just incase we missed it changing. */
+
+ org_freedesktop_DBus_name_has_owner_async(priv->dbus_proxy, NOTIFICATION_WATCHER_DBUS_ADDR, check_owner_cb, data);
}
return FALSE;
@@ -1029,12 +1334,12 @@ app_indicator_new (const gchar *id,
@id: The unique id of the indicator to create.
@icon_name: The icon name for this indicator
@category: The category of indicator.
- @icon_path: A custom path for finding icons.
+ @icon_theme_path: A custom path for finding icons.
Creates a new #AppIndicator setting the properties:
#AppIndicator:id with @id, #AppIndicator:category
with @category, #AppIndicator:icon-name with
- @icon_name and #AppIndicator:icon-theme-path with @icon_path.
+ @icon_name and #AppIndicator:icon-theme-path with @icon_theme_path.
Return value: A pointer to a new #AppIndicator object.
*/
@@ -1042,13 +1347,13 @@ AppIndicator *
app_indicator_new_with_path (const gchar *id,
const gchar *icon_name,
AppIndicatorCategory category,
- const gchar *icon_path)
+ const gchar *icon_theme_path)
{
AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
PROP_ID_S, id,
PROP_CATEGORY_S, category_from_enum (category),
PROP_ICON_NAME_S, icon_name,
- PROP_ICON_THEME_PATH_S, icon_path,
+ PROP_ICON_THEME_PATH_S, icon_theme_path,
NULL);
return indicator;
@@ -1138,6 +1443,56 @@ app_indicator_set_icon (AppIndicator *self, const gchar *icon_name)
return;
}
+/**
+ app_indicator_set_label:
+ @self: The #AppIndicator object to use
+ @label: The label to show next to the icon.
+ @guide: A guide to size the label correctly.
+
+ This is a wrapper function for the #AppIndicator:label and
+ #AppIndicator:guide properties. This function can take #NULL
+ as either @label or @guide and will clear the entries.
+*/
+void
+app_indicator_set_label (AppIndicator *self, const gchar * label, const gchar * guide)
+{
+ g_return_if_fail (IS_APP_INDICATOR (self));
+ /* Note: The label can be NULL, it's okay */
+ /* Note: The guide can be NULL, it's okay */
+
+ g_object_set(G_OBJECT(self),
+ PROP_LABEL_S, label == NULL ? "" : label,
+ PROP_LABEL_GUIDE_S, guide == NULL ? "" : guide,
+ NULL);
+
+ return;
+}
+
+/**
+ app_indicator_set_icon_theme_path:
+ @self: The #AppIndicator object to use
+ @icon_theme_path: The icon theme path to set.
+
+ Sets the path to use when searching for icons.
+**/
+void
+app_indicator_set_icon_theme_path (AppIndicator *self, const gchar *icon_theme_path)
+{
+ g_return_if_fail (IS_APP_INDICATOR (self));
+
+ if (g_strcmp0 (self->priv->icon_theme_path, icon_theme_path) != 0)
+ {
+ if (self->priv->icon_theme_path != NULL)
+ g_free(self->priv->icon_theme_path);
+
+ self->priv->icon_theme_path = g_strdup(icon_theme_path);
+
+ g_signal_emit (self, signals[NEW_ICON_THEME_PATH], 0, g_strdup(self->priv->icon_theme_path));
+ }
+
+ return;
+}
+
static void
activate_menuitem (DbusmenuMenuitem *mi, guint timestamp, gpointer user_data)
{
@@ -1418,12 +1773,12 @@ container_iterate (GtkWidget *widget,
container_iterate,
child);
g_signal_connect_object (submenu,
- "add",
+ "child-added",
G_CALLBACK (submenu_changed),
child,
0);
g_signal_connect_object (submenu,
- "remove",
+ "child-removed",
G_CALLBACK (submenu_changed),
child,
0);
@@ -1562,16 +1917,37 @@ app_indicator_set_menu (AppIndicator *self, GtkMenu *menu)
check_connect (self);
g_signal_connect (menu,
- "add",
+ "child-added",
G_CALLBACK (client_menu_changed),
self);
g_signal_connect (menu,
- "remove",
+ "child-removed",
G_CALLBACK (client_menu_changed),
self);
}
/**
+ app_indicator_set_ordering_index:
+ @self: The #AppIndicator
+ @ordering_index: A value for the ordering of this app indicator
+
+ Sets the ordering index for the app indicator which effects the
+ placement of it on the panel. For almost all app indicator
+ this is not the function you're looking for.
+
+ Wrapper function for property #AppIndicator:ordering-index.
+**/
+void
+app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index)
+{
+ g_return_if_fail (IS_APP_INDICATOR (self));
+
+ self->priv->ordering_index = ordering_index;
+
+ return;
+}
+
+/**
app_indicator_get_id:
@self: The #AppIndicator object to use
@@ -1636,6 +2012,22 @@ app_indicator_get_icon (AppIndicator *self)
}
/**
+ app_indicator_get_icon_theme_path:
+ @self: The #AppIndicator object to use
+
+ Wrapper function for property #AppIndicator:icon-theme-path.
+
+ Return value: The current icon theme path.
+*/
+const gchar *
+app_indicator_get_icon_theme_path (AppIndicator *self)
+{
+ g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
+
+ return self->priv->icon_theme_path;
+}
+
+/**
app_indicator_get_attention_icon:
@self: The #AppIndicator object to use
@@ -1671,3 +2063,56 @@ app_indicator_get_menu (AppIndicator *self)
return GTK_MENU(priv->menu);
}
+
+/**
+ app_indicator_get_label:
+ @self: The #AppIndicator object to use
+
+ Wrapper function for property #AppIndicator:label.
+
+ Return value: The current label.
+*/
+const gchar *
+app_indicator_get_label (AppIndicator *self)
+{
+ g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
+
+ return self->priv->label;
+}
+
+/**
+ app_indicator_get_label_guide:
+ @self: The #AppIndicator object to use
+
+ Wrapper function for property #AppIndicator:label-guide.
+
+ Return value: The current label guide.
+*/
+const gchar *
+app_indicator_get_label_guide (AppIndicator *self)
+{
+ g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
+
+ return self->priv->label_guide;
+}
+
+/**
+ app_indicator_get_ordering_index:
+ @self: The #AppIndicator object to use
+
+ Wrapper function for property #AppIndicator:ordering-index.
+
+ Return value: The current ordering index.
+*/
+guint32
+app_indicator_get_ordering_index (AppIndicator *self)
+{
+ g_return_val_if_fail (IS_APP_INDICATOR (self), 0);
+
+ if (self->priv->ordering_index == 0) {
+ return generate_id(self->priv->category, self->priv->id);
+ } else {
+ return self->priv->ordering_index;
+ }
+}
+
diff --git a/src/app-indicator.h b/src/app-indicator.h
index e37abd4..ce152bb 100644
--- a/src/app-indicator.h
+++ b/src/app-indicator.h
@@ -69,6 +69,7 @@ G_BEGIN_DECLS
Gets a pointer to the #AppIndicatorClass for the object @obj.
*/
+
#define APP_INDICATOR_TYPE (app_indicator_get_type ())
#define APP_INDICATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_INDICATOR_TYPE, AppIndicator))
#define APP_INDICATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APP_INDICATOR_TYPE, AppIndicatorClass))
@@ -92,14 +93,26 @@ G_BEGIN_DECLS
String identifier for the #AppIndicator::new-status signal.
*/
/**
+ APP_INDICATOR_SIGNAL_NEW_LABEL:
+
+ String identifier for the #AppIndicator::new-label signal.
+*/
+/**
APP_INDICATOR_SIGNAL_CONNECTION_CHANGED:
String identifier for the #AppIndicator::connection-changed signal.
*/
+/**
+ APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH:
+
+ String identifier for the #AppIndicator::new-icon-theme-path signal.
+*/
#define APP_INDICATOR_SIGNAL_NEW_ICON "new-icon"
#define APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON "new-attention-icon"
#define APP_INDICATOR_SIGNAL_NEW_STATUS "new-status"
+#define APP_INDICATOR_SIGNAL_NEW_LABEL "new-label"
#define APP_INDICATOR_SIGNAL_CONNECTION_CHANGED "connection-changed"
+#define APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH "new-icon-theme-path"
/**
AppIndicatorCategory:
@@ -147,13 +160,21 @@ typedef struct _AppIndicatorPrivate AppIndicatorPrivate;
@new_icon: Slot for #AppIndicator::new-icon.
@new_attention_icon: Slot for #AppIndicator::new-attention-icon.
@new_status: Slot for #AppIndicator::new-status.
+ @new_icon_theme_path: Slot for #AppIndicator::new-icon-theme-path
+ @new_label: Slot for #AppIndicator::new-label.
@connection_changed: Slot for #AppIndicator::connection-changed.
+ @app_indicator_reserved_sw: Reserved for future use.
+ @app_indicator_reserved_ats: Reserved for future use.
@fallback: Function that gets called to make a #GtkStatusIcon when
there is no Application Indicator area available.
@unfallback: The function that gets called if an Application
Indicator area appears after the fallback has been created.
@app_indicator_reserved_1: Reserved for future use.
@app_indicator_reserved_2: Reserved for future use.
+ @app_indicator_reserved_3: Reserved for future use.
+ @app_indicator_reserved_4: Reserved for future use.
+ @app_indicator_reserved_5: Reserved for future use.
+ @app_indicator_reserved_6: Reserved for future use.
The signals and external functions that make up the #AppIndicator
class object.
@@ -163,18 +184,27 @@ struct _AppIndicatorClass {
GObjectClass parent_class;
/* DBus Signals */
- void (* new_icon) (AppIndicator *indicator,
+ void (* new_icon) (AppIndicator *indicator,
+ gpointer user_data);
+ void (* new_attention_icon) (AppIndicator *indicator,
+ gpointer user_data);
+ void (* new_status) (AppIndicator *indicator,
+ const gchar *status,
gpointer user_data);
- void (* new_attention_icon) (AppIndicator *indicator,
+ void (* new_icon_theme_path) (AppIndicator *indicator,
+ const gchar *icon_theme_path,
gpointer user_data);
- void (* new_status) (AppIndicator *indicator,
- const gchar *status,
+ void (* new_label) (AppIndicator *indicator,
+ const gchar *label,
+ const gchar *guide,
gpointer user_data);
/* Local Signals */
void (* connection_changed) (AppIndicator * indicator,
gboolean connected,
gpointer user_data);
+ void (*app_indicator_reserved_sw)(void);
+ void (*app_indicator_reserved_ats)(void);
/* Overridable Functions */
GtkStatusIcon * (*fallback) (AppIndicator * indicator);
@@ -184,6 +214,10 @@ struct _AppIndicatorClass {
/* Reserved */
void (*app_indicator_reserved_1)(void);
void (*app_indicator_reserved_2)(void);
+ void (*app_indicator_reserved_3)(void);
+ void (*app_indicator_reserved_4)(void);
+ void (*app_indicator_reserved_5)(void);
+ void (*app_indicator_reserved_6)(void);
};
/**
@@ -215,7 +249,7 @@ AppIndicator *app_indicator_new (const gchar
AppIndicator *app_indicator_new_with_path (const gchar *id,
const gchar *icon_name,
AppIndicatorCategory category,
- const gchar *icon_path);
+ const gchar *icon_theme_path);
/* Set properties */
void app_indicator_set_status (AppIndicator *self,
@@ -226,14 +260,25 @@ void app_indicator_set_menu (AppIndicator
GtkMenu *menu);
void app_indicator_set_icon (AppIndicator *self,
const gchar *icon_name);
+void app_indicator_set_label (AppIndicator *self,
+ const gchar *label,
+ const gchar *guide);
+void app_indicator_set_icon_theme_path(AppIndicator *self,
+ const gchar *icon_theme_path);
+void app_indicator_set_ordering_index (AppIndicator *self,
+ guint32 ordering_index);
/* Get properties */
const gchar * app_indicator_get_id (AppIndicator *self);
AppIndicatorCategory app_indicator_get_category (AppIndicator *self);
AppIndicatorStatus app_indicator_get_status (AppIndicator *self);
const gchar * app_indicator_get_icon (AppIndicator *self);
+const gchar * app_indicator_get_icon_theme_path(AppIndicator *self);
const gchar * app_indicator_get_attention_icon (AppIndicator *self);
GtkMenu * app_indicator_get_menu (AppIndicator *self);
+const gchar * app_indicator_get_label (AppIndicator *self);
+const gchar * app_indicator_get_label_guide (AppIndicator *self);
+guint32 app_indicator_get_ordering_index (AppIndicator *self);
G_END_DECLS
diff --git a/src/application-service-appstore.c b/src/application-service-appstore.c
index 15abea3..e3befff 100644
--- a/src/application-service-appstore.c
+++ b/src/application-service-appstore.c
@@ -31,30 +31,53 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "application-service-marshal.h"
#include "dbus-properties-client.h"
#include "dbus-shared.h"
+#include "notification-approver-client.h"
+#include "generate-id.h"
/* DBus Prototypes */
static gboolean _application_service_server_get_applications (ApplicationServiceAppstore * appstore, GPtrArray ** apps, GError ** error);
#include "application-service-server.h"
-#define NOTIFICATION_ITEM_PROP_ID "Id"
-#define NOTIFICATION_ITEM_PROP_CATEGORY "Category"
-#define NOTIFICATION_ITEM_PROP_STATUS "Status"
-#define NOTIFICATION_ITEM_PROP_ICON_NAME "IconName"
-#define NOTIFICATION_ITEM_PROP_AICON_NAME "AttentionIconName"
-#define NOTIFICATION_ITEM_PROP_ICON_PATH "IconThemePath"
-#define NOTIFICATION_ITEM_PROP_MENU "Menu"
-
-#define NOTIFICATION_ITEM_SIG_NEW_ICON "NewIcon"
-#define NOTIFICATION_ITEM_SIG_NEW_AICON "NewAttentionIcon"
-#define NOTIFICATION_ITEM_SIG_NEW_STATUS "NewStatus"
+#define NOTIFICATION_ITEM_PROP_ID "Id"
+#define NOTIFICATION_ITEM_PROP_CATEGORY "Category"
+#define NOTIFICATION_ITEM_PROP_STATUS "Status"
+#define NOTIFICATION_ITEM_PROP_ICON_NAME "IconName"
+#define NOTIFICATION_ITEM_PROP_AICON_NAME "AttentionIconName"
+#define NOTIFICATION_ITEM_PROP_ICON_THEME_PATH "IconThemePath"
+#define NOTIFICATION_ITEM_PROP_MENU "Menu"
+#define NOTIFICATION_ITEM_PROP_LABEL "XAyatanaLabel"
+#define NOTIFICATION_ITEM_PROP_LABEL_GUIDE "XAyatanaLabelGuide"
+#define NOTIFICATION_ITEM_PROP_ORDERING_INDEX "XAyatanaOrderingIndex"
+
+#define NOTIFICATION_ITEM_SIG_NEW_ICON "NewIcon"
+#define NOTIFICATION_ITEM_SIG_NEW_AICON "NewAttentionIcon"
+#define NOTIFICATION_ITEM_SIG_NEW_STATUS "NewStatus"
+#define NOTIFICATION_ITEM_SIG_NEW_LABEL "XAyatanaNewLabel"
+#define NOTIFICATION_ITEM_SIG_NEW_ICON_THEME_PATH "NewIconThemePath"
+
+#define OVERRIDE_GROUP_NAME "Ordering Index Overrides"
+#define OVERRIDE_FILE_NAME "ordering-override.keyfile"
/* Private Stuff */
-typedef struct _ApplicationServiceAppstorePrivate ApplicationServiceAppstorePrivate;
struct _ApplicationServiceAppstorePrivate {
DBusGConnection * bus;
GList * applications;
- AppLruFile * lrufile;
+ GList * approvers;
+ GHashTable * ordering_overrides;
+};
+
+typedef enum {
+ VISIBLE_STATE_HIDDEN,
+ VISIBLE_STATE_SHOWN
+} visible_state_t;
+
+#define STATE2STRING(x) ((x) == VISIBLE_STATE_HIDDEN ? "hidden" : "visible")
+
+typedef struct _Approver Approver;
+struct _Approver {
+ DBusGProxy * proxy;
+ gboolean destroy_by_proxy;
};
typedef struct _Application Application;
@@ -71,8 +94,13 @@ struct _Application {
gchar * icon;
gchar * aicon;
gchar * menu;
- gchar * icon_path;
+ gchar * icon_theme_path;
+ gchar * label;
+ gchar * guide;
gboolean currently_free;
+ guint ordering_index;
+ GList * approved_by;
+ visible_state_t visible_state;
};
#define APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(o) \
@@ -83,6 +111,8 @@ enum {
APPLICATION_ADDED,
APPLICATION_REMOVED,
APPLICATION_ICON_CHANGED,
+ APPLICATION_LABEL_CHANGED,
+ APPLICATION_ICON_THEME_PATH_CHANGED,
LAST_SIGNAL
};
@@ -93,8 +123,15 @@ static void application_service_appstore_class_init (ApplicationServiceAppstoreC
static void application_service_appstore_init (ApplicationServiceAppstore *self);
static void application_service_appstore_dispose (GObject *object);
static void application_service_appstore_finalize (GObject *object);
+static gint app_sort_func (gconstpointer a, gconstpointer b, gpointer userdata);
+static void load_override_file (GHashTable * hash, const gchar * filename);
static AppIndicatorStatus string_to_status(const gchar * status_string);
-static void apply_status (Application * app, AppIndicatorStatus status);
+static void apply_status (Application * app);
+static AppIndicatorCategory string_to_cat(const gchar * cat_string);
+static void approver_free (gpointer papprover, gpointer user_data);
+static void check_with_new_approver (gpointer papp, gpointer papprove);
+static void check_with_old_approver (gpointer papprove, gpointer papp);
+static Application * find_application (ApplicationServiceAppstore * appstore, const gchar * address, const gchar * object);
G_DEFINE_TYPE (ApplicationServiceAppstore, application_service_appstore, G_TYPE_OBJECT);
@@ -113,8 +150,8 @@ application_service_appstore_class_init (ApplicationServiceAppstoreClass *klass)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ApplicationServiceAppstoreClass, application_added),
NULL, NULL,
- _application_service_marshal_VOID__STRING_INT_STRING_STRING_STRING,
- G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE);
+ _application_service_marshal_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING,
+ G_TYPE_NONE, 7, G_TYPE_STRING, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE);
signals[APPLICATION_REMOVED] = g_signal_new ("application-removed",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST,
@@ -129,6 +166,32 @@ application_service_appstore_class_init (ApplicationServiceAppstoreClass *klass)
NULL, NULL,
_application_service_marshal_VOID__INT_STRING,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING, G_TYPE_NONE);
+ signals[APPLICATION_ICON_THEME_PATH_CHANGED] = g_signal_new ("application-icon-theme-path-changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ApplicationServiceAppstoreClass, application_icon_theme_path_changed),
+ NULL, NULL,
+ _application_service_marshal_VOID__INT_STRING,
+ G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_STRING, G_TYPE_NONE);
+ signals[APPLICATION_LABEL_CHANGED] = g_signal_new ("application-label-changed",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ApplicationServiceAppstoreClass, application_label_changed),
+ NULL, NULL,
+ _application_service_marshal_VOID__INT_STRING_STRING,
+ G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_NONE);
+
+ dbus_g_object_register_marshaller(_application_service_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_object_register_marshaller(_application_service_marshal_VOID__BOOLEAN_STRING_OBJECT,
+ G_TYPE_NONE,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_INVALID);
dbus_g_object_type_install_info(APPLICATION_SERVICE_APPSTORE_TYPE,
&dbus_glib__application_service_server_object_info);
@@ -139,10 +202,18 @@ application_service_appstore_class_init (ApplicationServiceAppstoreClass *klass)
static void
application_service_appstore_init (ApplicationServiceAppstore *self)
{
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(self);
+
+ ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE (self);
priv->applications = NULL;
- priv->lrufile = NULL;
+ priv->approvers = NULL;
+
+ priv->ordering_overrides = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ load_override_file(priv->ordering_overrides, DATADIR "/" OVERRIDE_FILE_NAME);
+ gchar * userfile = g_build_filename(g_get_user_data_dir(), "indicators", "application", OVERRIDE_FILE_NAME, NULL);
+ load_override_file(priv->ordering_overrides, userfile);
+ g_free(userfile);
GError * error = NULL;
priv->bus = dbus_g_bus_get(DBUS_BUS_STARTER, &error);
@@ -155,6 +226,8 @@ application_service_appstore_init (ApplicationServiceAppstore *self)
dbus_g_connection_register_g_object(priv->bus,
INDICATOR_APPLICATION_DBUS_OBJ,
G_OBJECT(self));
+
+ self->priv = priv;
return;
}
@@ -162,7 +235,7 @@ application_service_appstore_init (ApplicationServiceAppstore *self)
static void
application_service_appstore_dispose (GObject *object)
{
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(object);
+ ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE(object)->priv;
while (priv->applications != NULL) {
application_service_appstore_application_remove(APPLICATION_SERVICE_APPSTORE(object),
@@ -170,6 +243,12 @@ application_service_appstore_dispose (GObject *object)
((Application *)priv->applications->data)->dbus_object);
}
+ if (priv->approvers != NULL) {
+ g_list_foreach(priv->approvers, approver_free, object);
+ g_list_free(priv->approvers);
+ priv->approvers = NULL;
+ }
+
G_OBJECT_CLASS (application_service_appstore_parent_class)->dispose (object);
return;
}
@@ -177,11 +256,72 @@ application_service_appstore_dispose (GObject *object)
static void
application_service_appstore_finalize (GObject *object)
{
+ ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE(object)->priv;
+
+ if (priv->ordering_overrides != NULL) {
+ g_hash_table_destroy(priv->ordering_overrides);
+ priv->ordering_overrides = NULL;
+ }
G_OBJECT_CLASS (application_service_appstore_parent_class)->finalize (object);
return;
}
+/* Loads the file and adds the override entries to the table
+ of overrides */
+static void
+load_override_file (GHashTable * hash, const gchar * filename)
+{
+ g_return_if_fail(hash != NULL);
+ g_return_if_fail(filename != NULL);
+
+ if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
+ return;
+ }
+
+ g_debug("Loading overrides from: '%s'", filename);
+
+ GError * error = NULL;
+ GKeyFile * keyfile = g_key_file_new();
+ g_key_file_load_from_file(keyfile, filename, G_KEY_FILE_NONE, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to load keyfile '%s' because: %s", filename, error->message);
+ g_error_free(error);
+ g_key_file_free(keyfile);
+ return;
+ }
+
+ gchar ** keys = g_key_file_get_keys(keyfile, OVERRIDE_GROUP_NAME, NULL, &error);
+ if (error != NULL) {
+ g_warning("Unable to get keys from keyfile '%s' because: %s", filename, error->message);
+ g_error_free(error);
+ g_key_file_free(keyfile);
+ return;
+ }
+
+ gchar * key = keys[0];
+ gint i;
+
+ for (i = 0; (key = keys[i]) != NULL; i++) {
+ GError * valerror = NULL;
+ gint val = g_key_file_get_integer(keyfile, OVERRIDE_GROUP_NAME, key, &valerror);
+
+ if (valerror != NULL) {
+ g_warning("Unable to get key '%s' out of file '%s' because: %s", key, filename, valerror->message);
+ g_error_free(valerror);
+ continue;
+ }
+ g_debug("%s: override '%s' with value '%d'", filename, key, val);
+
+ g_hash_table_insert(hash, g_strdup(key), GINT_TO_POINTER(val));
+ }
+ g_strfreev(keys);
+ g_key_file_free(keyfile);
+
+ return;
+}
+
/* Return from getting the properties from the item. We're looking at those
and making sure we have everythign that we need. If we do, then we'll
move on up to sending this onto the indicator. */
@@ -190,6 +330,7 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err
{
if (error != NULL) {
g_warning("Unable to get properties: %s", error->message);
+ /* TODO: We need to free all the application data here */
return;
}
@@ -209,8 +350,9 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err
app->id = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ID));
app->category = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_CATEGORY));
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(app->appstore);
- app_lru_file_touch(priv->lrufile, app->id, app->category);
+ app->status = string_to_status(g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS)));
+
+ ApplicationServiceAppstorePrivate * priv = app->appstore->priv;
app->icon = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_NAME));
@@ -227,18 +369,57 @@ get_all_properties_cb (DBusGProxy * proxy, GHashTable * properties, GError * err
app->aicon = g_value_dup_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_AICON_NAME));
}
- gpointer icon_path_data = g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_PATH);
- if (icon_path_data != NULL) {
- app->icon_path = g_value_dup_string((GValue *)icon_path_data);
+ gpointer icon_theme_path_data = g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ICON_THEME_PATH);
+ if (icon_theme_path_data != NULL) {
+ app->icon_theme_path = g_value_dup_string((GValue *)icon_theme_path_data);
} else {
- app->icon_path = g_strdup("");
+ app->icon_theme_path = g_strdup("");
}
- apply_status(app, string_to_status(g_value_get_string(g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_STATUS))));
+ gpointer ordering_index_over = g_hash_table_lookup(priv->ordering_overrides, app->id);
+ if (ordering_index_over == NULL) {
+ gpointer ordering_index_data = g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_ORDERING_INDEX);
+ if (ordering_index_data == NULL || g_value_get_uint(ordering_index_data) == 0) {
+ app->ordering_index = generate_id(string_to_cat(app->category), app->id);
+ } else {
+ app->ordering_index = g_value_get_uint(ordering_index_data);
+ }
+ } else {
+ app->ordering_index = GPOINTER_TO_UINT(ordering_index_over);
+ }
+ g_debug("'%s' ordering index is '%X'", app->id, app->ordering_index);
+
+ gpointer label_data = g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_LABEL);
+ if (label_data != NULL) {
+ app->label = g_value_dup_string((GValue *)label_data);
+ } else {
+ app->label = g_strdup("");
+ }
+
+ gpointer guide_data = g_hash_table_lookup(properties, NOTIFICATION_ITEM_PROP_LABEL_GUIDE);
+ if (guide_data != NULL) {
+ app->guide = g_value_dup_string((GValue *)guide_data);
+ } else {
+ app->guide = g_strdup("");
+ }
+
+ priv->applications = g_list_insert_sorted_with_data (priv->applications, app, app_sort_func, NULL);
+ g_list_foreach(priv->approvers, check_with_old_approver, app);
+
+ apply_status(app);
return;
}
+/* Check the application against an approver */
+static void
+check_with_old_approver (gpointer papprove, gpointer papp)
+{
+ /* Funny the parallels, eh? */
+ check_with_new_approver(papp, papprove);
+ return;
+}
+
/* Simple translation function -- could be optimized */
static AppIndicatorStatus
string_to_status(const gchar * status_string)
@@ -260,19 +441,58 @@ string_to_status(const gchar * status_string)
return retval;
}
+/* Simple translation function -- could be optimized */
+static AppIndicatorCategory
+string_to_cat(const gchar * cat_string)
+{
+ GEnumClass * klass = G_ENUM_CLASS(g_type_class_ref(APP_INDICATOR_TYPE_INDICATOR_CATEGORY));
+ g_return_val_if_fail(klass != NULL, APP_INDICATOR_CATEGORY_OTHER);
+
+ AppIndicatorCategory retval = APP_INDICATOR_CATEGORY_OTHER;
+
+ GEnumValue * val = g_enum_get_value_by_nick(klass, cat_string);
+ if (val == NULL) {
+ g_warning("Unrecognized status '%s' assuming other.", cat_string);
+ } else {
+ retval = (AppIndicatorCategory)val->value;
+ }
+
+ g_type_class_unref(klass);
+
+ return retval;
+}
+
+
/* A small helper function to get the position of an application
- in the app list. */
+ in the app list of the applications that are visible. */
static gint
get_position (Application * app) {
ApplicationServiceAppstore * appstore = app->appstore;
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+ ApplicationServiceAppstorePrivate * priv = appstore->priv;
- GList * applistitem = g_list_find(priv->applications, app);
- if (applistitem == NULL) {
- return -1;
+ GList * lapp;
+ gint count;
+
+ /* Go through the list and try to find ours */
+ for (lapp = priv->applications, count = 0; lapp != NULL; lapp = g_list_next(lapp), count++) {
+ if (lapp->data == app) {
+ break;
+ }
+
+ /* If the selected app isn't visible let's not
+ count it's position */
+ Application * thisapp = (Application *)(lapp->data);
+ if (thisapp->visible_state == VISIBLE_STATE_HIDDEN) {
+ count--;
+ }
}
- return g_list_position(priv->applications, applistitem);
+ if (lapp == NULL) {
+ g_warning("Unable to find position for app '%s'", app->id);
+ return -1;
+ }
+
+ return count;
}
/* A simple global function for dealing with freeing the information
@@ -281,6 +501,7 @@ static void
application_free (Application * app)
{
if (app == NULL) return;
+ g_debug("Application free '%s'", app->id);
/* Handle the case where this could be called by unref'ing one of
the proxy objects. */
@@ -315,8 +536,17 @@ application_free (Application * app)
if (app->menu != NULL) {
g_free(app->menu);
}
- if (app->icon_path != NULL) {
- g_free(app->icon_path);
+ if (app->icon_theme_path != NULL) {
+ g_free(app->icon_theme_path);
+ }
+ if (app->label != NULL) {
+ g_free(app->label);
+ }
+ if (app->guide != NULL) {
+ g_free(app->guide);
+ }
+ if (app->approved_by != NULL) {
+ g_list_free(app->approved_by);
}
g_free(app);
@@ -329,94 +559,90 @@ static void
application_removed_cb (DBusGProxy * proxy, gpointer userdata)
{
Application * app = (Application *)userdata;
+ g_debug("Application proxy destroyed '%s'", app->id);
/* Remove from the panel */
- apply_status(app, APP_INDICATOR_STATUS_PASSIVE);
+ app->status = APP_INDICATOR_STATUS_PASSIVE;
+ apply_status(app);
+
+ /* Remove from the application list */
+ app->appstore->priv->applications = g_list_remove(app->appstore->priv->applications, app);
/* Destroy the data */
application_free(app);
return;
}
-static gboolean
-can_add_application (GList *applications, Application *app)
-{
- if (applications)
- {
- GList *l = NULL;
-
- for (l = applications; l != NULL; l = g_list_next (l))
- {
- Application *tmp_app = (Application *)l->data;
-
- if (g_strcmp0 (tmp_app->dbus_name, app->dbus_name) == 0 &&
- g_strcmp0 (tmp_app->dbus_object, app->dbus_object) == 0)
- {
- return FALSE;
- }
- }
- }
-
- return TRUE;
-}
-
/* This function takes two Application structure
- pointers and uses the lrufile to compare them. */
+ pointers and uses their ordering index to compare them. */
static gint
app_sort_func (gconstpointer a, gconstpointer b, gpointer userdata)
{
Application * appa = (Application *)a;
Application * appb = (Application *)b;
- AppLruFile * lrufile = (AppLruFile *)userdata;
-
- return app_lru_file_sort(lrufile, appa->id, appb->id);
+ return (appb->ordering_index/2) - (appa->ordering_index/2);
}
/* Change the status of the application. If we're going passive
it removes it from the panel. If we're coming online, then
it add it to the panel. Otherwise it changes the icon. */
static void
-apply_status (Application * app, AppIndicatorStatus status)
+apply_status (Application * app)
{
- if (app->status == status) {
+ ApplicationServiceAppstore * appstore = app->appstore;
+ ApplicationServiceAppstorePrivate * priv = appstore->priv;
+
+ /* g_debug("Applying status. Status: %d Approved by: %d Approvers: %d Visible: %d", app->status, g_list_length(app->approved_by), g_list_length(priv->approvers), app->visible_state); */
+
+ visible_state_t goal_state = VISIBLE_STATE_HIDDEN;
+
+ if (app->status != APP_INDICATOR_STATUS_PASSIVE &&
+ g_list_length(app->approved_by) >= g_list_length(priv->approvers)) {
+ goal_state = VISIBLE_STATE_SHOWN;
+ }
+
+ /* Nothing needs to change, we're good */
+ if (app->visible_state == goal_state /* ) { */
+ && goal_state == VISIBLE_STATE_HIDDEN) {
+ /* TODO: Uhg, this is a little wrong in that we're going to
+ send an icon every time the status changes and the indicator
+ is visible even though it might not be updating. But, at
+ this point we need a small patch that is harmless. In the
+ future we need to track which icon is shown and remove the
+ duplicate message. */
return;
}
- g_debug("Changing app status to: %d", status);
- ApplicationServiceAppstore * appstore = app->appstore;
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+ g_debug("Changing app '%s' state from %s to %s", app->id, STATE2STRING(app->visible_state), STATE2STRING(goal_state));
/* This means we're going off line */
- if (status == APP_INDICATOR_STATUS_PASSIVE) {
+ if (goal_state == VISIBLE_STATE_HIDDEN) {
gint position = get_position(app);
if (position == -1) return;
g_signal_emit(G_OBJECT(appstore),
signals[APPLICATION_REMOVED], 0,
position, TRUE);
- priv->applications = g_list_remove(priv->applications, app);
} else {
/* Figure out which icon we should be using */
gchar * newicon = app->icon;
- if (status == APP_INDICATOR_STATUS_ATTENTION && app->aicon != NULL && app->aicon[0] != '\0') {
+ if (app->status == APP_INDICATOR_STATUS_ATTENTION && app->aicon != NULL && app->aicon[0] != '\0') {
newicon = app->aicon;
}
/* Determine whether we're already shown or not */
- if (app->status == APP_INDICATOR_STATUS_PASSIVE) {
- if (can_add_application (priv->applications, app)) {
- /* Put on panel */
- priv->applications = g_list_insert_sorted_with_data (priv->applications, app, app_sort_func, priv->lrufile);
-
- g_signal_emit(G_OBJECT(app->appstore),
- signals[APPLICATION_ADDED], 0,
- newicon,
- g_list_index(priv->applications, app), /* Position */
- app->dbus_name,
- app->menu,
- app->icon_path,
- TRUE);
- }
+ if (app->visible_state == VISIBLE_STATE_HIDDEN) {
+ /* Put on panel */
+ g_signal_emit(G_OBJECT(app->appstore),
+ signals[APPLICATION_ADDED], 0,
+ newicon,
+ get_position(app), /* Position */
+ app->dbus_name,
+ app->menu,
+ app->icon_theme_path,
+ app->label,
+ app->guide,
+ TRUE);
} else {
/* Icon update */
gint position = get_position(app);
@@ -428,7 +654,7 @@ apply_status (Application * app, AppIndicatorStatus status)
}
}
- app->status = status;
+ app->visible_state = goal_state;
return;
}
@@ -458,7 +684,7 @@ new_icon_cb (DBusGProxy * proxy, GValue value, GError * error, gpointer userdata
if (app->icon != NULL) g_free(app->icon);
app->icon = g_strdup(newicon);
- if (app->status == APP_INDICATOR_STATUS_ACTIVE) {
+ if (app->visible_state == VISIBLE_STATE_SHOWN && app->status == APP_INDICATOR_STATUS_ACTIVE) {
gint position = get_position(app);
if (position == -1) return;
@@ -496,7 +722,7 @@ new_aicon_cb (DBusGProxy * proxy, GValue value, GError * error, gpointer userdat
if (app->aicon != NULL) g_free(app->aicon);
app->aicon = g_strdup(newicon);
- if (app->status == APP_INDICATOR_STATUS_ATTENTION) {
+ if (app->visible_state == VISIBLE_STATE_SHOWN && app->status == APP_INDICATOR_STATUS_ATTENTION) {
gint position = get_position(app);
if (position == -1) return;
@@ -550,7 +776,76 @@ new_status (DBusGProxy * proxy, const gchar * status, gpointer data)
Application * app = (Application *)data;
if (!app->validated) return;
- apply_status(app, string_to_status(status));
+ app->status = string_to_status(status);
+ apply_status(app);
+
+ return;
+}
+
+/* Called when the Notification Item signals that it
+ has a new icon theme path. */
+static void
+new_icon_theme_path (DBusGProxy * proxy, const gchar * icon_theme_path, gpointer data)
+{
+ Application * app = (Application *)data;
+ if (!app->validated) return;
+
+ if (g_strcmp0(icon_theme_path, app->icon_theme_path)) {
+ /* If the new icon theme path is actually a new icon theme path */
+ if (app->icon_theme_path != NULL) g_free(app->icon_theme_path);
+ app->icon_theme_path = g_strdup(icon_theme_path);
+
+ if (app->visible_state != VISIBLE_STATE_HIDDEN) {
+ gint position = get_position(app);
+ if (position == -1) return;
+
+ g_signal_emit(G_OBJECT(app->appstore),
+ signals[APPLICATION_ICON_THEME_PATH_CHANGED], 0,
+ position, app->icon_theme_path, TRUE);
+ }
+ }
+
+ return;
+}
+
+/* Called when the Notification Item signals that it
+ has a new label. */
+static void
+new_label (DBusGProxy * proxy, const gchar * label, const gchar * guide, gpointer data)
+{
+ Application * app = (Application *)data;
+ if (!app->validated) return;
+
+ gboolean changed = FALSE;
+
+ if (g_strcmp0(app->label, label) != 0) {
+ changed = TRUE;
+ if (app->label != NULL) {
+ g_free(app->label);
+ app->label = NULL;
+ }
+ app->label = g_strdup(label);
+ }
+
+ if (g_strcmp0(app->guide, guide) != 0) {
+ changed = TRUE;
+ if (app->guide != NULL) {
+ g_free(app->guide);
+ app->guide = NULL;
+ }
+ app->guide = g_strdup(guide);
+ }
+
+ if (changed) {
+ gint position = get_position(app);
+ if (position == -1) return;
+
+ g_signal_emit(app->appstore, signals[APPLICATION_LABEL_CHANGED], 0,
+ position,
+ app->label != NULL ? app->label : "",
+ app->guide != NULL ? app->guide : "",
+ TRUE);
+ }
return;
}
@@ -567,11 +862,21 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
g_return_if_fail(IS_APPLICATION_SERVICE_APPSTORE(appstore));
g_return_if_fail(dbus_name != NULL && dbus_name[0] != '\0');
g_return_if_fail(dbus_object != NULL && dbus_object[0] != '\0');
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+ ApplicationServiceAppstorePrivate * priv = appstore->priv;
+ Application * app = find_application(appstore, dbus_name, dbus_object);
+
+ if (app != NULL) {
+ g_warning("Application already exists! Rerequesting properties.");
+ org_freedesktop_DBus_Properties_get_all_async(app->prop_proxy,
+ NOTIFICATION_ITEM_DBUS_IFACE,
+ get_all_properties_cb,
+ app);
+ return;
+ }
/* Build the application entry. This will be carried
along until we're sure we've got everything. */
- Application * app = g_new0(Application, 1);
+ app = g_new0(Application, 1);
app->validated = FALSE;
app->dbus_name = g_strdup(dbus_name);
@@ -581,8 +886,13 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
app->icon = NULL;
app->aicon = NULL;
app->menu = NULL;
- app->icon_path = NULL;
+ app->icon_theme_path = NULL;
+ app->label = NULL;
+ app->guide = NULL;
app->currently_free = FALSE;
+ app->ordering_index = 0;
+ app->approved_by = NULL;
+ app->visible_state = VISIBLE_STATE_HIDDEN;
/* Get the DBus proxy for the NotificationItem interface */
GError * error = NULL;
@@ -628,6 +938,15 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
NOTIFICATION_ITEM_SIG_NEW_STATUS,
G_TYPE_STRING,
G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_ICON_THEME_PATH,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_LABEL,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
dbus_g_proxy_connect_signal(app->dbus_proxy,
NOTIFICATION_ITEM_SIG_NEW_ICON,
@@ -644,6 +963,16 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
G_CALLBACK(new_status),
app,
NULL);
+ dbus_g_proxy_connect_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_ICON_THEME_PATH,
+ G_CALLBACK(new_icon_theme_path),
+ app,
+ NULL);
+ dbus_g_proxy_connect_signal(app->dbus_proxy,
+ NOTIFICATION_ITEM_SIG_NEW_LABEL,
+ G_CALLBACK(new_label),
+ app,
+ NULL);
/* Get all the propertiees */
org_freedesktop_DBus_Properties_get_all_async(app->prop_proxy,
@@ -656,8 +985,26 @@ application_service_appstore_application_add (ApplicationServiceAppstore * appst
return;
}
+/* Looks for an application in the list of applications */
+static Application *
+find_application (ApplicationServiceAppstore * appstore, const gchar * address, const gchar * object)
+{
+ ApplicationServiceAppstorePrivate * priv = appstore->priv;
+ GList * listpntr;
+
+ for (listpntr = priv->applications; listpntr != NULL; listpntr = g_list_next(listpntr)) {
+ Application * app = (Application *)listpntr->data;
+
+ if (!g_strcmp0(app->dbus_name, address) && !g_strcmp0(app->dbus_object, object)) {
+ return app;
+ }
+ }
+
+ return NULL;
+}
+
/* Removes an application. Currently only works for the apps
- that are shown. /TODO Need to fix that. */
+ that are shown. */
void
application_service_appstore_application_remove (ApplicationServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object)
{
@@ -665,30 +1012,40 @@ application_service_appstore_application_remove (ApplicationServiceAppstore * ap
g_return_if_fail(dbus_name != NULL && dbus_name[0] != '\0');
g_return_if_fail(dbus_object != NULL && dbus_object[0] != '\0');
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+ Application * app = find_application(appstore, dbus_name, dbus_object);
+ if (app != NULL) {
+ application_removed_cb(NULL, app);
+ } else {
+ g_warning("Unable to find application %s:%s", dbus_name, dbus_object);
+ }
+
+ return;
+}
+
+gchar**
+application_service_appstore_application_get_list (ApplicationServiceAppstore * appstore)
+{
+ ApplicationServiceAppstorePrivate * priv = appstore->priv;
+ gchar ** out;
+ gchar ** outpntr;
GList * listpntr;
- for (listpntr = priv->applications; listpntr != NULL; listpntr = g_list_next(listpntr)) {
- Application * app = (Application *)listpntr->data;
+ out = g_new(gchar*, g_list_length(priv->applications) + 1);
- if (!g_strcmp0(app->dbus_name, dbus_name) && !g_strcmp0(app->dbus_object, dbus_object)) {
- application_removed_cb(NULL, app);
- break; /* NOTE: Must break as the list will become inconsistent */
- }
+ for (listpntr = priv->applications, outpntr = out; listpntr != NULL; listpntr = g_list_next(listpntr), ++outpntr) {
+ Application * app = (Application *)listpntr->data;
+ *outpntr = g_strdup_printf("%s%s", app->dbus_name, app->dbus_object);
}
-
- return;
+ *outpntr = 0;
+ return out;
}
/* Creates a basic appstore object and attaches the
LRU file object to it. */
ApplicationServiceAppstore *
-application_service_appstore_new (AppLruFile * lrufile)
+application_service_appstore_new (void)
{
- g_return_val_if_fail(IS_APP_LRU_FILE(lrufile), NULL);
ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(g_object_new(APPLICATION_SERVICE_APPSTORE_TYPE, NULL));
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
- priv->lrufile = lrufile;
return appstore;
}
@@ -696,20 +1053,25 @@ application_service_appstore_new (AppLruFile * lrufile)
static gboolean
_application_service_server_get_applications (ApplicationServiceAppstore * appstore, GPtrArray ** apps, GError ** error)
{
- ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE(appstore);
+ ApplicationServiceAppstorePrivate * priv = appstore->priv;
*apps = g_ptr_array_new();
GList * listpntr;
gint position = 0;
for (listpntr = priv->applications; listpntr != NULL; listpntr = g_list_next(listpntr)) {
+ Application * app = (Application *)listpntr->data;
+ if (app->visible_state == VISIBLE_STATE_HIDDEN) {
+ continue;
+ }
+
GValueArray * values = g_value_array_new(5);
GValue value = {0};
/* Icon name */
g_value_init(&value, G_TYPE_STRING);
- g_value_set_string(&value, ((Application *)listpntr->data)->icon);
+ g_value_set_string(&value, app->icon);
g_value_array_append(values, &value);
g_value_unset(&value);
@@ -721,19 +1083,31 @@ _application_service_server_get_applications (ApplicationServiceAppstore * appst
/* DBus Address */
g_value_init(&value, G_TYPE_STRING);
- g_value_set_string(&value, ((Application *)listpntr->data)->dbus_name);
+ g_value_set_string(&value, app->dbus_name);
g_value_array_append(values, &value);
g_value_unset(&value);
/* DBus Object */
g_value_init(&value, DBUS_TYPE_G_OBJECT_PATH);
- g_value_set_static_boxed(&value, ((Application *)listpntr->data)->menu);
+ g_value_set_static_boxed(&value, app->menu);
g_value_array_append(values, &value);
g_value_unset(&value);
/* Icon path */
g_value_init(&value, G_TYPE_STRING);
- g_value_set_string(&value, ((Application *)listpntr->data)->icon_path);
+ g_value_set_string(&value, app->icon_theme_path);
+ g_value_array_append(values, &value);
+ g_value_unset(&value);
+
+ /* Label */
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, app->label);
+ g_value_array_append(values, &value);
+ g_value_unset(&value);
+
+ /* Guide */
+ g_value_init(&value, G_TYPE_STRING);
+ g_value_set_string(&value, app->guide);
g_value_array_append(values, &value);
g_value_unset(&value);
@@ -743,3 +1117,189 @@ _application_service_server_get_applications (ApplicationServiceAppstore * appst
return TRUE;
}
+/* Removes and approver from our list of approvers and
+ then sees if that changes our status. Most likely this
+ could make us visible if this approver rejected us. */
+static void
+remove_approver (gpointer papp, gpointer pproxy)
+{
+ Application * app = (Application *)papp;
+ app->approved_by = g_list_remove(app->approved_by, pproxy);
+ apply_status(app);
+ return;
+}
+
+/* Frees the data associated with an approver */
+static void
+approver_free (gpointer papprover, gpointer user_data)
+{
+ Approver * approver = (Approver *)papprover;
+ g_return_if_fail(approver != NULL);
+
+ ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(user_data);
+ g_list_foreach(appstore->priv->applications, remove_approver, approver->proxy);
+
+ if (approver->proxy != NULL) {
+ if (!approver->destroy_by_proxy) {
+ g_object_unref(approver->proxy);
+ }
+ approver->proxy = NULL;
+ }
+
+ g_free(approver);
+ return;
+}
+
+/* What did the approver tell us? */
+static void
+approver_request_cb (DBusGProxy *proxy, gboolean OUT_approved, GError *error, gpointer userdata)
+{
+ if (error == NULL) {
+ g_debug("Approver responded: %s", OUT_approved ? "approve" : "rejected");
+ } else {
+ g_debug("Approver responded error: %s", error->message);
+ }
+
+ Application * app = (Application *)userdata;
+
+ if (OUT_approved || error != NULL) {
+ app->approved_by = g_list_prepend(app->approved_by, proxy);
+ } else {
+ app->approved_by = g_list_remove(app->approved_by, proxy);
+ }
+
+ apply_status(app);
+ return;
+}
+
+/* Run the applications through the new approver */
+static void
+check_with_new_approver (gpointer papp, gpointer papprove)
+{
+ Application * app = (Application *)papp;
+ Approver * approver = (Approver *)papprove;
+
+ org_ayatana_StatusNotifierApprover_approve_item_async(approver->proxy,
+ app->id,
+ app->category,
+ 0,
+ app->dbus_name,
+ app->dbus_object,
+ approver_request_cb,
+ app);
+
+ return;
+}
+
+/* Look through all the approvers and find the one with a given
+ proxy. */
+static gint
+approver_find_by_proxy (gconstpointer papprover, gconstpointer pproxy)
+{
+ Approver * approver = (Approver *)papprover;
+
+ if (approver->proxy == pproxy) {
+ return 0;
+ }
+
+ return -1;
+}
+
+/* Tracks when a proxy gets destroyed so that we know that the
+ approver has dropped off the bus. */
+static void
+approver_destroyed (gpointer pproxy, gpointer pappstore)
+{
+ ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(pappstore);
+
+ GList * lapprover = g_list_find_custom(appstore->priv->approvers, pproxy, approver_find_by_proxy);
+ if (lapprover == NULL) {
+ g_warning("Approver proxy died, but we don't seem to have that approver.");
+ return;
+ }
+
+ Approver * approver = (Approver *)lapprover->data;
+ approver->destroy_by_proxy = TRUE;
+
+ appstore->priv->approvers = g_list_remove(appstore->priv->approvers, approver);
+ approver_free(approver, appstore);
+
+ return;
+}
+
+/* A signal when an approver changes the why that it thinks about
+ a particular indicator. */
+void
+approver_revise_judgement (DBusGProxy * proxy, gboolean new_status, gchar * address, DBusGProxy * get_path, gpointer user_data)
+{
+ g_return_if_fail(IS_APPLICATION_SERVICE_APPSTORE(user_data));
+ g_return_if_fail(address != NULL && address[0] != '\0');
+ g_return_if_fail(get_path != NULL);
+ const gchar * path = dbus_g_proxy_get_path(get_path);
+ g_return_if_fail(path != NULL && path[0] != '\0');
+
+ ApplicationServiceAppstore * appstore = APPLICATION_SERVICE_APPSTORE(user_data);
+
+ Application * app = find_application(appstore, address, path);
+
+ if (app == NULL) {
+ g_warning("Unable to update approver status of application (%s:%s) as it was not found", address, path);
+ return;
+ }
+
+ if (new_status) {
+ app->approved_by = g_list_prepend(app->approved_by, proxy);
+ } else {
+ app->approved_by = g_list_remove(app->approved_by, proxy);
+ }
+ apply_status(app);
+
+ return;
+}
+
+/* Adds a new approver to the app store */
+void
+application_service_appstore_approver_add (ApplicationServiceAppstore * appstore, const gchar * dbus_name, const gchar * dbus_object)
+{
+ g_return_if_fail(IS_APPLICATION_SERVICE_APPSTORE(appstore));
+ g_return_if_fail(dbus_name != NULL);
+ g_return_if_fail(dbus_object != NULL);
+ ApplicationServiceAppstorePrivate * priv = APPLICATION_SERVICE_APPSTORE_GET_PRIVATE (appstore);
+
+ Approver * approver = g_new0(Approver, 1);
+ approver->destroy_by_proxy = FALSE;
+
+ GError * error = NULL;
+ approver->proxy = dbus_g_proxy_new_for_name_owner(priv->bus,
+ dbus_name,
+ dbus_object,
+ NOTIFICATION_APPROVER_DBUS_IFACE,
+ &error);
+ if (error != NULL) {
+ g_warning("Unable to get approver interface on '%s:%s' : %s", dbus_name, dbus_object, error->message);
+ g_error_free(error);
+ g_free(approver);
+ return;
+ }
+
+ g_signal_connect(G_OBJECT(approver->proxy), "destroy", G_CALLBACK(approver_destroyed), appstore);
+
+ dbus_g_proxy_add_signal(approver->proxy,
+ "ReviseJudgement",
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal(approver->proxy,
+ "ReviseJudgement",
+ G_CALLBACK(approver_revise_judgement),
+ appstore,
+ NULL);
+
+ priv->approvers = g_list_prepend(priv->approvers, approver);
+
+ g_list_foreach(priv->applications, check_with_new_approver, approver);
+
+ return;
+}
+
diff --git a/src/application-service-appstore.h b/src/application-service-appstore.h
index d2e0013..aa2824b 100644
--- a/src/application-service-appstore.h
+++ b/src/application-service-appstore.h
@@ -25,7 +25,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <glib.h>
#include <glib-object.h>
-#include "application-service-lru-file.h"
G_BEGIN_DECLS
@@ -38,6 +37,7 @@ G_BEGIN_DECLS
typedef struct _ApplicationServiceAppstore ApplicationServiceAppstore;
typedef struct _ApplicationServiceAppstoreClass ApplicationServiceAppstoreClass;
+typedef struct _ApplicationServiceAppstorePrivate ApplicationServiceAppstorePrivate;
struct _ApplicationServiceAppstoreClass {
GObjectClass parent_class;
@@ -45,20 +45,28 @@ struct _ApplicationServiceAppstoreClass {
void (*application_added) (ApplicationServiceAppstore * appstore, gchar *, gint, gchar *, gchar *, gpointer);
void (*application_removed) (ApplicationServiceAppstore * appstore, gint, gpointer);
void (*application_icon_changed)(ApplicationServiceAppstore * appstore, gint, const gchar *, gpointer);
+ void (*application_icon_theme_path_changed)(ApplicationServiceAppstore * appstore, gint, const gchar *, gpointer);
+ void (*application_label_changed)(ApplicationServiceAppstore * appstore, gint, const gchar *, const gchar *, gpointer);
};
struct _ApplicationServiceAppstore {
GObject parent;
+
+ ApplicationServiceAppstorePrivate * priv;
};
-ApplicationServiceAppstore * application_service_appstore_new (AppLruFile * lrufile);
+ApplicationServiceAppstore * application_service_appstore_new (void);
GType application_service_appstore_get_type (void);
void application_service_appstore_application_add (ApplicationServiceAppstore * appstore,
- const gchar * dbus_name,
- const gchar * dbus_object);
+ const gchar * dbus_name,
+ const gchar * dbus_object);
void application_service_appstore_application_remove (ApplicationServiceAppstore * appstore,
- const gchar * dbus_name,
- const gchar * dbus_object);
+ const gchar * dbus_name,
+ const gchar * dbus_object);
+void application_service_appstore_approver_add (ApplicationServiceAppstore * appstore,
+ const gchar * dbus_name,
+ const gchar * dbus_object);
+gchar** application_service_appstore_application_get_list (ApplicationServiceAppstore * appstore);
G_END_DECLS
diff --git a/src/application-service-lru-file.c b/src/application-service-lru-file.c
deleted file mode 100644
index c69a20f..0000000
--- a/src/application-service-lru-file.c
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
-This object manages the database of when the entires were touched
-and loved. And writes that out to disk occationally as well.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- 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
-
-#include <string.h>
-#include <gio/gio.h>
-#include <json-glib/json-glib.h>
-#include "application-service-lru-file.h"
-
-#define ENTRY_CATEGORY "category"
-#define ENTRY_FIRST_TIME "first-time"
-#define ENTRY_LAST_TIME "last-time"
-#define ENTRY_VERSION "version"
-
-#define CONFIG_DIR ("indicators" G_DIR_SEPARATOR_S "application")
-#define CONFIG_FILE "lru-file.json"
-
-typedef struct _AppLruFilePrivate AppLruFilePrivate;
-struct _AppLruFilePrivate {
- GHashTable * apps;
- gboolean dirty;
- guint timer;
- gchar * filename;
-};
-
-typedef struct _AppData AppData;
-struct _AppData {
- gchar * category;
- GTimeVal last_touched;
- GTimeVal first_touched;
-};
-
-#define APP_LRU_FILE_GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_LRU_FILE_TYPE, AppLruFilePrivate))
-
-static void app_lru_file_class_init (AppLruFileClass *klass);
-static void app_lru_file_init (AppLruFile *self);
-static void app_lru_file_dispose (GObject *object);
-static void app_lru_file_finalize (GObject *object);
-static void app_data_free (gpointer data);
-static void get_dirty (AppLruFile * lrufile);
-static gboolean load_from_file (gpointer data);
-static void load_file_object_cb (JsonObject * obj, const gchar * key, JsonNode * value, gpointer data);
-static void clean_off_hash_cb (gpointer key, gpointer value, gpointer data);
-static void clean_off_write_end_cb (GObject * obj, GAsyncResult * res, gpointer data);
-
-G_DEFINE_TYPE (AppLruFile, app_lru_file, G_TYPE_OBJECT);
-
-/* Set up the class variable stuff */
-static void
-app_lru_file_class_init (AppLruFileClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (klass, sizeof (AppLruFilePrivate));
-
- object_class->dispose = app_lru_file_dispose;
- object_class->finalize = app_lru_file_finalize;
-
- return;
-}
-
-/* Set all the data of the per-instance variables */
-static void
-app_lru_file_init (AppLruFile *self)
-{
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(self);
-
- /* Default values */
- priv->apps = NULL;
- priv->dirty = FALSE;
- priv->timer = 0;
- priv->filename = NULL;
-
- /* Now let's build some stuff */
- priv->apps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, app_data_free);
- priv->filename = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, CONFIG_FILE, NULL);
-
- /* No reason to delay other stuff for this, we'll
- merge any values that get touched. */
- g_idle_add(load_from_file, self);
-
- return;
-}
-
-/* Get rid of objects and other big things */
-static void
-app_lru_file_dispose (GObject *object)
-{
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(object);
-
- if (priv->timer != 0) {
- g_source_remove(priv->timer);
- priv->timer = 0;
- }
-
- if (priv->apps != NULL) {
- /* Cleans up the keys and entries itself */
- g_hash_table_destroy(priv->apps);
- priv->apps = NULL;
- }
-
- G_OBJECT_CLASS (app_lru_file_parent_class)->dispose (object);
- return;
-}
-
-/* Deallocate memory */
-static void
-app_lru_file_finalize (GObject *object)
-{
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(object);
-
- if (priv->filename != NULL) {
- g_free(priv->filename);
- priv->filename = NULL;
- }
-
- G_OBJECT_CLASS (app_lru_file_parent_class)->finalize (object);
- return;
-}
-
-/* Takes an AppData structure and free's the
- memory from it. */
-static void
-app_data_free (gpointer data)
-{
- AppData * appdata = (AppData *)data;
-
- if (appdata->category != NULL) {
- g_free(appdata->category);
- }
-
- g_free(appdata);
-
- return;
-}
-
-/* Loads all of the data out of a json file */
-static gboolean
-load_from_file (gpointer data)
-{
- AppLruFile * lrufile = (AppLruFile *)data;
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile);
-
- if (!g_file_test(priv->filename, G_FILE_TEST_EXISTS)) {
- return FALSE;
- }
-
- JsonParser * parser = json_parser_new();
-
- if (!json_parser_load_from_file(parser, priv->filename, NULL)) {
- g_warning("Unable to parse JSON file '%s'", priv->filename);
- g_object_unref(parser);
- return FALSE;
- }
-
- JsonNode * root = json_parser_get_root(parser);
- JsonObject * rootobj = json_node_get_object(root);
- if (rootobj == NULL) {
- g_warning("Malformed LRU file. The root node is not an object.");
- g_object_unref(parser);
- return FALSE;
- }
-
- json_object_foreach_member(rootobj, load_file_object_cb, lrufile);
-
- g_object_unref(parser);
- return FALSE;
-}
-
-/* Looks at the various things that we need on a node, makes
- sure that we have them, and then copies them into the
- application hash table of love. */
-static void
-load_file_object_cb (JsonObject * rootobj, const gchar * key, JsonNode * value, gpointer data)
-{
- AppLruFile * lrufile = (AppLruFile *)data;
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile);
-
- /* We're not looking at this today. */
- if (!g_strcmp0(key, ENTRY_VERSION)) {
- return;
- }
-
- JsonObject * obj = json_node_get_object(value);
- if (obj == NULL) {
- g_warning("Data for node '%s' is not an object.", key);
- return;
- }
-
- const gchar * obj_category = json_object_get_string_member(obj, ENTRY_CATEGORY);
- const gchar * obj_first = json_object_get_string_member(obj, ENTRY_FIRST_TIME);
- const gchar * obj_last = json_object_get_string_member(obj, ENTRY_LAST_TIME);
-
- if (obj_category == NULL || obj_first == NULL || obj_last == NULL) {
- g_warning("Node '%s' is missing data. Got: ('%s', '%s', '%s')", key, obj_category, obj_first, obj_last);
- get_dirty(lrufile);
- return;
- }
-
- /* Check to see how old this entry is. If it hasn't been
- used in the last year, we remove the cruft. */
- GTimeVal currenttime;
- g_get_current_time(&currenttime);
- GDate * currentdate = g_date_new();
- g_date_set_time_val(currentdate, &currenttime);
-
- GTimeVal lasttouch;
- g_time_val_from_iso8601(obj_last, &lasttouch);
- GDate * lastdate = g_date_new();
- g_date_set_time_val(lastdate, &lasttouch);
-
- gint spread = g_date_days_between(lastdate, currentdate);
-
- g_date_free(currentdate);
- g_date_free(lastdate);
-
- if (spread > 365) {
- g_debug("Removing node '%s' as it's %d days old.", key, spread);
- get_dirty(lrufile);
- return;
- }
-
- /* See if we already have one of these. It's a little bit
- unlikely, but since we're async, we need to check */
- gpointer datapntr = g_hash_table_lookup(priv->apps, key);
- if (datapntr == NULL) {
- /* Build a new node */
- AppData * appdata = g_new0(AppData, 1);
- appdata->category = g_strdup(obj_category);
- g_time_val_from_iso8601(obj_first, &appdata->first_touched);
- g_time_val_from_iso8601(obj_last, &appdata->last_touched);
-
- g_hash_table_insert(priv->apps, g_strdup(key), appdata);
- } else {
- /* Merge nodes */
- AppData * appdata = (AppData *)datapntr;
- GTimeVal temptime;
- g_time_val_from_iso8601(obj_first, &temptime);
-
- if (temptime.tv_sec < appdata->first_touched.tv_sec) {
- g_time_val_from_iso8601(obj_first, &appdata->first_touched);
- }
- }
-
- return;
-}
-
-/* Write out our cache to a file so that we can unmark the dirty
- bit and be happy. */
-static gboolean
-clean_off (gpointer data)
-{
- AppLruFile * lrufile = (AppLruFile *)data;
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile);
- priv->timer = 0;
-
- GError * error = NULL;
-
- /* Check to see if our directory exists. Build it if not. */
- gchar * dirname = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, NULL);
- if (!g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
- GFile * dirfile = g_file_new_for_path(dirname);
- g_file_make_directory_with_parents(dirfile, NULL, NULL);
- g_object_unref(dirfile);
- }
- g_free(dirname);
-
- GFile * file = g_file_new_for_path(priv->filename);
- GFileOutputStream * ostream = g_file_replace(file,
- NULL, /* etag */
- TRUE, /* backup */
- G_FILE_CREATE_NONE, /* flags */
- NULL, /* cancelable */
- &error);
- if (error != NULL) {
- g_warning("Unable to open a file to store LRU file: %s", error->message);
- return FALSE;
- }
-
- /* This is how the file will start */
- GString * filestring = g_string_new("{\n \"" ENTRY_VERSION "\": 1");
-
- /* Put the middle in. */
- g_hash_table_foreach (priv->apps, clean_off_hash_cb, filestring);
-
- /* And then tack on the end. */
- g_string_append(filestring, "\n}\n");
- gchar * filedata = g_string_free(filestring, FALSE);
- g_output_stream_write_async(G_OUTPUT_STREAM(ostream),
- filedata,
- strlen(filedata),
- G_PRIORITY_DEFAULT_IDLE,
- NULL,
- clean_off_write_end_cb,
- filedata);
-
- return FALSE; /* drop the timer */
-}
-
-/* Looks at every value in the applications hash table and
- turns it into a string for writing out. */
-static void
-clean_off_hash_cb (gpointer key, gpointer value, gpointer data)
-{
- /* Mega-cast */
- gchar * id = (gchar *)key;
- AppData * appdata = (AppData *)value;
- GString * string = (GString *)data;
-
- gchar * firsttime = g_time_val_to_iso8601(&appdata->first_touched);
- gchar * lasttime = g_time_val_to_iso8601(&appdata->last_touched);
-
- g_string_append_printf(string, ",\n \"%s\": { \"" ENTRY_FIRST_TIME "\": \"%s\", \"" ENTRY_LAST_TIME "\": \"%s\", \"" ENTRY_CATEGORY "\": \"%s\"}", id, firsttime, lasttime, appdata->category);
-
- g_free(lasttime);
- g_free(firsttime);
-
- return;
-}
-
-/* Very much like clean_off_write_cb except that it is the
- last actor on this Output Stream so it closes it. */
-static void
-clean_off_write_end_cb (GObject * obj, GAsyncResult * res, gpointer data)
-{
- g_free(data);
-
- GError * error = NULL;
- g_output_stream_close(G_OUTPUT_STREAM(obj), NULL, &error);
-
- if (error != NULL) {
- g_warning("Unable to close LRU File: %s", error->message);
- g_error_free(error);
- }
-
- return;
-}
-
-/* Sets the dirty bit if not already set and makes sure that
- we have a timer to fix that at some point. */
-static void
-get_dirty (AppLruFile * lrufile)
-{
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile);
-
- priv->dirty = TRUE;
-
- if (priv->timer == 0) {
- priv->timer = g_timeout_add_seconds(60, clean_off, lrufile);
- }
-
- return;
-}
-
-/* API */
-
-/* Simple helper to create a new object */
-AppLruFile *
-app_lru_file_new (void)
-{
- return APP_LRU_FILE(g_object_new(APP_LRU_FILE_TYPE, NULL));
-}
-
-/* This updates the timestamp for a particular
- entry in the database. It also queues up a
- write out of the database. But that'll happen
- later. */
-void
-app_lru_file_touch (AppLruFile * lrufile, const gchar * id, const gchar * category)
-{
- g_return_if_fail(IS_APP_LRU_FILE(lrufile));
- g_return_if_fail(id != NULL && id[0] != '\0');
- g_return_if_fail(category != NULL && category[0] != '\0');
-
- AppData * appdata = NULL;
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile);
- gpointer data = g_hash_table_lookup(priv->apps, id);
-
- if (data == NULL) {
- /* Oh, we don't have one, let's build it and put it
- into the hash table ourselves */
- appdata = g_new0(AppData, 1);
-
- appdata->category = g_strdup(category);
- g_get_current_time(&(appdata->first_touched));
- /* NOTE: last touched set below */
-
- g_hash_table_insert(priv->apps, g_strdup(id), appdata);
- } else {
- /* Boring, we've got this one already */
- appdata = (AppData *)data;
- }
-
- /* Touch it and mark the DB as dirty */
- g_get_current_time(&(appdata->last_touched));
- get_dirty(lrufile);
- return;
-}
-
-/* Used to sort or compare different applications. */
-gint
-app_lru_file_sort (AppLruFile * lrufile, const gchar * id_a, const gchar * id_b)
-{
- g_return_val_if_fail(IS_APP_LRU_FILE(lrufile), -1);
-
- /* Let's first look to see if the IDs are the same, this
- really shouldn't happen, but it'll be confusing if we
- don't get it out of the way to start. */
- if (g_strcmp0(id_a, id_b) == 0) {
- return 0;
- }
-
- AppLruFilePrivate * priv = APP_LRU_FILE_GET_PRIVATE(lrufile);
-
- /* Now make sure we have app data for both of these. If
- not we'll assume that the one without is newer? */
- gpointer data_a = g_hash_table_lookup(priv->apps, id_a);
- if (data_a == NULL) {
- return -1;
- }
-
- gpointer data_b = g_hash_table_lookup(priv->apps, id_b);
- if (data_b == NULL) {
- return 1;
- }
-
- /* Ugly casting */
- AppData * appdata_a = (AppData *)data_a;
- AppData * appdata_b = (AppData *)data_b;
-
- /* Look at categories, we'll put the categories in alpha
- order if they're not the same. */
- gint catcompare = g_strcmp0(appdata_a->category, appdata_b->category);
- if (catcompare != 0) {
- return catcompare;
- }
-
- /* Now we're looking at the first time that these two were
- seen in this account. Only using seconds. I mean, seriously. */
- if (appdata_a->first_touched.tv_sec < appdata_b->first_touched.tv_sec) {
- return -1;
- }
- if (appdata_b->first_touched.tv_sec < appdata_a->first_touched.tv_sec) {
- return 1;
- }
-
- /* Eh, this seems roughly impossible. But if we have to choose,
- I like A better. */
- return 1;
-}
diff --git a/src/application-service-lru-file.h b/src/application-service-lru-file.h
deleted file mode 100644
index 7c92c82..0000000
--- a/src/application-service-lru-file.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-This object manages the database of when the entires were touched
-and loved. And writes that out to disk occationally as well.
-
-Copyright 2010 Canonical Ltd.
-
-Authors:
- 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/>.
-*/
-
-#ifndef __APP_LRU_FILE_H__
-#define __APP_LRU_FILE_H__
-
-#include <glib.h>
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define APP_LRU_FILE_TYPE (app_lru_file_get_type ())
-#define APP_LRU_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), APP_LRU_FILE_TYPE, AppLruFile))
-#define APP_LRU_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), APP_LRU_FILE_TYPE, AppLruFileClass))
-#define IS_APP_LRU_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), APP_LRU_FILE_TYPE))
-#define IS_APP_LRU_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_LRU_FILE_TYPE))
-#define APP_LRU_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_LRU_FILE_TYPE, AppLruFileClass))
-
-typedef struct _AppLruFile AppLruFile;
-typedef struct _AppLruFileClass AppLruFileClass;
-
-struct _AppLruFileClass {
- GObjectClass parent_class;
-
-};
-
-struct _AppLruFile {
- GObject parent;
-
-};
-
-GType app_lru_file_get_type (void);
-
-AppLruFile * app_lru_file_new (void);
-void app_lru_file_touch (AppLruFile * lrufile, const gchar * id, const gchar * category);
-gint app_lru_file_sort (AppLruFile * lrufile, const gchar * id_a, const gchar * id_b);
-
-G_END_DECLS
-
-#endif
diff --git a/src/application-service-marshal.list b/src/application-service-marshal.list
index 4ac8398..2b2efa5 100644
--- a/src/application-service-marshal.list
+++ b/src/application-service-marshal.list
@@ -16,5 +16,8 @@
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
-VOID: STRING, INT, STRING, STRING, STRING
+VOID: STRING, INT, STRING, STRING, STRING, STRING, STRING
+VOID: INT, STRING, STRING
VOID: INT, STRING
+VOID: STRING, STRING
+VOID: BOOL, STRING, OBJECT
diff --git a/src/application-service-watcher.c b/src/application-service-watcher.c
index eff249d..50b0be9 100644
--- a/src/application-service-watcher.c
+++ b/src/application-service-watcher.c
@@ -30,11 +30,25 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "application-service-watcher.h"
#include "dbus-shared.h"
+/* Enum for the properties so that they can be quickly
+ found and looked up. */
+enum {
+ PROP_0,
+ PROP_PROTOCOL_VERSION,
+ PROP_IS_STATUS_NOTIFIER_HOST_REGISTERED,
+ PROP_REGISTERED_STATUS_NOTIFIER_ITEMS
+};
+
+/* The strings so that they can be slowly looked up. */
+#define PROP_PROTOCOL_VERSION_S "protocol-version"
+#define PROP_IS_STATUS_NOTIFIER_HOST_REGISTERED_S "is-status-notifier-host-registered"
+#define PROP_REGISTERED_STATUS_NOTIFIER_ITEMS_S "registered-status-notifier-items"
+
+#define CURRENT_PROTOCOL_VERSION 0
+
static gboolean _notification_watcher_server_register_status_notifier_item (ApplicationServiceWatcher * appwatcher, const gchar * service, DBusGMethodInvocation * method);
-static gboolean _notification_watcher_server_registered_status_notifier_items (ApplicationServiceWatcher * appwatcher, GArray ** apps);
-static gboolean _notification_watcher_server_protocol_version (ApplicationServiceWatcher * appwatcher, char ** version);
-static gboolean _notification_watcher_server_register_notification_host (ApplicationServiceWatcher * appwatcher, const gchar * host);
-static gboolean _notification_watcher_server_is_notification_host_registered (ApplicationServiceWatcher * appwatcher, gboolean * haveHost);
+static gboolean _notification_watcher_server_register_status_notifier_host (ApplicationServiceWatcher * appwatcher, const gchar * host);
+static gboolean _notification_watcher_server_x_ayatana_register_notification_approver (ApplicationServiceWatcher * appwatcher, const gchar * path, const GArray * categories, DBusGMethodInvocation * method);
static void get_name_cb (DBusGProxy * proxy, guint status, GError * error, gpointer data);
#include "notification-watcher-server.h"
@@ -51,10 +65,9 @@ struct _ApplicationServiceWatcherPrivate {
/* Signals Stuff */
enum {
- SERVICE_REGISTERED,
- SERVICE_UNREGISTERED,
- NOTIFICATION_HOST_REGISTERED,
- NOTIFICATION_HOST_UNREGISTERED,
+ STATUS_NOTIFIER_ITEM_REGISTERED,
+ STATUS_NOTIFIER_ITEM_UNREGISTERED,
+ STATUS_NOTIFIER_HOST_REGISTERED,
LAST_SIGNAL
};
@@ -65,6 +78,8 @@ static void application_service_watcher_class_init (ApplicationServiceWatcherCla
static void application_service_watcher_init (ApplicationServiceWatcher *self);
static void application_service_watcher_dispose (GObject *object);
static void application_service_watcher_finalize (GObject *object);
+static void application_service_watcher_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
+static void application_service_watcher_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
G_DEFINE_TYPE (ApplicationServiceWatcher, application_service_watcher, G_TYPE_OBJECT);
@@ -78,31 +93,52 @@ application_service_watcher_class_init (ApplicationServiceWatcherClass *klass)
object_class->dispose = application_service_watcher_dispose;
object_class->finalize = application_service_watcher_finalize;
- signals[SERVICE_REGISTERED] = g_signal_new ("service-registered",
+ /* Property funcs */
+ object_class->set_property = application_service_watcher_set_property;
+ object_class->get_property = application_service_watcher_get_property;
+
+ /* Properties */
+ g_object_class_install_property (object_class,
+ PROP_PROTOCOL_VERSION,
+ g_param_spec_int(PROP_PROTOCOL_VERSION_S,
+ "Protocol Version",
+ "Which version of the StatusNotifierProtocol this watcher implements",
+ 0, G_MAXINT,
+ CURRENT_PROTOCOL_VERSION,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_IS_STATUS_NOTIFIER_HOST_REGISTERED,
+ g_param_spec_boolean(PROP_IS_STATUS_NOTIFIER_HOST_REGISTERED_S,
+ "Is StatusNotifierHost Registered",
+ "True if there is at least one StatusNotifierHost registered",
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class,
+ PROP_REGISTERED_STATUS_NOTIFIER_ITEMS,
+ g_param_spec_boxed(PROP_REGISTERED_STATUS_NOTIFIER_ITEMS_S,
+ "Registered StatusNotifierItems",
+ "The list of StatusNotifierItems registered to this watcher",
+ G_TYPE_STRV,
+ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+ /* Signals */
+ signals[STATUS_NOTIFIER_ITEM_REGISTERED] = g_signal_new ("status-notifier-item-registered",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ApplicationServiceWatcherClass, service_registered),
+ G_STRUCT_OFFSET (ApplicationServiceWatcherClass, status_notifier_item_registered),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING, G_TYPE_NONE);
- signals[SERVICE_UNREGISTERED] = g_signal_new ("service-unregistered",
+ signals[STATUS_NOTIFIER_ITEM_UNREGISTERED] = g_signal_new ("status-notifier-item-unregistered",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ApplicationServiceWatcherClass, service_unregistered),
+ G_STRUCT_OFFSET (ApplicationServiceWatcherClass, status_notifier_item_unregistered),
NULL, NULL,
g_cclosure_marshal_VOID__STRING,
G_TYPE_NONE, 1, G_TYPE_STRING, G_TYPE_NONE);
- signals[NOTIFICATION_HOST_REGISTERED] = g_signal_new ("notification-host-registered",
- G_TYPE_FROM_CLASS(klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ApplicationServiceWatcherClass, notification_host_registered),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0, G_TYPE_NONE);
- signals[NOTIFICATION_HOST_UNREGISTERED] = g_signal_new ("notification-host-unregistered",
+ signals[STATUS_NOTIFIER_HOST_REGISTERED] = g_signal_new ("status-notifier-host-registered",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (ApplicationServiceWatcherClass, notification_host_unregistered),
+ G_STRUCT_OFFSET (ApplicationServiceWatcherClass, status_notifier_host_registered),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, G_TYPE_NONE);
@@ -174,6 +210,29 @@ application_service_watcher_finalize (GObject *object)
return;
}
+static void
+application_service_watcher_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ /* There are no writable properties for now */
+}
+
+static void
+application_service_watcher_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ ApplicationServiceWatcherPrivate * priv = APPLICATION_SERVICE_WATCHER_GET_PRIVATE(object);
+ switch (prop_id) {
+ case PROP_PROTOCOL_VERSION:
+ g_value_set_int (value, CURRENT_PROTOCOL_VERSION);
+ break;
+ case PROP_IS_STATUS_NOTIFIER_HOST_REGISTERED:
+ g_value_set_boolean (value, TRUE);
+ break;
+ case PROP_REGISTERED_STATUS_NOTIFIER_ITEMS:
+ g_value_set_boxed (value, application_service_appstore_application_get_list(priv->appstore));
+ break;
+ }
+}
+
ApplicationServiceWatcher *
application_service_watcher_new (ApplicationServiceAppstore * appstore)
{
@@ -204,33 +263,12 @@ _notification_watcher_server_register_status_notifier_item (ApplicationServiceWa
}
static gboolean
-_notification_watcher_server_registered_status_notifier_items (ApplicationServiceWatcher * appwatcher, GArray ** apps)
+_notification_watcher_server_register_status_notifier_host (ApplicationServiceWatcher * appwatcher, const gchar * host)
{
return FALSE;
}
-static gboolean
-_notification_watcher_server_protocol_version (ApplicationServiceWatcher * appwatcher, char ** version)
-{
- *version = g_strdup("Ayatana Version 1");
- return TRUE;
-}
-
-static gboolean
-_notification_watcher_server_register_notification_host (ApplicationServiceWatcher * appwatcher, const gchar * host)
-{
-
- return FALSE;
-}
-
-static gboolean
-_notification_watcher_server_is_notification_host_registered (ApplicationServiceWatcher * appwatcher, gboolean * haveHost)
-{
- *haveHost = TRUE;
- return TRUE;
-}
-
/* Function to handle the return of the get name. There isn't a whole
lot that can be done, but we're atleast going to tell people. */
static void
@@ -249,3 +287,16 @@ get_name_cb (DBusGProxy * proxy, guint status, GError * error, gpointer data)
return;
}
+
+static gboolean
+_notification_watcher_server_x_ayatana_register_notification_approver (ApplicationServiceWatcher * appwatcher, const gchar * path, const GArray * categories, DBusGMethodInvocation * method)
+{
+ ApplicationServiceWatcherPrivate * priv = APPLICATION_SERVICE_WATCHER_GET_PRIVATE(appwatcher);
+
+ application_service_appstore_approver_add(priv->appstore,
+ dbus_g_method_get_sender(method),
+ path);
+
+ dbus_g_method_return(method, G_TYPE_NONE);
+ return TRUE;
+}
diff --git a/src/application-service-watcher.h b/src/application-service-watcher.h
index ee6a723..6c430db 100644
--- a/src/application-service-watcher.h
+++ b/src/application-service-watcher.h
@@ -44,10 +44,9 @@ struct _ApplicationServiceWatcherClass {
GObjectClass parent_class;
/* Signals */
- void (*service_registered) (ApplicationServiceWatcher * watcher, gchar * object, gpointer data);
- void (*service_unregistered) (ApplicationServiceWatcher * watcher, gchar * object, gpointer data);
- void (*notification_host_registered) (ApplicationServiceWatcher * watcher, gpointer data);
- void (*notification_host_unregistered) (ApplicationServiceWatcher * watcher, gpointer data);
+ void (*status_notifier_item_registered) (ApplicationServiceWatcher * watcher, gchar * object, gpointer data);
+ void (*status_notifier_item_unregistered) (ApplicationServiceWatcher * watcher, gchar * object, gpointer data);
+ void (*status_notifier_host_registered) (ApplicationServiceWatcher * watcher, gpointer data);
};
struct _ApplicationServiceWatcher {
diff --git a/src/application-service.c b/src/application-service.c
index ffd042e..94e7d2e 100644
--- a/src/application-service.c
+++ b/src/application-service.c
@@ -25,7 +25,6 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "notification-item-client.h"
#include "application-service-appstore.h"
#include "application-service-watcher.h"
-#include "application-service-lru-file.h"
#include "dbus-shared.h"
/* The base main loop */
@@ -36,8 +35,6 @@ static ApplicationServiceAppstore * appstore = NULL;
static ApplicationServiceWatcher * watcher = NULL;
/* The service management interface */
static IndicatorService * service = NULL;
-/* The LRU file interface */
-static AppLruFile * lrufile = NULL;
/* Recieves the disonnection signal from the service
object and closes the mainloop. */
@@ -62,11 +59,8 @@ main (int argc, char ** argv)
service = indicator_service_new(INDICATOR_APPLICATION_DBUS_ADDR);
g_signal_connect(G_OBJECT(service), INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_disconnected), NULL);
- /* Start up the LRU file reading */
- lrufile = app_lru_file_new();
-
/* Building our app store */
- appstore = application_service_appstore_new(lrufile);
+ appstore = application_service_appstore_new();
/* Adding a watcher for the Apps coming up */
watcher = application_service_watcher_new(appstore);
@@ -79,7 +73,6 @@ main (int argc, char ** argv)
g_object_unref(G_OBJECT(watcher));
g_object_unref(G_OBJECT(appstore));
g_object_unref(G_OBJECT(service));
- g_object_unref(G_OBJECT(lrufile));
return 0;
}
diff --git a/src/application-service.xml b/src/application-service.xml
index 0b2e959..031bf68 100644
--- a/src/application-service.xml
+++ b/src/application-service.xml
@@ -26,7 +26,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
<!-- Methods -->
<method name="GetApplications">
- <arg type="a(sisos)" name="applications" direction="out" />
+ <arg type="a(sisosss)" name="applications" direction="out" />
</method>
<!-- Signals -->
@@ -36,6 +36,8 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
<arg type="s" name="dbusaddress" direction="out" />
<arg type="o" name="dbusobject" direction="out" />
<arg type="s" name="iconpath" direction="out" />
+ <arg type="s" name="label" direction="out" />
+ <arg type="s" name="labelguide" direction="out" />
</signal>
<signal name="ApplicationRemoved">
<arg type="i" name="position" direction="out" />
@@ -44,6 +46,14 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
<arg type="i" name="position" direction="out" />
<arg type="s" name="icon_name" direction="out" />
</signal>
-
+ <signal name="ApplicationIconThemePathChanged">
+ <arg type="i" name="position" direction="out" />
+ <arg type="s" name="icon_theme_path" direction="out" />
+ </signal>
+ <signal name="ApplicationLabelChanged">
+ <arg type="i" name="position" direction="out" />
+ <arg type="s" name="label" direction="out" />
+ <arg type="s" name="guide" direction="out" />
+ </signal>
</interface>
</node>
diff --git a/src/dbus-properties-client.h b/src/dbus-properties-client.h
deleted file mode 100644
index 6f08e78..0000000
--- a/src/dbus-properties-client.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Generated by dbus-binding-tool; do not edit! */
-
-#include <glib.h>
-#include <dbus/dbus-glib.h>
-
-G_BEGIN_DECLS
-
-#ifndef _DBUS_GLIB_ASYNC_DATA_FREE
-#define _DBUS_GLIB_ASYNC_DATA_FREE
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-void
-_dbus_glib_async_data_free (gpointer stuff)
-{
- g_slice_free (DBusGAsyncData, stuff);
-}
-#endif
-
-#ifndef DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_DBus_Properties
-#define DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_DBus_Properties
-
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-gboolean
-org_freedesktop_DBus_Properties_get (DBusGProxy *proxy, const char * IN_Interface_Name, const char * IN_Property_Name, GValue* OUT_Value, GError **error)
-
-{
- return dbus_g_proxy_call (proxy, "Get", error, G_TYPE_STRING, IN_Interface_Name, G_TYPE_STRING, IN_Property_Name, G_TYPE_INVALID, G_TYPE_VALUE, OUT_Value, G_TYPE_INVALID);
-}
-
-typedef void (*org_freedesktop_DBus_Properties_get_reply) (DBusGProxy *proxy, GValue OUT_Value, GError *error, gpointer userdata);
-
-static void
-org_freedesktop_DBus_Properties_get_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
-{
- DBusGAsyncData *data = (DBusGAsyncData*) user_data;
- GError *error = NULL;
- GValue OUT_Value = { 0, };
- dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_VALUE, &OUT_Value, G_TYPE_INVALID);
- (*(org_freedesktop_DBus_Properties_get_reply)data->cb) (proxy, OUT_Value, error, data->userdata);
- return;
-}
-
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-DBusGProxyCall*
-org_freedesktop_DBus_Properties_get_async (DBusGProxy *proxy, const char * IN_Interface_Name, const char * IN_Property_Name, org_freedesktop_DBus_Properties_get_reply callback, gpointer userdata)
-
-{
- DBusGAsyncData *stuff;
- stuff = g_slice_new (DBusGAsyncData);
- stuff->cb = G_CALLBACK (callback);
- stuff->userdata = userdata;
- return dbus_g_proxy_begin_call (proxy, "Get", org_freedesktop_DBus_Properties_get_async_callback, stuff, _dbus_glib_async_data_free, G_TYPE_STRING, IN_Interface_Name, G_TYPE_STRING, IN_Property_Name, G_TYPE_INVALID);
-}
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-gboolean
-org_freedesktop_DBus_Properties_set (DBusGProxy *proxy, const char * IN_Interface_Name, const char * IN_Property_Name, const GValue* IN_Value, GError **error)
-
-{
- return dbus_g_proxy_call (proxy, "Set", error, G_TYPE_STRING, IN_Interface_Name, G_TYPE_STRING, IN_Property_Name, G_TYPE_VALUE, IN_Value, G_TYPE_INVALID, G_TYPE_INVALID);
-}
-
-typedef void (*org_freedesktop_DBus_Properties_set_reply) (DBusGProxy *proxy, GError *error, gpointer userdata);
-
-static void
-org_freedesktop_DBus_Properties_set_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
-{
- DBusGAsyncData *data = (DBusGAsyncData*) user_data;
- GError *error = NULL;
- dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID);
- (*(org_freedesktop_DBus_Properties_set_reply)data->cb) (proxy, error, data->userdata);
- return;
-}
-
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-DBusGProxyCall*
-org_freedesktop_DBus_Properties_set_async (DBusGProxy *proxy, const char * IN_Interface_Name, const char * IN_Property_Name, const GValue* IN_Value, org_freedesktop_DBus_Properties_set_reply callback, gpointer userdata)
-
-{
- DBusGAsyncData *stuff;
- stuff = g_slice_new (DBusGAsyncData);
- stuff->cb = G_CALLBACK (callback);
- stuff->userdata = userdata;
- return dbus_g_proxy_begin_call (proxy, "Set", org_freedesktop_DBus_Properties_set_async_callback, stuff, _dbus_glib_async_data_free, G_TYPE_STRING, IN_Interface_Name, G_TYPE_STRING, IN_Property_Name, G_TYPE_VALUE, IN_Value, G_TYPE_INVALID);
-}
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-gboolean
-org_freedesktop_DBus_Properties_get_all (DBusGProxy *proxy, const char * IN_Interface_Name, GHashTable** OUT_Properties, GError **error)
-
-{
- return dbus_g_proxy_call (proxy, "GetAll", error, G_TYPE_STRING, IN_Interface_Name, G_TYPE_INVALID, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), OUT_Properties, G_TYPE_INVALID);
-}
-
-typedef void (*org_freedesktop_DBus_Properties_get_all_reply) (DBusGProxy *proxy, GHashTable *OUT_Properties, GError *error, gpointer userdata);
-
-static void
-org_freedesktop_DBus_Properties_get_all_async_callback (DBusGProxy *proxy, DBusGProxyCall *call, void *user_data)
-{
- DBusGAsyncData *data = (DBusGAsyncData*) user_data;
- GError *error = NULL;
- GHashTable* OUT_Properties;
- dbus_g_proxy_end_call (proxy, call, &error, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &OUT_Properties, G_TYPE_INVALID);
- (*(org_freedesktop_DBus_Properties_get_all_reply)data->cb) (proxy, OUT_Properties, error, data->userdata);
- return;
-}
-
-static
-#ifdef G_HAVE_INLINE
-inline
-#endif
-DBusGProxyCall*
-org_freedesktop_DBus_Properties_get_all_async (DBusGProxy *proxy, const char * IN_Interface_Name, org_freedesktop_DBus_Properties_get_all_reply callback, gpointer userdata)
-
-{
- DBusGAsyncData *stuff;
- stuff = g_slice_new (DBusGAsyncData);
- stuff->cb = G_CALLBACK (callback);
- stuff->userdata = userdata;
- return dbus_g_proxy_begin_call (proxy, "GetAll", org_freedesktop_DBus_Properties_get_all_async_callback, stuff, _dbus_glib_async_data_free, G_TYPE_STRING, IN_Interface_Name, G_TYPE_INVALID);
-}
-#endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_org_freedesktop_DBus_Properties */
-
-G_END_DECLS
diff --git a/src/dbus-shared.h b/src/dbus-shared.h
index f158b1c..1d8d89c 100644
--- a/src/dbus-shared.h
+++ b/src/dbus-shared.h
@@ -31,3 +31,5 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#define NOTIFICATION_ITEM_DBUS_IFACE "org.kde.StatusNotifierItem"
#define NOTIFICATION_ITEM_DEFAULT_OBJ "/StatusNotifierItem"
+#define NOTIFICATION_APPROVER_DBUS_IFACE "org.ayatana.StatusNotifierApprover"
+
diff --git a/src/generate-id.c b/src/generate-id.c
new file mode 100644
index 0000000..14d762e
--- /dev/null
+++ b/src/generate-id.c
@@ -0,0 +1,69 @@
+/*
+Quick litte lack to generate the ordering ID.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ 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 "generate-id.h"
+
+#define MULTIPLIER 32
+
+guint32
+generate_id (const AppIndicatorCategory catenum, const gchar * id)
+{
+ guchar category = 0;
+ guchar first = 0;
+ guchar second = 0;
+ guchar third = 0;
+
+ switch (catenum) {
+ case APP_INDICATOR_CATEGORY_OTHER:
+ category = MULTIPLIER * 5;
+ break;
+ case APP_INDICATOR_CATEGORY_APPLICATION_STATUS:
+ category = MULTIPLIER * 4;
+ break;
+ case APP_INDICATOR_CATEGORY_COMMUNICATIONS:
+ category = MULTIPLIER * 3;
+ break;
+ case APP_INDICATOR_CATEGORY_SYSTEM_SERVICES:
+ category = MULTIPLIER * 2;
+ break;
+ case APP_INDICATOR_CATEGORY_HARDWARE:
+ category = MULTIPLIER * 1;
+ break;
+ default:
+ g_warning("Got an undefined category: %d", category);
+ category = 0;
+ break;
+ }
+
+ if (id != NULL) {
+ if (id[0] != '\0') {
+ first = id[0];
+ if (id[1] != '\0') {
+ second = id[1];
+ if (id[2] != '\0') {
+ third = id[2];
+ }
+ }
+ }
+ }
+
+ return (((((category * 256) + first) * 256) + second) * 256) + third;
+}
diff --git a/src/generate-id.h b/src/generate-id.h
new file mode 100644
index 0000000..9d3167d
--- /dev/null
+++ b/src/generate-id.h
@@ -0,0 +1,30 @@
+/*
+Quick litte lack to generate the ordering ID.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ 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/>.
+*/
+
+#ifndef __GENERATE_ID_H__
+#define __GENERATE_ID_H__
+
+#include <glib.h>
+#include "app-indicator.h"
+
+guint32 generate_id (const AppIndicatorCategory category, const gchar * id);
+
+#endif /* __GENERATE_ID_H__ */
diff --git a/src/indicator-application.c b/src/indicator-application.c
index 7735539..ecf19e8 100644
--- a/src/indicator-application.c
+++ b/src/indicator-application.c
@@ -91,10 +91,12 @@ struct _IndicatorApplicationPrivate {
typedef struct _ApplicationEntry ApplicationEntry;
struct _ApplicationEntry {
IndicatorObjectEntry entry;
- gchar * icon_path;
+ gchar * icon_theme_path;
gboolean old_service;
gchar * dbusobject;
gchar * dbusaddress;
+ gchar * guide;
+ gchar * longname;
};
#define INDICATOR_APPLICATION_GET_PRIVATE(o) \
@@ -112,9 +114,11 @@ static void disconnected (IndicatorApplication * application);
static void disconnected_helper (gpointer data, gpointer user_data);
static gboolean disconnected_kill (gpointer user_data);
static void disconnected_kill_helper (gpointer data, gpointer user_data);
-static void application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, const gchar * icon_path, IndicatorApplication * application);
+static void application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, const gchar * icon_theme_path, const gchar * label, const gchar * guide, IndicatorApplication * application);
static void application_removed (DBusGProxy * proxy, gint position , IndicatorApplication * application);
+static void application_label_changed (DBusGProxy * proxy, gint position, const gchar * label, const gchar * guide, IndicatorApplication * application);
static void application_icon_changed (DBusGProxy * proxy, gint position, const gchar * iconname, IndicatorApplication * application);
+static void application_icon_theme_path_changed (DBusGProxy * proxy, gint position, const gchar * icon_theme_path, IndicatorApplication * application);
static void get_applications (DBusGProxy *proxy, GPtrArray *OUT_applications, GError *error, gpointer userdata);
static void get_applications_helper (gpointer data, gpointer user_data);
static void theme_dir_unref(IndicatorApplication * ia, const gchar * dir);
@@ -137,19 +141,27 @@ indicator_application_class_init (IndicatorApplicationClass *klass)
io_class->get_entries = get_entries;
io_class->get_location = get_location;
- dbus_g_object_register_marshaller(_application_service_marshal_VOID__STRING_INT_STRING_STRING_STRING,
+ dbus_g_object_register_marshaller(_application_service_marshal_VOID__STRING_INT_STRING_STRING_STRING_STRING_STRING,
G_TYPE_NONE,
G_TYPE_STRING,
G_TYPE_INT,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_object_register_marshaller(_application_service_marshal_VOID__INT_STRING,
G_TYPE_NONE,
G_TYPE_INT,
G_TYPE_STRING,
G_TYPE_INVALID);
+ dbus_g_object_register_marshaller(_application_service_marshal_VOID__INT_STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
return;
}
@@ -277,6 +289,8 @@ connected (IndicatorApplication * application)
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
G_TYPE_INVALID);
dbus_g_proxy_add_signal(priv->service_proxy,
"ApplicationRemoved",
@@ -287,6 +301,17 @@ connected (IndicatorApplication * application)
G_TYPE_INT,
G_TYPE_STRING,
G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->service_proxy,
+ "ApplicationIconThemePathChanged",
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal(priv->service_proxy,
+ "ApplicationLabelChanged",
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
/* Connect to them */
g_debug("Connect to them.");
@@ -305,6 +330,16 @@ connected (IndicatorApplication * application)
G_CALLBACK(application_icon_changed),
application,
NULL /* Disconnection Signal */);
+ dbus_g_proxy_connect_signal(priv->service_proxy,
+ "ApplicationIconThemePathChanged",
+ G_CALLBACK(application_icon_theme_path_changed),
+ application,
+ NULL /* Disconnection Signal */);
+ dbus_g_proxy_connect_signal(priv->service_proxy,
+ "ApplicationLabelChanged",
+ G_CALLBACK(application_label_changed),
+ application,
+ NULL /* Disconnection Signal */);
}
/* Query it for existing applications */
@@ -416,11 +451,51 @@ application_added_search (gconstpointer a, gconstpointer b)
return -1;
}
+/* Does a quick meausre of how big the string is in
+ pixels with a Pango layout */
+static gint
+measure_string (GtkStyle * style, PangoContext * context, const gchar * string)
+{
+ PangoLayout * layout = pango_layout_new(context);
+ pango_layout_set_text(layout, string, -1);
+ pango_layout_set_font_description(layout, style->font_desc);
+
+ gint width;
+ pango_layout_get_pixel_size(layout, &width, NULL);
+ g_object_unref(layout);
+ return width;
+}
+
+/* Try to get a good guess at what a maximum width of the entire
+ string would be. */
+static void
+guess_label_size (ApplicationEntry * app)
+{
+ /* This is during startup. */
+ if (app->entry.label == NULL) return;
+
+ GtkStyle * style = gtk_widget_get_style(GTK_WIDGET(app->entry.label));
+ PangoContext * context = gtk_widget_get_pango_context(GTK_WIDGET(app->entry.label));
+
+ gint length = measure_string(style, context, gtk_label_get_text(app->entry.label));
+
+ if (app->guide != NULL) {
+ gint guidelen = measure_string(style, context, app->guide);
+ if (guidelen > length) {
+ length = guidelen;
+ }
+ }
+
+ gtk_widget_set_size_request(GTK_WIDGET(app->entry.label), length, -1);
+
+ return;
+}
+
/* Here we respond to new applications by building up the
ApplicationEntry and signaling the indicator host that
we've got a new indicator. */
static void
-application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, const gchar * icon_path, IndicatorApplication * application)
+application_added (DBusGProxy * proxy, const gchar * iconname, gint position, const gchar * dbusaddress, const gchar * dbusobject, const gchar * icon_theme_path, const gchar * label, const gchar * guide, IndicatorApplication * application)
{
g_return_if_fail(IS_INDICATOR_APPLICATION(application));
g_debug("Building new application entry: %s with icon: %s", dbusaddress, iconname);
@@ -442,28 +517,46 @@ application_added (DBusGProxy * proxy, const gchar * iconname, gint position, co
ApplicationEntry * app = g_new(ApplicationEntry, 1);
app->old_service = FALSE;
- app->icon_path = NULL;
- if (icon_path != NULL && icon_path[0] != '\0') {
- app->icon_path = g_strdup(icon_path);
- theme_dir_ref(application, icon_path);
+ app->icon_theme_path = NULL;
+ if (icon_theme_path != NULL && icon_theme_path[0] != '\0') {
+ app->icon_theme_path = g_strdup(icon_theme_path);
+ theme_dir_ref(application, icon_theme_path);
}
app->dbusaddress = g_strdup(dbusaddress);
app->dbusobject = g_strdup(dbusobject);
+ app->guide = NULL;
/* We make a long name using the suffix, and if that
icon is available we want to use it. Otherwise we'll
just use the name we were given. */
- gchar * longname = NULL;
+ app->longname = NULL;
if (!g_str_has_suffix(iconname, PANEL_ICON_SUFFIX)) {
- longname = g_strdup_printf("%s-%s", iconname, PANEL_ICON_SUFFIX);
+ app->longname = g_strdup_printf("%s-%s", iconname, PANEL_ICON_SUFFIX);
} else {
- longname = g_strdup(iconname);
+ app->longname = g_strdup(iconname);
+ }
+ app->entry.image = indicator_image_helper(app->longname);
+
+ if (label == NULL || label[0] == '\0') {
+ app->entry.label = NULL;
+ } else {
+ app->entry.label = GTK_LABEL(gtk_label_new(label));
+ g_object_ref(G_OBJECT(app->entry.label));
+ gtk_widget_show(GTK_WIDGET(app->entry.label));
+
+ if (app->guide != NULL) {
+ g_free(app->guide);
+ app->guide = NULL;
+ }
+
+ if (guide != NULL) {
+ app->guide = g_strdup(guide);
+ }
+
+ guess_label_size(app);
}
- app->entry.image = indicator_image_helper(longname);
- g_free(longname);
- app->entry.label = NULL;
app->entry.menu = GTK_MENU(dbusmenu_gtkmenu_new((gchar *)dbusaddress, (gchar *)dbusobject));
/* Keep copies of these for ourself, just in case. */
@@ -474,7 +567,6 @@ application_added (DBusGProxy * proxy, const gchar * iconname, gint position, co
priv->applications = g_list_insert(priv->applications, app, position);
- /* TODO: Need to deal with position here somehow */
g_signal_emit(G_OBJECT(application), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED_ID, 0, &(app->entry), TRUE);
return;
}
@@ -496,9 +588,9 @@ application_removed (DBusGProxy * proxy, gint position, IndicatorApplication * a
priv->applications = g_list_remove(priv->applications, app);
g_signal_emit(G_OBJECT(application), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED_ID, 0, &(app->entry), TRUE);
- if (app->icon_path != NULL) {
- theme_dir_unref(application, app->icon_path);
- g_free(app->icon_path);
+ if (app->icon_theme_path != NULL) {
+ theme_dir_unref(application, app->icon_theme_path);
+ g_free(app->icon_theme_path);
}
if (app->dbusaddress != NULL) {
g_free(app->dbusaddress);
@@ -506,11 +598,16 @@ application_removed (DBusGProxy * proxy, gint position, IndicatorApplication * a
if (app->dbusobject != NULL) {
g_free(app->dbusobject);
}
+ if (app->guide != NULL) {
+ g_free(app->guide);
+ }
+ if (app->longname != NULL) {
+ g_free(app->longname);
+ }
if (app->entry.image != NULL) {
g_object_unref(G_OBJECT(app->entry.image));
}
if (app->entry.label != NULL) {
- g_warning("Odd, an application indicator with a label?");
g_object_unref(G_OBJECT(app->entry.label));
}
if (app->entry.menu != NULL) {
@@ -521,6 +618,87 @@ application_removed (DBusGProxy * proxy, gint position, IndicatorApplication * a
return;
}
+/* The callback for the signal that the label for an application
+ has changed. */
+static void
+application_label_changed (DBusGProxy * proxy, gint position, const gchar * label, const gchar * guide, IndicatorApplication * application)
+{
+ IndicatorApplicationPrivate * priv = INDICATOR_APPLICATION_GET_PRIVATE(application);
+ ApplicationEntry * app = (ApplicationEntry *)g_list_nth_data(priv->applications, position);
+ gboolean signal_reload = FALSE;
+
+ if (app == NULL) {
+ g_warning("Unable to find application at position: %d", position);
+ return;
+ }
+
+ if (label == NULL || label[0] == '\0') {
+ /* No label, let's see if we need to delete the old one */
+ if (app->entry.label != NULL) {
+ g_object_unref(G_OBJECT(app->entry.label));
+ app->entry.label = NULL;
+
+ signal_reload = TRUE;
+ }
+ } else {
+ /* We've got a label, is this just an update or is
+ it a new thing. */
+ if (app->entry.label != NULL) {
+ gtk_label_set_text(app->entry.label, label);
+ } else {
+ app->entry.label = GTK_LABEL(gtk_label_new(label));
+ g_object_ref(G_OBJECT(app->entry.label));
+ gtk_widget_show(GTK_WIDGET(app->entry.label));
+
+ signal_reload = TRUE;
+ }
+ }
+
+ /* Copy the guide if we have one */
+ if (app->guide != NULL) {
+ g_free(app->guide);
+ app->guide = NULL;
+ }
+
+ if (guide != NULL && guide[0] != '\0') {
+ app->guide = g_strdup(guide);
+ }
+
+ /* Protected against not having a label */
+ guess_label_size(app);
+
+ if (signal_reload) {
+ /* Telling the listener that this has been removed, and then
+ readded to make it reparse the entry. */
+ if (app->entry.label != NULL) {
+ gtk_widget_hide(GTK_WIDGET(app->entry.label));
+ }
+
+ if (app->entry.image != NULL) {
+ gtk_widget_hide(GTK_WIDGET(app->entry.image));
+ }
+
+ if (app->entry.menu != NULL) {
+ gtk_menu_detach(app->entry.menu);
+ }
+
+ g_signal_emit(G_OBJECT(application), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED_ID, 0, &(app->entry), TRUE);
+
+ if (app->entry.label != NULL) {
+ gtk_widget_show(GTK_WIDGET(app->entry.label));
+ }
+
+ if (app->entry.image != NULL) {
+ indicator_image_helper_update(app->entry.image, app->longname);
+ gtk_widget_show(GTK_WIDGET(app->entry.image));
+ }
+
+ g_signal_emit(G_OBJECT(application), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED_ID, 0, &(app->entry), TRUE);
+ }
+
+ return;
+}
+
/* The callback for the signal that the icon for an application
has changed. */
static void
@@ -537,9 +715,45 @@ application_icon_changed (DBusGProxy * proxy, gint position, const gchar * iconn
/* We make a long name using the suffix, and if that
icon is available we want to use it. Otherwise we'll
just use the name we were given. */
- gchar * longname = g_strdup_printf("%s-%s", iconname, PANEL_ICON_SUFFIX);
- indicator_image_helper_update(app->entry.image, longname);
- g_free(longname);
+ if (app->longname != NULL) {
+ g_free(app->longname);
+ app->longname = NULL;
+ }
+ if (!g_str_has_suffix(iconname, PANEL_ICON_SUFFIX)) {
+ app->longname = g_strdup_printf("%s-%s", iconname, PANEL_ICON_SUFFIX);
+ } else {
+ app->longname = g_strdup(iconname);
+ }
+ indicator_image_helper_update(app->entry.image, app->longname);
+
+ return;
+}
+
+/* The callback for the signal that the icon theme path for an application
+ has changed. */
+static void
+application_icon_theme_path_changed (DBusGProxy * proxy, gint position, const gchar * icon_theme_path, IndicatorApplication * application)
+{
+ IndicatorApplicationPrivate * priv = INDICATOR_APPLICATION_GET_PRIVATE(application);
+ ApplicationEntry * app = (ApplicationEntry *)g_list_nth_data(priv->applications, position);
+
+ if (app == NULL) {
+ g_warning("Unable to find application at position: %d", position);
+ return;
+ }
+
+ if (g_strcmp0(icon_theme_path, app->icon_theme_path) != 0) {
+ if(app->icon_theme_path != NULL) {
+ theme_dir_unref(application, app->icon_theme_path);
+ g_free(app->icon_theme_path);
+ app->icon_theme_path = NULL;
+ }
+ if (icon_theme_path != NULL && icon_theme_path[0] != '\0') {
+ app->icon_theme_path = g_strdup(icon_theme_path);
+ theme_dir_ref(application, app->icon_theme_path);
+ }
+ indicator_image_helper_update(app->entry.image, app->longname);
+ }
return;
}
@@ -565,19 +779,21 @@ get_applications_helper (gpointer data, gpointer user_data)
{
GValueArray * array = (GValueArray *)data;
- g_return_if_fail(array->n_values == 5);
+ g_return_if_fail(array->n_values == 7);
const gchar * icon_name = g_value_get_string(g_value_array_get_nth(array, 0));
gint position = g_value_get_int(g_value_array_get_nth(array, 1));
const gchar * dbus_address = g_value_get_string(g_value_array_get_nth(array, 2));
const gchar * dbus_object = g_value_get_boxed(g_value_array_get_nth(array, 3));
- const gchar * icon_path = g_value_get_string(g_value_array_get_nth(array, 4));
+ const gchar * icon_theme_path = g_value_get_string(g_value_array_get_nth(array, 4));
+ const gchar * label = g_value_get_string(g_value_array_get_nth(array, 5));
+ const gchar * guide = g_value_get_string(g_value_array_get_nth(array, 6));
- return application_added(NULL, icon_name, position, dbus_address, dbus_object, icon_path, user_data);
+ return application_added(NULL, icon_name, position, dbus_address, dbus_object, icon_theme_path, label, guide, user_data);
}
-/* Refs a theme directory, and it may add it to the search
- path */
+/* Unrefs a theme directory. This may involve removing it from
+ the search path. */
static void
theme_dir_unref(IndicatorApplication * ia, const gchar * dir)
{
@@ -636,8 +852,8 @@ theme_dir_unref(IndicatorApplication * ia, const gchar * dir)
return;
}
-/* Unrefs a theme directory. This may involve removing it from
- the search path. */
+/* Refs a theme directory, and it may add it to the search
+ path */
static void
theme_dir_ref(IndicatorApplication * ia, const gchar * dir)
{
diff --git a/src/notification-approver.xml b/src/notification-approver.xml
new file mode 100644
index 0000000..4a8e39b
--- /dev/null
+++ b/src/notification-approver.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/">
+ <interface name="org.ayatana.StatusNotifierApprover">
+
+<!-- Methods -->
+ <method name="ApproveItem">
+ <!-- KSNI ID -->
+ <arg type="s" name="id" direction="in" />
+ <!-- KSNI Category -->
+ <arg type="s" name="category" direction="in" />
+ <!-- Application PID -->
+ <arg type="u" name="pid" direction="in" />
+ <!-- Application DBus Address -->
+ <arg type="s" name="address" direction="in" />
+ <!-- Application DBus Path for KSNI interface -->
+ <arg type="o" name="path" direction="in" />
+ <!-- So, what do you think? -->
+ <arg type="b" name="approved" direction="out" />
+ </method>
+
+<!-- Signals -->
+ <signal name="ReviseJudgement">
+ <arg type="b" name="approved" direction="out" />
+ <arg type="s" name="address" direction="out" />
+ <arg type="o" name="path" direction="out" />
+ </signal>
+
+ </interface>
+</node>
diff --git a/src/notification-item.xml b/src/notification-item.xml
index a0141c3..05afd83 100644
--- a/src/notification-item.xml
+++ b/src/notification-item.xml
@@ -12,6 +12,9 @@
to find the icons specified above. -->
<property name="IconThemePath" type="s" access="read" />
<property name="Menu" type="o" access="read" />
+ <property name="XAyatanaLabel" type="s" access="read" />
+ <property name="XAyatanaLabelGuide" type="s" access="read" />
+ <property name="XAyatanaOrderingIndex" type="u" access="read" />
<!-- Methods -->
<!-- None currently -->
@@ -19,11 +22,18 @@
<!-- Signals -->
<signal name="NewIcon">
</signal>
+ <signal name="NewIconThemePath">
+ <arg type="s" name="icon_theme_path" direction="out" />
+ </signal>
<signal name="NewAttentionIcon">
</signal>
<signal name="NewStatus">
<arg type="s" name="status" direction="out" />
</signal>
+ <signal name="XAyatanaNewLabel">
+ <arg type="s" name="label" direction="out" />
+ <arg type="s" name="guide" direction="out" />
+ </signal>
</interface>
</node>
diff --git a/src/notification-watcher.xml b/src/notification-watcher.xml
index c2324f1..b03c5e3 100644
--- a/src/notification-watcher.xml
+++ b/src/notification-watcher.xml
@@ -3,36 +3,34 @@
<interface name="org.kde.StatusNotifierWatcher">
<!-- Properties -->
- <!-- None currently -->
+ <property name="ProtocolVersion" type="i" access="read" />
+ <property name="IsStatusNotifierHostRegistered" type="b" access="read" />
+ <property name="RegisteredStatusNotifierItems" type="as" access="read" />
<!-- Methods -->
<method name="RegisterStatusNotifierItem">
<annotation name="org.freedesktop.DBus.GLib.Async" value="true" />
<arg type="s" name="service" direction="in" />
</method>
- <method name="RegisteredStatusNotifierItems">
- <arg type="as" name="services" direction="out" />
- </method>
- <method name="ProtocolVersion">
- <arg type="s" name="version" direction="out" />
- </method>
- <method name="RegisterNotificationHost">
+ <method name="RegisterStatusNotifierHost">
<arg type="s" name="service" direction="in" />
</method>
- <method name="IsNotificationHostRegistered">
- <arg type="b" name="hasHost" direction="out" />
+ <method name="XAyatanaRegisterNotificationApprover">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true" />
+ <!-- The path where to find the approver interface -->
+ <arg type="o" name="path" direction="in" />
+ <!-- List of categories to approve, none represents all -->
+ <arg type="as" name="categories" direction="in" />
</method>
<!-- Signals -->
- <signal name="ServiceRegistered">
+ <signal name="StatusNotifierItemRegistered">
<arg type="s" name="service" direction="out" />
</signal>
- <signal name="ServiceUnregistered">
+ <signal name="StatusNotifierItemUnregistered">
<arg type="s" name="service" direction="out" />
</signal>
- <signal name="NotificationHostRegistered">
- </signal>
- <signal name="NotificationHostUnregistered">
+ <signal name="StatusNotifierHostRegistered">
</signal>
</interface>
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8d356bc..f2bdbb2 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,5 +1,6 @@
check_PROGRAMS = \
+ test-approver \
test-libappindicator \
test-libappindicator-dbus-client \
test-libappindicator-dbus-server \
@@ -99,6 +100,33 @@ test_libappindicator_status_server_LDADD = \
$(top_builddir)/src/libappindicator.la
#########################################
+## test-approver
+#########################################
+
+test_approver_SOURCES = \
+ test-approver.c
+
+test_approver_CFLAGS = \
+ $(INDICATOR_CFLAGS) \
+ -Wall -Werror \
+ -I$(top_srcdir)/src \
+ -I$(top_builddir)/src
+
+test_approver_LDADD = \
+ $(INDICATOR_LIBS) \
+ $(top_builddir)/src/libappindicator.la
+
+test-approver-tester: test-approver Makefile.am
+ @echo "#!/bin/bash" > $@
+ @echo export INDICATOR_SERVICE_SHUTDOWN_TIMEOUT=1000 >> $@
+ @echo . $(srcdir)/run-xvfb.sh >> $@
+ @echo $(DBUS_RUNNER) --task $(builddir)/test-approver --task-name Approver --task $(top_builddir)/src/indicator-application-service --task-name Service --ignore-return >> $@
+ @chmod +x $@
+
+TESTS += test-approver-tester
+
+
+#########################################
## test-libappindicator-fallback
#########################################
diff --git a/tests/test-approver.c b/tests/test-approver.c
new file mode 100644
index 0000000..2665505
--- /dev/null
+++ b/tests/test-approver.c
@@ -0,0 +1,179 @@
+#include <glib.h>
+#include <glib-object.h>
+
+#include <dbus/dbus-glib-bindings.h>
+
+#include "notification-watcher-client.h"
+#include "dbus-shared.h"
+#include "app-indicator.h"
+
+#define APPROVER_PATH "/my/approver"
+
+#define INDICATOR_ID "test-indicator-id"
+#define INDICATOR_ICON "test-indicator-icon-name"
+#define INDICATOR_CATEGORY APP_INDICATOR_CATEGORY_APPLICATION_STATUS
+
+#define TEST_APPROVER_TYPE (test_approver_get_type ())
+#define TEST_APPROVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_APPROVER_TYPE, TestApprover))
+#define TEST_APPROVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_APPROVER_TYPE, TestApproverClass))
+#define IS_TEST_APPROVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_APPROVER_TYPE))
+#define IS_TEST_APPROVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_APPROVER_TYPE))
+#define TEST_APPROVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_APPROVER_TYPE, TestApproverClass))
+
+typedef struct _TestApprover TestApprover;
+typedef struct _TestApproverClass TestApproverClass;
+
+struct _TestApproverClass {
+ GObjectClass parent_class;
+};
+
+struct _TestApprover {
+ GObject parent;
+};
+
+GType test_approver_get_type (void);
+
+static void test_approver_class_init (TestApproverClass *klass);
+static void test_approver_init (TestApprover *self);
+static gboolean _notification_approver_server_approve_item (TestApprover * ta, const gchar * id, const gchar * category, guint pid, const gchar * address, const gchar * path, gboolean * approved, GError ** error);
+
+#include "../src/notification-approver-server.h"
+
+GMainLoop * main_loop = NULL;
+DBusGConnection * session_bus = NULL;
+DBusGProxy * bus_proxy = NULL;
+AppIndicator * app_indicator = NULL;
+gboolean passed = FALSE;
+
+G_DEFINE_TYPE (TestApprover, test_approver, G_TYPE_OBJECT);
+
+static void
+test_approver_class_init (TestApproverClass *klass)
+{
+ dbus_g_object_type_install_info(TEST_APPROVER_TYPE,
+ &dbus_glib__notification_approver_server_object_info);
+
+ return;
+}
+
+static void
+test_approver_init (TestApprover *self)
+{
+ dbus_g_connection_register_g_object(session_bus,
+ APPROVER_PATH,
+ G_OBJECT(self));
+
+ return;
+}
+
+static gboolean
+_notification_approver_server_approve_item (TestApprover * ta, const gchar * id, const gchar * category, guint pid, const gchar * address, const gchar * path, gboolean * approved, GError ** error)
+{
+ *approved = TRUE;
+ g_debug("Asked to approve indicator");
+
+ if (g_strcmp0(id, INDICATOR_ID) == 0) {
+ passed = TRUE;
+ }
+
+ g_main_loop_quit(main_loop);
+
+ return TRUE;
+}
+
+static void
+register_cb (DBusGProxy * proxy, GError * error, gpointer user_data)
+{
+ if (error != NULL) {
+ g_warning("Unable to register approver: %s", error->message);
+ g_error_free(error);
+ g_main_loop_quit(main_loop);
+ return;
+ }
+
+ g_debug("Building App Indicator");
+ app_indicator = app_indicator_new(INDICATOR_ID, INDICATOR_ICON, INDICATOR_CATEGORY);
+
+ GtkWidget * menu = gtk_menu_new();
+ GtkWidget * mi = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
+
+ app_indicator_set_menu(app_indicator, GTK_MENU(menu));
+
+ return;
+}
+
+gint owner_count = 0;
+gboolean
+check_for_service (gpointer user_data)
+{
+ g_debug("Checking for Watcher");
+
+ if (owner_count > 100) {
+ g_warning("Couldn't find watcher after 100 tries.");
+ g_main_loop_quit(main_loop);
+ return FALSE;
+ }
+
+ owner_count++;
+
+ gboolean has_owner = FALSE;
+ org_freedesktop_DBus_name_has_owner(bus_proxy, NOTIFICATION_WATCHER_DBUS_ADDR, &has_owner, NULL);
+
+ if (has_owner) {
+ const char * cats = NULL;
+ DBusGProxy * proxy = dbus_g_proxy_new_for_name(session_bus,
+ NOTIFICATION_WATCHER_DBUS_ADDR,
+ NOTIFICATION_WATCHER_DBUS_OBJ,
+ NOTIFICATION_WATCHER_DBUS_IFACE);
+
+ g_debug("Registering Approver");
+ org_kde_StatusNotifierWatcher_x_ayatana_register_notification_approver_async (proxy, APPROVER_PATH, &cats, register_cb, NULL);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+fail_timeout (gpointer user_data)
+{
+ g_debug("Failure timeout initiated.");
+ g_main_loop_quit(main_loop);
+ return FALSE;
+}
+
+int
+main (int argc, char ** argv)
+{
+ GError * error = NULL;
+
+ gtk_init(&argc, &argv);
+ g_debug("Initing");
+
+ session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+ if (error != NULL) {
+ g_warning("Unable to get session bus: %s", error->message);
+ g_error_free(error);
+ return -1;
+ }
+
+ TestApprover * approver = g_object_new(TEST_APPROVER_TYPE, NULL);
+
+ bus_proxy = dbus_g_proxy_new_for_name(session_bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
+
+ g_timeout_add(100, check_for_service, NULL);
+ g_timeout_add_seconds(2, fail_timeout, NULL);
+
+ main_loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(main_loop);
+
+ g_object_unref(approver);
+
+ if (!passed) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/tests/test-libappindicator.c b/tests/test-libappindicator.c
index 86879b3..8d12ac5 100644
--- a/tests/test-libappindicator.c
+++ b/tests/test-libappindicator.c
@@ -163,12 +163,145 @@ test_libappindicator_init (void)
}
void
+test_libappindicator_set_label (void)
+{
+ AppIndicator * ci = app_indicator_new ("my-id",
+ "my-name",
+ APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
+
+ g_assert(ci != NULL);
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ /* First check all the clearing modes, this is important as
+ we're going to use them later, we need them to work. */
+ app_indicator_set_label(ci, NULL, NULL);
+
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ app_indicator_set_label(ci, "", NULL);
+
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ app_indicator_set_label(ci, NULL, "");
+
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ app_indicator_set_label(ci, "", "");
+
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ app_indicator_set_label(ci, "label", "");
+
+ g_assert(g_strcmp0(app_indicator_get_label(ci), "label") == 0);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ app_indicator_set_label(ci, NULL, NULL);
+
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ app_indicator_set_label(ci, "label", "guide");
+
+ g_assert(g_strcmp0(app_indicator_get_label(ci), "label") == 0);
+ g_assert(g_strcmp0(app_indicator_get_label_guide(ci), "guide") == 0);
+
+ app_indicator_set_label(ci, "label2", "guide");
+
+ g_assert(g_strcmp0(app_indicator_get_label(ci), "label2") == 0);
+ g_assert(g_strcmp0(app_indicator_get_label_guide(ci), "guide") == 0);
+
+ app_indicator_set_label(ci, "trick-label", "trick-guide");
+
+ g_assert(g_strcmp0(app_indicator_get_label(ci), "trick-label") == 0);
+ g_assert(g_strcmp0(app_indicator_get_label_guide(ci), "trick-guide") == 0);
+
+ g_object_unref(G_OBJECT(ci));
+ return;
+}
+
+void
+label_signals_cb (AppIndicator * appindicator, gchar * label, gchar * guide, gpointer user_data)
+{
+ gint * label_signals_count = (gint *)user_data;
+ (*label_signals_count)++;
+ return;
+}
+
+void
+label_signals_check (void)
+{
+ while (g_main_context_pending(NULL)) {
+ g_main_context_iteration(NULL, TRUE);
+ }
+
+ return;
+}
+
+void
+test_libappindicator_label_signals (void)
+{
+ gint label_signals_count = 0;
+ AppIndicator * ci = app_indicator_new ("my-id",
+ "my-name",
+ APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
+
+ g_assert(ci != NULL);
+ g_assert(app_indicator_get_label(ci) == NULL);
+ g_assert(app_indicator_get_label_guide(ci) == NULL);
+
+ g_signal_connect(G_OBJECT(ci), APP_INDICATOR_SIGNAL_NEW_LABEL, G_CALLBACK(label_signals_cb), &label_signals_count);
+
+ /* Shouldn't be a signal as it should be stuck in idle */
+ app_indicator_set_label(ci, "label", "guide");
+ g_assert(label_signals_count == 0);
+
+ /* Should show up after idle processing */
+ label_signals_check();
+ g_assert(label_signals_count == 1);
+
+ /* Shouldn't signal with no change */
+ label_signals_count = 0;
+ app_indicator_set_label(ci, "label", "guide");
+ label_signals_check();
+ g_assert(label_signals_count == 0);
+
+ /* Change one, we should get one signal */
+ app_indicator_set_label(ci, "label2", "guide");
+ label_signals_check();
+ g_assert(label_signals_count == 1);
+
+ /* Change several times, one signal */
+ label_signals_count = 0;
+ app_indicator_set_label(ci, "label1", "guide0");
+ app_indicator_set_label(ci, "label1", "guide1");
+ app_indicator_set_label(ci, "label2", "guide2");
+ app_indicator_set_label(ci, "label3", "guide3");
+ label_signals_check();
+ g_assert(label_signals_count == 1);
+
+ /* Clear should signal too */
+ label_signals_count = 0;
+ app_indicator_set_label(ci, NULL, NULL);
+ label_signals_check();
+ g_assert(label_signals_count == 1);
+
+ return;
+}
+
+void
test_libappindicator_props_suite (void)
{
g_test_add_func ("/indicator-application/libappindicator/init", test_libappindicator_init);
g_test_add_func ("/indicator-application/libappindicator/init_props", test_libappindicator_init_with_props);
g_test_add_func ("/indicator-application/libappindicator/init_set_props", test_libappindicator_init_set_props);
g_test_add_func ("/indicator-application/libappindicator/prop_signals", test_libappindicator_prop_signals);
+ g_test_add_func ("/indicator-application/libappindicator/set_label", test_libappindicator_set_label);
+ g_test_add_func ("/indicator-application/libappindicator/label_signals", test_libappindicator_label_signals);
return;
}