Merge lp:~ted/indicator-appmenu/hud into lp:indicator-appmenu/0.4

Proposed by Ted Gould on 2012-01-24
Status: Merged
Merged at revision: 138
Proposed branch: lp:~ted/indicator-appmenu/hud
Merge into: lp:indicator-appmenu/0.4
Diff against target: 7994 lines (+7473/-31)
74 files modified
.bzrignore (+144/-0)
Makefile.am (+7/-1)
autogen.sh (+1/-1)
configure.ac (+84/-14)
data/Makefile.am (+22/-4)
data/com.canonical.hud.service.in (+3/-0)
data/com.canonical.indicator.appmenu.gschema.xml.in (+3/-3)
data/com.canonical.indicator.appmenu.hud.gschema.xml.in (+14/-0)
data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in (+90/-0)
docs/HUD Architecture.svg (+368/-0)
docs/Makefile.am (+1/-0)
docs/man/Makefile.am (+16/-0)
docs/man/hud-cli.xml (+38/-0)
docs/man/hud-dump-application.xml (+45/-0)
docs/man/hud-list-applications.xml (+44/-0)
docs/man/hud-verify-app-info.xml (+35/-0)
po/POTFILES.in (+14/-0)
src/Makefile.am (+127/-1)
src/dbusmenu-collector.c (+711/-0)
src/dbusmenu-collector.h (+73/-0)
src/distance.c (+401/-0)
src/distance.h (+31/-0)
src/dump-app-info.c (+174/-0)
src/dump-app-info.h (+30/-0)
src/hud-cli.c (+237/-0)
src/hud-dbus.c (+286/-0)
src/hud-dbus.h (+54/-0)
src/hud-dump-application.c (+73/-0)
src/hud-list-applications (+9/-0)
src/hud-search.c (+690/-0)
src/hud-search.h (+64/-0)
src/hud-service.c (+62/-0)
src/hud-verify-app-info.c (+104/-0)
src/hud.xml (+37/-0)
src/indicator-appmenu.c (+0/-4)
src/indicator-tracker.c (+618/-0)
src/indicator-tracker.h (+66/-0)
src/load-app-info.c (+319/-0)
src/load-app-info.h (+30/-0)
src/shared-values.h (+33/-0)
src/usage-tracker.c (+507/-0)
src/usage-tracker.h (+58/-0)
src/utils.c (+30/-0)
src/utils.h (+35/-0)
src/window-menus.c (+0/-2)
src/window-menus.h (+0/-1)
tests/Makefile.am (+444/-0)
tests/bad-app-info/dual-headers.hud-app-info (+3/-0)
tests/bad-app-info/item-no-count.hud-app-info (+8/-0)
tests/bad-app-info/item-no-name.hud-app-info (+8/-0)
tests/bad-app-info/menu-no-name.hud-app-info (+8/-0)
tests/bad-app-info/missing-desktop.hud-app-info (+7/-0)
tests/bad-app-info/missing-menus.hud-app-info (+6/-0)
tests/bad-app-info/multiple-menus.hud-app-info (+13/-0)
tests/good-app-info/testapp.desktop.hud-app-info (+8/-0)
tests/good-app-info/testapp100.desktop.hud-app-info (+8/-0)
tests/good-app-info/tons-of-entries.hud-app-info (+143/-0)
tests/run-xvfb.sh (+7/-0)
tests/test-app-indicator-tracker.c (+85/-0)
tests/test-app-indicator.c (+65/-0)
tests/test-bad-app-info.c (+106/-0)
tests/test-create-db.sql (+1/-0)
tests/test-distance.c (+182/-0)
tests/test-indicator-tracker-owner.c (+70/-0)
tests/test-indicator-tracker.c (+90/-0)
tests/test-load-app-info.c (+85/-0)
tests/test-usage-db-ancient.c (+38/-0)
tests/test-usage-db-ancient.sql (+7/-0)
tests/test-usage-db-old.c (+81/-0)
tests/test-usage-db-old.sql (+11/-0)
tests/test-usage-db-simple.c (+107/-0)
tests/test-usage-db-simple.sql (+11/-0)
tests/test-usage-db-testapp.c (+82/-0)
tests/test-usage-dump-entries.sql (+1/-0)
To merge this branch: bzr merge lp:~ted/indicator-appmenu/hud
Reviewer Review Type Date Requested Status
Charles Kerr (community) 2012-01-26 Approve on 2012-01-27
Allan LeSage 2012-01-26 Approve on 2012-01-27
Tony Espy indicatortracker 2012-01-26 Approve on 2012-01-26
Lars Karlitski (community) Approve on 2012-01-26
Conor Curran (community) 2012-01-24 Approve on 2012-01-26
Alberto Ruiz huddbus 2012-01-26 Pending
Review via email: mp+89905@code.launchpad.net

Description of the change

Adding the HUD feature.

To post a comment you must log in.
lp:~ted/indicator-appmenu/hud updated on 2012-01-24
165. By Ted Gould on 2012-01-24

Resolving conflict with trunk

166. By Ted Gould on 2012-01-24

Simplified the architecture diagram

Just some drive-by comments.

You are putting dbusmenu-collector in the dbusmenu namespace. That seems like looking for trouble with the dbusmenu maintainer? I heard he doesn't go lightly on this ;-) Although, seriously; if you're not planning on migrating it to libdbusmenu then I think you should rename the class.

SQL-wise it looks like you want an index on the 'application' column at the very least. Maybe a compound index (application,entry,timestamp) or just one on 'timestamp' although the queries affected by this are not exercised nearly as much as the one that would just use the apps index, so the compound index may be overkill.

Ted Gould (ted) wrote :

On Wed, 2012-01-25 at 08:17 +0000, Mikkel Kamstrup Erlandsen wrote:
> You are putting dbusmenu-collector in the dbusmenu namespace. That
> seems like looking for trouble with the dbusmenu maintainer? I heard
> he doesn't go lightly on this ;-) Although, seriously; if you're not
> planning on migrating it to libdbusmenu then I think you should rename
> the class.

Makes sense. No, don't plan on migrated it to dbusmenu.

> SQL-wise it looks like you want an index on the 'application' column
> at the very least. Maybe a compound index (application,entry,timestamp)
> or just one on 'timestamp' although the queries affected by this are
> not exercised nearly as much as the one that would just use the apps
> index, so the compound index may be overkill.

I'm honestly not enough of an SQL expert to really grok this... I got
"put an index on application" which I think I could Google :-) If
that's not what you meant, speak up now!

Conor Curran (cjcurran) wrote :

Distance is looking very good Ted. I wouldn't claim to be an expert in Levenshtein distance calculations but couldn't see anything obvious to comment on. Are there tests ? Seems like something you unit test easily.

review: Approve
Lars Karlitski (larsu) wrote :

Here's my review of HudSearch.

hud-search.h:

* no need to include both glib.h and glib-object.h

hud-search.c:

* G_DEFINE_TYPE already declares _init and _class_init functions, no need to
  do it again in line 57-58

* hud_search_init:
  - priv members tracker, matcher, window_changed_sig, active_window,
    collector, usage, and appmenu are initialized to NULL only to be
    overwritten uncoditionally a few lines down
  - active_app is not initialized at all when active_window is NULL
    (are private structs initalized to zero when allocating the instance?)

* hud_search_dispose:
  - using g_clear_object would save some lines

* hud_search_finalize:
  - can be removed, it only chains up to parent class

* hud_search_new:
  - no need to cast the return value of g_object_new

* found_list_to_usage_array:
  - The loop stops once it finds an item in found_list that has a distance
    greater than max-distance - is found_list sorted? It doesn't look that
    way from a quick glance at dbusmenu_collector_search. break -> continue?
  - move get_settings_uint out of the loop

* desktop2icon:
  - why a g_file_test? g_desktop_app_info_new_from_filename probably
    does the exact same test
  - renaming to desktop_to_icon would make it fit better

* search_current_app:
  - no need to call dbusmenu_collector_search when address or path are NULL
    (i.e. no current app)

* search_indicators:
  - move get_settings_uint out of the loop
  - rename indicator-penalty to indicator-weight? It's not really a penalty

* search_and_sort:
  - second loop ("Calculate overall") could be merged with the first
  - imo, sorting by summing the relative usage and distance isn't a very good
    strategy, as both usages and distances will tend to only have few very
    small and many very large values, which is always tricky when dealing
    with percentages.

    Somewhat exaggerated example: Usage count for "open" and "save" is 10 each.
    I type "blur" (which is a perfect match) for the first time:

           distance usage total (as in usage_sort())
    blur 0 0 % 0 0 % 100 %
    open 4 50 % 10 50 % 100 %
    save 4 50 % 10 50 % 100 %

    Ranking those 3 the same is surely not what we want?

* hud_search_suggestions:
  - found_list is passed to search_and_sort() and then freed. Should be moved
    into search_and_sort entirely
  - g_list_append is O(n), use g_list_prepend and reverse the list afterwards
    (shouldn't be much of an issue with only 5 items, though)

* hud_search_execute:
  - input isn't checked (whether 'key' really contains a variant and if 'app',
    'display', 'address', 'path', 'id' are not NULL)

* active_window_changed:
  - dfeet, terminal, and hud prototype windows are ignored (seems like old
    debugging code), which they probably shouldn't in the final version

* hud_search_suggest_new:
  - is 'key' the same key that is extracted in hud_search_execute?
    If so, it's using 'db' and not 'display' in the 2nd tuple position

* And most importantly: there are some seriously long lines (> 200 chars) ;)

Ted Gould (ted) wrote :

On Thu, 2012-01-26 at 12:34 +0000, Conor Curran wrote:
> Distance is looking very good Ted. I wouldn't claim to be an
> expert in Levenshtein distance calculations but couldn't see
> anything obvious to comment on. Are there tests ? Seems like
> something you unit test easily.

Yup, there are. I think this is something we need to tune, so I'm
really interesting in increasing the number of them. We have about 5
different setups that were used to get us to "mostly works", I imagine
it'll take 50 to get us to "really works" ;-) Thanks for the review!

Ted Gould (ted) wrote :
Download full text (5.3 KiB)

On Thu, 2012-01-26 at 12:37 +0000, Lars Uebernickel wrote:
> hud-search.h:
>
> * no need to include both glib.h and glib-object.h

Old template :-) Fixed r167

> hud-search.c:
>
> * G_DEFINE_TYPE already declares _init and _class_init functions, no need to
> do it again in line 57-58

Ditto. Fixed r168

> * hud_search_init:
> - priv members tracker, matcher, window_changed_sig, active_window,
> collector, usage, and appmenu are initialized to NULL only to be
> overwritten uncoditionally a few lines down

I actually do that on purpose. And the reason is that I figure that the
code in init() will change, and perhaps I'd screw up and not define a
variable or put a conditional in that doesn't work. So the idea is to
isolate the damage caused by those mistakes. If the mistakes aren't
made, the compiler will optimize the inefficiencies out anyway.

> - active_app is not initialized at all when active_window is NULL
> (are private structs initalized to zero when allocating the instance?)

They are, but being explicit for the reasons mentioned above. r169

> * hud_search_dispose:
> - using g_clear_object would save some lines

Cool! I didn't know about that function. It sure will! r170

> * hud_search_finalize:
> - can be removed, it only chains up to parent class

Yeah, I typically leave it there for future use. Low runtime cost, huge
developer savings when you need to put something there :-)

> * hud_search_new:
> - no need to cast the return value of g_object_new

Fair. r171

> * found_list_to_usage_array:
> - The loop stops once it finds an item in found_list that has a distance
> greater than max-distance - is found_list sorted? It doesn't look that
> way from a quick glance at dbusmenu_collector_search. break -> continue?

Yes! The list used to be sorted, but no longer is. r172

> - move get_settings_uint out of the loop

Done. r173

> * desktop2icon:
> - why a g_file_test? g_desktop_app_info_new_from_filename probably
> does the exact same test

So that we can have better error reporting... but, we're not doing that.
So I added that. r174

> - renaming to desktop_to_icon would make it fit better

Done. r175

> * search_current_app:
> - no need to call dbusmenu_collector_search when address or path are NULL
> (i.e. no current app)

Fixed for the desktop file as well. r176

> * search_indicators:
> - move get_settings_uint out of the loop

Fixed. r177

> - rename indicator-penalty to indicator-weight? It's not really a penalty

Hmm, I actually do consider it a penalty... it makes your distance
further if you're an indicator. It seems like it's penalizing those
entrys? I'm not dead set on the name, but it seems an apt description
to me.

> * search_and_sort:
> - second loop ("Calculate overall") could be merged with the first

Yes, I did that to make the code easier to read. I'm sure the compiler
merges them when it unrolls them.

> - imo, sorting by summing the relative usage and distance isn't a very good
> strategy, as both usages and distances will tend to only have few very
> small and many very large values, which is always tricky when dealing
> wi...

Read more...

lp:~ted/indicator-appmenu/hud updated on 2012-01-26
167. By Ted Gould on 2012-01-26

Only including glib

168. By Ted Gould on 2012-01-26

Remvoing unneeded prototypes

169. By Ted Gould on 2012-01-26

Explicitly initializing active_app

170. By Ted Gould on 2012-01-26

Switching dispose to use g_object_clear

171. By Ted Gould on 2012-01-26

Removing unneeded cast

172. By Ted Gould on 2012-01-26

Making it so that we don't stop looking on a single entry that has too large of a distance

173. By Ted Gould on 2012-01-26

Get the settings once, on the outside of the loop

174. By Ted Gould on 2012-01-26

Add better errors when loading a desktop file

175. By Ted Gould on 2012-01-26

Rename desktop2icon to desktop_to_icon to match naming style

176. By Ted Gould on 2012-01-26

Not calling deeper functions if we don't have an active application

177. By Ted Gould on 2012-01-26

Move getting the indicator penalty out of the loop

178. By Ted Gould on 2012-01-26

Prepend and reverse for performance

179. By Ted Gould on 2012-01-26

Put some error handling for bad values into _execute()

180. By Ted Gould on 2012-01-26

Change the name of the variable to match what it actually is

Lars Karlitski (larsu) wrote :
Download full text (4.1 KiB)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 01/26/2012 04:34 PM, Ted Gould wrote:
> [...]
>
>> * hud_search_init: - priv members tracker, matcher,
>> window_changed_sig, active_window, collector, usage, and appmenu
>> are initialized to NULL only to be overwritten uncoditionally a
>> few lines down
>
> I actually do that on purpose. And the reason is that I figure
> that the code in init() will change, and perhaps I'd screw up and
> not define a variable or put a conditional in that doesn't work.
> So the idea is to isolate the damage caused by those mistakes. If
> the mistakes aren't made, the compiler will optimize the
> inefficiencies out anyway.

Fair enough.

> [...]
>
>> * desktop2icon: - why a g_file_test?
>> g_desktop_app_info_new_from_filename probably does the exact same
>> test
>
> So that we can have better error reporting... but, we're not doing
> that. So I added that. r174

Agreed, better error reporting is always ... better.

> [...]
>> - rename indicator-penalty to indicator-weight? It's not really
>> a penalty
>
> Hmm, I actually do consider it a penalty... it makes your distance
> further if you're an indicator. It seems like it's penalizing
> those entrys? I'm not dead set on the name, but it seems an apt
> description to me.

You use it to weigh the relative importance between menu items and
indicators. Also, it's not a penalty anymore if it's greater than 100.

Not that important to me, though. It's probably used as a penalty
most of the time anyway.

>> * search_and_sort: - second loop ("Calculate overall") could be
>> merged with the first
>
> Yes, I did that to make the code easier to read. I'm sure the
> compiler merges them when it unrolls them.

Fair.

>> - imo, sorting by summing the relative usage and distance isn't a
>> very good strategy, as both usages and distances will tend to
>> only have few very small and many very large values, which is
>> always tricky when dealing with percentages.
>>
>> Somewhat exaggerated example: Usage count for "open" and "save"
>> is 10 each. I type "blur" (which is a perfect match) for the
>> first time:
>>
>> distance usage total (as in usage_sort()) blur 0
>> 0 % 0 0 % 100 % open 4 50 % 10 50 %
>> 100 % save 4 50 % 10 50 % 100 %
>>
>> Ranking those 3 the same is surely not what we want?
>
> Yes. I think there needs be some amount of multiplicative factor
> here. I consider that in the category of "search tuning" and
> something we should work on in the future. Open to ideas here, but
> I want to merge this before optimizing.

I agree, this can be done after the merge.

>> * hud_search_suggestions: - found_list is passed to
>> search_and_sort() and then freed. Should be moved into
>> search_and_sort entirely
>
> That can't be done because the found list holds references to all
> the memory and the values aren't copied into the usage structures
> (and they're not real objects so they can't be referenced). So
> when the found list can't be free'd before the search suggestions
> are built. Probably should turn the found structures into real
> objects so they can be ref'd appropriately....

Read more...

review: Approve
Ted Gould (ted) wrote :

To make sure this merge request tracks these issues as they're slightly
larger. Here's the references.

On Wed, 2012-01-25 at 08:17 +0000, Mikkel Kamstrup Erlandsen wrote:
> You are putting dbusmenu-collector in the dbusmenu namespace. That seems
> like looking for trouble with the dbusmenu maintainer? I heard he doesn't
> go lightly on this ;-) Although, seriously; if you're not planning on
> migrating it to libdbusmenu then I think you should rename the class.

https://code.launchpad.net/~ted/indicator-appmenu/dbusmenu-collector-rename/+merge/90284

> SQL-wise it looks like you want an index on the 'application' column at
> the very least. Maybe a compound index (application,entry,timestamp) or
> just one on 'timestamp' although the queries affected by this are not
> exercised nearly as much as the one that would just use the apps index,
> so the compound index may be overkill.

https://code.launchpad.net/~ted/indicator-appmenu/db-now-with-indexes/+merge/90204

lp:~ted/indicator-appmenu/hud updated on 2012-01-26
181. By Ted Gould on 2012-01-26

Problem with infinite loop and continue, switching to a for loop to stop that.

Tony Espy (awe) wrote :

Ted, here are my comments. Nothing major, mostly style comments.
/t

indicator-tracker.h
-------------------

 * copyright -> 2012

 * why do use G_BEGIN_DECLS? Will this file ever be compiled as C++?

 * In general, I'm used to the style where when declaring a pointer variable, parameter, or struct member,
   the "*" prefixes the name without any whitespace. I'm also used to the same usage for return values,
   but it seems pretty common in Gtk/Glib code to use whitespace in this case...

eg.

struct _IndicatorTrackerIndicator {
        gchar * name;
 gchar * dbus_name;
        gchar * dbus_name_wellknown;
        gchar * dbus_object;
        gchar * prefix;
        gchar * icon;
};

OR

GList * indicator_tracker_get_indicators (IndicatorTracker * tracker);

---

indicator-tracker.c
-------------------

 * copyright -> 2012

 * Do we have in internal style-guide within PS or Systems? Just wondering about things like
   line length ( you have lots of very long lines > 80 chars ), or indentation rules ( tabs vs.
   spaces; you use tabs for instance, whereas recommended Gtk guidelines recommend two spaces ).

   GTK+ style guide dictates two spaces for indentation, and line length <= 80
   ( pretty standard C style ).

 * Same comment as header file re: your usage of whitespace and "*"s for variables, struct members,
   and param names. Your usage is inconsistent across the file.

 * Indentation is messed up: indicator_tracker_class_init().

 * Do you really need to add decls for all of your "static functions"?

 * Any feeling on using gtk-doc style comments for properties, signals, and
   public methods?

 * why the "return" statements at the end of void methods?

 * indicator_tracker_init() -- why do you bother initializing the priv members
   app_indicators, app_proxy_cancel, and app_indicators to NULL, when you initialize
   them to real objects a few lines later?

 * again, a style comment... you declare indicator_cnt in the middle of the block.
   Are C99 style declarations OK?

 * it took me awhile to discover that using ":" in struct initialization was a GNU
   C extension.

 * system_watch_vanished() -- not a big fan of your decrementing 'i' at the end
   of the loop, just to have it incremented in the for loop again. Isn't there
   a better way you can code this ( ie. with maybe a more complex iteration
   for clause then a simple i++ )?

  * l528: could you explain the comment "let's go nuclear"? I *think* what your
    saying is that forcing a name change causes the entire list to be retrieved?

  * l535: why do you initialize the ptrs but not the guint?

  * l593: why do g_warning() like in the following method?

review: Approve (indicatortracker)
Allan LeSage (allanlesage) wrote :

For usage-tracker.c:

Ted did you consider just up-ticking an entry-count, e.g., when it's marked, and then returning the count when found? I assume this complicates your expiration mechanism. I'm told that the count operation is pretty quick so maybe that's fine.

I'm assuming it's no coincidence that you gave me a file to review which has high test-coverage :) . In configure_db if we get an "Error building LRU DB", what happens, and do we really want to continue? I see that these are the non-tested paths.

I should say that I don't feel confident enough yet in gobject to give a larsu- or awe-style critique of your C--I'm informing you so that you can refer this review again as necessary; I assume you'll apply their advice here.

Mikkel's advice to add an index is good advice IMHO but I wonder how much benefit you'd see as this is a dirt-simple db; it would good to add some performance testing for this, e.g.

All the GoogleTest work is really excellent to see. This is barely worth a commit but consider putting the asserted val first in an assertion.

On line 362 I'm seeing what might be a 'TODO', but I'm not sure how you'd load existing data on a fresh build--what does this mean, and do you want to address?

I'm eager to get into the performance testing and filling in the gaps for test; I think items 2 and 6 above are worth addressing for an approval.

review: Needs Information
Charles Kerr (charlesk) wrote :

Review of dbusmenu-collector.

MUST

 * in dbusmenu_collector_dispose(), priv->bus needs to be unreffed and set to NULL (/after/ priv->signal is cleared)

 * remove_underline() needs to be static.

SHOULD

 * as remove_underline()'s TODO comment requested, here's a bulk copy implementation

        static gchar *
        remove_underline (const gchar * input)
        {
                const gunichar underline = g_utf8_get_char("_");
                GString * output = g_string_sized_new (strlen(input)+1);

                const gchar * begin = input;
                const gchar * end;
                while ((end = g_utf8_strchr (begin, -1, underline))) {
                        g_string_append_len (output, begin, end-begin);
                        begin = g_utf8_next_char (end);
                }
                g_string_append (output, begin);
                return g_string_free (output, FALSE);
        }

 * in just_do_it(), results should be declared on the stack and initialized to NULL, not passed in as an argument

 * indicator_name appears to always be NULL everywhere and should be removed unless you have future plans for it

 * in dbusmenu_collector_search(), should add g_return_val_if_fail (IS_DBUSMENU_COLLECTOR(collector), NULL);

 * in menuitem_to_tokens(), pull the PROP_TYPE string once into a local variable, instead of pulling it **9 times** :)

 * in process_client(), no need to walk the menuitem's hashtable twice for DBUSMENU_MENUITEM_PROP_LABEL -- remove the _property_exist() call and add an "if (label == NULL) continue;"

 * in tokens_to_children(), no need to walk the menuitem's hashtable twice for DBUSMENU_MENUITEM_PROP_TYPE -- pull the string to a stack variable, and stack for !type || !g_strcmp0(type, DBUSMENU_CLIENT_TYPES_DEFAULT)

MAY

 * in just_do_it(), having each search failure walk the hashtable for g_debug() smells like overkill?

 * trivial comment typo in update_layout_cb: s/becasue/because/

 * in dbusmenu_collector_found_new(), g_strdup() handles NULL correctly, so there's no need for the "if (foo != NULL)" in "if (foo != NULL) bar = g_strdup (foo);"

 * in tokens_to_children(), a comment explaining why we skip disabled/invisible rootitems would be nice-to-have

 * no glib comment annotation anywhere (example: transfer & element-type in dbusmenu_collector_search())

review: Needs Fixing
Charles Kerr (charlesk) wrote :

Also, I didn't read indicator-tracker.c very closely but did see these items:

 * indicator-sound recently changed signature from "com.canonical.indicators.sound" (note the plural) to "com.canonical.indicator.sound" (note the singular) to follow the other indicators' conventions. You may want to use the new signature?

 * struct _AppIndicator's fields "alert", "alert_name", and "normal_name" appear to be unused.

 * I agree with awe that having a "return;" alone at the end of a void function is kind of weird ;)

Tim Penhey (thumper) wrote :

I have to express my sadness that there is a function called "just_do_it".

Charles Kerr (charlesk) wrote :

More meddling after reading larsu's great review:

 * g_clear_object() could be used in a couple of places in indicator_tracker_dispose() and dbusmenu_collector_dispose(). (Thanks larsu! I didn't know about that function either ;)

 * as per larsu and awe's questions about NULLing out the priv fields right before assigning them... maybe a memset (priv, 0, sizeof(Priv)) would be better idiom. It's only 1 loc, of course, but more importantly, it makes clear the intent of unconditionally zeroing everything.

 * several files include both glib.h and glib-object.h:
     _ dbusmenu-collector.h
     _ distance.c
     _ hud-dbus.h
     _ hud-search.h
     _ hud-verify-app-info.c
     _ indicator-tracker.h
     _ usage-traacker.h
     _ window-menus.h

 * several files have the redundant declarations of _init and _class_init after G_DEFINE_TYPE
     _ dbusmenu-collector.c
     _ hud-dbus.c,
     _ hud-search.c,
     _ indicator-appmenu.c (twice)
     _ indicator-tracker.c
     _ usage-tracker.c
     _ window-menus.c

Tony Espy (awe) wrote :
Download full text (6.9 KiB)

Charles inspired me to ask a few follow-up questions of my own!

 * larsu points out that only <glib.h> is required, not an additional include for <glib-object.h>; I looked at this, and can't quite grok how this chaining works as <glib.h> doesn't appear to pull in any of the GObject headers?

 * Re: the NULLing out priv struct seems to be extra work. Ted, I understand your reasoning, but after looking at the documentation, it appears that the GObject initialization sequence handles NULLing/ZEROing out memory ( ultimately by g_type_create_instance() ). That said, the GObject Reference Manual section on Object Instantiation states:

  "Although one can expect all class instance and members ( except the fields pointing to the parents ) to be set
   to zero, some consider it good practice to explicitly set them."

  I would suggest a simple comment explaining your practice as a few of use have noticed this pattern and commented
  on it being odd. [Charles, I like the memset idea, however it really isn't needed due to the way GObject works.]

 * Regarding line length... the vt130 comment was funny, but not a real justification. I would suggest checking out a few public C/C++ styleguides ( GTK+, Google Style Guides, ... ) for reference, most if not all recommend 80 char lines.

> On Thu, 2012-01-26 at 12:37 +0000, Lars Uebernickel wrote:
> > hud-search.h:
> >
> > * no need to include both glib.h and glib-object.h
>
> Old template :-) Fixed r167
>
> > hud-search.c:
> >
> > * G_DEFINE_TYPE already declares _init and _class_init functions, no need to
> > do it again in line 57-58
>
> Ditto. Fixed r168
>
> > * hud_search_init:
> > - priv members tracker, matcher, window_changed_sig, active_window,
> > collector, usage, and appmenu are initialized to NULL only to be
> > overwritten uncoditionally a few lines down
>
> I actually do that on purpose. And the reason is that I figure that the
> code in init() will change, and perhaps I'd screw up and not define a
> variable or put a conditional in that doesn't work. So the idea is to
> isolate the damage caused by those mistakes. If the mistakes aren't
> made, the compiler will optimize the inefficiencies out anyway.
>
> > - active_app is not initialized at all when active_window is NULL
> > (are private structs initalized to zero when allocating the instance?)
>
> They are, but being explicit for the reasons mentioned above. r169
>
> > * hud_search_dispose:
> > - using g_clear_object would save some lines
>
> Cool! I didn't know about that function. It sure will! r170
>
> > * hud_search_finalize:
> > - can be removed, it only chains up to parent class
>
> Yeah, I typically leave it there for future use. Low runtime cost, huge
> developer savings when you need to put something there :-)
>
> > * hud_search_new:
> > - no need to cast the return value of g_object_new
>
> Fair. r171
>
> > * found_list_to_usage_array:
> > - The loop stops once it finds an item in found_list that has a distance
> > greater than max-distance - is found_list sorted? It doesn't look that
> > way from a quick glance at dbusmenu_collector_search. break ->
> continue?
> ...

Read more...

Lars Karlitski (larsu) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 01/27/2012 03:07 PM, Tony Espy wrote:
> Charles inspired me to ask a few follow-up questions of my own!
>
> * larsu points out that only <glib.h> is required, not an
> additional include for <glib-object.h>; I looked at this, and can't
> quite grok how this chaining works as <glib.h> doesn't appear to
> pull in any of the GObject headers?

