Merge lp:~larsu/libindicator/add-indicator-ng into lp:libindicator/13.04
- add-indicator-ng
- Merge into trunk.13.04
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 |
Related bugs: |
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 |
Commit message
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=
BusName=
ObjectPath=
For unity-panel-
This can be tested with the example indicator in lp:~larsu/libunity/add-indicator (examples/
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:477
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 478. By Lars Karlitski
-
indicator-ng.h: use local include
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:478
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:484
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 485. By Lars Karlitski
-
indicator-ng: properly unset action group when the service disappears
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:485
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:489
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Charles Kerr (charlesk) wrote : | # |
1. You're overriding get_label, get_image, get_entry, get_name, get_accessible... and also have that indicator_
2. Bonus points if you'd add "void indicator_
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_
4. is there a reason to not use (&s&s&sb) in indicator_
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_
7. In indicator_
8. g_build_filename() instead of g_strconcat()?
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:491
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 492. By Lars Karlitski
-
indicator-ng: use an IndicatorObject
Entry 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.
Lars Karlitski (larsu) wrote : | # |
> 1. You're overriding get_label, get_image, get_entry, get_name,
> get_accessible... and also have that indicator_
> around visible/invisible entries in the parent. It would be more
> straightforward to remove all of that, replace the
> label/image/
> "IndicatorObjec
> "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_
> (IndicatorObject * io, IndicatorObject
> accessible_desc);" in indicator-object.h, similar to the existing
> i_o_e_activate() func, and use that instead of
> indicator_
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_
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_
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_
Fixed in r493.
> 4. is there a reason to not use (&s&s&sb) in indicator_
> 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_
> important point.
Yeah, I dislike that call when dealing with swapped arguments.
> 7. In indicator_
> pointer if g_menu_
> 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...
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:496
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ted Gould (ted) 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.
* 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_
* In indicator_
* 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_
* 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 ...
- 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
Lars Karlitski (larsu) wrote : | # |
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_
> 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_
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_
> 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>
> ...
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:501
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Ted Gould (ted) wrote : | # |
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_
> > 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_
> 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_
> > 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_
Service", "Name", error)) &&
516 + (bus_name = g_key_file_
"BusName", error)) &&
517 + (self->object_path = g_key_file_
Service", "ObjectPath", error)))
It seems ...
Lars Karlitski (larsu) 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.
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_
> Service", "Name", error)) &&
> 516 + (bus_name = g_key_file_
> "BusName", error)) &&
> 517 + (self->object_path = g_key_file_
> Service",...
Ted Gould (ted) wrote : | # |
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...
Lars Karlitski (larsu) wrote : | # |
> 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...
- 502. By Lars Karlitski
-
indicator-ng: simplify flow in initable_init
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:502
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Coverity artifacts:
http://
http://
http://
Click here to trigger a rebuild:
http://
- 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:503
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Coverity artifacts:
http://
http://
http://
Click here to trigger a rebuild:
http://
- 504. By Lars Karlitski
-
indicator-ng: don't hide the indicator if the service is already running
Ted Gould (ted) : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:504
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Coverity artifacts:
http://
http://
http://
Click here to trigger a rebuild:
http://
- 505. By Lars Karlitski
-
indicator-ng: add license header
Charles Kerr (charlesk) wrote : | # |
Thanks for that update :)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:505
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https:/
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Coverity artifacts:
http://
http://
http://
Click here to trigger a rebuild:
http://
Preview Diff
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 |
FAILED: Continuous integration, rev:475 jenkins. qa.ubuntu. com/job/ libindicator- ci/13/ jenkins. qa.ubuntu. com/job/ libindicator- ci/./build= pbuilder, distribution= coverity- raring, flavor= amd64/13/ console jenkins. qa.ubuntu. com/job/ libindicator- ci/./build= pbuilder, distribution= raring, flavor= armhf/13/ console jenkins. qa.ubuntu. com/job/ libindicator- ci/./build= pbuilder, distribution= raring, flavor= i386/13/ console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: jenkins. qa.ubuntu. com/job/ libindicator- ci/13// rebuild/?
http://