/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* 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.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"
#define WIDTH_REQUISITION 20
#define HEIGHT_REQUISITION 20
static void glade_placeholder_finalize (GObject *object);
static void glade_placeholder_realize (GtkWidget *widget);
static void glade_placeholder_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void glade_placeholder_send_configure (GladePlaceholder *placeholder);
static gboolean glade_placeholder_expose (GtkWidget *widget,
GdkEventExpose *event);
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 char *placeholder_xpm[] = {
/* columns rows colors chars-per-pixel */
"8 8 2 1",
" c #bbbbbb",
". c #d6d6d6",
/* pixels */
" . . ",
". . ",
" ..",
" ..",
". . ",
" . . ",
" .. ",
" .. "
};
G_DEFINE_TYPE (GladePlaceholder, glade_placeholder, GTK_TYPE_WIDGET)
static void
glade_placeholder_class_init (GladePlaceholderClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = glade_placeholder_finalize;
widget_class->realize = glade_placeholder_realize;
widget_class->size_allocate = glade_placeholder_size_allocate;
widget_class->expose_event = glade_placeholder_expose;
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;
}
static void
glade_placeholder_notify_parent (GObject *gobject,
GParamSpec *arg1,
gpointer user_data)
{
GladePlaceholder *placeholder = GLADE_PLACEHOLDER (gobject);
GladeWidget *parent = glade_placeholder_get_parent (placeholder);
if (placeholder->packing_actions)
{
g_list_foreach (placeholder->packing_actions, (GFunc)g_object_unref, NULL);
g_list_free (placeholder->packing_actions);
placeholder->packing_actions = NULL;
}
if (parent && parent->adaptor->packing_actions)
placeholder->packing_actions = glade_widget_adaptor_pack_actions_new (parent->adaptor);
}
static void
glade_placeholder_init (GladePlaceholder *placeholder)
{
placeholder->placeholder_pixmap = NULL;
placeholder->packing_actions = NULL;
GTK_WIDGET_SET_FLAGS (GTK_WIDGET (placeholder), GTK_CAN_FOCUS);
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_show (GTK_WIDGET (placeholder));
}
/**
* glade_placeholder_new:
*
* Returns: a new #GladePlaceholder cast as a #GtkWidget
*/
GtkWidget *
glade_placeholder_new (void)
{
return g_object_new (GLADE_TYPE_PLACEHOLDER, NULL);
}
static void
glade_placeholder_finalize (GObject *object)
{
GladePlaceholder *placeholder;
g_return_if_fail (GLADE_IS_PLACEHOLDER (object));
placeholder = GLADE_PLACEHOLDER (object);
/* placeholder->placeholder_pixmap can be NULL if the placeholder is
* destroyed before it's realized */
if (placeholder->placeholder_pixmap)
g_object_unref (placeholder->placeholder_pixmap);
if (placeholder->packing_actions)
{
g_list_foreach (placeholder->packing_actions, (GFunc)g_object_unref, NULL);
g_list_free (placeholder->packing_actions);
}
G_OBJECT_CLASS (glade_placeholder_parent_class)->finalize (object);
}
static void
glade_placeholder_realize (GtkWidget *widget)
{
GladePlaceholder *placeholder;
GdkWindowAttr attributes;
gint attributes_mask;
g_return_if_fail (GLADE_IS_PLACEHOLDER (widget));
placeholder = GLADE_PLACEHOLDER (widget);
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
attributes.window_type = GDK_WINDOW_CHILD;
attributes.x = widget->allocation.x;
attributes.y = widget->allocation.y;
attributes.width = widget->allocation.width;
attributes.height = widget->allocation.height;
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.visual = gtk_widget_get_visual (widget);
attributes.colormap = gtk_widget_get_colormap (widget);
attributes.event_mask =
gtk_widget_get_events (widget) |
GDK_EXPOSURE_MASK |
GDK_BUTTON_PRESS_MASK |
GDK_BUTTON_RELEASE_MASK |
GDK_POINTER_MOTION_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
gdk_window_set_user_data (widget->window, placeholder);
widget->style = gtk_style_attach (widget->style, widget->window);
glade_placeholder_send_configure (GLADE_PLACEHOLDER (widget));
if (!placeholder->placeholder_pixmap)
{
placeholder->placeholder_pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
gtk_widget_get_colormap (GTK_WIDGET (placeholder)),
NULL, NULL, placeholder_xpm);
g_assert(G_IS_OBJECT(placeholder->placeholder_pixmap));
}
gdk_window_set_back_pixmap (GTK_WIDGET (placeholder)->window, placeholder->placeholder_pixmap, FALSE);
}
static void
glade_placeholder_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
g_return_if_fail (GLADE_IS_PLACEHOLDER (widget));
g_return_if_fail (allocation != NULL);
widget->allocation = *allocation;
if (GTK_WIDGET_REALIZED (widget))
{
gdk_window_move_resize (widget->window,
allocation->x, allocation->y,
allocation->width, allocation->height);
glade_placeholder_send_configure (GLADE_PLACEHOLDER (widget));
}
}
static void
glade_placeholder_send_configure (GladePlaceholder *placeholder)
{
GtkWidget *widget;
GdkEvent *event = gdk_event_new (GDK_CONFIGURE);
widget = GTK_WIDGET (placeholder);
event->configure.window = g_object_ref (widget->window);
event->configure.send_event = TRUE;
event->configure.x = widget->allocation.x;
event->configure.y = widget->allocation.y;
event->configure.width = widget->allocation.width;
event->configure.height = widget->allocation.height;
gtk_widget_event (widget, event);
gdk_event_free (event);
}
static GladeProject*
glade_placeholder_get_project (GladePlaceholder *placeholder)
{
GladeWidget *parent;
parent = glade_placeholder_get_parent (placeholder);
return parent ? GLADE_PROJECT (parent->project) : NULL;
}
static gboolean
glade_placeholder_expose (GtkWidget *widget, GdkEventExpose *event)
{
GdkGC *light_gc;
GdkGC *dark_gc;
gint w, h;
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (widget), FALSE);
light_gc = widget->style->light_gc[GTK_STATE_NORMAL];
dark_gc = widget->style->dark_gc[GTK_STATE_NORMAL];
gdk_drawable_get_size (event->window, &w, &h);
gdk_draw_line (event->window, light_gc, 0, 0, w - 1, 0);
gdk_draw_line (event->window, light_gc, 0, 0, 0, h - 1);
gdk_draw_line (event->window, dark_gc, 0, h - 1, w - 1, h - 1);
gdk_draw_line (event->window, dark_gc, w - 1, 0, w - 1, h - 1);
glade_util_draw_selection_nodes (event->window);
return FALSE;
}
static gboolean
glade_placeholder_motion_notify_event (GtkWidget *widget, GdkEventMotion *event)
{
GladePointerMode pointer_mode;
GladeWidget *gparent;
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (widget), FALSE);
gparent = glade_placeholder_get_parent (GLADE_PLACEHOLDER (widget));
pointer_mode = glade_app_get_pointer_mode ();
if (pointer_mode == GLADE_POINTER_SELECT &&
/* If we are the child of a widget that is in a GladeFixed, then
* we are the means of drag/resize and we dont want to fight for
* the cursor (ideally; GladeCursor should somehow deal with such
* concurrencies I suppose).
*/
(gparent->parent &&
GLADE_IS_FIXED (gparent->parent)) == FALSE)
glade_cursor_set (event->window, GLADE_CURSOR_SELECTOR);
else if (pointer_mode == GLADE_POINTER_ADD_WIDGET)
glade_cursor_set (event->window, GLADE_CURSOR_ADD_WIDGET);
return FALSE;
}
static gboolean
glade_placeholder_button_press (GtkWidget *widget, GdkEventButton *event)
{
GladePlaceholder *placeholder;
GladeProject *project;
GladeWidgetAdaptor *adaptor;
GladePalette *palette;
gboolean handled = FALSE;
g_return_val_if_fail (GLADE_IS_PLACEHOLDER (widget), FALSE);
adaptor = glade_palette_get_current_item (glade_app_get_palette ());
palette = glade_app_get_palette ();
placeholder = GLADE_PLACEHOLDER (widget);
project = glade_placeholder_get_project (placeholder);
if (!GTK_WIDGET_HAS_FOCUS (widget))
gtk_widget_grab_focus (widget);
if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
{
if (adaptor != NULL)
{
/* A widget type is selected in the palette.
* Add a new widget of that type.
*/
glade_command_create
(adaptor,
glade_placeholder_get_parent (placeholder),
placeholder, project);
glade_palette_deselect_current_item (glade_app_get_palette(), TRUE);
/* reset the cursor */
glade_cursor_set (event->window, GLADE_CURSOR_SELECTOR);
handled = TRUE;
}
}
else if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
{
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;
}
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;
}
syntax highlighted by Code2HTML, v. 0.9.1