Merge lp:~larsu/libindicator/add-indicator-ng into lp:libindicator/13.04

Proposed by Lars Karlitski
Status: Merged
Approved by: Charles Kerr
Approved revision: 505
Merged at revision: 475
Proposed branch: lp:~larsu/libindicator/add-indicator-ng
Merge into: lp:libindicator/13.04
Diff against target: 1278 lines (+1078/-15)
17 files modified
Makefile.am.coverage (+3/-1)
configure.ac (+1/-1)
libindicator/Makefile.am (+8/-0)
libindicator/indicator-image-helper.c (+20/-4)
libindicator/indicator-image-helper.h (+5/-3)
libindicator/indicator-ng.c (+566/-0)
libindicator/indicator-ng.h (+48/-0)
libindicator/indicator-object.c (+7/-0)
libindicator/indicator-object.h (+1/-0)
tests/Makefile.am (+42/-1)
tests/com.canonical.indicator.test.service.in (+3/-0)
tests/com.canonical.test.indicator (+4/-0)
tests/com.canonical.test.nosuchservice.indicator (+4/-0)
tests/indicator-test-service.c (+108/-0)
tests/test-indicator-ng.c (+167/-0)
tools/indicator-loader.c (+22/-5)
trim-lcov.py (+69/-0)
To merge this branch: bzr merge lp:~larsu/libindicator/add-indicator-ng
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing
Charles Kerr (community) Approve
Ted Gould (community) Approve
Review via email: mp+143934@code.launchpad.net

Description of the change

Add IndicatorNg.

