/*
* glade-gtk-popovermenu.c - GladeWidgetAdaptor for GtkPopoverMenu
*
* Copyright (C) 2014 Red Hat, Inc
*
* Authors:
* Matthias Clasen <mclasen@redhat.com>
*
* This library 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; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <config.h>
#include <glib/gi18n-lib.h>
#include <gladeui/glade.h>
#include "glade-popover-menu-editor.h"
static void
glade_gtk_popover_menu_parse_finished (GladeProject * project,
GObject * object)
{
GladeWidget *gbox;
gint submenus;
gbox = glade_widget_get_from_gobject (object);
glade_widget_property_get (gbox, "submenus", &submenus);
glade_widget_property_set (gbox, "submenus", submenus);
}
static void
glade_gtk_popover_menu_selection_changed (GladeProject * project,
GladeWidget * gwidget)
{
GList *list;
GtkWidget *page, *sel_widget;
GtkWidget *popover = GTK_WIDGET (glade_widget_get_object (gwidget));
if ((list = glade_project_selection_get (project)) != NULL &&
g_list_length (list) == 1)
{
sel_widget = list->data;
if (GTK_IS_WIDGET (sel_widget) &&
gtk_widget_is_ancestor (sel_widget, popover))
{
GList *children, *l;
children = gtk_container_get_children (GTK_CONTAINER (popover));
for (l = children; l; l = l->next)
{
page = l->data;
if (sel_widget == page ||
gtk_widget_is_ancestor (sel_widget, page))
{
gint position;
glade_widget_property_get (glade_widget_get_from_gobject (page), "position", &position);
glade_widget_property_set (glade_widget_get_from_gobject (popover), "current", position);
break;
}
}
g_list_free (children);
}
}
}
static void
glade_gtk_popover_menu_project_changed (GladeWidget * gwidget,
GParamSpec * pspec,
gpointer userdata)
{
GladeProject * project = glade_widget_get_project (gwidget);
GladeProject * old_project = g_object_get_data (G_OBJECT (gwidget), "popover-menu-project-ptr");
if (old_project)
g_signal_handlers_disconnect_by_func (G_OBJECT (old_project),
G_CALLBACK (glade_gtk_popover_menu_selection_changed),
gwidget);
if (project)
g_signal_connect (G_OBJECT (project), "selection-changed",
G_CALLBACK (glade_gtk_popover_menu_selection_changed),
gwidget);
g_object_set_data (G_OBJECT (gwidget), "popover-menu-project-ptr", project);
}
static gint
get_visible_child (GtkPopoverMenu *popover, GtkWidget **visible_child)
{
gchar *visible;
GList *children, *l;
gint ret, i;
ret = -1;
g_object_get (G_OBJECT (popover), "visible-submenu", &visible, NULL);
children = gtk_container_get_children (GTK_CONTAINER (popover));
for (l = children, i = 0; visible && l; l = l->next, i++)
{
GtkWidget *child = l->data;
gchar *name;
gboolean found;
gtk_container_child_get (GTK_CONTAINER (popover), child, "submenu", &name, NULL);
found = name != NULL && !strcmp (visible, name);
g_free (name);
if (found)
{
if (visible_child)
*visible_child = child;
ret = i;
break;
}
}
g_list_free (children);
g_free (visible);
return ret;
}
static void
glade_gtk_popover_menu_visible_submenu_changed (GObject *popover,
GParamSpec *pspec,
gpointer data)
{
GladeWidget *gwidget = glade_widget_get_from_gobject (popover);
GladeProject *project = glade_widget_get_project (gwidget);
gint current;
GList *list;
GtkWidget *visible_child;
current = get_visible_child (GTK_POPOVER_MENU (popover), &visible_child);
glade_widget_property_set (gwidget, "current", current);
if ((list = glade_project_selection_get (project)) != NULL &&
list->next == NULL)
{
GObject *selected = list->data;
if (GTK_IS_WIDGET (selected) &&
gtk_widget_is_ancestor (GTK_WIDGET (selected), GTK_WIDGET (popover)) &&
(GtkWidget*)selected != visible_child &&
!gtk_widget_is_ancestor (GTK_WIDGET (selected), GTK_WIDGET (visible_child)))
{
glade_project_selection_clear (project, TRUE);
}
}
}
void
glade_gtk_popover_menu_post_create (GladeWidgetAdaptor *adaptor,
GObject *container,
GladeCreateReason reason)
{
GladeWidget *parent = glade_widget_get_from_gobject (container);
GladeProject *project = glade_widget_get_project (parent);
if (reason == GLADE_CREATE_LOAD)
g_signal_connect (project, "parse-finished",
G_CALLBACK (glade_gtk_popover_menu_parse_finished),
container);
g_signal_connect (G_OBJECT (parent), "notify::project",
G_CALLBACK (glade_gtk_popover_menu_project_changed), NULL);
glade_gtk_popover_menu_project_changed (parent, NULL, NULL);
g_signal_connect (container, "notify::visible-submenu",
G_CALLBACK (glade_gtk_popover_menu_visible_submenu_changed), NULL);
GWA_GET_CLASS (GTK_TYPE_POPOVER)->post_create (adaptor, container, reason);
}
void
glade_gtk_popover_menu_add_child (GladeWidgetAdaptor *adaptor,
GObject *parent,
GObject *child)
{
gtk_container_add (GTK_CONTAINER (parent), GTK_WIDGET (child));
if (!glade_widget_superuser ())
{
GladeWidget *gbox;
gint submenus;
gbox = glade_widget_get_from_gobject (parent);
glade_widget_property_get (gbox, "submenus", &submenus);
glade_widget_property_set (gbox, "submenus", submenus);
}
}
void
glade_gtk_popover_menu_remove_child (GladeWidgetAdaptor *adaptor,
GObject *parent,
GObject *child)
{
gtk_container_remove (GTK_CONTAINER (parent), GTK_WIDGET (child));
if (!glade_widget_superuser ())
{
GladeWidget *gbox;
gint submenus;
gbox = glade_widget_get_from_gobject (parent);
glade_widget_property_get (gbox, "submenus", &submenus);
glade_widget_property_set (gbox, "submenus", submenus);
}
}
void
glade_gtk_popover_menu_replace_child (GladeWidgetAdaptor * adaptor,
GObject * container,
GObject * current,
GObject * new_widget)
{
gchar *visible;
gchar *name;
gint position;
GladeWidget *gwidget;
g_object_get (G_OBJECT (container), "visible-submenu", &visible, NULL);
gtk_container_child_get (GTK_CONTAINER (container),
GTK_WIDGET (current),
"submenu", &name,
"position", &position,
NULL);
gtk_container_add (GTK_CONTAINER (container), GTK_WIDGET (new_widget));
gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (current));
gtk_container_child_set (GTK_CONTAINER (container),
GTK_WIDGET (new_widget),
"submenu", name,
"position", position,
NULL);
g_object_set (G_OBJECT (container), "visible-submenu", visible, NULL);
gwidget = glade_widget_get_from_gobject (new_widget);
if (gwidget)
{
glade_widget_pack_property_set (gwidget, "submenu", name);
glade_widget_pack_property_set (gwidget, "position", position);
}
g_free (visible);
g_free (name);
}
typedef struct {
gint size;
gboolean include_placeholders;
} ChildData;
static void
count_child (GtkWidget *child, gpointer data)
{
ChildData *cdata = data;
if (cdata->include_placeholders || !GLADE_IS_PLACEHOLDER (child))
cdata->size++;
}
static gint
count_children (GtkContainer *container,
gboolean include_placeholders)
{
ChildData data;
data.size = 0;
data.include_placeholders = include_placeholders;
gtk_container_foreach (container, count_child, &data);
return data.size;
}
static gchar *
get_unused_name (GtkPopoverMenu *popover)
{
gint i;
gchar *name = NULL;
GList *children, *l;
gboolean exists;
children = gtk_container_get_children (GTK_CONTAINER (popover));
i = g_list_length (children);
while (1)
{
name = g_strdup_printf ("submenu%d", i);
exists = FALSE;
for (l = children; l && !exists; l = l->next)
{
gchar *submenu;
gtk_container_child_get (GTK_CONTAINER (popover), GTK_WIDGET (l->data),
"submenu", &submenu, NULL);
if (!strcmp (submenu, name))
exists = TRUE;
g_free (submenu);
}
if (!exists)
break;
g_free (name);
i++;
}
g_list_free (children);
return name;
}
static void
glade_gtk_popover_menu_set_submenus (GObject * object,
const GValue * value)
{
GladeWidget *gbox;
GtkWidget *child;
gint new_size, i;
gint old_size;
gchar *name;
gint page;
new_size = g_value_get_int (value);
old_size = count_children (GTK_CONTAINER (object), TRUE);
if (old_size == new_size)
return;
else if (old_size < new_size)
{
for (i = old_size; i < new_size; i++)
{
name = get_unused_name (GTK_POPOVER_MENU (object));
child = glade_placeholder_new ();
gtk_container_add_with_properties (GTK_CONTAINER (object), child,
"submenu", name, NULL);
g_free (name);
}
}
else
{
GList *children, *l;
children = gtk_container_get_children (GTK_CONTAINER (object));
for (l = g_list_last (children); l; l = l->prev)
{
if (old_size <= new_size)
break;
child = l->data;
if (GLADE_IS_PLACEHOLDER (child))
{
gtk_container_remove (GTK_CONTAINER (object), child);
old_size--;
}
}
}
gbox = glade_widget_get_from_gobject (object);
glade_widget_property_get (gbox, "current", &page);
glade_widget_property_set (gbox, "current", page);
}
static void
glade_gtk_popover_menu_set_current (GObject *object,
const GValue *value)
{
gint new_page;
GList *children;
GtkWidget *child;
gchar *submenu;
new_page = g_value_get_int (value);
children = gtk_container_get_children (GTK_CONTAINER (object));
child = g_list_nth_data (children, new_page);
if (child)
{
gtk_container_child_get (GTK_CONTAINER (object), child,
"submenu", &submenu,
NULL);
gtk_popover_menu_open_submenu (GTK_POPOVER_MENU (object), submenu);
g_free (submenu);
}
g_list_free (children);
}
void
glade_gtk_popover_menu_set_property (GladeWidgetAdaptor * adaptor,
GObject * object,
const gchar * id,
const GValue * value)
{
if (!strcmp (id, "submenus"))
glade_gtk_popover_menu_set_submenus (object, value);
else if (!strcmp (id, "current"))
glade_gtk_popover_menu_set_current (object, value);
else
GWA_GET_CLASS (GTK_TYPE_POPOVER)->set_property (adaptor, object, id, value);
}
void
glade_gtk_popover_menu_get_property (GladeWidgetAdaptor * adaptor,
GObject * object,
const gchar * id,
GValue * value)
{
if (!strcmp (id, "submenus"))
{
g_value_reset (value);
g_value_set_int (value, count_children (GTK_CONTAINER (object), TRUE));
}
else if (!strcmp (id, "current"))
{
g_value_reset (value);
g_value_set_int (value, get_visible_child (GTK_POPOVER_MENU (object), NULL));
}
else
GWA_GET_CLASS (GTK_TYPE_POPOVER)->get_property (adaptor, object, id, value);
}
static gboolean
glade_gtk_popover_menu_verify_submenus (GObject * object,
const GValue *value)
{
gint new_size, old_size;
new_size = g_value_get_int (value);
old_size = count_children (GTK_CONTAINER (object), FALSE);
return old_size <= new_size;
}
static gboolean
glade_gtk_popover_menu_verify_current (GObject *object,
const GValue *value)
{
gint current;
gint submenus;
current = g_value_get_int (value);
submenus = count_children (GTK_CONTAINER (object), TRUE);
return 0 <= current && current < submenus;
}
gboolean
glade_gtk_popover_menu_verify_property (GladeWidgetAdaptor * adaptor,
GObject * object,
const gchar * id,
const GValue * value)
{
if (!strcmp (id, "submenus"))
return glade_gtk_popover_menu_verify_submenus (object, value);
else if (!strcmp (id, "current"))
return glade_gtk_popover_menu_verify_current (object, value);
else if (GWA_GET_CLASS (GTK_TYPE_POPOVER)->verify_property)
return GWA_GET_CLASS (GTK_TYPE_POPOVER)->verify_property (adaptor, object, id, value);
return TRUE;
}
static void
update_position (GtkWidget *widget, gpointer data)
{
GtkContainer *parent = data;
GladeWidget *gwidget;
gint position;
gwidget = glade_widget_get_from_gobject (widget);
if (gwidget)
{
gtk_container_child_get (parent, widget, "position", &position, NULL);
glade_widget_pack_property_set (gwidget, "position", position);
}
}
static void
glade_gtk_popover_menu_set_child_position (GObject * container,
GObject * child,
GValue * value)
{
static gboolean recursion = FALSE;
gint new_position, old_position;
gchar *visible_child;
GladeWidget *gbox;
g_object_get (container, "visible-submenu", &visible_child, NULL);
if (recursion)
return;
gtk_container_child_get (GTK_CONTAINER (container), GTK_WIDGET (child), "position", &old_position, NULL);
new_position = g_value_get_int (value);
if (old_position != new_position)
{
recursion = TRUE;
gtk_container_child_set (GTK_CONTAINER (container), GTK_WIDGET (child),
"position", new_position,
NULL);
gtk_container_forall (GTK_CONTAINER (container), update_position, container);
recursion = FALSE;
}
g_object_set (container, "visible-submenu", visible_child, NULL);
g_free (visible_child);
gbox = glade_widget_get_from_gobject (container);
glade_widget_pack_property_set (gbox, "visible-submenu", get_visible_child (GTK_POPOVER_MENU (container), NULL));
}
void
glade_gtk_popover_menu_set_child_property (GladeWidgetAdaptor * adaptor,
GObject * container,
GObject * child,
const gchar * id,
GValue * value)
{
if (!strcmp (id, "position"))
glade_gtk_popover_menu_set_child_position (container, child, value);
else if (!strcmp (id, "submenu"))
gtk_container_child_set_property (GTK_CONTAINER (container),
GTK_WIDGET (child), id, value);
else
GWA_GET_CLASS (GTK_TYPE_POPOVER)->child_set_property (adaptor, container, child, id, value);
}
void
glade_gtk_popover_menu_get_child_property (GladeWidgetAdaptor * adaptor,
GObject * container,
GObject * child,
const gchar * id,
GValue * value)
{
gtk_container_child_get_property (GTK_CONTAINER (container),
GTK_WIDGET (child), id, value);
}
GladeEditable *
glade_gtk_popover_menu_create_editable (GladeWidgetAdaptor * adaptor,
GladeEditorPageType type)
{
if (type == GLADE_PAGE_GENERAL)
return (GladeEditable *) glade_popover_menu_editor_new ();
else
return GWA_GET_CLASS (GTK_TYPE_POPOVER)->create_editable (adaptor, type);
}