Sorry if I wasn't clear: glib-object.h includes glib.h, not the other
way around. (glib itself doesn't depend on gobject)

> [...] * Regarding line length... the vt130 comment was funny, but
> not a real justification. I would suggest checking out a few public
> C/C++ styleguides ( GTK+, Google Style Guides, ... ) for reference,
> most if not all recommend 80 char lines.

In general, I totally agree with a 80 char limit. However, the g*
libraries have an affinity towards fairly long function names, which
can make this hard to enforce sometimes.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJPIrXeAAoJEAbD1bcyW6kFyu8H/0wxSAHY4iEwIRUzhEkvFzFV
f/dkNSdWR9ODyOruDmGs3QIldW1iV3B2rTv4ZxXt3RECVUTsme0tCR7Gea+oU81H
8lQKV0tP2E81g6H5QqOP4o/tDbw3OaFoBIYfDNdCejaRjM1c6fip8kOmtcfl2gdR
PoonUiw8D9BT6gFqo3dFsy+KNmEg1rHRwvjq9GVrkfJXywCylIkz63FIoHQijR7s
nr7hkO/6CG5jAEU9FDcKgTg+KK622WqVWCy7Bupp24LVEtdyuJO3wuYseH2Xe9Kf
WI0pUAODJh3CAp/dydkMHWiVR0L+jXKuKzmoCio8p4L0/Q//GwSuvILISnxysLU=
=de0l
-----END PGP SIGNATURE-----

Tony Espy (awe) wrote :

> > * larsu points out that only <glib.h> is required, not an
> > additional include for <glib-object.h>; I looked at this, and can't
> > quite grok how this chaining works as <glib.h> doesn't appear to
> > pull in any of the GObject headers?
>
> Sorry if I wasn't clear: glib-object.h includes glib.h, not the other
> way around. (glib itself doesn't depend on gobject)

Ah, I didn't think to look at the reverse! Thanks for the clarification!

> > [...] * Regarding line length... the vt130 comment was funny, but
> > not a real justification. I would suggest checking out a few public
> > C/C++ styleguides ( GTK+, Google Style Guides, ... ) for reference,
> > most if not all recommend 80 char lines.
>
> In general, I totally agree with a 80 char limit. However, the g*
> libraries have an affinity towards fairly long function names, which
> can make this hard to enforce sometimes.

Well, I'm certainly up for setting the length longer ( 100? 120? ), as
long as there's an agreed upon convention we all can abide by...

> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.11 (GNU/Linux)
> Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
>
> iQEcBAEBAgAGBQJPIrXeAAoJEAbD1bcyW6kFyu8H/0wxSAHY4iEwIRUzhEkvFzFV
> f/dkNSdWR9ODyOruDmGs3QIldW1iV3B2rTv4ZxXt3RECVUTsme0tCR7Gea+oU81H
> 8lQKV0tP2E81g6H5QqOP4o/tDbw3OaFoBIYfDNdCejaRjM1c6fip8kOmtcfl2gdR
> PoonUiw8D9BT6gFqo3dFsy+KNmEg1rHRwvjq9GVrkfJXywCylIkz63FIoHQijR7s
> nr7hkO/6CG5jAEU9FDcKgTg+KK622WqVWCy7Bupp24LVEtdyuJO3wuYseH2Xe9Kf
> WI0pUAODJh3CAp/dydkMHWiVR0L+jXKuKzmoCio8p4L0/Q//GwSuvILISnxysLU=
> =de0l
> -----END PGP SIGNATURE-----

Charles Kerr (charlesk) wrote :

I'm not terribly worried about the 80 column limit, whitespace, tab damage, etc.

If we want to develop a Systems team "house style" I would like that. But since there's currently not one, IMO it's beyond the scope of this code review.

Ted Gould (ted) wrote :
Download full text (4.3 KiB)

> * copyright -> 2012

Changing to 2011-2012 as most of the file was written in 2011 :-)
r182

> * why do use G_BEGIN_DECLS? Will this file ever be compiled as C++?

No, mostly a template issue. Doesn't hurt/help anything.

> * In general, I'm used to the style where when declaring a pointer variable, parameter, or struct member,
> the "*" prefixes the name without any whitespace. I'm also used to the same usage for return values,
> but it seems pretty common in Gtk/Glib code to use whitespace in this case...
<snip>
> * Do we have in internal style-guide within PS or Systems? Just wondering about things like
> line length ( you have lots of very long lines > 80 chars ), or indentation rules ( tabs vs.
> spaces; you use tabs for instance, whereas recommended Gtk guidelines recommend two spaces ).
>
> GTK+ style guide dictates two spaces for indentation, and line length <= 80
> ( pretty standard C style ).
>
> * Same comment as header file re: your usage of whitespace and "*"s for variables, struct members,
> and param names. Your usage is inconsistent across the file.
>
> * Indentation is messed up: indicator_tracker_class_init().

Uhm, no we don't. And in general, I'm opposed to that. I feel that
people spend too much time worrying about it and not enough time making
their code readable/good. So I have a style that I use, and it's one I
like, but I don't think it's good for everyone.

I once was talking to a guy about why he liked using Python in his
company; his reason "it makes my developers more productive." When I
asked why his reply was: "Since the language specifies the coding style
my developers don't spend all their time arguing about it." ;-)

> * Do you really need to add decls for all of your "static functions"?

Only if they're used before they're defined. I generally don't add a
decl unless I need it. There might be some because of refactoring
though.

> * Any feeling on using gtk-doc style comments for properties, signals, and
> public methods?

In general, I'm only religious about that for stuff that gets exported
to users (libraries). In general, if I'm not generating the docs so
that errors are found, I write in conversational style. I'm flexible
there.

> * why the "return" statements at the end of void methods?

I think it increases clarity because of code highlighting. With the way
my editor is set up I get a big yellow line there which I think is a
good reminder and sets up a certain balance in the function.

I'm always surprised how controversial this is :-) Cody and I argued
about this constantly.

> * indicator_tracker_init() -- why do you bother initializing the priv members
> app_indicators, app_proxy_cancel, and app_indicators to NULL, when you initialize
> them to real objects a few lines later?

Just to be clear that they are initialized. I figure that if later I
change the code and mistakenly don't do this, it reduces the chance of
error.

> * again, a style comment... you declare indicator_cnt in the middle of the block.
> Are C99 style declarations OK?

I'm okay with it. I think it increases the number of errors that the
compiler can catch, pl...

Read more...

lp:~ted/indicator-appmenu/hud updated on 2012-01-27
182. By Ted Gould on 2012-01-27

Updating copyright

183. By Ted Gould on 2012-01-27

Initializing position

184. By Ted Gould on 2012-01-27

Warn if an invalid item is removed

Ted Gould (ted) wrote :

>For usage-tracker.c:
>
> Ted did you consider just up-ticking an entry-count, e.g., when it's
> marked, and then returning the count when found? I assume this
> complicates your expiration mechanism. I'm told that the count
> operation is pretty quick so maybe that's fine.

Yeah, I was figuring that not just asking the database was a premature
optimization. I think we can definitely cache that value if it proves
to be something we need. Initially, I was thinking that apps might load
their values on install, but I like the lazy loading mechanism that's
there now so that's no longer an issue. But, yes, performance testing
should guide our decision there.

> I'm assuming it's no coincidence that you gave me a file to review
> which has high test-coverage :) . In configure_db if we get an "Error
> building LRU DB", what happens, and do we really want to continue? I
> see that these are the non-tested paths.

I added checks on the public functions to ensure we have a database. I
think that perhaps we should see if we can't make a file DB, switch to
an in-memory one. Not sure on that, but since it could fail as well, we
need to handle the DB being missing. r185

> Mikkel's advice to add an index is good advice IMHO but I wonder how
> much benefit you'd see as this is a dirt-simple db; it would good to
> add some performance testing for this, e.g.

Yeah, if you look at that branch I made it easier to adjust that. I'm
not sure how to do that performance testing, but I'm open to suggestions
there :-)

> All the GoogleTest work is really excellent to see. This is barely
> worth a commit but consider putting the asserted val first in an
> assertion.

Hmm, I guess my feeling is that "readable" is good here. I'd say
"variable is 5" not "5 is variable". Why do you think the value should
be first? Just to be easier to find?

> On line 362 I'm seeing what might be a 'TODO', but I'm not sure how
> you'd load existing data on a fresh build--what does this mean, and do
> you want to address?

Actually it was before I added the lazy loading of app data scheme.
Removed the comment as it's dated. r186

lp:~ted/indicator-appmenu/hud updated on 2012-01-27
185. By Ted Gould on 2012-01-27

Adding checks to ensure we have a database

186. By Ted Gould on 2012-01-27

Remove dated comment

187. By Ted Gould on 2012-01-27

Using the clear object function in usage tracker

188. By Ted Gould on 2012-01-27

Using clear object in indicator tracker

Allan LeSage (allanlesage) :
review: Approve
Allan LeSage (allanlesage) wrote :

Ted, a quick response to the 'ordering of the assertion' point above: I agree that it's more readable as "variable is 5", however the desired_result is often buried after a long evaluation, and yes it's deemed cleaner to find that result in a predictable place in the line (or so says Kent Beck fwiw).

Ted Gould (ted) wrote :

> * in dbusmenu_collector_dispose(), priv->bus needs to be unreffed and
> set to NULL (/after/ priv->signal is cleared)

Fixed r189

> * remove_underline() needs to be static.

Fixed r190

> * as remove_underline()'s TODO comment requested, here's a bulk copy
> implementation

Cool, thanks! r191

> * in just_do_it(), results should be declared on the stack and
> initialized to NULL, not passed in as an argument

Ah, refactoring debt. It used to be just_do_it()'s got chained
together. Also cleaned up indicator_name which is unused for the same
reason. Fixed r192

> * indicator_name appears to always be NULL everywhere and should be
> removed unless you have future plans for it

Oh, should have read this before that last commit :-)

> * in dbusmenu_collector_search(), should add g_return_val_if_fail
> (IS_DBUSMENU_COLLECTOR(collector), NULL);

Fixed. r193

> * in menuitem_to_tokens(), pull the PROP_TYPE string once into a
> local variable, instead of pulling it **9 times** :)

Heh, cut-and-paste got out of hand :-) r194

> * in process_client(), no need to walk the menuitem's hashtable twice
> for DBUSMENU_MENUITEM_PROP_LABEL -- remove the _property_exist() call
> and add an "if (label == NULL) continue;"

Actually the label will never be NULL. If there isn't a value the
default value will be given. Which in this case will be "Label Empty".
This means that most dbusmenu code needs to not worry about whether a
core property is set or not, they'll just get a value that they can use.
And defaults are never sent over the bus.

> * in tokens_to_children(), no need to walk the menuitem's hashtable
> twice for DBUSMENU_MENUITEM_PROP_TYPE -- pull the string to a stack
> variable, and stack for !type || !g_strcmp0(type,
> DBUSMENU_CLIENT_TYPES_DEFAULT)

Same basic reason. But, we could probably handle that here... not sure
that I don't want to use it just because it keeps the pattern the same
everywhere...

> * in just_do_it(), having each search failure walk the hashtable for
> g_debug() smells like overkill?

Yeah, but it really should never happen. So if it does, we need some
sort of debugging information. Really, *never*.

> * trivial comment typo in update_layout_cb: s/becasue/because/

Fixed r195

> * in dbusmenu_collector_found_new(), g_strdup() handles NULL
> correctly, so there's no need for the "if (foo != NULL)" in "if (foo !
> = NULL) bar = g_strdup (foo);"

Okay, I think we deleted that code with the indicator_name change above
anyway.

> * in tokens_to_children(), a comment explaining why we skip
> disabled/invisible rootitems would be nice-to-have
>
> * no glib comment annotation anywhere (example: transfer &
> element-type in dbusmenu_collector_search())

Fixed r196

lp:~ted/indicator-appmenu/hud updated on 2012-01-27
189. By Ted Gould on 2012-01-27

Unref the bus and clear search_settings

190. By Ted Gould on 2012-01-27

Make remove_underline static

191. By Charles Kerr on 2012-01-27

Implement a bulk copy for removing the underline

192. By Ted Gould on 2012-01-27

Cleaned up indicator-name and results chaining as they're not used now that indicators are no longer handled in the collector

193. By Ted Gould on 2012-01-27

Checking the object before searching

194. By Ted Gould on 2012-01-27

Getting the type only once for the whole function

195. By Ted Gould on 2012-01-27

Typo

196. By Ted Gould on 2012-01-27

Adding some comments in the code to describe the search function and which items we use

Ted Gould (ted) wrote :

> * indicator-sound recently changed signature from
> "com.canonical.indicators.sound" (note the plural) to
> "com.canonical.indicator.sound" (note the singular) to follow the
> other indicators' conventions. You may want to use the new signature?

Added both, will delete as indicator-sound gets shipped wider. r197

> * struct _AppIndicator's fields "alert", "alert_name", and
> "normal_name" appear to be unused.

Future use :-)

> * I agree with awe that having a "return;" alone at the end of a void
> function is kind of weird ;)

I think not having it is weird :-)

lp:~ted/indicator-appmenu/hud updated on 2012-01-27
197. By Ted Gould on 2012-01-27

Add in the new indicator sound paths and menus

Ted Gould (ted) wrote :

> * several files include both glib.h and glib-object.h:
> _ dbusmenu-collector.h
> _ distance.c
> _ hud-dbus.h
> _ hud-search.h
> _ hud-verify-app-info.c
> _ indicator-tracker.h
> _ usage-traacker.h
> _ window-menus.h

Okay, so this is what I'm using to detect that:

grep glib-object.h *.[ch] | cut -d ":" -f 1 | sort -u | xargs grep glib.h | cut -d ":" -f 1

We could add this to make check :-)

Fixed r198

> * several files have the redundant declarations of _init and _class_init after G_DEFINE_TYPE
> _ dbusmenu-collector.c
> _ hud-dbus.c,
> _ hud-search.c,
> _ indicator-appmenu.c (twice)
> _ indicator-tracker.c
> _ usage-tracker.c
> _ window-menus.c

Fixed r199

lp:~ted/indicator-appmenu/hud updated on 2012-01-27
198. By Ted Gould on 2012-01-27

Removing extra includes of glib.h

199. By Ted Gould on 2012-01-27

Removing extra prototypes

Ted Gould (ted) wrote :

> * Regarding line length... the vt130 comment was funny, but not a
> real justification. I would suggest checking out a few public C/C++
> styleguides ( GTK+, Google Style Guides, ... ) for reference, most if
> not all recommend 80 char lines.

I would say that those people need to justify why they've set it, and
I'd guess it is more historical than meeting modern requirements. Today
a basic text editor can choose to wrap or not, and when to wrap. So for
instance in VIM I'll use:

 :se wrap
 :se nowrap

To change how I'm viewing the code. Same with collapsing deeply nested
code. I have my browser set to auto collapse[1]. Sometimes seeing the
full line is useful, sometimes it's more clear to not see it. But, I
believe this is a "user preference" not a way that the code should be
written. If you write it making a choice for the user, they can't undo
it easily. But if you have long lines, you're giving them a choice on
how they view the code to optimize for their goals.

[1] For the VIM people, what I use is:
  set foldmethod=syntax
  set foldlevelstart=3

Charles Kerr (charlesk) wrote :

Ted, looks good.

review: Approve
Tony Espy (awe) wrote :

> > * Regarding line length... the vt130 comment was funny, but not a
> > real justification. I would suggest checking out a few public C/C++
> > styleguides ( GTK+, Google Style Guides, ... ) for reference, most if
> > not all recommend 80 char lines.
>
> I would say that those people need to justify why they've set it, and
> I'd guess it is more historical than meeting modern requirements.

Check out some of the style guides I referenced. If you'd like an example
of another important current project that means a lot to Ubuntu:

http://www.kernel.org/doc/Documentation/CodingStyle

Yes, modern editors have flexibility to wrap lines, however as I ( and others )
pointed out, read-ability still suffers.

> If you write it making a choice for the user, they can't undo
> it easily. But if you have long lines, you're giving them a choice on
> how they view the code to optimize for their goals.

Code is not written for the sake of the user. It's written to perform a
task, or set of tasks, and be maintainable at the same time.

This is not about freedom for the user, it's about making it easy for
developers to collaborate on code. When a group of people work on common code,
style conventions like line length, tabs vs. spaces, variable naming conventions,
..., help keep the code maintainable.