IndicatorNg is an indicator object that reads an indicator service file and watches the bus for a corresponding service to appear. It turns the menus and actions exported by the service into an indicator entry. I think this is a good solution for the transition period in which we support both styles of indicators. (It means we don't need to copy templates around.)

An indicator service file must have an ".indicator" extension and contents simlilar to this:

  [Indicator Service]
  Name=indicator-test
  BusName=com.canonical.indicator.test
  ObjectPath=/com/canonical/indicator/test

For unity-panel-service, these files will be installed somewhere. The indicator-loader in this branch accepts a path to such a file as the first command line argument (instead of the .so file).

This can be tested with the example indicator in lp:~larsu/libunity/add-indicator (examples/indicator.vala).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
478. By Lars Karlitski

indicator-ng.h: use local include

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
479. By Lars Karlitski

indicator-ng: always set an accessible description to avoid imminent warning

480. By Lars Karlitski

indicator-ng: fix crash (tried to free a string with g_object_unref)

481. By Lars Karlitski

indicator-ng: add indicator_ng_new_for_profile

482. By Lars Karlitski

indicator-ng: add getters

483. By Lars Karlitski

Make sure indicator-ng.h is installed

484. By Lars Karlitski

Add basic tests for indicator-ng

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
485. By Lars Karlitski

indicator-ng: properly unset action group when the service disappears

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
486. By Lars Karlitski

indicator-ng: auto start service if it's not running

487. By Lars Karlitski

indicator-ng: use base->get_entries to get the invisible ones, too

488. By Lars Karlitski

indicator-ng: set name hint to the value of the service file's "Name" field

489. By Lars Karlitski

indicator-ng: more elaborate testing

Use GTestDBus to spawn a small test service (tests/indicator-test-service.c)
and check whether the indicator menu gets turned into a gtkmenu correctly.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Charles Kerr (charlesk) wrote :

1. You're overriding get_label, get_image, get_entry, get_name, get_accessible... and also have that indicator_ng_get_entry() func to work around visible/invisible entries in the parent. It would be more straightforward to remove all of that, replace the label/image/gtkmenu/accessible_desc fields in _IndicatorNg with "IndicatorObjectEntry entry;" and override get_entries() as a one-liner "return g_list_append (NULL, &self->entry);"

2. Bonus points if you'd add "void indicator_object_entry_set_accessible_desc (IndicatorObject * io, IndicatorObjectEntry * entry, const char * accessible_desc);" in indicator-object.h, similar to the existing i_o_e_activate() func, and use that instead of indicator_ng_set_accessible_desc()

3. I'm wondering if we should use a 'broken' image as a fallback when setting the image. That would prevent the "disappearing indicator" problem from regressing, such as we had with indicator-session when the cog icon wasn't in the theme. A broken icon isn't ideal, but it's better than no icon at all. Same for the gtk_widget_hide() call in indicator_ng_update_entry().

4. is there a reason to not use (&s&s&sb) in indicator_ng_update_entry's g_variant_get() call?

5. "added < 2 && removed < 2 && added ^ removed" deserves a comment in the source code. I /think/ you're XORing to confirm that added and removed aren't the same. Whether or not that's the case, it deserves a code comment.

6. It might be slightly cleaner to use g_object_connect() in indicator_ng_service_appeared(), and g_object_disconnect(). This isn't an important point.

7. In indicator_ng_menu_changed(), we may use "action" as an uninitialized pointer if g_menu_model_get_item_attribute() fails. This seems unlikely to me, but it would be better to check for failure and log a g_warning() instead of using an uninitialized pointer.

8. g_build_filename() instead of g_strconcat()?

Revision history for this message
Charles Kerr (charlesk) wrote :

NVM about g_build_filename(); that doesn't make sense for object paths.

490. By Lars Karlitski

indicator-ng: test indicator_ng_get_property

491. By Lars Karlitski

Add trim-lcov.py

It strips some unreachable branches from the coverage reports, such as
g_return_if_fail.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
492. By Lars Karlitski

indicator-ng: use an IndicatorObjectEntry internally

We needed the entry anyway (that's what indicator_ng_get_entry was for).

493. By Lars Karlitski

indicator-ng: show broken image when g_icon_for_string returns NULL

494. By Lars Karlitski

indicator-ng: save unnecessary allocations by using "&" in g_variant_get

495. By Lars Karlitski

indicator-ng: document error conditions in menu_changed

496. By Lars Karlitski

indicator-ng: check return value of g_menu_model_get_item_attribute

If it returns false, we'd use uninitialized memory.

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (3.2 KiB)

> 1. You're overriding get_label, get_image, get_entry, get_name,
> get_accessible... and also have that indicator_ng_get_entry() func to work
> around visible/invisible entries in the parent. It would be more
> straightforward to remove all of that, replace the
> label/image/gtkmenu/accessible_desc fields in _IndicatorNg with
> "IndicatorObjectEntry entry;" and override get_entries() as a one-liner
> "return g_list_append (NULL, &self->entry);"

Very good point, thank you.

 1 file changed, 29 insertions(+), 86 deletions(-)

r492.

> 2. Bonus points if you'd add "void indicator_object_entry_set_accessible_desc
> (IndicatorObject * io, IndicatorObjectEntry * entry, const char *
> accessible_desc);" in indicator-object.h, similar to the existing
> i_o_e_activate() func, and use that instead of
> indicator_ng_set_accessible_desc()

Makes sense, but I'm not sure this works without potentially breaking some code, because accessible_desc is declared as const. That's the reason I still kept an accessible_desc in the IndicatorNg struct.

Or am I missing something?

> 3. I'm wondering if we should use a 'broken' image as a fallback when setting
> the image. That would prevent the "disappearing indicator" problem from
> regressing, such as we had with indicator-session when the cog icon wasn't in
> the theme. A broken icon isn't ideal, but it's better than no icon at all.
> Same for the gtk_widget_hide() call in indicator_ng_update_entry().

I totally agree and this is the way I intended it to work. The icon string we get falls into one of these cases:

 (1) an empty string
 (2) a valid icon name, for which an icon is available on the system
 (3) a valid icon name without an installed icon
 (4) a malformed icon string (i.e. g_icon_new_for_string() returns NULL)

For (1), no icon should be displayed. (2) obviously shows the installed icon. Both (3) and (4) should display the broken image. I already did this for (3) (set_from_gicon_string() returns TRUE, but GtkImage sets a broken icon), but I missed case (4).

Fixed in r493.

> 4. is there a reason to not use (&s&s&sb) in indicator_ng_update_entry's
> g_variant_get() call?

No, fixed in r494.

> 5. "added < 2 && removed < 2 && added ^ removed" deserves a comment in the
> source code. I /think/ you're XORing to confirm that added and removed aren't
> the same. Whether or not that's the case, it deserves a code comment.

Very true. r495.

> 6. It might be slightly cleaner to use g_object_connect() in
> indicator_ng_service_appeared(), and g_object_disconnect(). This isn't an
> important point.

Yeah, I dislike that call when dealing with swapped arguments.

> 7. In indicator_ng_menu_changed(), we may use "action" as an uninitialized
> pointer if g_menu_model_get_item_attribute() fails. This seems unlikely to me,
> but it would be better to check for failure and log a g_warning() instead of
> using an uninitialized pointer.

Good catch! I don't think it makes sense to throw a warning here. Even if the menu item has an "action" attribute, the value might not point to a valid action.

Fixed in r496.

> 8. g_build_filename() instead of g_strconcat()?

That's an object path, not a filename.

Thank...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :
Download full text (3.6 KiB)

* When the service vanishes the program is immediately hiding the indicator and destroying everything. It doesn't make much sense to show this error to the user unless it can't be restarted. It should try to restart, and if either a timeout happens or an error on restart happens it hides the indicator. Much better user experience if we can handle the error in a reasonable way.

* I don't understand that, if we only have one action group, the action names need the "indicator" prefix. It seems they could just be unprefixed and make it easier on everyone.

* In indicator_ng_initable_init() you should really check for the group before looking at individual values in the group. It makes the errors much better for people trying to figure out WTH is going on when they mess up the file.

* In indicator_ng_initable_init() it seems like name and the bus name check should be separate, they're pretty independent and again it'll help error reporting.

* It looks like the name that is gotten from the Key file is not going through translations, which it needs to as it is a user visible string. This also means that the indicator file will need to give the translation domain.

* I don't understand why the profile is a string and not an enum. It seems like there'll always be a limited set, and they can't overlap, so enum is the right type to use. Seems like this should be passed up to the "environments" of indicator-object.

* We probably need a way for a .indicator file to support multiple profiles in the same file. It doesn't really make sense to have a whole set of files since all of them will have to be loaded for the convergence story.

* Having a single action that has a state (sssb) doesn't make a lot of sense as it makes it nearly impossible for us to remove or add attributes later. Better to split it into 4 today so that it can be 5 (or 3) tomorrow.

* The indicator needs a way to provide a string that can be used for measuring the space needed. We provide that in the libappindicator API and we should honor it further up the stack.

* If I remember right gtk_image_set_from_gicon() only supports icons that are square. Not all of our icons are square. Icons like the power icon are rectangular.

* You shouldn't use math on string pointers. This is bad "g_strdup (action + 10)". The problem is that if someone changes "indicator." to "bob." they'll grep through the code and never see that. Then they'll introduce a bug. If you do strlen() on a constant string the compiler can optimize that, and you still can explain the meaning of what you're intention is.

* Overall there are *very* few comments in the code.

* In the new interfacing there is no way to check the version of the service. The scenario would be that an upgrade happens, and the service no longer matches the version that the panel-service thinks it's looking for. It needs to handle that error instead of displaying erroneous results or an empty menu. I'd recommend adding this to the indicator file format and as an attribute on the base menu.

* It seems like the about-to-show needs to be passed down to the indicator somehow. This isn't currently handled and it's required for ...

Read more...

review: Needs Fixing
497. By Lars Karlitski

indicator-ng: require header item to have x-canonical-type set

498. By Lars Karlitski

indicator-ng: lazily allocate entry.label and entry.image

Most indicators only need one of those.

499. By Lars Karlitski

indicator-ng: use indicator_image_helper

gtk_icon_set_from_gicon doesn't scale rectangular icons correctly.

This adds indicator_image_helper_update_from_gicon() and makes refresh_image()
set a broken image instead of erroring out when an icon couldn't be found.

500. By Lars Karlitski

indicator-ng: use strlen instead of hard coding the length

501. By Lars Karlitski

trim-lcov.py: add license header

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (6.5 KiB)

Thanks a lot for the detailed review!

> * When the service vanishes the program is immediately hiding the indicator
> and destroying everything. It doesn't make much sense to show this error to
> the user unless it can't be restarted. It should try to restart, and if
> either a timeout happens or an error on restart happens it hides the
> indicator. Much better user experience if we can handle the error in a
> reasonable way.

Right, this isn't true for all indicators though. How about a "auto-reload" property that is set for all built-in indicators (or even settable from the .indicator file)?

> * I don't understand that, if we only have one action group, the action names
> need the "indicator" prefix. It seems they could just be unprefixed and make
> it easier on everyone.

I want to enable applications to reuse their app. actions in an indicator. Without this distinction, apps would have to add the same actions twice.

> * In indicator_ng_initable_init() you should really check for the group before
> looking at individual values in the group. It makes the errors much better
> for people trying to figure out WTH is going on when they mess up the file.

Good point. I started to implement it when I realised that g_key_file_get_string() already returns an error when the group doesn't exist:

  could not load indicator from <filename>: Key file does not have group 'Indicator Service'

I don't know what else a special check could add.

> * In indicator_ng_initable_init() it seems like name and the bus name check
> should be separate, they're pretty independent and again it'll help error
> reporting.

On second look, this is indeed a bit convoluted. But I'm not sure what you mean: move the g_bus_watch_name call out of the condition, or out of the function? It doesn't have any error conditions (I admit the "return self->name_watch_id > 0" is a bit misleading).

> * It looks like the name that is gotten from the Key file is not going through
> translations, which it needs to as it is a user visible string. This also
> means that the indicator file will need to give the translation domain.

The name from the key file the name-hint really, which isn't user-visible, is it? Would it be clearer if we named the key "NameHint" instead?

> * I don't understand why the profile is a string and not an enum. It seems
> like there'll always be a limited set, and they can't overlap, so enum is the
> right type to use. Seems like this should be passed up to the "environments"
> of indicator-object.

Oh! I meant to look into "environments", but forgot. Maybe we don't even need a separate "profile" property at all.

How exactly do these work? Does unity-panrl-service already create an indicator-object for each "environment" (greeter, desktop)?

> * We probably need a way for a .indicator file to support multiple profiles in
> the same file. It doesn't really make sense to have a whole set of files
> since all of them will have to be loaded for the convergence story.

That's already the way it works: each indicator-service can expose as many profiles as it wants to on <objectpath>/<profile>, where <objectpath> is from the .indicator file.

> ...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ted Gould (ted) wrote :
Download full text (7.2 KiB)

On Fri, 2013-01-25 at 10:58 +0000, Lars Uebernickel wrote:
> > * When the service vanishes the program is immediately hiding the indicator
> > and destroying everything. It doesn't make much sense to show this error to
> > the user unless it can't be restarted. It should try to restart, and if
> > either a timeout happens or an error on restart happens it hides the
> > indicator. Much better user experience if we can handle the error in a
> > reasonable way.
>
> Right, this isn't true for all indicators though. How about a
> "auto-reload" property that is set for all built-in indicators (or
> even settable from the .indicator file)?

Uhm, which ones don't? That would be bad if there were some that don't.
Remember that we only want to use this for system indicators, not
applications. Because we need to wait to display the panel until all of
the services that have .indicator files are started and giving us
default state. We can't assume that for appindicators.

> > * I don't understand that, if we only have one action group, the action names
> > need the "indicator" prefix. It seems they could just be unprefixed and make
> > it easier on everyone.
>
> I want to enable applications to reuse their app. actions in an
> indicator. Without this distinction, apps would have to add the same
> actions twice.

Are you thinking appindicators here? I don't think we can drop KSNI
there, so we won't be able to use this for that. Otherwise all
indicators don't have any application actions.

Really application actions will probably only be used long term to
support GNOME applications on the desktop. They've shown no desire to
work with us (and KDE) on indicators in the past, I doubt they will in
the future. So never the two shall meet :-)

> > * In indicator_ng_initable_init() you should really check for the group before
> > looking at individual values in the group. It makes the errors much better
> > for people trying to figure out WTH is going on when they mess up the file.
>
> Good point. I started to implement it when I realised that
> g_key_file_get_string() already returns an error when the group
> doesn't exist:
>
> could not load indicator from <filename>: Key file does not have group 'Indicator Service'
>
> I don't know what else a special check could add.

Ah, okay. That should be fine then. Didn't realize it gave that error.

> > * In indicator_ng_initable_init() it seems like name and the bus name check
> > should be separate, they're pretty independent and again it'll help error
> > reporting.
>
> On second look, this is indeed a bit convoluted. But I'm not sure
> what you mean: move the g_bus_watch_name call out of the condition, or
> out of the function? It doesn't have any error conditions (I admit
> the "return self->name_watch_id > 0" is a bit misleading).

I was more thinking of breaking up this if statement into to:

