#include <string.h>
#define GTK_TEXT_USE_INTERNAL_UNSUPPORTED_API
#include "gtkimmulticontext.h"
#include "gdk/gdkkeysyms.h"
#include "gtkprivate.h"
#include "gtksizegroup.h" /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=72258 */
#include "gtktextutil.h"
#include "gtkwindow.h"
#include "gtkalias.h"
#if 0
#define DEBUG_VALIDATION_AND_SCROLLING
#endif
#ifdef DEBUG_VALIDATION_AND_SCROLLING
#define DV(x) (x)
#else
#define DV(x)
#endif
#define SCREEN_WIDTH(widget) text_window_get_width (GTK_TEXT_VIEW (widget)->text_window)
#define SCREEN_HEIGHT(widget) text_window_get_height (GTK_TEXT_VIEW (widget)->text_window)
#define SPACE_FOR_CURSOR 1
struct _GtkTextPendingScroll
{
GtkTextMark *mark;
gdouble within_margin;
gboolean use_align;
gdouble xalign;
gdouble yalign;
};
enum
{
SET_SCROLL_ADJUSTMENTS,
POPULATE_POPUP,
MOVE_CURSOR,
PAGE_HORIZONTALLY,
SET_ANCHOR,
INSERT_AT_CURSOR,
DELETE_FROM_CURSOR,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_PIXELS_ABOVE_LINES,
PROP_PIXELS_BELOW_LINES,
PROP_PIXELS_INSIDE_WRAP,
PROP_EDITABLE,
PROP_WRAP_MODE,
LAST_PROP
};
static void gtk_text_view_init (GtkTextView *text_view);
static void gtk_text_view_class_init (GtkTextViewClass *klass);
static void gtk_text_view_destroy (GtkObject *object);
static void gtk_text_view_finalize (GObject *object);
static void gtk_text_view_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_text_view_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec);
static void gtk_text_view_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_text_view_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void gtk_text_view_realize (GtkWidget *widget);
static void gtk_text_view_unrealize (GtkWidget *widget);
/* Target side drag signals */
static void gtk_text_view_drag_leave (GtkWidget *widget,
GdkDragContext *context,
guint time);
static gboolean gtk_text_view_drag_motion (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time);
static gboolean gtk_text_view_drag_drop (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time);
static void gtk_text_view_drag_data_received (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection_data,
guint info,
guint time);
static void gtk_text_view_set_scroll_adjustments (GtkTextView *text_view,
GtkAdjustment *hadj,
GtkAdjustment *vadj);
static gboolean gtk_text_view_popup_menu (GtkWidget *widget);
static void gtk_text_view_move_cursor (GtkTextView *text_view,
GtkMovementStep step,
gint count,
gboolean extend_selection);
static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
const GtkTextIter *location,
GtkTextMark *mark,
gpointer data);
static void gtk_text_view_get_cursor_location (GtkTextView *text_view,
GdkRectangle *pos);
static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
gint *x,
gint *y);
static void gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
gint x,
gint y);
static GtkAdjustment* get_hadjustment (GtkTextView *text_view);
static GtkAdjustment* get_vadjustment (GtkTextView *text_view);
static void gtk_text_view_do_popup (GtkTextView *text_view,
GdkEventButton *event);
static void gtk_text_view_queue_scroll (GtkTextView *text_view,
GtkTextMark *mark,
gdouble within_margin,
gboolean use_align,
gdouble xalign,
gdouble yalign);
static gboolean gtk_text_view_flush_scroll (GtkTextView *text_view);
static void gtk_text_view_update_adjustments (GtkTextView *text_view);
static void gtk_text_view_invalidate (GtkTextView *text_view);
static void gtk_text_view_flush_first_validate (GtkTextView *text_view);
static void gtk_text_view_update_im_spot_location (GtkTextView *text_view);
/* Container methods */
static void gtk_text_view_add (GtkContainer *container,
GtkWidget *child);
static void gtk_text_view_remove (GtkContainer *container,
GtkWidget *child);
static void gtk_text_view_forall (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
gpointer callback_data);
/* FIXME probably need the focus methods. */
typedef struct _GtkTextViewChild GtkTextViewChild;
struct _GtkTextViewChild
{
GtkWidget *widget;
GtkTextChildAnchor *anchor;
gint from_top_of_line;
gint from_left_of_buffer;
/* These are ignored if anchor != NULL */
GtkTextWindowType type;
gint x;
gint y;
};
static GtkTextViewChild* text_view_child_new_anchored (GtkWidget *child,
GtkTextChildAnchor *anchor,
GtkTextLayout *layout);
static GtkTextViewChild* text_view_child_new_window (GtkWidget *child,
GtkTextWindowType type,
gint x,
gint y);
static void text_view_child_free (GtkTextViewChild *child);
static void text_view_child_set_parent_window (GtkTextView *text_view,
GtkTextViewChild *child);
struct _GtkTextWindow
{
GtkTextWindowType type;
GtkWidget *widget;
GdkWindow *window;
GdkWindow *bin_window;
GtkRequisition requisition;
GdkRectangle allocation;
};
static GtkTextWindow *text_window_new (GtkTextWindowType type,
GtkWidget *widget,
gint width_request,
gint height_request);
static void text_window_free (GtkTextWindow *win);
static void text_window_realize (GtkTextWindow *win,
GdkWindow *parent);
static gint text_window_get_height (GtkTextWindow *win);
static const GtkTargetEntry target_table[] = {
{ "GTK_TEXT_BUFFER_CONTENTS", GTK_TARGET_SAME_APP, 0 },
};
static GtkContainerClass *parent_class = NULL;
static guint signals[LAST_SIGNAL] = { 0 };
GType
gtk_text_view_get_type (void)
{
static GType our_type = 0;
if (our_type == 0)
{
static const GTypeInfo our_info =
{
sizeof (GtkTextViewClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) gtk_text_view_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GtkTextView),
0, /* n_preallocs */
(GInstanceInitFunc) gtk_text_view_init,
};
our_type = g_type_register_static (GTK_TYPE_CONTAINER, I_("GtkTextView"),
&our_info, 0);
}
return our_type;
}
static void
add_move_binding (GtkBindingSet *binding_set,
guint keyval,
guint modmask,
GtkMovementStep step,
gint count)
{
g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
gtk_binding_entry_add_signal (binding_set, keyval, modmask,
"move_cursor", 3,
G_TYPE_ENUM, step,
G_TYPE_INT, count,
G_TYPE_BOOLEAN, FALSE);
/* Selection-extending version */
gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
"move_cursor", 3,
G_TYPE_ENUM, step,
G_TYPE_INT, count,
G_TYPE_BOOLEAN, TRUE);
}
static void
gtk_text_view_class_init (GtkTextViewClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
GtkBindingSet *binding_set;
parent_class = g_type_class_peek_parent (klass);
/* Default handlers and virtual methods
*/
gobject_class->set_property = gtk_text_view_set_property;
gobject_class->get_property = gtk_text_view_get_property;
object_class->destroy = gtk_text_view_destroy;
gobject_class->finalize = gtk_text_view_finalize;
klass->move_focus = gtk_text_view_move_focus;
klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
/*
* Properties
*/
g_object_class_install_property (gobject_class,
PROP_PIXELS_ABOVE_LINES,
g_param_spec_int ("pixels-above-lines",
P_("Pixels Above Lines"),
P_("Pixels of blank space above paragraphs"),
0,
G_MAXINT,
0,
GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_PIXELS_BELOW_LINES,
g_param_spec_int ("pixels-below-lines",
P_("Pixels Below Lines"),
P_("Pixels of blank space below paragraphs"),
0,
G_MAXINT,
0,
GTK_PARAM_READWRITE));
g_object_class_install_property (gobject_class,
PROP_PIXELS_INSIDE_WRAP,
g_param_spec_int ("pixels-inside-wrap",
P_("Pixels Inside Wrap"),
P_("Pixels of blank space between wrapped lines in a paragraph"),
0,
G_MAXINT,
0,
GTK_PARAM_READWRITE));
/*
* Style properties
*/
gtk_widget_class_install_style_property (widget_class,
g_param_spec_boxed ("error-underline-color",
P_("Error underline color"),
P_("Color with which to draw error-indication underlines"),
GDK_TYPE_COLOR,
GTK_PARAM_READABLE));
/*
* Signals
*/
/**
* GtkTextView::move-cursor:
* @widget: the object which received the signal
* @step: the granularity of the move, as a #GtkMovementStep
* @count: the number of @step units to move
* @extend_selection: %TRUE if the move should extend the selection
*
* The ::move-cursor signal is a keybinding signal which gets emitted
* when the user initiates a cursor movement.
*
* Applications should not connect to it, but may emit it with
* g_signal_emit_by_name() if they need to control scrolling
* programmatically.
*
*/
signals[MOVE_CURSOR] =
g_signal_new (I_("move_cursor"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, move_cursor),
NULL, NULL,
_gtk_marshal_VOID__ENUM_INT_BOOLEAN,
G_TYPE_NONE, 3,
GTK_TYPE_MOVEMENT_STEP,
G_TYPE_INT,
G_TYPE_BOOLEAN);
signals[PAGE_HORIZONTALLY] =
g_signal_new (I_("page_horizontally"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, page_horizontally),
NULL, NULL,
_gtk_marshal_VOID__INT_BOOLEAN,
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_BOOLEAN);
signals[MOVE_VIEWPORT] =
_gtk_binding_signal_new (I_("move_viewport"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_CALLBACK (gtk_text_view_move_viewport),
NULL, NULL,
_gtk_marshal_VOID__ENUM_INT,
G_TYPE_NONE, 2,
GTK_TYPE_SCROLL_STEP,
G_TYPE_INT);
signals[SET_ANCHOR] =
g_signal_new (I_("set_anchor"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, set_anchor),
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[INSERT_AT_CURSOR] =
g_signal_new (I_("insert_at_cursor"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, insert_at_cursor),
NULL, NULL,
_gtk_marshal_VOID__STRING,
G_TYPE_NONE, 1,
G_TYPE_STRING);
signals[DELETE_FROM_CURSOR] =
g_signal_new (I_("delete_from_cursor"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, delete_from_cursor),
NULL, NULL,
_gtk_marshal_VOID__ENUM_INT,
G_TYPE_NONE, 2,
GTK_TYPE_DELETE_TYPE,
G_TYPE_INT);
signals[BACKSPACE] =
g_signal_new (I_("backspace"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, backspace),
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[CUT_CLIPBOARD] =
g_signal_new (I_("cut_clipboard"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, cut_clipboard),
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[COPY_CLIPBOARD] =
g_signal_new (I_("copy_clipboard"),
G_OBJECT_CLASS_TYPE (gobject_class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkTextViewClass, copy_clipboard),
NULL, NULL,
_gtk_marshal_VOID__VOID,
G_TYPE_NONE, 0);