glade/gladeui/glade-placeholder.c
2011-10-06 16:12:22 -03:00

519 lines
15 KiB
C

/*
* Copyright (C) 2003, 2004 Joaquin Cuenca Abela
*
* Authors:
* Joaquin Cuenca Abela <e98cuenc@yahoo.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU 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"
/**
* SECTION:glade-placeholder
* @Short_Description: A #GtkWidget to fill empty places.
*
* Generally in Glade, container widgets are implemented with #GladePlaceholder
* children to allow users to 'click' and add thier widgets to a container.
* It is the responsability of the plugin writer to create placeholders for
* container widgets where appropriate; usually in #GladePostCreateFunc
* when the #GladeCreateReason is %GLADE_CREATE_USER.
*/
#include <gtk/gtk.h>
#include "glade-marshallers.h"
#include "glade.h"
#include "glade-placeholder.h"
#include "glade-xml-utils.h"
#include "glade-project.h"
#include "glade-command.h"
#include "glade-palette.h"
#include "glade-popup.h"
#include "glade-cursor.h"
#include "glade-widget.h"
#include "glade-app.h"
#include <math.h>
#define WIDTH_REQUISITION 20
#define HEIGHT_REQUISITION 20
static void glade_placeholder_finalize (GObject *object);
static void glade_placeholder_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void glade_placeholder_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void glade_placeholder_realize (GtkWidget *widget);
static void glade_placeholder_unrealize (GtkWidget *widget);
static void glade_placeholder_map (GtkWidget *widget);
static void glade_placeholder_unmap (GtkWidget *widget);
static void glade_placeholder_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean glade_placeholder_draw (GtkWidget *widget, cairo_t *cr);
static gboolean glade_placeholder_motion_notify_event (GtkWidget *widget,
GdkEventMotion *event);
static gboolean glade_placeholder_button_press (GtkWidget *widget,
GdkEventButton *event);
static gboolean glade_placeholder_popup_menu (GtkWidget *widget);
static cairo_pattern_t *placeholder_pattern = NULL;
struct _GladePlaceholderPrivate
{
GList *packing_actions;
GdkWindow *event_window;
};
enum
{
PROP_0,
PROP_HADJUSTMENT,
PROP_VADJUSTMENT,
PROP_HSCROLL_POLICY,
PROP_VSCROLL_POLICY
};
#define GLADE_PLACEHOLDER_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), \
GLADE_TYPE_PLACEHOLDER, \
GladePlaceholderPrivate))
G_DEFINE_TYPE_WITH_CODE (GladePlaceholder, glade_placeholder, GTK_TYPE_WIDGET,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL))
static void glade_placeholder_class_init (GladePlaceholderClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
gchar *path;
cairo_surface_t *surface;
object_class->finalize = glade_placeholder_finalize;
object_class->set_property = glade_placeholder_set_property;
object_class->get_property = glade_placeholder_get_property;
widget_class->realize = glade_placeholder_realize;
widget_class->unrealize = glade_placeholder_unrealize;
widget_class->map = glade_placeholder_map;
widget_class->unmap = glade_placeholder_unmap;
widget_class->size_allocate = glade_placeholder_size_allocate;
widget_class->draw = glade_placeholder_draw;
widget_class->motion_notify_event = glade_placeholder_motion_notify_event;
widget_class->button_press_event = glade_placeholder_button_press;
widget_class->popup_menu = glade_placeholder_popup_menu;
/* GtkScrollable implementation */
g_object_class_override_property (object_class, PROP_HADJUSTMENT,
"hadjustment");
g_object_class_override_property (object_class, PROP_VADJUSTMENT,
"vadjustment");
g_object_class_override_property (object_class, PROP_HSCROLL_POLICY,
"hscroll-policy");
g_object_class_override_property (object_class, PROP_VSCROLL_POLICY,
"vscroll-policy");
/* Create our tiled background pattern */
path = g_build_filename (glade_app_get_pixmaps_dir (), "placeholder.png", NULL);
surface = cairo_image_surface_create_from_png (path);
if (!surface)
g_warning ("Failed to create surface for %s\n", path);
else
{
placeholder_pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (placeholder_pattern, CAIRO_EXTEND_REPEAT);
}
g_free (path);
g_type_class_add_private (klass, sizeof (GladePlaceholderPrivate));
}
static void
glade_placeholder_notify_parent (GObject *gobject,
GParamSpec *arg1,
gpointer user_data)
{
GladePlaceholder *placeholder = GLADE_PLACEHOLDER (gobject);
GladeWidgetAdaptor *parent_adaptor = NULL;
GladeWidget *parent = glade_placeholder_get_parent (placeholder);
if (placeholder->priv->packing_actions)
{
g_list_foreach (placeholder->priv->packing_actions, (GFunc) g_object_unref,
NULL);
g_list_free (placeholder->priv->packing_actions);
placeholder->priv->packing_actions = NULL;
}
if (parent)
parent_adaptor = glade_widget_get_adaptor (parent);
if (parent_adaptor)
placeholder->priv->packing_actions =
glade_widget_adaptor_pack_actions_new (parent_adaptor);
}
static void
glade_placeholder_init (GladePlaceholder *placeholder)
{
placeholder->priv = GLADE_PLACEHOLDER_GET_PRIVATE (placeholder);
placeholder->priv->packing_actions = NULL;
gtk_widget_set_can_focus (GTK_WIDGET (placeholder), TRUE);
gtk_widget_set_has_window (GTK_WIDGET (placeholder), FALSE);
gtk_widget_set_size_request (GTK_WIDGET (placeholder),
WIDTH_REQUISITION, HEIGHT_REQUISITION);
g_signal_connect (placeholder, "notify::parent",
G_CALLBACK (glade_placeholder_notify_parent), NULL);
gtk_widget_set_hexpand (GTK_WIDGET (placeholder), TRUE);
gtk_widget_set_vexpand (GTK_WIDGET (placeholder), TRUE);
gtk_widget_show (GTK_WIDGET (placeholder));
}
static void
glade_placeholder_finalize (GObject *object)
{
GladePlaceholder *placeholder;
g_return_if_fail (GLADE_IS_PLACEHOLDER (object));
placeholder = GLADE_PLACEHOLDER (object);
if (placeholder->priv->packing_actions)
{
g_list_foreach (placeholder->priv->packing_actions, (GFunc) g_object_unref, NULL);
g_list_free (placeholder->priv->packing_actions);
}
G_OBJECT_CLASS (glade_placeholder_parent_class)->finalize (object);
}
static void
glade_placeholder_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case PROP_HADJUSTMENT:
case PROP_VADJUSTMENT:
case PROP_HSCROLL_POLICY:
case PROP_VSCROLL_POLICY:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_placeholder_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
case PROP_HADJUSTMENT:
case PROP_VADJUSTMENT:
g_value_set_object (value, NULL);
break;
case PROP_HSCROLL_POLICY:
case PROP_VSCROLL_POLICY:
g_value_set_enum (value, GTK_SCROLL_MINIMUM);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
glade_placeholder_realize (GtkWidget *widget)
{
GladePlaceholder *placeholder;
GtkAllocation allocation;
GdkWindow *window;
GdkWindowAttr attributes;
gint attributes_mask;
placeholder = GLADE_PLACEHOLDER (widget);
gtk_widget_set_realized (widget, TRUE);
gtk_widget_get_allocation (widget, &allocation);
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.window_type = GDK_WINDOW_CHILD;
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask =
gtk_widget_get_events (widget) |
GDK_POINTER_MOTION_MASK |
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y;
window = gtk_widget_get_parent_window (widget);
gtk_widget_set_window (widget, g_object_ref (window));
placeholder->priv->event_window =
gdk_window_new (gtk_widget_get_parent_window (widget), &attributes,
attributes_mask);
gdk_window_set_user_data (placeholder->priv->event_window, widget);
gtk_widget_style_attach (widget);
}
static void
glade_placeholder_unrealize (GtkWidget *widget)
{
GladePlaceholder *placeholder;
placeholder = GLADE_PLACEHOLDER (widget);
if (placeholder->priv->event_window)
{
gdk_window_set_user_data (placeholder->priv->event_window, NULL);
gdk_window_destroy (placeholder->priv->event_window);
placeholder->priv->event_window = NULL;
}
GTK_WIDGET_CLASS (glade_placeholder_parent_class)->unrealize (widget);
}
static void
glade_placeholder_map (GtkWidget *widget)
{
GladePlaceholder *placeholder;
placeholder = GLADE_PLACEHOLDER (widget);
if (placeholder->priv->event_window)
{
gdk_window_show (placeholder->priv->event_window);
}
GTK_WIDGET_CLASS (glade_placeholder_parent_class)->map (widget);
}
static void
glade_placeholder_unmap (GtkWidget *widget)
{
GladePlaceholder *placeholder;
placeholder = GLADE_PLACEHOLDER (widget);
if (placeholder->priv->event_window)
{
gdk_window_hide (placeholder->priv->event_window);
}
GTK_WIDGET_CLASS (glade_placeholder_parent_class)->unmap (widget);
}
static void
glade_placeholder_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
GladePlaceholder *placeholder;
placeholder = GLADE_PLACEHOLDER (widget);
gtk_widget_set_allocation (widget, allocation);
if (gtk_widget_get_realized (widget))
{
gdk_window_move_resize (placeholder->priv->event_window,
allocation->x, allocation->y,
allocation->width, allocation->height);
}
}
static gboolean
glade_placeholder_draw (GtkWidget *widget, cairo_t *cr)
{
GtkStyle *style;
GdkColor *light;
GdkColor *dark;
gint w, h;
style = gtk_widget_get_style (widget);
light = &style->light[GTK_STATE_NORMAL];
dark = &style->dark[GTK_STATE_NORMAL];
h = gtk_widget_get_allocated_height (widget);
w = gtk_widget_get_allocated_width (widget);
if (placeholder_pattern)
{
cairo_save (cr);
cairo_rectangle (cr, 0, 0, w, h);
cairo_set_source (cr, placeholder_pattern);
cairo_fill (cr);
cairo_restore (cr);
}
cairo_set_line_width (cr, 1.0);
glade_utils_cairo_draw_line (cr, light, 0, 0, w - 1, 0);
glade_utils_cairo_draw_line (cr, light, 0, 0, 0, h - 1);
glade_utils_cairo_draw_line (cr, dark, 0, h - 1, w - 1, h - 1);
glade_utils_cairo_draw_line (cr, dark, w - 1, 0, w - 1, h - 1);
return FALSE;
}
static gboolean
glade_placeholder_motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
GladePointerMode pointer_mode;
GladeProject *project;
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (widget), FALSE);
project = glade_placeholder_get_project (GLADE_PLACEHOLDER (widget));
pointer_mode = glade_project_get_pointer_mode (project);
if (pointer_mode == GLADE_POINTER_SELECT)
glade_cursor_set (project, event->window, GLADE_CURSOR_SELECTOR);
else if (pointer_mode == GLADE_POINTER_ADD_WIDGET)
glade_cursor_set (project, event->window, GLADE_CURSOR_ADD_WIDGET);
return FALSE;
}
static gboolean
glade_placeholder_button_press (GtkWidget *widget, GdkEventButton *event)
{
GladePlaceholder *placeholder;
GladeProject *project;
GladeWidgetAdaptor *adaptor;
gboolean handled = FALSE;
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (widget), FALSE);
placeholder = GLADE_PLACEHOLDER (widget);
project = glade_placeholder_get_project (placeholder);
adaptor = glade_project_get_add_item (project);
if (!gtk_widget_has_focus (widget))
gtk_widget_grab_focus (widget);
if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
{
if (adaptor != NULL)
{
GladeWidget *parent = glade_placeholder_get_parent (placeholder);
if (!glade_util_check_and_warn_scrollable
(parent, adaptor, glade_app_get_window ()))
{
/* A widget type is selected in the palette.
* Add a new widget of that type.
*/
glade_command_create (adaptor, parent, placeholder, project);
glade_project_set_add_item (project, NULL);
/* reset the cursor */
glade_cursor_set (project, event->window, GLADE_CURSOR_SELECTOR);
}
handled = TRUE;
}
}
if (!handled && glade_popup_is_popup_event (event))
{
glade_popup_placeholder_pop (placeholder, event);
handled = TRUE;
}
return handled;
}
static gboolean
glade_placeholder_popup_menu (GtkWidget *widget)
{
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (widget), FALSE);
glade_popup_placeholder_pop (GLADE_PLACEHOLDER (widget), NULL);
return TRUE;
}
/**
* glade_placeholder_new:
*
* Returns: a new #GladePlaceholder cast as a #GtkWidget
*/
GtkWidget *
glade_placeholder_new (void)
{
return g_object_new (GLADE_TYPE_PLACEHOLDER, NULL);
}
GladeProject *
glade_placeholder_get_project (GladePlaceholder *placeholder)
{
GladeWidget *parent;
parent = glade_placeholder_get_parent (placeholder);
return parent ? glade_widget_get_project (parent) : NULL;
}
GladeWidget *
glade_placeholder_get_parent (GladePlaceholder *placeholder)
{
GtkWidget *widget;
GladeWidget *parent = NULL;
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (placeholder), NULL);
for (widget = gtk_widget_get_parent (GTK_WIDGET (placeholder));
widget != NULL; widget = gtk_widget_get_parent (widget))
{
if ((parent = glade_widget_get_from_gobject (widget)) != NULL)
break;
}
return parent;
}
GList *
glade_placeholder_packing_actions (GladePlaceholder *placeholder)
{
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (placeholder), NULL);
return placeholder->priv->packing_actions;
}