/*
* Copyright 2012 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
* Authors:
* Renato Araujo Oliveira Filho
* Olivier Tilloy
*/
extern "C" {
#include
}
#include "qmenumodel.h"
#include "menunode.h"
#include "converter.h"
#include
#include
const QEvent::Type MenuModelEvent::eventType = static_cast(QEvent::registerEventType());
/*!
\qmltype QMenuModel
\brief The QMenuModel class implements the base list model for menus
\b {This component is under heavy development.}
This is a abstracted class used by \l QDBusMenuModel.
*/
/*! \internal */
QMenuModel::QMenuModel(GMenuModel *other, QObject *parent)
: QAbstractItemModel(parent),
m_root(0)
{
setMenuModel(other);
}
/*! \internal */
QMenuModel::~QMenuModel()
{
clearModel();
}
/*! \internal */
void QMenuModel::setMenuModel(GMenuModel *other)
{
if ((m_root != 0) && (m_root->model() == other)) {
return;
}
beginResetModel();
clearModel();
if (other) {
m_root = new MenuNode("", other, 0, 0, this);
}
endResetModel();
}
/*! \internal */
void QMenuModel::clearModel()
{
if (m_root) {
delete m_root;
m_root = NULL;
}
}
/*! \internal */
QHash QMenuModel::roleNames() const
{
static QHash roles;
if (roles.isEmpty()) {
roles[Action] = "action";
roles[Label] = "label";
roles[Extra] = "extra";
roles[Depth] = "depth";
roles[hasSection] = "hasSection";
roles[hasSubMenu] = "hasSubMenu";
}
return roles;
}
/*! \internal */
QModelIndex QMenuModel::index(int row, int column, const QModelIndex &parent) const
{
MenuNode *node = nodeFromIndex(parent);
if (node == 0) {
return QModelIndex();
}
if (parent.isValid()) {
MenuNode *child = node->child(parent.row());
if (child) {
node = child;
}
}
return createIndex(row, column, node);
}
/*! \internal */
QModelIndex QMenuModel::parent(const QModelIndex &index) const
{
if (index.isValid() && index.internalPointer()) {
MenuNode *node = nodeFromIndex(index);
if (node->parent()) {
return createIndex(node->position(), 0, node->parent());
}
}
return QModelIndex();
}
/*! \internal */
QVariant QMenuModel::data(const QModelIndex &index, int role) const
{
QVariant attribute;
if (!index.isValid()) {
return attribute;
}
MenuNode *node = nodeFromIndex(index);
int row = node ? node->realPosition(index.row()) : -1;
if (row >= 0) {
switch (role) {
case Action:
attribute = getStringAttribute(node, row, G_MENU_ATTRIBUTE_ACTION);
break;
case Qt::DisplayRole:
case Label:
attribute = getStringAttribute(node, row, G_MENU_ATTRIBUTE_LABEL);
break;
case Extra:
attribute = getExtraProperties(node, row);
break;
case hasSection:
attribute = QVariant(hasLink(node, row, G_MENU_LINK_SECTION));
break;
case hasSubMenu:
attribute = QVariant(hasLink(node, row, G_MENU_LINK_SUBMENU));
break;
case Depth:
attribute = QVariant(node->depth());
break;
default:
break;
}
}
return attribute;
}
/*! \internal */
int QMenuModel::rowCount(const QModelIndex &index) const
{
if (index.isValid()) {
MenuNode *node = nodeFromIndex(index);
if (node) {
MenuNode *child = node->child(index.row());
if (child) {
return child->size();
}
}
return 0;
}
if (m_root) {
return m_root->size();
}
return 0;
}
/*! \internal */
int QMenuModel::columnCount(const QModelIndex &) const
{
return 1;
}
/*! \internal */
QVariant QMenuModel::getStringAttribute(MenuNode *node,
int row,
const QString &attribute) const
{
QVariant result;
gchar* value = NULL;
g_menu_model_get_item_attribute(node->model(),
row,
attribute.toUtf8().data(),
"s", &value);
if (value) {
result = QVariant(QString::fromUtf8(value));
g_free(value);
}
return result;
}
/*! \internal */
QVariant QMenuModel::getExtraProperties(MenuNode *node, int row) const
{
GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(node->model(), row);
if (iter == NULL) {
return QVariant();
}
QVariantMap extra;
const gchar *attrName = NULL;
GVariant *value = NULL;
while (g_menu_attribute_iter_get_next (iter, &attrName, &value)) {
if (strncmp("x-", attrName, 2) == 0) {
extra.insert(parseExtraPropertyName(attrName),
Converter::toQVariant(value));
}
}
return extra;
}
bool QMenuModel::event(QEvent* e)
{
if (e->type() == MenuNodeItemChangeEvent::eventType) {
MenuNodeItemChangeEvent *mnice = static_cast(e);
QModelIndex index = indexFromNode(mnice->node);
if (mnice->removed > 0) {
beginRemoveRows(index, mnice->position, mnice->position + mnice->removed - 1);
mnice->node->commitOperation();
endRemoveRows();
}
if (mnice->added > 0) {
beginInsertRows(index, mnice->position, mnice->position + mnice->added - 1);
mnice->node->commitOperation();
endInsertRows();
}
return true;
} else if (e->type() == MenuModelEvent::eventType) {
MenuModelEvent *mme = static_cast(e);
setMenuModel(mme->model);
return true;
}
return QAbstractItemModel::event(e);
}
/*! \internal */
QModelIndex QMenuModel::indexFromNode(MenuNode *node) const
{
if (node == m_root) {
return QModelIndex();
}
return createIndex(node->position(), 0, node);
}
/*! \internal */
MenuNode *QMenuModel::nodeFromIndex(const QModelIndex &index) const
{
MenuNode *node = 0;
if (index.isValid()) {
node = reinterpret_cast(index.internalPointer());
} else {
node = m_root;
}
return node;
}
/*! \internal */
QString QMenuModel::parseExtraPropertyName(const QString &name) const
{
QString newName(name);
if (name.startsWith("x-")) {
newName = name.mid(2);
}
return newName.replace("-", "_");
}
GMenuModel *QMenuModel::menuModel() const
{
return m_root->model();
}
bool QMenuModel::hasLink(MenuNode *node, int row, const QString &linkType) const
{
MenuNode *child = node->child(row);
return (child && (child->linkType() == linkType));
}
MenuModelEvent::MenuModelEvent(GMenuModel* _model)
: QEvent(MenuModelEvent::eventType),
model(_model)
{
if (model) {
g_object_ref(model);
}
}
MenuModelEvent::~MenuModelEvent()
{
if (model) {
g_object_unref(model);
}
}