Merge lp:~unity-api-team/hud/forward-port into lp:hud/14.10

Proposed by Pete Woods
Status: Merged
Approved by: Pete Woods
Approved revision: 395
Merged at revision: 394
Proposed branch: lp:~unity-api-team/hud/forward-port
Merge into: lp:hud/14.10
Diff against target: 1455 lines (+515/-209)
14 files modified
data/hud.conf.in (+0/-12)
debian/changelog (+34/-0)
libqtgmenu/QtGMenuImporter.cpp (+2/-2)
libqtgmenu/QtGMenuImporter.h (+2/-2)
libqtgmenu/internal/QtGActionGroup.cpp (+31/-31)
libqtgmenu/internal/QtGActionGroup.h (+7/-3)
libqtgmenu/internal/QtGMenuImporterPrivate.cpp (+21/-13)
libqtgmenu/internal/QtGMenuImporterPrivate.h (+4/-3)
libqtgmenu/internal/QtGMenuModel.cpp (+331/-99)
libqtgmenu/internal/QtGMenuModel.h (+20/-17)
service/DBusMenuCollector.cpp (+9/-17)
service/HudServiceImpl.cpp (+45/-5)
service/HudServiceImpl.h (+5/-1)
tests/unit/qtgmenu/TestQtGMenu.cpp (+4/-4)
To merge this branch: bzr merge lp:~unity-api-team/hud/forward-port
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Indicator Applet Developers Pending
Review via email: mp+223025@code.launchpad.net

Commit message

Forward port trusty SRU

Description of the change

Forward port trusty SRU

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Antti Kaijanmäki (kaijanmaki) wrote :

* Are any changes against your component pending/needed to land the MP under review in a functional state and are those called out explicitly by the submitter?

Only affects HUD.

 * Did you do exploratory testing related to the component you own with the MP changeset included?

Ran the whole TestPlan. Note, HUD is disabled on the phone, so only could do simple smoke test. Desktop testing completed fully.

 * Has the submitter requested review by all the relevant teams/reviewres?
Yes.

 * If you are the reviewer owning the component the MP is against, have you checked that submitter has accurately filled out the submitter checklist and has taken no shortcut?

