/*
* 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
#ifndef G_MENU_ATTRIBUTE_ACCEL_TEXT
#define G_MENU_ATTRIBUTE_ACCEL_TEXT "x-canonical-accel"
#endif
#ifndef G_MENU_ATTRIBUTE_SUBMENU_ACTION
#define G_MENU_ATTRIBUTE_SUBMENU_ACTION "submenu-action"
#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 gboolean g_closure_equal(GtkAccelKey *key, GClosure *closure, gpointer data)
{
return closure == data;
}
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 char *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)
{
if (action->name != NULL)
{
char *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 char *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 (action->subname != NULL)
{
char *subname = g_strdup_printf("unity.%s", action->subname);
GVariant *variant = g_variant_ref_sink(g_variant_new_string(subname));
g_hash_table_insert(*attributes, G_MENU_ATTRIBUTE_SUBMENU_ACTION, variant);
g_free(subname);
}
}
if (item->menu_item != NULL)
{
char *accel_name = NULL;
const char *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)
{
GList *closures =
gtk_widget_list_accel_closures(GTK_WIDGET(item->menu_item));
GList *iter;
for (iter = closures; iter != NULL && accel_name == NULL;
iter = g_list_next(iter))
{
GClosure *closure = iter->data;
GtkAccelGroup *accel_group =
gtk_accel_group_from_accel_closure(closure);
if (accel_group != NULL)
{
GtkAccelKey *accel_key =
gtk_accel_group_find(accel_group,
g_closure_equal,
closure);
if (accel_key != NULL)
accel_name =
gtk_accelerator_name(accel_key->accel_key,
accel_key->accel_mods);
}
}
g_list_free(closures);
}
if (accel_name != NULL)
g_hash_table_insert(*attributes,
G_MENU_ATTRIBUTE_ACCEL,
g_variant_ref_sink(g_variant_new_string(accel_name)));
else
{
#if GTK_MAJOR_VERSION == 2
/* LP: #1208019 */
GtkLabel *accel_label = gtk_menu_item_get_nth_label(item->menu_item, 0);
if (GTK_IS_ACCEL_LABEL(accel_label))
{
/* Eclipse uses private API. */
if (GTK_ACCEL_LABEL(accel_label)->accel_string != NULL)
accel_name =
g_strdup(GTK_ACCEL_LABEL(accel_label)->accel_string);
}
#endif
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_TEXT,
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)
{
char *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);
}