515 + if ((self->name = g_key_file_get_string (keyfile, "Indicator
Service", "Name", error)) &&
516 + (bus_name = g_key_file_get_string (keyfile, "Indicator Service",
"BusName", error)) &&
517 + (self->object_path = g_key_file_get_string (keyfile, "Indicator
Service", "ObjectPath", error)))

It seems ...

Read more...

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (5.3 KiB)

> > Right, this isn't true for all indicators though. How about a
> > "auto-reload" property that is set for all built-in indicators (or
> > even settable from the .indicator file)?
>
> Uhm, which ones don't? That would be bad if there were some that don't.
> Remember that we only want to use this for system indicators, not
> applications. Because we need to wait to display the panel until all of
> the services that have .indicator files are started and giving us
> default state. We can't assume that for appindicators.

Matthew is thinking about having the sync indicator only show up when it is syncing. The (now deprecated) print indicator only shows when printing. Similar for keyboard / bluetooth, alhough they would probably only hide their indicators instead of terminating the service.

I definitely want to use the same mechanism for appindicators.

> > I want to enable applications to reuse their app. actions in an
> > indicator. Without this distinction, apps would have to add the same
> > actions twice.
>
> Are you thinking appindicators here? I don't think we can drop KSNI
> there, so we won't be able to use this for that. Otherwise all
> indicators don't have any application actions.

An application having an indicator is pretty common. I think it would be much easier for them if the could just hook up existing actions to the indicator menu, in the same way that they hook up those actions to the application menu.

I'm not saying we should drop support for KSNI. Though I think that spec needs to be updated (it depends on dbusmenu, doesn't it?)

> Really application actions will probably only be used long term to
> support GNOME applications on the desktop. They've shown no desire to
> work with us (and KDE) on indicators in the past, I doubt they will in
> the future. So never the two shall meet :-)

Hm? Why would a $TOOLKIT application not have application actions? I thought application menus were part of our platform?

What do you mean by GNOME applications? Apps using GNOME tech? Lots of those use indicators and go out of their way to support the Unity desktop (tomboy, transmission, shutter, sparkleshare). Also note that even though the indicator concept wasn't picked up by GNOME's default applications, there are features in GTK+ and GMenuModel that are *only* used in Unity's indicators.

I'm really just trying to make it as easy as possible for application developers to add an indicator to their app and hook it up to existing actions (see also my libunity merge request).

> [...]
> > On second look, this is indeed a bit convoluted. But I'm not sure
> > what you mean: move the g_bus_watch_name call out of the condition, or
> > out of the function? It doesn't have any error conditions (I admit
> > the "return self->name_watch_id > 0" is a bit misleading).
>
> I was more thinking of breaking up this if statement into to:
>
> 515 + if ((self->name = g_key_file_get_string (keyfile, "Indicator
> Service", "Name", error)) &&
> 516 + (bus_name = g_key_file_get_string (keyfile, "Indicator Service",
> "BusName", error)) &&
> 517 + (self->object_path = g_key_file_get_string (keyfile, "Indicator
> Service",...

Read more...

Revision history for this message
Ted Gould (ted) wrote :
Download full text (6.0 KiB)

On Mon, 2013-01-28 at 10:27 +0000, Lars Uebernickel wrote:
> > > Right, this isn't true for all indicators though. How about a
> > > "auto-reload" property that is set for all built-in indicators (or
> > > even settable from the .indicator file)?
> >
> > Uhm, which ones don't? That would be bad if there were some that don't.
> > Remember that we only want to use this for system indicators, not
> > applications. Because we need to wait to display the panel until all of
> > the services that have .indicator files are started and giving us
> > default state. We can't assume that for appindicators.
>
> Matthew is thinking about having the sync indicator only show up when
> it is syncing. The (now deprecated) print indicator only shows when
> printing. Similar for keyboard � bluetooth, alhough they would
> probably only hide their indicators instead of terminating the
> service.

Sure. Though, it would be nice if they could terminate the service...
it seems like that's largely a special case rather than the standard
though. Seems like default should be auto-reload.

> I definitely want to use the same mechanism for appindicators.

Hmm, so then we'd need another property for which ones should be waited
on for initial bring up. And perhaps a way to mark KSNI ones as
there'll be different properties. I think that we're a ways away from
that.

But, in general, I'm not sure if using the same mechanism makes sense.
This can be a lot simpler and cleaner if it doesn't have to take on all
of the KSNI interface as well. Remember it is a registration interface,
so we wouldn't be able to really *replace* it, it'd have to be
additional and we'd have to start the service, then wait for the
registration.

> > > I want to enable applications to reuse their app. actions in an
> > > indicator. Without this distinction, apps would have to add the same
> > > actions twice.
> >
> > Are you thinking appindicators here? I don't think we can drop KSNI
> > there, so we won't be able to use this for that. Otherwise all
> > indicators don't have any application actions.
>
> An application having an indicator is pretty common. I think it would
> be much easier for them if the could just hook up existing actions to
> the indicator menu, in the same way that they hook up those actions to
> the application menu.

I'd challenge the initial assumption. I can think of very few
applications that have indicators, and I'd go further to say that most
that do are annoying :-)

I have no issue with them hooking up actions to the indicator. But it
would probably make more sense to allow registering action groups and
prefixes in general. As most applications don't have app�win actions,
but might have their own scheme for such a separation. For instance, I
think for Inkscape it'd make much more sense to have app�doc�win.
Unfortunately GtkApplication isn't very flexible, we shouldn't make the
same mistake.

> I'm not saying we should drop support for KSNI. Though I think that
> spec needs to be updated (it depends on dbusmenu, doesn't it?)

It just provides a path for a menu. I think if we ported qt-sni and
libappindicator there'd be no issue with those being GMenu ba...

Read more...

Revision history for this message
Lars Karlitski (larsu) wrote :
Download full text (7.3 KiB)

> On Mon, 2013-01-28 at 10:27 +0000, Lars Uebernickel wrote:
> > > > Right, this isn't true for all indicators though. How about a
> > > > "auto-reload" property that is set for all built-in indicators (or
> > > > even settable from the .indicator file)?
> > >
> > > Uhm, which ones don't? That would be bad if there were some that don't.
> > > Remember that we only want to use this for system indicators, not
> > > applications. Because we need to wait to display the panel until all of
> > > the services that have .indicator files are started and giving us
> > > default state. We can't assume that for appindicators.
> >
> > Matthew is thinking about having the sync indicator only show up when
> > it is syncing. The (now deprecated) print indicator only shows when
> > printing. Similar for keyboard � bluetooth, alhough they would
> > probably only hide their indicators instead of terminating the
> > service.
>
> Sure. Though, it would be nice if they could terminate the service...
> it seems like that's largely a special case rather than the standard
> though. Seems like default should be auto-reload.

The default for system indicator should be auto-reload, I agree. I still think having a key in the .indicator file is a good idea.

> > I definitely want to use the same mechanism for appindicators.
>
> Hmm, so then we'd need another property for which ones should be waited
> on for initial bring up. And perhaps a way to mark KSNI ones as
> there'll be different properties. I think that we're a ways away from
> that.
>
> But, in general, I'm not sure if using the same mechanism makes sense.
> This can be a lot simpler and cleaner if it doesn't have to take on all
> of the KSNI interface as well. Remember it is a registration interface,
> so we wouldn't be able to really *replace* it, it'd have to be
> additional and we'd have to start the service, then wait for the
> registration.

Let's talk about this in more detail later. Or is it important for this MR?

> > > > I want to enable applications to reuse their app. actions in an
> > > > indicator. Without this distinction, apps would have to add the same
> > > > actions twice.
> > >
> > > Are you thinking appindicators here? I don't think we can drop KSNI
> > > there, so we won't be able to use this for that. Otherwise all
> > > indicators don't have any application actions.
> >
> > An application having an indicator is pretty common. I think it would
> > be much easier for them if the could just hook up existing actions to
> > the indicator menu, in the same way that they hook up those actions to
> > the application menu.
>
> I'd challenge the initial assumption. I can think of very few
> applications that have indicators, and I'd go further to say that most
> that do are annoying :-)

Really? Even if there are only a few, doesn't it make sense to allow them to use app. actions? I don't think sticking indicator. in front is such a big deal really.

> I have no issue with them hooking up actions to the indicator. But it
> would probably make more sense to allow registering action groups and
> prefixes in general. As most applications don't have app�win actions,
> but might...

Read more...

502. By Lars Karlitski

indicator-ng: simplify flow in initable_init

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
503. By Lars Karlitski

indicator-ng: try to restart the service when it crashes

This uses a (slightly) awkward heuristic: when the well-known name vanishes
from the session bus, it only restarts the service when it didn't explicitly
hide the indicator before.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
504. By Lars Karlitski

indicator-ng: don't hide the indicator if the service is already running

Revision history for this message
Ted Gould (ted) :
review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
505. By Lars Karlitski

indicator-ng: add license header

Revision history for this message
Charles Kerr (charlesk) wrote :

Thanks for that update :)

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile.am.coverage'
2--- Makefile.am.coverage 2012-03-27 21:33:31 +0000
3+++ Makefile.am.coverage 2013-02-14 21:19:21 +0000
4@@ -1,6 +1,8 @@
5
6 # Coverage targets
7
8+EXTRA_DIST = trim-lcov.py
9+
10 .PHONY: clean-gcno clean-gcda \
11 coverage-html generate-coverage-html clean-coverage-html \
12 coverage-gcovr generate-coverage-gcovr clean-coverage-gcovr
13@@ -23,7 +25,7 @@
14
15 generate-coverage-html:
16 @echo Collecting coverage data
17- $(LCOV) --directory $(top_builddir) --capture --output-file coverage.info --no-checksum --compat-libtool
18+ $(LCOV) --directory $(top_builddir) --capture --no-checksum --compat-libtool | $(top_srcdir)/trim-lcov.py > coverage.info
19 LANG=C $(GENHTML) --prefix $(top_builddir) --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
20
21 clean-coverage-html: clean-gcda
22
23=== modified file 'configure.ac'
24--- configure.ac 2012-11-21 19:21:10 +0000
25+++ configure.ac 2013-02-14 21:19:21 +0000
26@@ -43,7 +43,7 @@
27 ##############################
28
29 GTK_REQUIRED_VERSION=2.18
30-GTK3_REQUIRED_VERSION=2.91
31+GTK3_REQUIRED_VERSION=3.6
32 GIO_UNIX_REQUIRED_VERSION=2.22
33
34 AC_ARG_WITH([gtk],
35
36=== modified file 'libindicator/Makefile.am'
37--- libindicator/Makefile.am 2012-01-24 16:10:15 +0000
38+++ libindicator/Makefile.am 2013-02-14 21:19:21 +0000
39@@ -36,6 +36,10 @@
40 indicator-service.h \
41 indicator-service-manager.h
42
43+if USE_GTK3
44+indicator_headers += indicator-ng.h
45+endif
46+
47 libindicatorinclude_HEADERS = \
48 $(indicator_headers)
49
50@@ -53,6 +57,10 @@
51 indicator-service.c \
52 indicator-service-manager.c
53
54+if USE_GTK3
55+libindicator_la_SOURCES += indicator-ng.c
56+endif
57+
58 libindicator_la_CFLAGS = \
59 $(LIBINDICATOR_CFLAGS) \
60 $(COVERAGE_CFLAGS) \
61
62=== modified file 'libindicator/indicator-image-helper.c'
63--- libindicator/indicator-image-helper.c 2012-04-11 06:01:51 +0000
64+++ libindicator/indicator-image-helper.c 2013-02-14 21:19:21 +0000
65@@ -62,7 +62,12 @@
66 /* Grab the filename */
67 icon_filename = gtk_icon_info_get_filename(icon_info);
68 }
69- g_return_if_fail(icon_filename != NULL); /* An error because we don't have a filename */
70+
71+ /* show a broken image if we don't have a filename */
72+ if (icon_filename == NULL) {
73+ gtk_image_set_from_stock (image, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_LARGE_TOOLBAR);
74+ return;
75+ }
76
77 /* Build a pixbuf */
78 GError * error = NULL;
79@@ -132,7 +137,8 @@
80 /* Build us an image */
81 GtkImage * image = GTK_IMAGE(gtk_image_new());
82
83- indicator_image_helper_update(image, name);
84+ if (name)
85+ indicator_image_helper_update(image, name);
86
87 return image;
88 }
89@@ -144,17 +150,27 @@
90 g_return_if_fail(name != NULL);
91 g_return_if_fail(name[0] != '\0');
92 g_return_if_fail(GTK_IS_IMAGE(image));
93- gboolean seen_previously = FALSE;
94
95 /* Build us a GIcon */
96 GIcon * icon_names = g_themed_icon_new_with_default_fallbacks(name);
97 g_warn_if_fail(icon_names != NULL);
98 g_return_if_fail(icon_names != NULL);
99
100+ indicator_image_helper_update_from_gicon (image, icon_names);
101+
102+ g_object_unref (icon_names);
103+ return;
104+}
105+
106+void
107+indicator_image_helper_update_from_gicon (GtkImage *image, GIcon *icon)
108+{
109+ gboolean seen_previously = FALSE;
110+
111 seen_previously = (g_object_get_data(G_OBJECT(image), INDICATOR_NAMES_DATA) != NULL);
112
113 /* Attach our names to the image */
114- g_object_set_data_full(G_OBJECT(image), INDICATOR_NAMES_DATA, icon_names, g_object_unref);
115+ g_object_set_data_full(G_OBJECT(image), INDICATOR_NAMES_DATA, g_object_ref (icon), g_object_unref);
116
117 /* Put the pixbuf in */
118 refresh_image(image);
119
120=== modified file 'libindicator/indicator-image-helper.h'
121--- libindicator/indicator-image-helper.h 2010-03-11 16:22:17 +0000
122+++ libindicator/indicator-image-helper.h 2013-02-14 21:19:21 +0000
123@@ -26,8 +26,10 @@
124
125 #include <gtk/gtk.h>
126
127-GtkImage * indicator_image_helper (const gchar * name);
128-void indicator_image_helper_update (GtkImage * image,
129- const gchar * name);
130+GtkImage * indicator_image_helper (const gchar * name);
131+void indicator_image_helper_update (GtkImage * image,
132+ const gchar * name);
133+void indicator_image_helper_update_from_gicon (GtkImage * image,
134+ GIcon * icon);
135
136 #endif /* __INDICATOR_IMAGE_HELPER_H__ */
137
138=== added file 'libindicator/indicator-ng.c'
139--- libindicator/indicator-ng.c 1970-01-01 00:00:00 +0000
140+++ libindicator/indicator-ng.c 2013-02-14 21:19:21 +0000
141@@ -0,0 +1,566 @@
142+/*
143+ * Copyright 2013 Canonical Ltd.
144+ *
145+ * This program is free software: you can redistribute it and/or modify it
146+ * under the terms of the GNU General Public License version 3, as published
147+ * by the Free Software Foundation.
148+ *
149+ * This program is distributed in the hope that it will be useful, but
150+ * WITHOUT ANY WARRANTY; without even the implied warranties of
151+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
152+ * PURPOSE. See the GNU General Public License for more details.
153+ *
154+ * You should have received a copy of the GNU General Public License along
155+ * with this program. If not, see <http://www.gnu.org/licenses/>.
156+ *
157+ * Authors:
158+ * Lars Uebernickel <lars.uebernickel@canonical.com>
159+ */
160+
161+#include "indicator-ng.h"
162+#include "indicator-image-helper.h"
163+
164+#include <string.h>
165+
166+struct _IndicatorNg
167+{
168+ IndicatorObject parent;
169+
170+ gchar *service_file;
171+ gchar *name;
172+ gchar *object_path;
173+ gchar *bus_name;
174+ gchar *profile;
175+ gchar *header_action;
176+
177+ guint name_watch_id;
178+
179+ GDBusConnection *session_bus;
180+ GActionGroup *actions;
181+ GMenuModel *menu;
182+
183+ IndicatorObjectEntry entry;
184+ gchar *accessible_desc;
185+
186+ gint64 last_service_restart;
187+};
188+
189+static void indicator_ng_initable_iface_init (GInitableIface *initable);
190+G_DEFINE_TYPE_WITH_CODE (IndicatorNg, indicator_ng, INDICATOR_OBJECT_TYPE,
191+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, indicator_ng_initable_iface_init))
192+
193+enum
194+{
195+ PROP_0,
196+ PROP_SERVICE_FILE,
197+ PROP_PROFILE,
198+ N_PROPERTIES
199+};
200+
201+static GParamSpec *properties[N_PROPERTIES];
202+
203+static void
204+indicator_ng_get_property (GObject *object,
205+ guint property_id,
206+ GValue *value,
207+ GParamSpec *pspec)
208+{
209+ IndicatorNg *self = INDICATOR_NG (object);
210+
211+ switch (property_id)
212+ {
213+ case PROP_SERVICE_FILE:
214+ g_value_set_string (value, self->service_file);
215+ break;
216+
217+ case PROP_PROFILE:
218+ g_value_set_string (value, self->profile);
219+ break;
220+
221+ default:
222+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
223+ }
224+}
225+
226+static void
227+indicator_ng_set_property (GObject *object,
228+ guint property_id,
229+ const GValue *value,
230+ GParamSpec *pspec)
231+{
232+ IndicatorNg *self = INDICATOR_NG (object);
233+
234+ switch (property_id)
235+ {
236+ case PROP_SERVICE_FILE: /* construct-only */
237+ self->service_file = g_strdup (g_value_get_string (value));
238+ break;
239+
240+ case PROP_PROFILE: /* construct-only */
241+ self->profile = g_strdup (g_value_get_string (value));
242+ break;
243+
244+ default:
245+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
246+ }
247+}
248+
249+static void
250+indicator_ng_free_actions_and_menu (IndicatorNg *self)
251+{
252+ if (self->actions)
253+ {
254+ gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", NULL);
255+ g_signal_handlers_disconnect_by_data (self->actions, self);
256+ g_clear_object (&self->actions);
257+ }
258+
259+ if (self->menu)
260+ {
261+ g_signal_handlers_disconnect_by_data (self->menu, self);
262+ g_clear_object (&self->menu);
263+ }
264+}
265+
266+static void
267+indicator_ng_dispose (GObject *object)
268+{
269+ IndicatorNg *self = INDICATOR_NG (object);
270+
271+ if (self->name_watch_id)
272+ {
273+ g_bus_unwatch_name (self->name_watch_id);
274+ self->name_watch_id = 0;
275+ }
276+
277+ g_clear_object (&self->session_bus);
278+
279+ indicator_ng_free_actions_and_menu (self);
280+
281+ g_clear_object (&self->entry.label);
282+ g_clear_object (&self->entry.image);
283+ g_clear_object (&self->entry.menu);
284+
285+ G_OBJECT_CLASS (indicator_ng_parent_class)->dispose (object);
286+}
287+
288+static void
289+indicator_ng_finalize (GObject *object)
290+{
291+ IndicatorNg *self = INDICATOR_NG (object);
292+
293+ g_free (self->service_file);
294+ g_free (self->name);
295+ g_free (self->object_path);
296+ g_free (self->bus_name);
297+ g_free (self->accessible_desc);
298+ g_free (self->header_action);
299+
300+ G_OBJECT_CLASS (indicator_ng_parent_class)->finalize (object);
301+}
302+
303+static GList *
304+indicator_ng_get_entries (IndicatorObject *io)
305+{
306+ IndicatorNg *self = INDICATOR_NG (io);
307+
308+ return g_list_append (NULL, &self->entry);
309+}
310+
311+static void
312+indicator_ng_set_accessible_desc (IndicatorNg *self,
313+ const gchar *accessible_desc)
314+{
315+ g_free (self->accessible_desc);
316+ self->accessible_desc = g_strdup (accessible_desc);
317+
318+ self->entry.accessible_desc = self->accessible_desc;
319+ g_signal_emit_by_name (self, INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, &self->entry);
320+}
321+
322+static void
323+indicator_ng_set_icon_from_string (IndicatorNg *self,
324+ const gchar *str)
325+{
326+ GIcon *icon;
327+ GError *error = NULL;
328+
329+ if (str == NULL || *str == '\0')
330+ {
331+ if (self->entry.image)
332+ {
333+ gtk_image_clear (self->entry.image);
334+ gtk_widget_hide (GTK_WIDGET (self->entry.image));
335+ }
336+ return;
337+ }
338+
339+ if (!self->entry.image)
340+ self->entry.image = g_object_ref_sink (gtk_image_new ());
341+
342+ gtk_widget_show (GTK_WIDGET (self->entry.image));
343+
344+ icon = g_icon_new_for_string (str, &error);
345+ if (icon)
346+ {
347+ indicator_image_helper_update_from_gicon (self->entry.image, icon);
348+ g_object_unref (icon);
349+ }
350+ else
351+ {
352+ g_warning ("invalid icon string '%s': %s", str, error->message);
353+ gtk_image_set_from_stock (self->entry.image, GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_LARGE_TOOLBAR);
354+ g_error_free (error);
355+ }
356+}
357+
358+static void
359+indicator_ng_set_label (IndicatorNg *self,
360+ const gchar *label)
361+{
362+ if (label == NULL || *label == '\0')
363+ {
364+ if (self->entry.label)
365+ gtk_widget_hide (GTK_WIDGET (self->entry.label));
366+ return;
367+ }
368+
369+ if (!self->entry.label)
370+ self->entry.label = g_object_ref_sink (gtk_label_new (NULL));
371+
372+ gtk_label_set_label (GTK_LABEL (self->entry.label), label);
373+ gtk_widget_show (GTK_WIDGET (self->entry.label));
374+}
375+
376+static void
377+indicator_ng_update_entry (IndicatorNg *self)
378+{
379+ GVariant *state;
380+
381+ g_return_if_fail (self->menu != NULL);
382+ g_return_if_fail (self->actions != NULL);
383+
384+ if (!self->header_action ||
385+ !g_action_group_has_action (self->actions, self->header_action))
386+ {
387+ indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
388+ return;
389+ }
390+
391+ state = g_action_group_get_action_state (self->actions, self->header_action);
392+ if (state && g_variant_is_of_type (state, G_VARIANT_TYPE ("(sssb)")))
393+ {
394+ const gchar *label;
395+ const gchar *iconstr;
396+ const gchar *accessible_desc;
397+ gboolean visible;
398+
399+ g_variant_get (state, "(&s&s&sb)", &label, &iconstr, &accessible_desc, &visible);
400+
401+ indicator_ng_set_label (self, label);
402+ indicator_ng_set_icon_from_string (self, iconstr);
403+ indicator_ng_set_accessible_desc (self, accessible_desc);
404+ indicator_object_set_visible (INDICATOR_OBJECT (self), visible);
405+ }
406+ else
407+ g_warning ("the action of the indicator menu item must have state with type (sssb)");
408+
409+ if (state)
410+ g_variant_unref (state);
411+}
412+
413+static gboolean
414+indicator_ng_menu_item_is_of_type (GMenuModel *menu,
415+ gint index,
416+ const gchar *expected_type)
417+{
418+ gchar *type;
419+ gboolean has_type = FALSE;
420+
421+ if (g_menu_model_get_item_attribute (menu, index, "x-canonical-type", "s", &type))
422+ {
423+ has_type = g_str_equal (type, expected_type);
424+ g_free (type);
425+ }
426+
427+ return has_type;
428+}
429+
430+static void
431+indicator_ng_menu_changed (GMenuModel *menu,
432+ gint position,
433+ gint removed,
434+ gint added,
435+ gpointer user_data)
436+{
437+ IndicatorNg *self = user_data;
438+
439+ /* The menu may only contain one item (the indicator title menu).
440+ * Thus, the position is always 0, and there is either exactly one
441+ * item added or exactly one item removed.
442+ */
443+ g_return_if_fail (position == 0);
444+ g_return_if_fail (added < 2 && removed < 2 && added ^ removed);
445+
446+ if (removed)
447+ indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
448+
449+ if (added)
450+ {
451+ g_clear_pointer (&self->header_action, g_free);
452+
453+ if (indicator_ng_menu_item_is_of_type (self->menu, 0, "com.canonical.indicator.root"))
454+ {
455+ GMenuModel *popup;
456+ gchar *action;
457+
458+ if (g_menu_model_get_item_attribute (self->menu, 0, G_MENU_ATTRIBUTE_ACTION, "s", &action) &&
459+ g_str_has_prefix (action, "indicator."))
460+ {
461+ self->header_action = g_strdup (action + strlen ("indicator."));
462+ }
463+
464+ popup = g_menu_model_get_item_link (self->menu, 0, G_MENU_LINK_SUBMENU);
465+ if (popup)
466+ {
467+ gtk_menu_shell_bind_model (GTK_MENU_SHELL (self->entry.menu), popup, NULL, TRUE);
468+ g_object_unref (popup);
469+ }
470+
471+ indicator_ng_update_entry (self);
472+
473+ g_free (action);
474+ }
475+ else
476+ g_warning ("indicator menu item must be of type 'com.canonical.indicator.root'");
477+ }
478+}
479+
480+static void
481+indicator_ng_service_appeared (GDBusConnection *connection,
482+ const gchar *name,
483+ const gchar *name_owner,
484+ gpointer user_data)
485+{
486+ IndicatorNg *self = user_data;
487+ gchar *menu_object_path;
488+
489+ g_assert (!self->actions);
490+ g_assert (!self->menu);
491+
492+ self->session_bus = g_object_ref (connection);
493+
494+ self->actions = G_ACTION_GROUP (g_dbus_action_group_get (connection, name_owner, self->object_path));
495+ gtk_widget_insert_action_group (GTK_WIDGET (self->entry.menu), "indicator", self->actions);
496+ g_signal_connect_swapped (self->actions, "action-added", G_CALLBACK (indicator_ng_update_entry), self);
497+ g_signal_connect_swapped (self->actions, "action-removed", G_CALLBACK (indicator_ng_update_entry), self);
498+ g_signal_connect_swapped (self->actions, "action-state-changed", G_CALLBACK (indicator_ng_update_entry), self);
499+
500+ menu_object_path = g_strconcat (self->object_path, "/", self->profile, NULL);
501+ self->menu = G_MENU_MODEL (g_dbus_menu_model_get (connection, name_owner, menu_object_path));
502+ g_signal_connect (self->menu, "items-changed", G_CALLBACK (indicator_ng_menu_changed), self);
503+ if (g_menu_model_get_n_items (self->menu))
504+ indicator_ng_menu_changed (self->menu, 0, 0, 1, self);
505+
506+ indicator_ng_update_entry (self);
507+
508+ g_free (menu_object_path);
509+}
510+
511+static void
512+indicator_ng_service_started (GObject *source_object,
513+ GAsyncResult *result,
514+ gpointer user_data)
515+{
516+ IndicatorNg *self = user_data;
517+ GError *error = NULL;
518+ GVariant *reply;
519+
520+ reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object), result, &error);
521+ if (!reply)
522+ {
523+ g_warning ("Could not activate service '%s': %s", self->name, error->message);
524+ indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
525+ g_error_free (error);
526+ return;
527+ }
528+
529+ switch (g_variant_get_uint32 (reply))
530+ {
531+ case 1: /* DBUS_START_REPLY_SUCCESS */
532+ break;
533+
534+ case 2: /* DBUS_START_REPLY_ALREADY_RUNNING */
535+ g_warning ("could not start service '%s': it is already running", self->name);
536+ break;
537+
538+ default:
539+ g_assert_not_reached ();
540+ }
541+
542+ g_variant_unref (reply);
543+}
544+
545+static void
546+indicator_ng_service_vanished (GDBusConnection *connection,
547+ const gchar *name,
548+ gpointer user_data)
549+{
550+ IndicatorNg *self = user_data;
551+
552+ indicator_ng_free_actions_and_menu (self);
553+
554+ /* Names may vanish because the service decided it doesn't need to
555+ * show its indicator anymore, or because it crashed. Let's assume it
556+ * crashes and restart it unless it explicitly hid its indicator. */
557+
558+ if (indicator_object_entry_is_visible (INDICATOR_OBJECT (self), &self->entry))
559+ {
560+ gint64 now;
561+
562+ /* take care not to start it if it repeatedly crashes */
563+ now = g_get_monotonic_time ();
564+ if (now - self->last_service_restart < 1 * G_USEC_PER_SEC)
565+ return;
566+
567+ self->last_service_restart = now;
568+
569+ g_dbus_connection_call (self->session_bus,
570+ "org.freedesktop.DBus",
571+ "/",
572+ "org.freedesktop.DBus",
573+ "StartServiceByName",
574+ g_variant_new ("(su)", self->bus_name, 0),
575+ G_VARIANT_TYPE ("(u)"),
576+ G_DBUS_CALL_FLAGS_NONE,
577+ -1,
578+ NULL,
579+ indicator_ng_service_started,
580+ self);
581+ }
582+}
583+
584+static gboolean
585+indicator_ng_initable_init (GInitable *initable,
586+ GCancellable *cancellable,
587+ GError **error)
588+{
589+ IndicatorNg *self = INDICATOR_NG (initable);
590+ GKeyFile *keyfile;
591+
592+ keyfile = g_key_file_new ();
593+ if (!g_key_file_load_from_file (keyfile, self->service_file, G_KEY_FILE_NONE, error))
594+ goto out;
595+
596+ if (!(self->name = g_key_file_get_string (keyfile, "Indicator Service", "Name", error)))
597+ goto out;
598+
599+ self->entry.name_hint = self->name;
600+
601+ if (!(self->bus_name = g_key_file_get_string (keyfile, "Indicator Service", "BusName", error)))
602+ goto out;
603+
604+ if (!(self->object_path = g_key_file_get_string (keyfile, "Indicator Service", "ObjectPath", error)))
605+ goto out;
606+
607+ self->name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
608+ self->bus_name,
609+ G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
610+ indicator_ng_service_appeared,
611+ indicator_ng_service_vanished,
612+ self, NULL);
613+
614+out:
615+ g_key_file_free (keyfile);
616+
617+ return self->name_watch_id > 0;
618+}
619+
620+static void
621+indicator_ng_class_init (IndicatorNgClass *class)
622+{
623+ GObjectClass *object_class = G_OBJECT_CLASS (class);
624+ IndicatorObjectClass *io_class = INDICATOR_OBJECT_CLASS (class);
625+
626+ object_class->get_property = indicator_ng_get_property;
627+ object_class->set_property = indicator_ng_set_property;
628+ object_class->dispose = indicator_ng_dispose;
629+ object_class->finalize = indicator_ng_finalize;
630+
631+ io_class->get_entries = indicator_ng_get_entries;
632+
633+ properties[PROP_SERVICE_FILE] = g_param_spec_string ("service-file",
634+ "Service file",
635+ "Path of the service file",
636+ NULL,
637+ G_PARAM_READWRITE |
638+ G_PARAM_CONSTRUCT_ONLY |
639+ G_PARAM_STATIC_STRINGS);
640+
641+ properties[PROP_PROFILE] = g_param_spec_string ("profile",
642+ "Profile",
643+ "Indicator profile",
644+ "desktop",
645+ G_PARAM_READWRITE |
646+ G_PARAM_CONSTRUCT_ONLY |
647+ G_PARAM_STATIC_STRINGS);
648+
649+ g_object_class_install_properties(object_class, N_PROPERTIES, properties);
650+}
651+
652+static void
653+indicator_ng_initable_iface_init (GInitableIface *initable)
654+{
655+ initable->init = indicator_ng_initable_init;
656+}
657+
658+static void
659+indicator_ng_init (IndicatorNg *self)
660+{
661+ self->entry.menu = g_object_ref_sink (gtk_menu_new ());
662+
663+ /* work around IndicatorObject's warning that the accessible
664+ * description is missing. We never set it on construction, but when
665+ * the menu model has arrived on the bus.
666+ */
667+ self->accessible_desc = g_strdup ("");
668+ self->entry.accessible_desc = self->accessible_desc;
669+
670+ indicator_object_set_visible (INDICATOR_OBJECT (self), FALSE);
671+}
672+
673+IndicatorNg *
674+indicator_ng_new (const gchar *service_file,
675+ GError **error)
676+{
677+ return g_initable_new (INDICATOR_TYPE_NG, NULL, error,
678+ "service-file", service_file,
679+ NULL);
680+}
681+
682+IndicatorNg *
683+indicator_ng_new_for_profile (const gchar *service_file,
684+ const gchar *profile,
685+ GError **error)
686+{
687+ return g_initable_new (INDICATOR_TYPE_NG, NULL, error,
688+ "service-file", service_file,
689+ "profile", profile,
690+ NULL);
691+}
692+
693+const gchar *
694+indicator_ng_get_service_file (IndicatorNg *self)
695+{
696+ g_return_val_if_fail (INDICATOR_IS_NG (self), NULL);
697+
698+ return self->service_file;
699+}
700+
701+const gchar *
702+indicator_ng_get_profile (IndicatorNg *self)
703+{
704+ g_return_val_if_fail (INDICATOR_IS_NG (self), NULL);
705+
706+ return self->profile;
707+}
708
709=== added file 'libindicator/indicator-ng.h'
710--- libindicator/indicator-ng.h 1970-01-01 00:00:00 +0000
711+++ libindicator/indicator-ng.h 2013-02-14 21:19:21 +0000
712@@ -0,0 +1,48 @@
713+/*
714+ * Copyright 2013 Canonical Ltd.
715+ *
716+ * This program is free software: you can redistribute it and/or modify it
717+ * under the terms of the GNU General Public License version 3, as published
718+ * by the Free Software Foundation.
719+ *
720+ * This program is distributed in the hope that it will be useful, but
721+ * WITHOUT ANY WARRANTY; without even the implied warranties of
722+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
723+ * PURPOSE. See the GNU General Public License for more details.
724+ *
725+ * You should have received a copy of the GNU General Public License along
726+ * with this program. If not, see <http://www.gnu.org/licenses/>.
727+ *
728+ * Authors:
729+ * Lars Uebernickel <lars.uebernickel@canonical.com>
730+ */
731+
732+#ifndef __INDICATOR_NG_H__
733+#define __INDICATOR_NG_H__
734+
735+#include "indicator-object.h"
736+
737+#define INDICATOR_TYPE_NG (indicator_ng_get_type ())
738+#define INDICATOR_NG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_TYPE_NG, IndicatorNg))
739+#define INDICATOR_NG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_TYPE_NG, IndicatorNgClass))
740+#define INDICATOR_IS_NG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_NG))
741+#define INDICATOR_IS_NG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_TYPE_NG))
742+#define INDICATOR_NG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_TYPE_NG, IndicatorNgClass))
743+
744+typedef struct _IndicatorNg IndicatorNg;
745+typedef IndicatorObjectClass IndicatorNgClass;
746+
747+GType indicator_ng_get_type (void);
748+
749+IndicatorNg * indicator_ng_new (const gchar *service_file,
750+ GError **error);
751+
752+IndicatorNg * indicator_ng_new_for_profile (const gchar *service_file,
753+ const gchar *profile,
754+ GError **error);
755+
756+const gchar * indicator_ng_get_service_file (IndicatorNg *indicator);
757+
758+const gchar * indicator_ng_get_profile (IndicatorNg *indicator);
759+
760+#endif
761
762=== modified file 'libindicator/indicator-object.c'
763--- libindicator/indicator-object.c 2012-09-19 20:00:57 +0000
764+++ libindicator/indicator-object.c 2013-02-14 21:19:21 +0000
765@@ -935,3 +935,10 @@
766 }
767 }
768
769+gboolean
770+indicator_object_entry_is_visible (IndicatorObject * io, IndicatorObjectEntry * entry)
771+{
772+ g_return_val_if_fail (INDICATOR_IS_OBJECT (io), FALSE);
773+
774+ return entry_get_private (io, entry)->visibility == ENTRY_VISIBLE;
775+}
776
777=== modified file 'libindicator/indicator-object.h'
778--- libindicator/indicator-object.h 2012-02-23 11:28:01 +0000
779+++ libindicator/indicator-object.h 2013-02-14 21:19:21 +0000
780@@ -199,6 +199,7 @@
781 guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry);
782 guint indicator_object_get_show_now (IndicatorObject * io, IndicatorObjectEntry * entry);
783 void indicator_object_set_visible (IndicatorObject * io, gboolean visible);
784+gboolean indicator_object_entry_is_visible (IndicatorObject * io, IndicatorObjectEntry * entry);
785 void indicator_object_entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp);
786 void indicator_object_entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp);
787 void indicator_object_entry_close (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp);
788
789=== modified file 'tests/Makefile.am'
790--- tests/Makefile.am 2012-06-18 18:42:53 +0000
791+++ tests/Makefile.am 2013-02-14 21:19:21 +0000
792@@ -26,7 +26,8 @@
793 session.conf.in \
794 service-manager-connect.service.in \
795 service-version-bad.service.in \
796- service-version-good.service.in
797+ service-version-good.service.in \
798+ com.canonical.indicator.test.service.in
799
800 #############################
801 # Test Loader
802@@ -466,3 +467,43 @@
803
804 DISTCLEANFILES += $(XML_REPORT) $(HTML_REPORT)
805
806+#############################
807+# Indicator-ng
808+#############################
809+
810+if USE_GTK3
811+
812+com.canonical.indicator.test.service: com.canonical.indicator.test.service.in Makefile.am
813+ sed -e "s|\@builddir\@|$(abspath $(builddir))|" $< > $@
814+
815+check_PROGRAMS += test-indicator-ng
816+
817+test_indicator_ng_SOURCES = test-indicator-ng.c
818+
819+test_indicator_ng_CFLAGS = \
820+ $(LIBINDICATOR_CFLAGS) \
821+ -I$(top_srcdir) \
822+ -DSRCDIR="\"$(srcdir)\"" \
823+ -DBUILD_DIR="\"$(abs_builddir)\"" \
824+ -Wall
825+
826+test_indicator_ng_LDADD = \
827+ $(LIBINDICATOR_LIBS) \
828+ -L$(top_builddir)/libindicator/.libs \
829+ $(INDICATOR_LIB)
830+
831+TESTS += test-indicator-ng
832+DISTCLEANFILES += test-indicator-ng
833+
834+# Mock indicator service
835+check_PROGRAMS += indicator-test-service
836+
837+indicator_test_service_SOURCES = indicator-test-service.c
838+indicator_test_service_CFLAGS = $(LIBINDICATOR_CFLAGS)
839+indicator_test_service_LDADD = $(LIBINDICATOR_LIBS)
840+
841+EXTRA_indicator_test_service_DEPENDENCIES = com.canonical.indicator.test.service
842+
843+DISTCLEANFILES += indicator-test-service com.canonical.indicator.test.service
844+
845+endif
846
847=== added file 'tests/com.canonical.indicator.test.service.in'
848--- tests/com.canonical.indicator.test.service.in 1970-01-01 00:00:00 +0000
849+++ tests/com.canonical.indicator.test.service.in 2013-02-14 21:19:21 +0000
850@@ -0,0 +1,3 @@
851+[D-BUS Service]
852+Name=com.canonical.indicator.test
853+Exec=@builddir@/indicator-test-service
854
855=== added file 'tests/com.canonical.test.indicator'
856--- tests/com.canonical.test.indicator 1970-01-01 00:00:00 +0000
857+++ tests/com.canonical.test.indicator 2013-02-14 21:19:21 +0000
858@@ -0,0 +1,4 @@
859+[Indicator Service]
860+Name=indicator-test
861+BusName=com.canonical.indicator.test
862+ObjectPath=/com/canonical/indicator/test
863
864=== added file 'tests/com.canonical.test.nosuchservice.indicator'
865--- tests/com.canonical.test.nosuchservice.indicator 1970-01-01 00:00:00 +0000
866+++ tests/com.canonical.test.nosuchservice.indicator 2013-02-14 21:19:21 +0000
867@@ -0,0 +1,4 @@
868+[Indicator Service]
869+Name=indicator-test
870+BusName=com.canonical.indicator.test.nosuchservice
871+ObjectPath=/com/canonical/indicator/test
872
873=== added file 'tests/indicator-test-service.c'
874--- tests/indicator-test-service.c 1970-01-01 00:00:00 +0000
875+++ tests/indicator-test-service.c 2013-02-14 21:19:21 +0000
876@@ -0,0 +1,108 @@
877+
878+#include <gio/gio.h>
879+
880+typedef struct
881+{
882+ GSimpleActionGroup *actions;
883+ GMenu *menu;
884+
885+ guint actions_export_id;
886+ guint menu_export_id;
887+} IndicatorTestService;
888+
889+static void
890+bus_acquired (GDBusConnection *connection,
891+ const gchar *name,
892+ gpointer user_data)
893+{
894+ IndicatorTestService *indicator = user_data;
895+ GError *error = NULL;
896+
897+ indicator->actions_export_id = g_dbus_connection_export_action_group (connection,
898+ "/com/canonical/indicator/test",
899+ G_ACTION_GROUP (indicator->actions),
900+ &error);
901+ if (indicator->actions_export_id == 0)
902+ {
903+ g_warning ("cannot export action group: %s", error->message);
904+ g_error_free (error);
905+ return;
906+ }
907+
908+ indicator->menu_export_id = g_dbus_connection_export_menu_model (connection,
909+ "/com/canonical/indicator/test/desktop",
910+ G_MENU_MODEL (indicator->menu),
911+ &error);
912+ if (indicator->menu_export_id == 0)
913+ {
914+ g_warning ("cannot export menu: %s", error->message);
915+ g_error_free (error);
916+ return;
917+ }
918+}
919+
920+static void
921+name_lost (GDBusConnection *connection,
922+ const gchar *name,
923+ gpointer user_data)
924+{
925+ IndicatorTestService *indicator = user_data;
926+
927+ if (indicator->actions_export_id)
928+ g_dbus_connection_unexport_action_group (connection, indicator->actions_export_id);
929+
930+ if (indicator->menu_export_id)
931+ g_dbus_connection_unexport_menu_model (connection, indicator->menu_export_id);
932+}
933+
934+static void
935+activate_show (GSimpleAction *action,
936+ GVariant *parameter,
937+ gpointer user_data)
938+{
939+ g_message ("showing");
940+}
941+
942+int
943+main (int argc, char **argv)
944+{
945+ IndicatorTestService indicator = { 0 };
946+ GMenuItem *item;
947+ GMenu *submenu;
948+ GActionEntry entries[] = {
949+ { "_header", NULL, NULL, "('Test', 'indicator-test', 'Test indicator', true)", NULL },
950+ { "show", activate_show, NULL, NULL, NULL }
951+ };
952+ GMainLoop *loop;
953+
954+ indicator.actions = g_simple_action_group_new ();
955+ g_simple_action_group_add_entries (indicator.actions, entries, G_N_ELEMENTS (entries), NULL);
956+
957+ submenu = g_menu_new ();
958+ g_menu_append (submenu, "Show", "indicator.show");
959+ item = g_menu_item_new (NULL, "indicator._header");
960+ g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.root");
961+ g_menu_item_set_submenu (item, G_MENU_MODEL (submenu));
962+ indicator.menu = g_menu_new ();
963+ g_menu_append_item (indicator.menu, item);
964+
965+ g_bus_own_name (G_BUS_TYPE_SESSION,
966+ "com.canonical.indicator.test",
967+ G_BUS_NAME_OWNER_FLAGS_NONE,
968+ bus_acquired,
969+ NULL,
970+ name_lost,
971+ &indicator,
972+ NULL);
973+
974+ loop = g_main_loop_new (NULL, FALSE);
975+ g_main_loop_run (loop);
976+
977+ g_object_unref (submenu);
978+ g_object_unref (item);
979+ g_object_unref (indicator.actions);
980+ g_object_unref (indicator.menu);
981+ g_object_unref (loop);
982+
983+ return 0;
984+}
985
986=== added file 'tests/test-indicator-ng.c'
987--- tests/test-indicator-ng.c 1970-01-01 00:00:00 +0000
988+++ tests/test-indicator-ng.c 2013-02-14 21:19:21 +0000
989@@ -0,0 +1,167 @@
990+
991+#include <libindicator/indicator-ng.h>
992+
993+static void
994+indicator_ng_test_func (gconstpointer user_data)
995+{
996+ GTestFunc test_func = user_data;
997+ GTestDBus *bus;
998+
999+ bus = g_test_dbus_new (G_TEST_DBUS_NONE);
1000+ g_test_dbus_add_service_dir (bus, BUILD_DIR);
1001+ g_test_dbus_up (bus);
1002+
1003+ test_func ();
1004+
1005+ g_test_dbus_down (bus);
1006+ g_object_unref (bus);
1007+}
1008+
1009+#define indicator_ng_test_add(name, test_func) \
1010+ g_test_add_data_func ("/indicator-ng/" name, test_func, indicator_ng_test_func)
1011+
1012+static gboolean
1013+stop_main_loop (gpointer user_data)
1014+{
1015+ GMainLoop *loop = user_data;
1016+
1017+ g_main_loop_quit (loop);
1018+
1019+ return FALSE;
1020+}
1021+
1022+static void
1023+test_non_existing (void)
1024+{
1025+ IndicatorNg *indicator;
1026+ GError *error = NULL;
1027+
1028+ indicator = indicator_ng_new (SRCDIR "/com.canonical.does.not.exist.indicator", &error);
1029+ g_assert (indicator == NULL);
1030+ g_assert_error (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
1031+
1032+ g_clear_error (&error);
1033+}
1034+
1035+static void
1036+test_instantiation (void)
1037+{
1038+ IndicatorNg *indicator;
1039+ GError *error = NULL;
1040+ GMainLoop *loop;
1041+
1042+ indicator = indicator_ng_new (SRCDIR "/com.canonical.test.nosuchservice.indicator", &error);
1043+ g_assert (indicator);
1044+ g_assert (error == NULL);
1045+
1046+ g_assert_cmpstr (indicator_ng_get_service_file (indicator), ==, SRCDIR "/com.canonical.test.nosuchservice.indicator");
1047+ g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "desktop");
1048+
1049+ {
1050+ gchar *service_file;
1051+ gchar *profile;
1052+
1053+ g_object_get (indicator, "service-file", &service_file,
1054+ "profile", &profile,
1055+ NULL);
1056+
1057+ g_assert_cmpstr (service_file, ==, SRCDIR "/com.canonical.test.nosuchservice.indicator");
1058+ g_assert_cmpstr (profile, ==, "desktop");
1059+
1060+ g_free (service_file);
1061+ g_free (profile);
1062+ }
1063+
1064+ loop = g_main_loop_new (NULL, FALSE);
1065+ g_timeout_add (200, stop_main_loop, loop);
1066+ g_main_loop_run (loop);
1067+
1068+ /* no such service, so there shouldn't be any indicators */
1069+ g_assert (indicator_object_get_entries (INDICATOR_OBJECT (indicator)) == NULL);
1070+
1071+ g_main_loop_unref (loop);
1072+ g_object_unref (indicator);
1073+}
1074+
1075+static void
1076+test_instantiation_with_profile (void)
1077+{
1078+ IndicatorNg *indicator;
1079+ GError *error = NULL;
1080+
1081+ indicator = indicator_ng_new_for_profile (SRCDIR "/com.canonical.test.indicator", "greeter", &error);
1082+ g_assert (indicator);
1083+ g_assert (error == NULL);
1084+
1085+ g_assert_cmpstr (indicator_ng_get_profile (indicator), ==, "greeter");
1086+
1087+ g_object_unref (indicator);
1088+}
1089+
1090+static void
1091+test_menu (void)
1092+{
1093+ IndicatorNg *indicator;
1094+ GError *error = NULL;
1095+ GMainLoop *loop;
1096+ GList *entries;
1097+ IndicatorObjectEntry *entry;
1098+
1099+ indicator = indicator_ng_new (SRCDIR "/com.canonical.test.indicator", &error);
1100+ g_assert (indicator);
1101+ g_assert (error == NULL);
1102+
1103+ loop = g_main_loop_new (NULL, FALSE);
1104+ g_timeout_add (500, stop_main_loop, loop);
1105+ g_main_loop_run (loop);
1106+
1107+ entries = indicator_object_get_entries (INDICATOR_OBJECT (indicator));
1108+ g_assert_cmpint (g_list_length (entries), ==, 1);
1109+
1110+ entry = entries->data;
1111+ g_assert_cmpstr (entry->name_hint, ==, "indicator-test");
1112+ g_assert_cmpstr (entry->accessible_desc, ==, "Test indicator");
1113+ g_assert_cmpstr (gtk_label_get_label (entry->label), ==, "Test");
1114+ g_assert (gtk_image_get_storage_type (entry->image) == GTK_IMAGE_STOCK);
1115+ {
1116+ GList *children;
1117+ GtkMenuItem *item;
1118+
1119+ g_assert (entry->menu != NULL);
1120+
1121+ children = gtk_container_get_children (GTK_CONTAINER (entry->menu));
1122+ g_assert_cmpint (g_list_length (children), ==, 1);
1123+
1124+ item = children->data;
1125+ g_assert (GTK_IS_MENU_ITEM (item));
1126+ g_assert (gtk_widget_is_sensitive (GTK_WIDGET (item)));
1127+ g_assert_cmpstr (gtk_menu_item_get_label (item), ==, "Show");
1128+
1129+ g_list_free (children);
1130+ }
1131+
1132+ g_list_free (entries);
1133+ g_main_loop_unref (loop);
1134+ g_object_unref (indicator);
1135+}
1136+
1137+int
1138+main (int argc, char **argv)
1139+{
1140+ /* gvfs, dconf, and appmenu-gtk leak GDbusConnections, which confuses
1141+ * g_test_dbus_down. Make sure we're not using any of those.
1142+ */
1143+ g_setenv ("GIO_USE_VFS", "local", TRUE);
1144+ g_setenv ("GSETTINGS_BACKEND", "memory", TRUE);
1145+ g_unsetenv ("UBUNTU_MENUPROXY");
1146+
1147+ g_test_init (&argc, &argv, NULL);
1148+ gtk_init (&argc, &argv);
1149+
1150+ indicator_ng_test_add ("non-existing", test_non_existing);
1151+ indicator_ng_test_add ("instantiation", test_instantiation);
1152+ indicator_ng_test_add ("instantiation-with-profile", test_instantiation_with_profile);
1153+ indicator_ng_test_add ("menu", test_menu);
1154+
1155+ return g_test_run ();
1156+}
1157
1158=== modified file 'tools/indicator-loader.c'
1159--- tools/indicator-loader.c 2012-09-19 20:00:57 +0000
1160+++ tools/indicator-loader.c 2013-02-14 21:19:21 +0000
1161@@ -25,6 +25,10 @@
1162 #include <gtk/gtk.h>
1163 #include <libindicator/indicator-object.h>
1164
1165+#if GTK_MAJOR_VERSION == 3
1166+#include <libindicator/indicator-ng.h>
1167+#endif
1168+
1169 static GHashTable * entry_to_menuitem = NULL;
1170
1171 #define ENTRY_DATA_NAME "indicator-custom-entry-data"
1172@@ -129,14 +133,27 @@
1173 g_debug("Looking at Module: %s", name);
1174 g_return_val_if_fail(name != NULL, FALSE);
1175
1176- if (!g_str_has_suffix(name, G_MODULE_SUFFIX)) {
1177- return FALSE;
1178- }
1179-
1180 g_debug("Loading Module: %s", name);
1181
1182 /* Build the object for the module */
1183- IndicatorObject * io = indicator_object_new_from_file(name);
1184+ IndicatorObject *io;
1185+ if (g_str_has_suffix(name, G_MODULE_SUFFIX)) {
1186+ io = indicator_object_new_from_file(name);
1187+ }
1188+#if GTK_MAJOR_VERSION == 3
1189+ else if (g_str_has_suffix(name, ".indicator")) {
1190+ GError *error = NULL;
1191+
1192+ io = INDICATOR_OBJECT(indicator_ng_new(name, &error));
1193+ if (!io) {
1194+ g_warning ("could not load indicator from '%s': %s", name, error->message);
1195+ g_error_free (error);
1196+ return FALSE;
1197+ }
1198+ }
1199+#endif
1200+ else
1201+ return FALSE;
1202
1203 /* Connect to it's signals */
1204 g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, G_CALLBACK(entry_added), menu);
1205
1206=== added file 'trim-lcov.py'
1207--- trim-lcov.py 1970-01-01 00:00:00 +0000
1208+++ trim-lcov.py 2013-02-14 21:19:21 +0000
1209@@ -0,0 +1,69 @@
1210+#!/usr/bin/python
1211+
1212+# Copyright 2013 Canonical Ltd.
1213+#
1214+# This library is free software; you can redistribute it and/or
1215+# modify it under the terms of the GNU General Public License
1216+# version 3.0 as published by the Free Software Foundation.
1217+#
1218+# This library is distributed in the hope that it will be useful,
1219+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1220+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1221+# GNU General Public License version 3.0 for more details.
1222+#
1223+# You should have received a copy of the GNU General Public
1224+# License along with this library. If not, see
1225+# <http://www.gnu.org/licenses/>.
1226+#
1227+# Author: Ryan Lortie <desrt@desrt.ca>
1228+
1229+
1230+# This script removes branch and/or line coverage data for lines that
1231+# contain a particular substring.
1232+#
1233+# In the interest of "fairness" it removes all branch or coverage data
1234+# when a match is found -- not just negative data. It is therefore
1235+# likely that running this script will actually reduce the total number
1236+# of lines and branches that are marked as covered (in absolute terms).
1237+#
1238+# This script intentionally avoids checking for errors. Any exceptions
1239+# will trigger make to fail.
1240+
1241+import sys
1242+
1243+line_suppress = ['g_assert_not_reached']
1244+branch_suppress = ['g_assert', 'g_return_if_fail', 'g_return_val_if_fail', 'G_DEFINE_TYPE']
1245+
1246+def check_suppress(suppressions, source, data):
1247+ line, _, rest = data.partition(',')
1248+ line = int(line) - 1
1249+
1250+ assert line < len(source)
1251+
1252+ for suppression in suppressions:
1253+ if suppression in source[line]:
1254+ return True
1255+
1256+ return False
1257+
1258+source = []
1259+for line in sys.stdin:
1260+ line = line[:-1]
1261+
1262+ keyword, _, rest = line.partition(':')
1263+
1264+ # Source file
1265+ if keyword == 'SF':
1266+ source = file(rest).readlines()
1267+
1268+ # Branch coverage data
1269+ elif keyword == 'BRDA':
1270+ if check_suppress(branch_suppress, source, rest):
1271+ continue
1272+
1273+ # Line coverage data
1274+ elif keyword == 'DA':
1275+ if check_suppress(line_suppress, source, rest):
1276+ continue
1277+
1278+ print line

Subscribers

People subscribed via source and target branches