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

Proposed by Pete Woods on 2014-06-13
Status: Merged
Approved by: Pete Woods on 2014-06-13
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 on 2014-06-13
Indicator Applet Developers 2014-06-13 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.
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
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
1=== modified file 'data/hud.conf.in'
2--- data/hud.conf.in 2013-11-27 14:28:43 +0000
3+++ data/hud.conf.in 2014-06-13 07:41:53 +0000
4@@ -1,22 +1,10 @@
5 description "Unity HUD"
6
7-pre-start script
8- if [ -z $DBUS_SESSION_BUS_ADDRESS ]; then
9- echo "Working around missing DBUS_SESSION_BUS_ADDRESS variable"
10- printf "DuplicateSignature\0DBusSessionAddressNotSet" | /usr/share/apport/recoverable_problem -p `pidof -s -o 1 init`
11- . "${HOME}/.cache/upstart/dbus-session"
12- initctl set-env "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"
13- fi
14-end script
15-
16 # Currently started by the dbus service file
17 # start on dbus-activation com.canonical.hud
18 stop on desktop-end
19 start on started dbus and ((xsession SESSION=ubuntu-touch) or (xsession SESSION=ubuntu-touch-surfaceflinger) or (xsession SESSION=ubuntu))
20
21-env HUD_SERVICE_TIMEOUT=0
22-export HUD_SERVICE_TIMEOUT
23-
24 pre-start script
25 if [ -z $DBUS_SESSION_BUS_ADDRESS ]; then
26 echo "Working around missing DBUS_SESSION_BUS_ADDRESS variable"
27
28=== modified file 'debian/changelog'
29--- debian/changelog 2014-06-09 14:06:31 +0000
30+++ debian/changelog 2014-06-13 07:41:53 +0000
31@@ -1,3 +1,21 @@
32+hud (14.10-0ubuntu1) UNRELEASED; urgency=medium
33+
34+ [ Pete Woods ]
35+ * Forward port changes from trusty.
36+
37+ -- Pete Woods <pete.woods@canonical.com> Fri, 13 Jun 2014 08:22:34 +0100
38+
39+hud (14.04+14.04.20140604-0ubuntu1) trusty; urgency=medium
40+
41+ [ Pete Woods ]
42+ * Resolve crasher in previous attempted SRU. (LP: #1298656)
43+ - Fix order of menu traversal.
44+ - Add timeout to legacy HUD queries.
45+ - Improve legacy menu safety valve trigger.
46+ - Remove duplicate entries in upstart job.
47+
48+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 04 Jun 2014 14:04:12 +0000
49+
50 hud (13.10.1+14.10.20140609-0ubuntu1) utopic; urgency=low
51
52 [ Ubuntu daily release ]
53@@ -8,6 +26,22 @@
54
55 -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 09 Jun 2014 14:06:31 +0000
56
57+hud (13.10.1+14.04.20140425-0ubuntu1) trusty; urgency=low
58+
59+ [ Pete Woods ]
60+ * Harden HUD against misbehaving applications Report the offending
61+ applications using Apport's recoverable problem tool. Switch to
62+ using shared pointers where possible for managing memory. (LP:
63+ #1298656)
64+
65+ [ Marcus Tomlinson ]
66+ * Harden HUD against misbehaving applications Report the offending
67+ applications using Apport's recoverable problem tool. Switch to
68+ using shared pointers where possible for managing memory. (LP:
69+ #1298656)
70+
71+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 25 Apr 2014 08:48:46 +0000
72+
73 hud (13.10.1+14.04.20140402-0ubuntu2) utopic; urgency=medium
74
75 * No-change rebuild for shlib changes in qtbase and qtdeclarative.
76
77=== modified file 'libqtgmenu/QtGMenuImporter.cpp'
78--- libqtgmenu/QtGMenuImporter.cpp 2014-03-11 05:04:08 +0000
79+++ libqtgmenu/QtGMenuImporter.cpp 2014-06-13 07:41:53 +0000
80@@ -43,12 +43,12 @@
81 {
82 }
83
84-GMenuModel* QtGMenuImporter::GetGMenuModel() const
85+QSharedPointer<GMenuModel> QtGMenuImporter::GetGMenuModel() const
86 {
87 return d->GetGMenuModel();
88 }
89
90-GActionGroup* QtGMenuImporter::GetGActionGroup( int index ) const
91+QSharedPointer<GActionGroup> QtGMenuImporter::GetGActionGroup( int index ) const
92 {
93 return d->GetGActionGroup( index );
94 }
95
96=== modified file 'libqtgmenu/QtGMenuImporter.h'
97--- libqtgmenu/QtGMenuImporter.h 2014-03-12 09:04:14 +0000
98+++ libqtgmenu/QtGMenuImporter.h 2014-06-13 07:41:53 +0000
99@@ -47,8 +47,8 @@
100 const QMap<QString, QDBusObjectPath>& action_paths, QObject* parent = 0 );
101 virtual ~QtGMenuImporter();
102
103- GMenuModel* GetGMenuModel() const;
104- GActionGroup* GetGActionGroup( int index = 0 ) const;
105+ QSharedPointer<GMenuModel> GetGMenuModel() const;
106+ QSharedPointer<GActionGroup> GetGActionGroup( int index = 0 ) const;
107
108 std::shared_ptr< QMenu > GetQMenu() const;
109
110
111=== modified file 'libqtgmenu/internal/QtGActionGroup.cpp'
112--- libqtgmenu/internal/QtGActionGroup.cpp 2014-03-19 23:40:09 +0000
113+++ libqtgmenu/internal/QtGActionGroup.cpp 2014-06-13 07:41:53 +0000
114@@ -21,13 +21,20 @@
115
116 using namespace qtgmenu;
117
118-QtGActionGroup::QtGActionGroup( const QString& action_prefix, GActionGroup* action_group )
119+QtGActionGroup::QtGActionGroup( QSharedPointer<GDBusConnection> connection,
120+ const QString& action_prefix,
121+ const QString& service,
122+ const QString& path )
123 : m_action_prefix( action_prefix ),
124- m_action_group( action_group )
125+ m_action_group(
126+ G_ACTION_GROUP(
127+ g_dbus_action_group_get(connection.data(),
128+ service.toUtf8().constData(),
129+ path.toUtf8().constData())), &g_object_unref)
130 {
131 ConnectCallbacks();
132
133- auto actions_list = g_action_group_list_actions( m_action_group );
134+ auto actions_list = g_action_group_list_actions( m_action_group.data() );
135 if( actions_list )
136 {
137 for( int i = 0; actions_list[i]; ++i )
138@@ -46,7 +53,7 @@
139 return;
140 }
141
142- auto actions_list = g_action_group_list_actions( m_action_group );
143+ auto actions_list = g_action_group_list_actions( m_action_group.data() );
144 if( actions_list )
145 {
146 for( int i = 0; actions_list[i]; ++i )
147@@ -58,14 +65,9 @@
148 }
149
150 DisconnectCallbacks();
151-
152- g_object_unref( m_action_group );
153- m_action_group = nullptr;
154-
155- return;
156 }
157
158-GActionGroup* QtGActionGroup::ActionGroup() const
159+QSharedPointer<GActionGroup> QtGActionGroup::ActionGroup() const
160 {
161 return m_action_group;
162 }
163@@ -83,12 +85,12 @@
164 const QString& action(split.second);
165 QByteArray action_utf = action.toUtf8();
166
167- const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group,
168+ const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group.data(),
169 action_utf.constData() );
170
171 if( type == nullptr )
172 {
173- g_action_group_activate_action( m_action_group, action_utf.constData(), nullptr );
174+ g_action_group_activate_action( m_action_group.data(), action_utf.constData(), nullptr );
175 }
176 else
177 {
178@@ -96,7 +98,7 @@
179 if( g_variant_type_equal( type, G_VARIANT_TYPE_STRING ) )
180 {
181 GVariant* param = g_variant_new_string( action_utf.constData() );
182- g_action_group_activate_action( m_action_group, action_utf.constData(), param );
183+ g_action_group_activate_action( m_action_group.data(), action_utf.constData(), param );
184 g_variant_unref( param );
185 }
186 }
187@@ -116,21 +118,19 @@
188
189 void QtGActionGroup::EmitStates()
190 {
191- auto actions_list = g_action_group_list_actions( m_action_group );
192+ auto actions_list = g_action_group_list_actions( m_action_group.data() );
193
194 for( int i = 0; actions_list && actions_list[i]; ++i )
195 {
196 gchar* action_name = actions_list[i];
197
198- bool enabled = G_ACTION_GROUP_GET_IFACE( m_action_group ) ->get_action_enabled( m_action_group,
199+ bool enabled = G_ACTION_GROUP_GET_IFACE( m_action_group.data() ) ->get_action_enabled( m_action_group.data(),
200 action_name );
201- if( !enabled )
202- emit ActionEnabled( FullName(m_action_prefix, action_name), enabled );
203+ emit ActionEnabled( FullName(m_action_prefix, action_name), enabled );
204
205- const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group,
206+ const GVariantType* type = g_action_group_get_action_parameter_type( m_action_group.data(),
207 action_name );
208- if( type != nullptr )
209- emit ActionParameterized( FullName(m_action_prefix, action_name), type != nullptr );
210+ emit ActionParameterized( FullName(m_action_prefix, action_name), type != nullptr );
211 }
212
213 g_strfreev( actions_list );
214@@ -142,12 +142,12 @@
215 QtGActionGroup* self = reinterpret_cast< QtGActionGroup* >( user_data );
216 emit self->ActionAdded( action_name );
217
218- bool enabled = G_ACTION_GROUP_GET_IFACE( self->m_action_group ) ->get_action_enabled(
219- self->m_action_group, action_name );
220+ bool enabled = G_ACTION_GROUP_GET_IFACE( self->m_action_group.data() ) ->get_action_enabled(
221+ self->m_action_group.data(), action_name );
222 if( !enabled )
223 emit self->ActionEnabled( FullName(self->m_action_prefix, action_name), enabled );
224
225- const GVariantType* type = g_action_group_get_action_parameter_type( self->m_action_group,
226+ const GVariantType* type = g_action_group_get_action_parameter_type( self->m_action_group.data(),
227 action_name );
228 if( type != nullptr )
229 emit self->ActionParameterized( FullName(self->m_action_prefix, action_name), type != nullptr );
230@@ -178,22 +178,22 @@
231 {
232 if( m_action_group && m_action_added_handler == 0 )
233 {
234- m_action_added_handler = g_signal_connect( m_action_group, "action-added",
235+ m_action_added_handler = g_signal_connect( m_action_group.data(), "action-added",
236 G_CALLBACK( ActionAddedCallback ), this );
237 }
238 if( m_action_group && m_action_removed_handler == 0 )
239 {
240- m_action_removed_handler = g_signal_connect( m_action_group, "action-removed",
241+ m_action_removed_handler = g_signal_connect( m_action_group.data(), "action-removed",
242 G_CALLBACK( ActionRemovedCallback ), this );
243 }
244 if( m_action_group && m_action_enabled_handler == 0 )
245 {
246- m_action_enabled_handler = g_signal_connect( m_action_group, "action-enabled-changed",
247+ m_action_enabled_handler = g_signal_connect( m_action_group.data(), "action-enabled-changed",
248 G_CALLBACK( ActionEnabledCallback ), this );
249 }
250 if( m_action_group && m_action_state_changed_handler == 0 )
251 {
252- m_action_state_changed_handler = g_signal_connect( m_action_group, "action-state-changed",
253+ m_action_state_changed_handler = g_signal_connect( m_action_group.data(), "action-state-changed",
254 G_CALLBACK( ActionStateChangedCallback ), this );
255 }
256 }
257@@ -202,19 +202,19 @@
258 {
259 if( m_action_group && m_action_added_handler != 0 )
260 {
261- g_signal_handler_disconnect( m_action_group, m_action_added_handler );
262+ g_signal_handler_disconnect( m_action_group.data(), m_action_added_handler );
263 }
264 if( m_action_group && m_action_removed_handler != 0 )
265 {
266- g_signal_handler_disconnect( m_action_group, m_action_removed_handler );
267+ g_signal_handler_disconnect( m_action_group.data(), m_action_removed_handler );
268 }
269 if( m_action_group && m_action_enabled_handler != 0 )
270 {
271- g_signal_handler_disconnect( m_action_group, m_action_enabled_handler );
272+ g_signal_handler_disconnect( m_action_group.data(), m_action_enabled_handler );
273 }
274 if( m_action_group && m_action_state_changed_handler != 0 )
275 {
276- g_signal_handler_disconnect( m_action_group, m_action_state_changed_handler );
277+ g_signal_handler_disconnect( m_action_group.data(), m_action_state_changed_handler );
278 }
279
280 m_action_added_handler = 0;
281
282=== modified file 'libqtgmenu/internal/QtGActionGroup.h'
283--- libqtgmenu/internal/QtGActionGroup.h 2014-03-19 23:40:09 +0000
284+++ libqtgmenu/internal/QtGActionGroup.h 2014-06-13 07:41:53 +0000
285@@ -20,6 +20,7 @@
286 #define QTGACTIONGROUP_H
287
288 #include <QObject>
289+#include <QSharedPointer>
290 #include <QVariant>
291
292 #undef signals
293@@ -33,10 +34,13 @@
294 Q_OBJECT
295
296 public:
297- QtGActionGroup( const QString& action_prefix, GActionGroup* action_group );
298+ QtGActionGroup(QSharedPointer<GDBusConnection> connection,
299+ const QString& action_prefix,
300+ const QString& service,
301+ const QString& path);
302 virtual ~QtGActionGroup();
303
304- GActionGroup* ActionGroup() const;
305+ QSharedPointer<GActionGroup> ActionGroup() const;
306
307 Q_SIGNALS:
308 void ActionAdded( QString action_name );
309@@ -66,7 +70,7 @@
310 private:
311
312 QString m_action_prefix;
313- GActionGroup* m_action_group = nullptr;
314+ QSharedPointer<GActionGroup> m_action_group;
315
316 gulong m_action_added_handler = 0;
317 gulong m_action_removed_handler = 0;
318
319=== modified file 'libqtgmenu/internal/QtGMenuImporterPrivate.cpp'
320--- libqtgmenu/internal/QtGMenuImporterPrivate.cpp 2014-03-19 23:26:48 +0000
321+++ libqtgmenu/internal/QtGMenuImporterPrivate.cpp 2014-06-13 07:41:53 +0000
322@@ -30,7 +30,7 @@
323 m_service_watcher( service, QDBusConnection::sessionBus(),
324 QDBusServiceWatcher::WatchForOwnerChange ),
325 m_parent( parent ),
326- m_connection( g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ) ),
327+ m_connection( g_bus_get_sync( G_BUS_TYPE_SESSION, NULL, NULL ), &g_object_unref ),
328 m_service( service ),
329 m_menu_path( menu_path ),
330 m_action_paths( action_paths )
331@@ -48,26 +48,24 @@
332 {
333 ClearMenuModel();
334 ClearActionGroups();
335-
336- g_object_unref( m_connection );
337 }
338
339-GMenuModel* QtGMenuImporterPrivate::GetGMenuModel()
340+QSharedPointer<GMenuModel> QtGMenuImporterPrivate::GetGMenuModel()
341 {
342 if( m_menu_model == nullptr )
343 {
344- return nullptr;
345+ return QSharedPointer<GMenuModel>();
346 }
347
348 return m_menu_model->Model();
349 }
350
351-GActionGroup* QtGMenuImporterPrivate::GetGActionGroup( int index )
352+QSharedPointer<GActionGroup> QtGMenuImporterPrivate::GetGActionGroup( int index )
353 {
354 if( index >= m_action_groups.size() ||
355 m_action_groups[index] == nullptr )
356 {
357- return nullptr;
358+ return QSharedPointer<GActionGroup>();
359 }
360
361 return m_action_groups[index]->ActionGroup();
362@@ -160,13 +158,12 @@
363 ClearMenuModel();
364
365 QString menu_path = m_menu_path.path();
366- m_menu_model =
367- std::make_shared< QtGMenuModel > (
368- G_MENU_MODEL( g_dbus_menu_model_get( m_connection, m_service.toUtf8().constData(), menu_path.toUtf8().constData() ) ),
369- m_service, menu_path, m_action_paths );
370+ m_menu_model = std::make_shared< QtGMenuModel > ( m_connection, m_service, menu_path, m_action_paths );
371
372 connect( m_menu_model.get(), SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int,
373 int ) ), &m_parent, SIGNAL( MenuItemsChanged()) );
374+
375+ connect( m_menu_model.get(), SIGNAL( MenuInvalid() ), this, SLOT( MenuInvalid() ) );
376 }
377
378 void QtGMenuImporterPrivate::RefreshGActionGroup()
379@@ -181,8 +178,8 @@
380
381 QString action_path = action_path_it.value().path();
382 m_action_groups.push_back(
383- std::make_shared< QtGActionGroup > ( action_path_it.key(),
384- G_ACTION_GROUP( g_dbus_action_group_get( m_connection, m_service.toUtf8().constData(), action_path.toUtf8().constData() ) ) ) );
385+ std::make_shared<QtGActionGroup>(m_connection,
386+ action_path_it.key(), m_service, action_path));
387
388 auto action_group = m_action_groups.back();
389
390@@ -198,3 +195,14 @@
391
392 LinkMenuActions();
393 }
394+
395+void QtGMenuImporterPrivate::MenuInvalid()
396+{
397+ disconnect( &m_service_watcher, SIGNAL( serviceRegistered( const QString& ) ), this,
398+ SLOT( ServiceRegistered() ) );
399+
400+ disconnect( &m_service_watcher, SIGNAL( serviceUnregistered( const QString& ) ), this,
401+ SLOT( ServiceUnregistered() ) );
402+
403+ ServiceUnregistered();
404+}
405
406=== modified file 'libqtgmenu/internal/QtGMenuImporterPrivate.h'
407--- libqtgmenu/internal/QtGMenuImporterPrivate.h 2014-03-19 23:26:48 +0000
408+++ libqtgmenu/internal/QtGMenuImporterPrivate.h 2014-06-13 07:41:53 +0000
409@@ -43,8 +43,8 @@
410 const QMap<QString, QDBusObjectPath>& action_paths, QtGMenuImporter& parent );
411 virtual ~QtGMenuImporterPrivate();
412
413- GMenuModel* GetGMenuModel();
414- GActionGroup* GetGActionGroup( int index = 0);
415+ QSharedPointer<GMenuModel> GetGMenuModel();
416+ QSharedPointer<GActionGroup> GetGActionGroup( int index = 0);
417
418 std::shared_ptr< QMenu > GetQMenu();
419
420@@ -62,13 +62,14 @@
421
422 void RefreshGMenuModel();
423 void RefreshGActionGroup();
424+ void MenuInvalid();
425
426 private:
427 QDBusServiceWatcher m_service_watcher;
428
429 QtGMenuImporter& m_parent;
430
431- GDBusConnection* m_connection;
432+ QSharedPointer<GDBusConnection> m_connection;
433 QString m_service;
434 QDBusObjectPath m_menu_path;
435 QMap<QString, QDBusObjectPath> m_action_paths;
436
437=== modified file 'libqtgmenu/internal/QtGMenuModel.cpp'
438--- libqtgmenu/internal/QtGMenuModel.cpp 2014-03-19 23:26:48 +0000
439+++ libqtgmenu/internal/QtGMenuModel.cpp 2014-06-13 07:41:53 +0000
440@@ -18,44 +18,52 @@
441
442 #include <QtGMenuModel.h>
443 #include <QtGMenuUtils.h>
444+#include <QCoreApplication>
445+#include <QDBusConnection>
446+#include <QDBusConnectionInterface>
447 #include <QDebug>
448+#include <QProcess>
449+#include <QRegularExpression>
450
451 using namespace qtgmenu;
452
453-QtGMenuModel::QtGMenuModel( GMenuModel* model )
454- : QtGMenuModel( model, LinkType::Root, nullptr, 0 )
455-{
456-}
457+static const QRegularExpression SINGLE_UNDERSCORE("(?<![_])[_](?![_])");
458
459-QtGMenuModel::QtGMenuModel( GMenuModel* model, const QString& bus_name, const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths )
460- : QtGMenuModel( model, LinkType::Root, nullptr, 0 )
461+QtGMenuModel::QtGMenuModel( QSharedPointer<GDBusConnection> connection, const QString& bus_name,
462+ const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths )
463+ : QtGMenuModel( QSharedPointer<GMenuModel>(G_MENU_MODEL( g_dbus_menu_model_get( connection.data(),
464+ bus_name.toUtf8().constData(),
465+ menu_path.toUtf8().constData() ) ), &g_object_unref ),
466+ LinkType::Root, nullptr, 0 )
467 {
468+ m_connection = connection;
469 m_bus_name = bus_name;
470 m_menu_path = menu_path;
471 m_action_paths = action_paths;
472 }
473
474-QtGMenuModel::QtGMenuModel( GMenuModel* model, LinkType link_type, QtGMenuModel* parent, int index )
475+QtGMenuModel::QtGMenuModel( QSharedPointer<GMenuModel> model, LinkType link_type, QtGMenuModel* parent, int index )
476 : m_parent( parent ),
477 m_model( model ),
478- m_link_type( link_type )
479+ m_link_type( link_type ),
480+ m_menu( new QMenu() ),
481+ m_ext_menu( new QMenu() )
482 {
483 ConnectCallback();
484
485 if( m_parent )
486 {
487- m_parent->InsertChild( this, index );
488-
489+ m_connection = m_parent->m_connection;
490 m_bus_name = m_parent->m_bus_name;
491 m_menu_path = m_parent->m_menu_path;
492 m_action_paths = m_parent->m_action_paths;
493
494 gchar* label = NULL;
495- if( g_menu_model_get_item_attribute( m_parent->m_model, index,
496+ if( g_menu_model_get_item_attribute( m_parent->m_model.data(), index,
497 G_MENU_ATTRIBUTE_LABEL, "s", &label ) )
498 {
499 QString qlabel = QString::fromUtf8( label );
500- qlabel.replace( '_', '&' );
501+ qlabel.replace( SINGLE_UNDERSCORE, "&" );
502 g_free( label );
503
504 m_ext_menu->setTitle( qlabel );
505@@ -63,7 +71,7 @@
506
507 gchar* action_name = NULL;
508 QString qaction_name;
509- if( g_menu_model_get_item_attribute( m_parent->m_model, index,
510+ if( g_menu_model_get_item_attribute( m_parent->m_model.data(), index,
511 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
512 {
513 qaction_name = QString::fromUtf8( action_name );
514@@ -74,7 +82,7 @@
515
516 // if this model has a "commitLabel" property, it is a libhud parameterized action
517 gchar* commit_label = NULL;
518- if( g_menu_model_get_item_attribute( m_parent->m_model, index,
519+ if( g_menu_model_get_item_attribute( m_parent->m_model.data(), index,
520 "commitLabel", "s", &commit_label ) )
521 {
522 g_free( commit_label );
523@@ -100,7 +108,7 @@
524
525 if( m_model )
526 {
527- m_size = g_menu_model_get_n_items( m_model );
528+ m_size = g_menu_model_get_n_items( m_model.data() );
529 }
530
531 ChangeMenuItems( 0, m_size, 0 );
532@@ -112,23 +120,15 @@
533 {
534 if( m_size > 0 )
535 {
536- MenuItemsChangedCallback( m_model, 0, m_size, 0, this );
537+ ChangeMenuItems( 0, 0, m_size );
538 }
539 DisconnectCallback();
540- g_object_unref( m_model );
541 }
542
543- for( auto child : m_children )
544- {
545- delete child;
546- }
547 m_children.clear();
548-
549- delete m_menu;
550- delete m_ext_menu;
551 }
552
553-GMenuModel* QtGMenuModel::Model() const
554+QSharedPointer<GMenuModel> QtGMenuModel::Model() const
555 {
556 return m_model;
557 }
558@@ -138,24 +138,19 @@
559 return m_link_type;
560 }
561
562-int QtGMenuModel::Size() const
563-{
564- return m_size;
565-}
566-
567 QtGMenuModel* QtGMenuModel::Parent() const
568 {
569 return m_parent;
570 }
571
572-QtGMenuModel* QtGMenuModel::Child( int index ) const
573+QSharedPointer<QtGMenuModel> QtGMenuModel::Child( int index ) const
574 {
575 if( m_children.contains( index ) )
576 {
577 return m_children.value( index );
578 }
579
580- return nullptr;
581+ return QSharedPointer<QtGMenuModel>();
582 }
583
584 std::shared_ptr< QMenu > QtGMenuModel::GetQMenu()
585@@ -178,7 +173,10 @@
586 auto action_it = m_actions.find( action_name );
587 if( action_it != end( m_actions ) )
588 {
589- action_it->second->setEnabled( enabled );
590+ for( auto& action : action_it->second )
591+ {
592+ action->setEnabled( enabled );
593+ }
594 }
595 }
596
597@@ -187,78 +185,122 @@
598 auto action_it = m_actions.find( action_name );
599 if( action_it != end( m_actions ) )
600 {
601- action_it->second->setProperty( c_property_isParameterized, parameterized );
602+ for( auto& action : action_it->second )
603+ {
604+ action->setProperty( c_property_isParameterized, parameterized );
605+ }
606 }
607 }
608
609-QtGMenuModel* QtGMenuModel::CreateChild( QtGMenuModel* parent, GMenuModel* model, int index )
610+QSharedPointer<QtGMenuModel> QtGMenuModel::CreateChild( QtGMenuModel* parent_qtgmenu, QSharedPointer<GMenuModel> parent_gmenu, int child_index )
611 {
612- LinkType linkType( LinkType::SubMenu );
613- GMenuModel* link = g_menu_model_get_item_link( model, index, G_MENU_LINK_SUBMENU );
614-
615- if( !link )
616- {
617- linkType = LinkType::Section;
618- link = g_menu_model_get_item_link( model, index, G_MENU_LINK_SECTION );
619- }
620-
621- if( link )
622- {
623- return new QtGMenuModel( link, linkType, parent, index );
624- }
625-
626- return nullptr;
627+ QSharedPointer<QtGMenuModel> new_child;
628+
629+ GMenuLinkIter* link_it = g_menu_model_iterate_item_links( parent_gmenu.data(), child_index );
630+
631+ // get the first link, if it exists, create the child accordingly
632+ if( link_it && g_menu_link_iter_next( link_it ) )
633+ {
634+ // if link is a sub menu
635+ if( strcmp( g_menu_link_iter_get_name( link_it ), G_MENU_LINK_SUBMENU ) == 0 )
636+ {
637+ new_child.reset(
638+ new QtGMenuModel(
639+ QSharedPointer<GMenuModel>(
640+ g_menu_link_iter_get_value(link_it),
641+ &g_object_unref), LinkType::SubMenu,
642+ parent_qtgmenu, child_index));
643+ }
644+ // else if link is a section
645+ else if( strcmp( g_menu_link_iter_get_name( link_it ), G_MENU_LINK_SECTION ) == 0 )
646+ {
647+ new_child.reset(
648+ new QtGMenuModel(
649+ QSharedPointer<GMenuModel>(
650+ g_menu_link_iter_get_value(link_it),
651+ &g_object_unref), LinkType::Section,
652+ parent_qtgmenu, child_index));
653+ }
654+ }
655+
656+ g_object_unref( link_it );
657+ return new_child;
658 }
659
660 void QtGMenuModel::MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed,
661 gint added, gpointer user_data )
662 {
663 QtGMenuModel* self = reinterpret_cast< QtGMenuModel* >( user_data );
664+
665+ if( self->m_model != model )
666+ {
667+ qWarning() << "\"items-changed\" signal received from an unrecognised menu model";
668+ return;
669+ }
670+
671 self->ChangeMenuItems( index, added, removed );
672 }
673
674-void QtGMenuModel::ChangeMenuItems( int index, int added, int removed )
675+void QtGMenuModel::ChangeMenuItems( const int index, const int added, const int removed )
676 {
677+ const int n_items = g_menu_model_get_n_items( m_model.data() );
678+
679+ if( index < 0 || added < 0 || removed < 0 || index + added > n_items || index + removed > m_size )
680+ {
681+ ReportRecoverableError(index, added, removed);
682+ return;
683+ }
684+
685+ // process removed items first (see "items-changed" on the GMenuModel man page)
686 if( removed > 0 )
687 {
688+ // remove QAction from 'index' of our QMenu, 'removed' times
689 for( int i = 0; i < removed; ++i )
690 {
691 if( index < m_menu->actions().size() )
692 {
693 QAction* at_action = m_menu->actions().at( index );
694- ActionRemoved( at_action->property( c_property_actionName ).toString() );
695+ ActionRemoved( at_action->property( c_property_actionName ).toString(), at_action );
696 m_menu->removeAction( at_action );
697 }
698 }
699
700+ // update m_children
701 for( int i = index; i < m_size; ++i )
702 {
703- if( i <= ( index + removed ) )
704+ // remove children from index until ( index + removed )
705+ if( i < ( index + removed ) )
706 {
707- delete m_children.take( i );
708+ m_children.take( i );
709 }
710+ // shift children from ( index + removed ) to m_size into the now empty positions
711 else if( m_children.contains( i ) )
712 {
713 m_children.insert( i - removed, m_children.take( i ) );
714 }
715 }
716
717+ // update m_size
718 m_size -= removed;
719 }
720
721+ // now process added items
722 if( added > 0 )
723 {
724- // shift items up
725- for( int i = ( m_size + added ) - 1; i >= index; --i )
726+ // update m_children (start from the end and work backwards as not to overlap items as we shift them up)
727+ for( int i = m_size - 1; i >= index; --i )
728 {
729+ // shift 'added' items up from their current index to ( index + added )
730 if( m_children.contains( i ) )
731 {
732 m_children.insert( i + added, m_children.take( i ) );
733 }
734 }
735
736+ // update m_size
737 m_size += added;
738
739+ // now add a new QAction to our QMenu for each new item
740 for( int i = index; i < ( index + added ); ++i )
741 {
742 QAction* at_action = nullptr;
743@@ -267,32 +309,37 @@
744 at_action = m_menu->actions().at( i );
745 }
746
747- QtGMenuModel* model = CreateChild( this, m_model, i );
748+ // try first to create a child model
749+ QSharedPointer< QtGMenuModel > model = CreateChild( this, m_model, i );
750
751+ // if this is a menu item and not a model
752 if( !model )
753 {
754 QAction* new_action = CreateAction( i );
755 ActionAdded( new_action->property( c_property_actionName ).toString(), new_action );
756 m_menu->insertAction( at_action, new_action );
757 }
758+ // else if this is a section model
759 else if( model->Type() == LinkType::Section )
760 {
761+ InsertChild( model, i );
762 m_menu->insertSeparator( at_action );
763 }
764+ // else if this is a sub menu model
765 else if( model->Type() == LinkType::SubMenu )
766 {
767- m_menu->insertMenu( at_action, model->m_ext_menu );
768+ InsertChild( model, i );
769+ ActionAdded( model->m_ext_menu->menuAction()->property( c_property_actionName ).toString(),
770+ model->m_ext_menu->menuAction() );
771+ m_menu->insertMenu( at_action, model->m_ext_menu.data() );
772 }
773 }
774 }
775
776 // update external menu
777 UpdateExtQMenu();
778- if( m_link_type == LinkType::Section && m_parent )
779- {
780- m_parent->UpdateExtQMenu();
781- }
782
783+ // now tell the outside world that items have changed
784 emit MenuItemsChanged( this, index, removed, added );
785 }
786
787@@ -300,7 +347,7 @@
788 {
789 if( m_model && m_items_changed_handler == 0 )
790 {
791- m_items_changed_handler = g_signal_connect( m_model, "items-changed",
792+ m_items_changed_handler = g_signal_connect( m_model.data(), "items-changed",
793 G_CALLBACK( MenuItemsChangedCallback ), this );
794 }
795 }
796@@ -309,13 +356,13 @@
797 {
798 if( m_model && m_items_changed_handler != 0 )
799 {
800- g_signal_handler_disconnect( m_model, m_items_changed_handler );
801+ g_signal_handler_disconnect( m_model.data(), m_items_changed_handler );
802 }
803
804 m_items_changed_handler = 0;
805 }
806
807-void QtGMenuModel::InsertChild( QtGMenuModel* child, int index )
808+void QtGMenuModel::InsertChild( QSharedPointer<QtGMenuModel> child, int index )
809 {
810 if( m_children.contains( index ) )
811 {
812@@ -325,35 +372,27 @@
813 child->m_parent = this;
814 m_children.insert( index, child );
815
816- connect( child, SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ), this,
817+ connect( child.data(), SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ), this,
818 SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ) );
819
820- connect( child, SIGNAL( ActionTriggered( QString, bool ) ), this,
821+ connect( child.data(), SIGNAL( ActionTriggered( QString, bool ) ), this,
822 SIGNAL( ActionTriggered( QString, bool ) ) );
823-}
824-
825-int QtGMenuModel::ChildIndex( QtGMenuModel* child )
826-{
827- for( int i = 0; i < m_children.size(); ++i )
828- {
829- if( child == m_children[i] )
830- {
831- return i;
832- }
833- }
834-
835- return -1;
836+
837+ connect( child.data(), SIGNAL( MenuInvalid() ), this, SIGNAL( MenuInvalid() ) );
838+
839+ // emit signal informing subscribers that this child has added all of its menu items
840+ emit MenuItemsChanged( child.data(), 0, 0, child->m_size );
841 }
842
843 QAction* QtGMenuModel::CreateAction( int index )
844 {
845+ QAction* action = new QAction( m_menu.data() );
846+
847 // action label
848- QAction* action = new QAction( this );
849-
850 gchar* label = NULL;
851- if( g_menu_model_get_item_attribute( m_model, index, G_MENU_ATTRIBUTE_LABEL, "s", &label ) ) {
852+ if( g_menu_model_get_item_attribute( m_model.data(), index, G_MENU_ATTRIBUTE_LABEL, "s", &label ) ) {
853 QString qlabel = QString::fromUtf8( label );
854- qlabel.replace( '_', '&' );
855+ qlabel.replace( SINGLE_UNDERSCORE, "&" );
856 g_free( label );
857
858 action->setText( qlabel );
859@@ -361,7 +400,7 @@
860
861 // action name
862 gchar* action_name = NULL;
863- if( g_menu_model_get_item_attribute( m_model, index,
864+ if( g_menu_model_get_item_attribute( m_model.data(), index,
865 G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
866 {
867 QString qaction_name = QString::fromUtf8( action_name );
868@@ -378,7 +417,7 @@
869 action->setProperty( c_property_menuPath, m_menu_path );
870
871 // action icon
872- GVariant* icon = g_menu_model_get_item_attribute_value( m_model, index, G_MENU_ATTRIBUTE_ICON,
873+ GVariant* icon = g_menu_model_get_item_attribute_value( m_model.data(), index, G_MENU_ATTRIBUTE_ICON,
874 G_VARIANT_TYPE_VARIANT );
875
876 if( icon )
877@@ -388,7 +427,7 @@
878
879 // action shortcut
880 gchar* shortcut = NULL;
881- if( g_menu_model_get_item_attribute( m_model, index, "accel", "s", &shortcut ) )
882+ if( g_menu_model_get_item_attribute( m_model.data(), index, "accel", "s", &shortcut ) )
883 {
884 QString qshortcut = QString::fromUtf8( shortcut );
885 g_free( shortcut );
886@@ -398,7 +437,7 @@
887
888 // action shortcut
889 gchar* toolbar_item = NULL;
890- if( g_menu_model_get_item_attribute( m_model, index, c_property_hud_toolbar_item, "s", &toolbar_item ) )
891+ if( g_menu_model_get_item_attribute( m_model.data(), index, c_property_hud_toolbar_item, "s", &toolbar_item ) )
892 {
893 QString qtoolbar_item = QString::fromUtf8( toolbar_item );
894 g_free( toolbar_item );
895@@ -408,7 +447,7 @@
896
897 // action keywords
898 gchar* keywords = NULL;
899- if( g_menu_model_get_item_attribute( m_model, index, c_property_keywords, "s", &keywords ) )
900+ if( g_menu_model_get_item_attribute( m_model.data(), index, c_property_keywords, "s", &keywords ) )
901 {
902 QVariant qkeywords = QString::fromUtf8( keywords );
903 g_free( keywords );
904@@ -458,14 +497,13 @@
905
906 if( action->isSeparator() )
907 {
908- QtGMenuModel* child = Child( i );
909+ QSharedPointer<QtGMenuModel> child = Child( i );
910 if( !child || child->Type() != LinkType::Section )
911 {
912 continue;
913 }
914- QMenu* section = child->m_ext_menu;
915
916- for( QAction* sub_action : section->actions() )
917+ for( QAction* sub_action : child->m_ext_menu->actions() )
918 {
919 m_ext_menu->addAction( sub_action );
920 }
921@@ -485,6 +523,12 @@
922 m_ext_menu->removeAction( last_action );
923 }
924 }
925+
926+ // if this is a section within a parent menu, we need to update the parent menu as well
927+ if( m_link_type == LinkType::Section && m_parent )
928+ {
929+ m_parent->UpdateExtQMenu();
930+ }
931 }
932
933 void QtGMenuModel::ActionAdded( const QString& name, QAction* action )
934@@ -494,17 +538,205 @@
935 {
936 m_parent->ActionAdded( name, action );
937 }
938-
939- m_actions[name] = action;
940+ else
941+ {
942+ // check if the action name is already in our map
943+ if( m_actions.find( name ) != m_actions.end() )
944+ {
945+ // add the QAction pointer to the list of actions under this name
946+ m_actions[name].push_back( action );
947+ }
948+ else
949+ {
950+ // otherwise insert the new action into the map
951+ m_actions.insert( std::make_pair( name, std::vector< QAction* >{ action } ) );
952+ }
953+ }
954 }
955
956-void QtGMenuModel::ActionRemoved( const QString& name )
957+void QtGMenuModel::ActionRemoved( const QString& name, QAction* action )
958 {
959 // remove action from top menu's m_actions
960 if( m_parent )
961 {
962- m_parent->ActionRemoved( name );
963- }
964-
965- m_actions.erase( name );
966+ m_parent->ActionRemoved( name, action );
967+ }
968+ else
969+ {
970+ // check if this action is actually in our map
971+ if( m_actions.find( name ) != m_actions.end() )
972+ {
973+ // remove the QAction pointer from the list of actions under this name
974+ auto& actionList = m_actions[name];
975+ auto actionIt = std::find( actionList.begin(), actionList.end(), action );
976+
977+ if( actionIt != actionList.end())
978+ {
979+ actionList.erase( actionIt );
980+ }
981+
982+ // if there are no more references to this action, remove it from the map
983+ if( actionList.size() == 0 )
984+ {
985+ m_actions.erase( name );
986+ }
987+ }
988+ }
989+}
990+
991+static void write_pair(QIODevice& device, const QString& key, const QString& value, bool last = false)
992+{
993+ device.write(key.toUtf8());
994+ device.write("", 1);
995+ device.write(value.toUtf8());
996+ if( !last )
997+ {
998+ device.write("", 1);
999+ }
1000+
1001+ if( !value.isEmpty())
1002+ {
1003+ qWarning() << key << " =" << value;
1004+ }
1005+}
1006+
1007+void QtGMenuModel::ReportRecoverableError(const int index, const int added, const int removed)
1008+{
1009+ if( m_error_reported )
1010+ {
1011+ return;
1012+ }
1013+
1014+ // gmenumodel properties
1015+ int gmenu_item_count = 0;
1016+ QString gmenu_action_names;
1017+
1018+ gmenu_item_count = g_menu_model_get_n_items( m_model.data() );
1019+
1020+ qWarning() << "Illegal arguments when updating GMenuModel: position ="
1021+ << index << ", added =" << added << ", removed =" << removed
1022+ << ", size =" << gmenu_item_count;
1023+
1024+ for( int i = 0; i < gmenu_item_count; ++i )
1025+ {
1026+ gchar* action_name = NULL;
1027+ if( g_menu_model_get_item_attribute( m_model.data(), i,
1028+ G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
1029+ {
1030+ gmenu_action_names += action_name;
1031+ gmenu_action_names += ";";
1032+ g_free( action_name );
1033+ }
1034+ }
1035+
1036+ // parent model properties
1037+ QString parent_menu_label;
1038+ QString parent_menu_name;
1039+ QString parent_action_names;
1040+ QString parent_link_type;
1041+
1042+ if( m_parent )
1043+ {
1044+ parent_menu_label = m_parent->m_menu->menuAction()->text();
1045+ parent_menu_name = m_parent->m_menu->menuAction()->property( c_property_actionName ).toString();
1046+
1047+ for( QAction* action : m_parent->m_menu->actions() )
1048+ {
1049+ parent_action_names += action->property( c_property_actionName ).toString() + ";";
1050+ }
1051+
1052+ switch( m_parent->m_link_type )
1053+ {
1054+ case LinkType::Root:
1055+ parent_link_type = "root";
1056+ break;
1057+ case LinkType::Section:
1058+ parent_link_type = "section";
1059+ break;
1060+ case LinkType::SubMenu:
1061+ parent_link_type = "sub menu";
1062+ break;
1063+ }
1064+ }
1065+
1066+ // local model properties
1067+ QString menu_label;
1068+ QString menu_name;
1069+ QString action_names;
1070+ QString link_type;
1071+ QString action_paths;
1072+
1073+ menu_label = m_menu->menuAction()->text();
1074+ menu_name = m_menu->menuAction()->property( c_property_actionName ).toString();
1075+ for( QAction* action : m_menu->actions() )
1076+ {
1077+ action_names += action->property( c_property_actionName ).toString() + ";";
1078+ }
1079+
1080+ switch( m_link_type )
1081+ {
1082+ case LinkType::Root:
1083+ link_type = "root";
1084+ break;
1085+ case LinkType::Section:
1086+ link_type = "section";
1087+ break;
1088+ case LinkType::SubMenu:
1089+ link_type = "sub menu";
1090+ break;
1091+ }
1092+
1093+ for( auto const& action : m_action_paths )
1094+ {
1095+ action_paths += action.path() + ";";
1096+ }
1097+
1098+ uint sender_pid = QDBusConnection::sessionBus().interface()->servicePid(
1099+ m_bus_name);
1100+ if( sender_pid == 0 ) {
1101+ qWarning() << "Failed to read PID, cannot report error";
1102+ return;
1103+ }
1104+
1105+ QProcess recoverable;
1106+ recoverable.setProcessChannelMode(QProcess::ForwardedChannels);
1107+ recoverable.start("/usr/share/apport/recoverable_problem",
1108+ QStringList() << "-p" << QString::number(sender_pid));
1109+ if (recoverable.waitForStarted())
1110+ {
1111+ write_pair(recoverable, "DuplicateSignature", "GMenuModelItemsChangedInvalidIndex");
1112+ write_pair(recoverable, "BusName", m_bus_name);
1113+ write_pair(recoverable, "Position", QString::number(index));
1114+ write_pair(recoverable, "Added", QString::number(added));
1115+ write_pair(recoverable, "Removed", QString::number(removed));
1116+ write_pair(recoverable, "ItemCount", QString::number(gmenu_item_count));
1117+ write_pair(recoverable, "ActionNames", gmenu_action_names);
1118+
1119+ if ( m_parent )
1120+ {
1121+ write_pair(recoverable, "ParentMenuLabel", parent_menu_label);
1122+ write_pair(recoverable, "ParentMenuName", parent_menu_name);
1123+ write_pair(recoverable, "ParentActionNames", parent_action_names);
1124+ write_pair(recoverable, "ParentLinkType", parent_link_type);
1125+ }
1126+
1127+ write_pair(recoverable, "MenuLabel", menu_label);
1128+ write_pair(recoverable, "MenuName", menu_name);
1129+ write_pair(recoverable, "ActionNames", action_names);
1130+ write_pair(recoverable, "LinkType", link_type);
1131+
1132+ write_pair(recoverable, "MenuPath", m_menu_path);
1133+ write_pair(recoverable, "ActionPaths", action_paths, true);
1134+
1135+ recoverable.closeWriteChannel();
1136+ recoverable.waitForFinished();
1137+
1138+ m_error_reported = true;
1139+ }
1140+ else
1141+ {
1142+ qWarning() << "Failed to report recoverable error";
1143+ }
1144+
1145+ emit MenuInvalid();
1146 }
1147
1148=== modified file 'libqtgmenu/internal/QtGMenuModel.h'
1149--- libqtgmenu/internal/QtGMenuModel.h 2014-03-19 23:26:48 +0000
1150+++ libqtgmenu/internal/QtGMenuModel.h 2014-06-13 07:41:53 +0000
1151@@ -43,17 +43,14 @@
1152 Root, Section, SubMenu
1153 };
1154
1155- explicit QtGMenuModel( GMenuModel* model );
1156- QtGMenuModel( GMenuModel* model, const QString& bus_name, const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths );
1157+ QtGMenuModel( QSharedPointer<GDBusConnection> connection, const QString& bus_name, const QString& menu_path, const QMap<QString, QDBusObjectPath>& action_paths );
1158 virtual ~QtGMenuModel();
1159
1160- GMenuModel* Model() const;
1161+ QSharedPointer<GMenuModel> Model() const;
1162 LinkType Type() const;
1163
1164- int Size() const;
1165-
1166 QtGMenuModel* Parent() const;
1167- QtGMenuModel* Child( int index ) const;
1168+ QSharedPointer<QtGMenuModel> Child( int index ) const;
1169
1170 std::shared_ptr< QMenu > GetQMenu();
1171
1172@@ -68,6 +65,7 @@
1173 Q_SIGNALS:
1174 void MenuItemsChanged( QtGMenuModel* model, int index, int removed, int added );
1175 void ActionTriggered( QString action_name, bool checked );
1176+ void MenuInvalid();
1177
1178 public Q_SLOTS:
1179 void ActionEnabled( QString action_name, bool enabled );
1180@@ -77,20 +75,19 @@
1181 void ActionTriggered( bool );
1182
1183 private:
1184- QtGMenuModel( GMenuModel* model, LinkType link_type, QtGMenuModel* parent, int index );
1185+ QtGMenuModel( QSharedPointer<GMenuModel> model, LinkType link_type, QtGMenuModel* parent, int index );
1186
1187- static QtGMenuModel* CreateChild( QtGMenuModel* parent, GMenuModel* model, int index );
1188+ static QSharedPointer<QtGMenuModel> CreateChild( QtGMenuModel* parent_qtgmenu, QSharedPointer<GMenuModel> parent_gmenu, int child_index );
1189
1190 static void MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed, gint added,
1191 gpointer user_data );
1192
1193- void ChangeMenuItems( int index, int added, int removed );
1194+ void ChangeMenuItems( const int index, const int added, const int removed );
1195
1196 void ConnectCallback();
1197 void DisconnectCallback();
1198
1199- void InsertChild( QtGMenuModel* child, int index );
1200- int ChildIndex( QtGMenuModel* child );
1201+ void InsertChild( QSharedPointer<QtGMenuModel> child, int index );
1202
1203 QAction* CreateAction( int index );
1204
1205@@ -98,26 +95,32 @@
1206 void UpdateExtQMenu();
1207
1208 void ActionAdded( const QString& name, QAction* action );
1209- void ActionRemoved( const QString& name );
1210+ void ActionRemoved( const QString& name, QAction* action );
1211+
1212+ void ReportRecoverableError(const int index, const int added, const int removed);
1213
1214 private:
1215 QtGMenuModel* m_parent = nullptr;
1216- QMap< int, QtGMenuModel* > m_children;
1217+ QMap< int, QSharedPointer<QtGMenuModel>> m_children;
1218
1219- GMenuModel* m_model = nullptr;
1220+ QSharedPointer<GMenuModel> m_model;
1221 gulong m_items_changed_handler = 0;
1222
1223 LinkType m_link_type;
1224 int m_size = 0;
1225
1226- QMenu* m_menu = new QMenu();
1227- QMenu* m_ext_menu = new QMenu();
1228+ QScopedPointer<QMenu> m_menu;
1229+ QScopedPointer<QMenu> m_ext_menu;
1230
1231+ QSharedPointer<GDBusConnection> m_connection;
1232 QString m_bus_name;
1233 QString m_menu_path;
1234 QMap<QString, QDBusObjectPath> m_action_paths;
1235
1236- std::map< QString, QAction* > m_actions;
1237+ // a map of QActions indexed by their name and stored with a reference count
1238+ std::map< QString, std::vector< QAction* > > m_actions;
1239+
1240+ bool m_error_reported = false;
1241 };
1242
1243 } // namespace qtgmenu
1244
1245=== modified file 'service/DBusMenuCollector.cpp'
1246--- service/DBusMenuCollector.cpp 2014-04-01 13:13:04 +0000
1247+++ service/DBusMenuCollector.cpp 2014-06-13 07:41:53 +0000
1248@@ -82,25 +82,18 @@
1249 return !m_menuImporter.isNull();
1250 }
1251
1252-inline uint qHash(const QStringList &key, uint seed) {
1253- uint hash(0);
1254- for (const QString &s : key) {
1255- hash ^= qHash(s, seed);
1256- }
1257- return hash;
1258-}
1259-
1260 void DBusMenuCollector::openMenu(QMenu *menu, unsigned int &limit) {
1261+ --limit;
1262+ if (limit == 0) {
1263+ QString error = "Hit DBusMenu safety valve opening menu at " + m_service
1264+ + " " + m_path.path();
1265+ throw std::logic_error(error.toStdString());
1266+ }
1267+
1268 if (!menu) {
1269 return;
1270 }
1271
1272- if (limit == 0) {
1273- QString error = "Hit DBusMenu safety valve for menu at " + m_service
1274- + " " + m_path.path();
1275- throw std::logic_error(error.toStdString());
1276- }
1277-
1278 menu->aboutToShow();
1279
1280 for (int i(0); m_menuImporter && i < menu->actions().size(); ++i) {
1281@@ -114,15 +107,15 @@
1282
1283 QMenu *child(action->menu());
1284 if (child) {
1285- --limit;
1286 openMenu(child, limit);
1287 }
1288 }
1289 }
1290
1291 void DBusMenuCollector::hideMenu(QMenu *menu, unsigned int &limit) {
1292+ --limit;
1293 if (limit == 0) {
1294- QString error = "Hit DBusMenu safety valve for menu at " + m_service
1295+ QString error = "Hit DBusMenu safety valve closing menu at " + m_service
1296 + " " + m_path.path();
1297 throw std::logic_error(error.toStdString());
1298 }
1299@@ -131,7 +124,6 @@
1300 QAction *action = menu->actions().at(i);
1301 QMenu *child(action->menu());
1302 if (child) {
1303- --limit;
1304 hideMenu(child, limit);
1305 }
1306 }
1307
1308=== modified file 'service/HudServiceImpl.cpp'
1309--- service/HudServiceImpl.cpp 2014-02-18 13:35:12 +0000
1310+++ service/HudServiceImpl.cpp 2014-06-13 07:41:53 +0000
1311@@ -104,13 +104,24 @@
1312 QList<Suggestion> &suggestions, QDBusVariant &querykey) {
1313 QString sender(messageSender());
1314
1315- Query::Ptr query(m_legacyQueries[sender]);
1316+ QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries[sender]);
1317+ Query::Ptr query(entry.first);
1318+ QSharedPointer<QTimer> legacyTimeout(entry.second);
1319 if (query.isNull()) {
1320 query = createQuery(queryString, sender,
1321 Query::EmptyBehaviour::NO_SUGGESTIONS);
1322- m_legacyQueries[sender] = query;
1323+
1324+ legacyTimeout.reset(new QTimer());
1325+ legacyTimeout->setInterval(2000);
1326+ legacyTimeout->setSingleShot(true);
1327+ connect(legacyTimeout.data(), SIGNAL(timeout()), this,
1328+ SLOT(legacyTimeout()));
1329+
1330+ m_legacyQueries[sender] = qMakePair(query, legacyTimeout);
1331 } else {
1332 query->UpdateQuery(queryString);
1333+ legacyTimeout->stop();
1334+ legacyTimeout->setProperty("sender", QVariant());
1335 }
1336
1337 // The legacy API only allows you to search the current application
1338@@ -142,8 +153,13 @@
1339 Q_UNUSED(timestamp);
1340 QString sender(messageSender());
1341
1342- Query::Ptr query(m_legacyQueries.take(sender));
1343+ QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries.take(sender));
1344+ Query::Ptr query(entry.first);
1345+ QSharedPointer<QTimer> legacyTimeout(entry.second);
1346 if (!query.isNull()) {
1347+ legacyTimeout->stop();
1348+ legacyTimeout->setProperty("sender", QVariant());
1349+
1350 query->ExecuteCommand(itemKey, timestamp);
1351 closeQuery(query->path());
1352 }
1353@@ -156,8 +172,32 @@
1354 // We don't actually close legacy queries, or we'd be constructing
1355 // and destructing them during the search, due to the way that
1356 // Unity7 uses the API.
1357- Query::Ptr query(m_legacyQueries[sender]);
1358+ QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries[sender]);
1359+ Query::Ptr query(entry.first);
1360+ QSharedPointer<QTimer> legacyTimeout(entry.second);
1361 if (!query.isNull()) {
1362 query->UpdateQuery(QString());
1363- }
1364+ legacyTimeout->start();
1365+ legacyTimeout->setProperty("sender", sender);
1366+ }
1367+}
1368+
1369+void HudServiceImpl::legacyTimeout() {
1370+ QObject *timer(sender());
1371+ if (!timer) {
1372+ return;
1373+ }
1374+
1375+ QVariant from(timer->property("sender"));
1376+ if (from.isNull()) {
1377+ return;
1378+ }
1379+
1380+ QString sender(from.toString());
1381+ QPair<Query::Ptr, QSharedPointer<QTimer>> entry(m_legacyQueries.take(sender));
1382+ Query::Ptr query(entry.first);
1383+ if (query) {
1384+ closeQuery(query->path());
1385+ }
1386+
1387 }
1388
1389=== modified file 'service/HudServiceImpl.h'
1390--- service/HudServiceImpl.h 2014-02-18 13:35:12 +0000
1391+++ service/HudServiceImpl.h 2014-06-13 07:41:53 +0000
1392@@ -28,6 +28,7 @@
1393 #include <QDBusVariant>
1394 #include <QMap>
1395 #include <QScopedPointer>
1396+#include <QTimer>
1397
1398 class HudAdaptor;
1399
1400@@ -73,6 +74,9 @@
1401
1402 void CloseQuery(const QDBusVariant &querykey);
1403
1404+protected Q_SLOTS:
1405+ void legacyTimeout();
1406+
1407 protected:
1408 Query::Ptr createQuery(const QString &query, const QString &service,
1409 Query::EmptyBehaviour emptyBehaviour);
1410@@ -85,7 +89,7 @@
1411
1412 QMap<QDBusObjectPath, Query::Ptr> m_queries;
1413
1414- QMap<QString, Query::Ptr> m_legacyQueries;
1415+ QMap<QString, QPair<Query::Ptr, QSharedPointer<QTimer>>> m_legacyQueries;
1416
1417 QSharedPointer<ApplicationList> m_applicationList;
1418
1419
1420=== modified file 'tests/unit/qtgmenu/TestQtGMenu.cpp'
1421--- tests/unit/qtgmenu/TestQtGMenu.cpp 2014-03-20 06:46:57 +0000
1422+++ tests/unit/qtgmenu/TestQtGMenu.cpp 2014-06-13 07:41:53 +0000
1423@@ -71,28 +71,28 @@
1424
1425 int GetGMenuSize()
1426 {
1427- GMenuModel* menu = m_importer.GetGMenuModel();
1428+ QSharedPointer<GMenuModel> menu = m_importer.GetGMenuModel();
1429
1430 if( !menu )
1431 {
1432 return 0;
1433 }
1434
1435- gint item_count = g_menu_model_get_n_items( G_MENU_MODEL( menu ) );
1436+ gint item_count = g_menu_model_get_n_items( G_MENU_MODEL( menu.data() ) );
1437
1438 return item_count;
1439 }
1440
1441 int GetGActionCount()
1442 {
1443- GActionGroup* actions = m_importer.GetGActionGroup();
1444+ QSharedPointer<GActionGroup> actions = m_importer.GetGActionGroup();
1445
1446 if( !actions )
1447 {
1448 return 0;
1449 }
1450
1451- gchar** actions_list = g_action_group_list_actions( actions );
1452+ gchar** actions_list = g_action_group_list_actions( actions.data() );
1453
1454 int action_count = 0;
1455 while( actions_list[action_count] != nullptr )

Subscribers

People subscribed via source and target branches