This is a forward port of an already accepted SRU.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'data/hud.conf.in'
--- data/hud.conf.in 2013-11-27 14:28:43 +0000
+++ data/hud.conf.in 2014-06-13 07:41:53 +0000
@@ -1,22 +1,10 @@
1description "Unity HUD"1description "Unity HUD"
22
3pre-start script
4 if [ -z $DBUS_SESSION_BUS_ADDRESS ]; then
5 echo "Working around missing DBUS_SESSION_BUS_ADDRESS variable"
6 printf "DuplicateSignature\0DBusSessionAddressNotSet" | /usr/share/apport/recoverable_problem -p `pidof -s -o 1 init`
7 . "${HOME}/.cache/upstart/dbus-session"
8 initctl set-env "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
9 fi
10end script
11
12# Currently started by the dbus service file3# Currently started by the dbus service file
13# start on dbus-activation com.canonical.hud4# start on dbus-activation com.canonical.hud
14stop on desktop-end5stop on desktop-end
15start on started dbus and ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger) or (xsession SESSION=ubuntu))6start on started dbus and ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger) or (xsession SESSION=ubuntu))
167
17env HUD_SERVICE_TIMEOUT=0
18export HUD_SERVICE_TIMEOUT
19
20pre-start script8pre-start script
21 if [ -z $DBUS_SESSION_BUS_ADDRESS ]; then9 if [ -z $DBUS_SESSION_BUS_ADDRESS ]; then
22 echo "Working around missing DBUS_SESSION_BUS_ADDRESS variable"10 echo "Working around missing DBUS_SESSION_BUS_ADDRESS variable"
2311
=== modified file 'debian/changelog'
--- debian/changelog 2014-06-09 14:06:31 +0000
+++ debian/changelog 2014-06-13 07:41:53 +0000
@@ -1,3 +1,21 @@
1hud (14.10-0ubuntu1) UNRELEASED; urgency=medium
2
3 [ Pete Woods ]
4 * Forward port changes from trusty.
5
6 -- Pete Woods <pete.woods@canonical.com> Fri, 13 Jun 2014 08:22:34 +0100
7
8hud (14.04+14.04.20140604-0ubuntu1) trusty; urgency=medium
9
10 [ Pete Woods ]
11 * Resolve crasher in previous attempted SRU. (LP: #1298656)
12 - Fix order of menu traversal.
13 - Add timeout to legacy HUD queries.
14 - Improve legacy menu safety valve trigger.
15 - Remove duplicate entries in upstart job.
16
17 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 04 Jun 2014 14:04:12 +0000
18
1hud (13.10.1+14.10.20140609-0ubuntu1) utopic; urgency=low19hud (13.10.1+14.10.20140609-0ubuntu1) utopic; urgency=low
220
3 [ Ubuntu daily release ]21 [ Ubuntu daily release ]
@@ -8,6 +26,22 @@
826
9 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 09 Jun 2014 14:06:31 +000027 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 09 Jun 2014 14:06:31 +0000
1028
29hud (13.10.1+14.04.20140425-0ubuntu1) trusty; urgency=low
30
31 [ Pete Woods ]
32 * Harden HUD against misbehaving applications Report the offending
33 applications using Apport's recoverable problem tool. Switch to
34 using shared pointers where possible for managing memory. (LP:
35 #1298656)
36
37 [ Marcus Tomlinson ]
38 * Harden HUD against misbehaving applications Report the offending
39 applications using Apport's recoverable problem tool. Switch to
40 using shared pointers where possible for managing memory. (LP:
41 #1298656)
42
43 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 25 Apr 2014 08:48:46 +0000
44
11hud (13.10.1+14.04.20140402-0ubuntu2) utopic; urgency=medium45hud (13.10.1+14.04.20140402-0ubuntu2) utopic; urgency=medium
1246
13 * No-change rebuild for shlib changes in qtbase and qtdeclarative.47 * No-change rebuild for shlib changes in qtbase and qtdeclarative.
1448
=== modified file 'libqtgmenu/QtGMenuImporter.cpp'
--- libqtgmenu/QtGMenuImporter.cpp 2014-03-11 05:04:08 +0000
+++ libqtgmenu/QtGMenuImporter.cpp 2014-06-13 07:41:53 +0000
@@ -43,12 +43,12 @@
43{43{
44}44}
4545
46GMenuModel* QtGMenuImporter::GetGMenuModel() const46QSharedPointer<GMenuModel> QtGMenuImporter::GetGMenuModel() const
47{47{
48 return d->GetGMenuModel();48 return d->GetGMenuModel();
49}49}
5050
51GActionGroup* QtGMenuImporter::GetGActionGroup( int index ) const51QSharedPointer<GActionGroup> QtGMenuImporter::GetGActionGroup( int index ) const
52{52{
53 return d->GetGActionGroup( index );53 return d->GetGActionGroup( index );
54}54}
5555
=== modified file 'libqtgmenu/QtGMenuImporter.h'
--- libqtgmenu/QtGMenuImporter.h 2014-03-12 09:04:14 +0000
+++ libqtgmenu/QtGMenuImporter.h 2014-06-13 07:41:53 +0000
@@ -47,8 +47,8 @@
47 const QMap<QString, QDBusObjectPath>& action_paths, QObject* parent = 0 );47 const QMap<QString, QDBusObjectPath>& action_paths, QObject* parent = 0 );
48 virtual ~QtGMenuImporter();48 virtual ~QtGMenuImporter();
4949
50 GMenuModel* GetGMenuModel() const;50 QSharedPointer<GMenuModel> GetGMenuModel() const;
51 GActionGroup* GetGActionGroup( int index = 0 ) const;51 QSharedPointer<GActionGroup> GetGActionGroup( int index = 0 ) const;
5252
53 std::shared_ptr< QMenu > GetQMenu() const;53 std::shared_ptr< QMenu > GetQMenu() const;
5454
5555
=== modified file 'libqtgmenu/internal/QtGActionGroup.cpp'
--- libqtgmenu/internal/QtGActionGroup.cpp 2014-03-19 23:40:09 +0000
+++ libqtgmenu/internal/QtGActionGroup.cpp 2014-06-13 07:41:53 +0000
@@ -21,13 +21,20 @@
2121
22using namespace qtgmenu;22using namespace qtgmenu;
2323
24QtGActionGroup::QtGActionGroup( const QString& action_prefix, GActionGroup* action_group )24QtGActionGroup::QtGActionGroup( QSharedPointer<GDBusConnection> connection,
25 const QString& action_prefix,
26 const QString& service,
27 const QString& path )
25 : m_action_prefix( action_prefix ),28 : m_action_prefix( action_prefix ),
26 m_action_group( action_group )29 m_action_group(
30 G_ACTION_GROUP(
31 g_dbus_action_group_get(connection.data(),
32 service.toUtf8().constData(),
33 path.toUtf8().constData())), &g_object_unref)
27{34{
28 ConnectCallbacks();35 ConnectCallbacks();
2936
30 auto actions_list = g_action_group_list_actions( m_action_group );37 auto actions_list = g_action_group_list_actions( m_action_group.data() );
31 if( actions_list )38 if( actions_list )
32 {39 {
33 for( int i = 0; actions_list[i]; ++i )40 for( int i = 0; actions_list[i]; ++i )
@@ -46,7 +53,7 @@
46 return;53 return;
47 }54 }
4855
49 auto actions_list = g_action_group_list_actions( m_action_group );56 auto actions_list = g_action_group_list_actions( m_action_group.data() );
50 if( actions_list )57 if( actions_list )
51 {58 {
52 for( int i = 0; actions_list[i]; ++i )59 for( int i = 0; actions_list[i]; ++i )
@@ -58,14 +65,9 @@
58 }65 }
5966
60 DisconnectCallbacks();67 DisconnectCallbacks();
61
62 g_object_unref( m_action_group );
63 m_action_group = nullptr;
64
65 return;
66}68}
6769
68GActionGroup* QtGActionGroup::ActionGroup() const70QSharedPointer<GActionGroup> QtGActionGroup::ActionGroup() const
69{71{
70 return m_action_group;72 return m_action_group;
71}73}
@@ -83,12 +85,12 @@
83 const QString& action(split.second);85 const QString& action(split.second);
84 QByteArray action_utf = action.toUtf8();86 QByteArray action_utf = action.toUtf8();
8587
86 const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group,88 const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group.data(),
87 action_utf.constData() );89 action_utf.constData() );
8890
89 if( type == nullptr )91 if( type == nullptr )
90 {92 {
91 g_action_group_activate_action( m_action_group, action_utf.constData(), nullptr );93 g_action_group_activate_action( m_action_group.data(), action_utf.constData(), nullptr );
92 }94 }
93 else95 else
94 {96 {
@@ -96,7 +98,7 @@
96 if( g_variant_type_equal( type, G_VARIANT_TYPE_STRING ) )98 if( g_variant_type_equal( type, G_VARIANT_TYPE_STRING ) )
97 {99 {
98 GVariant* param = g_variant_new_string( action_utf.constData() );100 GVariant* param = g_variant_new_string( action_utf.constData() );
99 g_action_group_activate_action( m_action_group, action_utf.constData(), param );101 g_action_group_activate_action( m_action_group.data(), action_utf.constData(), param );
100 g_variant_unref( param );102 g_variant_unref( param );
101 }103 }
102 }104 }
@@ -116,21 +118,19 @@
116118
117void QtGActionGroup::EmitStates()119void QtGActionGroup::EmitStates()
118{120{
119 auto actions_list = g_action_group_list_actions( m_action_group );121 auto actions_list = g_action_group_list_actions( m_action_group.data() );
120122
121 for( int i = 0; actions_list && actions_list[i]; ++i )123 for( int i = 0; actions_list && actions_list[i]; ++i )
122 {124 {
123 gchar* action_name = actions_list[i];125 gchar* action_name = actions_list[i];
124126
125 bool enabled = G_ACTION_GROUP_GET_IFACE( m_action_group ) ->get_action_enabled( m_action_group,127 bool enabled = G_ACTION_GROUP_GET_IFACE( m_action_group.data() ) ->get_action_enabled( m_action_group.data(),
126 action_name );128 action_name );
127 if( !enabled )129 emit ActionEnabled( FullName(m_action_prefix, action_name), enabled );
128 emit ActionEnabled( FullName(m_action_prefix, action_name), enabled );
129130
130 const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group,131 const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group.data(),
131 action_name );132 action_name );
132 if( type != nullptr )133 emit ActionParameterized( FullName(m_action_prefix, action_name), type != nullptr );
133 emit ActionParameterized( FullName(m_action_prefix, action_name), type != nullptr );
134 }134 }
135135
136 g_strfreev( actions_list );136 g_strfreev( actions_list );
@@ -142,12 +142,12 @@
142 QtGActionGroup* self = reinterpret_cast< QtGActionGroup* >( user_data );142 QtGActionGroup* self = reinterpret_cast< QtGActionGroup* >( user_data );
143 emit self->ActionAdded( action_name );143 emit self->ActionAdded( action_name );
144144
145 bool enabled = G_ACTION_GROUP_GET_IFACE( self->m_action_group ) ->get_action_enabled(145 bool enabled = G_ACTION_GROUP_GET_IFACE( self->m_action_group.data() ) ->get_action_enabled(
146 self->m_action_group, action_name );146 self->m_action_group.data(), action_name );
147 if( !enabled )147 if( !enabled )
148 emit self->ActionEnabled( FullName(self->m_action_prefix, action_name), enabled );148 emit self->ActionEnabled( FullName(self->m_action_prefix, action_name), enabled );
149149
150 const GVariantType* type = g_action_group_get_action_parameter_type( self->m_action_group,150 const GVariantType* type = g_action_group_get_action_parameter_type( self->m_action_group.data(),
151 action_name );151 action_name );
152 if( type != nullptr )152 if( type != nullptr )
153 emit self->ActionParameterized( FullName(self->m_action_prefix, action_name), type != nullptr );153 emit self->ActionParameterized( FullName(self->m_action_prefix, action_name), type != nullptr );
@@ -178,22 +178,22 @@
178{178{
179 if( m_action_group && m_action_added_handler == 0 )179 if( m_action_group && m_action_added_handler == 0 )
180 {180 {
181 m_action_added_handler = g_signal_connect( m_action_group, "action-added",181 m_action_added_handler = g_signal_connect( m_action_group.data(), "action-added",
182 G_CALLBACK( ActionAddedCallback ), this );182 G_CALLBACK( ActionAddedCallback ), this );
183 }183 }
184 if( m_action_group && m_action_removed_handler == 0 )184 if( m_action_group && m_action_removed_handler == 0 )
185 {185 {
186 m_action_removed_handler = g_signal_connect( m_action_group, "action-removed",186 m_action_removed_handler = g_signal_connect( m_action_group.data(), "action-removed",
187 G_CALLBACK( ActionRemovedCallback ), this );187 G_CALLBACK( ActionRemovedCallback ), this );
188 }188 }
189 if( m_action_group && m_action_enabled_handler == 0 )189 if( m_action_group && m_action_enabled_handler == 0 )
190 {190 {
191 m_action_enabled_handler = g_signal_connect( m_action_group, "action-enabled-changed",191 m_action_enabled_handler = g_signal_connect( m_action_group.data(), "action-enabled-changed",
192 G_CALLBACK( ActionEnabledCallback ), this );192 G_CALLBACK( ActionEnabledCallback ), this );
193 }193 }
194 if( m_action_group && m_action_state_changed_handler == 0 )194 if( m_action_group && m_action_state_changed_handler == 0 )
195 {195 {
196 m_action_state_changed_handler = g_signal_connect( m_action_group, "action-state-changed",196 m_action_state_changed_handler = g_signal_connect( m_action_group.data(), "action-state-changed",
197 G_CALLBACK( ActionStateChangedCallback ), this );197 G_CALLBACK( ActionStateChangedCallback ), this );
198 }198 }
199}199}
@@ -202,19 +202,19 @@
202{202{
203 if( m_action_group && m_action_added_handler != 0 )203 if( m_action_group && m_action_added_handler != 0 )
204 {204 {
205 g_signal_handler_disconnect( m_action_group, m_action_added_handler );205 g_signal_handler_disconnect( m_action_group.data(), m_action_added_handler );
206 }206 }
207 if( m_action_group && m_action_removed_handler != 0 )207 if( m_action_group && m_action_removed_handler != 0 )
208 {208 {
209 g_signal_handler_disconnect( m_action_group, m_action_removed_handler );209 g_signal_handler_disconnect( m_action_group.data(), m_action_removed_handler );
210 }210 }
211 if( m_action_group && m_action_enabled_handler != 0 )211 if( m_action_group && m_action_enabled_handler != 0 )
212 {212 {
213 g_signal_handler_disconnect( m_action_group, m_action_enabled_handler );213 g_signal_handler_disconnect( m_action_group.data(), m_action_enabled_handler );
214 }214 }
215 if( m_action_group && m_action_state_changed_handler != 0 )215 if( m_action_group && m_action_state_changed_handler != 0 )
216 {216 {
217 g_signal_handler_disconnect( m_action_group, m_action_state_changed_handler );217 g_signal_handler_disconnect( m_action_group.data(), m_action_state_changed_handler );
218 }218 }
219219
220 m_action_added_handler = 0;220 m_action_added_handler = 0;
221221
=== modified file 'libqtgmenu/internal/QtGActionGroup.h'
--- libqtgmenu/internal/QtGActionGroup.h 2014-03-19 23:40:09 +0000
+++ libqtgmenu/internal/QtGActionGroup.h 2014-06-13 07:41:53 +0000
@@ -20,6 +20,7 @@
20#define QTGACTIONGROUP_H20#define QTGACTIONGROUP_H
2121
22#include <QObject>22#include <QObject>
23#include <QSharedPointer>
23#include <QVariant>24#include <QVariant>
2425
25#undef signals26#undef signals
@@ -33,10 +34,13 @@
33Q_OBJECT34Q_OBJECT
3435
35public:36public:
36 QtGActionGroup( const QString& action_prefix, GActionGroup* action_group );37 QtGActionGroup(QSharedPointer<GDBusConnection> connection,
38 const QString& action_prefix,
39 const QString& service,
40 const QString& path);
37 virtual ~QtGActionGroup();41 virtual ~QtGActionGroup();
3842
39 GActionGroup* ActionGroup() const;43 QSharedPointer<GActionGroup> ActionGroup() const;
4044
41Q_SIGNALS:45Q_SIGNALS:
42 void ActionAdded( QString action_name );46 void ActionAdded( QString action_name );
@@ -66,7 +70,7 @@
66private:70private:
6771
68 QString m_action_prefix;72 QString m_action_prefix;
69 GActionGroup* m_action_group = nullptr;73 QSharedPointer<GActionGroup> m_action_group;
7074
71 gulong m_action_added_handler = 0;75 gulong m_action_added_handler = 0;
72 gulong m_action_removed_handler = 0;76 gulong m_action_removed_handler = 0;
7377
=== modified file 'libqtgmenu/internal/QtGMenuImporterPrivate.cpp'
--- libqtgmenu/internal/QtGMenuImporterPrivate.cpp 2014-03-19 23:26:48 +0000
+++ libqtgmenu/internal/QtGMenuImporterPrivate.cpp 2014-06-13 07:41:53 +0000
@@ -30,7 +30,7 @@
30 m_service_watcher( service, QDBusConnection::sessionBus(),30 m_service_watcher( service, QDBusConnection::sessionBus(),
31 QDBusServiceWatcher::WatchForOwnerChange ),31 QDBusServiceWatcher::WatchForOwnerChange ),
32 m_parent( parent ),32 m_parent( parent ),
33 m_connection( g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ) ),33 m_connection( g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ), &g_object_unref ),
34 m_service( service ),34 m_service( service ),
35 m_menu_path( menu_path ),35 m_menu_path( menu_path ),
36 m_action_paths( action_paths )36 m_action_paths( action_paths )
@@ -48,26 +48,24 @@
48{48{
49 ClearMenuModel();49 ClearMenuModel();
50 ClearActionGroups();50 ClearActionGroups();
51
52 g_object_unref( m_connection );
53}51}
5452
55GMenuModel* QtGMenuImporterPrivate::GetGMenuModel()53QSharedPointer<GMenuModel> QtGMenuImporterPrivate::GetGMenuModel()
56{54{
57 if( m_menu_model == nullptr )55 if( m_menu_model == nullptr )
58 {56 {
59 return nullptr;57 return QSharedPointer<GMenuModel>();
60 }58 }
6159
62 return m_menu_model->Model();60 return m_menu_model->Model();
63}61}
6462
65GActionGroup* QtGMenuImporterPrivate::GetGActionGroup( int index )63QSharedPointer<GActionGroup> QtGMenuImporterPrivate::GetGActionGroup( int index )
66{64{
67 if( index >= m_action_groups.size() ||65 if( index >= m_action_groups.size() ||
68 m_action_groups[index] == nullptr )66 m_action_groups[index] == nullptr )
69 {67 {
70 return nullptr;68 return QSharedPointer<GActionGroup>();
71 }69 }
7270
73 return m_action_groups[index]->ActionGroup();71 return m_action_groups[index]->ActionGroup();
@@ -160,13 +158,12 @@
160 ClearMenuModel();158 ClearMenuModel();
161159
162 QString menu_path = m_menu_path.path();160 QString menu_path = m_menu_path.path();
163 m_menu_model =161 m_menu_model = std::make_shared< QtGMenuModel > ( m_connection, m_service, menu_path, m_action_paths );
164 std::make_shared< QtGMenuModel > (
165 G_MENU_MODEL( g_dbus_menu_model_get( m_connection, m_service.toUtf8().constData(), menu_path.toUtf8().constData() ) ),
166 m_service, menu_path, m_action_paths );
167162
168 connect( m_menu_model.get(), SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int,163 connect( m_menu_model.get(), SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int,
169 int ) ), &m_parent, SIGNAL( MenuItemsChanged()) );164 int ) ), &m_parent, SIGNAL( MenuItemsChanged()) );
165
166 connect( m_menu_model.get(), SIGNAL( MenuInvalid() ), this, SLOT( MenuInvalid() ) );
170}167}
171168
172void QtGMenuImporterPrivate::RefreshGActionGroup()169void QtGMenuImporterPrivate::RefreshGActionGroup()
@@ -181,8 +178,8 @@
181178
182 QString action_path = action_path_it.value().path();179 QString action_path = action_path_it.value().path();
183 m_action_groups.push_back(180 m_action_groups.push_back(
184 std::make_shared< QtGActionGroup > ( action_path_it.key(),181 std::make_shared<QtGActionGroup>(m_connection,
185 G_ACTION_GROUP( g_dbus_action_group_get( m_connection, m_service.toUtf8().constData(), action_path.toUtf8().constData() ) ) ) );182 action_path_it.key(), m_service, action_path));
186183
187 auto action_group = m_action_groups.back();184 auto action_group = m_action_groups.back();
188185
@@ -198,3 +195,14 @@
198195
199 LinkMenuActions();196 LinkMenuActions();
200}197}
198
199void QtGMenuImporterPrivate::MenuInvalid()
200{
201 disconnect( &m_service_watcher, SIGNAL( serviceRegistered( const QString& ) ), this,
202 SLOT( ServiceRegistered() ) );
203
204 disconnect( &m_service_watcher, SIGNAL( serviceUnregistered( const QString& ) ), this,
205 SLOT( ServiceUnregistered() ) );
206
207 ServiceUnregistered();
208}
201209
=== modified file 'libqtgmenu/internal/QtGMenuImporterPrivate.h'
--- libqtgmenu/internal/QtGMenuImporterPrivate.h 2014-03-19 23:26:48 +0000
+++ libqtgmenu/internal/QtGMenuImporterPrivate.h 2014-06-13 07:41:53 +0000
@@ -43,8 +43,8 @@
43 const QMap<QString, QDBusObjectPath>& action_paths, QtGMenuImporter& parent );43 const QMap<QString, QDBusObjectPath>& action_paths, QtGMenuImporter& parent );
44 virtual ~QtGMenuImporterPrivate();44 virtual ~QtGMenuImporterPrivate();
4545
46 GMenuModel* GetGMenuModel();46 QSharedPointer<GMenuModel> GetGMenuModel();
47 GActionGroup* GetGActionGroup( int index = 0);47 QSharedPointer<GActionGroup> GetGActionGroup( int index = 0);
4848
49 std::shared_ptr< QMenu > GetQMenu();49 std::shared_ptr< QMenu > GetQMenu();
5050
@@ -62,13 +62,14 @@
6262
63 void RefreshGMenuModel();63 void RefreshGMenuModel();
64 void RefreshGActionGroup();64 void RefreshGActionGroup();
65 void MenuInvalid();
6566
66private:67private:
67 QDBusServiceWatcher m_service_watcher;68 QDBusServiceWatcher m_service_watcher;
6869
69 QtGMenuImporter& m_parent;70 QtGMenuImporter& m_parent;
7071
71 GDBusConnection* m_connection;72 QSharedPointer<GDBusConnection> m_connection;
72 QString m_service;73 QString m_service;
73 QDBusObjectPath m_menu_path;74 QDBusObjectPath m_menu_path;
74 QMap<QString, QDBusObjectPath> m_action_paths;75 QMap<QString, QDBusObjectPath> m_action_paths;
7576
=== modified file 'libqtgmenu/internal/QtGMenuModel.cpp'
--- libqtgmenu/internal/QtGMenuModel.cpp 2014-03-19 23:26:48 +0000
+++ libqtgmenu/internal/QtGMenuModel.cpp 2014-06-13 07:41:53 +0000
@@ -18,44 +18,52 @@
1818
19#include <QtGMenuModel.h>19#include <QtGMenuModel.h>
20#include <QtGMenuUtils.h>20#include <QtGMenuUtils.h>
21#include <QCoreApplication>
22#include <QDBusConnection>
23#include <QDBusConnectionInterface>
21#include <QDebug>24#include <QDebug>
25#include <QProcess>
26#include <QRegularExpression>
2227
23using namespace qtgmenu;28using namespace qtgmenu;
2429
25QtGMenuModel::QtGMenuModel( GMenuModel* model )30static const QRegularExpression SINGLE_UNDERSCORE("(?<![_])[_](?![_])");
26 : QtGMenuModel( model, LinkType::Root, nullptr, 0 )
27{
28}
2931
30QtGMenuModel::QtGMenuModel( GMenuModel* model, const QString& bus_name, const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths )32QtGMenuModel::QtGMenuModel( QSharedPointer<GDBusConnection> connection, const QString& bus_name,
31 : QtGMenuModel( model, LinkType::Root, nullptr, 0 )33 const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths )
34 : QtGMenuModel( QSharedPointer<GMenuModel>(G_MENU_MODEL( g_dbus_menu_model_get( connection.data(),
35 bus_name.toUtf8().constData(),
36 menu_path.toUtf8().constData() ) ), &g_object_unref ),
37 LinkType::Root, nullptr, 0 )
32{38{
39 m_connection = connection;
33 m_bus_name = bus_name;40 m_bus_name = bus_name;
34 m_menu_path = menu_path;41 m_menu_path = menu_path;
35 m_action_paths = action_paths;42 m_action_paths = action_paths;
36}43}
3744
38QtGMenuModel::QtGMenuModel( GMenuModel* model, LinkType link_type, QtGMenuModel* parent, int index )45QtGMenuModel::QtGMenuModel( QSharedPointer<GMenuModel> model, LinkType link_type, QtGMenuModel* parent, int index )
39 : m_parent( parent ),46 : m_parent( parent ),
40 m_model( model ),47 m_model( model ),
41 m_link_type( link_type )48 m_link_type( link_type ),
49 m_menu( new QMenu() ),
50 m_ext_menu( new QMenu() )
42{51{
43 ConnectCallback();52 ConnectCallback();
4453
45 if( m_parent )54 if( m_parent )
46 {55 {
47 m_parent->InsertChild( this, index );56 m_connection = m_parent->m_connection;
48
49 m_bus_name = m_parent->m_bus_name;57 m_bus_name = m_parent->m_bus_name;
50 m_menu_path = m_parent->m_menu_path;58 m_menu_path = m_parent->m_menu_path;
51 m_action_paths = m_parent->m_action_paths;59 m_action_paths = m_parent->m_action_paths;
5260
53 gchar* label = NULL;61 gchar* label = NULL;
54 if( g_menu_model_get_item_attribute( m_parent->m_model, index,62 if( g_menu_model_get_item_attribute( m_parent->m_model.data(), index,
55 G_MENU_ATTRIBUTE_LABEL, "s", &label ) )63 G_MENU_ATTRIBUTE_LABEL, "s", &label ) )
56 {64 {
57 QString qlabel = QString::fromUtf8( label );65 QString qlabel = QString::fromUtf8( label );
58 qlabel.replace( '_', '&' );66 qlabel.replace( SINGLE_UNDERSCORE, "&" );
59 g_free( label );67 g_free( label );
6068
61 m_ext_menu->setTitle( qlabel );69 m_ext_menu->setTitle( qlabel );
@@ -63,7 +71,7 @@
6371
64 gchar* action_name = NULL;72 gchar* action_name = NULL;
65 QString qaction_name;73 QString qaction_name;
66 if( g_menu_model_get_item_attribute( m_parent->m_model, index,74 if( g_menu_model_get_item_attribute( m_parent->m_model.data(), index,
67 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )75 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
68 {76 {
69 qaction_name = QString::fromUtf8( action_name );77 qaction_name = QString::fromUtf8( action_name );
@@ -74,7 +82,7 @@
7482
75 // if this model has a "commitLabel" property, it is a libhud parameterized action83 // if this model has a "commitLabel" property, it is a libhud parameterized action
76 gchar* commit_label = NULL;84 gchar* commit_label = NULL;
77 if( g_menu_model_get_item_attribute( m_parent->m_model, index,85 if( g_menu_model_get_item_attribute( m_parent->m_model.data(), index,
78 "commitLabel", "s", &commit_label ) )86 "commitLabel", "s", &commit_label ) )
79 {87 {
80 g_free( commit_label );88 g_free( commit_label );
@@ -100,7 +108,7 @@
100108
101 if( m_model )109 if( m_model )
102 {110 {
103 m_size = g_menu_model_get_n_items( m_model );111 m_size = g_menu_model_get_n_items( m_model.data() );
104 }112 }
105113
106 ChangeMenuItems( 0, m_size, 0 );114 ChangeMenuItems( 0, m_size, 0 );
@@ -112,23 +120,15 @@
112 {120 {
113 if( m_size > 0 )121 if( m_size > 0 )
114 {122 {
115 MenuItemsChangedCallback( m_model, 0, m_size, 0, this );123 ChangeMenuItems( 0, 0, m_size );
116 }124 }
117 DisconnectCallback();125 DisconnectCallback();
118 g_object_unref( m_model );
119 }126 }
120127
121 for( auto child : m_children )
122 {
123 delete child;
124 }
125 m_children.clear();128 m_children.clear();
126
127 delete m_menu;
128 delete m_ext_menu;
129}129}
130130
131GMenuModel* QtGMenuModel::Model() const131QSharedPointer<GMenuModel> QtGMenuModel::Model() const
132{132{
133 return m_model;133 return m_model;
134}134}
@@ -138,24 +138,19 @@
138 return m_link_type;138 return m_link_type;
139}139}
140140
141int QtGMenuModel::Size() const
142{
143 return m_size;
144}
145
146QtGMenuModel* QtGMenuModel::Parent() const141QtGMenuModel* QtGMenuModel::Parent() const
147{142{
148 return m_parent;143 return m_parent;
149}144}
150145
151QtGMenuModel* QtGMenuModel::Child( int index ) const146QSharedPointer<QtGMenuModel> QtGMenuModel::Child( int index ) const
152{147{
153 if( m_children.contains( index ) )148 if( m_children.contains( index ) )
154 {149 {
155 return m_children.value( index );150 return m_children.value( index );
156 }151 }
157152
158 return nullptr;153 return QSharedPointer<QtGMenuModel>();
159}154}
160155
161std::shared_ptr< QMenu > QtGMenuModel::GetQMenu()156std::shared_ptr< QMenu > QtGMenuModel::GetQMenu()
@@ -178,7 +173,10 @@
178 auto action_it = m_actions.find( action_name );173 auto action_it = m_actions.find( action_name );
179 if( action_it != end( m_actions ) )174 if( action_it != end( m_actions ) )
180 {175 {
181 action_it->second->setEnabled( enabled );176 for( auto& action : action_it->second )
177 {
178 action->setEnabled( enabled );
179 }
182 }180 }
183}181}
184182
@@ -187,78 +185,122 @@
187 auto action_it = m_actions.find( action_name );185 auto action_it = m_actions.find( action_name );
188 if( action_it != end( m_actions ) )186 if( action_it != end( m_actions ) )
189 {187 {
190 action_it->second->setProperty( c_property_isParameterized, parameterized );188 for( auto& action : action_it->second )
189 {
190 action->setProperty( c_property_isParameterized, parameterized );
191 }
191 }192 }
192}193}
193194
194QtGMenuModel* QtGMenuModel::CreateChild( QtGMenuModel* parent, GMenuModel* model, int index )195QSharedPointer<QtGMenuModel> QtGMenuModel::CreateChild( QtGMenuModel* parent_qtgmenu, QSharedPointer<GMenuModel> parent_gmenu, int child_index )
195{196{
196 LinkType linkType( LinkType::SubMenu );197 QSharedPointer<QtGMenuModel> new_child;
197 GMenuModel* link = g_menu_model_get_item_link( model, index, G_MENU_LINK_SUBMENU );198
198199 GMenuLinkIter* link_it = g_menu_model_iterate_item_links( parent_gmenu.data(), child_index );
199 if( !link )200
200 {201 // get the first link, if it exists, create the child accordingly
201 linkType = LinkType::Section;202 if( link_it && g_menu_link_iter_next( link_it ) )
202 link = g_menu_model_get_item_link( model, index, G_MENU_LINK_SECTION );203 {
203 }204 // if link is a sub menu
204205 if( strcmp( g_menu_link_iter_get_name( link_it ), G_MENU_LINK_SUBMENU ) == 0 )
205 if( link )206 {
206 {207 new_child.reset(
207 return new QtGMenuModel( link, linkType, parent, index );208 new QtGMenuModel(
208 }209 QSharedPointer<GMenuModel>(
209210 g_menu_link_iter_get_value(link_it),
210 return nullptr;211 &g_object_unref), LinkType::SubMenu,
212 parent_qtgmenu, child_index));
213 }
214 // else if link is a section
215 else if( strcmp( g_menu_link_iter_get_name( link_it ), G_MENU_LINK_SECTION ) == 0 )
216 {
217 new_child.reset(
218 new QtGMenuModel(
219 QSharedPointer<GMenuModel>(
220 g_menu_link_iter_get_value(link_it),
221 &g_object_unref), LinkType::Section,
222 parent_qtgmenu, child_index));
223 }
224 }
225
226 g_object_unref( link_it );
227 return new_child;
211}228}
212229
213void QtGMenuModel::MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed,230void QtGMenuModel::MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed,
214 gint added, gpointer user_data )231 gint added, gpointer user_data )
215{232{
216 QtGMenuModel* self = reinterpret_cast< QtGMenuModel* >( user_data );233 QtGMenuModel* self = reinterpret_cast< QtGMenuModel* >( user_data );
234
235 if( self->m_model != model )
236 {
237 qWarning() << "\"items-changed\" signal received from an unrecognised menu model";
238 return;
239 }
240
217 self->ChangeMenuItems( index, added, removed );241 self->ChangeMenuItems( index, added, removed );
218}242}
219243
220void QtGMenuModel::ChangeMenuItems( int index, int added, int removed )244void QtGMenuModel::ChangeMenuItems( const int index, const int added, const int removed )
221{245{
246 const int n_items = g_menu_model_get_n_items( m_model.data() );
247
248 if( index < 0 || added < 0 || removed < 0 || index + added > n_items || index + removed > m_size )
249 {
250 ReportRecoverableError(index, added, removed);
251 return;
252 }
253
254 // process removed items first (see "items-changed" on the GMenuModel man page)
222 if( removed > 0 )255 if( removed > 0 )
223 {256 {
257 // remove QAction from 'index' of our QMenu, 'removed' times
224 for( int i = 0; i < removed; ++i )258 for( int i = 0; i < removed; ++i )
225 {259 {
226 if( index < m_menu->actions().size() )260 if( index < m_menu->actions().size() )
227 {261 {
228 QAction* at_action = m_menu->actions().at( index );262 QAction* at_action = m_menu->actions().at( index );
229 ActionRemoved( at_action->property( c_property_actionName ).toString() );263 ActionRemoved( at_action->property( c_property_actionName ).toString(), at_action );
230 m_menu->removeAction( at_action );264 m_menu->removeAction( at_action );
231 }265 }
232 }266 }
233267
268 // update m_children
234 for( int i = index; i < m_size; ++i )269 for( int i = index; i < m_size; ++i )
235 {270 {
236 if( i <= ( index + removed ) )271 // remove children from index until ( index + removed )
272 if( i < ( index + removed ) )
237 {273 {
238 delete m_children.take( i );274 m_children.take( i );
239 }275 }
276 // shift children from ( index + removed ) to m_size into the now empty positions
240 else if( m_children.contains( i ) )277 else if( m_children.contains( i ) )
241 {278 {
242 m_children.insert( i - removed, m_children.take( i ) );279 m_children.insert( i - removed, m_children.take( i ) );
243 }280 }
244 }281 }
245282
283 // update m_size
246 m_size -= removed;284 m_size -= removed;
247 }285 }
248286
287 // now process added items
249 if( added > 0 )288 if( added > 0 )
250 {289 {
251 // shift items up290 // update m_children (start from the end and work backwards as not to overlap items as we shift them up)
252 for( int i = ( m_size + added ) - 1; i >= index; --i )291 for( int i = m_size - 1; i >= index; --i )
253 {292 {
293 // shift 'added' items up from their current index to ( index + added )
254 if( m_children.contains( i ) )294 if( m_children.contains( i ) )
255 {295 {
256 m_children.insert( i + added, m_children.take( i ) );296 m_children.insert( i + added, m_children.take( i ) );
257 }297 }
258 }298 }
259299
300 // update m_size
260 m_size += added;301 m_size += added;
261302
303 // now add a new QAction to our QMenu for each new item
262 for( int i = index; i < ( index + added ); ++i )304 for( int i = index; i < ( index + added ); ++i )
263 {305 {
264 QAction* at_action = nullptr;306 QAction* at_action = nullptr;
@@ -267,32 +309,37 @@
267 at_action = m_menu->actions().at( i );309 at_action = m_menu->actions().at( i );
268 }310 }
269311
270 QtGMenuModel* model = CreateChild( this, m_model, i );312 // try first to create a child model
313 QSharedPointer< QtGMenuModel > model = CreateChild( this, m_model, i );
271314
315 // if this is a menu item and not a model
272 if( !model )316 if( !model )
273 {317 {
274 QAction* new_action = CreateAction( i );318 QAction* new_action = CreateAction( i );
275 ActionAdded( new_action->property( c_property_actionName ).toString(), new_action );319 ActionAdded( new_action->property( c_property_actionName ).toString(), new_action );
276 m_menu->insertAction( at_action, new_action );320 m_menu->insertAction( at_action, new_action );
277 }321 }
322 // else if this is a section model
278 else if( model->Type() == LinkType::Section )323 else if( model->Type() == LinkType::Section )
279 {324 {
325 InsertChild( model, i );
280 m_menu->insertSeparator( at_action );326 m_menu->insertSeparator( at_action );
281 }327 }
328 // else if this is a sub menu model
282 else if( model->Type() == LinkType::SubMenu )329 else if( model->Type() == LinkType::SubMenu )
283 {330 {
284 m_menu->insertMenu( at_action, model->m_ext_menu );331 InsertChild( model, i );
332 ActionAdded( model->m_ext_menu->menuAction()->property( c_property_actionName ).toString(),
333 model->m_ext_menu->menuAction() );
334 m_menu->insertMenu( at_action, model->m_ext_menu.data() );
285 }335 }
286 }336 }
287 }337 }
288338
289 // update external menu339 // update external menu
290 UpdateExtQMenu();340 UpdateExtQMenu();
291 if( m_link_type == LinkType::Section && m_parent )
292 {
293 m_parent->UpdateExtQMenu();
294 }
295341
342 // now tell the outside world that items have changed
296 emit MenuItemsChanged( this, index, removed, added );343 emit MenuItemsChanged( this, index, removed, added );
297}344}
298345
@@ -300,7 +347,7 @@
300{347{
301 if( m_model && m_items_changed_handler == 0 )348 if( m_model && m_items_changed_handler == 0 )
302 {349 {
303 m_items_changed_handler = g_signal_connect( m_model, "items-changed",350 m_items_changed_handler = g_signal_connect( m_model.data(), "items-changed",
304 G_CALLBACK( MenuItemsChangedCallback ), this );351 G_CALLBACK( MenuItemsChangedCallback ), this );
305 }352 }
306}353}
@@ -309,13 +356,13 @@
309{356{
310 if( m_model && m_items_changed_handler != 0 )357 if( m_model && m_items_changed_handler != 0 )
311 {358 {
312 g_signal_handler_disconnect( m_model, m_items_changed_handler );359 g_signal_handler_disconnect( m_model.data(), m_items_changed_handler );
313 }360 }
314361
315 m_items_changed_handler = 0;362 m_items_changed_handler = 0;
316}363}
317364
318void QtGMenuModel::InsertChild( QtGMenuModel* child, int index )365void QtGMenuModel::InsertChild( QSharedPointer<QtGMenuModel> child, int index )
319{366{
320 if( m_children.contains( index ) )367 if( m_children.contains( index ) )
321 {368 {
@@ -325,35 +372,27 @@
325 child->m_parent = this;372 child->m_parent = this;
326 m_children.insert( index, child );373 m_children.insert( index, child );
327374
328 connect( child, SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ), this,375 connect( child.data(), SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ), this,
329 SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ) );376 SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ) );
330377
331 connect( child, SIGNAL( ActionTriggered( QString, bool ) ), this,378 connect( child.data(), SIGNAL( ActionTriggered( QString, bool ) ), this,
332 SIGNAL( ActionTriggered( QString, bool ) ) );379 SIGNAL( ActionTriggered( QString, bool ) ) );
333}380
334381 connect( child.data(), SIGNAL( MenuInvalid() ), this, SIGNAL( MenuInvalid() ) );
335int QtGMenuModel::ChildIndex( QtGMenuModel* child )382
336{383 // emit signal informing subscribers that this child has added all of its menu items
337 for( int i = 0; i < m_children.size(); ++i )384 emit MenuItemsChanged( child.data(), 0, 0, child->m_size );
338 {
339 if( child == m_children[i] )
340 {
341 return i;
342 }
343 }
344
345 return -1;
346}385}
347386
348QAction* QtGMenuModel::CreateAction( int index )387QAction* QtGMenuModel::CreateAction( int index )
349{388{
389 QAction* action = new QAction( m_menu.data() );
390
350 // action label391 // action label
351 QAction* action = new QAction( this );
352
353 gchar* label = NULL;392 gchar* label = NULL;
354 if( g_menu_model_get_item_attribute( m_model, index, G_MENU_ATTRIBUTE_LABEL, "s", &label ) ) {393 if( g_menu_model_get_item_attribute( m_model.data(), index, G_MENU_ATTRIBUTE_LABEL, "s", &label ) ) {
355 QString qlabel = QString::fromUtf8( label );394 QString qlabel = QString::fromUtf8( label );
356 qlabel.replace( '_', '&' );395 qlabel.replace( SINGLE_UNDERSCORE, "&" );
357 g_free( label );396 g_free( label );
358397
359 action->setText( qlabel );398 action->setText( qlabel );
@@ -361,7 +400,7 @@
361400
362 // action name401 // action name
363 gchar* action_name = NULL;402 gchar* action_name = NULL;
364 if( g_menu_model_get_item_attribute( m_model, index,403 if( g_menu_model_get_item_attribute( m_model.data(), index,
365 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )404 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
366 {405 {
367 QString qaction_name = QString::fromUtf8( action_name );406 QString qaction_name = QString::fromUtf8( action_name );
@@ -378,7 +417,7 @@
378 action->setProperty( c_property_menuPath, m_menu_path );417 action->setProperty( c_property_menuPath, m_menu_path );
379418
380 // action icon419 // action icon
381 GVariant* icon = g_menu_model_get_item_attribute_value( m_model, index, G_MENU_ATTRIBUTE_ICON,420 GVariant* icon = g_menu_model_get_item_attribute_value( m_model.data(), index, G_MENU_ATTRIBUTE_ICON,
382 G_VARIANT_TYPE_VARIANT );421 G_VARIANT_TYPE_VARIANT );
383422
384 if( icon )423 if( icon )
@@ -388,7 +427,7 @@
388427
389 // action shortcut428 // action shortcut
390 gchar* shortcut = NULL;429 gchar* shortcut = NULL;
391 if( g_menu_model_get_item_attribute( m_model, index, "accel", "s", &shortcut ) )430 if( g_menu_model_get_item_attribute( m_model.data(), index, "accel", "s", &shortcut ) )
392 {431 {
393 QString qshortcut = QString::fromUtf8( shortcut );432 QString qshortcut = QString::fromUtf8( shortcut );
394 g_free( shortcut );433 g_free( shortcut );
@@ -398,7 +437,7 @@
398437
399 // action shortcut438 // action shortcut
400 gchar* toolbar_item = NULL;439 gchar* toolbar_item = NULL;
401 if( g_menu_model_get_item_attribute( m_model, index, c_property_hud_toolbar_item, "s", &toolbar_item ) )440 if( g_menu_model_get_item_attribute( m_model.data(), index, c_property_hud_toolbar_item, "s", &toolbar_item ) )
402 {441 {
403 QString qtoolbar_item = QString::fromUtf8( toolbar_item );442 QString qtoolbar_item = QString::fromUtf8( toolbar_item );
404 g_free( toolbar_item );443 g_free( toolbar_item );
@@ -408,7 +447,7 @@
408447
409 // action keywords448 // action keywords
410 gchar* keywords = NULL;449 gchar* keywords = NULL;
411 if( g_menu_model_get_item_attribute( m_model, index, c_property_keywords, "s", &keywords ) )450 if( g_menu_model_get_item_attribute( m_model.data(), index, c_property_keywords, "s", &keywords ) )
412 {451 {
413 QVariant qkeywords = QString::fromUtf8( keywords );452 QVariant qkeywords = QString::fromUtf8( keywords );
414 g_free( keywords );453 g_free( keywords );
@@ -458,14 +497,13 @@
458497
459 if( action->isSeparator() )498 if( action->isSeparator() )
460 {499 {
461 QtGMenuModel* child = Child( i );500 QSharedPointer<QtGMenuModel> child = Child( i );
462 if( !child || child->Type() != LinkType::Section )501 if( !child || child->Type() != LinkType::Section )
463 {502 {
464 continue;503 continue;
465 }504 }
466 QMenu* section = child->m_ext_menu;
467505
468 for( QAction* sub_action : section->actions() )506 for( QAction* sub_action : child->m_ext_menu->actions() )
469 {507 {
470 m_ext_menu->addAction( sub_action );508 m_ext_menu->addAction( sub_action );
471 }509 }
@@ -485,6 +523,12 @@
485 m_ext_menu->removeAction( last_action );523 m_ext_menu->removeAction( last_action );
486 }524 }
487 }525 }
526
527 // if this is a section within a parent menu, we need to update the parent menu as well
528 if( m_link_type == LinkType::Section && m_parent )
529 {
530 m_parent->UpdateExtQMenu();
531 }
488}532}
489533
490void QtGMenuModel::ActionAdded( const QString& name, QAction* action )534void QtGMenuModel::ActionAdded( const QString& name, QAction* action )
@@ -494,17 +538,205 @@
494 {538 {
495 m_parent->ActionAdded( name, action );539 m_parent->ActionAdded( name, action );
496 }540 }
497541 else
498 m_actions[name] = action;542 {
543 // check if the action name is already in our map
544 if( m_actions.find( name ) != m_actions.end() )
545 {
546 // add the QAction pointer to the list of actions under this name
547 m_actions[name].push_back( action );
548 }
549 else
550 {
551 // otherwise insert the new action into the map
552 m_actions.insert( std::make_pair( name, std::vector< QAction* >{ action } ) );
553 }
554 }
499}555}
500556
501void QtGMenuModel::ActionRemoved( const QString& name )557void QtGMenuModel::ActionRemoved( const QString& name, QAction* action )
502{558{
503 // remove action from top menu's m_actions559 // remove action from top menu's m_actions
504 if( m_parent )560 if( m_parent )
505 {561 {
506 m_parent->ActionRemoved( name );562 m_parent->ActionRemoved( name, action );
507 }563 }
508564 else
509 m_actions.erase( name );565 {
566 // check if this action is actually in our map
567 if( m_actions.find( name ) != m_actions.end() )
568 {
569 // remove the QAction pointer from the list of actions under this name
570 auto& actionList = m_actions[name];
571 auto actionIt = std::find( actionList.begin(), actionList.end(), action );
572
573 if( actionIt != actionList.end())
574 {
575 actionList.erase( actionIt );
576 }
577
578 // if there are no more references to this action, remove it from the map
579 if( actionList.size() == 0 )
580 {
581 m_actions.erase( name );
582 }
583 }
584 }
585}
586
587static void write_pair(QIODevice& device, const QString& key, const QString& value, bool last = false)
588{
589 device.write(key.toUtf8());
590 device.write("", 1);
591 device.write(value.toUtf8());
592 if( !last )
593 {
594 device.write("", 1);
595 }
596
597 if( !value.isEmpty())
598 {
599 qWarning() << key << " =" << value;
600 }
601}
602
603void QtGMenuModel::ReportRecoverableError(const int index, const int added, const int removed)
604{
605 if( m_error_reported )
606 {
607 return;
608 }
609
610 // gmenumodel properties
611 int gmenu_item_count = 0;
612 QString gmenu_action_names;
613
614 gmenu_item_count = g_menu_model_get_n_items( m_model.data() );
615
616 qWarning() << "Illegal arguments when updating GMenuModel: position ="
617 << index << ", added =" << added << ", removed =" << removed
618 << ", size =" << gmenu_item_count;
619
620 for( int i = 0; i < gmenu_item_count; ++i )
621 {
622 gchar* action_name = NULL;
623 if( g_menu_model_get_item_attribute( m_model.data(), i,
624 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
625 {
626 gmenu_action_names += action_name;
627 gmenu_action_names += ";";
628 g_free( action_name );
629 }
630 }
631
632 // parent model properties
633 QString parent_menu_label;
634 QString parent_menu_name;
635 QString parent_action_names;
636 QString parent_link_type;
637
638 if( m_parent )
639 {
640 parent_menu_label = m_parent->m_menu->menuAction()->text();
641 parent_menu_name = m_parent->m_menu->menuAction()->property( c_property_actionName ).toString();
642
643 for( QAction* action : m_parent->m_menu->actions() )
644 {
645 parent_action_names += action->property( c_property_actionName ).toString() + ";";
646 }
647
648 switch( m_parent->m_link_type )
649 {
650 case LinkType::Root:
651 parent_link_type = "root";
652 break;
653 case LinkType::Section:
654 parent_link_type = "section";
655 break;
656 case LinkType::SubMenu:
657 parent_link_type = "sub menu";
658 break;
659 }
660 }
661
662 // local model properties
663 QString menu_label;
664 QString menu_name;
665 QString action_names;
666 QString link_type;
667 QString action_paths;
668
669 menu_label = m_menu->menuAction()->text();
670 menu_name = m_menu->menuAction()->property( c_property_actionName ).toString();
671 for( QAction* action : m_menu->actions() )
672 {
673 action_names += action->property( c_property_actionName ).toString() + ";";
674 }
675
676 switch( m_link_type )
677 {
678 case LinkType::Root:
679 link_type = "root";
680 break;
681 case LinkType::Section:
682 link_type = "section";
683 break;
684 case LinkType::SubMenu:
685 link_type = "sub menu";
686 break;
687 }
688
689 for( auto const& action : m_action_paths )
690 {
691 action_paths += action.path() + ";";
692 }
693
694 uint sender_pid = QDBusConnection::sessionBus().interface()->servicePid(
695 m_bus_name);
696 if( sender_pid == 0 ) {
697 qWarning() << "Failed to read PID, cannot report error";
698 return;
699 }
700
701 QProcess recoverable;
702 recoverable.setProcessChannelMode(QProcess::ForwardedChannels);
703 recoverable.start("/usr/share/apport/recoverable_problem",
704 QStringList() << "-p" << QString::number(sender_pid));
705 if (recoverable.waitForStarted())
706 {
707 write_pair(recoverable, "DuplicateSignature", "GMenuModelItemsChangedInvalidIndex");
708 write_pair(recoverable, "BusName", m_bus_name);
709 write_pair(recoverable, "Position", QString::number(index));
710 write_pair(recoverable, "Added", QString::number(added));
711 write_pair(recoverable, "Removed", QString::number(removed));
712 write_pair(recoverable, "ItemCount", QString::number(gmenu_item_count));
713 write_pair(recoverable, "ActionNames", gmenu_action_names);
714
715 if ( m_parent )
716 {
717 write_pair(recoverable, "ParentMenuLabel", parent_menu_label);
718 write_pair(recoverable, "ParentMenuName", parent_menu_name);
719 write_pair(recoverable, "ParentActionNames", parent_action_names);
720 write_pair(recoverable, "ParentLinkType", parent_link_type);
721 }
722
723 write_pair(recoverable, "MenuLabel", menu_label);
724 write_pair(recoverable, "MenuName", menu_name);
725 write_pair(recoverable, "ActionNames", action_names);
726 write_pair(recoverable, "LinkType", link_type);
727
728 write_pair(recoverable, "MenuPath", m_menu_path);
729 write_pair(recoverable, "ActionPaths", action_paths, true);
730
731 recoverable.closeWriteChannel();
732 recoverable.waitForFinished();
733
734 m_error_reported = true;
735 }
736 else
737 {
738 qWarning() << "Failed to report recoverable error";
739 }
740
741 emit MenuInvalid();
510}742}
511743
=== modified file 'libqtgmenu/internal/QtGMenuModel.h'
--- libqtgmenu/internal/QtGMenuModel.h 2014-03-19 23:26:48 +0000
+++ libqtgmenu/internal/QtGMenuModel.h 2014-06-13 07:41:53 +0000
@@ -43,17 +43,14 @@
43 Root, Section, SubMenu43 Root, Section, SubMenu
44 };44 };
4545
46 explicit QtGMenuModel( GMenuModel* model );46 QtGMenuModel( QSharedPointer<GDBusConnection> connection, const QString& bus_name, const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths );
47 QtGMenuModel( GMenuModel* model, const QString& bus_name, const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths );
48 virtual ~QtGMenuModel();47 virtual ~QtGMenuModel();
4948
50 GMenuModel* Model() const;49 QSharedPointer<GMenuModel> Model() const;
51 LinkType Type() const;50 LinkType Type() const;
5251
53 int Size() const;
54
55 QtGMenuModel* Parent() const;52 QtGMenuModel* Parent() const;
56 QtGMenuModel* Child( int index ) const;53 QSharedPointer<QtGMenuModel> Child( int index ) const;
5754
58 std::shared_ptr< QMenu > GetQMenu();55 std::shared_ptr< QMenu > GetQMenu();
5956
@@ -68,6 +65,7 @@
68Q_SIGNALS:65Q_SIGNALS:
69 void MenuItemsChanged( QtGMenuModel* model, int index, int removed, int added );66 void MenuItemsChanged( QtGMenuModel* model, int index, int removed, int added );
70 void ActionTriggered( QString action_name, bool checked );67 void ActionTriggered( QString action_name, bool checked );
68 void MenuInvalid();
7169
72public Q_SLOTS:70public Q_SLOTS:
73 void ActionEnabled( QString action_name, bool enabled );71 void ActionEnabled( QString action_name, bool enabled );
@@ -77,20 +75,19 @@
77 void ActionTriggered( bool );75 void ActionTriggered( bool );
7876
79private:77private:
80 QtGMenuModel( GMenuModel* model, LinkType link_type, QtGMenuModel* parent, int index );78 QtGMenuModel( QSharedPointer<GMenuModel> model, LinkType link_type, QtGMenuModel* parent, int index );
8179
82 static QtGMenuModel* CreateChild( QtGMenuModel* parent, GMenuModel* model, int index );80 static QSharedPointer<QtGMenuModel> CreateChild( QtGMenuModel* parent_qtgmenu, QSharedPointer<GMenuModel> parent_gmenu, int child_index );
8381
84 static void MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed, gint added,82 static void MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed, gint added,
85 gpointer user_data );83 gpointer user_data );
8684
87 void ChangeMenuItems( int index, int added, int removed );85 void ChangeMenuItems( const int index, const int added, const int removed );
8886
89 void ConnectCallback();87 void ConnectCallback();
90 void DisconnectCallback();88 void DisconnectCallback();
9189
92 void InsertChild( QtGMenuModel* child, int index );90 void InsertChild( QSharedPointer<QtGMenuModel> child, int index );
93 int ChildIndex( QtGMenuModel* child );
9491
95 QAction* CreateAction( int index );92 QAction* CreateAction( int index );
9693
@@ -98,26 +95,32 @@
98 void UpdateExtQMenu();95 void UpdateExtQMenu();
9996
100 void ActionAdded( const QString& name, QAction* action );97 void ActionAdded( const QString& name, QAction* action );
101 void ActionRemoved( const QString& name );98 void ActionRemoved( const QString& name, QAction* action );
99
100 void ReportRecoverableError(const int index, const int added, const int removed);
102101
103private:102private:
104 QtGMenuModel* m_parent = nullptr;103 QtGMenuModel* m_parent = nullptr;
105 QMap< int, QtGMenuModel* > m_children;104 QMap< int, QSharedPointer<QtGMenuModel>> m_children;
106105
107 GMenuModel* m_model = nullptr;106 QSharedPointer<GMenuModel> m_model;
108 gulong m_items_changed_handler = 0;107 gulong m_items_changed_handler = 0;
109108
110 LinkType m_link_type;109 LinkType m_link_type;
111 int m_size = 0;110 int m_size = 0;
112111
113 QMenu* m_menu = new QMenu();112 QScopedPointer<QMenu> m_menu;
114 QMenu* m_ext_menu = new QMenu();113 QScopedPointer<QMenu> m_ext_menu;
115114
115 QSharedPointer<GDBusConnection> m_connection;
116 QString m_bus_name;116 QString m_bus_name;
117 QString m_menu_path;117 QString m_menu_path;
118 QMap<QString, QDBusObjectPath> m_action_paths;118 QMap<QString, QDBusObjectPath> m_action_paths;
119119
120 std::map< QString, QAction* > m_actions;120 // a map of QActions indexed by their name and stored with a reference count
121 std::map< QString, std::vector< QAction* > > m_actions;
122
123 bool m_error_reported = false;
121};124};
122125
123} // namespace qtgmenu126} // namespace qtgmenu
124127
=== modified file 'service/DBusMenuCollector.cpp'
--- service/DBusMenuCollector.cpp 2014-04-01 13:13:04 +0000
+++ service/DBusMenuCollector.cpp 2014-06-13 07:41:53 +0000
@@ -82,25 +82,18 @@
82 return !m_menuImporter.isNull();82 return !m_menuImporter.isNull();
83}83}
8484
85inline uint qHash(const QStringList &key, uint seed) {
86 uint hash(0);
87 for (const QString &s : key) {
88 hash ^= qHash(s, seed);
89 }
90 return hash;
91}
92
93void DBusMenuCollector::openMenu(QMenu *menu, unsigned int &limit) {85void DBusMenuCollector::openMenu(QMenu *menu, unsigned int &limit) {
86 --limit;
87 if (limit == 0) {
88 QString error = "Hit DBusMenu safety valve opening menu at " + m_service
89 + " " + m_path.path();
90 throw std::logic_error(error.toStdString());
91 }
92
94 if (!menu) {93 if (!menu) {
95 return;94 return;
96 }95 }
9796
98 if (limit == 0) {
99 QString error = "Hit DBusMenu safety valve for menu at " + m_service
100 + " " + m_path.path();
101 throw std::logic_error(error.toStdString());
102 }
103
104 menu->aboutToShow();97 menu->aboutToShow();
10598
106 for (int i(0); m_menuImporter && i < menu->actions().size(); ++i) {99 for (int i(0); m_menuImporter && i < menu->actions().size(); ++i) {
@@ -114,15 +107,15 @@
114107
115 QMenu *child(action->menu());108 QMenu *child(action->menu());
116 if (child) {109 if (child) {
117 --limit;
118 openMenu(child, limit);110 openMenu(child, limit);
119 }111 }
120 }112 }
121}113}
122114
123void DBusMenuCollector::hideMenu(QMenu *menu, unsigned int &limit) {115void DBusMenuCollector::hideMenu(QMenu *menu, unsigned int &limit) {
116 --limit;
124 if (limit == 0) {117 if (limit == 0) {
125 QString error = "Hit DBusMenu safety valve for menu at " + m_service118 QString error = "Hit DBusMenu safety valve closing menu at " + m_service
126 + " " + m_path.path();119 + " " + m_path.path();
127 throw std::logic_error(error.toStdString());120 throw std::logic_error(error.toStdString());
128 }121 }
@@ -131,7 +124,6 @@
131 QAction *action = menu->actions().at(i);124 QAction *action = menu->actions().at(i);
132 QMenu *child(action->menu());125 QMenu *child(action->menu());
133 if (child) {126 if (child) {
134 --limit;
135 hideMenu(child, limit);127 hideMenu(child, limit);
136 }128 }
137 }129 }
138130
=== modified file 'service/HudServiceImpl.cpp'
--- service/HudServiceImpl.cpp 2014-02-18 13:35:12 +0000
+++ service/HudServiceImpl.cpp 2014-06-13 07:41:53 +0000
@@ -104,13 +104,24 @@
104 QList<Suggestion> &suggestions, QDBusVariant &querykey) {104 QList<Suggestion> &suggestions, QDBusVariant &querykey) {
105 QString sender(messageSender());105 QString sender(messageSender());
106106
107 Query::Ptr query(m_legacyQueries[sender]);107 QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries[sender]);
108 Query::Ptr query(entry.first);
109 QSharedPointer<QTimer> legacyTimeout(entry.second);
108 if (query.isNull()) {110 if (query.isNull()) {
109 query = createQuery(queryString, sender,111 query = createQuery(queryString, sender,
110 Query::EmptyBehaviour::NO_SUGGESTIONS);112 Query::EmptyBehaviour::NO_SUGGESTIONS);
111 m_legacyQueries[sender] = query;113
114 legacyTimeout.reset(new QTimer());
115 legacyTimeout->setInterval(2000);
116 legacyTimeout->setSingleShot(true);
117 connect(legacyTimeout.data(), SIGNAL(timeout()), this,
118 SLOT(legacyTimeout()));
119
120 m_legacyQueries[sender] = qMakePair(query, legacyTimeout);
112 } else {121 } else {
113 query->UpdateQuery(queryString);122 query->UpdateQuery(queryString);
123 legacyTimeout->stop();
124 legacyTimeout->setProperty("sender", QVariant());
114 }125 }
115126
116 // The legacy API only allows you to search the current application127 // The legacy API only allows you to search the current application
@@ -142,8 +153,13 @@
142 Q_UNUSED(timestamp);153 Q_UNUSED(timestamp);
143 QString sender(messageSender());154 QString sender(messageSender());
144155
145 Query::Ptr query(m_legacyQueries.take(sender));156 QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries.take(sender));
157 Query::Ptr query(entry.first);
158 QSharedPointer<QTimer> legacyTimeout(entry.second);
146 if (!query.isNull()) {159 if (!query.isNull()) {
160 legacyTimeout->stop();
161 legacyTimeout->setProperty("sender", QVariant());
162
147 query->ExecuteCommand(itemKey, timestamp);163 query->ExecuteCommand(itemKey, timestamp);
148 closeQuery(query->path());164 closeQuery(query->path());
149 }165 }
@@ -156,8 +172,32 @@
156 // We don't actually close legacy queries, or we'd be constructing172 // We don't actually close legacy queries, or we'd be constructing
157 // and destructing them during the search, due to the way that173 // and destructing them during the search, due to the way that
158 // Unity7 uses the API.174 // Unity7 uses the API.
159 Query::Ptr query(m_legacyQueries[sender]);175 QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries[sender]);
176 Query::Ptr query(entry.first);
177 QSharedPointer<QTimer> legacyTimeout(entry.second);
160 if (!query.isNull()) {178 if (!query.isNull()) {
161 query->UpdateQuery(QString());179 query->UpdateQuery(QString());
162 }180 legacyTimeout->start();
181 legacyTimeout->setProperty("sender", sender);
182 }
183}
184
185void HudServiceImpl::legacyTimeout() {
186 QObject *timer(sender());
187 if (!timer) {
188 return;
189 }
190
191 QVariant from(timer->property("sender"));
192 if (from.isNull()) {
193 return;
194 }
195
196 QString sender(from.toString());
197 QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries.take(sender));
198 Query::Ptr query(entry.first);
199 if (query) {
200 closeQuery(query->path());
201 }
202
163}203}
164204
=== modified file 'service/HudServiceImpl.h'
--- service/HudServiceImpl.h 2014-02-18 13:35:12 +0000
+++ service/HudServiceImpl.h 2014-06-13 07:41:53 +0000
@@ -28,6 +28,7 @@
28#include <QDBusVariant>28#include <QDBusVariant>
29#include <QMap>29#include <QMap>
30#include <QScopedPointer>30#include <QScopedPointer>
31#include <QTimer>
3132
32class HudAdaptor;33class HudAdaptor;
3334
@@ -73,6 +74,9 @@
7374
74 void CloseQuery(const QDBusVariant &querykey);75 void CloseQuery(const QDBusVariant &querykey);
7576
77protected Q_SLOTS:
78 void legacyTimeout();
79
76protected:80protected:
77 Query::Ptr createQuery(const QString &query, const QString &service,81 Query::Ptr createQuery(const QString &query, const QString &service,
78 Query::EmptyBehaviour emptyBehaviour);82 Query::EmptyBehaviour emptyBehaviour);
@@ -85,7 +89,7 @@
8589
86 QMap<QDBusObjectPath, Query::Ptr> m_queries;90 QMap<QDBusObjectPath, Query::Ptr> m_queries;
8791
88 QMap<QString, Query::Ptr> m_legacyQueries;92 QMap<QString, QPair<Query::Ptr, QSharedPointer<QTimer>>> m_legacyQueries;
8993
90 QSharedPointer<ApplicationList> m_applicationList;94 QSharedPointer<ApplicationList> m_applicationList;
9195
9296
=== modified file 'tests/unit/qtgmenu/TestQtGMenu.cpp'
--- tests/unit/qtgmenu/TestQtGMenu.cpp 2014-03-20 06:46:57 +0000
+++ tests/unit/qtgmenu/TestQtGMenu.cpp 2014-06-13 07:41:53 +0000
@@ -71,28 +71,28 @@
7171
72 int GetGMenuSize()72 int GetGMenuSize()
73 {73 {
74 GMenuModel* menu = m_importer.GetGMenuModel();74 QSharedPointer<GMenuModel> menu = m_importer.GetGMenuModel();
7575
76 if( !menu )76 if( !menu )
77 {77 {
78 return 0;78 return 0;
79 }79 }
8080
81 gint item_count = g_menu_model_get_n_items( G_MENU_MODEL( menu ) );81 gint item_count = g_menu_model_get_n_items( G_MENU_MODEL( menu.data() ) );
8282
83 return item_count;83 return item_count;
84 }84 }
8585
86 int GetGActionCount()86 int GetGActionCount()
87 {87 {
88 GActionGroup* actions = m_importer.GetGActionGroup();88 QSharedPointer<GActionGroup> actions = m_importer.GetGActionGroup();
8989
90 if( !actions )90 if( !actions )
91 {91 {
92 return 0;92 return 0;
93 }93 }
9494
95 gchar** actions_list = g_action_group_list_actions( actions );95 gchar** actions_list = g_action_group_list_actions( actions.data() );
9696
97 int action_count = 0;97 int action_count = 0;
98 while( actions_list[action_count] != nullptr )98 while( actions_list[action_count] != nullptr )

Subscribers

People subscribed via source and target branches