/*
* 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 of the License.
*
* 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: Ryan Lortie
* William Hua
*/
#include "unity-gtk-menu-section-private.h"
#ifndef G_MENU_ATTRIBUTE_ACCEL
#define G_MENU_ATTRIBUTE_ACCEL "accel"
#endif
G_DEFINE_TYPE (UnityGtkMenuSection,
unity_gtk_menu_section,
G_TYPE_MENU_MODEL);
static gint
g_uintcmp (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
}
static void
unity_gtk_menu_section_set_parent_shell (UnityGtkMenuSection *section,
UnityGtkMenuShell *parent_shell)
{
g_return_if_fail (UNITY_GTK_IS_MENU_SECTION (section));
section->parent_shell = parent_shell;
}
static void
unity_gtk_menu_section_dispose (GObject *object)
{
UnityGtkMenuSection *section;
g_return_if_fail (UNITY_GTK_IS_MENU_SECTION (object));
section = UNITY_GTK_MENU_SECTION (object);
unity_gtk_menu_section_set_parent_shell (section, NULL);
G_OBJECT_CLASS (unity_gtk_menu_section_parent_class)->dispose (object);
}
static gboolean
unity_gtk_menu_section_is_mutable (GMenuModel *model)
{
g_return_val_if_fail (UNITY_GTK_IS_MENU_SECTION (model), TRUE);
return TRUE;
}
static gint
unity_gtk_menu_section_get_n_items (GMenuModel *model)
{
UnityGtkMenuSection *section;
GSequenceIter *begin;
GSequenceIter *end;
g_return_val_if_fail (UNITY_GTK_IS_MENU_SECTION (model), 0);
section = UNITY_GTK_MENU_SECTION (model);
begin = unity_gtk_menu_section_get_begin_iter (section);
end = unity_gtk_menu_section_get_end_iter (section);
g_return_val_if_fail (begin != NULL && end != NULL, 0);
return g_sequence_iter_get_position (end) - g_sequence_iter_get_position (begin);
}
static void
unity_gtk_menu_section_get_item_attributes (GMenuModel *model,
gint item_index,
GHashTable **attributes)
{
UnityGtkMenuSection *section;
UnityGtkMenuShell *parent_shell;
UnityGtkMenuItem *item;
GSequenceIter *iter;
guint index;
const gchar *label;
GIcon *icon;
UnityGtkAction *action;
g_return_if_fail (UNITY_GTK_IS_MENU_SECTION (model));
g_return_if_fail (attributes != NULL);
section = UNITY_GTK_MENU_SECTION (model);
parent_shell = section->parent_shell;
g_return_if_fail (parent_shell != NULL);
iter = unity_gtk_menu_section_get_iter (section, item_index);
index = GPOINTER_TO_UINT (g_sequence_get (iter));
item = unity_gtk_menu_shell_get_item (parent_shell, index);
label = unity_gtk_menu_item_get_label (item);
icon = unity_gtk_menu_item_get_icon (item);
action = item->action;
*attributes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_variant_unref);
if (label != NULL)
g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_LABEL, g_variant_ref_sink (g_variant_new_string (label)));
if (icon != NULL)
{
g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ICON, g_icon_serialize (icon));
g_object_unref (icon);
}
if (action != NULL && action->name != NULL)
{
gchar *name = g_strdup_printf ("unity.%s", action->name);
GVariant *variant = g_variant_ref_sink (g_variant_new_string (name));
g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ACTION, variant);
if (action->items_by_name != NULL)
{
GHashTableIter iter;
gpointer key;
gpointer value;
const gchar *target = NULL;
g_hash_table_iter_init (&iter, action->items_by_name);
while (target == NULL && g_hash_table_iter_next (&iter, &key, &value))
if (value == item)
target = key;
if (target != NULL)
g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_TARGET, g_variant_ref_sink (g_variant_new_string (target)));
}
else if (unity_gtk_menu_item_get_draw_as_radio (item))
g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_TARGET, g_variant_ref_sink (g_variant_new_string (action->name)));
g_free (name);
}
if (item->menu_item != NULL)
{
gchar *accel_name = NULL;
const gchar *accel_path = gtk_menu_item_get_accel_path (item->menu_item);
if (accel_path != NULL)
{
GtkAccelKey accel_key;
if (gtk_accel_map_lookup_entry (accel_path, &accel_key))
accel_name = gtk_accelerator_name (accel_key.accel_key, accel_key.accel_mods);
}
if (accel_name == NULL)
accel_name = g_strdup (gtk_menu_item_get_nth_label_label (item->menu_item, 1));
if (accel_name != NULL)
g_hash_table_insert (*attributes, G_MENU_ATTRIBUTE_ACCEL, g_variant_ref_sink (g_variant_new_string (accel_name)));
g_free (accel_name);
}
}
static void
unity_gtk_menu_section_get_item_links (GMenuModel *model,
gint item_index,
GHashTable **links)
{
UnityGtkMenuSection *section;
UnityGtkMenuShell *parent_shell;
UnityGtkMenuItem *item;
GSequenceIter *iter;
guint index;
UnityGtkMenuShell *child_shell;
g_return_if_fail (UNITY_GTK_IS_MENU_SECTION (model));
g_return_if_fail (links != NULL);
section = UNITY_GTK_MENU_SECTION (model);
parent_shell = section->parent_shell;
g_return_if_fail (parent_shell != NULL);
iter = unity_gtk_menu_section_get_iter (section, item_index);
index = GPOINTER_TO_UINT (g_sequence_get (iter));
item = unity_gtk_menu_shell_get_item (parent_shell, index);
child_shell = unity_gtk_menu_item_get_child_shell (item);
*links = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
if (child_shell != NULL)
g_hash_table_insert (*links, G_MENU_LINK_SUBMENU, g_object_ref (child_shell));
}
static void
unity_gtk_menu_section_class_init (UnityGtkMenuSectionClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GMenuModelClass *menu_model_class = G_MENU_MODEL_CLASS (klass);
object_class->dispose = unity_gtk_menu_section_dispose;
menu_model_class->is_mutable = unity_gtk_menu_section_is_mutable;
menu_model_class->get_n_items = unity_gtk_menu_section_get_n_items;
menu_model_class->get_item_attributes = unity_gtk_menu_section_get_item_attributes;
menu_model_class->get_item_links = unity_gtk_menu_section_get_item_links;
}
static void
unity_gtk_menu_section_init (UnityGtkMenuSection *self)
{
}
UnityGtkMenuSection *
unity_gtk_menu_section_new (UnityGtkMenuShell *parent_shell,
guint section_index)
{
UnityGtkMenuSection *section = g_object_new (UNITY_GTK_TYPE_MENU_SECTION, NULL);
unity_gtk_menu_section_set_parent_shell (section, parent_shell);
section->section_index = section_index;
return section;
}
GSequenceIter *
unity_gtk_menu_section_get_begin_iter (UnityGtkMenuSection *section)
{
UnityGtkMenuShell *parent_shell;
GSequence *separator_indices;
GSequence *visible_indices;
GSequenceIter *separator_iter;
GSequenceIter *visible_iter;
guint section_index;
g_return_val_if_fail (UNITY_GTK_IS_MENU_SECTION (section), NULL);
parent_shell = section->parent_shell;
g_return_val_if_fail (parent_shell != NULL, NULL);
separator_indices = unity_gtk_menu_shell_get_separator_indices (parent_shell);
visible_indices = unity_gtk_menu_shell_get_visible_indices (parent_shell);
section_index = section->section_index;
if (section_index > 0)
separator_iter = g_sequence_get_iter_at_pos (separator_indices, section_index - 1);
else
separator_iter = NULL;
if (separator_iter != NULL)
{
gpointer separator_index = g_sequence_get (separator_iter);
visible_iter = g_sequence_lookup (visible_indices, separator_index, g_uintcmp, NULL);
visible_iter = g_sequence_iter_next (visible_iter);
}
else
visible_iter = g_sequence_get_begin_iter (visible_indices);
return visible_iter;
}
GSequenceIter *
unity_gtk_menu_section_get_end_iter (UnityGtkMenuSection *section)
{
UnityGtkMenuShell *parent_shell;
GSequence *separator_indices;
GSequence *visible_indices;
GSequenceIter *separator_iter;
GSequenceIter *visible_iter;
g_return_val_if_fail (UNITY_GTK_IS_MENU_SECTION (section), NULL);
parent_shell = section->parent_shell;
g_return_val_if_fail (parent_shell != NULL, NULL);
separator_indices = unity_gtk_menu_shell_get_separator_indices (parent_shell);
visible_indices = unity_gtk_menu_shell_get_visible_indices (parent_shell);
separator_iter = g_sequence_get_iter_at_pos (separator_indices, section->section_index);
if (g_sequence_iter_is_end (separator_iter))
separator_iter = NULL;
if (separator_iter != NULL)
visible_iter = g_sequence_lookup (visible_indices, g_sequence_get (separator_iter), g_uintcmp, NULL);
else
visible_iter = g_sequence_get_end_iter (visible_indices);
return visible_iter;
}
GSequenceIter *
unity_gtk_menu_section_get_iter (UnityGtkMenuSection *section,
guint index)
{
g_return_val_if_fail (UNITY_GTK_IS_MENU_SECTION (section), NULL);
return g_sequence_iter_move (unity_gtk_menu_section_get_begin_iter (section), index);
}
void
unity_gtk_menu_section_print (UnityGtkMenuSection *section,
guint indent)
{
gchar *space;
g_return_if_fail (section == NULL || UNITY_GTK_IS_MENU_SECTION (section));
space = g_strnfill (indent, ' ');
if (section != NULL)
{
g_print ("%s%u (%s *) %p\n", space, section->section_index, G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (section)), section);
if (section->parent_shell != NULL)
g_print ("%s (%s *) %p\n", space, G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (section->parent_shell)), section->parent_shell);
}
else
g_print ("%sNULL\n", space);
g_free (space);
}