This is the last I'll say on the issue...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2012-01-12 11:54:26 +0000
3+++ .bzrignore 2012-01-27 17:10:57 +0000
4@@ -23,4 +23,148 @@
5 tools/.libs
6 src/application-menu-renderer-client.h
7 src/application-menu-renderer-server.h
8+test-usage-db-simple
9+test-usage-db-simple-test
10+test-usage-db-simple.xml
11+test-usage-db-old
12+test-usage-db-old-test
13+test-usage-db-old.xml
14+test-usage-db-ancient
15+test-usage-db-ancient-test
16+test-indicator-tracker
17+test-indicator-tracker-owner
18+test-indicator-tracker-test
19+ABOUT-NLS
20+config.rpath
21+hud-[0-9]*.[0-9]*.[0-9]*.tar.gz
22+m4
23+data/com.canonical.hud.service
24+po/Makevars.template
25+po/Rules-quot
26+po/boldquot.sed
27+po/en@boldquot.header
28+po/en@quot.header
29+po/insert-header.sin
30+po/quot.sed
31+po/remove-potcdate.sin
32+src/.deps
33+src/.libs
34+src/hud-cli
35+src/hud-service
36+src/hud.interface.c
37+src/hud.interface.h
38+tests/.deps
39+tests/.libs
40+tests/test-distance
41+tests/test-distance-test
42+tests/test-distance.xml
43+po/hud.pot
44+hud-dump-application
45+hud-verify-app-info
46+test-bad-app-info-dual-header
47+test-bad-app-info-item-no-count
48+test-bad-app-info-item-no-name
49+test-bad-app-info-menu-no-name
50+test-bad-app-info-missing-desktop
51+test-bad-app-info-missing-menus
52+test-bad-app-info
53+test-good-app-info-tons-of-entries
54+test-load-app-info
55+test-usage-db-testapp
56+test-usage-db-testapp-test
57+test-usage-db-testapp.xml
58+test-bad-app-info-multiple-menus
59 data/com.canonical.indicator.appmenu.gschema.valid
60+com.canonical.indicator.appmenu.hud.gschema.valid
61+com.canonical.indicator.appmenu.gschema.xml
62+com.canonical.indicator.appmenu.hud.gschema.xml
63+com.canonical.indicator.appmenu.hud.search.gschema.xml
64+com.canonical.indicator.appmenu.hud.search.gschema.valid
65+docs/man/hud-cli.1
66+docs/man/hud-dump-application.1
67+docs/man/hud-list-applications.1
68+docs/man/hud-verify-app-info.1
69+m4/codeset.m4
70+m4/gettext.m4
71+m4/glibc2.m4
72+m4/glibc21.m4
73+m4/gtk-doc.m4
74+m4/iconv.m4
75+m4/intdiv0.m4
76+m4/intl.m4
77+m4/intldir.m4
78+m4/intlmacosx.m4
79+m4/intmax.m4
80+m4/inttypes-pri.m4
81+m4/inttypes_h.m4
82+m4/lcmessage.m4
83+m4/lib-ld.m4
84+m4/lib-link.m4
85+m4/lib-prefix.m4
86+m4/lock.m4
87+m4/longlong.m4
88+m4/nls.m4
89+m4/po.m4
90+m4/printf-posix.m4
91+m4/progtest.m4
92+m4/size_max.m4
93+m4/stdint_h.m4
94+m4/uintmax_t.m4
95+m4/visibility.m4
96+m4/wchar_t.m4
97+m4/wint_t.m4
98+m4/xsize.m4
99+src/gen-application-menu-registrar.xml.c
100+src/gen-application-menu-registrar.xml.h
101+src/gen-application-menu-renderer.xml.c
102+src/gen-application-menu-renderer.xml.h
103+src/libappmenu_la-gen-application-menu-registrar.xml.lo
104+src/libappmenu_la-gen-application-menu-renderer.xml.lo
105+coverage.info
106+coveragereport
107+src/hud_cli-hud-cli.gcno
108+src/hud_dump_application-dump-app-info.gcno
109+src/hud_dump_application-hud-dump-application.gcno
110+src/hud_service-dbusmenu-collector.gcno
111+src/hud_service-distance.gcno
112+src/hud_service-dump-app-info.gcno
113+src/hud_service-hud-dbus.gcno
114+src/hud_service-hud-search.gcno
115+src/hud_service-hud-service.gcno
116+src/hud_service-indicator-tracker.gcno
117+src/hud_service-load-app-info.gcno
118+src/hud_service-usage-tracker.gcno
119+src/hud_service-utils.gcno
120+src/hud_verify_app_info-hud-verify-app-info.gcno
121+src/hud_verify_app_info-load-app-info.gcno
122+src/libappmenu_la-gdk-get-func.gcno
123+src/libappmenu_la-indicator-appmenu-marshal.gcno
124+src/libappmenu_la-indicator-appmenu.gcno
125+src/libappmenu_la-window-menus.gcno
126+tests/test-app-indicator
127+tests/test-app-indicator-test
128+tests/test-app-indicator-tracker
129+tests/test-usage-db-off-test
130+tests/test-usage-db-off.xml
131+tests/test_app_indicator-test-app-indicator.gcda
132+tests/test_app_indicator-test-app-indicator.gcno
133+tests/test_app_indicator_tracker-test-app-indicator-tracker.gcda
134+tests/test_app_indicator_tracker-test-app-indicator-tracker.gcno
135+tests/test_bad_app_info-test-bad-app-info.gcda
136+tests/test_bad_app_info-test-bad-app-info.gcno
137+tests/test_distance-test-distance.gcda
138+tests/test_distance-test-distance.gcno
139+tests/test_indicator_tracker-test-indicator-tracker.gcda
140+tests/test_indicator_tracker-test-indicator-tracker.gcno
141+tests/test_indicator_tracker_owner-test-indicator-tracker-owner.gcda
142+tests/test_indicator_tracker_owner-test-indicator-tracker-owner.gcno
143+tests/test_load_app_info-test-load-app-info.gcda
144+tests/test_load_app_info-test-load-app-info.gcno
145+tests/test_usage_db_ancient-test-usage-db-ancient.gcda
146+tests/test_usage_db_ancient-test-usage-db-ancient.gcno
147+tests/test_usage_db_old-test-usage-db-old.gcda
148+tests/test_usage_db_old-test-usage-db-old.gcno
149+tests/test_usage_db_simple-test-usage-db-simple.gcda
150+tests/test_usage_db_simple-test-usage-db-simple.gcno
151+tests/test_usage_db_testapp-test-usage-db-testapp.gcda
152+tests/test_usage_db_testapp-test-usage-db-testapp.gcno
153
154=== modified file 'Makefile.am'
155--- Makefile.am 2012-01-12 11:52:48 +0000
156+++ Makefile.am 2012-01-27 17:10:57 +0000
157@@ -1,14 +1,20 @@
158+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
159
160 SUBDIRS = \
161+ data \
162+ po \
163 src \
164- data
165+ docs
166
167 if BUILD_TESTS
168 SUBDIRS += \
169 scripts \
170+ tests \
171 tools
172 endif
173
174+tests: src
175+
176 DISTCHECK_CONFIGURE_FLAGS = --enable-localinstall --enable-deprecations
177
178 dist-hook:
179
180=== modified file 'autogen.sh'
181--- autogen.sh 2010-04-19 17:29:36 +0000
182+++ autogen.sh 2012-01-27 17:10:57 +0000
183@@ -9,4 +9,4 @@
184
185 USE_GNOME2_MACROS=1 \
186 USE_COMMON_DOC_BUILD=yes \
187-gnome-autogen.sh --enable-gtk-doc $@
188+. gnome-autogen.sh --enable-gtk-doc $@
189
190=== modified file 'configure.ac'
191--- configure.ac 2012-01-12 11:52:48 +0000
192+++ configure.ac 2012-01-27 17:10:57 +0000
193@@ -1,24 +1,29 @@
194+AC_INIT([indicator-appmenu],
195+ [0.3.2+hud5],
196+ [http://bugs.launchpad.net/indicator-appmenu],
197+ [indicator-appmenu],
198+ [http://launchpad.net/indicator-appmenu])
199
200-AC_INIT(indicator-appmenu, 0.3.2, ted@canonical.com)
201 AC_COPYRIGHT([Copyright 2010 Canonical])
202
203 AC_PREREQ(2.53)
204
205 AM_CONFIG_HEADER(config.h)
206-AM_INIT_AUTOMAKE(indicator-appmenu, 0.3.2)
207-
208-AM_MAINTAINER_MODE
209-
210-AC_ISC_POSIX
211+AC_CONFIG_SRCDIR([configure.ac])
212+AC_CONFIG_MACRO_DIR([m4])
213+
214+AM_INIT_AUTOMAKE([1.11 -Wall foreign subdir-objects])
215+AM_MAINTAINER_MODE([enable])
216+
217+AM_SILENT_RULES([yes])
218+
219+# Check for programs
220 AC_PROG_CC
221 AM_PROG_CC_C_O
222-AC_STDC_HEADERS
223-AC_PROG_LIBTOOL
224-
225-AC_SUBST(VERSION)
226-AC_CONFIG_MACRO_DIR([m4])
227-
228-m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
229+
230+# Initialize libtool
231+LT_PREREQ([2.2.6])
232+LT_INIT
233
234 AC_PATH_PROG([GLIB_MKENUMS], [glib-mkenums])
235 AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal])
236@@ -85,10 +90,38 @@
237 AC_SUBST(INDICATOR_LIBS)
238
239 ###########################
240+# Dependencies
241+###########################
242+
243+DBUSMENU_GLIB_REQUIRED_VERSION=0.4.0
244+BAMF_REQUIRED_VERSION=0.2.53
245+SQLITE_REQUIRED_VERSION=0.0
246+
247+PKG_CHECK_MODULES([HUD],[
248+ dbusmenu-glib-0.4 >= $DBUSMENU_GLIB_REQUIRED_VERSION
249+ libbamf3 >= $BAMF_REQUIRED_VERSION
250+ sqlite3 >= $SQLITE_REQUIRED_VERSION
251+])
252+
253+###########################
254+# CLI Dependencies
255+###########################
256+
257+AC_CHECK_HEADER("readline/readline.h",
258+ [has_readline_h="yes"])
259+AC_CHECK_HEADER("readline/history.h",
260+ [has_history_h="yes"])
261+AC_CHECK_HEADER("curses.h",
262+ [has_curses_h="yes"])
263+
264+AM_CONDITIONAL([BUILD_CLI], [test "x$has_readline_h" == "xyes" && test "x$has_history_h" == "xyes" && test "x$has_curses_h" == "xyes"]),
265+
266+###########################
267 # Test Dependencies
268 ###########################
269
270 DBUSMENU_JSONLOADER_REQUIRED_VERSION=0.3.3
271+LIBAPPINDICATOR_REQUIRED_VERSION=0.0.0
272
273 AC_ARG_ENABLE([tests],
274 AC_HELP_STRING([--disable-tests], [Disable test scripts and tools]),,
275@@ -96,7 +129,8 @@
276
277 if test x"$enable_tests" != x"no" ; then
278 PKG_CHECK_MODULES(INDICATORTEST,
279- dbusmenu-jsonloader-0.4 >= $DBUSMENU_JSONLOADER_REQUIRED_VERSION,
280+ dbusmenu-jsonloader-0.4 >= $DBUSMENU_JSONLOADER_REQUIRED_VERSION
281+ appindicator3-0.1 >= $LIBAPPINDICATOR_REQUIRED_VERSION,
282 [have_dbusmenu_jsonloader=yes],
283 [have_dbusmenu_jsonloader=no]
284 )
285@@ -162,6 +196,37 @@
286 fi
287 AC_SUBST(DBUSSERVICEDIR)
288
289+##############################
290+# Custom Junk
291+##############################
292+
293+AC_DEFUN([AC_DEFINE_PATH], [
294+ test "x$prefix" = xNONE && prefix="$ac_default_prefix"
295+ test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
296+ ac_define_path=`eval echo [$]$2`
297+ ac_define_path=`eval echo [$]ac_define_path`
298+ $1="$ac_define_path"
299+ AC_SUBST($1)
300+ ifelse($3, ,
301+ AC_DEFINE_UNQUOTED($1, "$ac_define_path"),
302+ AC_DEFINE_UNQUOTED($1, "$ac_define_path", $3))
303+])
304+
305+###########################
306+# Internationalization
307+###########################
308+
309+GETTEXT_PACKAGE=indicator-appmenu
310+
311+IT_PROG_INTLTOOL([0.41.0])
312+
313+AM_GNU_GETTEXT([external])
314+AM_GNU_GETTEXT_VERSION([0.17])
315+
316+AC_SUBST(GETTEXT_PACKAGE)
317+AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE", [Define to the gettext package name.])
318+AC_DEFINE_PATH(GNOMELOCALEDIR, "${datadir}/locale", [locale directory])
319+
320 ###########################
321 # Files
322 ###########################
323@@ -172,6 +237,10 @@
324 scripts/Makefile
325 data/Makefile
326 tools/Makefile
327+po/Makefile.in
328+tests/Makefile
329+docs/Makefile
330+docs/man/Makefile
331 ])
332
333 ###########################
334@@ -185,5 +254,6 @@
335 Prefix: $prefix
336 Indicator Dir: $INDICATORDIR
337 gcov: $use_gcov
338+ Local Install: $with_localinstall
339 Test tools: $have_dbusmenu_jsonloader
340 ])
341
342=== modified file 'data/Makefile.am'
343--- data/Makefile.am 2012-01-12 13:47:47 +0000
344+++ data/Makefile.am 2012-01-27 17:10:57 +0000
345@@ -1,9 +1,27 @@
346
347+gsettings_in_files = \
348+ com.canonical.indicator.appmenu.gschema.xml.in \
349+ com.canonical.indicator.appmenu.hud.gschema.xml.in \
350+ com.canonical.indicator.appmenu.hud.search.gschema.xml.in
351+
352 gsettings_SCHEMAS = \
353- com.canonical.indicator.appmenu.gschema.xml
354+ $(gsettings_in_files:.xml.in=.xml)
355
356+@INTLTOOL_XML_NOMERGE_RULE@
357 @GSETTINGS_RULES@
358
359-EXTRA_DIST = $(gsettings_SCHEMAS)
360-
361-CLEANFILES = $(gsettings_SCHEMAS:.xml=.valid)
362+dbus_servicesdir = $(DBUSSERVICEDIR)
363+dbus_services_DATA = com.canonical.hud.service
364+
365+%.service: %.service.in
366+ sed -e "s|\@libexecdir\@|$(libexecdir)|" $< > $@
367+
368+EXTRA_DIST = \
369+ $(dbus_services_DATA:.service=.service.in) \
370+ $(gsettings_in_files)
371+
372+CLEANFILES = \
373+ $(dbus_services_DATA) \
374+ $(gsettings_SCHEMAS:.xml=.valid) \
375+ $(gsettings_SCHEMAS)
376+
377
378=== added file 'data/com.canonical.hud.service.in'
379--- data/com.canonical.hud.service.in 1970-01-01 00:00:00 +0000
380+++ data/com.canonical.hud.service.in 2012-01-27 17:10:57 +0000
381@@ -0,0 +1,3 @@
382+[D-BUS Service]
383+Name=com.canonical.hud
384+Exec=@libexecdir@/hud-service
385
386=== renamed file 'data/com.canonical.indicator.appmenu.gschema.xml' => 'data/com.canonical.indicator.appmenu.gschema.xml.in'
387--- data/com.canonical.indicator.appmenu.gschema.xml 2012-01-12 11:54:09 +0000
388+++ data/com.canonical.indicator.appmenu.gschema.xml.in 2012-01-27 17:10:57 +0000
389@@ -6,10 +6,10 @@
390 <schema id="com.canonical.indicator.appmenu" path="/com/canonical/indicator/appmenu/" gettext-domain="indicator-appmenu">
391 <key name="menu-mode" enum="menu-enum">
392 <default>'global'</default>
393- <summary>Where the menus displayed</summary>
394- <description>
395+ <_summary>Where the menus displayed</_summary>
396+ <_description>
397 Controls the menu display location. TODO: add more
398- </description>
399+ </_description>
400 </key>
401 </schema>
402 </schemalist>
403
404=== added file 'data/com.canonical.indicator.appmenu.hud.gschema.xml.in'
405--- data/com.canonical.indicator.appmenu.hud.gschema.xml.in 1970-01-01 00:00:00 +0000
406+++ data/com.canonical.indicator.appmenu.hud.gschema.xml.in 2012-01-27 17:10:57 +0000
407@@ -0,0 +1,14 @@
408+<schemalist>
409+ <schema id="com.canonical.indicator.appmenu.hud" path="/com/canonical/indicator/appmenu/hud/" gettext-domain="indicator-appmenu">
410+ <key name="store-usage-data" type="b">
411+ <default>true</default>
412+ <_summary>Whether to store usage data</_summary>
413+ <_description>
414+ When the HUD executes operations it stores the execution in
415+ order to make the future results better. Some users could choose
416+ to not want this data to be stored. If that is the case they
417+ should disable this property.
418+ </_description>
419+ </key>
420+ </schema>
421+</schemalist>
422
423=== added file 'data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in'
424--- data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in 1970-01-01 00:00:00 +0000
425+++ data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in 2012-01-27 17:10:57 +0000
426@@ -0,0 +1,90 @@
427+<schemalist>
428+ <schema id="com.canonical.indicator.appmenu.hud.search" path="/com/canonical/indicator/appmenu/hud/search/" gettext-domain="indicator-appmenu">
429+ <key name="indicator-penalty" type="u">
430+ <default>50</default>
431+ <_summary>The penalty given to a menu item being in an indicator</_summary>
432+ <_description>
433+ In order to have the application's menu items appear higher
434+ in the search results a slight penalty is given to the indicator
435+ menu items. This value represents then percentage of that peanlty
436+ so a value of '50' is a 50% additional to the calculated distance.
437+ </_description>
438+ </key>
439+ <key name="add-penalty" type="u">
440+ <default>10</default>
441+ <_summary>Penalty for extra characters added to the search</_summary>
442+ <_description>
443+ Penalty applied for extra characters that are added on the end of
444+ the string that are not related to the source string.
445+ </_description>
446+ </key>
447+ <key name="add-penalty-pre" type="u">
448+ <default>1</default>
449+ <_summary>Penalty for extra characters added to the search before the length of the search string</_summary>
450+ <_description>
451+ Extra characters that occur before the length of the search string
452+ could cover them get this penalty. In the case of "return" and
453+ a search string of "turn" the 'r' and the 'e' would get hit with
454+ this penalty.
455+ </_description>
456+ </key>
457+ <key name="drop-penalty" type="u">
458+ <default>10</default>
459+ <_summary>Penalty applied if a character is dropped</_summary>
460+ <_description>
461+ The penalty applied for characters that were dropped off the end
462+ of the search string compared to the tested string. This is mostly
463+ used for seeding and creating a max gradient on the edge of the
464+ matrix, as mostly dropped single characters are covered by the
465+ 'drop-penalty-end' value.
466+ </_description>
467+ </key>
468+ <key name="drop-penalty-end" type="u">
469+ <default>10</default>
470+ <_summary>Penalty applied if a character is dropped after the search string length</_summary>
471+ <_description>
472+ Drop penalty that is added to characters that are longer than the
473+ current search string. So for a search string of "add" and a source
474+ string of "added" there would be drop end penalties for both 'e' and
475+ 'd'.
476+ </_description>
477+ </key>
478+ <key name="transpose-penalty" type="u">
479+ <default>10</default>
480+ <_summary>Penalty applied when characters are transposed</_summary>
481+ <_description>
482+ When two characters are transposed this penalty is applied. This is
483+ for values like "ea" when the correct value is "ae". This penalty should
484+ be less than twice the swap penalty to have any effect.
485+ </_description>
486+ </key>
487+ <key name="swap-penalty" type="u">
488+ <default>10</default>
489+ <_summary>Penalty applied when the characters are not the same</_summary>
490+ <_description>
491+ When two characters don't match at all this is the penalty
492+ that is given. This should probably be the highest penalty
493+ in the set of all the values.
494+ </_description>
495+ </key>
496+ <key name="swap-penalty-case" type="u">
497+ <default>1</default>
498+ <_summary>The swap penalty when the swap is a case change</_summary>
499+ <_description>
500+ While not strictly a swap, this value allows for penalizing
501+ the search slightly when the only difference between the
502+ characters is in its case. For instance, 'c' vs. 'C'.
503+ </_description>
504+ </key>
505+ <key name="max-distance" type="u">
506+ <default>30</default>
507+ <_summary>The highest distance value that is shown in the results</_summary>
508+ <_description>
509+ After the distances are calculated (including the indicator
510+ penalty) then all values above this max are dropped. This
511+ means that the history for those entries aren't looked up
512+ as well.
513+ </_description>
514+ </key>
515+ </schema>
516+</schemalist>
517
518=== added directory 'docs'
519=== added file 'docs/HUD Architecture.svg'
520--- docs/HUD Architecture.svg 1970-01-01 00:00:00 +0000
521+++ docs/HUD Architecture.svg 2012-01-27 17:10:57 +0000
522@@ -0,0 +1,368 @@
523+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
524+<!-- Created with Inkscape (http://www.inkscape.org/) -->
525+
526+<svg
527+ xmlns:dc="http://purl.org/dc/elements/1.1/"
528+ xmlns:cc="http://creativecommons.org/ns#"
529+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
530+ xmlns:svg="http://www.w3.org/2000/svg"
531+ xmlns="http://www.w3.org/2000/svg"
532+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
533+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
534+ width="706.61322"
535+ height="774.54626"
536+ id="svg2"
537+ version="1.1"
538+ inkscape:version="0.48.2 r9819"
539+ sodipodi:docname="HUD Architecture.svg">
540+ <defs
541+ id="defs4">
542+ <inkscape:path-effect
543+ effect="spiro"
544+ id="path-effect4218"
545+ is_visible="true" />
546+ <inkscape:path-effect
547+ effect="spiro"
548+ id="path-effect4214"
549+ is_visible="true" />
550+ <inkscape:path-effect
551+ effect="spiro"
552+ id="path-effect4210"
553+ is_visible="true" />
554+ <inkscape:path-effect
555+ effect="spiro"
556+ id="path-effect4206"
557+ is_visible="true" />
558+ <inkscape:path-effect
559+ effect="spiro"
560+ id="path-effect4054"
561+ is_visible="true" />
562+ <inkscape:path-effect
563+ effect="spiro"
564+ id="path-effect3863"
565+ is_visible="true" />
566+ <inkscape:path-effect
567+ effect="spiro"
568+ id="path-effect3829"
569+ is_visible="true" />
570+ <inkscape:path-effect
571+ effect="spiro"
572+ id="path-effect3825"
573+ is_visible="true" />
574+ </defs>
575+ <sodipodi:namedview
576+ id="base"
577+ pagecolor="#ffffff"
578+ bordercolor="#666666"
579+ borderopacity="1.0"
580+ inkscape:pageopacity="0.0"
581+ inkscape:pageshadow="2"
582+ inkscape:zoom="1.1071895"
583+ inkscape:cx="232.7327"
584+ inkscape:cy="382.51953"
585+ inkscape:document-units="px"
586+ inkscape:current-layer="layer1"
587+ showgrid="false"
588+ inkscape:showpageshadow="false"
589+ inkscape:window-width="1680"
590+ inkscape:window-height="1026"
591+ inkscape:window-x="0"
592+ inkscape:window-y="24"
593+ inkscape:window-maximized="1"
594+ fit-margin-top="50"
595+ fit-margin-left="50"
596+ fit-margin-right="50"
597+ fit-margin-bottom="50" />
598+ <metadata
599+ id="metadata7">
600+ <rdf:RDF>
601+ <cc:Work
602+ rdf:about="">
603+ <dc:format>image/svg+xml</dc:format>
604+ <dc:type
605+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
606+ <dc:title />
607+ </cc:Work>
608+ </rdf:RDF>
609+ </metadata>
610+ <g
611+ inkscape:label="Layer 1"
612+ inkscape:groupmode="layer"
613+ id="layer1"
614+ transform="translate(-153.88477,-277.83542)">
615+ <rect
616+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
617+ id="rect2985"
618+ width="192.93913"
619+ height="104.04572"
620+ x="241.00642"
621+ y="328.33542" />
622+ <rect
623+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
624+ id="rect2985-3"
625+ width="192.93913"
626+ height="104.04572"
627+ x="245.62126"
628+ y="558.84198" />
629+ <rect
630+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
631+ id="rect2985-9"
632+ width="192.93913"
633+ height="104.04572"
634+ x="586.43182"
635+ y="384.48648" />
636+ <rect
637+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
638+ id="rect2985-7"
639+ width="192.93913"
640+ height="104.04572"
641+ x="617.05884"
642+ y="561.62695" />
643+ <flowRoot
644+ xml:space="preserve"
645+ id="flowRoot3805"
646+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Bumped BRK;-inkscape-font-specification:Bumped BRK"><flowRegion
647+ id="flowRegion3807"><rect
648+ id="rect3809"
649+ width="187.07358"
650+ height="95.192307"
651+ x="242.53345"
652+ y="43.193981"
653+ style="font-family:Bumped BRK;-inkscape-font-specification:Bumped BRK" /></flowRegion><flowPara
654+ id="flowPara3811" /></flowRoot> <flowRoot
655+ xml:space="preserve"
656+ id="flowRoot3813"
657+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
658+ transform="translate(0,310.93426)"><flowRegion
659+ id="flowRegion3815"><rect
660+ id="rect3817"
661+ width="175.48492"
662+ height="46.354519"
663+ x="249.15552"
664+ y="52.299332"
665+ style="text-align:center;text-anchor:middle"
666+ ry="0"
667+ rx="0" /></flowRegion><flowPara
668+ id="flowPara3819"
669+ style="font-weight:bold;-inkscape-font-specification:Ubuntu Bold">Unity</flowPara><flowPara
670+ id="flowPara3821"
671+ style="font-weight:bold;-inkscape-font-specification:Ubuntu Bold">GUI</flowPara></flowRoot> <path
672+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
673+ d="m 449.47325,302.28261 c 29.69243,-2.03406 58.53652,-14.78861 80.02111,-35.38449 10.47599,-10.04266 19.17966,-21.79342 29.4484,-32.0479 8.83486,-8.8226 18.80808,-16.50398 29.59403,-22.79336"
674+ id="path3823"
675+ inkscape:path-effect="#path-effect3825"
676+ inkscape:original-d="m 449.47325,302.28261 c 35.92724,5.92939 56.6183,-19.31435 80.02111,-35.38449 11.7014,-8.03507 19.73501,-22.46822 29.4484,-32.0479 9.7134,-9.57969 21.10658,-14.30591 29.59403,-22.79336"
677+ inkscape:connector-curvature="0"
678+ transform="translate(0,287.36218)"
679+ sodipodi:nodetypes="cssc" />
680+ <path
681+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
682+ d="m 449.81773,318.01003 c 21.09159,-2.06081 42.43769,-1.50335 63.39297,1.65552 17.10783,2.5789 34.02902,6.89013 51.32108,7.44983 12.84929,0.4159 25.76421,-1.26865 38.07692,-4.96655"
683+ id="path3827"
684+ inkscape:path-effect="#path-effect3829"
685+ inkscape:original-d="m 449.81773,318.01003 c 11.59516,1.94007 62.94692,-1.93293 63.39297,1.65552 1.01665,8.17897 40.57446,3.86763 51.32108,7.44983 19.17114,6.39038 20.04051,-0.45745 38.07692,-4.96655"
686+ inkscape:connector-curvature="0"
687+ transform="translate(0,287.36218)"
688+ sodipodi:nodetypes="cssc" />
689+ <flowRoot
690+ xml:space="preserve"
691+ id="flowRoot3813-1"
692+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
693+ transform="translate(2.6759825,544.72955)"><flowRegion
694+ id="flowRegion3815-4"><rect
695+ id="rect3817-9"
696+ width="175.48492"
697+ height="46.354519"
698+ x="249.15552"
699+ y="52.299332"
700+ style="text-align:center;text-anchor:middle"
701+ ry="0"
702+ rx="0" /></flowRegion><flowPara
703+ id="flowPara3819-7"
704+ style="font-weight:bold">HUD</flowPara><flowPara
705+ id="flowPara3821-6"
706+ style="font-weight:bold">Service</flowPara></flowRoot> <path
707+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
708+ d="m 342.69231,158.25251 0,103.4699"
709+ id="path3861"
710+ inkscape:path-effect="#path-effect3863"
711+ inkscape:original-d="m 342.69231,158.25251 c 0,102.64214 0,103.4699 0,103.4699"
712+ inkscape:connector-curvature="0"
713+ transform="translate(0,287.36218)" />
714+ <text
715+ xml:space="preserve"
716+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
717+ x="658.00934"
718+ y="442.75534"
719+ id="text3865"
720+ sodipodi:linespacing="100%"><tspan
721+ sodipodi:role="line"
722+ id="tspan3867"
723+ x="658.00934"
724+ y="442.75534"
725+ style="font-weight:bold">BAMF</tspan></text>
726+ <text
727+ xml:space="preserve"
728+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
729+ x="714.17834"
730+ y="609.96881"
731+ id="text3869"
732+ sodipodi:linespacing="100%"><tspan
733+ sodipodi:role="line"
734+ id="tspan3871"
735+ x="714.17834"
736+ y="609.96881"
737+ style="font-weight:bold">Indicator</tspan><tspan
738+ sodipodi:role="line"
739+ x="714.17834"
740+ y="627.96881"
741+ id="tspan3873"
742+ style="font-weight:bold">AppMenu</tspan></text>
743+ <text
744+ xml:space="preserve"
745+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#444444;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
746+ x="203.62877"
747+ y="481.20834"
748+ id="text3875"
749+ sodipodi:linespacing="100%"><tspan
750+ sodipodi:role="line"
751+ id="tspan3877"
752+ x="203.62877"
753+ y="481.20834"
754+ style="font-size:16px;fill:#444444;fill-opacity:1">Text Area</tspan><tspan
755+ sodipodi:role="line"
756+ x="203.62877"
757+ y="497.20834"
758+ id="tspan3879"
759+ style="font-size:16px;fill:#444444;fill-opacity:1">Target Text</tspan><tspan
760+ sodipodi:role="line"
761+ x="203.62877"
762+ y="513.20837"
763+ id="tspan3881"
764+ style="font-size:16px;fill:#444444;fill-opacity:1">Suggestions</tspan></text>
765+ <text
766+ xml:space="preserve"
767+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
768+ x="470.16724"
769+ y="506.04111"
770+ id="text3883"
771+ sodipodi:linespacing="100%"><tspan
772+ sodipodi:role="line"
773+ id="tspan3885"
774+ x="470.16724"
775+ y="506.04111">Focused</tspan><tspan
776+ sodipodi:role="line"
777+ x="470.16724"
778+ y="524.04114"
779+ id="tspan3925">Window</tspan></text>
780+ <text
781+ xml:space="preserve"
782+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
783+ x="471.28049"
784+ y="644.96588"
785+ id="text3887"
786+ sodipodi:linespacing="100%"><tspan
787+ sodipodi:role="line"
788+ id="tspan3889"
789+ x="471.28049"
790+ y="644.96588">Window to</tspan><tspan
791+ sodipodi:role="line"
792+ x="471.28049"
793+ y="662.96588"
794+ id="tspan3891">Menu mapping</tspan></text>
795+ <rect
796+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
797+ id="rect2985-3-6-2"
798+ width="259.15988"
799+ height="245.59258"
800+ x="230.90923"
801+ y="756.28912" />
802+ <text
803+ xml:space="preserve"
804+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
805+ x="326.11465"
806+ y="994.28821"
807+ id="text3865-8-2"
808+ sodipodi:linespacing="100%"><tspan
809+ sodipodi:role="line"
810+ id="tspan3867-7-3"
811+ x="326.11465"
812+ y="994.28821"
813+ style="font-weight:bold">Window</tspan></text>
814+ <rect
815+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
816+ id="rect2985-7-7"
817+ width="239.29364"
818+ height="44.447052"
819+ x="239.60066"
820+ y="925.6922" />
821+ <text
822+ xml:space="preserve"
823+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:center;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:middle;baseline-shift:baseline;color:#000000;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
824+ x="359.65784"
825+ y="953.23474"
826+ id="text3887-1"
827+ sodipodi:linespacing="100%"><tspan
828+ sodipodi:role="line"
829+ x="359.65784"
830+ y="953.23474"
831+ id="tspan3891-2">Appmenu Module</tspan></text>
832+ <path
833+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
834+ d="m 485.89465,652.84114 c 16.23733,-3.84221 30.5802,-15.04776 38.23686,-29.87304 6.96387,-13.48385 8.38625,-29.21496 7.3396,-44.35478 -1.04664,-15.13982 -4.41236,-30.01318 -6.51184,-45.04321 -2.74315,-19.63804 -3.3102,-39.70499 0.0347,-59.24953 3.34491,-19.54455 10.71754,-38.58634 22.79255,-54.3144 15.0578,-19.61323 37.35193,-33.53801 61.58305,-38.46438"
835+ id="path4052"
836+ inkscape:path-effect="#path-effect4054"
837+ inkscape:original-d="m 485.89465,652.84114 c 69.8028,11.24543 30.70744,15.30352 38.23686,-29.87304 4.70103,-28.20618 0.82776,-60.68417 0.82776,-89.39799 0,-3.09441 20.32766,-111.06434 22.82725,-113.56393 2.41681,-2.4168 58.09902,-38.46438 61.58305,-38.46438"
838+ inkscape:connector-curvature="0"
839+ transform="translate(0,287.36218)"
840+ sodipodi:nodetypes="csssc" />
841+ <rect
842+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
843+ id="rect4056"
844+ width="237.68489"
845+ height="150.42575"
846+ x="239.97879"
847+ y="765.35535" />
848+ <text
849+ xml:space="preserve"
850+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:center;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:middle;baseline-shift:baseline;color:#000000;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
851+ x="358.56305"
852+ y="846.6972"
853+ id="text4093"
854+ sodipodi:linespacing="100%"><tspan
855+ sodipodi:role="line"
856+ id="tspan4095"
857+ x="358.56305"
858+ y="846.6972">Menus</tspan></text>
859+ <path
860+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
861+ d="m 325.97456,760.29042 c -3.42872,-7.04416 -5.0107,-14.97733 -4.54619,-22.79786 0.46451,-7.82052 2.97446,-15.51064 7.21293,-22.0994 4.43899,-6.90047 10.66453,-12.51742 15.05132,-19.45118 4.11736,-6.5079 6.50844,-14.09719 6.86513,-21.78993"
862+ id="path4204"
863+ inkscape:path-effect="#path-effect4206"
864+ inkscape:original-d="m 325.97456,760.29042 c -12.83313,-16.30425 -6.68734,-30.5721 2.66674,-44.89726 4.67704,-7.16258 10.74147,-11.99823 15.05132,-19.45118 4.30985,-7.45294 6.86513,-13.52319 6.86513,-21.78993"
865+ inkscape:connector-curvature="0"
866+ sodipodi:nodetypes="cssc" />
867+ <text
868+ xml:space="preserve"
869+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
870+ x="357.66235"
871+ y="720.89227"
872+ id="text3118"
873+ sodipodi:linespacing="100%"><tspan
874+ sodipodi:role="line"
875+ id="tspan3120"
876+ x="357.66235"
877+ y="720.89227">Dbusmenu</tspan></text>
878+ <text
879+ xml:space="preserve"
880+ style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#444444;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
881+ x="556.55219"
882+ y="804.53925"
883+ id="text3118-3"
884+ sodipodi:linespacing="100%"><tspan
885+ sodipodi:role="line"
886+ id="tspan3120-3"
887+ x="556.55219"
888+ y="804.53925">Registration</tspan></text>
889+ </g>
890+</svg>
891
892=== added file 'docs/Makefile.am'
893--- docs/Makefile.am 1970-01-01 00:00:00 +0000
894+++ docs/Makefile.am 2012-01-27 17:10:57 +0000
895@@ -0,0 +1,1 @@
896+SUBDIRS = man
897
898=== added directory 'docs/man'
899=== added file 'docs/man/Makefile.am'
900--- docs/man/Makefile.am 1970-01-01 00:00:00 +0000
901+++ docs/man/Makefile.am 2012-01-27 17:10:57 +0000
902@@ -0,0 +1,16 @@
903+
904+man_MANS = \
905+ hud-cli.1 \
906+ hud-dump-application.1 \
907+ hud-list-applications.1 \
908+ hud-verify-app-info.1
909+
910+.xml.1:
911+ @$(XSLT_PROC) -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
912+
913+EXTRA_DIST = \
914+ $(man_MANS) \
915+ $(man_MANS:.1=.xml)
916+
917+CLEANFILES = \
918+ $(man_MANS)
919
920=== added file 'docs/man/hud-cli.xml'
921--- docs/man/hud-cli.xml 1970-01-01 00:00:00 +0000
922+++ docs/man/hud-cli.xml 2012-01-27 17:10:57 +0000
923@@ -0,0 +1,38 @@
924+<refentry id="hud-cli" lang="en">
925+
926+<refmeta>
927+ <refentrytitle>hud-cli</refentrytitle>
928+ <manvolnum>1</manvolnum>
929+ <refmiscinfo class="manual">User Commands</refmiscinfo>
930+</refmeta>
931+
932+<refnamediv>
933+ <refname>hud-cli</refname>
934+ <refpurpose>Tool to exercise the HUD on the command line</refpurpose>
935+</refnamediv>
936+
937+<refsynopsisdiv>
938+ <cmdsynopsis>
939+ <command>hud-cli</command>
940+ </cmdsynopsis>
941+ <cmdsynopsis>
942+ <command>hud-cli</command>
943+ <arg choice="plain"><replaceable>search string</replaceable></arg>
944+ </cmdsynopsis>
945+</refsynopsisdiv>
946+
947+<refsect1>
948+ <title>AUTHOR</title>
949+ <para>
950+ Written by Ted Gould <email>ted@canonical.com</email> and others.
951+ </para>
952+</refsect1>
953+
954+<refsect1>
955+ <title>BUGS</title>
956+ <para>
957+ Bugs should be submitted to Launchpad: <ulink url="https://bugs.launchpad.net/indicator-appmenu/+filebug"/>
958+ </para>
959+</refsect1>
960+
961+</refentry>
962
963=== added file 'docs/man/hud-dump-application.xml'
964--- docs/man/hud-dump-application.xml 1970-01-01 00:00:00 +0000
965+++ docs/man/hud-dump-application.xml 2012-01-27 17:10:57 +0000
966@@ -0,0 +1,45 @@
967+<refentry id="hud-dump-application" lang="en">
968+
969+<refmeta>
970+ <refentrytitle>hud-dump-application</refentrytitle>
971+ <manvolnum>1</manvolnum>
972+ <refmiscinfo class="manual">User Commands</refmiscinfo>
973+</refmeta>
974+
975+<refnamediv>
976+ <refname>hud-dump-application</refname>
977+ <refpurpose>Tool to dump information on an application from the HUD usage database</refpurpose>
978+</refnamediv>
979+
980+<refsynopsisdiv>
981+ <cmdsynopsis>
982+ <command>hud-dump-application</command>
983+ <arg choice="plain"><replaceable>desktop file path</replaceable></arg>
984+ </cmdsynopsis>
985+</refsynopsisdiv>
986+
987+<refsect1>
988+ <title>AUTHOR</title>
989+ <para>
990+ Written by Ted Gould <email>ted@canonical.com</email> and others.
991+ </para>
992+</refsect1>
993+
994+<refsect1>
995+ <title>BUGS</title>
996+ <para>
997+ Bugs should be submitted to Launchpad: <ulink url="https://bugs.launchpad.net/indicator-appmenu/+filebug"/>
998+ </para>
999+</refsect1>
1000+
1001+<refsect1>
1002+ <title>SEE ALSO</title>
1003+ <para>
1004+ <citerefentry>
1005+ <refentrytitle>hud-list-applications</refentrytitle>
1006+ <manvolnum>1</manvolnum>
1007+ </citerefentry>
1008+ </para>
1009+</refsect1>
1010+
1011+</refentry>
1012
1013=== added file 'docs/man/hud-list-applications.xml'
1014--- docs/man/hud-list-applications.xml 1970-01-01 00:00:00 +0000
1015+++ docs/man/hud-list-applications.xml 2012-01-27 17:10:57 +0000
1016@@ -0,0 +1,44 @@
1017+<refentry id="hud-list-applications" lang="en">
1018+
1019+<refmeta>
1020+ <refentrytitle>hud-list-applications</refentrytitle>
1021+ <manvolnum>1</manvolnum>
1022+ <refmiscinfo class="manual">User Commands</refmiscinfo>
1023+</refmeta>
1024+
1025+<refnamediv>
1026+ <refname>hud-list-applications</refname>
1027+ <refpurpose>Tool to list the applications in the HUD usage database</refpurpose>
1028+</refnamediv>
1029+
1030+<refsynopsisdiv>
1031+ <cmdsynopsis>
1032+ <command>hud-list-applications</command>
1033+ </cmdsynopsis>
1034+</refsynopsisdiv>
1035+
1036+<refsect1>
1037+ <title>AUTHOR</title>
1038+ <para>
1039+ Written by Ted Gould <email>ted@canonical.com</email> and others.
1040+ </para>
1041+</refsect1>
1042+
1043+<refsect1>
1044+ <title>BUGS</title>
1045+ <para>
1046+ Bugs should be submitted to Launchpad: <ulink url="https://bugs.launchpad.net/indicator-appmenu/+filebug"/>
1047+ </para>
1048+</refsect1>
1049+
1050+<refsect1>
1051+ <title>SEE ALSO</title>
1052+ <para>
1053+ <citerefentry>
1054+ <refentrytitle>hud-dump-application</refentrytitle>
1055+ <manvolnum>1</manvolnum>
1056+ </citerefentry>
1057+ </para>
1058+</refsect1>
1059+
1060+</refentry>
1061
1062=== added file 'docs/man/hud-verify-app-info.xml'
1063--- docs/man/hud-verify-app-info.xml 1970-01-01 00:00:00 +0000
1064+++ docs/man/hud-verify-app-info.xml 2012-01-27 17:10:57 +0000
1065@@ -0,0 +1,35 @@
1066+<refentry id="hud-verify-app-info" lang="en">
1067+
1068+<refmeta>
1069+ <refentrytitle>hud-verify-app-info</refentrytitle>
1070+ <manvolnum>1</manvolnum>
1071+ <refmiscinfo class="manual">User Commands</refmiscinfo>
1072+</refmeta>
1073+
1074+<refnamediv>
1075+ <refname>hud-verify-app-info</refname>
1076+ <refpurpose>Tool to verify an app-info file that is expected to be used in the HUD</refpurpose>
1077+</refnamediv>
1078+
1079+<refsynopsisdiv>
1080+ <cmdsynopsis>
1081+ <command>hud-verify-app-info</command>
1082+ <arg choice="plain"><replaceable>path to app info file</replaceable></arg>
1083+ </cmdsynopsis>
1084+</refsynopsisdiv>
1085+
1086+<refsect1>
1087+ <title>AUTHOR</title>
1088+ <para>
1089+ Written by Ted Gould <email>ted@canonical.com</email> and others.
1090+ </para>
1091+</refsect1>
1092+
1093+<refsect1>
1094+ <title>BUGS</title>
1095+ <para>
1096+ Bugs should be submitted to Launchpad: <ulink url="https://bugs.launchpad.net/indicator-appmenu/+filebug"/>
1097+ </para>
1098+</refsect1>
1099+
1100+</refentry>
1101
1102=== added directory 'po'
1103=== added file 'po/POTFILES.in'
1104--- po/POTFILES.in 1970-01-01 00:00:00 +0000
1105+++ po/POTFILES.in 2012-01-27 17:10:57 +0000
1106@@ -0,0 +1,14 @@
1107+data/com.canonical.indicator.appmenu.gschema.xml.in
1108+data/com.canonical.indicator.appmenu.hud.gschema.xml.in
1109+data/com.canonical.indicator.appmenu.hud.search.gschema.xml.in
1110+src/hud-service.c
1111+src/indicator-tracker.c
1112+src/hud-dbus.c
1113+src/dbusmenu-collector.c
1114+src/usage-tracker.c
1115+src/hud.interface.c
1116+src/hud-search.c
1117+src/hud-cli.c
1118+src/distance.c
1119+src/load-app-info.c
1120+src/dump-app-info.c
1121
1122=== added directory 'service'
1123=== modified file 'src/Makefile.am'
1124--- src/Makefile.am 2011-12-07 00:25:50 +0000
1125+++ src/Makefile.am 2012-01-27 17:10:57 +0000
1126@@ -1,7 +1,18 @@
1127 CLEANFILES =
1128 DISTCLEANFILES =
1129 BUILT_SOURCES =
1130-EXTRA_DIST =
1131+EXTRA_DIST = \
1132+ hud-list-applications
1133+
1134+bin_PROGRAMS = \
1135+ hud-dump-application \
1136+ hud-verify-app-info
1137+
1138+bin_SCRIPTS = \
1139+ hud-list-applications
1140+
1141+libexec_PROGRAMS = \
1142+ hud-service
1143
1144 include $(top_srcdir)/Makefile.am.marshal
1145
1146@@ -65,6 +76,121 @@
1147 gen-application-menu-registrar.xml.c \
1148 gen-application-menu-registrar.xml.h
1149
1150+####################
1151+# HUD Service
1152+####################
1153+
1154+hud_service_SOURCES = \
1155+ dbusmenu-collector.h \
1156+ dbusmenu-collector.c \
1157+ distance.h \
1158+ distance.c \
1159+ dump-app-info.h \
1160+ dump-app-info.c \
1161+ shared-values.h \
1162+ hud-service.c \
1163+ hud-dbus.h \
1164+ hud-dbus.c \
1165+ hud-search.h \
1166+ hud-search.c \
1167+ hud.interface.h \
1168+ hud.interface.c \
1169+ indicator-tracker.h \
1170+ indicator-tracker.c \
1171+ load-app-info.h \
1172+ load-app-info.c \
1173+ usage-tracker.h \
1174+ usage-tracker.c \
1175+ utils.h \
1176+ utils.c
1177+
1178+hud_service_CFLAGS = \
1179+ $(HUD_CFLAGS) \
1180+ $(COVERAGE_CFLAGS) \
1181+ -DDATADIR=\""$(datadir)"\" \
1182+ -Wall -Werror
1183+hud_service_LDFLAGS = \
1184+ $(HUD_LIBS)
1185+
1186+####################
1187+# HUD CLI
1188+####################
1189+
1190+hud_cli_SOURCES = \
1191+ shared-values.h \
1192+ hud-cli.c \
1193+ hud.interface.h \
1194+ hud.interface.c
1195+
1196+hud_cli_CFLAGS = \
1197+ $(HUD_CFLAGS) \
1198+ $(COVERAGE_CFLAGS) \
1199+ -Wall -Werror
1200+
1201+hud_cli_LDFLAGS = \
1202+ $(HUD_LIBS) \
1203+ -lreadline -lcurses
1204+
1205+
1206+if BUILD_CLI
1207+bin_PROGRAMS += \
1208+ hud-cli
1209+else
1210+EXTRA_DIST += \
1211+ $(hud_cli_SOURCES)
1212+endif
1213+
1214+#######################
1215+# HUD Dump Application
1216+#######################
1217+
1218+hud_dump_application_SOURCES = \
1219+ dump-app-info.h \
1220+ dump-app-info.c \
1221+ hud-dump-application.c
1222+
1223+hud_dump_application_CFLAGS = \
1224+ $(HUD_CFLAGS) \
1225+ $(COVERAGE_CFLAGS) \
1226+ -Wall -Werror
1227+hud_dump_application_LDFLAGS = \
1228+ $(HUD_LIBS)
1229+
1230+#######################
1231+# HUD Verify App Info
1232+#######################
1233+
1234+hud_verify_app_info_SOURCES = \
1235+ load-app-info.h \
1236+ load-app-info.c \
1237+ hud-verify-app-info.c
1238+
1239+hud_verify_app_info_CFLAGS = \
1240+ $(HUD_CFLAGS) \
1241+ $(COVERAGE_CFLAGS) \
1242+ -Wall -Werror
1243+hud_verify_app_info_LDFLAGS = \
1244+ $(HUD_LIBS)
1245+
1246+####################
1247+# HUD DBus Interface
1248+####################
1249+
1250+EXTRA_DIST += \
1251+ hud.xml
1252+
1253+BUILT_SOURCES += \
1254+ hud.interface.c \
1255+ hud.interface.h
1256+
1257+%.interface.h: %.xml
1258+ echo "extern const char * $(subst -,_,$(subst .,_,$(basename $(notdir $@))));" > $@
1259+
1260+%.interface.c: %.xml
1261+ echo "const char * $(subst -,_,$(subst .,_,$(basename $(notdir $@)))) = " > $@
1262+ sed -e "s:\":\\\\\":g" -e s:^:\": -e s:\$$:\\\\n\": $< >> $@
1263+ echo ";" >> $@
1264+
1265 CLEANFILES += $(BUILT_SOURCES)
1266
1267 EXTRA_DIST += $(DBUS_SPECS) clean-namespaces.xslt
1268
1269=== added file 'src/dbusmenu-collector.c'
1270--- src/dbusmenu-collector.c 1970-01-01 00:00:00 +0000
1271+++ src/dbusmenu-collector.c 2012-01-27 17:10:57 +0000
1272@@ -0,0 +1,711 @@
1273+/*
1274+An object to collect the various DBusmenu objects that exist
1275+on dbus.
1276+
1277+Copyright 2011 Canonical Ltd.
1278+
1279+Authors:
1280+ Ted Gould <ted@canonical.com>
1281+
1282+This program is free software: you can redistribute it and/or modify it
1283+under the terms of the GNU General Public License version 3, as published
1284+by the Free Software Foundation.
1285+
1286+This program is distributed in the hope that it will be useful, but
1287+WITHOUT ANY WARRANTY; without even the implied warranties of
1288+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1289+PURPOSE. See the GNU General Public License for more details.
1290+
1291+You should have received a copy of the GNU General Public License along
1292+with this program. If not, see <http://www.gnu.org/licenses/>.
1293+*/
1294+
1295+#ifdef HAVE_CONFIG_H
1296+#include "config.h"
1297+#endif
1298+
1299+#include <gio/gio.h>
1300+#include <glib.h>
1301+#include <glib/gi18n.h>
1302+
1303+#include <libdbusmenu-glib/client.h>
1304+
1305+#include "dbusmenu-collector.h"
1306+#include "distance.h"
1307+#include "shared-values.h"
1308+#include "utils.h"
1309+
1310+#define GENERIC_ICON "dbusmenu-lens-panel"
1311+
1312+typedef struct _DbusmenuCollectorPrivate DbusmenuCollectorPrivate;
1313+
1314+struct _DbusmenuCollectorPrivate {
1315+ GDBusConnection * bus;
1316+ guint signal;
1317+ GHashTable * hash;
1318+ GSettings * search_settings;
1319+};
1320+
1321+struct _DbusmenuCollectorFound {
1322+ gchar * dbus_addr;
1323+ gchar * dbus_path;
1324+ gint dbus_id;
1325+
1326+ gchar * display_string;
1327+ gchar * db_string;
1328+
1329+ gchar * app_icon;
1330+
1331+ guint distance;
1332+ DbusmenuMenuitem * item;
1333+ gchar * indicator;
1334+};
1335+
1336+typedef struct _menu_key_t menu_key_t;
1337+struct _menu_key_t {
1338+ gchar * sender;
1339+ gchar * path;
1340+};
1341+
1342+typedef struct _search_item_t search_item_t;
1343+struct _search_item_t {
1344+ gchar * string;
1345+ guint distance;
1346+};
1347+
1348+static void dbusmenu_collector_dispose (GObject *object);
1349+static void dbusmenu_collector_finalize (GObject *object);
1350+static void update_layout_cb (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data);
1351+static guint menu_hash_func (gconstpointer key);
1352+static gboolean menu_equal_func (gconstpointer a, gconstpointer b);
1353+static void menu_key_destroy (gpointer key);
1354+static DbusmenuCollectorFound * dbusmenu_collector_found_new (DbusmenuClient * client, DbusmenuMenuitem * item, GStrv strings, guint distance, GStrv usedstrings);
1355+
1356+G_DEFINE_TYPE (DbusmenuCollector, dbusmenu_collector, G_TYPE_OBJECT);
1357+
1358+static void
1359+dbusmenu_collector_class_init (DbusmenuCollectorClass *klass)
1360+{
1361+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
1362+
1363+ g_type_class_add_private (klass, sizeof (DbusmenuCollectorPrivate));
1364+
1365+ object_class->dispose = dbusmenu_collector_dispose;
1366+ object_class->finalize = dbusmenu_collector_finalize;
1367+
1368+ return;
1369+}
1370+
1371+static void
1372+dbusmenu_collector_init (DbusmenuCollector *self)
1373+{
1374+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), DBUSMENU_COLLECTOR_TYPE, DbusmenuCollectorPrivate);
1375+
1376+ self->priv->bus = NULL;
1377+ self->priv->signal = 0;
1378+ self->priv->hash = NULL;
1379+ self->priv->search_settings = NULL;
1380+
1381+ if (settings_schema_exists("com.canonical.indicator.appmenu.hud.search")) {
1382+ self->priv->search_settings = g_settings_new("com.canonical.indicator.appmenu.hud.search");
1383+ }
1384+
1385+ self->priv->hash = g_hash_table_new_full(menu_hash_func, menu_equal_func,
1386+ menu_key_destroy, g_object_unref /* DbusmenuClient */);
1387+
1388+ self->priv->bus = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
1389+ self->priv->signal = g_dbus_connection_signal_subscribe(self->priv->bus,
1390+ NULL, /* sender */
1391+ "com.canonical.dbusmenu", /* interface */
1392+ "LayoutUpdated", /* member */
1393+ NULL, /* object path */
1394+ NULL, /* arg0 */
1395+ G_DBUS_SIGNAL_FLAGS_NONE, /* flags */
1396+ update_layout_cb, /* cb */
1397+ self, /* data */
1398+ NULL); /* free func */
1399+
1400+ GError * error = NULL;
1401+ g_dbus_connection_emit_signal(self->priv->bus,
1402+ NULL, /* destination */
1403+ "/", /* object */
1404+ "com.canonical.dbusmenu",
1405+ "FindServers",
1406+ NULL, /* params */
1407+ &error);
1408+ if (error != NULL) {
1409+ g_warning("Unable to emit 'FindServers': %s", error->message);
1410+ g_error_free(error);
1411+ }
1412+
1413+ return;
1414+}
1415+
1416+static void
1417+dbusmenu_collector_dispose (GObject *object)
1418+{
1419+ DbusmenuCollector * collector = DBUSMENU_COLLECTOR(object);
1420+
1421+ if (collector->priv->signal != 0) {
1422+ g_dbus_connection_signal_unsubscribe(collector->priv->bus, collector->priv->signal);
1423+ collector->priv->signal = 0;
1424+ }
1425+
1426+ g_clear_object(&collector->priv->bus);
1427+
1428+ if (collector->priv->hash != NULL) {
1429+ g_hash_table_destroy(collector->priv->hash);
1430+ collector->priv->hash = NULL;
1431+ }
1432+
1433+ g_clear_object(&collector->priv->search_settings);
1434+
1435+ G_OBJECT_CLASS (dbusmenu_collector_parent_class)->dispose (object);
1436+ return;
1437+}
1438+
1439+static void
1440+dbusmenu_collector_finalize (GObject *object)
1441+{
1442+
1443+ G_OBJECT_CLASS (dbusmenu_collector_parent_class)->finalize (object);
1444+ return;
1445+}
1446+
1447+DbusmenuCollector *
1448+dbusmenu_collector_new (void)
1449+{
1450+ return DBUSMENU_COLLECTOR(g_object_new(DBUSMENU_COLLECTOR_TYPE, NULL));
1451+}
1452+
1453+static void
1454+update_layout_cb (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
1455+{
1456+ g_debug("Client updating %s:%s", sender, path);
1457+ DbusmenuCollector * collector = DBUSMENU_COLLECTOR(user_data);
1458+ g_return_if_fail(collector != NULL);
1459+
1460+ menu_key_t search_key = {
1461+ sender: (gchar *)sender,
1462+ path: (gchar *)path
1463+ };
1464+
1465+ gpointer found = g_hash_table_lookup(collector->priv->hash, &search_key);
1466+ if (found == NULL) {
1467+ /* Build one because we don't have it */
1468+ menu_key_t * built_key = g_new0(menu_key_t, 1);
1469+ built_key->sender = g_strdup(sender);
1470+ built_key->path = g_strdup(path);
1471+
1472+ DbusmenuClient * client = dbusmenu_client_new(sender, path);
1473+
1474+ g_hash_table_insert(collector->priv->hash, built_key, client);
1475+ } else {
1476+ g_debug("\tAlready exists");
1477+ }
1478+
1479+ /* Assume that dbusmenu client is doing this for us */
1480+ return;
1481+}
1482+
1483+static guint
1484+menu_hash_func (gconstpointer key)
1485+{
1486+ const menu_key_t * mk = (const menu_key_t*)key;
1487+
1488+ return g_str_hash(mk->sender) + g_str_hash(mk->path) - 5381;
1489+}
1490+
1491+static gboolean
1492+menu_equal_func (gconstpointer a, gconstpointer b)
1493+{
1494+ const menu_key_t * ak = (const menu_key_t *)a;
1495+ const menu_key_t * bk = (const menu_key_t *)b;
1496+
1497+ if (g_strcmp0(ak->sender, bk->sender) == 0 &&
1498+ g_strcmp0(ak->path, bk->path) == 0) {
1499+ return TRUE;
1500+ }
1501+
1502+ return FALSE;
1503+}
1504+
1505+static void
1506+menu_key_destroy (gpointer key)
1507+{
1508+ menu_key_t * ikey = (menu_key_t *)key;
1509+
1510+ g_free(ikey->sender);
1511+ g_free(ikey->path);
1512+
1513+ g_free(ikey);
1514+ return;
1515+}
1516+
1517+static gchar *
1518+remove_underline (const gchar * input)
1519+{
1520+ const gunichar underline = g_utf8_get_char("_");
1521+ GString * output = g_string_sized_new (strlen(input)+1);
1522+
1523+ const gchar * begin = input;
1524+ const gchar * end;
1525+ while ((end = g_utf8_strchr (begin, -1, underline))) {
1526+ g_string_append_len (output, begin, end-begin);
1527+ begin = g_utf8_next_char (end);
1528+ }
1529+ g_string_append (output, begin);
1530+ return g_string_free (output, FALSE);
1531+}
1532+
1533+static GStrv
1534+menuitem_to_tokens (DbusmenuMenuitem * item, GStrv label_prefix)
1535+{
1536+ const gchar * label_property = NULL;
1537+ const gchar * item_type = NULL;
1538+
1539+ if (label_property == NULL && !dbusmenu_menuitem_property_exist(item, DBUSMENU_MENUITEM_PROP_TYPE)) {
1540+ label_property = DBUSMENU_MENUITEM_PROP_LABEL;
1541+ }
1542+
1543+ if (label_property == NULL) {
1544+ item_type = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_TYPE);
1545+ }
1546+
1547+ if (label_property == NULL && g_strcmp0(item_type, DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0) {
1548+ return NULL;
1549+ }
1550+
1551+ if (label_property == NULL && g_strcmp0(item_type, DBUSMENU_CLIENT_TYPES_DEFAULT) == 0) {
1552+ label_property = DBUSMENU_MENUITEM_PROP_LABEL;
1553+ }
1554+
1555+ /* Indicator Messages */
1556+ if (label_property == NULL && g_strcmp0(item_type, "application-item") == 0) {
1557+ label_property = "label";
1558+ }
1559+
1560+ if (label_property == NULL && g_strcmp0(item_type, "indicator-item") == 0) {
1561+ label_property = "indicator-label";
1562+ }
1563+
1564+ /* Indicator Date Time */
1565+ if (label_property == NULL && g_strcmp0(item_type, "appointment-item") == 0) {
1566+ label_property = "appointment-label";
1567+ }
1568+
1569+ if (label_property == NULL && g_strcmp0(item_type, "timezone-item") == 0) {
1570+ label_property = "timezone-name";
1571+ }
1572+
1573+ /* Indicator Sound */
1574+ if (label_property == NULL && g_strcmp0(item_type, "x-canonical-sound-menu-player-metadata-type") == 0) {
1575+ label_property = "x-canonical-sound-menu-player-metadata-player-name";
1576+ }
1577+
1578+ if (label_property == NULL && g_strcmp0(item_type, "x-canonical-sound-menu-mute-type") == 0) {
1579+ label_property = "label";
1580+ }
1581+
1582+ /* NOTE: Need to handle the transport item at some point */
1583+
1584+ /* Indicator User */
1585+ if (label_property == NULL && g_strcmp0(item_type, "x-canonical-user-item") == 0) {
1586+ label_property = "user-item-name";
1587+ }
1588+
1589+ /* Tokenize */
1590+ if (label_property != NULL && dbusmenu_menuitem_property_exist(item, label_property)) {
1591+ GStrv newstr = NULL;
1592+ const gchar * label = dbusmenu_menuitem_property_get(item, label_property);
1593+
1594+ if (label_prefix != NULL && label_prefix[0] != NULL) {
1595+ gint i;
1596+ guint prefix_len = g_strv_length(label_prefix);
1597+ newstr = g_new(gchar *, prefix_len + 2);
1598+
1599+ for (i = 0; i < prefix_len; i++) {
1600+ newstr[i] = g_strdup(label_prefix[i]);
1601+ }
1602+
1603+ newstr[prefix_len] = g_strdup(label);
1604+ newstr[prefix_len + 1] = NULL;
1605+ } else {
1606+ newstr = g_new0(gchar *, 2);
1607+ newstr[0] = g_strdup(label);
1608+ newstr[1] = NULL;
1609+ }
1610+
1611+ return newstr;
1612+ }
1613+
1614+ return NULL;
1615+}
1616+
1617+static GList *
1618+tokens_to_children (DbusmenuMenuitem * rootitem, const gchar * search, GList * results, GStrv label_prefix, DbusmenuClient * client)
1619+{
1620+ if (search == NULL) {
1621+ return results;
1622+ }
1623+
1624+ if (rootitem == NULL) {
1625+ return results;
1626+ }
1627+
1628+ /* We can only evaluate these properties if we know the type */
1629+ if (!dbusmenu_menuitem_property_exist(rootitem, DBUSMENU_MENUITEM_PROP_TYPE) ||
1630+ g_strcmp0(dbusmenu_menuitem_property_get(rootitem, DBUSMENU_MENUITEM_PROP_TYPE), DBUSMENU_CLIENT_TYPES_DEFAULT) == 0) {
1631+ /* Skip the items that are disabled or not visible as they wouldn't
1632+ be usable in the application so we don't want to show them and
1633+ act like they're usable in the HUD either */
1634+ if (!dbusmenu_menuitem_property_get_bool(rootitem, DBUSMENU_MENUITEM_PROP_ENABLED)) {
1635+ return results;
1636+ }
1637+
1638+ if (!dbusmenu_menuitem_property_get_bool(rootitem, DBUSMENU_MENUITEM_PROP_VISIBLE)) {
1639+ return results;
1640+ }
1641+ }
1642+
1643+ GStrv newstr = menuitem_to_tokens(rootitem, label_prefix);
1644+
1645+ if (!dbusmenu_menuitem_get_root(rootitem) && newstr != NULL) {
1646+ GStrv used_strings = NULL;
1647+ guint distance = calculate_distance(search, newstr, &used_strings);
1648+ if (distance < G_MAXUINT) {
1649+ // g_debug("Distance %d for '%s' in \"'%s'\" using \"'%s'\"", distance, search, g_strjoinv("' '", newstr), g_strjoinv("' '", used_strings));
1650+ results = g_list_prepend(results, dbusmenu_collector_found_new(client, rootitem, newstr, distance, used_strings));
1651+ }
1652+ g_strfreev(used_strings);
1653+ }
1654+
1655+ if (newstr == NULL) {
1656+ newstr = g_strdupv(label_prefix);
1657+ }
1658+
1659+ GList * children = dbusmenu_menuitem_get_children(rootitem);
1660+ GList * child;
1661+
1662+ for (child = children; child != NULL; child = g_list_next(child)) {
1663+ DbusmenuMenuitem * item = DBUSMENU_MENUITEM(child->data);
1664+
1665+ results = tokens_to_children(item, search, results, newstr, client);
1666+ }
1667+
1668+ g_strfreev(newstr);
1669+ return results;
1670+}
1671+
1672+static GList *
1673+process_client (DbusmenuCollector * collector, DbusmenuClient * client, const gchar * search, GList * results, GStrv prefix)
1674+{
1675+ /* Handle the case where there are no search terms */
1676+ if (search == NULL || search[0] == '\0') {
1677+ GList * children = dbusmenu_menuitem_get_children(dbusmenu_client_get_root(client));
1678+ GList * child;
1679+
1680+ for (child = children; child != NULL; child = g_list_next(child)) {
1681+ DbusmenuMenuitem * item = DBUSMENU_MENUITEM(child->data);
1682+
1683+ if (!dbusmenu_menuitem_property_exist(item, DBUSMENU_MENUITEM_PROP_LABEL)) {
1684+ continue;
1685+ }
1686+
1687+ const gchar * label = dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_LABEL);
1688+ const gchar * array[2];
1689+ array[0] = label;
1690+ array[1] = NULL;
1691+
1692+ results = g_list_prepend(results, dbusmenu_collector_found_new(client, item, (GStrv)array, calculate_distance(NULL, (GStrv)array, NULL), NULL));
1693+ }
1694+
1695+ return results;
1696+ }
1697+
1698+ results = tokens_to_children(dbusmenu_client_get_root(client), search, results, prefix, client);
1699+ return results;
1700+}
1701+
1702+static void
1703+hash_print (gpointer key, gpointer value, gpointer user_data)
1704+{
1705+ menu_key_t * menukey = (menu_key_t *)key;
1706+
1707+ g_debug("Addr: '%s' Path: '%s' Hash: '%u'", menukey->sender, menukey->path, menu_hash_func(key));
1708+
1709+ return;
1710+}
1711+
1712+static GList *
1713+just_do_it (DbusmenuCollector * collector, const gchar * dbus_addr, const gchar * dbus_path, const gchar * search, GStrv prefix)
1714+{
1715+ GList * results = NULL;
1716+ g_return_val_if_fail(IS_DBUSMENU_COLLECTOR(collector), results);
1717+
1718+ menu_key_t search_key = {
1719+ sender: (gchar *)dbus_addr,
1720+ path: (gchar *)dbus_path
1721+ };
1722+
1723+ gpointer found = g_hash_table_lookup(collector->priv->hash, &search_key);
1724+ if (found != NULL) {
1725+ results = process_client(collector, DBUSMENU_CLIENT(found), search, results, prefix);
1726+ } else {
1727+ g_warning("Unable to find menu '%s' on '%s' with hash '%u'", dbus_path, dbus_addr, menu_hash_func(&search_key));
1728+
1729+ g_debug("Dumping Hash");
1730+ g_hash_table_foreach(collector->priv->hash, hash_print, NULL);
1731+ }
1732+
1733+ return results;
1734+}
1735+
1736+/**
1737+ dbusmenu_collector_search:
1738+ @collector: The #DbusmenuCollector object
1739+ @dbus_addr: Address on DBus for the menus to search
1740+ @dbus_path: Path the to object we should search
1741+ @prefix: Possible a string prefix for the name in the app
1742+ @search: Search string being used
1743+
1744+ Searches through a set of menus that should be collected in this
1745+ object. Returns any reasonable matches from the set of menus.
1746+
1747+ Return Value: (element-type DbusmenuCollectorFound) (transfer full):
1748+ List of entries that match as #DbusmenueCollectorFound objects
1749+*/
1750+GList *
1751+dbusmenu_collector_search (DbusmenuCollector * collector, const gchar * dbus_addr, const gchar * dbus_path, const gchar * prefix, const gchar * search)
1752+{
1753+ g_return_val_if_fail(IS_DBUSMENU_COLLECTOR(collector), NULL);
1754+
1755+ GList * items = NULL;
1756+ GStrv prefixarray = NULL;
1757+ gchar * localprefixarray[2];
1758+
1759+ if (prefix != NULL) {
1760+ prefixarray = localprefixarray;
1761+
1762+ prefixarray[0] = (gchar *)prefix;
1763+ prefixarray[1] = NULL;
1764+ }
1765+
1766+ if (dbus_addr != NULL && dbus_path != NULL) {
1767+ items = just_do_it(collector, dbus_addr, dbus_path, search, prefixarray);
1768+ }
1769+
1770+ return items;
1771+}
1772+
1773+void
1774+dbusmenu_collector_execute (DbusmenuCollector * collector, const gchar * dbus_addr, const gchar * dbus_path, gint id, guint timestamp)
1775+{
1776+ g_return_if_fail(IS_DBUSMENU_COLLECTOR(collector));
1777+
1778+ menu_key_t search_key = {
1779+ sender: (gchar *)dbus_addr,
1780+ path: (gchar *)dbus_path
1781+ };
1782+
1783+ gpointer found = g_hash_table_lookup(collector->priv->hash, &search_key);
1784+ if (found == NULL) {
1785+ g_warning("Unable to find dbusmenu client: %s:%s", dbus_addr, dbus_path);
1786+ return;
1787+ }
1788+
1789+ DbusmenuMenuitem * root = dbusmenu_client_get_root(DBUSMENU_CLIENT(found));
1790+ if (root == NULL) {
1791+ g_warning("Dbusmenu Client %s:%s has no menuitems", dbus_addr, dbus_path);
1792+ return;
1793+ }
1794+
1795+ DbusmenuMenuitem * item = dbusmenu_menuitem_find_id(root, id);
1796+ if (item == NULL) {
1797+ g_warning("Unable to find menuitem %d on client %s:%s", id, dbus_addr, dbus_path);
1798+ return;
1799+ }
1800+
1801+ g_debug("Executing menuitem %d: %s", id, dbusmenu_menuitem_property_get(item, DBUSMENU_MENUITEM_PROP_LABEL));
1802+ dbusmenu_menuitem_handle_event(item, DBUSMENU_MENUITEM_EVENT_ACTIVATED, NULL, timestamp);
1803+
1804+ return;
1805+}
1806+
1807+guint
1808+dbusmenu_collector_found_get_distance (DbusmenuCollectorFound * found)
1809+{
1810+ g_return_val_if_fail(found != NULL, G_MAXUINT);
1811+ return found->distance;
1812+}
1813+
1814+void
1815+dbusmenu_collector_found_set_distance (DbusmenuCollectorFound * found, guint distance)
1816+{
1817+ g_return_if_fail(found != NULL);
1818+ found->distance = distance;
1819+ return;
1820+}
1821+
1822+const gchar *
1823+dbusmenu_collector_found_get_display (DbusmenuCollectorFound * found)
1824+{
1825+ g_return_val_if_fail(found != NULL, NULL);
1826+ return found->display_string;
1827+}
1828+
1829+const gchar *
1830+dbusmenu_collector_found_get_db (DbusmenuCollectorFound * found)
1831+{
1832+ g_return_val_if_fail(found != NULL, NULL);
1833+ return found->db_string;
1834+}
1835+
1836+void
1837+dbusmenu_collector_found_list_free (GList * found_list)
1838+{
1839+ g_list_free_full(found_list, (GDestroyNotify)dbusmenu_collector_found_free);
1840+ return;
1841+}
1842+
1843+static DbusmenuCollectorFound *
1844+dbusmenu_collector_found_new (DbusmenuClient * client, DbusmenuMenuitem * item, GStrv strings, guint distance, GStrv usedstrings)
1845+{
1846+ // g_debug("New Found: '%s', %d, '%s'", string, distance, indicator_name);
1847+ DbusmenuCollectorFound * found = g_new0(DbusmenuCollectorFound, 1);
1848+
1849+ g_object_get(client,
1850+ DBUSMENU_CLIENT_PROP_DBUS_NAME, &found->dbus_addr,
1851+ DBUSMENU_CLIENT_PROP_DBUS_OBJECT, &found->dbus_path,
1852+ NULL);
1853+ found->dbus_id = dbusmenu_menuitem_get_id(item);
1854+
1855+ found->db_string = g_strjoinv(DB_SEPARATOR, strings);
1856+ found->distance = distance;
1857+ found->item = item;
1858+ found->indicator = NULL;
1859+ found->app_icon = NULL;
1860+
1861+ found->display_string = NULL;
1862+ if (strings != NULL) {
1863+ static gchar * connector = NULL;
1864+
1865+ if (connector == NULL) {
1866+ /* TRANSLATORS: This string is a printf format string to build
1867+ a string representing menu hierarchy in an application. The
1868+ strings are <top> <separator> <bottom>. So if the separator
1869+ is ">" and the item is "Open" in the "File" menu the final
1870+ string would be "File > Open" */
1871+ connector = g_markup_escape_text(_("%s > %s"), -1);
1872+ }
1873+
1874+ gchar * firstunderline = remove_underline(strings[0]);
1875+ found->display_string = g_markup_escape_text(firstunderline, -1);
1876+ g_free(firstunderline);
1877+ int i;
1878+ for (i = 1; strings[i] != NULL; i++) {
1879+ gchar * nounder = remove_underline(strings[i]);
1880+ gchar * escaped = g_markup_escape_text(nounder, -1);
1881+ gchar * tmp = g_strdup_printf(connector, found->display_string, escaped);
1882+ g_free(found->display_string);
1883+ g_free(escaped);
1884+ g_free(nounder);
1885+ found->display_string = tmp;
1886+ }
1887+
1888+ /* NOTE: Should probably find some way to use remalloc here, not sure
1889+ how to do that with the escaping and the translated connector
1890+ though. Will take some thinking. */
1891+ }
1892+
1893+ if (found->display_string != NULL && usedstrings != NULL) {
1894+ int str;
1895+ for (str = 0; usedstrings[str] != NULL; str++) {
1896+ if (usedstrings[str][0] == '\0') continue; // No NULL strings
1897+ if (usedstrings[str][0] == '&') continue; // Not a useful match and it violates markup rules
1898+ gchar * nounder = remove_underline(usedstrings[str]);
1899+ GStrv split = g_strsplit(found->display_string, nounder, -1);
1900+ gchar * bold = g_strconcat("<b>", nounder, "</b>", NULL);
1901+ gchar * tmp = g_strjoinv(bold, split);
1902+
1903+ g_free(found->display_string);
1904+ found->display_string = tmp;
1905+
1906+ g_strfreev(split);
1907+ g_free(bold);
1908+ g_free(nounder);
1909+ }
1910+ }
1911+
1912+ g_object_ref(G_OBJECT(item));
1913+
1914+ return found;
1915+}
1916+
1917+void
1918+dbusmenu_collector_found_free (DbusmenuCollectorFound * found)
1919+{
1920+ g_return_if_fail(found != NULL);
1921+ g_free(found->dbus_addr);
1922+ g_free(found->dbus_path);
1923+ g_free(found->display_string);
1924+ g_free(found->db_string);
1925+ g_free(found->indicator);
1926+ g_free(found->app_icon);
1927+ g_object_unref(found->item);
1928+ g_free(found);
1929+ return;
1930+}
1931+
1932+const gchar *
1933+dbusmenu_collector_found_get_indicator (DbusmenuCollectorFound * found)
1934+{
1935+ // g_debug("Getting indicator for found '%s', indicator: '%s'", found->display_string, found->indicator);
1936+ g_return_val_if_fail(found != NULL, NULL);
1937+ return found->indicator;
1938+}
1939+
1940+void
1941+dbusmenu_collector_found_set_indicator (DbusmenuCollectorFound * found, const gchar * indicator)
1942+{
1943+ g_return_if_fail(found != NULL);
1944+ g_free(found->indicator);
1945+ found->indicator = g_strdup(indicator);
1946+ return;
1947+}
1948+
1949+const gchar *
1950+dbusmenu_collector_found_get_dbus_addr (DbusmenuCollectorFound * found)
1951+{
1952+ g_return_val_if_fail(found != NULL, NULL);
1953+ return found->dbus_addr;
1954+}
1955+
1956+const gchar *
1957+dbusmenu_collector_found_get_dbus_path (DbusmenuCollectorFound * found)
1958+{
1959+ g_return_val_if_fail(found != NULL, NULL);
1960+ return found->dbus_path;
1961+}
1962+
1963+gint
1964+dbusmenu_collector_found_get_dbus_id (DbusmenuCollectorFound * found)
1965+{
1966+ g_return_val_if_fail(found != NULL, -1);
1967+ return found->dbus_id;
1968+}
1969+
1970+const gchar *
1971+dbusmenu_collector_found_get_app_icon (DbusmenuCollectorFound * found)
1972+{
1973+ g_return_val_if_fail(found != NULL, NULL);
1974+ return found->app_icon;
1975+}
1976+
1977+void
1978+dbusmenu_collector_found_set_app_icon (DbusmenuCollectorFound * found, const gchar * app_icon)
1979+{
1980+ g_return_if_fail(found != NULL);
1981+ g_free(found->app_icon);
1982+ found->app_icon= g_strdup(app_icon);
1983+}
1984
1985=== added file 'src/dbusmenu-collector.h'
1986--- src/dbusmenu-collector.h 1970-01-01 00:00:00 +0000
1987+++ src/dbusmenu-collector.h 2012-01-27 17:10:57 +0000
1988@@ -0,0 +1,73 @@
1989+/*
1990+An object to collect the various DBusmenu objects that exist
1991+on dbus.
1992+
1993+Copyright 2011 Canonical Ltd.
1994+
1995+Authors:
1996+ Ted Gould <ted@canonical.com>
1997+
1998+This program is free software: you can redistribute it and/or modify it
1999+under the terms of the GNU General Public License version 3, as published
2000+by the Free Software Foundation.
2001+
2002+This program is distributed in the hope that it will be useful, but
2003+WITHOUT ANY WARRANTY; without even the implied warranties of
2004+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2005+PURPOSE. See the GNU General Public License for more details.
2006+
2007+You should have received a copy of the GNU General Public License along
2008+with this program. If not, see <http://www.gnu.org/licenses/>.
2009+*/
2010+
2011+#ifndef __DBUSMENU_COLLECTOR_H__
2012+#define __DBUSMENU_COLLECTOR_H__
2013+
2014+#include <glib-object.h>
2015+
2016+G_BEGIN_DECLS
2017+
2018+#define DBUSMENU_COLLECTOR_TYPE (dbusmenu_collector_get_type ())
2019+#define DBUSMENU_COLLECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DBUSMENU_COLLECTOR_TYPE, DbusmenuCollector))
2020+#define DBUSMENU_COLLECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), DBUSMENU_COLLECTOR_TYPE, DbusmenuCollectorClass))
2021+#define IS_DBUSMENU_COLLECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), DBUSMENU_COLLECTOR_TYPE))
2022+#define IS_DBUSMENU_COLLECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), DBUSMENU_COLLECTOR_TYPE))
2023+#define DBUSMENU_COLLECTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), DBUSMENU_COLLECTOR_TYPE, DbusmenuCollectorClass))
2024+
2025+typedef struct _DbusmenuCollector DbusmenuCollector;
2026+typedef struct _DbusmenuCollectorClass DbusmenuCollectorClass;
2027+typedef struct _DbusmenuCollectorPrivate DbusmenuCollectorPrivate;
2028+typedef struct _DbusmenuCollectorFound DbusmenuCollectorFound;
2029+
2030+struct _DbusmenuCollectorClass {
2031+ GObjectClass parent_class;
2032+};
2033+
2034+struct _DbusmenuCollector {
2035+ GObject parent;
2036+
2037+ DbusmenuCollectorPrivate * priv;
2038+};
2039+
2040+GType dbusmenu_collector_get_type (void);
2041+DbusmenuCollector * dbusmenu_collector_new (void);
2042+GList * dbusmenu_collector_search (DbusmenuCollector * collector, const gchar * dbus_addr, const gchar * dbus_path, const gchar * prefix, const gchar * search);
2043+void dbusmenu_collector_execute (DbusmenuCollector * collector, const gchar * dbus_addr, const gchar * dbus_path, gint id, guint timestamp);
2044+
2045+guint dbusmenu_collector_found_get_distance (DbusmenuCollectorFound * found);
2046+void dbusmenu_collector_found_set_distance (DbusmenuCollectorFound * found, guint distance);
2047+const gchar * dbusmenu_collector_found_get_display (DbusmenuCollectorFound * found);
2048+const gchar * dbusmenu_collector_found_get_db (DbusmenuCollectorFound * found);
2049+void dbusmenu_collector_found_free (DbusmenuCollectorFound * found);
2050+void dbusmenu_collector_found_list_free (GList * found_list);
2051+const gchar * dbusmenu_collector_found_get_indicator (DbusmenuCollectorFound * found);
2052+void dbusmenu_collector_found_set_indicator (DbusmenuCollectorFound * found, const gchar * indicator);
2053+const gchar * dbusmenu_collector_found_get_dbus_addr (DbusmenuCollectorFound * found);
2054+const gchar * dbusmenu_collector_found_get_dbus_path (DbusmenuCollectorFound * found);
2055+gint dbusmenu_collector_found_get_dbus_id (DbusmenuCollectorFound * found);
2056+const gchar * dbusmenu_collector_found_get_app_icon (DbusmenuCollectorFound * found);
2057+void dbusmenu_collector_found_set_app_icon (DbusmenuCollectorFound * found, const gchar * app_icon);
2058+
2059+G_END_DECLS
2060+
2061+#endif
2062
2063=== added file 'src/distance.c'
2064--- src/distance.c 1970-01-01 00:00:00 +0000
2065+++ src/distance.c 2012-01-27 17:10:57 +0000
2066@@ -0,0 +1,401 @@
2067+/*
2068+Functions to calculate the distance between two strings.
2069+
2070+Copyright 2011 Canonical Ltd.
2071+
2072+Authors:
2073+ Ted Gould <ted@canonical.com>
2074+
2075+This program is free software: you can redistribute it and/or modify it
2076+under the terms of the GNU General Public License version 3, as published
2077+by the Free Software Foundation.
2078+
2079+This program is distributed in the hope that it will be useful, but
2080+WITHOUT ANY WARRANTY; without even the implied warranties of
2081+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2082+PURPOSE. See the GNU General Public License for more details.
2083+
2084+You should have received a copy of the GNU General Public License along
2085+with this program. If not, see <http://www.gnu.org/licenses/>.
2086+*/
2087+
2088+#include <glib-object.h>
2089+#include <glib/gprintf.h>
2090+#include <glib/gi18n.h>
2091+#include <gio/gio.h>
2092+
2093+#include "distance.h"
2094+#include "utils.h"
2095+
2096+#define ADD_PENALTY get_settings_uint(get_settings(), "add-penalty", 10)
2097+#define PRE_ADD_PENALTY get_settings_uint(get_settings(), "add-penalty-pre", 1)
2098+#define DROP_PENALTY get_settings_uint(get_settings(), "drop-penalty", 10)
2099+#define END_DROP_PENALTY get_settings_uint(get_settings(), "drop-penalty-end", 10)
2100+#define TRANSPOSE_PENALTY get_settings_uint(get_settings(), "transpose-penalty", 10)
2101+#define SWAP_PENALTY get_settings_uint(get_settings(), "swap-penalty", 10)
2102+#define SWAP_CASE_PENALTY get_settings_uint(get_settings(), "swap-penalty-case", 1)
2103+
2104+/* Checks to see if we can get the setting, and if we can use that,
2105+ otherwise use the fallback value we have here */
2106+static GSettings *
2107+get_settings (void)
2108+{
2109+ static gboolean first = TRUE;
2110+ static GSettings * settings = NULL;
2111+
2112+ if (first) {
2113+ first = FALSE;
2114+ if (settings_schema_exists("com.canonical.indicator.appmenu.hud.search")) {
2115+ settings = g_settings_new("com.canonical.indicator.appmenu.hud.search");
2116+ }
2117+ }
2118+
2119+ return settings;
2120+}
2121+
2122+static gboolean
2123+ignore_character (gchar inchar)
2124+{
2125+ static gchar * ignore = NULL;
2126+ if (ignore == NULL) {
2127+ /* TRANSLATORS: These are chacaters that should not be considered
2128+ mistakes in the comparison functions. Typically they are gramatical
2129+ characters that can be found in menus. */
2130+ ignore = _(" _->");
2131+ }
2132+
2133+ int i;
2134+ for (i = 0; i < 4; i++) {
2135+ if (ignore[i] == inchar) {
2136+ return TRUE;
2137+ }
2138+ }
2139+ return FALSE;
2140+}
2141+
2142+static guint
2143+swap_cost (gchar a, gchar b)
2144+{
2145+ if (a == b)
2146+ return 0;
2147+ if (ignore_character(a) || ignore_character(b))
2148+ return 0;
2149+ if (g_unichar_toupper(a) == g_unichar_toupper(b))
2150+ return SWAP_CASE_PENALTY; /* Some penalty, but close */
2151+ return SWAP_PENALTY;
2152+}
2153+
2154+#define MATRIX_VAL(needle_loc, haystack_loc) (penalty_matrix[(needle_loc + 1) + (haystack_loc + 1) * (len_needle + 1)])
2155+
2156+static void
2157+dumpmatrix (const gchar * needle, guint len_needle, const gchar * haystack, guint len_haystack, guint * penalty_matrix)
2158+{
2159+#ifndef DUMP_MATRIX
2160+ return;
2161+#endif
2162+ gint i, j;
2163+
2164+ g_printf("\n");
2165+ /* Character Column */
2166+ g_printf(" ");
2167+ /* Base val column */
2168+ g_printf(" ");
2169+ for (i = 0; i < len_needle; i++) {
2170+ g_printf("%c ", needle[i]);
2171+ }
2172+ g_printf("\n");
2173+
2174+ /* Character Column */
2175+ g_printf(" ");
2176+ for (i = -1; i < (gint)len_needle; i++) {
2177+ g_printf("%u ", MATRIX_VAL(i, -1));
2178+ }
2179+ g_printf("\n");
2180+
2181+ for (j = 0; j < len_haystack; j++) {
2182+ g_printf("%c ", haystack[j]);
2183+ for (i = -1; i < (gint)len_needle; i++) {
2184+ g_printf("%u ", MATRIX_VAL(i, j));
2185+ }
2186+ g_printf("\n");
2187+ }
2188+ g_printf("\n");
2189+
2190+ return;
2191+}
2192+
2193+static guint
2194+calculate_token_distance (const gchar * needle, const gchar * haystack)
2195+{
2196+ // g_debug("Comparing token '%s' to token '%s'", needle, haystack);
2197+
2198+ guint len_needle = 0;
2199+ guint len_haystack = 0;
2200+
2201+ if (needle != NULL) {
2202+ len_needle = g_utf8_strlen(needle, -1);
2203+ }
2204+
2205+ if (haystack != NULL) {
2206+ len_haystack = g_utf8_strlen(haystack, -1);
2207+ }
2208+
2209+ /* Handle the cases of very short or NULL strings quickly */
2210+ if (len_needle == 0) {
2211+ return DROP_PENALTY * len_haystack;
2212+ }
2213+
2214+ if (len_haystack == 0) {
2215+ return ADD_PENALTY * len_needle;
2216+ }
2217+
2218+ /* Allocate the matrix of penalties */
2219+ guint * penalty_matrix = g_malloc0(sizeof(guint) * (len_needle + 1) * (len_haystack + 1));
2220+ int i;
2221+
2222+ /* Take the first row and first column and make them additional letter penalties */
2223+ for (i = 0; i < len_needle + 1; i++) {
2224+ MATRIX_VAL(i - 1, -1) = i * ADD_PENALTY;
2225+ }
2226+
2227+ for (i = 0; i < len_haystack + 1; i++) {
2228+ if (i < len_haystack - len_needle) {
2229+ MATRIX_VAL(-1, i - 1) = i * PRE_ADD_PENALTY;
2230+ } else {
2231+ MATRIX_VAL(-1, i - 1) = (len_haystack - len_needle) * PRE_ADD_PENALTY + (i - (len_haystack - len_needle)) * DROP_PENALTY;
2232+ }
2233+ }
2234+
2235+ /* Now go through the matrix building up the penalties */
2236+ int ineedle, ihaystack;
2237+ for (ineedle = 0; ineedle < len_needle; ineedle++) {
2238+ for (ihaystack = 0; ihaystack < len_haystack; ihaystack++) {
2239+ char needle_let = needle[ineedle];
2240+ char haystack_let = haystack[ihaystack];
2241+
2242+ guint subst_pen = MATRIX_VAL(ineedle - 1, ihaystack - 1) + swap_cost(needle_let, haystack_let);
2243+ guint drop_pen = MATRIX_VAL(ineedle - 1, ihaystack);
2244+ if (ineedle < ihaystack) {
2245+ drop_pen += DROP_PENALTY;
2246+ } else {
2247+ drop_pen += END_DROP_PENALTY;
2248+ }
2249+
2250+ guint add_pen = MATRIX_VAL(ineedle, ihaystack - 1);
2251+ if (len_haystack - len_needle - ineedle > 0) {
2252+ add_pen += PRE_ADD_PENALTY;
2253+ } else {
2254+ add_pen += ADD_PENALTY;
2255+ }
2256+ guint transpose_pen = drop_pen + 1; /* ensures won't be chosen */
2257+
2258+ if (ineedle > 0 && ihaystack > 0 && needle_let == haystack[ihaystack - 1] && haystack_let == needle[ineedle - 1]) {
2259+ transpose_pen = MATRIX_VAL(ineedle - 2, ihaystack - 2) + TRANSPOSE_PENALTY;
2260+ }
2261+
2262+ MATRIX_VAL(ineedle, ihaystack) = MIN(MIN(subst_pen, drop_pen), MIN(add_pen, transpose_pen));
2263+ }
2264+ }
2265+
2266+ dumpmatrix(needle, len_needle, haystack, len_haystack, penalty_matrix);
2267+
2268+ guint retval = penalty_matrix[(len_needle + 1) * (len_haystack + 1) - 1];
2269+ g_free(penalty_matrix);
2270+
2271+ return retval;
2272+}
2273+
2274+/* Looks through the array of paths and tries to find a minimum path
2275+ looking up from the needle specified. This way we can look through
2276+ all the possible paths */
2277+guint
2278+minimize_distance_recurse (guint needle, guint num_needles, guint haystack, guint num_haystacks, guint * distances, guint * matches)
2279+{
2280+ gint i;
2281+
2282+ /* Put where we are in the array so that we don't forget */
2283+ matches[needle] = haystack;
2284+
2285+ /* First check to see if we've already used this entry */
2286+ for (i = needle - 1; i >= 0; i--) {
2287+ if (matches[i] == haystack) {
2288+ return G_MAXUINT;
2289+ }
2290+ }
2291+
2292+ /* If we're the last needle, we can return our distance */
2293+ if (needle + 1 >= num_needles) {
2294+ return distances[(needle * num_haystacks) + haystack];
2295+ }
2296+
2297+ guint * local_match = g_new0(guint, num_needles);
2298+ for (i = 0; i < num_needles && i < needle + 1; i++) {
2299+ local_match[i] = matches[i];
2300+ }
2301+
2302+ /* Now look where we can get the minimum with the other needles */
2303+ guint min = G_MAXUINT;
2304+ for (i = 0; i < num_haystacks; i++) {
2305+ guint local = minimize_distance_recurse(needle + 1, num_needles, i, num_haystacks, distances, local_match);
2306+
2307+ if (local < min) {
2308+ min = local;
2309+
2310+ int j;
2311+ for (j = needle + 1; j < num_needles; j++) {
2312+ matches[j] = local_match[j];
2313+ }
2314+ }
2315+ }
2316+
2317+ g_free(local_match);
2318+
2319+ /* Return the min of everyone else plus our distance */
2320+ if (min < G_MAXUINT) {
2321+ min += distances[(needle * num_haystacks) + haystack];
2322+ }
2323+
2324+ return min;
2325+}
2326+
2327+/* Figuring out the lowest path through the distance array
2328+ where we don't use the same haystack tokens */
2329+guint
2330+minimize_distance (guint num_needles, guint num_haystacks, guint * distances, guint * matches)
2331+{
2332+ guint final_distance = G_MAXUINT;
2333+ guint * local_matches = g_new0(guint, num_needles);
2334+
2335+ guint haystack_token;
2336+ for (haystack_token = 0; haystack_token < num_haystacks; haystack_token++) {
2337+ guint distance = minimize_distance_recurse(0, num_needles, haystack_token, num_haystacks, distances, local_matches);
2338+
2339+ if (distance < final_distance) {
2340+ final_distance = distance;
2341+
2342+ guint match_cnt;
2343+ for (match_cnt = 0; match_cnt < num_needles; match_cnt++) {
2344+ matches[match_cnt] = local_matches[match_cnt];
2345+ }
2346+ }
2347+ }
2348+
2349+ g_free(local_matches);
2350+
2351+ return final_distance;
2352+}
2353+
2354+/* Dups a specific token in the array of strv arrays */
2355+gchar *
2356+find_token (guint token_number, GStrv * haystacks, guint num_haystacks)
2357+{
2358+ guint haystack;
2359+
2360+ for (haystack = 0; haystack < num_haystacks; haystack++) {
2361+ guint strvlen = g_strv_length(haystacks[haystack]);
2362+
2363+ if (token_number < strvlen) {
2364+ return g_strdup(haystacks[haystack][token_number]);
2365+ }
2366+
2367+ token_number -= strvlen;
2368+ }
2369+
2370+ return NULL;
2371+}
2372+
2373+#define SEPARATORS " .->"
2374+
2375+guint
2376+calculate_distance (const gchar * needle, GStrv haystacks, GStrv * matches)
2377+{
2378+ g_return_val_if_fail(needle != NULL || haystacks != NULL, G_MAXUINT);
2379+ guint final_distance = G_MAXUINT;
2380+
2381+ if (needle == NULL) {
2382+ return DROP_PENALTY * g_utf8_strlen(haystacks[0], 1024);
2383+ }
2384+ if (haystacks == NULL) {
2385+ return ADD_PENALTY * g_utf8_strlen(needle, 1024);
2386+ }
2387+
2388+ /* Tokenize all the haystack strings */
2389+ gint i;
2390+ guint num_haystacks = g_strv_length(haystacks);
2391+ guint num_haystack_tokens = 0;
2392+ GStrv * haystacks_array = g_new0(GStrv, num_haystacks);
2393+ for (i = 0; i < num_haystacks; i++) {
2394+ haystacks_array[i] = g_strsplit_set(haystacks[i], SEPARATORS, 0);
2395+ num_haystack_tokens += g_strv_length(haystacks_array[i]);
2396+ }
2397+
2398+ /* Tokenize our needles the same way */
2399+ GStrv needle_tokens = g_strsplit_set(needle, SEPARATORS, 0);
2400+ guint num_needle_tokens = g_strv_length(needle_tokens);
2401+
2402+ /* If we can't even find a set that works, let's just cut
2403+ our losses here */
2404+ if (num_needle_tokens > num_haystack_tokens) {
2405+ goto cleanup_tokens;
2406+ }
2407+
2408+ /* We need a place to store all the distances */
2409+ guint * distances = g_new0(guint, num_haystack_tokens * num_needle_tokens);
2410+
2411+ /* Calculate all the distance combinations */
2412+ gint needle_token;
2413+ for (needle_token = 0; needle_tokens[needle_token] != NULL; needle_token++) {
2414+ gchar * ineedle = needle_tokens[needle_token];
2415+
2416+ guint haystacks_cnt;
2417+ guint haystack_token_cnt = 0;
2418+ for (haystacks_cnt = 0; haystacks_cnt < num_haystacks; haystacks_cnt++) {
2419+ guint haystack_cnt;
2420+ for (haystack_cnt = 0; haystacks_array[haystacks_cnt][haystack_cnt] != NULL; haystack_cnt++) {
2421+ gchar * ihaystack = haystacks_array[haystacks_cnt][haystack_cnt];
2422+ guint distance = calculate_token_distance(ineedle, ihaystack);
2423+
2424+ distances[(needle_token * num_haystack_tokens) + haystack_token_cnt] = distance;
2425+ haystack_token_cnt++;
2426+ }
2427+ }
2428+ }
2429+
2430+ /* Now, try to find a path through the array that results in the
2431+ lowest total value */
2432+ guint * final_matches = g_new0(guint, num_needle_tokens);
2433+
2434+ final_distance = minimize_distance(num_needle_tokens, num_haystack_tokens, distances, final_matches);
2435+
2436+ /* Set up an array for matches so that we can enter
2437+ the items as we go */
2438+ if (matches != NULL) {
2439+ GStrv match_array = NULL;
2440+ match_array = g_new0(gchar *, num_needle_tokens + 1);
2441+ match_array[num_needle_tokens] = NULL;
2442+
2443+ /* Copy the strings that we care about */
2444+ int i;
2445+ for (i = 0; i < num_needle_tokens; i++) {
2446+ match_array[i] = find_token(final_matches[i], haystacks_array, num_haystacks);
2447+ }
2448+
2449+ *matches = match_array;
2450+ }
2451+
2452+ g_free(final_matches);
2453+ g_free(distances);
2454+
2455+cleanup_tokens:
2456+ g_strfreev(needle_tokens);
2457+ for (i = 0; i < num_haystacks; i++) {
2458+ g_strfreev(haystacks_array[i]);
2459+ }
2460+ g_free(haystacks_array);
2461+
2462+ if (final_distance != G_MAXUINT) {
2463+ return final_distance / num_needle_tokens;
2464+ } else {
2465+ return G_MAXUINT;
2466+ }
2467+}
2468
2469=== added file 'src/distance.h'
2470--- src/distance.h 1970-01-01 00:00:00 +0000
2471+++ src/distance.h 2012-01-27 17:10:57 +0000
2472@@ -0,0 +1,31 @@
2473+/*
2474+Functions to calculate the distance between two strings.
2475+
2476+Copyright 2011 Canonical Ltd.
2477+
2478+Authors:
2479+ Ted Gould <ted@canonical.com>
2480+
2481+This program is free software: you can redistribute it and/or modify it
2482+under the terms of the GNU General Public License version 3, as published
2483+by the Free Software Foundation.
2484+
2485+This program is distributed in the hope that it will be useful, but
2486+WITHOUT ANY WARRANTY; without even the implied warranties of
2487+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2488+PURPOSE. See the GNU General Public License for more details.
2489+
2490+You should have received a copy of the GNU General Public License along
2491+with this program. If not, see <http://www.gnu.org/licenses/>.
2492+*/
2493+
2494+#ifndef __DISTANCE_H__
2495+#define __DISTANCE_H__
2496+
2497+G_BEGIN_DECLS
2498+
2499+guint calculate_distance (const gchar * needle, GStrv haystack, GStrv * matched);
2500+
2501+G_END_DECLS
2502+
2503+#endif /* __DISTANCE_H__ */
2504
2505=== added file 'src/dump-app-info.c'
2506--- src/dump-app-info.c 1970-01-01 00:00:00 +0000
2507+++ src/dump-app-info.c 2012-01-27 17:10:57 +0000
2508@@ -0,0 +1,174 @@
2509+/*
2510+Prints out application info for debugging and CLI tools
2511+
2512+Copyright 2011 Canonical Ltd.
2513+
2514+Authors:
2515+ Ted Gould <ted@canonical.com>
2516+
2517+This program is free software: you can redistribute it and/or modify it
2518+under the terms of the GNU General Public License version 3, as published
2519+by the Free Software Foundation.
2520+
2521+This program is distributed in the hope that it will be useful, but
2522+WITHOUT ANY WARRANTY; without even the implied warranties of
2523+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2524+PURPOSE. See the GNU General Public License for more details.
2525+
2526+You should have received a copy of the GNU General Public License along
2527+with this program. If not, see <http://www.gnu.org/licenses/>.
2528+*/
2529+
2530+#include "dump-app-info.h"
2531+#include "shared-values.h"
2532+
2533+typedef enum _tree_type_t tree_type_t;
2534+enum _tree_type_t {
2535+ MENU_TYPE,
2536+ ITEM_TYPE
2537+};
2538+
2539+typedef struct _menu_t menu_t;
2540+struct _menu_t {
2541+ tree_type_t tree_type;
2542+ gchar * name;
2543+ int count;
2544+ GList * subitems;
2545+};
2546+
2547+GList *
2548+place_on_tree (GList * tree_in, gchar ** entries)
2549+{
2550+ menu_t * menu;
2551+
2552+ if (entries[0] == NULL) {
2553+ return tree_in;
2554+ }
2555+
2556+ GList * entry = tree_in;
2557+ while (entry != NULL) {
2558+ menu = (menu_t *)entry->data;
2559+
2560+ if (g_strcmp0(menu->name, entries[0]) == 0) {
2561+ break;
2562+ }
2563+
2564+ entry = g_list_next(entry);
2565+ }
2566+
2567+ if (entry != NULL) {
2568+ if (menu->tree_type == ITEM_TYPE) {
2569+ if (entries[1] != NULL) {
2570+ g_warning("Error parsing on entry '%s'", entries[0]);
2571+ } else {
2572+ menu->count++;
2573+ }
2574+ } else {
2575+ menu->subitems = place_on_tree(menu->subitems, &entries[1]);
2576+ }
2577+
2578+ return tree_in;
2579+ }
2580+
2581+ menu = g_new0(menu_t, 1);
2582+ menu->name = g_strdup(entries[0]);
2583+
2584+ if (entries[1] == NULL) {
2585+ /* This is an item */
2586+ menu->tree_type = ITEM_TYPE;
2587+ menu->count = 1;
2588+ menu->subitems = NULL;
2589+ } else {
2590+ /* This is a menu */
2591+ menu->tree_type = MENU_TYPE;
2592+ menu->subitems = place_on_tree(NULL, &entries[1]);
2593+ }
2594+
2595+ return g_list_append(tree_in, menu);
2596+}
2597+
2598+static int
2599+entry_cb (void * user_data, int columns, char ** values, char ** names)
2600+{
2601+ GList ** tree = (GList **)user_data;
2602+
2603+ gchar ** entries = g_strsplit(values[0], DB_SEPARATOR, -1);
2604+
2605+ *tree = place_on_tree(*tree, entries);
2606+
2607+ g_strfreev(entries);
2608+
2609+ return SQLITE_OK;
2610+}
2611+
2612+void
2613+print_tree (GList * tree, guint tab_depth)
2614+{
2615+ if (tree == NULL) {
2616+ return;
2617+ }
2618+
2619+ int i;
2620+ for (i = 0; i < tab_depth; i++) {
2621+ g_print("\t");
2622+ }
2623+
2624+ menu_t * menu = (menu_t *)tree->data;
2625+
2626+ if (menu->tree_type == ITEM_TYPE) {
2627+ g_print("<item name=\"%s\" count=\"%d\" />\n", menu->name, menu->count);
2628+ } else {
2629+ g_print("<menu name=\"%s\">\n", menu->name);
2630+
2631+ GList * subs = menu->subitems;
2632+ while (subs != NULL) {
2633+ print_tree(subs, tab_depth + 1);
2634+ subs = g_list_next(subs);
2635+ }
2636+
2637+ for (i = 0; i < tab_depth; i++) {
2638+ g_print("\t");
2639+ }
2640+ g_print("</menu>\n");
2641+ }
2642+
2643+ return;
2644+}
2645+
2646+void
2647+dump_app_info (const gchar * app, const gchar * domain, sqlite3 * db)
2648+{
2649+ g_return_if_fail(app != NULL);
2650+ g_return_if_fail(db != NULL);
2651+
2652+ g_print("<hudappinfo>\n");
2653+
2654+ g_print("\t<desktopfile path=\"%s\" />\n", app);
2655+
2656+ if (domain != NULL) {
2657+ g_print("\t<gettext-domain>%s</gettext-domain>\n", domain);
2658+ }
2659+
2660+ gchar * statement = g_strdup_printf("select entry from usage where application = '%s';", app);
2661+
2662+ int exec_status = SQLITE_OK;
2663+ gchar * failstring = NULL;
2664+ GList * tree = NULL;
2665+ exec_status = sqlite3_exec(db,
2666+ statement,
2667+ entry_cb, &tree, &failstring);
2668+ if (exec_status != SQLITE_OK) {
2669+ g_warning("Unable to get entries: %s", failstring);
2670+ }
2671+
2672+ g_free(statement);
2673+
2674+ if (tree != NULL) {
2675+ g_print("\t<menus>\n");
2676+ print_tree(tree, 2);
2677+ g_print("\t</menus>\n");
2678+ }
2679+
2680+ g_print("</hudappinfo>\n");
2681+ return;
2682+}
2683
2684=== added file 'src/dump-app-info.h'
2685--- src/dump-app-info.h 1970-01-01 00:00:00 +0000
2686+++ src/dump-app-info.h 2012-01-27 17:10:57 +0000
2687@@ -0,0 +1,30 @@
2688+/*
2689+Prints out application info for debugging and CLI tools
2690+
2691+Copyright 2011 Canonical Ltd.
2692+
2693+Authors:
2694+ Ted Gould <ted@canonical.com>
2695+
2696+This program is free software: you can redistribute it and/or modify it
2697+under the terms of the GNU General Public License version 3, as published
2698+by the Free Software Foundation.
2699+
2700+This program is distributed in the hope that it will be useful, but
2701+WITHOUT ANY WARRANTY; without even the implied warranties of
2702+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2703+PURPOSE. See the GNU General Public License for more details.
2704+
2705+You should have received a copy of the GNU General Public License along
2706+with this program. If not, see <http://www.gnu.org/licenses/>.
2707+*/
2708+
2709+#ifndef __DUMP_APP_INFO_H__
2710+#define __DUMP_APP_INFO_H__
2711+
2712+#include <glib.h>
2713+#include <sqlite3.h>
2714+
2715+void dump_app_info (const gchar * app, const gchar * domain, sqlite3 * db);
2716+
2717+#endif /* __DUMP_APP_INFO_H__ */
2718
2719=== added file 'src/hud-cli.c'
2720--- src/hud-cli.c 1970-01-01 00:00:00 +0000
2721+++ src/hud-cli.c 2012-01-27 17:10:57 +0000
2722@@ -0,0 +1,237 @@
2723+/*
2724+Small utility to excersise the HUD from the command line
2725+
2726+Copyright 2011 Canonical Ltd.
2727+
2728+Authors:
2729+ Ted Gould <ted@canonical.com>
2730+
2731+This program is free software: you can redistribute it and/or modify it
2732+under the terms of the GNU General Public License version 3, as published
2733+by the Free Software Foundation.
2734+
2735+This program is distributed in the hope that it will be useful, but
2736+WITHOUT ANY WARRANTY; without even the implied warranties of
2737+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2738+PURPOSE. See the GNU General Public License for more details.
2739+
2740+You should have received a copy of the GNU General Public License along
2741+with this program. If not, see <http://www.gnu.org/licenses/>.
2742+*/
2743+
2744+#include <glib.h>
2745+#include <gio/gunixinputstream.h>
2746+#include <gtk/gtk.h>
2747+#include <unistd.h>
2748+#include <stdio.h>
2749+#include <readline/readline.h>
2750+#include <readline/history.h>
2751+#include <stdlib.h>
2752+#include <curses.h>
2753+
2754+#include "shared-values.h"
2755+#include "hud.interface.h"
2756+
2757+
2758+static void print_suggestions(const char * query);
2759+static GDBusProxy * proxy = NULL;
2760+static GVariant * last_key = NULL;
2761+static void update(char *string);
2762+void sighandler(int);
2763+
2764+WINDOW *twindow = NULL;
2765+int use_curses = 0;
2766+
2767+int
2768+main (int argc, char *argv[])
2769+{
2770+
2771+ g_type_init();
2772+
2773+ int single_char;
2774+ int pos = 0;
2775+
2776+ char *line = (char*) malloc(256 * sizeof(char));
2777+
2778+ signal(SIGINT, sighandler);
2779+
2780+ GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
2781+ g_return_val_if_fail(session != NULL, 1);
2782+
2783+ GDBusNodeInfo * nodeinfo = g_dbus_node_info_new_for_xml(hud_interface, NULL);
2784+ g_return_val_if_fail(nodeinfo != NULL, 1);
2785+
2786+ GDBusInterfaceInfo * ifaceinfo = g_dbus_node_info_lookup_interface(nodeinfo, DBUS_IFACE);
2787+ g_return_val_if_fail(ifaceinfo != NULL, 1);
2788+
2789+ proxy = g_dbus_proxy_new_sync(session,
2790+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
2791+ ifaceinfo,
2792+ DBUS_NAME,
2793+ DBUS_PATH,
2794+ DBUS_IFACE,
2795+ NULL, NULL);
2796+ g_return_val_if_fail(proxy != NULL, 1);
2797+
2798+
2799+ // reading from pipe
2800+ if (!isatty (STDIN_FILENO) ) {
2801+ size_t a;
2802+ int r;
2803+ r = getline (&line, &a, stdin);
2804+
2805+ if ( r == -1 ){
2806+ perror("Error reading from pipe");
2807+ exit(EXIT_FAILURE);
2808+ }
2809+
2810+ // get rid of newline
2811+ if( line[r-1] == '\n' )
2812+ line[r-1] = '\0';
2813+
2814+ printf("\nsearch token: %s\n", line);
2815+ print_suggestions( line );
2816+ }
2817+ // read command line parameter - hud-cli "search string"
2818+ else if( argc > 1 ){
2819+ printf("\nsearch token: %s\n", argv[1]);
2820+ print_suggestions( argv[1] );
2821+ }
2822+ // interactive mode
2823+ else{
2824+ use_curses = 1;
2825+
2826+ twindow = initscr();
2827+ cbreak();
2828+ keypad(stdscr, TRUE);
2829+ noecho();
2830+
2831+ /* initialize the query screen */
2832+ update( "" );
2833+
2834+ /* interactive shell interface */
2835+ while( 1 ){
2836+
2837+ single_char = getch();
2838+ /* need to go left in the buffer */
2839+ if ( single_char == KEY_BACKSPACE ){
2840+ /* don't go too far left */
2841+ if( pos > 0 ){
2842+ pos--;
2843+ line[pos] = '\0';
2844+ update( line );
2845+ }
2846+ else
2847+ ; /* we are at the beginning of the buffer already */
2848+ }
2849+ /* ENTER will trigger the action for the first selected suggestion */
2850+ else if ( single_char == '\n' ){
2851+
2852+ /* FIXME: execute action on RETURN */
2853+ break;
2854+ }
2855+ /* add character to the buffer and terminate string */
2856+ else{
2857+ if ( pos < 256 -1 ){ // -1 for \0
2858+ line[pos] = single_char;
2859+ line[pos+1] = '\0';
2860+ pos++;
2861+ update( line );
2862+ }
2863+ else {
2864+
2865+ }
2866+ }
2867+ }
2868+ endwin();
2869+ }
2870+
2871+ free(line);
2872+
2873+ g_dbus_node_info_unref(nodeinfo);
2874+ g_object_unref(session);
2875+
2876+ return 0;
2877+}
2878+
2879+static void
2880+update( char *string ){
2881+
2882+ werase(twindow);
2883+ mvwprintw(twindow, 7, 10, "Search: %s", string);
2884+
2885+ print_suggestions( string );
2886+
2887+ // move cursor back to input line
2888+ wmove( twindow, 7, 10 + 8 + strlen(string) );
2889+
2890+ refresh();
2891+}
2892+
2893+
2894+static void
2895+print_suggestions (const char *query)
2896+{
2897+ GError * error = NULL;
2898+ GVariantBuilder querybuilder;
2899+ g_variant_builder_init(&querybuilder, G_VARIANT_TYPE_TUPLE);
2900+ g_variant_builder_add_value(&querybuilder, g_variant_new_string(query));
2901+ g_variant_builder_add_value(&querybuilder, g_variant_new_int32(5));
2902+
2903+ GVariant * suggests = g_dbus_proxy_call_sync(proxy,
2904+ "StartQuery",
2905+ g_variant_builder_end(&querybuilder),
2906+ G_DBUS_CALL_FLAGS_NONE,
2907+ -1,
2908+ NULL,
2909+ &error);
2910+
2911+ if (error != NULL) {
2912+ g_warning("Unable to get suggestions: %s", error->message);
2913+ g_error_free(error);
2914+ return ;
2915+ }
2916+
2917+ GVariant * target = g_variant_get_child_value(suggests, 0);
2918+ g_variant_unref(target);
2919+
2920+ GVariant * suggestions = g_variant_get_child_value(suggests, 1);
2921+ GVariantIter iter;
2922+ g_variant_iter_init(&iter, suggestions);
2923+ gchar * suggestion = NULL;
2924+ gchar * appicon = NULL;
2925+ gchar * icon = NULL;
2926+ gchar * complete = NULL;
2927+ gchar * accel = NULL;
2928+ GVariant * key = NULL;
2929+
2930+ last_key = NULL;
2931+
2932+ int i=0;
2933+ char *clean_line;
2934+
2935+ while (g_variant_iter_loop(&iter, "(sssssv)", &suggestion, &appicon, &icon, &complete, &accel, &key)) {
2936+ if( use_curses)
2937+ mvwprintw(twindow, 9 + i, 15, "%s", suggestion);
2938+ else{
2939+ pango_parse_markup(suggestion, -1, 0, NULL, &clean_line, NULL, NULL);
2940+ printf("\t%s\n", clean_line);
2941+ free(clean_line);
2942+ }
2943+ i++;
2944+
2945+ }
2946+
2947+ g_variant_unref(suggestions);
2948+
2949+ /* NOTE: Ignoring the Query Key as we're not handling the signal now
2950+ and just letting it timeout. */
2951+
2952+ return;
2953+}
2954+
2955+void sighandler(int signal){
2956+ endwin();
2957+ g_object_unref(proxy);
2958+ exit(EXIT_SUCCESS);
2959+}
2960
2961=== added file 'src/hud-dbus.c'
2962--- src/hud-dbus.c 1970-01-01 00:00:00 +0000
2963+++ src/hud-dbus.c 2012-01-27 17:10:57 +0000
2964@@ -0,0 +1,286 @@
2965+/*
2966+DBus facing code for the HUD
2967+
2968+Copyright 2011 Canonical Ltd.
2969+
2970+Authors:
2971+ Ted Gould <ted@canonical.com>
2972+
2973+This program is free software: you can redistribute it and/or modify it
2974+under the terms of the GNU General Public License version 3, as published
2975+by the Free Software Foundation.
2976+
2977+This program is distributed in the hope that it will be useful, but
2978+WITHOUT ANY WARRANTY; without even the implied warranties of
2979+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2980+PURPOSE. See the GNU General Public License for more details.
2981+
2982+You should have received a copy of the GNU General Public License along
2983+with this program. If not, see <http://www.gnu.org/licenses/>.
2984+*/
2985+
2986+#ifdef HAVE_CONFIG_H
2987+#include "config.h"
2988+#endif
2989+
2990+#include <gio/gio.h>
2991+
2992+#include "shared-values.h"
2993+#include "hud.interface.h"
2994+#include "hud-dbus.h"
2995+#include "hud-search.h"
2996+
2997+struct _HudDbusPrivate {
2998+ GDBusConnection * bus;
2999+ GCancellable * bus_lookup;
3000+ guint bus_registration;
3001+ HudSearch * search;
3002+};
3003+
3004+#define HUD_DBUS_GET_PRIVATE(o) \
3005+(G_TYPE_INSTANCE_GET_PRIVATE ((o), HUD_DBUS_TYPE, HudDbusPrivate))
3006+
3007+static void hud_dbus_dispose (GObject *object);
3008+static void hud_dbus_finalize (GObject *object);
3009+
3010+static void bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data);
3011+static void bus_method (GDBusConnection *connection,
3012+ const gchar *sender,
3013+ const gchar *object_path,
3014+ const gchar *interface_name,
3015+ const gchar *method_name,
3016+ GVariant *parameters,
3017+ GDBusMethodInvocation *invocation,
3018+ gpointer user_data);
3019+static GVariant * get_suggestions (HudDbus * self, const gchar * query);
3020+static void execute_query (HudDbus * self, GVariant * key, guint timestamp);
3021+
3022+
3023+G_DEFINE_TYPE (HudDbus, hud_dbus, G_TYPE_OBJECT);
3024+static GDBusNodeInfo * node_info = NULL;
3025+static GDBusInterfaceInfo * iface_info = NULL;
3026+static GDBusInterfaceVTable bus_vtable = {
3027+ method_call: bus_method,
3028+ get_property: NULL,
3029+ set_property: NULL,
3030+};
3031+
3032+static void
3033+hud_dbus_class_init (HudDbusClass *klass)
3034+{
3035+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
3036+
3037+ g_type_class_add_private (klass, sizeof (HudDbusPrivate));
3038+
3039+ object_class->dispose = hud_dbus_dispose;
3040+ object_class->finalize = hud_dbus_finalize;
3041+
3042+ if (node_info == NULL) {
3043+ GError * error = NULL;
3044+
3045+ node_info = g_dbus_node_info_new_for_xml(hud_interface, &error);
3046+ if (error != NULL) {
3047+ g_error("Unable to parse HUD interface: %s", error->message);
3048+ g_error_free(error);
3049+ }
3050+ }
3051+
3052+ if (node_info != NULL && iface_info == NULL) {
3053+ iface_info = g_dbus_node_info_lookup_interface(node_info, DBUS_IFACE);
3054+ if (iface_info == NULL) {
3055+ g_error("Unable to find interface '" DBUS_IFACE "'");
3056+ }
3057+ }
3058+
3059+ return;
3060+}
3061+
3062+static void
3063+hud_dbus_init (HudDbus *self)
3064+{
3065+ self->priv = HUD_DBUS_GET_PRIVATE(self);
3066+
3067+ self->priv->bus = NULL;
3068+ self->priv->bus_lookup = NULL;
3069+ self->priv->bus_registration = 0;
3070+ self->priv->search = NULL;
3071+
3072+ self->priv->bus_lookup = g_cancellable_new();
3073+ g_bus_get(G_BUS_TYPE_SESSION, self->priv->bus_lookup, bus_got_cb, self);
3074+
3075+ self->priv->search = hud_search_new();
3076+
3077+ return;
3078+}
3079+
3080+static void
3081+hud_dbus_dispose (GObject *object)
3082+{
3083+ HudDbus * self = HUD_DBUS(object);
3084+ g_return_if_fail(self != NULL);
3085+
3086+ if (self->priv->bus_lookup != NULL) {
3087+ g_cancellable_cancel(self->priv->bus_lookup);
3088+ g_object_unref(self->priv->bus_lookup);
3089+ self->priv->bus_lookup = NULL;
3090+ }
3091+
3092+ if (self->priv->bus_registration != 0) {
3093+ g_dbus_connection_unregister_object(self->priv->bus, self->priv->bus_registration);
3094+ self->priv->bus_registration = 0;
3095+ }
3096+
3097+ if (self->priv->bus != NULL) {
3098+ g_object_unref(self->priv->bus);
3099+ self->priv->bus = NULL;
3100+ }
3101+
3102+ if (self->priv->search != NULL) {
3103+ g_object_unref(self->priv->search);
3104+ self->priv->search = NULL;
3105+ }
3106+
3107+ G_OBJECT_CLASS (hud_dbus_parent_class)->dispose (object);
3108+ return;
3109+}
3110+
3111+static void
3112+hud_dbus_finalize (GObject *object)
3113+{
3114+
3115+ G_OBJECT_CLASS (hud_dbus_parent_class)->finalize (object);
3116+ return;
3117+}
3118+
3119+HudDbus *
3120+hud_dbus_new (void)
3121+{
3122+ return g_object_new(HUD_DBUS_TYPE, NULL);
3123+}
3124+
3125+static void
3126+bus_got_cb (GObject *object, GAsyncResult * res, gpointer user_data)
3127+{
3128+ GError * error = NULL;
3129+ HudDbus * self = HUD_DBUS(user_data);
3130+ GDBusConnection * bus;
3131+
3132+ bus = g_bus_get_finish(res, &error);
3133+ if (error != NULL) {
3134+ g_critical("Unable to get bus: %s", error->message);
3135+ g_error_free(error);
3136+ return;
3137+ }
3138+
3139+ self->priv->bus = bus;
3140+
3141+ /* Register object */
3142+ self->priv->bus_registration = g_dbus_connection_register_object(bus,
3143+ /* path */ DBUS_PATH,
3144+ /* interface */ iface_info,
3145+ /* vtable */ &bus_vtable,
3146+ /* userdata */ self,
3147+ /* destroy */ NULL,
3148+ /* error */ &error);
3149+
3150+ return;
3151+}
3152+
3153+static void
3154+bus_method (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data)
3155+{
3156+ HudDbus * self = HUD_DBUS(user_data);
3157+
3158+ if (g_strcmp0(method_name, "StartQuery") == 0) {
3159+ GVariant * ret = NULL;
3160+ gchar * query = NULL;
3161+ guint suggestion_count = 5;
3162+
3163+ g_variant_get(parameters, "(si)", &query, &suggestion_count);
3164+
3165+ ret = get_suggestions(self, query); // TODO: Add count
3166+
3167+ g_dbus_method_invocation_return_value(invocation, ret);
3168+ g_free(query);
3169+ } else if (g_strcmp0(method_name, "ExecuteQuery") == 0) {
3170+ GVariant * key = NULL;
3171+ guint timestamp = 0;
3172+
3173+ key = g_variant_get_child_value(parameters, 0);
3174+ g_variant_get_child(parameters, 1, "u", &timestamp);
3175+
3176+ execute_query(self, key, timestamp);
3177+
3178+ g_dbus_method_invocation_return_value(invocation, NULL);
3179+ g_variant_unref(key);
3180+ } else if (g_strcmp0(method_name, "CloseQuery") == 0) {
3181+ /* TODO: Use this */
3182+ g_dbus_method_invocation_return_value(invocation, NULL);
3183+ }
3184+
3185+ return;
3186+}
3187+
3188+/* Respond to the GetSuggestions command from DBus by looking
3189+ in our HUD search object for suggestions from that query */
3190+static GVariant *
3191+get_suggestions (HudDbus * self, const gchar * query)
3192+{
3193+ /* Do the search */
3194+ gchar * desktop = NULL;
3195+ gchar * target = NULL;
3196+ GList * suggestions = hud_search_suggestions(self->priv->search, query, &desktop, &target);
3197+
3198+ /* Build into into a variant */
3199+ GVariantBuilder ret;
3200+ g_variant_builder_init(&ret, G_VARIANT_TYPE_TUPLE);
3201+ g_variant_builder_add_value(&ret, g_variant_new_string(target));
3202+
3203+ /* Free the strings */
3204+ g_free(target);
3205+ g_free(desktop);
3206+
3207+ if (suggestions != NULL) {
3208+ GList * suggestion = suggestions;
3209+ GVariantBuilder builder;
3210+ g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
3211+
3212+ while (suggestion != NULL) {
3213+ HudSearchSuggest * suggest = (HudSearchSuggest *)suggestion->data;
3214+
3215+ GVariantBuilder tuple;
3216+ g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
3217+ g_variant_builder_add_value(&tuple, g_variant_new_string(hud_search_suggest_get_display(suggest)));
3218+ g_variant_builder_add_value(&tuple, g_variant_new_string(hud_search_suggest_get_app_icon(suggest)));
3219+ g_variant_builder_add_value(&tuple, g_variant_new_string(hud_search_suggest_get_item_icon(suggest)));
3220+ g_variant_builder_add_value(&tuple, g_variant_new_string("")); /* complete text */
3221+ g_variant_builder_add_value(&tuple, g_variant_new_string("")); /* accell */
3222+ g_variant_builder_add_value(&tuple, hud_search_suggest_get_key(suggest));
3223+
3224+ g_variant_builder_add_value(&builder, g_variant_builder_end(&tuple));
3225+
3226+ suggestion = g_list_next(suggestion);
3227+ }
3228+
3229+ g_variant_builder_add_value(&ret, g_variant_builder_end(&builder));
3230+ } else {
3231+ /* If we didn't get any suggestions we need to build
3232+ a null array to make the DBus interface happy */
3233+ g_variant_builder_add_value(&ret, g_variant_new_array(G_VARIANT_TYPE("(sssssv)"), NULL, 0));
3234+ }
3235+
3236+ /* Clean up the list */
3237+ g_list_free_full(suggestions, (GDestroyNotify)hud_search_suggest_free);
3238+
3239+ /* Add a query key */
3240+ g_variant_builder_add_value(&ret, g_variant_new_variant(g_variant_new_string("query key")));
3241+
3242+ return g_variant_builder_end(&ret);
3243+}
3244+
3245+static void
3246+execute_query (HudDbus * self, GVariant * key, guint timestamp)
3247+{
3248+ hud_search_execute(self->priv->search, key, timestamp);
3249+ return;
3250+}
3251
3252=== added file 'src/hud-dbus.h'
3253--- src/hud-dbus.h 1970-01-01 00:00:00 +0000
3254+++ src/hud-dbus.h 2012-01-27 17:10:57 +0000
3255@@ -0,0 +1,54 @@
3256+/*
3257+DBus facing code for the HUD
3258+
3259+Copyright 2011 Canonical Ltd.
3260+
3261+Authors:
3262+ Ted Gould <ted@canonical.com>
3263+
3264+This program is free software: you can redistribute it and/or modify it
3265+under the terms of the GNU General Public License version 3, as published
3266+by the Free Software Foundation.
3267+
3268+This program is distributed in the hope that it will be useful, but
3269+WITHOUT ANY WARRANTY; without even the implied warranties of
3270+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3271+PURPOSE. See the GNU General Public License for more details.
3272+
3273+You should have received a copy of the GNU General Public License along
3274+with this program. If not, see <http://www.gnu.org/licenses/>.
3275+*/
3276+
3277+#ifndef __HUD_DBUS_H__
3278+#define __HUD_DBUS_H__
3279+
3280+#include <glib-object.h>
3281+
3282+G_BEGIN_DECLS
3283+
3284+#define HUD_DBUS_TYPE (hud_dbus_get_type ())
3285+#define HUD_DBUS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HUD_DBUS_TYPE, HudDbus))
3286+#define HUD_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HUD_DBUS_TYPE, HudDbusClass))
3287+#define IS_HUD_DBUS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HUD_DBUS_TYPE))
3288+#define IS_HUD_DBUS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HUD_DBUS_TYPE))
3289+#define HUD_DBUS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), HUD_DBUS_TYPE, HudDbusClass))
3290+
3291+typedef struct _HudDbus HudDbus;
3292+typedef struct _HudDbusClass HudDbusClass;
3293+typedef struct _HudDbusPrivate HudDbusPrivate;
3294+
3295+struct _HudDbusClass {
3296+ GObjectClass parent_class;
3297+};
3298+
3299+struct _HudDbus {
3300+ GObject parent;
3301+ HudDbusPrivate * priv;
3302+};
3303+
3304+GType hud_dbus_get_type (void);
3305+HudDbus * hud_dbus_new (void);
3306+
3307+G_END_DECLS
3308+
3309+#endif
3310
3311=== added file 'src/hud-dump-application.c'
3312--- src/hud-dump-application.c 1970-01-01 00:00:00 +0000
3313+++ src/hud-dump-application.c 2012-01-27 17:10:57 +0000
3314@@ -0,0 +1,73 @@
3315+/*
3316+Small utility to dump application info in the HUD usage DB
3317+
3318+Copyright 2011 Canonical Ltd.
3319+
3320+Authors:
3321+ Ted Gould <ted@canonical.com>
3322+
3323+This program is free software: you can redistribute it and/or modify it
3324+under the terms of the GNU General Public License version 3, as published
3325+by the Free Software Foundation.
3326+
3327+This program is distributed in the hope that it will be useful, but
3328+WITHOUT ANY WARRANTY; without even the implied warranties of
3329+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3330+PURPOSE. See the GNU General Public License for more details.
3331+
3332+You should have received a copy of the GNU General Public License along
3333+with this program. If not, see <http://www.gnu.org/licenses/>.
3334+*/
3335+
3336+#include <glib.h>
3337+#include "dump-app-info.h"
3338+
3339+int
3340+main (int argv, char * argc[])
3341+{
3342+ if (argv != 2 && argv != 3) {
3343+ g_error("Usage: %s <desktop file path> [gettext domain]\n", argc[0]);
3344+ return 1;
3345+ }
3346+
3347+ const gchar * basecachedir = g_getenv("HUD_CACHE_DIR");
3348+ if (basecachedir == NULL) {
3349+ basecachedir = g_get_user_cache_dir();
3350+ }
3351+
3352+ gchar * cachedir = g_build_filename(basecachedir, "hud", NULL);
3353+ if (!g_file_test(cachedir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
3354+ g_warning("Cache directory '%s' doesn't exist", cachedir);
3355+ return 1;
3356+ }
3357+ g_free(cachedir);
3358+
3359+ gchar * cachefile = g_build_filename(basecachedir, "indicator-appmenu", "hud-usage-log.sqlite", NULL);
3360+ gboolean db_exists = g_file_test(cachefile, G_FILE_TEST_EXISTS);
3361+
3362+ if (!db_exists) {
3363+ g_warning("There is no HUD usage log: %s", cachefile);
3364+ return 1;
3365+ }
3366+
3367+ sqlite3 * db;
3368+ int open_status = sqlite3_open(cachefile, &db);
3369+
3370+ if (open_status != SQLITE_OK) {
3371+ g_warning("Error opening usage DB: %s", cachefile);
3372+ sqlite3_close(db);
3373+ return 1;
3374+ }
3375+
3376+ gchar * domain = NULL;
3377+ if (argv == 3) {
3378+ domain = argc[2];
3379+ }
3380+
3381+ dump_app_info(argc[1], domain, db);
3382+
3383+ sqlite3_close(db);
3384+ g_free(cachefile);
3385+
3386+ return 0;
3387+}
3388
3389=== added file 'src/hud-list-applications'
3390--- src/hud-list-applications 1970-01-01 00:00:00 +0000
3391+++ src/hud-list-applications 2012-01-27 17:10:57 +0000
3392@@ -0,0 +1,9 @@
3393+#!/bin/sh
3394+
3395+if [ -n $HUD_CACHE_DIR ]; then
3396+ HUD_CACHE_DIR=~/.cache
3397+fi;
3398+
3399+FINAL_DIR="$HUD_CACHE_DIR/indicator-appmenu"
3400+
3401+sqlite3 $FINAL_DIR/hud-usage-log.sqlite "select distinct application from usage"
3402
3403=== added file 'src/hud-search.c'
3404--- src/hud-search.c 1970-01-01 00:00:00 +0000
3405+++ src/hud-search.c 2012-01-27 17:10:57 +0000
3406@@ -0,0 +1,690 @@
3407+/*
3408+The HUD searching logic that brings together the DBus menus along
3409+with the user data to make a good search.
3410+
3411+Copyright 2011 Canonical Ltd.
3412+
3413+Authors:
3414+ Ted Gould <ted@canonical.com>
3415+
3416+This program is free software: you can redistribute it and/or modify it
3417+under the terms of the GNU General Public License version 3, as published
3418+by the Free Software Foundation.
3419+
3420+This program is distributed in the hope that it will be useful, but
3421+WITHOUT ANY WARRANTY; without even the implied warranties of
3422+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3423+PURPOSE. See the GNU General Public License for more details.
3424+
3425+You should have received a copy of the GNU General Public License along
3426+with this program. If not, see <http://www.gnu.org/licenses/>.
3427+*/
3428+
3429+#ifdef HAVE_CONFIG_H
3430+#include "config.h"
3431+#endif
3432+
3433+#include <libbamf/bamf-matcher.h>
3434+#include <gio/gio.h>
3435+#include <gio/gdesktopappinfo.h>
3436+
3437+#include "hud-search.h"
3438+#include "dbusmenu-collector.h"
3439+#include "indicator-tracker.h"
3440+#include "usage-tracker.h"
3441+#include "utils.h"
3442+
3443+struct _HudSearchPrivate {
3444+ BamfMatcher * matcher;
3445+ gulong window_changed_sig;
3446+
3447+ guint32 active_xid;
3448+ BamfApplication * active_app;
3449+
3450+ DbusmenuCollector * collector;
3451+ UsageTracker * usage;
3452+
3453+ GDBusProxy * appmenu;
3454+
3455+ GSettings * search_settings;
3456+
3457+ IndicatorTracker * tracker;
3458+};
3459+
3460+#define HUD_SEARCH_GET_PRIVATE(o) \
3461+(G_TYPE_INSTANCE_GET_PRIVATE ((o), HUD_SEARCH_TYPE, HudSearchPrivate))
3462+
3463+static void hud_search_dispose (GObject *object);
3464+static void hud_search_finalize (GObject *object);
3465+
3466+static void active_window_changed (BamfMatcher * matcher, BamfView * oldview, BamfView * newview, gpointer user_data);
3467+HudSearchSuggest * hud_search_suggest_new (const gchar * app, const gchar * app_icon, const gchar * display, const gchar * db, const gchar * icon, const gchar * dbus_address, const gchar * dbus_path, gint dbus_id);
3468+
3469+G_DEFINE_TYPE (HudSearch, hud_search, G_TYPE_OBJECT);
3470+
3471+static void
3472+hud_search_class_init (HudSearchClass *klass)
3473+{
3474+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
3475+
3476+ g_type_class_add_private (klass, sizeof (HudSearchPrivate));
3477+
3478+ object_class->dispose = hud_search_dispose;
3479+ object_class->finalize = hud_search_finalize;
3480+
3481+ return;
3482+}
3483+
3484+static void
3485+hud_search_init (HudSearch *self)
3486+{
3487+ /* Get Private */
3488+ self->priv = HUD_SEARCH_GET_PRIVATE(self);
3489+
3490+ /* Initialize Private */
3491+ self->priv->matcher = NULL;
3492+ self->priv->window_changed_sig = 0;
3493+ self->priv->active_xid = 0;
3494+ self->priv->active_app = NULL;
3495+ self->priv->collector = NULL;
3496+ self->priv->usage = NULL;
3497+ self->priv->appmenu = NULL;
3498+ self->priv->search_settings = NULL;
3499+ self->priv->tracker = NULL;
3500+
3501+ /* Settings */
3502+ if (settings_schema_exists("com.canonical.indicator.appmenu.hud.search")) {
3503+ self->priv->search_settings = g_settings_new("com.canonical.indicator.appmenu.hud.search");
3504+ }
3505+
3506+ /* Indicator Tracker */
3507+ self->priv->tracker = indicator_tracker_new();
3508+
3509+ /* BAMF */
3510+ self->priv->matcher = bamf_matcher_get_default();
3511+ self->priv->window_changed_sig = g_signal_connect(G_OBJECT(self->priv->matcher), "active-window-changed", G_CALLBACK(active_window_changed), self);
3512+
3513+ BamfWindow * active_window = bamf_matcher_get_active_window(self->priv->matcher);
3514+ if (active_window != NULL) {
3515+ self->priv->active_xid = bamf_window_get_xid(active_window);
3516+ self->priv->active_app = bamf_matcher_get_application_for_window(self->priv->matcher, active_window);
3517+ }
3518+
3519+ /* DBusMenu */
3520+ self->priv->collector = dbusmenu_collector_new();
3521+
3522+ /* Usage Tracker */
3523+ self->priv->usage = usage_tracker_new();
3524+
3525+ /* Appmenu */
3526+ self->priv->appmenu = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION,
3527+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
3528+ /* info */ NULL,
3529+ "com.canonical.AppMenu.Registrar",
3530+ "/com/canonical/AppMenu/Registrar",
3531+ "com.canonical.AppMenu.Registrar",
3532+ NULL, NULL);
3533+
3534+ return;
3535+}
3536+
3537+static void
3538+hud_search_dispose (GObject *object)
3539+{
3540+ HudSearch * self = HUD_SEARCH(object);
3541+
3542+ g_clear_object(&self->priv->search_settings);
3543+ g_clear_object(&self->priv->tracker);
3544+
3545+ if (self->priv->window_changed_sig != 0) {
3546+ g_signal_handler_disconnect(self->priv->matcher, self->priv->window_changed_sig);
3547+ self->priv->window_changed_sig = 0;
3548+ }
3549+
3550+ g_clear_object(&self->priv->matcher);
3551+ g_clear_object(&self->priv->collector);
3552+ g_clear_object(&self->priv->usage);
3553+ g_clear_object(&self->priv->appmenu);
3554+
3555+ G_OBJECT_CLASS (hud_search_parent_class)->dispose (object);
3556+ return;
3557+}
3558+
3559+static void
3560+hud_search_finalize (GObject *object)
3561+{
3562+
3563+ G_OBJECT_CLASS (hud_search_parent_class)->finalize (object);
3564+ return;
3565+}
3566+
3567+HudSearch *
3568+hud_search_new (void)
3569+{
3570+ HudSearch * ret = g_object_new(HUD_SEARCH_TYPE, NULL);
3571+ return ret;
3572+}
3573+
3574+static void
3575+get_current_window_address(HudSearch * search, gchar ** address, gchar ** path)
3576+{
3577+ if (search->priv->active_xid == 0) {
3578+ g_warning("Active application is unknown");
3579+ return;
3580+ }
3581+
3582+ if (search->priv->appmenu == NULL) {
3583+ g_warning("Unable to proxy appmenu");
3584+ return;
3585+ }
3586+
3587+ GError * error = NULL;
3588+ GVariant * dbusinfo = g_dbus_proxy_call_sync(search->priv->appmenu,
3589+ "GetMenuForWindow",
3590+ g_variant_new("(u)", search->priv->active_xid),
3591+ G_DBUS_CALL_FLAGS_NONE,
3592+ -1,
3593+ NULL,
3594+ &error);
3595+
3596+ if (error != NULL) {
3597+ g_warning("Unable to get menus from appmenu: %s", error->message);
3598+ g_error_free(error);
3599+ return;
3600+ }
3601+
3602+ g_variant_get(dbusinfo, "(so)", address, path);
3603+ return;
3604+}
3605+
3606+typedef struct _usage_wrapper_t usage_wrapper_t;
3607+struct _usage_wrapper_t {
3608+ DbusmenuCollectorFound * found;
3609+ guint count;
3610+ gfloat percent_usage;
3611+ gfloat percent_distance;
3612+};
3613+
3614+/* Sort the usage data based on the percentages */
3615+static gint
3616+usage_sort (gconstpointer a, gconstpointer b)
3617+{
3618+ usage_wrapper_t * wa = (usage_wrapper_t *)a;
3619+ usage_wrapper_t * wb = (usage_wrapper_t *)b;
3620+
3621+ gfloat totala = (1.0 - wa->percent_usage) + wa->percent_distance;
3622+ gfloat totalb = (1.0 - wb->percent_usage) + wb->percent_distance;
3623+
3624+ gfloat difference = (totala - totalb) * 100.0;
3625+ return (gint)difference;
3626+}
3627+
3628+/* Sort the array based on the distance in the found object */
3629+static gint
3630+distance_sort (gconstpointer a, gconstpointer b)
3631+{
3632+ usage_wrapper_t * wa = (usage_wrapper_t *)a;
3633+ usage_wrapper_t * wb = (usage_wrapper_t *)b;
3634+
3635+ return dbusmenu_collector_found_get_distance(wa->found) - dbusmenu_collector_found_get_distance(wb->found);
3636+}
3637+
3638+/* Take the found list and put it into the usage array so that
3639+ we can start to work with it. */
3640+static void
3641+found_list_to_usage_array (HudSearch * search, GList * found_list, GArray * usagedata)
3642+{
3643+ guint max_distance = get_settings_uint(search->priv->search_settings, "max-distance", 30);
3644+
3645+ GList * found = NULL;
3646+
3647+ for (found = found_list; found != NULL; found = g_list_next(found)) {
3648+ usage_wrapper_t usage;
3649+ usage.found = (DbusmenuCollectorFound *)found->data;
3650+ usage.count = 0;
3651+
3652+ if (dbusmenu_collector_found_get_distance(usage.found) > max_distance) {
3653+ continue;
3654+ }
3655+
3656+ g_array_append_val(usagedata, usage);
3657+ }
3658+
3659+ return;
3660+}
3661+
3662+/* Take a path to a desktop file and find its icon */
3663+static gchar *
3664+desktop_to_icon (const gchar * desktop)
3665+{
3666+ g_return_val_if_fail(desktop != NULL, g_strdup(""));
3667+
3668+ if (!g_file_test(desktop, G_FILE_TEST_EXISTS)) {
3669+ g_warning("Unable to find desktop file '%s'", desktop);
3670+ return g_strdup("");
3671+ }
3672+
3673+ /* Try to build an app info from the desktop file
3674+ path */
3675+ GDesktopAppInfo * appinfo = g_desktop_app_info_new_from_filename(desktop);
3676+
3677+ if (!G_IS_DESKTOP_APP_INFO(appinfo)) {
3678+ g_warning("Unable to parse desktop file '%s'", desktop);
3679+ return g_strdup("");
3680+ }
3681+
3682+ /* Get the name out of the icon, note the icon is not
3683+ ref'd so it doesn't need to be free'd */
3684+ gchar * retval = NULL;
3685+ GIcon * icon = g_app_info_get_icon(G_APP_INFO(appinfo));
3686+ if (icon != NULL) {
3687+ retval = g_icon_to_string(icon);
3688+ } else {
3689+ retval = g_strdup("");
3690+ }
3691+
3692+ /* Drop the app info */
3693+ g_object_unref(appinfo);
3694+ appinfo = NULL;
3695+
3696+ return retval;
3697+}
3698+
3699+/* Looks through the menus of the currently focused application
3700+ for the search string */
3701+static void
3702+search_current_app (HudSearch * search, const gchar * searchstr, GArray * usagedata, GList ** foundlist)
3703+{
3704+ if (search->priv->active_app == NULL) {
3705+ return;
3706+ }
3707+
3708+ gchar * address = NULL;
3709+ gchar * path = NULL;
3710+ GList * found_list = NULL;
3711+
3712+ get_current_window_address(search, &address, &path);
3713+
3714+ if (address != NULL && path != NULL) {
3715+ found_list = dbusmenu_collector_search(search->priv->collector, address, path, NULL, searchstr);
3716+ }
3717+
3718+ g_free(address);
3719+ g_free(path);
3720+
3721+ /* Set the name for the application */
3722+ const gchar * desktop_file = NULL;
3723+ if (search->priv->active_app != NULL) {
3724+ desktop_file = bamf_application_get_desktop_file(search->priv->active_app);
3725+ }
3726+
3727+ if (desktop_file != NULL) {
3728+ GList * founditem = found_list;
3729+ gchar * icon = desktop_to_icon(desktop_file);
3730+
3731+ while (founditem != NULL) {
3732+ DbusmenuCollectorFound * found = (DbusmenuCollectorFound *)founditem->data;
3733+
3734+ /* Indicator */
3735+ dbusmenu_collector_found_set_indicator(found, desktop_file);
3736+
3737+ /* Icon */
3738+ dbusmenu_collector_found_set_app_icon(found, icon);
3739+
3740+ founditem = g_list_next(founditem);
3741+ }
3742+
3743+ g_free(icon);
3744+ }
3745+
3746+ /* Copy the found list into the array */
3747+ found_list_to_usage_array(search, found_list, usagedata);
3748+ *foundlist = g_list_concat(*foundlist, found_list);
3749+
3750+ return;
3751+}
3752+
3753+/* Gets each indicator from the tracker and then looks at it's menus
3754+ with the search string. It also penalizes the entries by a configurable
3755+ value compared to what they'd be normally. */
3756+static void
3757+search_indicators (HudSearch * search, const gchar * searchstr, GArray * usagedata, GList ** foundlist)
3758+{
3759+ if (search->priv->tracker == NULL) {
3760+ return;
3761+ }
3762+
3763+ guint indicator_penalty = get_settings_uint(search->priv->search_settings, "indicator-penalty", 50);
3764+ GList * indicators = indicator_tracker_get_indicators(search->priv->tracker);
3765+ GList * lindicator = NULL;
3766+
3767+ for (lindicator = indicators; lindicator != NULL; lindicator = g_list_next(lindicator)) {
3768+ IndicatorTrackerIndicator * indicator = (IndicatorTrackerIndicator *)lindicator->data;
3769+
3770+ /* Search the menu items of the indicator */
3771+ GList * found_list = dbusmenu_collector_search(search->priv->collector, indicator->dbus_name, indicator->dbus_object, indicator->prefix, searchstr);
3772+
3773+ /* Increase distance */
3774+ GList * founditem = found_list;
3775+ while (founditem != NULL) {
3776+ DbusmenuCollectorFound * found = (DbusmenuCollectorFound *)founditem->data;
3777+
3778+ /* Distance */
3779+ guint distance = dbusmenu_collector_found_get_distance(found);
3780+ distance = distance + ((distance * indicator_penalty) / 100);
3781+ dbusmenu_collector_found_set_distance(found, distance);
3782+
3783+ /* Names */
3784+ dbusmenu_collector_found_set_indicator(found, indicator->name);
3785+
3786+ /* Icon */
3787+ dbusmenu_collector_found_set_app_icon(found, indicator->icon);
3788+
3789+ founditem = g_list_next(founditem);
3790+ }
3791+
3792+ found_list_to_usage_array(search, found_list, usagedata);
3793+
3794+ *foundlist = g_list_concat(*foundlist, found_list);
3795+ }
3796+
3797+ g_list_free(indicators);
3798+
3799+ return;
3800+}
3801+
3802+/* Grabs all the menus items, sorts them, then looks up usage data on
3803+ the top entries and sorts them again. Lastly, we have our top
3804+ entries. */
3805+static void
3806+search_and_sort (HudSearch * search, const gchar * searchstr, GArray * usagedata, GList ** foundlist)
3807+{
3808+ /* Get all the entries from the current app and the indicators
3809+ and put them into the array. We keep the found list as it
3810+ tracks the memory as well. */
3811+ search_current_app(search, searchstr, usagedata, foundlist);
3812+ search_indicators(search, searchstr, usagedata, foundlist);
3813+
3814+ /* Sort the list */
3815+ g_array_sort(usagedata, distance_sort);
3816+
3817+ /* Drop all but the first few */
3818+ if (usagedata->len > 15) {
3819+ usagedata = g_array_remove_range(usagedata, 15, usagedata->len - 15);
3820+ }
3821+
3822+ /* Get usage data */
3823+ int count;
3824+ for (count = 0; count < usagedata->len; count++) {
3825+ usage_wrapper_t * usage = &g_array_index(usagedata, usage_wrapper_t, count);
3826+
3827+ const gchar * desktopfile = NULL;
3828+
3829+ desktopfile = dbusmenu_collector_found_get_indicator(usage->found);
3830+
3831+ if (desktopfile != NULL) {
3832+ usage->count = usage_tracker_get_usage(search->priv->usage, desktopfile, dbusmenu_collector_found_get_db(usage->found));
3833+ } else {
3834+ usage->count = 0;
3835+ }
3836+ }
3837+
3838+ /* Calculate overall */
3839+ guint overall_usage = 0;
3840+ guint overall_distance = 0;
3841+ for (count = 0; count < usagedata->len; count++) {
3842+ usage_wrapper_t * usage = &g_array_index(usagedata, usage_wrapper_t, count);
3843+ overall_usage += usage->count;
3844+ overall_distance += dbusmenu_collector_found_get_distance(usage->found);
3845+ }
3846+
3847+
3848+ /* Build percentages */
3849+ for (count = 0; count < usagedata->len; count++) {
3850+ usage_wrapper_t * usage = &g_array_index(usagedata, usage_wrapper_t, count);
3851+
3852+ if (overall_usage != 0) {
3853+ usage->percent_usage = (gfloat)usage->count/(gfloat)overall_usage;
3854+ } else {
3855+ usage->percent_usage = 1.0;
3856+ }
3857+
3858+ usage->percent_distance = (gfloat)dbusmenu_collector_found_get_distance(usage->found)/(gfloat)overall_distance;
3859+ }
3860+
3861+ /* Sort based on aggregate */
3862+ g_array_sort(usagedata, usage_sort);
3863+
3864+ return;
3865+}
3866+
3867+/* Find a list of suggestions that should be sent to the user. Looks
3868+ through both applications and indicators. */
3869+GList *
3870+hud_search_suggestions (HudSearch * search, const gchar * searchstr, gchar ** desktop, gchar ** target)
3871+{
3872+ g_return_val_if_fail(IS_HUD_SEARCH(search), NULL);
3873+
3874+ /* We're currently not getting this data, let's just give a
3875+ NULL string back for now */
3876+ if (target != NULL) {
3877+ *target = g_strdup("");
3878+ }
3879+
3880+ /* Build an array of the entries that we're going
3881+ to use as an interim structure */
3882+ GArray * usagedata = g_array_sized_new(FALSE, TRUE, sizeof(usage_wrapper_t), 15);
3883+ GList * found_list = NULL;
3884+
3885+
3886+ /* The application desktop file is the default if the
3887+ entry in question is not for an indicator */
3888+ const gchar * appdesktopfile = NULL;
3889+
3890+ if (search->priv->active_app != NULL) {
3891+ appdesktopfile = bamf_application_get_desktop_file(search->priv->active_app);
3892+ }
3893+
3894+ if (appdesktopfile == NULL) {
3895+ appdesktopfile = "";
3896+ }
3897+
3898+ /* If we're given a valid pointer, return the value back to
3899+ it */
3900+ if (desktop != NULL) {
3901+ *desktop = g_strdup(appdesktopfile);
3902+ }
3903+
3904+ /* This is the core of searching a given application for their
3905+ entries. It will also delve into the indicators */
3906+ search_and_sort(search, searchstr, usagedata, &found_list);
3907+
3908+ /* Convert the entries that we were given into a list of structures
3909+ that we want to export an an API */
3910+ GList * retval = NULL;
3911+ int count;
3912+ for (count = 0; count < 5 && count < usagedata->len; count++) {
3913+ usage_wrapper_t * usage = &g_array_index(usagedata, usage_wrapper_t, count);
3914+
3915+ const gchar * desktopfile = dbusmenu_collector_found_get_indicator(usage->found);
3916+ if (desktopfile == NULL) {
3917+ desktopfile = appdesktopfile;
3918+ }
3919+
3920+ HudSearchSuggest * suggest = hud_search_suggest_new(desktopfile,
3921+ dbusmenu_collector_found_get_app_icon(usage->found),
3922+ dbusmenu_collector_found_get_display(usage->found),
3923+ dbusmenu_collector_found_get_db(usage->found),
3924+ "none",
3925+ dbusmenu_collector_found_get_dbus_addr(usage->found),
3926+ dbusmenu_collector_found_get_dbus_path(usage->found),
3927+ dbusmenu_collector_found_get_dbus_id(usage->found)
3928+ );
3929+
3930+ retval = g_list_prepend(retval, suggest);
3931+ }
3932+
3933+ retval = g_list_reverse(retval);
3934+
3935+ /* Free the interim arrays of data */
3936+ g_array_free(usagedata, TRUE);
3937+ dbusmenu_collector_found_list_free(found_list);
3938+
3939+ return retval;
3940+}
3941+
3942+void
3943+hud_search_execute (HudSearch * search, GVariant * key, guint timestamp)
3944+{
3945+ g_return_if_fail(IS_HUD_SEARCH(search));
3946+ gchar * app = NULL;
3947+ gchar * dbstring = NULL;
3948+ gchar * address = NULL;
3949+ gchar * path = NULL;
3950+ gint id = 0;
3951+ GVariant * unwrapped_key = NULL;
3952+
3953+ if (g_variant_is_of_type(key, G_VARIANT_TYPE_VARIANT)) {
3954+ unwrapped_key = g_variant_get_variant(key);
3955+ } else {
3956+ g_warning("Invalid key type; unable to execute");
3957+ }
3958+
3959+ if (unwrapped_key != NULL) {
3960+ g_variant_get(unwrapped_key, "(sssoi)", &app, &dbstring, &address, &path, &id);
3961+ }
3962+
3963+ if (app != NULL && dbstring != NULL && address != NULL && path != NULL && id != 0) {
3964+ dbusmenu_collector_execute(search->priv->collector, address, path, id, timestamp);
3965+ usage_tracker_mark_usage(search->priv->usage, app, dbstring);
3966+ }
3967+
3968+ g_free(address);
3969+ g_free(path);
3970+ g_free(app);
3971+ g_free(dbstring);
3972+
3973+ if (unwrapped_key != NULL) {
3974+ g_variant_unref(unwrapped_key);
3975+ }
3976+
3977+ return;
3978+}
3979+
3980+static void
3981+active_window_changed (BamfMatcher * matcher, BamfView * oldview, BamfView * newview, gpointer user_data)
3982+{
3983+ HudSearch * self = HUD_SEARCH(user_data);
3984+
3985+ if (!BAMF_IS_WINDOW(newview)) { return; }
3986+ BamfWindow * window = BAMF_WINDOW(newview);
3987+
3988+ BamfApplication * app = bamf_matcher_get_application_for_window(self->priv->matcher, window);
3989+ const gchar * desktop = NULL;
3990+
3991+ if (app != NULL) {
3992+ desktop = bamf_application_get_desktop_file(app);
3993+ }
3994+
3995+ /* If BAMF can't match it to an application we probably don't
3996+ want to be involved with it anyway. */
3997+ if (desktop == NULL) {
3998+ return;
3999+ }
4000+
4001+ /* NOTE: Both of these are debugging tools for now, so we want
4002+ to be able to use them effectively to work on the HUD, so we're
4003+ going to pretend we didn't switch windows if we switch to one
4004+ of them */
4005+ if (g_strstr_len(desktop, -1, "termina") != NULL ||
4006+ g_strstr_len(desktop, -1, "dfeet") != NULL) {
4007+ return;
4008+ }
4009+
4010+ /* Ignore the hud prototype window directly */
4011+ const gchar * window_name = bamf_view_get_name(newview);
4012+ if (g_strcmp0(window_name, "Hud Prototype Test") == 0
4013+ || g_strcmp0(window_name, "Hud") == 0) {
4014+ return;
4015+ }
4016+
4017+ /* This should ignore most of the windows involved in Unity */
4018+ const gchar * name = bamf_view_get_name(newview);
4019+ if (g_strcmp0(name, "DNDCollectionWindow") == 0
4020+ || g_strcmp0(name, "launcher") == 0
4021+ || g_strcmp0(name, "dash") == 0
4022+ || g_strcmp0(name, "panel") == 0
4023+ || g_strcmp0(name, "hud") == 0) {
4024+ return;
4025+ }
4026+
4027+ self->priv->active_xid = bamf_window_get_xid(window);
4028+ self->priv->active_app = app;
4029+
4030+ return;
4031+}
4032+
4033+struct _HudSearchSuggest {
4034+ gchar * display;
4035+ gchar * app_icon;
4036+ gchar * item_icon;
4037+ GVariant * key;
4038+};
4039+
4040+HudSearchSuggest *
4041+hud_search_suggest_new (const gchar * app, const gchar * app_icon, const gchar * display, const gchar * db, const gchar * item_icon, const gchar * dbus_address, const gchar * dbus_path, gint dbus_id)
4042+{
4043+ HudSearchSuggest * suggest = g_new0(HudSearchSuggest, 1);
4044+
4045+ suggest->display = g_strdup(display);
4046+ suggest->app_icon = g_strdup(app_icon);
4047+ suggest->item_icon = g_strdup(item_icon);
4048+
4049+ GVariantBuilder builder;
4050+ g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
4051+ g_variant_builder_add_value(&builder, g_variant_new_string(app));
4052+ g_variant_builder_add_value(&builder, g_variant_new_string(db));
4053+ g_variant_builder_add_value(&builder, g_variant_new_string(dbus_address));
4054+ g_variant_builder_add_value(&builder, g_variant_new_object_path(dbus_path));
4055+ g_variant_builder_add_value(&builder, g_variant_new_int32(dbus_id));
4056+
4057+ suggest->key = g_variant_new_variant(g_variant_builder_end(&builder));
4058+ g_variant_ref_sink(suggest->key);
4059+
4060+ return suggest;
4061+}
4062+
4063+const gchar *
4064+hud_search_suggest_get_app_icon (HudSearchSuggest * suggest)
4065+{
4066+ return suggest->app_icon;
4067+}
4068+
4069+const gchar *
4070+hud_search_suggest_get_item_icon (HudSearchSuggest * suggest)
4071+{
4072+ return suggest->item_icon;
4073+}
4074+
4075+const gchar *
4076+hud_search_suggest_get_display (HudSearchSuggest * suggest)
4077+{
4078+ return suggest->display;
4079+}
4080+
4081+GVariant *
4082+hud_search_suggest_get_key (HudSearchSuggest * suggest)
4083+{
4084+ return suggest->key;
4085+}
4086+
4087+void
4088+hud_search_suggest_free (HudSearchSuggest * suggest)
4089+{
4090+ g_free(suggest->display);
4091+ g_free(suggest->app_icon);
4092+ g_free(suggest->item_icon);
4093+ g_variant_unref(suggest->key);
4094+ g_free(suggest);
4095+ return;
4096+}
4097
4098=== added file 'src/hud-search.h'
4099--- src/hud-search.h 1970-01-01 00:00:00 +0000
4100+++ src/hud-search.h 2012-01-27 17:10:57 +0000
4101@@ -0,0 +1,64 @@
4102+/*
4103+The HUD searching logic that brings together the DBus menus along
4104+with the user data to make a good search.
4105+
4106+Copyright 2011 Canonical Ltd.
4107+
4108+Authors:
4109+ Ted Gould <ted@canonical.com>
4110+
4111+This program is free software: you can redistribute it and/or modify it
4112+under the terms of the GNU General Public License version 3, as published
4113+by the Free Software Foundation.
4114+
4115+This program is distributed in the hope that it will be useful, but
4116+WITHOUT ANY WARRANTY; without even the implied warranties of
4117+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4118+PURPOSE. See the GNU General Public License for more details.
4119+
4120+You should have received a copy of the GNU General Public License along
4121+with this program. If not, see <http://www.gnu.org/licenses/>.
4122+*/
4123+
4124+#ifndef __HUD_SEARCH_H__
4125+#define __HUD_SEARCH_H__
4126+
4127+#include <glib.h>
4128+
4129+G_BEGIN_DECLS
4130+
4131+#define HUD_SEARCH_TYPE (hud_search_get_type ())
4132+#define HUD_SEARCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), HUD_SEARCH_TYPE, HudSearch))
4133+#define HUD_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), HUD_SEARCH_TYPE, HudSearchClass))
4134+#define IS_HUD_SEARCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), HUD_SEARCH_TYPE))
4135+#define IS_HUD_SEARCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), HUD_SEARCH_TYPE))
4136+#define HUD_SEARCH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), HUD_SEARCH_TYPE, HudSearchClass))
4137+
4138+typedef struct _HudSearch HudSearch;
4139+typedef struct _HudSearchClass HudSearchClass;
4140+typedef struct _HudSearchPrivate HudSearchPrivate;
4141+typedef struct _HudSearchSuggest HudSearchSuggest;
4142+
4143+struct _HudSearchClass {
4144+ GObjectClass parent_class;
4145+};
4146+
4147+struct _HudSearch {
4148+ GObject parent;
4149+ HudSearchPrivate * priv;
4150+};
4151+
4152+GType hud_search_get_type (void);
4153+HudSearch * hud_search_new (void);
4154+GList * hud_search_suggestions (HudSearch * search, const gchar * searchstr, gchar ** desktop, gchar ** target);
4155+void hud_search_execute (HudSearch * search, GVariant * key, guint timestamp);
4156+
4157+const gchar * hud_search_suggest_get_app_icon (HudSearchSuggest * suggest);
4158+const gchar * hud_search_suggest_get_item_icon (HudSearchSuggest * suggest);
4159+const gchar * hud_search_suggest_get_display (HudSearchSuggest * suggest);
4160+GVariant * hud_search_suggest_get_key (HudSearchSuggest * suggest);
4161+void hud_search_suggest_free (HudSearchSuggest * suggest);
4162+
4163+G_END_DECLS
4164+
4165+#endif
4166
4167=== added file 'src/hud-service.c'
4168--- src/hud-service.c 1970-01-01 00:00:00 +0000
4169+++ src/hud-service.c 2012-01-27 17:10:57 +0000
4170@@ -0,0 +1,62 @@
4171+/*
4172+Basic HUD service.
4173+
4174+Copyright 2011 Canonical Ltd.
4175+
4176+Authors:
4177+ Ted Gould <ted@canonical.com>
4178+
4179+This program is free software: you can redistribute it and/or modify it
4180+under the terms of the GNU General Public License version 3, as published
4181+by the Free Software Foundation.
4182+
4183+This program is distributed in the hope that it will be useful, but
4184+WITHOUT ANY WARRANTY; without even the implied warranties of
4185+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4186+PURPOSE. See the GNU General Public License for more details.
4187+
4188+You should have received a copy of the GNU General Public License along
4189+with this program. If not, see <http://www.gnu.org/licenses/>.
4190+*/
4191+
4192+#include <glib.h>
4193+#include <gio/gio.h>
4194+
4195+#include "shared-values.h"
4196+#include "hud-dbus.h"
4197+
4198+static GMainLoop * mainloop = NULL;
4199+
4200+static void
4201+name_lost_cb (GDBusConnection * connection, const gchar * name, gpointer user_data)
4202+{
4203+ g_error("Unable to get name '%s'", name);
4204+ g_main_loop_quit(mainloop);
4205+ return;
4206+}
4207+
4208+int
4209+main (int argv, char * argc[])
4210+{
4211+ g_type_init();
4212+
4213+ HudDbus * dbus = hud_dbus_new();
4214+
4215+ g_bus_own_name(G_BUS_TYPE_SESSION,
4216+ /* Name */ DBUS_NAME,
4217+ /* Flags */ G_BUS_NAME_OWNER_FLAGS_NONE,
4218+ /* Bus Acquired */ NULL,
4219+ /* Name Acquired */ NULL,
4220+ /* Name Lost */ name_lost_cb,
4221+ /* User Data */ NULL,
4222+ /* Destroy */ NULL);
4223+
4224+
4225+ mainloop = g_main_loop_new(NULL, FALSE);
4226+ g_main_loop_run(mainloop);
4227+
4228+ g_main_loop_unref(mainloop);
4229+ g_object_unref(dbus);
4230+
4231+ return 0;
4232+}
4233
4234=== added file 'src/hud-verify-app-info.c'
4235--- src/hud-verify-app-info.c 1970-01-01 00:00:00 +0000
4236+++ src/hud-verify-app-info.c 2012-01-27 17:10:57 +0000
4237@@ -0,0 +1,104 @@
4238+/*
4239+Verify the validity of the App Info file to make sure it parses
4240+
4241+Copyright 2011 Canonical Ltd.
4242+
4243+Authors:
4244+ Ted Gould <ted@canonical.com>
4245+
4246+This program is free software: you can redistribute it and/or modify it
4247+under the terms of the GNU General Public License version 3, as published
4248+by the Free Software Foundation.
4249+
4250+This program is distributed in the hope that it will be useful, but
4251+WITHOUT ANY WARRANTY; without even the implied warranties of
4252+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4253+PURPOSE. See the GNU General Public License for more details.
4254+
4255+You should have received a copy of the GNU General Public License along
4256+with this program. If not, see <http://www.gnu.org/licenses/>.
4257+*/
4258+
4259+#include <unistd.h>
4260+#include <glib-object.h>
4261+#include <glib/gstdio.h>
4262+#include "load-app-info.h"
4263+
4264+static void
4265+build_db (sqlite3 * db)
4266+{
4267+ /* Create the table */
4268+ int exec_status = SQLITE_OK;
4269+ gchar * failstring = NULL;
4270+ exec_status = sqlite3_exec(db,
4271+ "create table usage (application text, entry text, timestamp datetime);",
4272+ NULL, NULL, &failstring);
4273+ if (exec_status != SQLITE_OK) {
4274+ g_warning("Unable to create table: %s", failstring);
4275+ }
4276+
4277+ /* Import data from the system */
4278+
4279+ return;
4280+}
4281+
4282+int
4283+main (int argv, char * argc[])
4284+{
4285+ gboolean passed = TRUE;
4286+
4287+ if (argv != 2) {
4288+ g_printerr("Usage: %s <app-info file path>\n", argc[0]);
4289+ return 1;
4290+ }
4291+
4292+ g_type_init();
4293+
4294+ gchar * filename = NULL;
4295+ gint tmpfile = g_file_open_tmp("hud-verify-app-info-temp-db-XXXXXX", &filename, NULL);
4296+
4297+ if (tmpfile < 0) {
4298+ passed = FALSE;
4299+ goto cleanup;
4300+ }
4301+
4302+ close(tmpfile);
4303+ /* NOTE: there is a small security bug here in that we're closing the
4304+ file and reopening it, so the temp isn't really gauranteed to be
4305+ safe. But, I don't think we're really worried about security in this
4306+ utility. */
4307+
4308+ sqlite3 * db = NULL;
4309+ int open_status = sqlite3_open(filename, &db);
4310+
4311+ if (open_status != SQLITE_OK) {
4312+ g_warning("Error opening usage DB: %s", filename);
4313+ passed = FALSE;
4314+ goto cleanup;
4315+ }
4316+
4317+ /* Create the table in the DB */
4318+ build_db(db);
4319+
4320+ passed = load_app_info(argc[1], db);
4321+
4322+cleanup:
4323+ if (db != NULL) {
4324+ sqlite3_close(db);
4325+ }
4326+
4327+ if (filename != NULL) {
4328+ if (g_getenv("HUD_VERIFY_NO_UNLINK") == NULL) {
4329+ g_unlink(filename);
4330+ } else {
4331+ g_print("Temp db '%s' not deleted\n", filename);
4332+ }
4333+ g_free(filename);
4334+ }
4335+
4336+ if (passed) {
4337+ return 0;
4338+ } else {
4339+ return 1;
4340+ }
4341+}
4342
4343=== added file 'src/hud.xml'
4344--- src/hud.xml 1970-01-01 00:00:00 +0000
4345+++ src/hud.xml 2012-01-27 17:10:57 +0000
4346@@ -0,0 +1,37 @@
4347+<?xml version="1.0" encoding="UTF-8"?>
4348+<node name="/">
4349+ <interface name="com.canonical.hud">
4350+<!-- Properties -->
4351+ <!-- None -->
4352+
4353+<!-- Functions -->
4354+ <method name="StartQuery">
4355+ <!-- in -->
4356+ <arg type="s" name="query" direction="in" />
4357+ <arg type="i" name="entries" direction="in" />
4358+ <!-- out -->
4359+ <arg type="s" name="target" direction="out" />
4360+ <arg type="a(sssssv)" name="suggestions" direction="out" />
4361+ <arg type="v" name="querykey" direction="out" />
4362+ </method>
4363+
4364+ <method name="ExecuteQuery">
4365+ <arg type="v" name="key" direction="in" />
4366+ <arg type="u" name="timestamp" direction="in" />
4367+ </method>
4368+
4369+ <method name="CloseQuery">
4370+ <arg type="v" name="querykey" direction="in" />
4371+ </method>
4372+
4373+<!-- Signals -->
4374+ <signal name="UpdatedQuery">
4375+ <arg type="s" name="target" direction="out" />
4376+ <arg type="a(sssssv)" name="suggestions" direction="out" />
4377+ <arg type="v" name="querykey" direction="out" />
4378+ </signal>
4379+
4380+<!-- End of interesting stuff -->
4381+
4382+ </interface>
4383+</node>
4384
4385=== modified file 'src/indicator-appmenu.c'
4386--- src/indicator-appmenu.c 2011-06-07 19:38:59 +0000
4387+++ src/indicator-appmenu.c 2012-01-27 17:10:57 +0000
4388@@ -139,12 +139,8 @@
4389 /**********************
4390 Prototypes
4391 **********************/
4392-static void indicator_appmenu_class_init (IndicatorAppmenuClass *klass);
4393-static void indicator_appmenu_init (IndicatorAppmenu *self);
4394 static void indicator_appmenu_dispose (GObject *object);
4395 static void indicator_appmenu_finalize (GObject *object);
4396-static void indicator_appmenu_debug_class_init (IndicatorAppmenuDebugClass *klass);
4397-static void indicator_appmenu_debug_init (IndicatorAppmenuDebug *self);
4398 static void indicator_appmenu_debug_dispose (GObject *object);
4399 static void build_window_menus (IndicatorAppmenu * iapp);
4400 static GList * get_entries (IndicatorObject * io);
4401
4402=== added file 'src/indicator-tracker.c'
4403--- src/indicator-tracker.c 1970-01-01 00:00:00 +0000
4404+++ src/indicator-tracker.c 2012-01-27 17:10:57 +0000
4405@@ -0,0 +1,618 @@
4406+/*
4407+Tracks the various indicators to know when they come on and off
4408+the bus for searching their menus.
4409+
4410+Copyright 2011-2012 Canonical Ltd.
4411+
4412+Authors:
4413+ Ted Gould <ted@canonical.com>
4414+
4415+This program is free software: you can redistribute it and/or modify it
4416+under the terms of the GNU General Public License version 3, as published
4417+by the Free Software Foundation.
4418+
4419+This program is distributed in the hope that it will be useful, but
4420+WITHOUT ANY WARRANTY; without even the implied warranties of
4421+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4422+PURPOSE. See the GNU General Public License for more details.
4423+
4424+You should have received a copy of the GNU General Public License along
4425+with this program. If not, see <http://www.gnu.org/licenses/>.
4426+*/
4427+
4428+#ifdef HAVE_CONFIG_H
4429+#include "config.h"
4430+#endif
4431+
4432+#include "indicator-tracker.h"
4433+
4434+#include <glib/gi18n.h>
4435+#include <gio/gio.h>
4436+
4437+typedef struct _SystemIndicator SystemIndicator;
4438+struct _SystemIndicator {
4439+ gchar * dbus_name;
4440+ gchar * dbus_menu_path;
4441+ gchar * indicator_name;
4442+ gchar * user_visible_name;
4443+ gchar * icon;
4444+};
4445+
4446+SystemIndicator system_indicators[] = {
4447+ {dbus_name: "com.canonical.indicator.datetime", dbus_menu_path: "/com/canonical/indicator/datetime/menu", indicator_name: "indicator-datetime", user_visible_name: N_("Date"), icon: "office-calendar"},
4448+ {dbus_name: "com.canonical.indicator.session", dbus_menu_path: "/com/canonical/indicator/session/menu", indicator_name: "indicator-session-device", user_visible_name: N_("Device"), icon: "system-devices-panel"},
4449+ {dbus_name: "com.canonical.indicator.session", dbus_menu_path: "/com/canonical/indicator/users/menu", indicator_name: "indicator-session-user", user_visible_name: N_("Users"), icon: "avatar-default"},
4450+ /* TODO: Delete this after everyone has the new version */
4451+ {dbus_name: "com.canonical.indicators.sound", dbus_menu_path: "/com/canonical/indicators/sound/menu", indicator_name: "indicator-sound", user_visible_name: N_("Sound"), icon: "audio-volume-high-panel"},
4452+ {dbus_name: "com.canonical.indicator.sound", dbus_menu_path: "/com/canonical/indicator/sound/menu", indicator_name: "indicator-sound", user_visible_name: N_("Sound"), icon: "audio-volume-high-panel"},
4453+ {dbus_name: "com.canonical.indicator.messages", dbus_menu_path: "/com/canonical/indicator/messages/menu", indicator_name: "indicator-messages", user_visible_name: N_("Messages"), icon: "indicator-messages"}
4454+};
4455+
4456+typedef struct _AppIndicator AppIndicator;
4457+struct _AppIndicator {
4458+ IndicatorTrackerIndicator system;
4459+
4460+ gboolean alert;
4461+
4462+ gchar * alert_name;
4463+ gchar * normal_name;
4464+};
4465+
4466+
4467+struct _IndicatorTrackerPrivate {
4468+ GArray * indicators;
4469+ guint watches[G_N_ELEMENTS(system_indicators)];
4470+
4471+ GDBusProxy * app_proxy;
4472+ GCancellable * app_proxy_cancel;
4473+ GArray * app_indicators;
4474+};
4475+
4476+#define INDICATOR_TRACKER_GET_PRIVATE(o) \
4477+(G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_TRACKER_TYPE, IndicatorTrackerPrivate))
4478+
4479+static void indicator_tracker_dispose (GObject *object);
4480+static void indicator_tracker_finalize (GObject *object);
4481+static void system_watch_appeared (GDBusConnection * connection, const gchar * name, const gchar * name_owner, gpointer user_data);
4482+static void system_watch_vanished (GDBusConnection * connection, const gchar * name, gpointer user_data);
4483+static void system_indicator_cleanup (gpointer pindicator);
4484+static void app_indicator_cleanup (gpointer pindicator);
4485+static void app_proxy_built (GObject * object, GAsyncResult * result, gpointer user_data);
4486+static void app_proxy_name_change (GObject * gobject, GParamSpec * pspec, gpointer user_data);
4487+static void app_proxy_signal (GDBusProxy *proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data);
4488+static void app_proxy_apps_replace (GObject * obj, GAsyncResult * res, gpointer user_data);
4489+static void app_proxy_new_indicator (IndicatorTracker * self, gint position, const gchar * id, const gchar * accessibledesc, const gchar * dbusaddress, const gchar * dbusobject, const gchar * iconname);
4490+static gboolean app_proxy_remove_indicator (IndicatorTracker * self, gint position);
4491+static void app_proxy_icon_changed (IndicatorTracker * self, gint position, const gchar * iconname, const gchar * accessibledesc);
4492+
4493+G_DEFINE_TYPE (IndicatorTracker, indicator_tracker, G_TYPE_OBJECT);
4494+
4495+static void
4496+indicator_tracker_class_init (IndicatorTrackerClass *klass)
4497+{
4498+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
4499+
4500+ g_type_class_add_private (klass, sizeof (IndicatorTrackerPrivate));
4501+
4502+ object_class->dispose = indicator_tracker_dispose;
4503+ object_class->finalize = indicator_tracker_finalize;
4504+
4505+ return;
4506+}
4507+
4508+static void
4509+indicator_tracker_init (IndicatorTracker *self)
4510+{
4511+ self->priv = INDICATOR_TRACKER_GET_PRIVATE(self);
4512+ self->priv->indicators = NULL;
4513+ self->priv->app_proxy = NULL;
4514+ self->priv->app_proxy_cancel = NULL;
4515+ self->priv->app_indicators = NULL;
4516+
4517+ /* NOTE: It would seem like we could combine these, eh?
4518+ Well, not really. And the reason is because the app
4519+ indicator service sends everything based on array index
4520+ so it's a lot easier to have an array to track that */
4521+ self->priv->indicators = g_array_new(FALSE, TRUE, sizeof(IndicatorTrackerIndicator));
4522+ self->priv->app_indicators = g_array_new(FALSE, FALSE, sizeof(AppIndicator));
4523+
4524+ int indicator_cnt;
4525+ for (indicator_cnt = 0; indicator_cnt < G_N_ELEMENTS(system_indicators); indicator_cnt++) {
4526+ self->priv->watches[indicator_cnt] = g_bus_watch_name(G_BUS_TYPE_SESSION,
4527+ system_indicators[indicator_cnt].dbus_name,
4528+ G_BUS_NAME_WATCHER_FLAGS_NONE,
4529+ system_watch_appeared, /* acquired */
4530+ system_watch_vanished, /* vanished */
4531+ self,
4532+ NULL); /* free func */
4533+ }
4534+
4535+ self->priv->app_proxy_cancel = g_cancellable_new();
4536+
4537+ g_dbus_proxy_new_for_bus(G_BUS_TYPE_SESSION,
4538+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
4539+ NULL, /* interface info */
4540+ "com.canonical.indicator.application",
4541+ "/com/canonical/indicator/application/service",
4542+ "com.canonical.indicator.application.service",
4543+ self->priv->app_proxy_cancel,
4544+ app_proxy_built,
4545+ self);
4546+
4547+ return;
4548+}
4549+
4550+static void
4551+indicator_tracker_dispose (GObject *object)
4552+{
4553+ IndicatorTracker * self = INDICATOR_TRACKER(object);
4554+
4555+ int indicator_cnt;
4556+ for (indicator_cnt = 0; indicator_cnt < G_N_ELEMENTS(system_indicators); indicator_cnt++) {
4557+ if (self->priv->watches[indicator_cnt] != 0) {
4558+ g_bus_unwatch_name(self->priv->watches[indicator_cnt]);
4559+ self->priv->watches[indicator_cnt] = 0;
4560+ }
4561+ }
4562+
4563+ if (self->priv->app_proxy_cancel != NULL) {
4564+ g_cancellable_cancel(self->priv->app_proxy_cancel);
4565+ g_object_unref(self->priv->app_proxy_cancel);
4566+ self->priv->app_proxy_cancel = NULL;
4567+ }
4568+
4569+ g_clear_object(&self->priv->app_proxy);
4570+
4571+ G_OBJECT_CLASS (indicator_tracker_parent_class)->dispose (object);
4572+ return;
4573+}
4574+
4575+static void
4576+indicator_tracker_finalize (GObject *object)
4577+{
4578+ g_return_if_fail(IS_INDICATOR_TRACKER(object));
4579+ IndicatorTracker * self = INDICATOR_TRACKER(object);
4580+
4581+
4582+ if (self->priv->indicators != NULL) {
4583+ /* Clear all the entries in the system indicator array */
4584+ while (self->priv->indicators->len != 0) {
4585+ IndicatorTrackerIndicator * indicator = &g_array_index(self->priv->indicators, IndicatorTrackerIndicator, 0);
4586+
4587+ system_indicator_cleanup(indicator);
4588+
4589+ g_array_remove_index_fast(self->priv->indicators, 0);
4590+ }
4591+
4592+ g_array_free(self->priv->indicators, TRUE /* delete segment */);
4593+ self->priv->indicators = NULL;
4594+ }
4595+
4596+ if (self->priv->app_indicators != NULL) {
4597+ /* Clear all the entries in the system indicator array */
4598+ while (self->priv->app_indicators->len != 0) {
4599+ AppIndicator * indicator = &g_array_index(self->priv->app_indicators, AppIndicator, 0);
4600+
4601+ app_indicator_cleanup(indicator);
4602+
4603+ g_array_remove_index_fast(self->priv->app_indicators, 0);
4604+ }
4605+
4606+ g_array_free(self->priv->app_indicators, TRUE /* delete segment */);
4607+ self->priv->app_indicators = NULL;
4608+ }
4609+
4610+ G_OBJECT_CLASS (indicator_tracker_parent_class)->finalize (object);
4611+ return;
4612+}
4613+
4614+IndicatorTracker *
4615+indicator_tracker_new (void)
4616+{
4617+ return INDICATOR_TRACKER(g_object_new(INDICATOR_TRACKER_TYPE, NULL));
4618+}
4619+
4620+GList *
4621+indicator_tracker_get_indicators (IndicatorTracker * tracker)
4622+{
4623+ g_return_val_if_fail(IS_INDICATOR_TRACKER(tracker), NULL);
4624+
4625+ GList * retval = NULL;
4626+ gint i;
4627+
4628+ for (i = 0; i < tracker->priv->indicators->len; i++) {
4629+ IndicatorTrackerIndicator * item = &g_array_index(tracker->priv->indicators, IndicatorTrackerIndicator, i);
4630+ retval = g_list_prepend(retval, item);
4631+ }
4632+
4633+ for (i = 0; i < tracker->priv->app_indicators->len; i++) {
4634+ AppIndicator * item = &g_array_index(tracker->priv->app_indicators, AppIndicator, i);
4635+ retval = g_list_prepend(retval, &(item->system));
4636+ }
4637+
4638+ return retval;
4639+}
4640+
4641+/* Function watches names on Dbus to find out when the system indicators
4642+ go on and off the bus. This makes it so that we can handle them properly
4643+ at the higher levels in the system. If a system indicator gets added to
4644+ the list it gets put the indicator structure. */
4645+static void
4646+system_watch_appeared (GDBusConnection * connection, const gchar * name, const gchar * name_owner, gpointer user_data)
4647+{
4648+ g_return_if_fail(IS_INDICATOR_TRACKER(user_data));
4649+ g_return_if_fail(name_owner != NULL);
4650+ g_return_if_fail(name_owner[0] != '\0');
4651+ IndicatorTracker * self = INDICATOR_TRACKER(user_data);
4652+
4653+ /* Check all the system indicators because there might be more than
4654+ one menu/indicator on a well known name */
4655+ int indicator_cnt;
4656+ for (indicator_cnt = 0; indicator_cnt < G_N_ELEMENTS(system_indicators); indicator_cnt++) {
4657+ SystemIndicator * sys_indicator = &system_indicators[indicator_cnt];
4658+
4659+ if (g_strcmp0(sys_indicator->dbus_name, name) != 0) {
4660+ continue;
4661+ }
4662+
4663+ /* Check to see if we already have this system indicator in the
4664+ list of indicators */
4665+ int i;
4666+ for (i = 0; i < self->priv->indicators->len; i++) {
4667+ IndicatorTrackerIndicator * indicator = &g_array_index(self->priv->indicators, IndicatorTrackerIndicator, i);
4668+
4669+ if (g_strcmp0(sys_indicator->dbus_name, indicator->dbus_name_wellknown) != 0) {
4670+ continue;
4671+ }
4672+
4673+ if (g_strcmp0(sys_indicator->dbus_menu_path, indicator->dbus_object) != 0) {
4674+ continue;
4675+ }
4676+
4677+ /* If both of them match, we need to break */
4678+ break;
4679+ }
4680+
4681+ /* We found it in the list so we broke out eary */
4682+ if (i != self->priv->indicators->len) {
4683+ continue;
4684+ }
4685+
4686+ g_debug("Adding an indicator for '%s' at owner '%s'", name, name_owner);
4687+ /* Okay, we need to build one for this system indicator */
4688+ IndicatorTrackerIndicator final_indicator = {
4689+ name: g_strdup(sys_indicator->indicator_name),
4690+ dbus_name: g_strdup(name_owner),
4691+ dbus_name_wellknown: g_strdup(sys_indicator->dbus_name),
4692+ dbus_object: g_strdup(sys_indicator->dbus_menu_path),
4693+ prefix: g_strdup(_(sys_indicator->user_visible_name)),
4694+ icon: g_strdup(sys_indicator->icon)
4695+ };
4696+
4697+ g_array_append_val(self->priv->indicators, final_indicator);
4698+ }
4699+
4700+ return;
4701+}
4702+
4703+/* When the names drop off of DBus we need to check to see if that
4704+ means any indicators getting lost as well. If so, remove them from
4705+ the indicator list. */
4706+static void
4707+system_watch_vanished (GDBusConnection * connection, const gchar * name, gpointer user_data)
4708+{
4709+ g_return_if_fail(IS_INDICATOR_TRACKER(user_data));
4710+ IndicatorTracker * self = INDICATOR_TRACKER(user_data);
4711+
4712+ /* See if any of our indicators know this name */
4713+ int i;
4714+ for (i = 0; i < self->priv->indicators->len; i++) {
4715+ IndicatorTrackerIndicator * indicator = &g_array_index(self->priv->indicators, IndicatorTrackerIndicator, i);
4716+
4717+ /* Doesn't match */
4718+ if (g_strcmp0(indicator->dbus_name_wellknown, name) != 0) {
4719+ continue;
4720+ }
4721+
4722+ system_indicator_cleanup(indicator);
4723+
4724+ g_array_remove_index_fast(self->priv->indicators, i);
4725+ /* Oh, this is confusing. Basically becasue we shorten the array
4726+ we need to check the one that replaced the entry we deleted
4727+ so we have to look in this same slot again. */
4728+ i--;
4729+ }
4730+
4731+ return;
4732+}
4733+
4734+/* Removes all the memory that is allocated inside the system indicator
4735+ structure. The actual indicator is not free'd */
4736+static void
4737+system_indicator_cleanup (gpointer pindicator)
4738+{
4739+ g_return_if_fail(pindicator != NULL);
4740+
4741+ IndicatorTrackerIndicator * indicator = (IndicatorTrackerIndicator *)pindicator;
4742+
4743+ g_free(indicator->name);
4744+ g_free(indicator->dbus_name);
4745+ g_free(indicator->dbus_name_wellknown);
4746+ g_free(indicator->dbus_object);
4747+ g_free(indicator->prefix);
4748+
4749+ return;
4750+}
4751+
4752+/* Deletes the allocated memory in the app indicator structure so
4753+ that we don't leak any! */
4754+static void
4755+app_indicator_cleanup (gpointer pindicator)
4756+{
4757+ g_return_if_fail(pindicator != NULL);
4758+
4759+ AppIndicator * indicator = (AppIndicator *)pindicator;
4760+
4761+ system_indicator_cleanup(&(indicator->system));
4762+
4763+ return;
4764+}
4765+
4766+/* Gets called when we have an app proxy. Now we can start talking
4767+ to it, and learning from it */
4768+static void
4769+app_proxy_built (GObject * object, GAsyncResult * result, gpointer user_data)
4770+{
4771+ GError * error = NULL;
4772+ GDBusProxy * proxy = g_dbus_proxy_new_for_bus_finish(result, &error);
4773+
4774+ if (error != NULL) {
4775+ g_warning("Unable to build App Indicator Proxy: %s", error->message);
4776+ g_error_free(error);
4777+ return;
4778+ }
4779+
4780+ g_return_if_fail(IS_INDICATOR_TRACKER(user_data));
4781+ IndicatorTracker * self = INDICATOR_TRACKER(user_data);
4782+
4783+ self->priv->app_proxy = proxy;
4784+
4785+ /* Watch to see if we get dropped off the bus or not */
4786+ g_signal_connect(G_OBJECT(self->priv->app_proxy), "notify::g-name-owner", G_CALLBACK(app_proxy_name_change), self);
4787+ g_signal_connect(G_OBJECT(self->priv->app_proxy), "g-signal", G_CALLBACK(app_proxy_signal), self);
4788+
4789+ /* Act like the name changed, as this function checks to see
4790+ if it is there or not anyway */
4791+ app_proxy_name_change(G_OBJECT(self->priv->app_proxy), NULL, self);
4792+
4793+ return;
4794+}
4795+
4796+/* React to the name changing, this usually means the service is either
4797+ leaving or joining the bus. */
4798+static void
4799+app_proxy_name_change (GObject * gobject, GParamSpec * pspec, gpointer user_data)
4800+{
4801+ g_return_if_fail(IS_INDICATOR_TRACKER(user_data));
4802+ IndicatorTracker * self = INDICATOR_TRACKER(user_data);
4803+
4804+ /* Check to see if this wire is hot! */
4805+ gchar * owner = g_dbus_proxy_get_name_owner(self->priv->app_proxy);
4806+ if (owner == NULL) {
4807+ /* Delete entries if we have them */
4808+
4809+ return;
4810+ }
4811+ g_free(owner);
4812+
4813+ /* Query to see if there's any indicator already out there. */
4814+ g_dbus_proxy_call (self->priv->app_proxy,
4815+ "GetApplications",
4816+ NULL, /* paramaters */
4817+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
4818+ -1, /* timeout */
4819+ NULL, /* cancellable */
4820+ app_proxy_apps_replace,
4821+ self);
4822+
4823+ return;
4824+}
4825+
4826+/* Replace our current list of applications with the new ones that we've
4827+ gotten from a 'GetApplications' to the application service */
4828+static void
4829+app_proxy_apps_replace (GObject * obj, GAsyncResult * res, gpointer user_data)
4830+{
4831+ GError * error = NULL;
4832+
4833+ GVariant * params = g_dbus_proxy_call_finish(G_DBUS_PROXY(obj), res, &error);
4834+
4835+ if (error != NULL) {
4836+ g_warning("Unable to get applications for indicator service: %s", error->message);
4837+ g_error_free(error);
4838+ return;
4839+ }
4840+
4841+ /* Remove the first application indicator until it starts
4842+ returning an error. This will clear the array. */
4843+ IndicatorTracker * self = INDICATOR_TRACKER(user_data);
4844+ while (app_proxy_remove_indicator(self, 0));
4845+
4846+ GVariant * array = g_variant_get_child_value(params, 0);
4847+
4848+ GVariantIter iter;
4849+ g_variant_iter_init(&iter, array);
4850+
4851+ gchar * iconname = NULL;
4852+ guint position;
4853+ gchar * dbusaddress = NULL;
4854+ gchar * dbusobject = NULL;
4855+ gchar * iconpath = NULL;
4856+ gchar * label = NULL;
4857+ gchar * labelguide = NULL;
4858+ gchar * accessibledesc = NULL;
4859+ gchar * hint = NULL;
4860+
4861+ while (g_variant_iter_loop(&iter,
4862+ "(sisosssss)",
4863+ &iconname,
4864+ &position,
4865+ &dbusaddress,
4866+ &dbusobject,
4867+ &iconpath,
4868+ &label,
4869+ &labelguide,
4870+ &accessibledesc,
4871+ &hint)) {
4872+ /* TODO: No icon name for ID */
4873+ app_proxy_new_indicator(self, position, iconname, accessibledesc, dbusaddress, dbusobject, iconname);
4874+ }
4875+
4876+ g_variant_unref(array);
4877+ g_variant_unref(params);
4878+
4879+ return;
4880+}
4881+
4882+/* Signals coming over dbus from the app indicator service. Gives
4883+ us updates to the indicator cache that we have. */
4884+static void
4885+app_proxy_signal (GDBusProxy *proxy, gchar * sender_name, gchar * signal_name, GVariant * parameters, gpointer user_data)
4886+{
4887+ IndicatorTracker * self = INDICATOR_TRACKER(user_data);
4888+
4889+ if (g_strcmp0(signal_name, "ApplicationAdded") == 0) {
4890+ gchar * iconname = NULL;
4891+ guint position;
4892+ gchar * dbusaddress = NULL;
4893+ gchar * dbusobject = NULL;
4894+ gchar * iconpath = NULL;
4895+ gchar * label = NULL;
4896+ gchar * labelguide = NULL;
4897+ gchar * accessibledesc = NULL;
4898+ gchar * hint = NULL;
4899+
4900+ g_variant_get(parameters, "(sisosssss)",
4901+ &iconname,
4902+ &position,
4903+ &dbusaddress,
4904+ &dbusobject,
4905+ &iconpath,
4906+ &label,
4907+ &labelguide,
4908+ &accessibledesc,
4909+ &hint);
4910+
4911+ /* TODO: No icon name for ID */
4912+ app_proxy_new_indicator(self, position, iconname, accessibledesc, dbusaddress, dbusobject, iconname);
4913+
4914+ g_free(iconname);
4915+ g_free(dbusaddress);
4916+ g_free(dbusobject);
4917+ g_free(iconpath);
4918+ g_free(label);
4919+ g_free(labelguide);
4920+ g_free(accessibledesc);
4921+ g_free(hint);
4922+ } else if (g_strcmp0(signal_name, "ApplicationRemoved") == 0) {
4923+ gint position;
4924+
4925+ g_variant_get_child(parameters, 0, "i", &position);
4926+
4927+ gboolean remove = app_proxy_remove_indicator(self, position);
4928+
4929+ /* If we can't remove it, something is really goofy, we're probably
4930+ out of sync. So let's go nuclear. */
4931+ if (!remove) {
4932+ g_warning("Unable to remove indicator '%d' getting full list", position);
4933+ app_proxy_name_change(G_OBJECT(self->priv->app_proxy), NULL, self);
4934+ }
4935+ } else if (g_strcmp0(signal_name, "ApplicationIconChanged") == 0) {
4936+ gchar * iconname = NULL;
4937+ guint position = 0;
4938+ gchar * accessibledesc = NULL;
4939+
4940+ g_variant_get(parameters, "(iss)",
4941+ &position,
4942+ &iconname,
4943+ &accessibledesc);
4944+
4945+ app_proxy_icon_changed(self, position, iconname, accessibledesc);
4946+
4947+ g_free(iconname);
4948+ g_free(accessibledesc);
4949+ } else if (g_strcmp0(signal_name, "ApplicationIconThemePathChanged") == 0) {
4950+ /* Don't care */
4951+ } else if (g_strcmp0(signal_name, "ApplicationLabelChanged") == 0) {
4952+ /* Don't care */
4953+ } else {
4954+ g_debug("Application Service signal '%s' not handled", signal_name);
4955+ }
4956+
4957+ return;
4958+}
4959+
4960+/* Add a new application indicator to our list */
4961+static void
4962+app_proxy_new_indicator (IndicatorTracker * self, gint position, const gchar * id, const gchar * accessibledesc, const gchar * dbusaddress, const gchar * dbusobject, const gchar * iconname)
4963+{
4964+ g_debug("New application indicator: %s", dbusobject);
4965+
4966+ AppIndicator indicator = {
4967+ system: {
4968+ name: g_strdup_printf("appindicator:%s", id),
4969+ dbus_name: g_strdup(dbusaddress),
4970+ dbus_name_wellknown: NULL,
4971+ dbus_object: g_strdup(dbusobject),
4972+ prefix: g_strdup(accessibledesc),
4973+ icon: g_strdup(iconname)
4974+ },
4975+ alert: FALSE,
4976+ alert_name: NULL,
4977+ normal_name: NULL
4978+ };
4979+
4980+ if (indicator.system.prefix == NULL || indicator.system.prefix[0] == '\0') {
4981+ g_free(indicator.system.prefix);
4982+ indicator.system.prefix = g_strdup(_("Unknown Indicator"));
4983+ }
4984+
4985+ g_array_insert_val(self->priv->app_indicators, position, indicator);
4986+
4987+ return;
4988+}
4989+
4990+/* Remove an application indicator */
4991+static gboolean
4992+app_proxy_remove_indicator(IndicatorTracker * self, gint position)
4993+{
4994+ if (position >= self->priv->app_indicators->len) {
4995+ g_warning("Application removed for position outside of array");
4996+ return FALSE;
4997+ }
4998+
4999+ AppIndicator * indicator = &g_array_index(self->priv->app_indicators, AppIndicator, position);
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches