Merge lp:~3v1n0/unity/indicators-p into lp:unity

Proposed by Marco Trevisan (Treviño)
Status: Merged
Approved by: Marco Trevisan (Treviño)
Approved revision: no longer in the source branch.
Merged at revision: 2132
Proposed branch: lp:~3v1n0/unity/indicators-p
Merge into: lp:unity
Diff against target: 2730 lines (+1212/-342)
31 files modified
CMakeLists.txt (+1/-1)
UnityCore/AppmenuIndicator.cpp (+37/-0)
UnityCore/AppmenuIndicator.h (+45/-0)
UnityCore/CMakeLists.txt (+2/-0)
UnityCore/DBusIndicators.cpp (+87/-38)
UnityCore/DBusIndicators.h (+5/-2)
UnityCore/Indicator.cpp (+31/-11)
UnityCore/Indicator.h (+11/-7)
UnityCore/IndicatorEntry.cpp (+29/-24)
UnityCore/IndicatorEntry.h (+12/-7)
UnityCore/Indicators.cpp (+28/-7)
UnityCore/Indicators.h (+31/-10)
plugins/unityshell/src/PanelIndicatorEntryView.cpp (+21/-7)
plugins/unityshell/src/PanelIndicatorEntryView.h (+1/-0)
plugins/unityshell/src/PanelIndicatorsView.cpp (+1/-1)
plugins/unityshell/src/PanelIndicatorsView.h (+1/-1)
plugins/unityshell/src/PanelView.cpp (+6/-5)
plugins/unityshell/src/PanelView.h (+3/-3)
services/CMakeLists.txt (+1/-1)
services/panel-indicator-entry-accessible.c (+2/-1)
services/panel-main.c (+33/-10)
services/panel-marshal.list (+1/-0)
services/panel-root-accessible.c (+5/-5)
services/panel-service.c (+410/-165)
services/panel-service.h (+11/-4)
tests/CMakeLists.txt (+15/-2)
tests/test_indicator.cpp (+207/-0)
tests/test_indicator_appmenu.cpp (+66/-0)
tests/test_indicator_entry.cpp (+99/-22)
tests/unit/TestMain.cpp (+4/-4)
tests/unit/TestPanelService.cpp (+6/-4)
To merge this branch: bzr merge lp:~3v1n0/unity/indicators-p
Reviewer Review Type Date Requested Status
Thomi Richards (community) Approve
Tim Penhey (community) Approve
Andrea Azzarone (community) Approve
Michal Hruby (community) Approve
Review via email: mp+96862@code.launchpad.net

Commit message

Updated unity-panel-service and libunity-core against the libindicator API changes

Description of the change

Updated unity-panel-service and libunity-core against the libindicator API changes

Supporting the new libindicator API into unity-panel-service and exposing them in the higher level wrapper library for future usage.
Doing this, I've performed some cleanup of the code, and in particular I've:
 - Introduced the new AppmenuIndicator class to handle the appmenu indicator
   (typing it differently allows us to remove the type-checking based only on the
   indicator name, this is something we should keep at lower level).
 - Fixed an issue in Indicator class, that didn't disconnect from signals of the removed
   entries
 - Removed the IsUnused / MarkUnused methods from IndicatorEntry, since they were
   already deprecated since last cycle and actually useless. The visible() method can
   now be used to perform a similar check.
 - Removed the sscanf based matching from the panel-service.

On the unity-panel-service side I've also fixed two issues:
 - When the menus are open now they are moved to the right position if their
   entry has been moved (this happens when scrolling on the language selector).
 - It is now synced with the compiz gsetting key used to define which keybinding
   must be used to open the menus (so now, changing that value applies to both
   opening and closing the indicators, not only to the open action).

On the Tests side, I've re-enabled and updated the panel-service unit-test, that now is working again, plus I've added new tests for the Indicator and AppmenuIndicator classes and updated the tests for IndicatorEntry against the new API.
Some more tests are covered on the lp:~3v1n0/unity/indicators-tests dependent branch.

To post a comment you must log in.
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) wrote :

Hi,

202 + // We have to do this because on certain systems X won't have time to
203 + // respond to our request for XUngrabPointer and this will cause the
204 + // menu not to show
205 + auto data = new ShowEntryData();

Can we use a smart pointer for this please? Once you use a smart pointer you won't need this:

279 + delete data;

...and there's a larger design issue around how we create Indicator instances. This code:

671 - Indicator::Ptr indicator(new Indicator(name));
672 + Indicator* indptr;
673 +
674 + if (name == "libappmenu.so")
675 + indptr = new AppmenuIndicator(name);
676 + else
677 + indptr = new Indicator(name);
678 +
679 + Indicator::Ptr indicator(indptr);

Makes me uneasy - especially this bit:

685 +
686 + if (indicator->IsAppmenu())
687 + {
688 + AppmenuIndicator *appmenu = dynamic_cast<AppmenuIndicator*>(indicator.get());
689 +
690 + if (appmenu)
691 + appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_, &Indicators::OnShowAppMenu));
692 + }
693 +

Why are we using a boolean member variable to encode class type? Maybe I'm missing something, but why not just do this:

AppmenuIndicator *appmenu = dynamic_cast<AppmenuIndicator*>(indicator.get());

if (appmenu)
    appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_, &Indicators::OnShowAppMenu));

..or even better, hook up the signal for all indicators, and have child classes that don't need the signal simply not use it.

This is wrong:

818 + return proxy_->id().c_str();

The method returns a std::string, so you don't need the ".c_str" on the end.

These:

1127 +#define COMPIZ_OPTIONS_PATH "/apps/compiz-1/plugins/unityshell/screen0/options"
1128 +#define MENU_TOGGLE_KEYBINDING_PATH COMPIZ_OPTIONS_PATH"/panel_first_menu"

Don't need to be #defines - nor do the three #defines right above them.

In panel-service.c, I'm 99% sure there's a memory leak on line 829, where we do this:

  gchar *entry_id = get_indicator_entry_id_by_entry (entry);
  g_hash_table_insert (self->priv->id2entry_hash, entry_id, entry);

entry_id needs to be freed.

The points above need to be fixed. You should also get someone else to look at this (someone who understands the panel code).

Cheers,

review: Needs Fixing
Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

> 202 + // We have to do this because on certain systems X won't have time
> to
> 203 + // respond to our request for XUngrabPointer and this will cause the
> 204 + // menu not to show
> 205 + auto data = new ShowEntryData();
>
> Can we use a smart pointer for this please? Once you use a smart pointer you
> won't need this:
>
> 279 + delete data;

Mh, I didn't used these, not to change too much the pre-existent code.

> ...and there's a larger design issue around how we create Indicator instances.
> This code:
>
> 671 - Indicator::Ptr indicator(new Indicator(name));
> 672 + Indicator* indptr;
> 673 +
> 674 + if (name == "libappmenu.so")
> 675 + indptr = new AppmenuIndicator(name);
> 676 + else
> 677 + indptr = new Indicator(name);
> 678 +
> 679 + Indicator::Ptr indicator(indptr);
>
> Makes me uneasy - especially this bit:
>
> 685 +
> 686 + if (indicator->IsAppmenu())
> 687 + {
> 688 + AppmenuIndicator *appmenu =
> dynamic_cast<AppmenuIndicator*>(indicator.get());
> 689 +
> 690 + if (appmenu)
> 691 + appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_,
> &Indicators::OnShowAppMenu));
> 692 + }
> 693 +
>
> Why are we using a boolean member variable to encode class type? Maybe I'm
> missing something, but why not just do this:
>
> AppmenuIndicator *appmenu = dynamic_cast<AppmenuIndicator*>(indicator.get());

Using that bool check is faster than doing an unneeded dymamic_cast (actually we could even use just a static_cast there). And currently we only have two kinds of indicators that we must care of, standard indicators and appmenu indicators. Maybe we could use a GetType or something...

> if (appmenu)
> appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_,
> &Indicators::OnShowAppMenu));
>
> ..or even better, hook up the signal for all indicators, and have child
> classes that don't need the signal simply not use it.

Mh, I don't like too much the idea of adding a signal that is not related to all the indicators to the Indicator class. Also because that class will be filled with some other stuff soon, and I don't want to repeat them in the base class...

> This is wrong:
>
> 818 + return proxy_->id().c_str();
>
> The method returns a std::string, so you don't need the ".c_str" on the end.

True. The mistake was already there, but I didn't notice it. Thanks.

> These:
>
> 1127 +#define COMPIZ_OPTIONS_PATH
> "/apps/compiz-1/plugins/unityshell/screen0/options"
> 1128 +#define MENU_TOGGLE_KEYBINDING_PATH
> COMPIZ_OPTIONS_PATH"/panel_first_menu"
>
> Don't need to be #defines - nor do the three #defines right above them.

Mh... In a C scope they don't look so bad... Also I should repeat some strings without them.

> In panel-service.c, I'm 99% sure there's a memory leak on line 829, where we
> do this:
>
> gchar *entry_id = get_indicator_entry_id_by_entry (entry);
> g_hash_table_insert (self->priv->id2entry_hash, entry_id, entry);
>
> entry_id needs to be freed.

Not really: http://developer.gnome.org/glib/2.29/glib-Hash-Tables.html#g-hash-table-insert :)

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

> > This is wrong:
> >
> > 818 + return proxy_->id().c_str();
> >
> > The method returns a std::string, so you don't need the ".c_str" on the end.
>
> True. The mistake was already there, but I didn't notice it. Thanks.

Ah, actually I already fixed in another branch... I put the fix here too.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

671 - Indicator::Ptr indicator(new Indicator(name));
672 + Indicator* indptr;
673 +
674 + if (name == "libappmenu.so")
675 + indptr = new AppmenuIndicator(name);
676 + else
677 + indptr = new Indicator(name);
678 +
679 + Indicator::Ptr indicator(indptr);

It's a kind of object factory, for me it's ok.

685 +
686 + if (indicator->IsAppmenu())
687 + {
688 + AppmenuIndicator *appmenu = dynamic_cast<AppmenuIndicator*>(indicator.get());
689 +
690 + if (appmenu)
691 + appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_, &Indicators::OnShowAppMenu));
692 + }
693 +

If you can remove this please do... :) Otherwise it's ok for me.

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

> 685 +
> 686 + if (indicator->IsAppmenu())
> 687 + {
> 688 + AppmenuIndicator *appmenu =
> dynamic_cast<AppmenuIndicator*>(indicator.get());
> 689 +
> 690 + if (appmenu)
> 691 + appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_,
> &Indicators::OnShowAppMenu));
> 692 + }
> 693 +
>
> If you can remove this please do... :) Otherwise it's ok for me.

Yes, done. It was possible to get that before as well, but I didn't like to connect to the appmenu specific signal before than the others :)

Revision history for this message
Michal Hruby (mhr3) wrote :

183 data->proxy = proxy_;

Makes me sad that it doesn't use the dbus proxy class from unity core.

194 + g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) send_show_entry, data, NULL);

Moreover this is pretty dangerous, it should at least grab a ref on the proxy. Alternatively such timeouts should be removed in the destructor.

1114 +#include <fcntl.h>

I hope this isn't needed?

Revision history for this message
Marco Trevisan (Treviño) (3v1n0) wrote :

> 183 data->proxy = proxy_;
>
> Makes me sad that it doesn't use the dbus proxy class from unity core.

Yes, you're right.
Unfortunately that code was written before it was available I guess, so I just didn't rewrite the whole thing.
I see if that's quickly feasible ;)

> 194 + g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) send_show_entry,
> data, NULL);
>
> Moreover this is pretty dangerous, it should at least grab a ref on the proxy.
> Alternatively such timeouts should be removed in the destructor.

Sure, again... I just followed the old school! :)

> 1114 +#include <fcntl.h>
>
> I hope this isn't needed?

Yes, no more needed. /me cleaning it.

Revision history for this message
Michal Hruby (mhr3) wrote :

> DBusIndicators: use glib::Object to handle the dbus proxy, so we have ref-counting for free

So much nicer, thx!

review: Approve
Revision history for this message
Andrea Azzarone (azzar1) :
review: Approve
Revision history for this message
Tim Penhey (thumper) wrote :

Gah... I was wanting to work out how to remove the IsAppmenu bit, but since the cure is worse than the symptoms, I'm prepared to accept it.

review: Approve
Revision history for this message
Thomi Richards (thomir-deactivatedaccount) :
review: Approve
Revision history for this message
Unity Merger (unity-merger) wrote :

Attempt to merge into lp:unity failed due to conflicts:

text conflict in CMakeLists.txt
text conflict in plugins/unityshell/src/PanelIndicatorEntryView.cpp

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2012-03-13 11:57:33 +0000
3+++ CMakeLists.txt 2012-03-20 10:02:20 +0000
4@@ -122,7 +122,7 @@
5 #
6 # Compiz Plugins
7 #
8-set (UNITY_PLUGIN_DEPS "compiz;nux-2.0>=2.0.0;libbamf3;dee-1.0;gio-2.0;gio-unix-2.0;dbusmenu-glib-0.4;x11;libstartup-notification-1.0;gthread-2.0;indicator3-0.4;atk;unity-misc>=0.4.0;gconf-2.0;libutouch-geis;gtk+-3.0>=3.1;sigc++-2.0;json-glib-1.0;libnotify;gnome-desktop-3.0;gdu;xfixes")
9+set (UNITY_PLUGIN_DEPS "compiz;nux-2.0>=2.0.0;libbamf3;dee-1.0;gio-2.0;gio-unix-2.0;dbusmenu-glib-0.4;x11;libstartup-notification-1.0;gthread-2.0;indicator3-0.4>=0.4.90;atk;unity-misc>=0.4.0;gconf-2.0;libutouch-geis;gtk+-3.0>=3.1;sigc++-2.0;json-glib-1.0;libnotify;gnome-desktop-3.0;gdu;xfixes")
10
11 add_subdirectory(plugins/unityshell)
12 add_subdirectory(plugins/gtkloader)
13
14=== added file 'UnityCore/AppmenuIndicator.cpp'
15--- UnityCore/AppmenuIndicator.cpp 1970-01-01 00:00:00 +0000
16+++ UnityCore/AppmenuIndicator.cpp 2012-03-20 10:02:20 +0000
17@@ -0,0 +1,37 @@
18+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
19+/*
20+ * Copyright (C) 2012 Canonical Ltd
21+ *
22+ * This program is free software: you can redistribute it and/or modify
23+ * it under the terms of the GNU General Public License version 3 as
24+ * published by the Free Software Foundation.
25+ *
26+ * This program is distributed in the hope that it will be useful,
27+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
28+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+ * GNU General Public License for more details.
30+ *
31+ * You should have received a copy of the GNU General Public License
32+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
33+ *
34+ * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
35+ */
36+
37+#include "AppmenuIndicator.h"
38+
39+namespace unity
40+{
41+namespace indicator
42+{
43+
44+AppmenuIndicator::AppmenuIndicator(std::string const& name)
45+ : Indicator(name)
46+{}
47+
48+void AppmenuIndicator::ShowAppmenu(unsigned int xid, int x, int y, unsigned int timestamp) const
49+{
50+ on_show_appmenu.emit(xid, x, y, timestamp);
51+}
52+
53+} // namespace indicator
54+} // namespace unity
55
56=== added file 'UnityCore/AppmenuIndicator.h'
57--- UnityCore/AppmenuIndicator.h 1970-01-01 00:00:00 +0000
58+++ UnityCore/AppmenuIndicator.h 2012-03-20 10:02:20 +0000
59@@ -0,0 +1,45 @@
60+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
61+/*
62+ * Copyright (C) 2012 Canonical Ltd
63+ *
64+ * This program is free software: you can redistribute it and/or modify
65+ * it under the terms of the GNU General Public License version 3 as
66+ * published by the Free Software Foundation.
67+ *
68+ * This program is distributed in the hope that it will be useful,
69+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
70+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
71+ * GNU General Public License for more details.
72+ *
73+ * You should have received a copy of the GNU General Public License
74+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
75+ *
76+ * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
77+ */
78+
79+#ifndef UNITY_APPMENU_INDICATOR_H
80+#define UNITY_APPMENU_INDICATOR_H
81+
82+#include "Indicator.h"
83+
84+namespace unity
85+{
86+namespace indicator
87+{
88+
89+class AppmenuIndicator : public Indicator
90+{
91+public:
92+ AppmenuIndicator(std::string const& name);
93+
94+ virtual bool IsAppmenu() const { return true; }
95+
96+ void ShowAppmenu(unsigned int xid, int x, int y, unsigned int timestamp) const;
97+
98+ sigc::signal<void, unsigned int, int, int, unsigned int> on_show_appmenu;
99+};
100+
101+}
102+}
103+
104+#endif // UNITY_APPMENU_INDICATOR_H
105
106=== modified file 'UnityCore/CMakeLists.txt'
107--- UnityCore/CMakeLists.txt 2012-02-07 07:42:12 +0000
108+++ UnityCore/CMakeLists.txt 2012-03-20 10:02:20 +0000
109@@ -8,6 +8,7 @@
110 #
111 set (CORE_HEADERS
112 ApplicationPreview.h
113+ AppmenuIndicator.h
114 Categories.h
115 Category.h
116 CheckOptionFilter.h
117@@ -44,6 +45,7 @@
118
119 set (CORE_SOURCES
120 ApplicationPreview.cpp
121+ AppmenuIndicator.cpp
122 Categories.cpp
123 Category.cpp
124 CheckOptionFilter.cpp
125
126=== modified file 'UnityCore/DBusIndicators.cpp'
127--- UnityCore/DBusIndicators.cpp 2012-02-09 10:33:04 +0000
128+++ UnityCore/DBusIndicators.cpp 2012-03-20 10:02:20 +0000
129@@ -79,12 +79,13 @@
130
131 struct ShowEntryData
132 {
133- GDBusProxy* proxy;
134+ glib::Object<GDBusProxy> proxy;
135 std::string entry_id;
136+ guint xid;
137 int x;
138 int y;
139+ guint button;
140 guint timestamp;
141- guint32 button;
142 };
143
144 bool run_local_panel_service();
145@@ -95,6 +96,7 @@
146 void on_sync_ready_cb(GObject* source, GAsyncResult* res, gpointer data);
147
148 bool send_show_entry(ShowEntryData* data);
149+bool send_show_appmenu(ShowEntryData* data);
150
151 } // anonymous namespace
152
153@@ -105,7 +107,6 @@
154 {
155 public:
156 Impl(DBusIndicators* owner);
157- ~Impl();
158
159 void OnRemoteProxyReady(GDBusProxy* proxy);
160 void Reconnect();
161@@ -121,17 +122,20 @@
162 GVariant* parameters);
163
164 virtual void OnEntryScroll(std::string const& entry_id, int delta);
165- virtual void OnEntryShowMenu(std::string const& entry_id,
166- int x, int y, int timestamp, int button);
167+ virtual void OnEntryShowMenu(std::string const& entry_id, unsigned int xid,
168+ int x, int y, unsigned int button,
169+ unsigned int timestamp);
170 virtual void OnEntrySecondaryActivate(std::string const& entry_id,
171 unsigned int timestamp);
172+ virtual void OnShowAppMenu(unsigned int xid, int x, int y,
173+ unsigned int timestamp);
174
175 std::string name() const;
176 std::string owner_name() const;
177 bool using_local_service() const;
178
179 DBusIndicators* owner_;
180- GDBusProxy* proxy_;
181+ glib::Object<GDBusProxy> proxy_;
182 typedef std::vector<SyncDataPtr> PendingSyncs;
183 PendingSyncs pending_syncs_;
184
185@@ -143,19 +147,10 @@
186 // Public Methods
187 DBusIndicators::Impl::Impl(DBusIndicators* owner)
188 : owner_(owner)
189- , proxy_(NULL)
190 {
191 Reconnect();
192 }
193
194-DBusIndicators::Impl::~Impl()
195-{
196- if (G_IS_OBJECT(proxy_))
197- {
198- g_object_unref(proxy_);
199- }
200-}
201-
202 void DBusIndicators::Impl::Reconnect()
203 {
204 g_spawn_command_line_sync("killall -9 unity-panel-service",
205@@ -212,22 +207,43 @@
206
207
208 void DBusIndicators::Impl::OnEntryShowMenu(std::string const& entry_id,
209- int x, int y, int timestamp, int button)
210+ unsigned int xid, int x, int y,
211+ unsigned int button,
212+ unsigned int timestamp)
213 {
214- owner_->on_entry_show_menu.emit(entry_id, x, y, timestamp, button);
215+ owner_->on_entry_show_menu.emit(entry_id, xid, x, y, button, timestamp);
216
217 // We have to do this because on certain systems X won't have time to
218 // respond to our request for XUngrabPointer and this will cause the
219 // menu not to show
220- ShowEntryData* data = new ShowEntryData();
221+ auto data = new ShowEntryData();
222 data->proxy = proxy_;
223 data->entry_id = entry_id;
224+ data->xid = xid;
225 data->x = x;
226 data->y = y;
227- data->timestamp = timestamp;
228 data->button = button;
229-
230- g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)send_show_entry, data, NULL);
231+ data->timestamp = timestamp;
232+
233+ g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) send_show_entry, data, NULL);
234+}
235+
236+void DBusIndicators::Impl::OnShowAppMenu(unsigned int xid, int x, int y,
237+ unsigned int timestamp)
238+{
239+ owner_->on_show_appmenu.emit(xid, x, y, timestamp);
240+
241+ // We have to do this because on certain systems X won't have time to
242+ // respond to our request for XUngrabPointer and this will cause the
243+ // menu not to show
244+ auto data = new ShowEntryData();
245+ data->proxy = proxy_;
246+ data->xid = xid;
247+ data->x = x;
248+ data->y = y;
249+ data->timestamp = timestamp;
250+
251+ g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc) send_show_appmenu, data, NULL);
252 }
253
254 void DBusIndicators::Impl::OnEntrySecondaryActivate(std::string const& entry_id,
255@@ -457,10 +473,13 @@
256 std::string signal_name(signal_name_);
257 if (signal_name == "EntryActivated")
258 {
259- const char* entry_name = g_variant_get_string(g_variant_get_child_value(parameters, 0), NULL);
260+ glib::String entry_name;
261+ nux::Rect geo;
262+ g_variant_get(parameters, "(s(iiuu))", &entry_name, &geo.x, &geo.y, &geo.width, &geo.height);
263+
264 if (entry_name)
265 {
266- owner_->ActivateEntry(entry_name);
267+ owner_->ActivateEntry(entry_name.Str(), geo);
268 }
269 }
270 else if (signal_name == "EntryActivateRequest")
271@@ -499,8 +518,7 @@
272
273 DBusIndicators::DBusIndicators()
274 : pimpl(new Impl(this))
275-{
276-}
277+{}
278
279 DBusIndicators::~DBusIndicators()
280 {
281@@ -519,9 +537,10 @@
282 }
283
284 void DBusIndicators::OnEntryShowMenu(std::string const& entry_id,
285- int x, int y, int timestamp, int button)
286+ unsigned int xid, int x, int y,
287+ unsigned int button, unsigned int timestamp)
288 {
289- pimpl->OnEntryShowMenu(entry_id, x, y, timestamp, button);
290+ pimpl->OnEntryShowMenu(entry_id, xid, x, y, button, timestamp);
291 }
292
293 void DBusIndicators::OnEntrySecondaryActivate(std::string const& entry_id,
294@@ -530,6 +549,12 @@
295 pimpl->OnEntrySecondaryActivate(entry_id, timestamp);
296 }
297
298+void DBusIndicators::OnShowAppMenu(unsigned int xid, int x, int y,
299+ unsigned int timestamp)
300+{
301+ pimpl->OnShowAppMenu(xid, x, y, timestamp);
302+}
303+
304 std::string DBusIndicators::name() const
305 {
306 return pimpl->name();
307@@ -651,21 +676,45 @@
308 bool send_show_entry(ShowEntryData* data)
309 {
310 g_return_val_if_fail(data != NULL, FALSE);
311- g_return_val_if_fail(G_IS_DBUS_PROXY(data->proxy), FALSE);
312+ g_return_val_if_fail(G_IS_DBUS_PROXY(data->proxy.RawPtr()), FALSE);
313
314 g_dbus_proxy_call(data->proxy,
315 "ShowEntry",
316- g_variant_new("(suiii)",
317+ g_variant_new("(suiiuu)",
318 data->entry_id.c_str(),
319- 0,
320- data->x,
321- data->y,
322- data->button),
323- G_DBUS_CALL_FLAGS_NONE,
324- -1,
325- NULL,
326- NULL,
327- NULL);
328+ data->xid,
329+ data->x,
330+ data->y,
331+ data->button,
332+ data->timestamp),
333+ G_DBUS_CALL_FLAGS_NONE,
334+ -1,
335+ NULL,
336+ NULL,
337+ NULL);
338+
339+ delete data;
340+ return FALSE;
341+}
342+
343+bool send_show_appmenu(ShowEntryData* data)
344+{
345+ g_return_val_if_fail(data != NULL, FALSE);
346+ g_return_val_if_fail(G_IS_DBUS_PROXY(data->proxy.RawPtr()), FALSE);
347+
348+ g_dbus_proxy_call(data->proxy,
349+ "ShowAppMenu",
350+ g_variant_new("(uiiu)",
351+ data->xid,
352+ data->x,
353+ data->y,
354+ data->timestamp),
355+ G_DBUS_CALL_FLAGS_NONE,
356+ -1,
357+ NULL,
358+ NULL,
359+ NULL);
360+
361 delete data;
362 return FALSE;
363 }
364
365=== modified file 'UnityCore/DBusIndicators.h'
366--- UnityCore/DBusIndicators.h 2011-07-29 07:15:54 +0000
367+++ UnityCore/DBusIndicators.h 2012-03-20 10:02:20 +0000
368@@ -42,10 +42,13 @@
369 EntryLocationMap const& locations);
370
371 virtual void OnEntryScroll(std::string const& entry_id, int delta);
372- virtual void OnEntryShowMenu(std::string const& entry_id,
373- int x, int y, int timestamp, int button);
374+ virtual void OnEntryShowMenu(std::string const& entry_id, unsigned int xid,
375+ int x, int y, unsigned int button,
376+ unsigned int timestamp);
377 virtual void OnEntrySecondaryActivate(std::string const& entry_id,
378 unsigned int timestamp);
379+ virtual void OnShowAppMenu(unsigned int xid, int x, int y,
380+ unsigned int timestamp);
381
382 std::string name() const;
383 std::string owner_name() const;
384
385=== modified file 'UnityCore/Indicator.cpp'
386--- UnityCore/Indicator.cpp 2011-09-06 00:19:51 +0000
387+++ UnityCore/Indicator.cpp 2012-03-20 10:02:20 +0000
388@@ -35,11 +35,18 @@
389
390 Indicator::~Indicator()
391 {
392- for (auto entry : entries_) {
393- on_entry_removed(entry->id());
394- }
395+ for (auto entry : entries_)
396+ on_entry_removed.emit(entry->id());
397
398 entries_.clear();
399+
400+ for (auto it : entries_connections_)
401+ {
402+ for (auto conn : it.second)
403+ conn.disconnect();
404+
405+ it.second.clear();
406+ }
407 }
408
409 std::string const& Indicator::name() const
410@@ -72,7 +79,12 @@
411 }
412
413 for (auto entry : to_rm) {
414- on_entry_removed(entry->id());
415+ for (auto conn : entries_connections_[entry])
416+ conn.disconnect();
417+
418+ entries_connections_[entry].clear();
419+
420+ on_entry_removed.emit(entry->id());
421 entries_.remove(entry);
422 }
423
424@@ -82,10 +94,19 @@
425 continue;
426
427 // Just add the new entry, and connect it up.
428- new_entry->on_show_menu.connect(sigc::mem_fun(this, &Indicator::OnEntryShowMenu));
429- new_entry->on_secondary_activate.connect(sigc::mem_fun(this, &Indicator::OnEntrySecondaryActivate));
430- new_entry->on_scroll.connect(sigc::mem_fun(this, &Indicator::OnEntryScroll));
431+ sigc::connection conn;
432+ std::vector<sigc::connection>& new_entry_connections = entries_connections_[new_entry];
433+
434+ conn = new_entry->on_show_menu.connect(sigc::mem_fun(this, &Indicator::OnEntryShowMenu));
435+ new_entry_connections.push_back(conn);
436+
437+ conn = new_entry->on_secondary_activate.connect(sigc::mem_fun(this, &Indicator::OnEntrySecondaryActivate));
438+ new_entry_connections.push_back(conn);
439+
440+ conn = new_entry->on_scroll.connect(sigc::mem_fun(this, &Indicator::OnEntryScroll));
441 entries_.push_back(new_entry);
442+ new_entry_connections.push_back(conn);
443+
444 on_entry_added.emit(new_entry);
445 }
446 }
447@@ -99,11 +120,10 @@
448 return Entry::Ptr();
449 }
450
451-void Indicator::OnEntryShowMenu(std::string const& entry_id,
452- int x, int y,
453- int timestamp, int button)
454+void Indicator::OnEntryShowMenu(std::string const& entry_id, unsigned int xid,
455+ int x, int y, unsigned int button, unsigned int timestamp)
456 {
457- on_show_menu.emit(entry_id, x, y, timestamp, button);
458+ on_show_menu.emit(entry_id, xid, x, y, button, timestamp);
459 }
460
461 void Indicator::OnEntrySecondaryActivate(std::string const& entry_id,
462
463=== modified file 'UnityCore/Indicator.h'
464--- UnityCore/Indicator.h 2011-09-06 00:19:51 +0000
465+++ UnityCore/Indicator.h 2012-03-20 10:02:20 +0000
466@@ -23,6 +23,7 @@
467
468 #include <vector>
469 #include <boost/utility.hpp>
470+#include <sigc++/connection.h>
471
472 #include "IndicatorEntry.h"
473
474@@ -39,28 +40,31 @@
475 typedef std::list<Entry::Ptr> Entries;
476
477 Indicator(std::string const& name);
478- ~Indicator();
479+ virtual ~Indicator();
480
481 std::string const& name() const;
482
483+ virtual bool IsAppmenu() const { return false; }
484+
485 void Sync(Entries const& new_entries);
486 Entry::Ptr GetEntry(std::string const& entry_id) const;
487 Entries GetEntries() const;
488
489- void OnEntryShowMenu(std::string const& entry_id, int x, int y, int timestamp, int button);
490- void OnEntrySecondaryActivate(std::string const& entry_id, unsigned int timestamp);
491- void OnEntryScroll(std::string const& entry_id, int delta);
492-
493 // Signals
494 sigc::signal<void, Entry::Ptr const&> on_entry_added;
495 sigc::signal<void, std::string const&> on_entry_removed;
496- sigc::signal<void, std::string const&, int, int, int, int> on_show_menu;
497+ sigc::signal<void, std::string const&, unsigned int, int, int, unsigned int, unsigned int> on_show_menu;
498 sigc::signal<void, std::string const&, unsigned int> on_secondary_activate;
499 sigc::signal<void, std::string const&, int> on_scroll;
500
501-private:
502+protected:
503+ void OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y, unsigned int button, unsigned int timestamp);
504+ void OnEntrySecondaryActivate(std::string const& entry_id, unsigned int timestamp);
505+ void OnEntryScroll(std::string const& entry_id, int delta);
506+
507 Entries entries_;
508 std::string name_;
509+ std::map<Entry::Ptr, std::vector<sigc::connection>> entries_connections_;
510
511 friend std::ostream& operator<<(std::ostream& out, Indicator const& i);
512 };
513
514=== modified file 'UnityCore/IndicatorEntry.cpp'
515--- UnityCore/IndicatorEntry.cpp 2011-08-09 12:21:18 +0000
516+++ UnityCore/IndicatorEntry.cpp 2012-03-20 10:02:20 +0000
517@@ -27,8 +27,6 @@
518 namespace indicator
519 {
520
521-std::string const Entry::UNUSED_ID("|");
522-
523 Entry::Entry(std::string const& id,
524 std::string const &name_hint,
525 std::string const& label,
526@@ -104,6 +102,12 @@
527 return priority_;
528 }
529
530+bool Entry::visible() const
531+{
532+ return ((label_visible_ && !label_.empty()) ||
533+ (image_type_ != 0 && image_visible_ && !image_data_.empty()));
534+}
535+
536 void Entry::set_active(bool active)
537 {
538 if (active_ == active)
539@@ -114,6 +118,16 @@
540 updated.emit();
541 }
542
543+void Entry::set_geometry(nux::Rect const& geometry)
544+{
545+ if (geometry_ == geometry)
546+ return;
547+
548+ geometry_ = geometry;
549+ geometry_changed.emit(geometry);
550+ updated.emit();
551+}
552+
553 void Entry::setLabel(std::string const& label, bool sensitive, bool visible)
554 {
555 if (label_ == label && sensitive == label_sensitive_ && visible == label_visible_)
556@@ -154,6 +168,11 @@
557 return active_;
558 }
559
560+nux::Rect const& Entry::geometry() const
561+{
562+ return geometry_;
563+}
564+
565 Entry& Entry::operator=(Entry const& rhs)
566 {
567 id_ = rhs.id_;
568@@ -186,28 +205,14 @@
569 updated.emit();
570 }
571
572-void Entry::MarkUnused()
573-{
574- id_ = UNUSED_ID;
575- name_hint_ = "";
576- label_ = "";
577- label_sensitive_ = false;
578- label_visible_ = false;
579- image_type_ = 0;
580- image_data_ = "";
581- image_sensitive_ = false;
582- image_visible_ = false;
583- updated.emit();
584-}
585-
586-bool Entry::IsUnused() const
587-{
588- return id_ == UNUSED_ID;
589-}
590-
591-void Entry::ShowMenu(int x, int y, int timestamp, int button)
592-{
593- on_show_menu.emit(id_, x, y, timestamp, button);
594+void Entry::ShowMenu(int x, int y, unsigned int button, unsigned int timestamp)
595+{
596+ ShowMenu(0, x, y, button, timestamp);
597+}
598+
599+void Entry::ShowMenu(unsigned int xid, int x, int y, unsigned int button, unsigned int timestamp)
600+{
601+ on_show_menu.emit(id_, xid, x, y, button, timestamp);
602 }
603
604 void Entry::SecondaryActivate(unsigned int timestamp)
605
606=== modified file 'UnityCore/IndicatorEntry.h'
607--- UnityCore/IndicatorEntry.h 2011-08-09 12:21:18 +0000
608+++ UnityCore/IndicatorEntry.h 2012-03-20 10:02:20 +0000
609@@ -28,7 +28,7 @@
610 #include <sigc++/signal.h>
611 #include <boost/shared_ptr.hpp>
612
613-#include "NuxCore/Rect.h"
614+#include <NuxCore/Rect.h>
615
616 namespace unity
617 {
618@@ -43,7 +43,6 @@
619 {
620 public:
621 typedef boost::shared_ptr<Entry> Ptr;
622- static std::string const UNUSED_ID;
623
624 Entry(std::string const& id,
625 std::string const& name_hint,
626@@ -72,8 +71,13 @@
627 void set_active(bool active);
628 bool active() const;
629
630+ void set_geometry(nux::Rect const& geometry);
631+ nux::Rect const& geometry() const;
632+
633 int priority() const;
634
635+ bool visible() const;
636+
637 /**
638 * Whether this entry should be shown to the user.
639 * Example uses: Application menubar items are only shown when the user
640@@ -82,10 +86,8 @@
641 bool show_now() const;
642 void set_show_now(bool show_now);
643
644- void MarkUnused();
645- bool IsUnused() const;
646-
647- void ShowMenu(int x, int y, int timestamp, int button);
648+ void ShowMenu(int x, int y, unsigned int button, unsigned int timestamp);
649+ void ShowMenu(unsigned int xid, int x, int y, unsigned int button, unsigned int timestamp);
650 void SecondaryActivate(unsigned int timestamp);
651 void Scroll(int delta);
652
653@@ -96,9 +98,10 @@
654 // Signals
655 sigc::signal<void> updated;
656 sigc::signal<void, bool> active_changed;
657+ sigc::signal<void, nux::Rect const&> geometry_changed;
658 sigc::signal<void, bool> show_now_changed;
659
660- sigc::signal<void, std::string const&, int, int, int, int> on_show_menu;
661+ sigc::signal<void, std::string const&, unsigned int, int, int, unsigned int, unsigned int> on_show_menu;
662 sigc::signal<void, std::string const&, unsigned int> on_secondary_activate;
663 sigc::signal<void, std::string const&, int> on_scroll;
664
665@@ -118,6 +121,8 @@
666
667 bool show_now_;
668 bool active_;
669+
670+ nux::Rect geometry_;
671 };
672
673 std::ostream& operator<<(std::ostream& out, Entry const& e);
674
675=== modified file 'UnityCore/Indicators.cpp'
676--- UnityCore/Indicators.cpp 2011-09-05 13:21:08 +0000
677+++ UnityCore/Indicators.cpp 2012-03-20 10:02:20 +0000
678@@ -17,8 +17,9 @@
679 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
680 * Tim Penhey <tim.penhey@canonical.com>
681 */
682+
683 #include "Indicators.h"
684-
685+#include "AppmenuIndicator.h"
686
687 namespace unity
688 {
689@@ -35,7 +36,7 @@
690 : owner_(owner)
691 {}
692
693- void ActivateEntry(std::string const& entry_id);
694+ void ActivateEntry(std::string const& entry_id, nux::Rect const& geometry);
695 void SetEntryShowNow(std::string const& entry_id, bool show_now);
696
697 IndicatorsList GetIndicators() const;
698@@ -65,10 +66,10 @@
699 delete pimpl;
700 }
701
702-void Indicators::ActivateEntry(std::string const& entry_id)
703+void Indicators::ActivateEntry(std::string const& entry_id, nux::Rect const& geometry)
704 {
705- pimpl->ActivateEntry(entry_id);
706- on_entry_activated.emit(entry_id);
707+ pimpl->ActivateEntry(entry_id, geometry);
708+ on_entry_activated.emit(entry_id, geometry);
709 }
710
711 void Indicators::SetEntryShowNow(std::string const& entry_id, bool show_now)
712@@ -96,13 +97,19 @@
713 return pimpl->RemoveIndicator(name);
714 }
715
716-void Indicators::Impl::ActivateEntry(std::string const& entry_id)
717+void Indicators::Impl::ActivateEntry(std::string const& entry_id, nux::Rect const& geometry)
718 {
719 if (active_entry_)
720+ {
721+ active_entry_->set_geometry(nux::Rect());
722 active_entry_->set_active(false);
723+ }
724+
725 active_entry_ = GetEntry(entry_id);
726+
727 if (active_entry_)
728 {
729+ active_entry_->set_geometry(geometry);
730 active_entry_->set_active(true);
731 }
732 }
733@@ -131,12 +138,26 @@
734
735 Indicator::Ptr Indicators::Impl::AddIndicator(std::string const& name)
736 {
737- Indicator::Ptr indicator(new Indicator(name));
738+ Indicator* indptr;
739+
740+ if (name == "libappmenu.so")
741+ {
742+ AppmenuIndicator *appmenu = new AppmenuIndicator(name);
743+ appmenu->on_show_appmenu.connect(sigc::mem_fun(owner_, &Indicators::OnShowAppMenu));
744+ indptr = appmenu;
745+ }
746+ else
747+ {
748+ indptr = new Indicator(name);
749+ }
750+
751+ Indicator::Ptr indicator(indptr);
752
753 // The owner Indicators class is interested in the other events.
754 indicator->on_show_menu.connect(sigc::mem_fun(owner_, &Indicators::OnEntryShowMenu));
755 indicator->on_secondary_activate.connect(sigc::mem_fun(owner_, &Indicators::OnEntrySecondaryActivate));
756 indicator->on_scroll.connect(sigc::mem_fun(owner_, &Indicators::OnEntryScroll));
757+
758 indicators_[name] = indicator;
759 owner_->on_object_added.emit(indicator);
760
761
762=== modified file 'UnityCore/Indicators.h'
763--- UnityCore/Indicators.h 2011-09-05 13:21:08 +0000
764+++ UnityCore/Indicators.h 2012-03-20 10:02:20 +0000
765@@ -1,6 +1,6 @@
766 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
767 /*
768- * Copyright (C) 2010 Canonical Ltd
769+ * Copyright (C) 2010-2012 Canonical Ltd
770 *
771 * This program is free software: you can redistribute it and/or modify
772 * it under the terms of the GNU General Public License version 3 as
773@@ -43,7 +43,7 @@
774 /**
775 * internal
776 */
777- void ActivateEntry(std::string const& entry_id);
778+ void ActivateEntry(std::string const& entry_id, nux::Rect const& geometry);
779
780 /**
781 * internal
782@@ -58,11 +58,22 @@
783 /**
784 * internal
785 */
786- virtual void OnEntryShowMenu(std::string const& entry_id,
787- int x, int y, int timestamp, int button) = 0;
788+ virtual void OnEntryShowMenu(std::string const& entry_id, unsigned int xid,
789+ int x, int y, unsigned int button,
790+ unsigned int timestamp) = 0;
791+
792+ /**
793+ * internal
794+ */
795 virtual void OnEntrySecondaryActivate(std::string const& entry_id,
796 unsigned int timestamp) = 0;
797
798+ /**
799+ * internal
800+ */
801+ virtual void OnShowAppMenu(unsigned int xid, int x, int y,
802+ unsigned int timestamp) = 0;
803+
804 // Signals
805 sigc::signal<void, Indicator::Ptr const&> on_object_added;
806 sigc::signal<void, Indicator::Ptr const&> on_object_removed;
807@@ -79,7 +90,7 @@
808 * An entry just got activated. View needs to repaint it.
809 * @param entry_id entry id
810 */
811- sigc::signal<void, std::string const&> on_entry_activated;
812+ sigc::signal<void, std::string const&, nux::Rect const&> on_entry_activated;
813
814 /**
815 * internal
816@@ -89,12 +100,22 @@
817 /**
818 * The service is about to show a menu.
819 * @param entry_id entry id
820- * @param x x coordinate
821- * @param y y coordinate
822- * @param timestamp current time
823+ * @param xid window xid
824+ * @param x coordinate
825+ * @param y coordinate
826 * @param button pressed button
827- */
828- sigc::signal<void, std::string const&, int, int, int, int> on_entry_show_menu;
829+ * @param timestamp current time
830+ */
831+ sigc::signal<void, std::string const&, unsigned int, int, int, unsigned int, unsigned int> on_entry_show_menu;
832+
833+ /**
834+ * The service is about to show an appmenu.
835+ * @param xid window xid
836+ * @param x coordinate
837+ * @param y coordinate
838+ * @param timestamp current time
839+ */
840+ sigc::signal<void, unsigned int, int, int, unsigned int> on_show_appmenu;
841
842 protected:
843 Indicator::Ptr GetIndicator(std::string const& name);
844
845=== modified file 'plugins/unityshell/src/PanelIndicatorEntryView.cpp'
846--- plugins/unityshell/src/PanelIndicatorEntryView.cpp 2012-03-16 01:48:22 +0000
847+++ plugins/unityshell/src/PanelIndicatorEntryView.cpp 2012-03-20 10:02:20 +0000
848@@ -110,8 +110,8 @@
849 {
850 proxy_->ShowMenu(GetAbsoluteX(),
851 GetAbsoluteY() + PANEL_HEIGHT,
852- time(NULL),
853- button);
854+ button,
855+ time(NULL));
856 }
857
858 void PanelIndicatorEntryView::OnMouseDown(int x, int y, long button_flags, long key_flags)
859@@ -193,6 +193,14 @@
860 // 3. Paint something
861 void PanelIndicatorEntryView::Refresh()
862 {
863+ if (!IsVisible())
864+ {
865+ SetVisible(false);
866+ return;
867+ }
868+
869+ SetVisible(true);
870+
871 PangoLayout* layout = NULL;
872 PangoFontDescription* desc = NULL;
873 PangoAttrList* attrs = NULL;
874@@ -472,10 +480,7 @@
875
876 std::string PanelIndicatorEntryView::GetName() const
877 {
878- if (proxy_->IsUnused())
879- return "";
880- else
881- return proxy_->id();
882+ return proxy_->id();
883 }
884
885 void PanelIndicatorEntryView::AddProperties(GVariantBuilder* builder)
886@@ -498,7 +503,7 @@
887
888 void PanelIndicatorEntryView::GetGeometryForSync(indicator::EntryLocationMap& locations)
889 {
890- if (proxy_->IsUnused())
891+ if (!IsVisible())
892 return;
893
894 locations[proxy_->id()] = GetAbsoluteGeometry();
895@@ -546,6 +551,15 @@
896 return (disabled_ || !proxy_.get() || !IsSensitive());
897 }
898
899+bool PanelIndicatorEntryView::IsVisible()
900+{
901+ if (proxy_.get())
902+ {
903+ return proxy_->visible();
904+ }
905+ return false;
906+}
907+
908 void PanelIndicatorEntryView::OnFontChanged(GObject* gobject, GParamSpec* pspec,
909 gpointer data)
910 {
911
912=== modified file 'plugins/unityshell/src/PanelIndicatorEntryView.h'
913--- plugins/unityshell/src/PanelIndicatorEntryView.h 2012-02-07 23:19:01 +0000
914+++ plugins/unityshell/src/PanelIndicatorEntryView.h 2012-03-20 10:02:20 +0000
915@@ -60,6 +60,7 @@
916 bool IsEntryValid() const;
917 bool IsSensitive() const;
918 bool IsActive() const;
919+ bool IsVisible();
920 int GetEntryPriority() const;
921
922 void DashShown();
923
924=== modified file 'plugins/unityshell/src/PanelIndicatorsView.cpp'
925--- plugins/unityshell/src/PanelIndicatorsView.cpp 2012-02-04 05:28:23 +0000
926+++ plugins/unityshell/src/PanelIndicatorsView.cpp 2012-03-20 10:02:20 +0000
927@@ -180,7 +180,7 @@
928 {
929 PanelIndicatorEntryView* view = entry.second;
930
931- if (!target && view->GetAbsoluteGeometry().IsPointInside(x, y))
932+ if (!target && view->IsVisible() && view->GetAbsoluteGeometry().IsPointInside(x, y))
933 {
934 view->Activate(0);
935 target = view;
936
937=== modified file 'plugins/unityshell/src/PanelIndicatorsView.h'
938--- plugins/unityshell/src/PanelIndicatorsView.h 2012-02-04 05:28:23 +0000
939+++ plugins/unityshell/src/PanelIndicatorsView.h 2012-03-20 10:02:20 +0000
940@@ -83,7 +83,7 @@
941 Entries entries_;
942
943 std::string GetName() const;
944- void AddProperties(GVariantBuilder* builder);
945+ void AddProperties(GVariantBuilder* builder);
946
947 private:
948 typedef std::vector<indicator::Indicator::Ptr> Indicators;
949
950=== modified file 'plugins/unityshell/src/PanelView.cpp'
951--- plugins/unityshell/src/PanelView.cpp 2012-03-13 02:52:00 +0000
952+++ plugins/unityshell/src/PanelView.cpp 2012-03-20 10:02:20 +0000
953@@ -483,7 +483,7 @@
954 {
955 // Appmenu is treated differently as it needs to expand
956 // We could do this in a more special way, but who has the time for special?
957- if (proxy->name().find("appmenu") != std::string::npos)
958+ if (proxy->IsAppmenu())
959 {
960 _menu_view->AddIndicator(proxy);
961 }
962@@ -500,7 +500,7 @@
963
964 void PanelView::OnObjectRemoved(indicator::Indicator::Ptr const& proxy)
965 {
966- if (proxy->name().find("appmenu") != std::string::npos)
967+ if (proxy->IsAppmenu())
968 {
969 _menu_view->RemoveIndicator(proxy);
970 }
971@@ -569,7 +569,7 @@
972 return TRUE;
973 }
974
975-void PanelView::OnEntryActivated(std::string const& entry_id)
976+void PanelView::OnEntryActivated(std::string const& entry_id, nux::Rect const& geo)
977 {
978 bool active = (entry_id.size() > 0);
979 if (active && !_track_menu_pointer_id)
980@@ -605,8 +605,9 @@
981 _needs_geo_sync = true;
982 }
983
984-void PanelView::OnEntryShowMenu(std::string const& entry_id,
985- int x, int y, int timestamp, int button)
986+void PanelView::OnEntryShowMenu(std::string const& entry_id, unsigned int xid,
987+ int x, int y, unsigned int button,
988+ unsigned int timestamp)
989 {
990 Display* d = nux::GetGraphicsDisplay()->GetX11Display();
991 XUngrabPointer(d, CurrentTime);
992
993=== modified file 'plugins/unityshell/src/PanelView.h'
994--- plugins/unityshell/src/PanelView.h 2012-02-04 05:28:23 +0000
995+++ plugins/unityshell/src/PanelView.h 2012-03-20 10:02:20 +0000
996@@ -57,10 +57,10 @@
997 void OnIndicatorViewUpdated(PanelIndicatorEntryView* view);
998 void OnMenuPointerMoved(int x, int y);
999 void OnEntryActivateRequest(std::string const& entry_id);
1000- void OnEntryActivated(std::string const& entry_id);
1001+ void OnEntryActivated(std::string const& entry_id, nux::Rect const& geo);
1002 void OnSynced();
1003- void OnEntryShowMenu(std::string const& entry_id,
1004- int x, int y, int timestamp, int button);
1005+ void OnEntryShowMenu(std::string const& entry_id, unsigned int xid, int x, int y,
1006+ unsigned int button, unsigned int timestamp);
1007
1008 void SetPrimary(bool primary);
1009 bool GetPrimary();
1010
1011=== modified file 'services/CMakeLists.txt'
1012--- services/CMakeLists.txt 2012-02-04 05:28:23 +0000
1013+++ services/CMakeLists.txt 2012-03-20 10:02:20 +0000
1014@@ -2,7 +2,7 @@
1015 # Panel Service
1016 #
1017 find_package(PkgConfig)
1018-pkg_check_modules(SERVICE_DEPS REQUIRED gtk+-3.0>=3.3 gobject-2.0 gio-2.0 gthread-2.0 indicator3-0.4 x11 gconf-2.0)
1019+pkg_check_modules(SERVICE_DEPS REQUIRED gtk+-3.0>=3.3 gobject-2.0 gio-2.0 gthread-2.0 indicator3-0.4>=0.4.90 x11 gconf-2.0)
1020
1021 execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable indicatordir OUTPUT_VARIABLE _indicatordir OUTPUT_STRIP_TRAILING_WHITESPACE)
1022 execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable iconsdir OUTPUT_VARIABLE _iconsdir OUTPUT_STRIP_TRAILING_WHITESPACE)
1023
1024=== modified file 'services/panel-indicator-entry-accessible.c'
1025--- services/panel-indicator-entry-accessible.c 2011-11-11 10:44:50 +0000
1026+++ services/panel-indicator-entry-accessible.c 2012-03-20 10:02:20 +0000
1027@@ -46,7 +46,8 @@
1028 #define GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_INDICATOR_ENTRY_ACCESSIBLE, PanelIndicatorEntryAccessiblePrivate))
1029
1030 static void
1031-on_entry_activated_cb (PanelService *service, const gchar *entry_id, gpointer user_data)
1032+on_entry_activated_cb (PanelService *service, const gchar *entry_id,
1033+ gint x, gint y, guint w, guint h, gpointer user_data)
1034 {
1035 gchar *s;
1036 gboolean adding = FALSE;
1037
1038=== modified file 'services/panel-main.c'
1039--- services/panel-main.c 2012-01-12 16:47:05 +0000
1040+++ services/panel-main.c 2012-03-20 10:02:20 +0000
1041@@ -58,13 +58,21 @@
1042 " <method name='SyncGeometries'>"
1043 " <arg type='a(ssiiii)' name='geometries' direction='in'/>"
1044 " </method>"
1045- ""
1046+ ""
1047 " <method name='ShowEntry'>"
1048 " <arg type='s' name='entry_id' direction='in'/>"
1049- " <arg type='u' name='timestamp' direction='in'/>"
1050- " <arg type='i' name='x' direction='in'/>"
1051- " <arg type='i' name='y' direction='in'/>"
1052- " <arg type='i' name='button' direction='in'/>"
1053+ " <arg type='u' name='xid' direction='in'/>"
1054+ " <arg type='i' name='x' direction='in'/>"
1055+ " <arg type='i' name='y' direction='in'/>"
1056+ " <arg type='u' name='button' direction='in'/>"
1057+ " <arg type='u' name='timestamp' direction='in'/>"
1058+ " </method>"
1059+ ""
1060+ " <method name='ShowAppMenu'>"
1061+ " <arg type='u' name='xid' direction='in'/>"
1062+ " <arg type='i' name='x' direction='in'/>"
1063+ " <arg type='i' name='y' direction='in'/>"
1064+ " <arg type='u' name='timestamp' direction='in'/>"
1065 " </method>"
1066 ""
1067 " <method name='SecondaryActivateEntry'>"
1068@@ -79,6 +87,7 @@
1069 ""
1070 " <signal name='EntryActivated'>"
1071 " <arg type='s' name='entry_id' />"
1072+ " <arg type='(iiuu)' name='entry_geometry' />"
1073 " </signal>"
1074 ""
1075 " <signal name='ReSync'>"
1076@@ -174,18 +183,31 @@
1077 }
1078 else if (g_strcmp0 (method_name, "ShowEntry") == 0)
1079 {
1080+ guint32 xid;
1081 gchar *entry_id;
1082- guint32 timestamp;
1083 gint32 x;
1084 gint32 y;
1085- gint32 button;
1086- g_variant_get (parameters, "(suiii)", &entry_id, &timestamp, &x, &y, &button, NULL);
1087+ guint32 button;
1088+ guint32 timestamp;
1089+ g_variant_get (parameters, "(suiiuu)", &entry_id, &xid, &x, &y, &button, &timestamp, NULL);
1090
1091- panel_service_show_entry (service, entry_id, timestamp, x, y, button);
1092+ panel_service_show_entry (service, entry_id, xid, x, y, button, timestamp);
1093
1094 g_dbus_method_invocation_return_value (invocation, NULL);
1095 g_free (entry_id);
1096 }
1097+ else if (g_strcmp0 (method_name, "ShowAppMenu") == 0)
1098+ {
1099+ guint32 xid;
1100+ gint32 x;
1101+ gint32 y;
1102+ guint32 timestamp;
1103+ g_variant_get (parameters, "(uiiu)", &xid, &x, &y, &timestamp, NULL);
1104+
1105+ panel_service_show_app_menu (service, xid, x, y, timestamp);
1106+
1107+ g_dbus_method_invocation_return_value (invocation, NULL);
1108+ }
1109 else if (g_strcmp0 (method_name, "SecondaryActivateEntry") == 0)
1110 {
1111 gchar *entry_id;
1112@@ -229,6 +251,7 @@
1113 static void
1114 on_service_entry_activated (PanelService *service,
1115 const gchar *entry_id,
1116+ gint x, gint y, guint w, guint h,
1117 GDBusConnection *connection)
1118 {
1119 GError *error = NULL;
1120@@ -237,7 +260,7 @@
1121 S_PATH,
1122 S_IFACE,
1123 "EntryActivated",
1124- g_variant_new ("(s)", entry_id),
1125+ g_variant_new ("(s(iiuu))", entry_id, x, y, w, h),
1126 &error);
1127
1128 if (error)
1129
1130=== modified file 'services/panel-marshal.list'
1131--- services/panel-marshal.list 2011-03-17 14:16:13 +0000
1132+++ services/panel-marshal.list 2012-03-20 10:02:20 +0000
1133@@ -1,2 +1,3 @@
1134 NONE:OBJECT,POINTER,INT,INT,INT,INT
1135 VOID:STRING,BOOLEAN
1136+VOID:STRING,INT,INT,UINT,UINT
1137
1138=== modified file 'services/panel-root-accessible.c'
1139--- services/panel-root-accessible.c 2011-11-11 10:44:50 +0000
1140+++ services/panel-root-accessible.c 2012-03-20 10:02:20 +0000
1141@@ -134,16 +134,16 @@
1142 if (INDICATOR_IS_OBJECT (indicator))
1143 {
1144 AtkObject *child;
1145- gpointer *data;
1146+ gpointer *weak_data;
1147
1148 child = panel_indicator_accessible_new (indicator);
1149 /* FIXME: use proper signals once we support dynamic adding/removing
1150 * of indicators */
1151- data = g_new0 (gpointer, 2);
1152- data[0] = root;
1153- data[1] = child;
1154+ weak_data = g_new0 (gpointer, 2);
1155+ weak_data[0] = root;
1156+ weak_data[1] = child;
1157 g_object_weak_ref (G_OBJECT (indicator),
1158- (GWeakNotify) on_indicator_removed, data);
1159+ (GWeakNotify) on_indicator_removed, weak_data);
1160
1161 atk_object_set_parent (child, accessible);
1162 root->priv->a11y_children = g_slist_append (root->priv->a11y_children, child);
1163
1164=== modified file 'services/panel-service.c'
1165--- services/panel-service.c 2012-02-22 13:59:25 +0000
1166+++ services/panel-service.c 2012-03-20 10:02:20 +0000
1167@@ -1,6 +1,6 @@
1168 // -*- Mode: C; indent-tabs-mode: nil; tab-width: 2 -*-
1169 /*
1170- * Copyright (C) 2010 Canonical Ltd
1171+ * Copyright (C) 2010-2012 Canonical Ltd
1172 *
1173 * This program is free software: you can redistribute it and/or modify
1174 * it under the terms of the GNU General Public License version 3 as
1175@@ -27,8 +27,10 @@
1176 #include "panel-service.h"
1177
1178 #include <stdlib.h>
1179+#include <string.h>
1180 #include <gtk/gtk.h>
1181 #include <gdk/gdkx.h>
1182+#include <gconf/gconf-client.h>
1183
1184 #include <X11/extensions/XInput2.h>
1185 #include <X11/XKBlib.h>
1186@@ -44,12 +46,15 @@
1187 #define N_TIMEOUT_SLOTS 50
1188 #define MAX_INDICATOR_ENTRIES 50
1189
1190+#define COMPIZ_OPTIONS_PATH "/apps/compiz-1/plugins/unityshell/screen0/options"
1191+#define MENU_TOGGLE_KEYBINDING_PATH COMPIZ_OPTIONS_PATH"/panel_first_menu"
1192+
1193 static PanelService *static_service = NULL;
1194
1195 struct _PanelServicePrivate
1196 {
1197 GSList *indicators;
1198- GHashTable *entry2indicator_hash;
1199+ GHashTable *id2entry_hash;
1200 GHashTable *panel2entries_hash;
1201
1202 guint initial_sync_id;
1203@@ -69,6 +74,10 @@
1204 gint last_bottom;
1205 guint32 last_menu_button;
1206
1207+ KeyCode toggle_key;
1208+ guint32 toggle_modifiers;
1209+ guint32 key_monitor_id;
1210+
1211 IndicatorObjectEntry *pressed_entry;
1212 gboolean use_event;
1213 };
1214@@ -121,7 +130,6 @@
1215 static void sort_indicators (PanelService *self);
1216
1217 static void notify_object (IndicatorObject *object);
1218-static IndicatorObjectEntry *get_entry_at (PanelService *self, gint x, gint y);
1219
1220 static GdkFilterReturn event_filter (GdkXEvent *ev,
1221 GdkEvent *gev,
1222@@ -137,24 +145,27 @@
1223 PanelServicePrivate *priv = PANEL_SERVICE (object)->priv;
1224 gint i;
1225
1226- g_hash_table_destroy (priv->entry2indicator_hash);
1227+ g_hash_table_destroy (priv->id2entry_hash);
1228 g_hash_table_destroy (priv->panel2entries_hash);
1229
1230 gdk_window_remove_filter (NULL, (GdkFilterFunc)event_filter, object);
1231
1232- if (G_IS_OBJECT (priv->menubar))
1233+ if (GTK_IS_WIDGET (priv->menubar) &&
1234+ gtk_widget_get_realized (GTK_WIDGET (priv->menubar)))
1235 {
1236 g_object_unref (priv->menubar);
1237 priv->menubar = NULL;
1238 }
1239
1240- if (G_IS_OBJECT (priv->offscreen_window))
1241+ if (GTK_IS_WIDGET (priv->offscreen_window) &&
1242+ gtk_widget_get_realized (GTK_WIDGET (priv->offscreen_window)))
1243 {
1244 g_object_unref (priv->offscreen_window);
1245 priv->offscreen_window = NULL;
1246 }
1247
1248- if (G_IS_OBJECT (priv->last_menu))
1249+ if (GTK_IS_WIDGET (priv->last_menu) &&
1250+ gtk_widget_get_realized (GTK_WIDGET (priv->last_menu)))
1251 {
1252 g_object_unref (priv->last_menu);
1253 priv->last_menu = NULL;
1254@@ -175,15 +186,28 @@
1255 }
1256 }
1257
1258+ if (priv->key_monitor_id)
1259+ {
1260+ gconf_client_notify_remove (gconf_client_get_default(), priv->key_monitor_id);
1261+ priv->key_monitor_id = 0;
1262+ }
1263+
1264 G_OBJECT_CLASS (panel_service_parent_class)->dispose (object);
1265 }
1266
1267 static void
1268+panel_service_class_finalize (GObject *object)
1269+{
1270+ static_service = NULL;
1271+}
1272+
1273+static void
1274 panel_service_class_init (PanelServiceClass *klass)
1275 {
1276 GObjectClass *obj_class = G_OBJECT_CLASS (klass);
1277
1278 obj_class->dispose = panel_service_class_dispose;
1279+ obj_class->finalize = panel_service_class_finalize;
1280
1281 /* Signals */
1282 _service_signals[ENTRY_ACTIVATED] =
1283@@ -192,8 +216,9 @@
1284 G_SIGNAL_RUN_LAST,
1285 0,
1286 NULL, NULL,
1287- g_cclosure_marshal_VOID__STRING,
1288- G_TYPE_NONE, 1, G_TYPE_STRING);
1289+ panel_marshal_VOID__STRING_INT_INT_UINT_UINT,
1290+ G_TYPE_NONE, 5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT,
1291+ G_TYPE_UINT, G_TYPE_UINT);
1292
1293 _service_signals[RE_SYNC] =
1294 g_signal_new ("re-sync",
1295@@ -212,6 +237,7 @@
1296 NULL, NULL,
1297 g_cclosure_marshal_VOID__STRING,
1298 G_TYPE_NONE, 1, G_TYPE_STRING);
1299+
1300 _service_signals[GEOMETRIES_CHANGED] =
1301 g_signal_new ("geometries-changed",
1302 G_OBJECT_CLASS_TYPE (obj_class),
1303@@ -245,6 +271,82 @@
1304 g_type_class_add_private (obj_class, sizeof (PanelServicePrivate));
1305 }
1306
1307+static IndicatorObjectEntry *
1308+get_entry_at (PanelService *self, gint x, gint y)
1309+{
1310+ GHashTableIter panel_iter, entries_iter;
1311+ gpointer key, value, k, v;
1312+
1313+ g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash);
1314+ while (g_hash_table_iter_next (&panel_iter, &key, &value))
1315+ {
1316+ GHashTable *entry2geometry_hash = value;
1317+ g_hash_table_iter_init (&entries_iter, entry2geometry_hash);
1318+
1319+ while (g_hash_table_iter_next (&entries_iter, &k, &v))
1320+ {
1321+ IndicatorObjectEntry *entry = k;
1322+ GdkRectangle *geo = v;
1323+
1324+ if (x >= geo->x && x <= (geo->x + geo->width) &&
1325+ y >= geo->y && y <= (geo->y + geo->height))
1326+ {
1327+ return entry;
1328+ }
1329+ }
1330+ }
1331+
1332+ return NULL;
1333+}
1334+
1335+static IndicatorObject *
1336+get_entry_parent_indicator (IndicatorObjectEntry *entry)
1337+{
1338+ if (!entry || !INDICATOR_IS_OBJECT (entry->parent_object))
1339+ return NULL;
1340+
1341+ return INDICATOR_OBJECT (entry->parent_object);
1342+}
1343+
1344+static gchar *
1345+get_indicator_entry_id_by_entry (IndicatorObjectEntry *entry)
1346+{
1347+ gchar *entry_id = NULL;
1348+
1349+ if (entry)
1350+ entry_id = g_strdup_printf ("%p", entry);
1351+
1352+ return entry_id;
1353+}
1354+
1355+static IndicatorObjectEntry *
1356+get_indicator_entry_by_id (PanelService *self, const gchar *entry_id)
1357+{
1358+ IndicatorObjectEntry *entry;
1359+
1360+ entry = g_hash_table_lookup (self->priv->id2entry_hash, entry_id);
1361+
1362+ if (entry)
1363+ {
1364+ if (g_slist_find (self->priv->indicators, entry->parent_object))
1365+ {
1366+ if (!INDICATOR_IS_OBJECT (entry->parent_object))
1367+ entry = NULL;
1368+ }
1369+ else
1370+ {
1371+ entry = NULL;
1372+ }
1373+
1374+ if (!entry)
1375+ {
1376+ g_warning("The entry id '%s' you're trying to lookup is not a valid IndicatorObjectEntry!", entry_id);
1377+ }
1378+ }
1379+
1380+ return entry;
1381+}
1382+
1383 static GdkFilterReturn
1384 event_filter (GdkXEvent *ev, GdkEvent *gev, PanelService *self)
1385 {
1386@@ -269,9 +371,9 @@
1387 if (!event)
1388 return ret;
1389
1390- if (event->evtype == XI_KeyRelease)
1391+ if (event->evtype == XI_KeyPress)
1392 {
1393- if (XkbKeycodeToKeysym(event->display, event->detail, 0, 0) == GDK_KEY_F10)
1394+ if (event->mods.base == priv->toggle_modifiers && event->detail == priv->toggle_key)
1395 {
1396 if (GTK_IS_MENU (priv->last_menu))
1397 gtk_menu_popdown (GTK_MENU (priv->last_menu));
1398@@ -293,16 +395,16 @@
1399 gboolean event_is_a_click = FALSE;
1400 entry = get_entry_at (self, event->root_x, event->root_y);
1401
1402- if (XIMaskIsSet (event->buttons.mask, 1) || XIMaskIsSet (event->buttons.mask, 3))
1403+ if (event->detail == 1 || event->detail == 3)
1404 {
1405 /* Consider only right and left clicks over the indicators entries */
1406 event_is_a_click = TRUE;
1407 }
1408- else if (XIMaskIsSet (event->buttons.mask, 2) && entry)
1409+ else if (entry && event->detail == 2)
1410 {
1411 /* Middle clicks over an appmenu entry are considered just like
1412 * all other clicks */
1413- IndicatorObject *obj = g_hash_table_lookup (priv->entry2indicator_hash, entry);
1414+ IndicatorObject *obj = get_entry_parent_indicator (entry);
1415
1416 if (g_strcmp0 (g_object_get_data (G_OBJECT (obj), "id"), "libappmenu.so") == 0)
1417 {
1418@@ -330,25 +432,23 @@
1419 /* If we were navigating over indicators using the keyboard
1420 * and now we click over the indicator under the mouse, we
1421 * must force it to show back again, not make it close */
1422- gchar *entry_id = g_strdup_printf ("%p", entry);
1423+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
1424 g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id);
1425 g_free (entry_id);
1426 }
1427 }
1428 }
1429 }
1430- else if ((XIMaskIsSet (event->buttons.mask, 2) ||
1431- XIMaskIsSet (event->buttons.mask, 4) ||
1432- XIMaskIsSet (event->buttons.mask, 5)) && entry)
1433+ else if (entry && (event->detail == 2 || event->detail == 4 || event->detail == 5))
1434 {
1435 /* If we're scrolling or middle-clicking over an indicator
1436 * (which is not an appmenu entry) then we need to send the
1437 * event to the indicator itself, and avoid it to close */
1438- gchar *entry_id = g_strdup_printf ("%p", entry);
1439+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
1440
1441- if (XIMaskIsSet (event->buttons.mask, 4) || XIMaskIsSet (event->buttons.mask, 5))
1442+ if (event->detail == 4 || event->detail == 5)
1443 {
1444- gint32 delta = XIMaskIsSet (event->buttons.mask, 4) ? 120 : -120;
1445+ gint32 delta = (event->detail == 4) ? 120 : -120;
1446 panel_service_scroll_entry (self, entry_id, delta);
1447 }
1448 else if (entry == priv->pressed_entry)
1449@@ -365,60 +465,6 @@
1450 return ret;
1451 }
1452
1453-static IndicatorObjectEntry *
1454-get_entry_at (PanelService *self, gint x, gint y)
1455-{
1456- GHashTableIter panel_iter, entries_iter;
1457- gpointer key, value, k, v;
1458-
1459- g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash);
1460- while (g_hash_table_iter_next (&panel_iter, &key, &value))
1461- {
1462- GHashTable *entry2geometry_hash = value;
1463- g_hash_table_iter_init (&entries_iter, entry2geometry_hash);
1464-
1465- while (g_hash_table_iter_next (&entries_iter, &k, &v))
1466- {
1467- IndicatorObjectEntry *entry = k;
1468- GdkRectangle *geo = v;
1469-
1470- if (x >= geo->x && x <= (geo->x + geo->width) &&
1471- y >= geo->y && y <= (geo->y + geo->height))
1472- {
1473- return entry;
1474- }
1475- }
1476- }
1477-
1478- return NULL;
1479-}
1480-
1481-static void
1482-panel_service_get_indicator_entry_by_id (PanelService *self,
1483- const gchar *entry_id,
1484- IndicatorObjectEntry **entry,
1485- IndicatorObject **object)
1486-{
1487- IndicatorObject *indicator;
1488- IndicatorObjectEntry *probably_entry;
1489- PanelServicePrivate *priv = self->priv;
1490-
1491- /* FIXME: eeek, why do we even do this? */
1492- if (sscanf (entry_id, "%p", &probably_entry) == 1)
1493- {
1494- /* check that there really is such IndicatorObjectEntry */
1495- indicator = g_hash_table_lookup (priv->entry2indicator_hash,
1496- probably_entry);
1497- if (object) *object = indicator;
1498- if (entry) *entry = indicator != NULL ? probably_entry : NULL;
1499- }
1500- else
1501- {
1502- if (object) *object = NULL;
1503- if (entry) *entry = NULL;
1504- }
1505-}
1506-
1507 static gboolean
1508 initial_resync (PanelService *self)
1509 {
1510@@ -431,6 +477,73 @@
1511 }
1512
1513 static void
1514+panel_service_update_menu_keybinding (PanelService *self)
1515+{
1516+ GConfClient *client = gconf_client_get_default ();
1517+ gchar *binding = gconf_client_get_string (client, MENU_TOGGLE_KEYBINDING_PATH, NULL);
1518+
1519+ KeyCode keycode = 0;
1520+ KeySym keysym = NoSymbol;
1521+ guint32 modifiers = 0;
1522+
1523+ gchar *keystart = (binding) ? strrchr(binding, '>') : NULL;
1524+
1525+ if (!keystart)
1526+ keystart = binding;
1527+
1528+ while (keystart && !g_ascii_isalnum (*keystart))
1529+ keystart++;
1530+
1531+ gchar *keyend = keystart;
1532+
1533+ while (keyend && g_ascii_isalnum (*keyend))
1534+ keyend++;
1535+
1536+ if (keystart != keyend)
1537+ {
1538+ gchar *keystr = g_strndup (keystart, keyend-keystart);
1539+ keysym = XStringToKeysym (keystr);
1540+ }
1541+
1542+ if (keysym != NoSymbol)
1543+ {
1544+ Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
1545+ keycode = XKeysymToKeycode(dpy, keysym);
1546+
1547+ if (g_strrstr (binding, "<Shift>"))
1548+ {
1549+ modifiers |= GDK_SHIFT_MASK;
1550+ }
1551+ if (g_strrstr (binding, "<Control>"))
1552+ {
1553+ modifiers |= GDK_CONTROL_MASK;
1554+ }
1555+ if (g_strrstr (binding, "<Alt>") || g_strrstr (binding, "<Mod1>"))
1556+ {
1557+ modifiers |= GDK_MOD1_MASK;
1558+ }
1559+ if (g_strrstr (binding, "<Super>"))
1560+ {
1561+ modifiers |= GDK_SUPER_MASK;
1562+ }
1563+ }
1564+
1565+ self->priv->toggle_key = keycode;
1566+ self->priv->toggle_modifiers = modifiers;
1567+
1568+ g_free (binding);
1569+}
1570+
1571+void
1572+on_keybinding_changed (GConfClient* client, guint id, GConfEntry* entry, gpointer data)
1573+{
1574+ PanelService *self = data;
1575+ g_return_if_fail (PANEL_IS_SERVICE (data));
1576+
1577+ panel_service_update_menu_keybinding (self);
1578+}
1579+
1580+static void
1581 panel_service_init (PanelService *self)
1582 {
1583 PanelServicePrivate *priv;
1584@@ -442,7 +555,7 @@
1585
1586 gdk_window_add_filter (NULL, (GdkFilterFunc)event_filter, self);
1587
1588- priv->entry2indicator_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
1589+ priv->id2entry_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1590 priv->panel2entries_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
1591 g_free,
1592 (GDestroyNotify) g_hash_table_destroy);
1593@@ -452,6 +565,14 @@
1594 sort_indicators (self);
1595 suppress_signals = FALSE;
1596
1597+ panel_service_update_menu_keybinding (self);
1598+
1599+ GConfClient *client = gconf_client_get_default ();
1600+ gconf_client_add_dir (client, COMPIZ_OPTIONS_PATH, GCONF_CLIENT_PRELOAD_NONE, NULL);
1601+ priv->key_monitor_id = gconf_client_notify_add (client, MENU_TOGGLE_KEYBINDING_PATH,
1602+ on_keybinding_changed, self,
1603+ NULL, NULL);
1604+
1605 priv->initial_sync_id = g_idle_add ((GSourceFunc)initial_resync, self);
1606 }
1607
1608@@ -481,17 +602,23 @@
1609 {
1610 for (l = entries; l; l = l->next)
1611 {
1612- g_hash_table_remove (self->priv->entry2indicator_hash, l->data);
1613-
1614+ IndicatorObjectEntry *entry;
1615+ gchar *entry_id;
1616 GHashTableIter iter;
1617 gpointer key, value;
1618+
1619+ entry = l->data;
1620+ entry_id = get_indicator_entry_id_by_entry (entry);
1621+ g_hash_table_remove (self->priv->id2entry_hash, entry_id);
1622+ g_free (entry_id);
1623+
1624 g_hash_table_iter_init (&iter, self->priv->panel2entries_hash);
1625 while (g_hash_table_iter_next (&iter, &key, &value))
1626 {
1627 GHashTable *entry2geometry_hash = value;
1628
1629 if (g_hash_table_size (entry2geometry_hash) > 1)
1630- g_hash_table_remove (entry2geometry_hash, l->data);
1631+ g_hash_table_remove (entry2geometry_hash, entry);
1632 else
1633 g_hash_table_iter_remove (&iter);
1634 }
1635@@ -586,7 +713,7 @@
1636 gpointer timeout = g_object_get_data (G_OBJECT (indicator), "remove-timeout");
1637
1638 if (timeout)
1639- g_source_remove (GPOINTER_TO_UINT (timeout));
1640+ g_source_remove (GPOINTER_TO_UINT (timeout));
1641
1642 g_object_set_data (G_OBJECT (indicator), "remove", GINT_TO_POINTER (TRUE));
1643 notify_object (indicator);
1644@@ -695,13 +822,11 @@
1645 IndicatorObjectEntry *entry,
1646 PanelService *self)
1647 {
1648- PanelServicePrivate *priv;
1649-
1650 g_return_if_fail (PANEL_IS_SERVICE (self));
1651 g_return_if_fail (entry != NULL);
1652- priv = self->priv;
1653
1654- g_hash_table_insert (priv->entry2indicator_hash, entry, object);
1655+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
1656+ g_hash_table_insert (self->priv->id2entry_hash, entry_id, entry);
1657
1658 if (GTK_IS_LABEL (entry->label))
1659 {
1660@@ -748,18 +873,18 @@
1661 IndicatorObjectEntry *entry,
1662 PanelService *self)
1663 {
1664- PanelServicePrivate *priv;
1665 g_return_if_fail (PANEL_IS_SERVICE (self));
1666 g_return_if_fail (entry != NULL);
1667
1668- priv = self->priv;
1669-
1670- g_hash_table_remove (priv->entry2indicator_hash, entry);
1671- /* Don't remove here the value from priv->panel2entries_hash, this should
1672- * be done in during the sync, to avoid false positive.
1673+ /* Don't remove here the value from panel2entries_hash, this should be
1674+ * done during the geometries sync, to avoid false positive.
1675 * FIXME this in libappmenu.so to avoid to send an "entry-removed" signal
1676 * when switching the focus from a window to one of its dialog children */
1677
1678+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
1679+ g_hash_table_remove (self->priv->id2entry_hash, entry_id);
1680+ g_free (entry_id);
1681+
1682 notify_object (object);
1683 }
1684
1685@@ -786,10 +911,8 @@
1686 return;
1687 }
1688
1689- entry_id = g_strdup_printf ("%p", entry);
1690-
1691+ entry_id = get_indicator_entry_id_by_entry (entry);
1692 g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, entry_id);
1693-
1694 g_free (entry_id);
1695 }
1696
1697@@ -808,10 +931,8 @@
1698 return;
1699 }
1700
1701- entry_id = g_strdup_printf ("%p", entry);
1702-
1703+ entry_id = get_indicator_entry_id_by_entry (entry);
1704 g_signal_emit (self, _service_signals[ENTRY_SHOW_NOW_CHANGED], 0, entry_id, show_now_changed);
1705-
1706 g_free (entry_id);
1707 }
1708
1709@@ -829,7 +950,7 @@
1710 gchar *name;
1711 GList *entries, *entry;
1712
1713- indicator_object_set_environment(object, (const GStrv)indicator_environment);
1714+ indicator_object_set_environment(object, (GStrv)indicator_environment);
1715
1716 if (_name != NULL)
1717 name = g_strdup (_name);
1718@@ -1079,7 +1200,7 @@
1719 {
1720 gint prio = -1;
1721 IndicatorObjectEntry *entry = e->data;
1722- gchar *id = g_strdup_printf ("%p", entry);
1723+ gchar *id = get_indicator_entry_id_by_entry (entry);
1724
1725 if (entry->name_hint)
1726 {
1727@@ -1095,13 +1216,14 @@
1728 indicator_entry_to_variant (entry, id, indicator_id, b, prio);
1729 g_free (id);
1730 }
1731+
1732+ g_list_free (entries);
1733 }
1734 else
1735 {
1736 /* Add a null entry to indicate that there is an indicator here, it's just empty */
1737 indicator_entry_null_to_variant (indicator_id, b);
1738 }
1739- g_list_free (entries);
1740 }
1741
1742 static void
1743@@ -1147,7 +1269,7 @@
1744 priv->use_event = FALSE;
1745 priv->pressed_entry = NULL;
1746
1747- g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, "");
1748+ g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, "", 0, 0, 0, 0);
1749 }
1750
1751 /*
1752@@ -1226,15 +1348,29 @@
1753 {
1754 IndicatorObject *object;
1755 IndicatorObjectEntry *entry;
1756+ gboolean valid_entry = TRUE;
1757 PanelServicePrivate *priv = self->priv;
1758
1759- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
1760+ entry = get_indicator_entry_by_id (self, entry_id);
1761+
1762+ /* If the entry we read is not valid, maybe it has already been removed
1763+ * or unparented, so we need to make sure that the related key on the
1764+ * entry2geometry_hash is correctly removed and the value is free'd */
1765+ if (!entry)
1766+ {
1767+ IndicatorObjectEntry *invalid_entry;
1768+ if (sscanf (entry_id, "%p", &invalid_entry) == 1)
1769+ {
1770+ entry = invalid_entry;
1771+ valid_entry = FALSE;
1772+ }
1773+ }
1774
1775 if (entry)
1776 {
1777 GHashTable *entry2geometry_hash = g_hash_table_lookup (priv->panel2entries_hash, panel_id);
1778
1779- if (width < 0 || height < 0)
1780+ if (width < 0 || height < 0 || !valid_entry)
1781 {
1782 if (entry2geometry_hash)
1783 {
1784@@ -1247,63 +1383,110 @@
1785 g_hash_table_remove (priv->panel2entries_hash, panel_id);
1786 }
1787 }
1788+
1789+ /* If the entry has been removed let's make sure that its menu is closed */
1790+ if (valid_entry && GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu)
1791+ {
1792+ gtk_menu_popdown (entry->menu);
1793+ }
1794 }
1795 else
1796 {
1797 GdkRectangle *geo = NULL;
1798
1799 if (entry2geometry_hash == NULL)
1800- {
1801- //FIXME - this leaks memory but i'm not 100% on the logic,
1802- // using g_free as the keys destructor function causes all
1803- // kinds of problems
1804- entry2geometry_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1805- NULL, g_free);
1806- g_hash_table_insert (priv->panel2entries_hash, g_strdup (panel_id),
1807- entry2geometry_hash);
1808- }
1809+ {
1810+ entry2geometry_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
1811+ NULL, g_free);
1812+ g_hash_table_insert (priv->panel2entries_hash, g_strdup (panel_id),
1813+ entry2geometry_hash);
1814+ }
1815 else
1816- {
1817- geo = g_hash_table_lookup (entry2geometry_hash, entry);
1818- }
1819+ {
1820+ geo = g_hash_table_lookup (entry2geometry_hash, entry);
1821+ }
1822
1823 if (geo == NULL)
1824 {
1825- geo = g_new (GdkRectangle, 1);
1826+ geo = g_new0 (GdkRectangle, 1);
1827 g_hash_table_insert (entry2geometry_hash, entry, geo);
1828 }
1829
1830+ /* If the current entry geometry has changed, we need to move the menu
1831+ * accordingly to the change we recorded! */
1832+ if (GTK_IS_MENU (priv->last_menu) && priv->last_menu == entry->menu)
1833+ {
1834+ GtkWidget *top_widget = gtk_widget_get_toplevel (GTK_WIDGET (priv->last_menu));
1835+
1836+ if (GTK_IS_WINDOW (top_widget))
1837+ {
1838+ GtkWindow *top_win = GTK_WINDOW (top_widget);
1839+ gint old_x, old_y;
1840+
1841+ gtk_window_get_position (top_win, &old_x, &old_y);
1842+ gtk_window_move (top_win, old_x - (geo->x - x), old_y - (geo->y - y));
1843+ }
1844+ }
1845+
1846 geo->x = x;
1847 geo->y = y;
1848 geo->width = width;
1849 geo->height = height;
1850 }
1851
1852- g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
1853+ if (valid_entry)
1854+ {
1855+ object = get_entry_parent_indicator (entry);
1856+ g_signal_emit (self, _service_signals[GEOMETRIES_CHANGED], 0, object, entry, x, y, width, height);
1857+ }
1858 }
1859 }
1860
1861 static gboolean
1862-should_skip_menu (IndicatorObjectEntry *entry)
1863+panel_service_entry_is_visible (PanelService *self, IndicatorObjectEntry *entry)
1864 {
1865- gboolean label_ok = FALSE;
1866- gboolean image_ok = FALSE;
1867-
1868- g_return_val_if_fail (entry != NULL, TRUE);
1869+ GHashTableIter panel_iter;
1870+ gpointer key, value;
1871+ gboolean found_geo;
1872+
1873+ g_return_val_if_fail (PANEL_IS_SERVICE (self), FALSE);
1874+ g_return_val_if_fail (entry != NULL, FALSE);
1875+
1876+ found_geo = FALSE;
1877+ g_hash_table_iter_init (&panel_iter, self->priv->panel2entries_hash);
1878+
1879+ while (g_hash_table_iter_next (&panel_iter, &key, &value) && !found_geo)
1880+ {
1881+ GHashTable *entry2geometry_hash = value;
1882+
1883+ if (g_hash_table_lookup (entry2geometry_hash, entry))
1884+ {
1885+ found_geo = TRUE;
1886+ }
1887+ }
1888+
1889+ if (!found_geo)
1890+ return FALSE;
1891
1892 if (GTK_IS_LABEL (entry->label))
1893 {
1894- label_ok = gtk_widget_get_visible (GTK_WIDGET (entry->label))
1895- && gtk_widget_is_sensitive (GTK_WIDGET (entry->label));
1896+ if (gtk_widget_get_visible (GTK_WIDGET (entry->label)) &&
1897+ gtk_widget_is_sensitive (GTK_WIDGET (entry->label)))
1898+ {
1899+ return TRUE;
1900+ }
1901 }
1902
1903 if (GTK_IS_IMAGE (entry->image))
1904 {
1905- image_ok = gtk_widget_get_visible (GTK_WIDGET (entry->image))
1906- && gtk_widget_is_sensitive (GTK_WIDGET (entry->image));
1907+ if (gtk_widget_get_visible (GTK_WIDGET (entry->image)) &&
1908+ gtk_widget_is_sensitive (GTK_WIDGET (entry->image)))
1909+ {
1910+ return TRUE;
1911+ }
1912 }
1913
1914- return !label_ok && !image_ok;
1915+ return TRUE;
1916 }
1917
1918 static int
1919@@ -1341,7 +1524,7 @@
1920 gint prio = -1;
1921 new_entry = ll->data;
1922
1923- if (should_skip_menu (new_entry))
1924+ if (!panel_service_entry_is_visible (self, new_entry))
1925 continue;
1926
1927 if (new_entry->name_hint)
1928@@ -1388,7 +1571,7 @@
1929
1930 if (new_entry)
1931 {
1932- id = g_strdup_printf ("%p", new_entry);
1933+ id = get_indicator_entry_id_by_entry (new_entry);
1934 g_signal_emit (self, _service_signals[ENTRY_ACTIVATE_REQUEST], 0, id);
1935 g_free (id);
1936 }
1937@@ -1433,7 +1616,7 @@
1938 }
1939
1940 /* Find the next/prev indicator */
1941- object = g_hash_table_lookup (priv->entry2indicator_hash, priv->last_entry);
1942+ object = get_entry_parent_indicator(priv->last_entry);
1943 if (object == NULL)
1944 {
1945 g_warning ("Unable to find IndicatorObject for entry");
1946@@ -1450,23 +1633,25 @@
1947 gtk_widget_destroy (menu);
1948 }
1949
1950-void
1951-panel_service_show_entry (PanelService *self,
1952- const gchar *entry_id,
1953- guint32 timestamp,
1954- gint32 x,
1955- gint32 y,
1956- gint32 button)
1957+static void
1958+panel_service_show_entry_common (PanelService *self,
1959+ IndicatorObject *object,
1960+ IndicatorObjectEntry *entry,
1961+ guint32 xid,
1962+ gint32 x,
1963+ gint32 y,
1964+ guint32 button,
1965+ guint32 timestamp)
1966 {
1967- IndicatorObject *object;
1968- IndicatorObjectEntry *entry;
1969- GtkWidget *last_menu;
1970- PanelServicePrivate *priv = self->priv;
1971-
1972- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
1973-
1974+ PanelServicePrivate *priv;
1975+ GtkWidget *last_menu;
1976+
1977+ g_return_if_fail (PANEL_IS_SERVICE (self));
1978+ g_return_if_fail (INDICATOR_IS_OBJECT (object));
1979 g_return_if_fail (entry);
1980
1981+ priv = self->priv;
1982+
1983 if (priv->last_entry == entry)
1984 return;
1985
1986@@ -1489,7 +1674,14 @@
1987
1988 if (entry != NULL)
1989 {
1990- g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, entry_id);
1991+ if (xid > 0)
1992+ {
1993+ indicator_object_entry_activate_window (object, entry, xid, CurrentTime);
1994+ }
1995+ else
1996+ {
1997+ indicator_object_entry_activate (object, entry, CurrentTime);
1998+ }
1999
2000 if (GTK_IS_MENU (entry->menu))
2001 {
2002@@ -1526,29 +1718,33 @@
2003 priv->last_menu_move_id = g_signal_connect_after (priv->last_menu, "move-current",
2004 G_CALLBACK (on_active_menu_move_current), self);
2005
2006- indicator_object_entry_activate (object, entry, CurrentTime);
2007-
2008 gtk_menu_popup (priv->last_menu, NULL, NULL, positon_menu, self, 0, CurrentTime);
2009 GdkWindow *gdkwin = gtk_widget_get_window (GTK_WIDGET (priv->last_menu));
2010+
2011 if (gdkwin != NULL)
2012- {
2013- gint left=0, top=0, width=0, height=0;
2014-
2015- gdk_window_get_geometry (gdkwin, NULL, NULL, &width, &height);
2016- gdk_window_get_origin (gdkwin, &left, &top);
2017-
2018- priv->last_left = left;
2019- priv->last_right = left + width -1;
2020- priv->last_top = top;
2021- priv->last_bottom = top + height -1;
2022- }
2023+ {
2024+ gint left=0, top=0, width=0, height=0;
2025+
2026+ gdk_window_get_geometry (gdkwin, NULL, NULL, &width, &height);
2027+ gdk_window_get_origin (gdkwin, &left, &top);
2028+
2029+ gchar *entry_id = get_indicator_entry_id_by_entry (entry);
2030+ g_signal_emit (self, _service_signals[ENTRY_ACTIVATED], 0, entry_id,
2031+ left, top, width, height);
2032+ g_free (entry_id);
2033+
2034+ priv->last_left = left;
2035+ priv->last_right = left + width -1;
2036+ priv->last_top = top;
2037+ priv->last_bottom = top + height -1;
2038+ }
2039 else
2040- {
2041- priv->last_left = 0;
2042- priv->last_right = 0;
2043- priv->last_top = 0;
2044- priv->last_bottom = 0;
2045- }
2046+ {
2047+ priv->last_left = 0;
2048+ priv->last_right = 0;
2049+ priv->last_top = 0;
2050+ priv->last_bottom = 0;
2051+ }
2052 }
2053
2054 /* We popdown the old one last so we don't accidently send key focus back to the
2055@@ -1560,6 +1756,53 @@
2056 }
2057
2058 void
2059+panel_service_show_entry (PanelService *self,
2060+ const gchar *entry_id,
2061+ guint32 xid,
2062+ gint32 x,
2063+ gint32 y,
2064+ guint32 button,
2065+ guint32 timestamp)
2066+{
2067+ IndicatorObject *object;
2068+ IndicatorObjectEntry *entry;
2069+
2070+ g_return_if_fail (PANEL_IS_SERVICE (self));
2071+
2072+ entry = get_indicator_entry_by_id (self, entry_id);
2073+ object = get_entry_parent_indicator (entry);
2074+
2075+ panel_service_show_entry_common (self, object, entry, xid, x, y, button, timestamp);
2076+}
2077+
2078+void
2079+panel_service_show_app_menu (PanelService *self,
2080+ guint32 xid,
2081+ gint32 x,
2082+ gint32 y,
2083+ guint32 timestamp)
2084+{
2085+ IndicatorObject *object;
2086+ IndicatorObjectEntry *entry;
2087+ GList *entries;
2088+
2089+ g_return_if_fail (PANEL_IS_SERVICE (self));
2090+
2091+ object = panel_service_get_indicator (self, "libappmenu.so");
2092+ g_return_if_fail (INDICATOR_IS_OBJECT (object));
2093+
2094+ entries = indicator_object_get_entries (object);
2095+
2096+ if (entries)
2097+ {
2098+ entry = entries->data;
2099+ g_list_free (entries);
2100+
2101+ panel_service_show_entry_common (self, object, entry, xid, x, y, 1, timestamp);
2102+ }
2103+}
2104+
2105+void
2106 panel_service_secondary_activate_entry (PanelService *self,
2107 const gchar *entry_id,
2108 guint32 timestamp)
2109@@ -1567,9 +1810,10 @@
2110 IndicatorObject *object;
2111 IndicatorObjectEntry *entry;
2112
2113- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
2114+ entry = get_indicator_entry_by_id (self, entry_id);
2115 g_return_if_fail (entry);
2116
2117+ object = get_entry_parent_indicator (entry);
2118 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE, entry,
2119 timestamp);
2120 }
2121@@ -1582,11 +1826,12 @@
2122 IndicatorObject *object;
2123 IndicatorObjectEntry *entry;
2124
2125- panel_service_get_indicator_entry_by_id (self, entry_id, &entry, &object);
2126+ entry = get_indicator_entry_by_id (self, entry_id);
2127 g_return_if_fail (entry);
2128
2129 GdkScrollDirection direction = delta < 0 ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
2130
2131+ object = get_entry_parent_indicator (entry);
2132 g_signal_emit_by_name(object, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry,
2133 abs(delta/120), direction);
2134 }
2135
2136=== modified file 'services/panel-service.h'
2137--- services/panel-service.h 2011-09-04 12:11:22 +0000
2138+++ services/panel-service.h 2012-03-20 10:02:20 +0000
2139@@ -95,10 +95,17 @@
2140
2141 void panel_service_show_entry (PanelService *self,
2142 const gchar *entry_id,
2143- guint32 timestamp,
2144- gint32 x,
2145- gint32 y,
2146- gint32 button);
2147+ guint32 xid,
2148+ gint32 x,
2149+ gint32 y,
2150+ guint32 button,
2151+ guint32 timestamp);
2152+
2153+void panel_service_show_app_menu (PanelService *self,
2154+ guint32 xid,
2155+ gint32 x,
2156+ gint32 y,
2157+ guint32 timestamp);
2158
2159 void panel_service_secondary_activate_entry (PanelService *self,
2160 const gchar *entry_id,
2161
2162=== modified file 'tests/CMakeLists.txt'
2163--- tests/CMakeLists.txt 2012-03-13 01:59:15 +0000
2164+++ tests/CMakeLists.txt 2012-03-20 10:02:20 +0000
2165@@ -45,9 +45,18 @@
2166 # We can't have convenience libs so we need to rebuild with what we need
2167 # Please keep actual test files alphabetically at top and then files
2168 # from ../${UNITY_SRC} or ../../services in alphabetically after that
2169+find_program(GLIB_GENMARSHAL glib-genmarshal)
2170+add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/panel-marshal.c
2171+ COMMAND ${GLIB_GENMARSHAL} ARGS ../services/panel-marshal.list --body --prefix=panel_marshal > ${CMAKE_CURRENT_BINARY_DIR}/panel-marshal.c
2172+ COMMAND ${GLIB_GENMARSHAL} ARGS ../services/panel-marshal.list --header --prefix=panel_marshal > ${CMAKE_CURRENT_BINARY_DIR}/panel-marshal.h
2173+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
2174+ DEPENDS ../services/panel-marshal.list
2175+ COMMENT "Generating marshallers for panel-service")
2176+
2177 add_executable (test-unit
2178+ unit/TestMain.cpp
2179 unit/TestQuicklistMenuitems.cpp
2180- unit/TestMain.cpp
2181+ unit/TestPanelService.cpp
2182 unit/TestUBus.cpp
2183 unit/TestStaticCairoText.cpp
2184 ${UNITY_SRC}/CairoBaseWindow.cpp
2185@@ -59,6 +68,8 @@
2186 ${UNITY_SRC}/QuicklistMenuItemSeparator.cpp
2187 ${UNITY_SRC}/QuicklistView.cpp
2188 ${UNITY_SRC}/StaticCairoText.cpp
2189+ ../services/panel-service.c
2190+ ${CMAKE_CURRENT_BINARY_DIR}/panel-marshal.c
2191 ${UNITY_SRC}/ubus-server.cpp
2192 )
2193 add_dependencies (test-unit unity-core-${UNITY_API_VERSION})
2194@@ -114,6 +125,9 @@
2195 test_glib_signals_utils.h
2196 test_glib_variant.cpp
2197 ${CMAKE_CURRENT_BINARY_DIR}/test_glib_signals_utils_marshal.cpp
2198+ test_indicator.cpp
2199+ test_indicator_appmenu.cpp
2200+ test_indicator_entry.cpp
2201 test_favorite_store_gsettings.cpp
2202 test_favorite_store_private.cpp
2203 test_home_lens.cpp
2204@@ -180,7 +194,6 @@
2205 test_filter.cpp
2206 test_gdbus_proxy.cpp
2207 test_hud.cpp
2208- test_indicator_entry.cpp
2209 test_lens.cpp
2210 test_main_dbus.cpp
2211 test_model.cpp
2212
2213=== added file 'tests/test_indicator.cpp'
2214--- tests/test_indicator.cpp 1970-01-01 00:00:00 +0000
2215+++ tests/test_indicator.cpp 2012-03-20 10:02:20 +0000
2216@@ -0,0 +1,207 @@
2217+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2218+/*
2219+ * Copyright (C) 2012 Canonical Ltd
2220+ *
2221+ * This program is free software: you can redistribute it and/or modify
2222+ * it under the terms of the GNU General Public License version 3 as
2223+ * published by the Free Software Foundation.
2224+ *
2225+ * This program is distributed in the hope that it will be useful,
2226+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2227+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2228+ * GNU General Public License for more details.
2229+ *
2230+ * You should have received a copy of the GNU General Public License
2231+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2232+ *
2233+ * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
2234+ */
2235+
2236+#include <UnityCore/Indicator.h>
2237+
2238+#include <gtest/gtest.h>
2239+
2240+using namespace std;
2241+using namespace unity;
2242+using namespace indicator;
2243+
2244+namespace
2245+{
2246+
2247+TEST(TestIndicator, Construction)
2248+{
2249+ Indicator indicator("indicator-test");
2250+
2251+ EXPECT_EQ(indicator.name(), "indicator-test");
2252+ EXPECT_FALSE(indicator.IsAppmenu());
2253+ EXPECT_EQ(indicator.GetEntry("test-entry"), nullptr);
2254+ EXPECT_TRUE(indicator.GetEntries().empty());
2255+}
2256+
2257+TEST(TestIndicator, Syncing)
2258+{
2259+ Indicator::Entries sync_data;
2260+ Entry* entry;
2261+
2262+ Indicator indicator("indicator-test");
2263+
2264+ // Connecting to signals
2265+ Indicator::Entries added;
2266+ indicator.on_entry_added.connect([&added] (Entry::Ptr const& e) {
2267+ added.push_back(e);
2268+ });
2269+
2270+ std::vector<std::string> removed;
2271+ sigc::connection removed_connection = indicator.on_entry_removed.connect([&removed]
2272+ (std::string const& en_id) {
2273+ removed.push_back(en_id);
2274+ });
2275+
2276+ entry = new Entry("test-entry-1", "name-hint", "label", true, true, 0, "icon",
2277+ true, true, -1);
2278+ Entry::Ptr entry1(entry);
2279+ sync_data.push_back(entry1);
2280+
2281+ entry = new Entry("test-entry-2", "name-hint", "label", true, true, 0, "icon",
2282+ true, true, -1);
2283+ Entry::Ptr entry2(entry);
2284+ sync_data.push_back(entry2);
2285+
2286+ entry = new Entry("test-entry-3", "name-hint", "label", true, true, 0, "icon",
2287+ true, true, -1);
2288+ Entry::Ptr entry3(entry);
2289+ sync_data.push_back(entry3);
2290+
2291+ // Sync the indicator, adding 3 entries
2292+ indicator.Sync(sync_data);
2293+ EXPECT_EQ(indicator.GetEntries().size(), 3);
2294+ EXPECT_EQ(indicator.GetEntry("test-entry-2"), entry2);
2295+ EXPECT_EQ(added.size(), 3);
2296+ EXPECT_EQ(added.front()->id(), "test-entry-1");
2297+ EXPECT_EQ(added.back()->id(), "test-entry-3");
2298+ EXPECT_EQ(removed.size(), 0);
2299+
2300+ added.clear();
2301+ removed.clear();
2302+
2303+ // Sync the indicator removing an entry
2304+ sync_data.remove(entry2);
2305+ EXPECT_EQ(sync_data.size(), 2);
2306+
2307+ indicator.Sync(sync_data);
2308+ EXPECT_EQ(indicator.GetEntries().size(), 2);
2309+ EXPECT_EQ(indicator.GetEntry("test-entry-2"), nullptr);
2310+ EXPECT_EQ(added.size(), 0);
2311+ EXPECT_EQ(removed.size(), 1);
2312+ EXPECT_EQ(removed.front(), entry2->id());
2313+
2314+ // Sync the indicator removing an entry and adding a new one
2315+ entry = new Entry("test-entry-4", "name-hint", "label", true, true, 0, "icon",
2316+ true, true, -1);
2317+ Entry::Ptr entry4(entry);
2318+ sync_data.push_back(entry4);
2319+ sync_data.remove(entry3);
2320+ EXPECT_EQ(sync_data.size(), 2);
2321+
2322+ added.clear();
2323+ removed.clear();
2324+
2325+ indicator.Sync(sync_data);
2326+ EXPECT_EQ(indicator.GetEntries().size(), 2);
2327+ EXPECT_EQ(added.size(), 1);
2328+ EXPECT_EQ(added.front(), entry4);
2329+ EXPECT_EQ(removed.size(), 1);
2330+ EXPECT_EQ(removed.front(), entry3->id());
2331+
2332+ // Remove all the indicators
2333+ added.clear();
2334+ removed.clear();
2335+
2336+ sync_data.clear();
2337+ indicator.Sync(sync_data);
2338+ EXPECT_EQ(indicator.GetEntries().size(), 0);
2339+ EXPECT_EQ(added.size(), 0);
2340+ EXPECT_EQ(removed.size(), 2);
2341+
2342+ removed_connection.disconnect();
2343+}
2344+
2345+TEST(TestIndicator, ChildrenSignals)
2346+{
2347+ Indicator::Entries sync_data;
2348+ Entry* entry;
2349+
2350+ Indicator indicator("indicator-test");
2351+
2352+ entry = new Entry("test-entry-1", "name-hint", "label", true, true, 0, "icon",
2353+ true, true, -1);
2354+ Entry::Ptr entry1(entry);
2355+ sync_data.push_back(entry1);
2356+
2357+ indicator.Sync(sync_data);
2358+
2359+ std::string show_entry;
2360+ int show_x, show_y;
2361+ unsigned int show_xid, show_button, show_timestamp;
2362+
2363+ // Connecting to signals
2364+ indicator.on_show_menu.connect([&] (std::string const& eid, unsigned int xid,
2365+ int x, int y, unsigned int button,
2366+ unsigned int timestamp) {
2367+ show_entry = eid;
2368+ show_xid = xid;
2369+ show_x = x;
2370+ show_y = y;
2371+ show_button = button;
2372+ show_timestamp = timestamp;
2373+ });
2374+
2375+ entry1->ShowMenu(123456789, 50, 100, 2, 1328058770);
2376+ EXPECT_EQ(show_entry, "test-entry-1");
2377+ EXPECT_EQ(show_xid, 123456789);
2378+ EXPECT_EQ(show_x, 50);
2379+ EXPECT_EQ(show_y, 100);
2380+ EXPECT_EQ(show_button, 2);
2381+ EXPECT_EQ(show_timestamp, 1328058770);
2382+
2383+ // Check if a removed entry still emits a signal to the old indicator
2384+ show_entry = "invalid-entry";
2385+ sync_data.remove(entry1);
2386+ indicator.Sync(sync_data);
2387+
2388+ entry1->ShowMenu(123456789, 50, 100, 2, 1328058770);
2389+ EXPECT_EQ(show_entry, "invalid-entry");
2390+
2391+ // Checking secondary activate signal
2392+ entry = new Entry("test-entry-2", "name-hint", "label", true, true, 0, "icon",
2393+ true, true, -1);
2394+ sync_data.push_back(Entry::Ptr(entry));
2395+ indicator.Sync(sync_data);
2396+
2397+ std::string secondary_activated;
2398+ unsigned int secondary_timestamp;
2399+ indicator.on_secondary_activate.connect([&] (std::string const& eid,
2400+ unsigned int timestamp) {
2401+ secondary_activated = eid;
2402+ secondary_timestamp = timestamp;
2403+ });
2404+
2405+ entry->SecondaryActivate(1328060717);
2406+ EXPECT_EQ(secondary_activated, "test-entry-2");
2407+ EXPECT_EQ(secondary_timestamp, 1328060717);
2408+
2409+ // Checking scroll signal
2410+ std::string scrolled_entry;
2411+ int scrolled_delta;
2412+
2413+ indicator.on_scroll.connect([&] (std::string const& eid, int delta) {
2414+ scrolled_entry = eid;
2415+ scrolled_delta = delta;
2416+ });
2417+
2418+ entry->Scroll(-5);
2419+ EXPECT_EQ(scrolled_entry, "test-entry-2");
2420+ EXPECT_EQ(scrolled_delta, -5);
2421+}
2422+
2423+}
2424
2425=== added file 'tests/test_indicator_appmenu.cpp'
2426--- tests/test_indicator_appmenu.cpp 1970-01-01 00:00:00 +0000
2427+++ tests/test_indicator_appmenu.cpp 2012-03-20 10:02:20 +0000
2428@@ -0,0 +1,66 @@
2429+// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2430+/*
2431+ * Copyright (C) 2012 Canonical Ltd
2432+ *
2433+ * This program is free software: you can redistribute it and/or modify
2434+ * it under the terms of the GNU General Public License version 3 as
2435+ * published by the Free Software Foundation.
2436+ *
2437+ * This program is distributed in the hope that it will be useful,
2438+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2439+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2440+ * GNU General Public License for more details.
2441+ *
2442+ * You should have received a copy of the GNU General Public License
2443+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2444+ *
2445+ * Authored by: Marco Trevisan (Treviño) <3v1n0@ubuntu.com>
2446+ */
2447+
2448+#include <UnityCore/AppmenuIndicator.h>
2449+
2450+#include <gtest/gtest.h>
2451+
2452+using namespace std;
2453+using namespace unity;
2454+using namespace indicator;
2455+
2456+namespace
2457+{
2458+
2459+TEST(TestAppmenuIndicator, Construction)
2460+{
2461+ AppmenuIndicator indicator("indicator-appmenu");
2462+
2463+ EXPECT_EQ(indicator.name(), "indicator-appmenu");
2464+ EXPECT_TRUE(indicator.IsAppmenu());
2465+}
2466+
2467+TEST(TestAppmenuIndicator, ShowAppmenu)
2468+{
2469+ AppmenuIndicator indicator("indicator-appmenu");
2470+
2471+ bool signal_emitted = false;
2472+ int show_x, show_y;
2473+ unsigned int show_xid, show_timestamp;
2474+
2475+ // Connecting to signals
2476+ indicator.on_show_appmenu.connect([&] (unsigned int xid, int x, int y,
2477+ unsigned int timestamp) {
2478+ signal_emitted = true;
2479+ show_xid = xid;
2480+ show_x = x;
2481+ show_y = y;
2482+ show_timestamp = timestamp;
2483+ });
2484+
2485+ indicator.ShowAppmenu(123456789, 50, 100, 1328308554);
2486+ EXPECT_TRUE(signal_emitted);
2487+
2488+ EXPECT_EQ(show_xid, 123456789);
2489+ EXPECT_EQ(show_x, 50);
2490+ EXPECT_EQ(show_y, 100);
2491+ EXPECT_EQ(show_timestamp, 1328308554);
2492+}
2493+
2494+}
2495
2496=== modified file 'tests/test_indicator_entry.cpp'
2497--- tests/test_indicator_entry.cpp 2011-09-08 14:41:57 +0000
2498+++ tests/test_indicator_entry.cpp 2012-03-20 10:02:20 +0000
2499@@ -24,7 +24,7 @@
2500 EXPECT_TRUE(entry.image_visible());
2501 EXPECT_FALSE(entry.active());
2502 EXPECT_FALSE(entry.show_now());
2503- EXPECT_FALSE(entry.IsUnused());
2504+ EXPECT_TRUE(entry.visible());
2505 EXPECT_EQ(entry.image_type(), 1);
2506 EXPECT_EQ(entry.image_data(), "some icon");
2507 EXPECT_EQ(entry.priority(), -1);
2508@@ -77,21 +77,6 @@
2509 EXPECT_EQ(entry.priority(), 5);
2510 }
2511
2512-TEST(TestIndicatorEntry, TestUnused)
2513-{
2514-
2515- indicator::Entry entry("id", "name_hint", "label", true, true,
2516- 0, "some icon", false, true, -1);
2517-
2518- Counter counter;
2519- entry.updated.connect(sigc::mem_fun(counter, &Counter::increment));
2520-
2521- entry.MarkUnused();
2522- EXPECT_TRUE(entry.IsUnused());
2523- // Setting unused emits updated.
2524- EXPECT_EQ(counter.count, 1);
2525-}
2526-
2527 TEST(TestIndicatorEntry, TestShowNowEvents)
2528 {
2529
2530@@ -174,16 +159,18 @@
2531
2532 struct ShowMenuRecorder
2533 {
2534- void OnShowMenu(std::string const& a, int b, int c, int d, int e)
2535+ void OnShowMenu(std::string const& a, unsigned int b, int c, int d, unsigned int e, unsigned int f)
2536 {
2537 name = a;
2538- x = b;
2539- y = c;
2540- timestamp = d;
2541+ xid = b;
2542+ x = c;
2543+ y = d;
2544 button = e;
2545+ timestamp = f;
2546 }
2547 std::string name;
2548- int x, y, timestamp, button;
2549+ unsigned int xid, timestamp, button;
2550+ int x, y;
2551 };
2552
2553 TEST(TestIndicatorEntry, TestOnShowMenu)
2554@@ -195,13 +182,103 @@
2555 ShowMenuRecorder recorder;
2556 entry.on_show_menu.connect(sigc::mem_fun(recorder, &ShowMenuRecorder::OnShowMenu));
2557
2558- entry.ShowMenu(10, 20, 12345, 1);
2559+ entry.ShowMenu(10, 20, 1, 12345);
2560 EXPECT_EQ(recorder.name, "id");
2561+ EXPECT_EQ(recorder.xid, 0);
2562 EXPECT_EQ(recorder.x, 10);
2563 EXPECT_EQ(recorder.y, 20);
2564 EXPECT_EQ(recorder.timestamp, 12345);
2565 EXPECT_EQ(recorder.button, 1);
2566 }
2567
2568+TEST(TestIndicatorEntry, TestOnShowMenuXid)
2569+{
2570+
2571+ indicator::Entry entry("xid", "name_hint", "label", true, true,
2572+ 0, "some icon", false, true, -1);
2573+
2574+ ShowMenuRecorder recorder;
2575+ entry.on_show_menu.connect(sigc::mem_fun(recorder, &ShowMenuRecorder::OnShowMenu));
2576+
2577+ entry.ShowMenu(88492615, 15, 25, 2, 123456);
2578+ EXPECT_EQ(recorder.name, "xid");
2579+ EXPECT_EQ(recorder.xid, 88492615);
2580+ EXPECT_EQ(recorder.x, 15);
2581+ EXPECT_EQ(recorder.y, 25);
2582+ EXPECT_EQ(recorder.timestamp, 123456);
2583+ EXPECT_EQ(recorder.button, 2);
2584+}
2585+
2586+TEST(TestIndicatorEntry, TestVisibility)
2587+{
2588+
2589+ indicator::Entry entry("id", "name_hint", "label", true, true,
2590+ 0, "some icon", false, false, -1);
2591+
2592+ EXPECT_TRUE(entry.visible());
2593+
2594+ entry.setLabel("", true, true);
2595+ EXPECT_FALSE(entry.visible());
2596+
2597+ entry.setLabel("valid-label", true, true);
2598+ EXPECT_TRUE(entry.visible());
2599+
2600+ entry.setLabel("invalid-label", true, false);
2601+ EXPECT_FALSE(entry.visible());
2602+
2603+ entry.setImage(1, "valid-image", true, true);
2604+ EXPECT_TRUE(entry.visible());
2605+
2606+ entry.setImage(1, "", true, true);
2607+ EXPECT_FALSE(entry.visible());
2608+
2609+ entry.setImage(1, "valid-image", true, true);
2610+ EXPECT_TRUE(entry.visible());
2611+
2612+ entry.setImage(0, "invalid-image-type", true, true);
2613+ EXPECT_FALSE(entry.visible());
2614+
2615+ entry.setLabel("valid-label", true, true);
2616+ EXPECT_TRUE(entry.visible());
2617+
2618+ entry.setLabel("invalid-label", true, false);
2619+ EXPECT_FALSE(entry.visible());
2620+
2621+ entry.setImage(1, "invalid-image", true, false);
2622+ EXPECT_FALSE(entry.visible());
2623+
2624+ entry.setLabel("valid-label", true, true);
2625+ entry.setImage(1, "valid-image", true, true);
2626+ EXPECT_TRUE(entry.visible());
2627+}
2628+
2629+TEST(TestIndicatorEntry, TestGeometry)
2630+{
2631+
2632+ indicator::Entry entry("id", "name_hint", "label", true, true,
2633+ 0, "some icon", false, true, -1);
2634+
2635+ Counter counter;
2636+ entry.updated.connect(sigc::mem_fun(counter, &Counter::increment));
2637+ bool geo_changed = false;
2638+ nux::Rect new_geo;
2639+
2640+ entry.geometry_changed.connect([&] (nux::Rect const& geo) {
2641+ geo_changed = true;
2642+ new_geo = geo;
2643+ });
2644+
2645+ // Setting to the same value doesn't emit any events.
2646+ entry.set_geometry(nux::Rect());
2647+ EXPECT_EQ(entry.geometry(), nux::Rect());
2648+ EXPECT_EQ(counter.count, 0);
2649+
2650+ // Setting to a different value does emit the events.
2651+ entry.set_geometry(nux::Rect(1, 2, 3, 4));
2652+ EXPECT_EQ(entry.geometry(), nux::Rect(1, 2, 3, 4));
2653+ EXPECT_TRUE(geo_changed);
2654+ EXPECT_EQ(new_geo, nux::Rect(1, 2, 3, 4));
2655+ EXPECT_EQ(counter.count, 1);
2656+}
2657
2658 }
2659
2660=== modified file 'tests/unit/TestMain.cpp'
2661--- tests/unit/TestMain.cpp 2012-01-09 16:49:50 +0000
2662+++ tests/unit/TestMain.cpp 2012-03-20 10:02:20 +0000
2663@@ -25,9 +25,9 @@
2664 #include "Nux/Nux.h"
2665 #include "Nux/WindowThread.h"
2666
2667-//void TestPanelServiceCreateSuite (void);
2668-void TestUBusCreateSuite(void);
2669-void TestQuicklistMenuitemsCreateSuite(void);
2670+void TestPanelServiceCreateSuite();
2671+void TestUBusCreateSuite();
2672+void TestQuicklistMenuitemsCreateSuite();
2673 void TestStaticCairoTextCreateSuite();
2674
2675 nux::WindowThread*
2676@@ -70,7 +70,7 @@
2677 g_test_init(&argc, &argv, NULL);
2678
2679 //Keep alphabetical please
2680- //TestPanelServiceCreateSuite ();
2681+ TestPanelServiceCreateSuite();
2682 TestQuicklistMenuitemsCreateSuite();
2683 TestStaticCairoTextCreateSuite();
2684 TestUBusCreateSuite();
2685
2686=== modified file 'tests/unit/TestPanelService.cpp'
2687--- tests/unit/TestPanelService.cpp 2011-08-09 12:21:18 +0000
2688+++ tests/unit/TestPanelService.cpp 2012-03-20 10:02:20 +0000
2689@@ -177,6 +177,7 @@
2690 GVariantIter* iter;
2691 gchar* indicator_id;
2692 gchar* entry_id;
2693+ gchar* entry_name_hint;
2694 gchar* label;
2695 gboolean label_sensitive;
2696 gboolean label_visible;
2697@@ -185,10 +186,11 @@
2698 gboolean image_sensitive;
2699 gboolean image_visible;
2700
2701- g_variant_get(result, "(a(sssbbusbb))", &iter);
2702- while (g_variant_iter_loop(iter, "(sssbbusbb)",
2703+ g_variant_get(result, "(a(ssssbbusbbi))", &iter);
2704+ while (g_variant_iter_loop(iter, "(ssssbbusbbi)",
2705 &indicator_id,
2706 &entry_id,
2707+ &entry_name_hint,
2708 &label,
2709 &label_sensitive,
2710 &label_visible,
2711@@ -215,8 +217,8 @@
2712 guint ret = 0;
2713 GVariantIter* iter;
2714 gchar* indicator_id;
2715- gchar* name_hint;
2716 gchar* entry_id;
2717+ gchar* entry_name_hint;
2718 gchar* label;
2719 gboolean label_sensitive;
2720 gboolean label_visible;
2721@@ -229,8 +231,8 @@
2722 g_variant_get(result, "(a(ssssbbusbbi))", &iter);
2723 while (g_variant_iter_loop(iter, "(ssssbbusbbi)",
2724 &indicator_id,
2725- &name_hint,
2726 &entry_id,
2727+ &entry_name_hint,
2728 &label,
2729 &label_sensitive,
2730 &label_visible,