2
* Copyright (C) 2013 Canonical, Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 3, as published
6
* by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful, but
9
* WITHOUT ANY WARRANTY; without even the implied warranties of
10
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
* PURPOSE. See the GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License along
14
* with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Author: Marcus Tomlinson <marcus.tomlinson@canonical.com>
19
#include <QtGMenuModel.h>
20
#include <QtGMenuUtils.h>
22
using namespace qtgmenu;
24
QtGMenuModel::QtGMenuModel( GMenuModel* model )
25
: QtGMenuModel( model, LinkType::Root, nullptr, 0 )
29
QtGMenuModel::QtGMenuModel( GMenuModel* model, const QString& bus_name, const QString& menu_path,
30
const QString& actions_path )
31
: QtGMenuModel( model, LinkType::Root, nullptr, 0 )
33
m_bus_name = bus_name;
34
m_menu_path = menu_path;
35
m_actions_path = actions_path;
38
QtGMenuModel::QtGMenuModel( GMenuModel* model, LinkType link_type, QtGMenuModel* parent, int index )
41
m_link_type( link_type )
47
m_parent->InsertChild( this, index );
49
m_bus_name = m_parent->m_bus_name;
50
m_menu_path = m_parent->m_menu_path;
51
m_actions_path = m_parent->m_actions_path;
54
if( g_menu_model_get_item_attribute( m_parent->m_model, index,
55
G_MENU_ATTRIBUTE_LABEL, "s", &label ) )
57
QString qlabel = QString::fromUtf8( label );
58
qlabel.replace( '_', '&' );
61
m_ext_menu->setTitle( qlabel );
64
gchar* action_name = NULL;
65
if( g_menu_model_get_item_attribute( m_parent->m_model, index,
66
G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
68
QString qaction_name = QString::fromUtf8( action_name );
69
g_free( action_name );
71
int name_length = qaction_name.size() - qaction_name.indexOf( '.' ) - 1;
72
qaction_name = qaction_name.right( name_length );
74
m_ext_menu->menuAction()->setProperty( c_property_actionName, qaction_name );
77
// if this model has a "commitLabel" property, it is a libhud parameterized action
78
gchar* commit_label = NULL;
79
if( g_menu_model_get_item_attribute( m_parent->m_model, index,
80
"commitLabel", "s", &commit_label ) )
82
g_free( commit_label );
85
m_ext_menu->menuAction()->setProperty( c_property_isParameterized, true );
88
m_ext_menu->menuAction()->setProperty( c_property_busName, m_bus_name );
89
m_ext_menu->menuAction()->setProperty( c_property_menuPath, m_menu_path );
90
m_ext_menu->menuAction()->setProperty( c_property_actionsPath, m_actions_path );
96
m_size = g_menu_model_get_n_items( m_model );
99
ChangeMenuItems( 0, m_size, 0 );
102
QtGMenuModel::~QtGMenuModel()
108
MenuItemsChangedCallback( m_model, 0, m_size, 0, this );
110
DisconnectCallback();
111
g_object_unref( m_model );
114
for( auto child : m_children )
124
GMenuModel* QtGMenuModel::Model() const
129
QtGMenuModel::LinkType QtGMenuModel::Type() const
134
int QtGMenuModel::Size() const
139
QtGMenuModel* QtGMenuModel::Parent() const
144
QtGMenuModel* QtGMenuModel::Child( int index ) const
146
if( m_children.contains( index ) )
148
return m_children.value( index );
154
std::shared_ptr< QMenu > QtGMenuModel::GetQMenu()
156
auto top_menu = std::make_shared< QMenu >();
158
AppendQMenu( top_menu );
163
void QtGMenuModel::ActionTriggered( bool checked )
165
QAction* action = dynamic_cast< QAction* >( QObject::sender() );
166
emit ActionTriggered( action->property( c_property_actionName ).toString(), checked );
169
void QtGMenuModel::ActionEnabled( QString action_name, bool enabled )
171
QAction* action = FindAction( action_name );
174
action->setEnabled( enabled );
178
void QtGMenuModel::ActionParameterized( QString action_name, bool parameterized )
180
QAction* action = FindAction( action_name );
183
action->setProperty( c_property_isParameterized, parameterized );
187
QtGMenuModel* QtGMenuModel::CreateChild( QtGMenuModel* parent, GMenuModel* model, int index )
189
LinkType linkType( LinkType::SubMenu );
190
GMenuModel* link = g_menu_model_get_item_link( model, index, G_MENU_LINK_SUBMENU );
194
linkType = LinkType::Section;
195
link = g_menu_model_get_item_link( model, index, G_MENU_LINK_SECTION );
200
return new QtGMenuModel( link, linkType, parent, index );
206
void QtGMenuModel::MenuItemsChangedCallback( GMenuModel* model, gint index, gint removed,
207
gint added, gpointer user_data )
209
QtGMenuModel* self = reinterpret_cast< QtGMenuModel* >( user_data );
210
self->ChangeMenuItems( index, added, removed );
213
void QtGMenuModel::ChangeMenuItems( int index, int added, int removed )
217
for( int i = 0; i < removed; ++i )
219
if( index < m_menu->actions().size() )
221
QAction* at_action = m_menu->actions().at( index );
222
m_menu->removeAction( at_action );
226
for( int i = index; i < m_size; ++i )
228
if( i <= ( index + removed ) )
230
delete m_children.take( i );
232
else if( m_children.contains( i ) )
234
m_children.insert( i - removed, m_children.take( i ) );
244
for( int i = ( m_size + added ) - 1; i >= index; --i )
246
if( m_children.contains( i ) )
248
m_children.insert( i + added, m_children.take( i ) );
254
for( int i = index; i < ( index + added ); ++i )
256
QAction* at_action = nullptr;
257
if( i < m_menu->actions().size() )
259
at_action = m_menu->actions().at( i );
262
QtGMenuModel* model = CreateChild( this, m_model, i );
266
m_menu->insertAction( at_action, CreateAction( i ) );
268
else if( model->Type() == LinkType::Section )
270
m_menu->insertSeparator( at_action );
272
else if( model->Type() == LinkType::SubMenu )
274
m_menu->insertMenu( at_action, model->m_ext_menu );
279
// update external menu
281
if( m_link_type == LinkType::Section && m_parent )
283
m_parent->UpdateExtQMenu();
286
emit MenuItemsChanged( this, index, removed, added );
289
void QtGMenuModel::ConnectCallback()
291
if( m_model && m_items_changed_handler == 0 )
293
m_items_changed_handler = g_signal_connect( m_model, "items-changed",
294
G_CALLBACK( MenuItemsChangedCallback ), this );
298
void QtGMenuModel::DisconnectCallback()
300
if( m_model && m_items_changed_handler != 0 )
302
g_signal_handler_disconnect( m_model, m_items_changed_handler );
305
m_items_changed_handler = 0;
308
void QtGMenuModel::InsertChild( QtGMenuModel* child, int index )
310
if( m_children.contains( index ) )
315
child->m_parent = this;
316
m_children.insert( index, child );
318
connect( child, SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ), this,
319
SIGNAL( MenuItemsChanged( QtGMenuModel*, int, int, int ) ) );
321
connect( child, SIGNAL( ActionTriggered( QString, bool ) ), this,
322
SIGNAL( ActionTriggered( QString, bool ) ) );
325
int QtGMenuModel::ChildIndex( QtGMenuModel* child )
327
for( int i = 0; i < m_children.size(); ++i )
329
if( child == m_children[i] )
338
QAction* QtGMenuModel::CreateAction( int index )
341
QAction* action = new QAction( this );
344
if( g_menu_model_get_item_attribute( m_model, index, G_MENU_ATTRIBUTE_LABEL, "s", &label ) ) {
345
QString qlabel = QString::fromUtf8( label );
346
qlabel.replace( '_', '&' );
349
action->setText( qlabel );
353
gchar* action_name = NULL;
354
if( g_menu_model_get_item_attribute( m_model, index,
355
G_MENU_ATTRIBUTE_ACTION, "s", &action_name ) )
357
QString qaction_name = QString::fromUtf8( action_name );
358
g_free( action_name );
360
int name_length = qaction_name.size() - qaction_name.indexOf( '.' ) - 1;
361
qaction_name = qaction_name.right( name_length );
363
action->setProperty( c_property_actionName, qaction_name );
366
// is parameterized (set false by default until signal received)
367
action->setProperty( c_property_isParameterized, false );
370
action->setProperty( c_property_busName, m_bus_name );
371
action->setProperty( c_property_menuPath, m_menu_path );
372
action->setProperty( c_property_actionsPath, m_actions_path );
375
GVariant* icon = g_menu_model_get_item_attribute_value( m_model, index, G_MENU_ATTRIBUTE_ICON,
376
G_VARIANT_TYPE_VARIANT );
380
g_variant_unref( icon );
384
gchar* shortcut = NULL;
385
if( g_menu_model_get_item_attribute( m_model, index, "accel", "s", &shortcut ) )
387
QString qshortcut = QString::fromUtf8( shortcut );
390
action->setShortcut( QtGMenuUtils::QStringToQKeySequence( qshortcut ) );
394
gchar* toolbar_item = NULL;
395
if( g_menu_model_get_item_attribute( m_model, index, c_property_hud_toolbar_item, "s", &toolbar_item ) )
397
QString qtoolbar_item = QString::fromUtf8( toolbar_item );
398
g_free( toolbar_item );
400
action->setProperty( c_property_hud_toolbar_item, qtoolbar_item );
404
gchar* keywords = NULL;
405
if( g_menu_model_get_item_attribute( m_model, index, c_property_keywords, "s", &keywords ) )
407
QVariant qkeywords = QString::fromUtf8( keywords );
410
action->setProperty( c_property_keywords, qkeywords );
414
connect( action, SIGNAL( triggered( bool ) ), this, SLOT( ActionTriggered( bool ) ) );
419
QAction* QtGMenuModel::FindAction( QString name )
421
if( m_ext_menu->menuAction()->property( c_property_actionName ) == name )
423
return m_ext_menu->menuAction();
426
for( QAction* action : m_menu->actions() )
428
if( action->property( c_property_actionName ) == name )
434
for( QtGMenuModel* child : m_children )
436
QAction* action = child->FindAction( name );
446
void QtGMenuModel::AppendQMenu( std::shared_ptr< QMenu > top_menu )
448
if( m_link_type == LinkType::Root )
450
for( QAction* action : m_ext_menu->actions() )
452
if( !action->menu() )
454
top_menu->addAction( action );
458
else if( m_link_type == LinkType::SubMenu )
460
top_menu->addAction( m_ext_menu->menuAction() );
463
if( m_link_type != LinkType::SubMenu )
465
for( auto& child : m_children )
467
child->AppendQMenu( top_menu );
472
void QtGMenuModel::UpdateExtQMenu()
476
for( int i = 0; i < m_menu->actions().size(); ++i )
478
QAction* action = m_menu->actions().at( i );
480
if( action->isSeparator() )
482
QtGMenuModel* child = Child( i );
483
if( !child || child->Type() != LinkType::Section )
487
QMenu* section = child->m_ext_menu;
489
for( QAction* sub_action : section->actions() )
491
m_ext_menu->addAction( sub_action );
493
m_ext_menu->addSeparator();
497
m_ext_menu->addAction( action );
501
if( m_ext_menu->actions().size() > 0 )
503
QAction* last_action = m_ext_menu->actions().last();
504
if( last_action && last_action->isSeparator() )
506
m_ext_menu->removeAction( last_action );