From 39339f14f5e9470c8007e1627517a1e7054f5fee Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Thu, 14 Sep 2000 16:41:20 +0000 Subject: [PATCH] Remove g_convert (moved to glib) and now useless utf_to_latin1() Thu Sep 14 12:21:12 2000 Owen Taylor * gtk/gtktexttypes.[ch]: Remove g_convert (moved to glib) and now useless utf_to_latin1() latin1_to_utf() * gtk/gtktextview.[ch]: Change ::move_insert and ::delete_text action signals to ::move and ::delete; create the signals with the right enumeration type, not GTK_TYPE_ENUM so that bindings work. Add C-d, M-d, C-v bindings, change Home, End to move to beginning/end of line, Add C-Home C-End to move to beginning/end of buffer. Change ::cut_text to ::cut_clipboard, etc; combine ::scroll_text into ::move; use new GtkSelectionData functions to simplify DND text handling. * gtk/gtkenums.h gtk/gtktextview.h: Move movement, deletion enumerations here, rename enumeration values to be consistently plural. * gtk/gtktextbuffer.c: Use new clipboard interfaces for cut/copy/paste and primary selection. * gtk/gtktextbuffer.[ch]: Remove excess time and 'interactive' arguments from cut/copy/paste; rename cut to cut_clipboard, etc; remove gtk_text_buffer_get_clipboard_contents(). * gtk/gtktextlayout.[ch]: Add gtk_text_layout_move_iter_to_line_end() to move the iter to line ends. * gtk/gtkselection.[ch] (gtk_selection_data_set/get_text): Functions to set or get a UTF-8 string on the selection data. * gtk/gtkclipboard.[ch]: New, simplified selection handling interfaces. * gtk/gtkinvisible.c (gtk_invisible_new): Realize newly created widgets - one of these is useless if we don't. * gtk/gtkselection.[ch] (gtk_selection_clear_targets): Export a public function clear all targets registered for the widget. * gtk/gtkselection.c (gtk_selection_owner_set) docs/Changes-2.0.txt: Never call gtk_widget_realize() - that was just asking for bizarre side-effects. * gtk/gtkselection.c (gtk_selection_owner_set): Call gdk_selection_owner_set even if the widget is the same so that we reliably update the timestamp on the server. * gdk/x11/gdkevents-x11.c gdk/x11/gdkx.h: Add a gdk_x11_get_server_time() function. * gdk/x11/gdkevents-x11.c gdk/x11/gdkprivate-x11.h gdk/x11/gdkselection-x11.c gdk/x11/gdkwindow-x11.h: Add some tricky filtering on serial numbers for selection clear events to fix up long-standard race condition FIXME's in gtkselection.c. * gdk/gdkproperty.h gdk/x11/gdkselection-x11.h: Add routines to convert from utf8 to compound text or STRING and from a text property to UTF-8. * gtk/gtkmain.[ch] (gtk_get_current_event_time): Add a convenience function gdk_get_current_event_time(). * gtk/gtkselection.c (gtk_selection_data_copy/free): Copy and free selection_data->data properly --- gdk/gdkproperty.h | 13 + gdk/x11/gdkevents-x11.c | 70 +++- gdk/x11/gdkprivate-x11.h | 6 +- gdk/x11/gdkselection-x11.c | 390 ++++++++++++++++++ gdk/x11/gdkwindow-x11.c | 2 + gdk/x11/gdkx.h | 2 + gtk/gtkclipboard.c | 806 +++++++++++++++++++++++++++++++++++++ gtk/gtkclipboard.h | 87 ++++ gtk/gtkenums.h | 25 ++ gtk/gtkinvisible.c | 5 +- gtk/gtkmain.c | 29 +- gtk/gtkmain.h | 4 +- gtk/gtkselection.c | 233 +++++++++-- gtk/gtkselection.h | 47 ++- gtk/gtktextbuffer.c | 635 ++++++++--------------------- gtk/gtktextbuffer.h | 35 +- gtk/gtktextlayout.c | 54 +++ gtk/gtktextlayout.h | 4 +- gtk/gtktextview.c | 574 ++++++++++---------------- gtk/gtktextview.h | 44 +- 20 files changed, 2082 insertions(+), 983 deletions(-) create mode 100644 gtk/gtkclipboard.c create mode 100644 gtk/gtkclipboard.h diff --git a/gdk/gdkproperty.h b/gdk/gdkproperty.h index 2bb9770229..2255d30d3c 100644 --- a/gdk/gdkproperty.h +++ b/gdk/gdkproperty.h @@ -43,6 +43,19 @@ gint gdk_text_property_to_text_list (GdkAtom encoding, const guchar *text, gint length, gchar ***list); +gint gdk_text_property_to_utf8_list (GdkAtom encoding, + gint format, + const guchar *text, + gint length, + gchar ***list); + +gchar *gdk_utf8_to_string_target (const gchar *str); +gboolean gdk_utf8_to_compound_text (const gchar *str, + GdkAtom *encoding, + gint *format, + guchar **ctext, + gint *length); + void gdk_free_text_list (gchar **list); gint gdk_string_to_compound_text (const gchar *str, GdkAtom *encoding, diff --git a/gdk/x11/gdkevents-x11.c b/gdk/x11/gdkevents-x11.c index a389dc4afa..cb1e107112 100644 --- a/gdk/x11/gdkevents-x11.c +++ b/gdk/x11/gdkevents-x11.c @@ -1059,12 +1059,17 @@ gdk_event_translate (GdkEvent *event, GDK_NOTE (EVENTS, g_message ("selection clear:\twindow: %ld", xevent->xproperty.window)); - - event->selection.type = GDK_SELECTION_CLEAR; - event->selection.window = window; - event->selection.selection = xevent->xselectionclear.selection; - event->selection.time = xevent->xselectionclear.time; - + + if (_gdk_selection_filter_clear_event (&xevent->xselectionclear)) + { + event->selection.type = GDK_SELECTION_CLEAR; + event->selection.window = window; + event->selection.selection = xevent->xselectionclear.selection; + event->selection.time = xevent->xselectionclear.time; + } + else + return_val = FALSE; + break; case SelectionRequest: @@ -1494,4 +1499,57 @@ gdk_flush (void) XSync (gdk_display, False); } +static GdkAtom timestamp_prop_atom = 0; + +static Bool +timestamp_predicate (Display *display, + XEvent *xevent, + XPointer arg) +{ + Window xwindow = GPOINTER_TO_UINT (arg); + + if (xevent->type == PropertyNotify && + xevent->xproperty.window == xwindow && + xevent->xproperty.atom == timestamp_prop_atom) + return True; + + return False; +} + +/** + * gdk_x11_get_server_time: + * @window: a #GdkWindow, used for communication with the server. + * The window must have GDK_PROPERTY_CHANGE_MASK in its + * events mask or a hang will result. + * + * Routine to get the current X server time stamp. + * + * Return value: the time stamp. + **/ +guint32 +gdk_x11_get_server_time (GdkWindow *window) +{ + Display *xdisplay; + Window xwindow; + guchar c = 'a'; + XEvent xevent; + + g_return_val_if_fail (GDK_IS_WINDOW (window), 0); + g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0); + + if (!timestamp_prop_atom) + timestamp_prop_atom = gdk_atom_intern ("GDK_TIMESTAMP_PROP", FALSE); + + xdisplay = GDK_WINDOW_XDISPLAY (window); + xwindow = GDK_WINDOW_XWINDOW (window); + + XChangeProperty (xdisplay, xwindow, + timestamp_prop_atom, timestamp_prop_atom, + 8, PropModeReplace, &c, 1); + + XIfEvent (xdisplay, &xevent, + timestamp_predicate, GUINT_TO_POINTER(xwindow)); + + return xevent.xproperty.time; +} diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h index a521412622..8a600ee694 100644 --- a/gdk/x11/gdkprivate-x11.h +++ b/gdk/x11/gdkprivate-x11.h @@ -73,13 +73,15 @@ void _gdk_window_process_expose (GdkWindow *window, gulong serial, GdkRectangle *area); +void _gdk_selection_window_destroyed (GdkWindow *window); +gboolean _gdk_selection_filter_clear_event (XSelectionClearEvent *event); + extern GdkDrawableClass _gdk_x11_drawable_class; extern gboolean gdk_use_xshm; extern Atom gdk_wm_delete_window; extern Atom gdk_wm_take_focus; extern Atom gdk_wm_protocols; extern Atom gdk_wm_window_protocols[]; -extern GdkWindow *selection_owner[]; extern gboolean gdk_null_window_warnings; extern const int gdk_nevent_masks; extern const int gdk_event_mask_table[]; @@ -95,5 +97,3 @@ extern GdkWindow *gdk_xim_window; /* currently using Window */ #endif /* __GDK_PRIVATE_X11_H__ */ - - diff --git a/gdk/x11/gdkselection-x11.c b/gdk/x11/gdkselection-x11.c index 232dcf6394..1879226788 100644 --- a/gdk/x11/gdkselection-x11.c +++ b/gdk/x11/gdkselection-x11.c @@ -33,6 +33,66 @@ #include "gdkprivate.h" #include "gdkprivate-x11.h" +typedef struct _OwnerInfo OwnerInfo; + +struct _OwnerInfo +{ + GdkAtom selection; + GdkWindow *owner; + gulong serial; +}; + +GSList *owner_list; + +/* When a window is destroyed we check if it is the owner + * of any selections. This is somewhat inefficient, but + * owner_list is typically short, and it is a low memory, + * low code solution + */ +void +_gdk_selection_window_destroyed (GdkWindow *window) +{ + GSList *tmp_list = owner_list; + while (tmp_list) + { + OwnerInfo *info = tmp_list->data; + if (info->owner == window) + { + owner_list = g_slist_remove (owner_list, info); + g_free (info); + } + tmp_list = tmp_list->next; + } +} + +/* We only pass through those SelectionClear events that actually + * reflect changes to the selection owner that we didn't make ourself. + */ +gboolean +_gdk_selection_filter_clear_event (XSelectionClearEvent *event) +{ + GSList *tmp_list = owner_list; + + while (tmp_list) + { + OwnerInfo *info = tmp_list->data; + if (info->selection == event->selection) + { + if ((GDK_DRAWABLE_XID (info->owner) == event->window && + event->serial >= info->serial)) + { + owner_list = g_slist_remove (owner_list, info); + g_free (info); + return TRUE; + } + else + return FALSE; + } + tmp_list = tmp_list->next; + } + + return FALSE; +} gboolean gdk_selection_owner_set (GdkWindow *owner, @@ -42,6 +102,8 @@ gdk_selection_owner_set (GdkWindow *owner, { Display *xdisplay; Window xwindow; + GSList *tmp_list; + OwnerInfo *info; if (owner) { @@ -56,6 +118,29 @@ gdk_selection_owner_set (GdkWindow *owner, xdisplay = gdk_display; xwindow = None; } + + tmp_list = owner_list; + while (tmp_list) + { + info = tmp_list->data; + if (info->selection == selection) + { + owner_list = g_slist_remove (owner_list, info); + g_free (info); + break; + } + tmp_list = tmp_list->next; + } + + if (owner) + { + info = g_new (OwnerInfo, 1); + info->owner = owner; + info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner)); + info->selection = selection; + + owner_list = g_slist_prepend (owner_list, info); + } XSetSelectionOwner (xdisplay, selection, xwindow, time); @@ -221,6 +306,166 @@ gdk_free_text_list (gchar **list) XFreeStringList (list); } +static gint +make_list (const gchar *text, + gint length, + gboolean latin1, + gchar ***list) +{ + GSList *strings = NULL; + gint n_strings = 0; + gint i; + const gchar *p = text; + const gchar *q; + GSList *tmp_list; + GError *error = NULL; + + while (p < text + length) + { + gchar *str; + + q = p; + while (*q && q < text + length) + q++; + + if (latin1) + { + str = g_convert (p, q - p, + "UTF-8", "ISO-8859-1", + NULL, NULL, &error); + + if (!str) + { + g_warning ("Error converting selection from STRING: %s", + error->message); + g_error_free (error); + } + } + else + str = g_strndup (p, q - p); + + if (str) + { + strings = g_slist_prepend (strings, str); + n_strings++; + } + + p = q + 1; + } + + if (list) + *list = g_new (gchar *, n_strings + 1); + + (*list)[n_strings] = NULL; + + i = n_strings; + tmp_list = strings; + while (tmp_list) + { + if (list) + (*list)[--i] = tmp_list->data; + else + g_free (tmp_list->data); + + tmp_list = tmp_list->next; + } + + g_slist_free (strings); + + return n_strings; +} + +/** + * gdk_text_property_to_utf8_list: + * @encoding: an atom representing the encoding of the text + * @format: the format of the property + * @text: the text to convert + * @length: the length of @text, in bytes + * @list: location to store the list of strings or %NULL. The + * list should be freed with g_strfreev(). + * + * Convert a text property in the giving encoding to + * a list of UTF-8 strings. + * + * Return value: the number of strings in the resulting + * list. + **/ +gint +gdk_text_property_to_utf8_list (GdkAtom encoding, + gint format, + const guchar *text, + gint length, + gchar ***list) +{ + g_return_val_if_fail (text != NULL, 0); + g_return_val_if_fail (length >= 0, 0); + + if (encoding == GDK_TARGET_STRING) + { + return make_list ((gchar *)text, length, TRUE, list); + } + else if (encoding == gdk_atom_intern ("UTF8_STRING", FALSE)) + { + return make_list ((gchar *)text, length, FALSE, list); + } + else + { + gchar **local_list; + gint local_count; + gint i; + gchar *charset = NULL; + gboolean need_conversion= g_get_charset (&charset); + gint count = 0; + GError *error = NULL; + + /* Probably COMPOUND text, we fall back to Xlib routines + */ + local_count = gdk_text_property_to_text_list (encoding, + format, + text, + length, + &local_list); + if (list) + *list = g_new (gchar *, local_count + 1); + + for (i=0; imessage); + g_error_free (error); + error = NULL; + } + } + else + { + if (list) + (*list)[count++] = g_strdup (local_list[i]); + } + } + + gdk_free_text_list (local_list); + (*list)[count] = NULL; + + return count; + } +} + gint gdk_string_to_compound_text (const gchar *str, GdkAtom *encoding, @@ -254,6 +499,151 @@ gdk_string_to_compound_text (const gchar *str, return res; } +/* The specifications for COMPOUND_TEXT and STRING specify that C0 and + * C1 are not allowed except for \n and \t, however the X conversions + * routines for COMPOUND_TEXT only enforce this in one direction, + * causing cut-and-paste of \r and \r\n separated text to fail. + * This routine strips out all non-allowed C0 and C1 characters + * from the input string and also canonicalizes \r, \r\n, and \n\r to \n + */ +static gchar * +sanitize_utf8 (const gchar *src) +{ + gint len = strlen (src); + GString *result = g_string_sized_new (len); + const gchar *p = src; + + while (*p) + { + if (*p == '\r' || *p == '\n') + { + p++; + if (*p == '\r' || *p == '\n') + p++; + + g_string_append_c (result, '\n'); + } + else + { + gunichar ch = g_utf8_get_char (p); + char buf[7]; + gint buflen; + + if (!((ch < 0x20 && ch != '\t') || (ch >= 0x7f && ch < 0xa0))) + { + buflen = g_unichar_to_utf8 (ch, buf); + g_string_append_len (result, buf, buflen); + } + + p = g_utf8_next_char (p); + } + } + + return g_string_free (result, FALSE); +} + +/** + * gdk_utf8_to_string_target: + * @str: a UTF-8 string + * + * Convert an UTF-8 string into the best possible representation + * as a STRING. The representation of characters not in STRING + * is not specified; it may be as pseudo-escape sequences + * \x{ABCD}, or it may be in some other form of approximation. + * + * Return value: the newly allocated string, or %NULL if the + * conversion failed. (It should not fail for + * any properly formed UTF-8 string.) + **/ +gchar * +gdk_utf8_to_string_target (const gchar *str) +{ + GError *error = NULL; + + gchar *tmp_str = sanitize_utf8 (str); + gchar *result = g_convert_with_fallback (tmp_str, -1, + "ISO-8859-1", "UTF-8", + NULL, NULL, NULL, &error); + if (!result) + { + g_warning ("Error converting from UTF-8 to STRING: %s", + error->message); + g_error_free (error); + } + + g_free (tmp_str); + return result; +} + +/** + * gdk_utf8_to_compound_text: + * @str: a UTF-8 string + * @encoding: location to store resulting encoding + * @format: location to store format of the result + * @ctext: location to store the data of the result + * @length: location to store the length of the data + * stored in @ctext + * + * Convert from UTF-8 to compound text. + * + * Return value: %TRUE if the conversion succeeded, otherwise + * false. + **/ +gboolean +gdk_utf8_to_compound_text (const gchar *str, + GdkAtom *encoding, + gint *format, + guchar **ctext, + gint *length) +{ + gboolean need_conversion; + gchar *charset; + gchar *locale_str, *tmp_str; + GError *error = NULL; + gboolean result; + + g_return_val_if_fail (str != NULL, FALSE); + + need_conversion = g_get_charset (&charset); + + tmp_str = sanitize_utf8 (str); + + if (need_conversion) + { + locale_str = g_convert_with_fallback (tmp_str, -1, + charset, "UTF-8", + NULL, NULL, NULL, &error); + g_free (tmp_str); + + if (!locale_str) + { + g_warning ("Error converting from UTF-8 to '%s': %s", + charset, error->message); + g_error_free (error); + + if (encoding) + *encoding = None; + if (format) + *format = None; + if (ctext) + *ctext = NULL; + if (length) + *length = 0; + + return FALSE; + } + } + else + locale_str = tmp_str; + + result = gdk_string_to_compound_text (locale_str, + encoding, format, ctext, length); + + g_free (locale_str); + + return result; +} + void gdk_free_compound_text (guchar *ctext) { if (ctext) diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c index d4741a4f72..761782d9f0 100644 --- a/gdk/x11/gdkwindow-x11.c +++ b/gdk/x11/gdkwindow-x11.c @@ -630,6 +630,8 @@ _gdk_windowing_window_destroy (GdkWindow *window, GdkWindowObject *private = (GdkWindowObject *)window; g_return_if_fail (GDK_IS_WINDOW (window)); + + _gdk_selection_window_destroyed (window); if (private->extension_events != 0) gdk_input_window_destroy (window); diff --git a/gdk/x11/gdkx.h b/gdk/x11/gdkx.h index 72c7d72bd2..697afea9ad 100644 --- a/gdk/x11/gdkx.h +++ b/gdk/x11/gdkx.h @@ -178,6 +178,8 @@ GdkWindow *gdk_window_foreign_new (GdkNativeWindow anid); /* Return the Gdk* for a particular XID */ gpointer gdk_xid_table_lookup (XID xid); +guint32 gdk_x11_get_server_time (GdkWindow *window); + #define gdk_window_lookup(xid) ((GdkWindow*) gdk_xid_table_lookup (xid)) #define gdk_pixmap_lookup(xid) ((GdkPixmap*) gdk_xid_table_lookup (xid)) #define gdk_font_lookup(xid) ((GdkFont*) gdk_xid_table_lookup (xid)) diff --git a/gtk/gtkclipboard.c b/gtk/gtkclipboard.c new file mode 100644 index 0000000000..6df1843cfb --- /dev/null +++ b/gtk/gtkclipboard.c @@ -0,0 +1,806 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2000 Red Hat, Inc. + * + * 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 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Global clipboard abstraction. + */ + +#include + +#include "gtkclipboard.h" +#include "gtkinvisible.h" +#include "gtkmain.h" +#include "gtksignal.h" + +#ifdef GDK_WINDOWING_X11 +#include "x11/gdkx.h" +#endif + +typedef struct _RequestContentsInfo RequestContentsInfo; +typedef struct _RequestTextInfo RequestTextInfo; + +struct _GtkClipboard +{ + GdkAtom selection; + + GtkClipboardGetFunc get_func; + GtkClipboardClearFunc clear_func; + gpointer user_data; + gboolean have_owner; + + guint32 timestamp; + + gboolean have_selection; +}; + +struct _RequestContentsInfo +{ + GtkClipboardReceivedFunc callback; + gpointer user_data; +}; + +struct _RequestTextInfo +{ + GtkClipboardTextReceivedFunc callback; + gpointer user_data; +}; + +static void clipboard_unset (GtkClipboard *clipboard); +static void selection_received (GtkWidget *widget, + GtkSelectionData *selection_data, + guint time); + +static GSList *clipboards; +static GtkWidget *clipboard_widget; + +enum { + TARGET_STRING, + TARGET_TEXT, + TARGET_COMPOUND_TEXT, + TARGET_UTF8_STRING +}; + +static const gchar *request_contents_key = "gtk-request-contents"; +static GQuark request_contents_key_id = 0; + +static const gchar *clipboards_owned_key = "gtk-clipboards-owned"; +static GQuark clipboards_owned_key_id = 0; + + +/** + * gtk_clipboard_get: + * @selection: a #GdkAtom which identifies the clipboard + * to use. A value of GDK_NONE here is the + * same as gdk_atom_intern ("CLIPBOARD", FALSE), + * and provides the default clipboard. Another + * common value is GDK_SELECTION_PRIMARY, which + * identifies the primary X selection. + * + * Returns the clipboard object for the given selection. + * + * Return value: the appropriate clipboard object. If no + * clipboard already exists, a new one will + * be created. Once a clipboard object has + * been created, it is persistant for all time. + **/ +GtkClipboard * +gtk_clipboard_get (GdkAtom selection) +{ + GtkClipboard *clipboard = NULL; + GSList *tmp_list; + + if (selection == GDK_NONE) + selection = gdk_atom_intern ("CLIPBOARD", FALSE); + + tmp_list = clipboards; + while (tmp_list) + { + clipboard = tmp_list->data; + if (clipboard->selection == selection) + break; + + tmp_list = tmp_list->next; + } + + if (!tmp_list) + { + clipboard = g_new0 (GtkClipboard, 1); + clipboard->selection = selection; + clipboards = g_slist_prepend (clipboards, clipboard); + } + + return clipboard; +} + +static void +selection_get_cb (GtkWidget *widget, + GtkSelectionData *selection_data, + guint time, + guint info) +{ + GtkClipboard *clipboard = gtk_clipboard_get (selection_data->selection); + + if (clipboard && clipboard->get_func) + clipboard->get_func (clipboard, selection_data, info, clipboard->user_data); +} + +static gboolean +selection_clear_event_cb (GtkWidget *widget, + GdkEventSelection *event) +{ + GtkClipboard *clipboard = gtk_clipboard_get (event->selection); + + if (clipboard) + { + clipboard_unset (clipboard); + return TRUE; + } + + return FALSE; +} + +GtkWidget * +make_clipboard_widget (gboolean provider) +{ + GtkWidget *widget = gtk_invisible_new (); + + gtk_signal_connect (GTK_OBJECT (widget), "selection_received", + GTK_SIGNAL_FUNC (selection_received), NULL); + + if (provider) + { + /* We need this for gdk_x11_get_server_time() */ + gtk_widget_add_events (widget, GDK_PROPERTY_CHANGE_MASK); + + gtk_signal_connect (GTK_OBJECT (widget), "selection_get", + GTK_SIGNAL_FUNC (selection_get_cb), NULL); + gtk_signal_connect (GTK_OBJECT (widget), "selection_clear_event", + GTK_SIGNAL_FUNC (selection_clear_event_cb), NULL); + } + + return widget; +} + + +void +ensure_clipboard_widget () +{ + if (!clipboard_widget) + clipboard_widget = make_clipboard_widget (TRUE); +} + +/* This function makes a very good guess at what the correct + * timestamp for a selection request should be. If there is + * a currently processed event, it uses the timestamp for that + * event, otherwise it uses the current server time. However, + * if the time resulting from that is older than the time used + * last time, it uses the time used last time instead. + * + * In order implement this correctly, we never use CurrentTime, + * but actually retrieve the actual timestamp from the server. + * This is a little slower but allows us to make the guarantee + * that the times used by this application will always ascend + * and we won't get selections being rejected just because + * we are using a correct timestamp from an event, but used + * CurrentTime previously. + */ +static guint32 +clipboard_get_timestamp (GtkClipboard *clipboard) +{ + guint32 timestamp = gtk_get_current_event_time (); + + ensure_clipboard_widget (); + + if (timestamp == GDK_CURRENT_TIME) + { + timestamp = gdk_x11_get_server_time (clipboard_widget->window); + } + else + { + if (clipboard->timestamp != GDK_CURRENT_TIME) + { + /* Check to see if clipboard->timestamp is newer than + * timestamp, accounting for wraparound. + */ + + guint32 max = timestamp + 0x80000000; + + if ((max > timestamp && + (clipboard->timestamp > timestamp && + clipboard->timestamp <= max)) || + (max <= timestamp && + (clipboard->timestamp > timestamp || + clipboard->timestamp <= max))) + { + timestamp = clipboard->timestamp; + } + } + } + + clipboard->timestamp = timestamp; + + return timestamp; +} + +static void +clipboard_owner_destroyed (gpointer data) +{ + GSList *clipboards = data; + GSList *tmp_list; + + tmp_list = clipboards; + while (tmp_list) + { + GtkClipboard *clipboard = tmp_list->data; + + clipboard->get_func = NULL; + clipboard->clear_func = NULL; + clipboard->user_data = NULL; + clipboard->have_owner = FALSE; + + gtk_clipboard_clear (clipboard); + + tmp_list = tmp_list->next; + } + + g_slist_free (clipboards); +} + +static void +clipboard_add_owner_notify (GtkClipboard *clipboard) +{ + if (!clipboards_owned_key_id) + clipboards_owned_key_id = g_quark_from_static_string (clipboards_owned_key); + + if (clipboard->have_owner) + g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id, + g_slist_prepend (g_object_steal_qdata (clipboard->user_data, + clipboards_owned_key_id), + clipboard), + clipboard_owner_destroyed); +} + +static void +clipboard_remove_owner_notify (GtkClipboard *clipboard) +{ + if (clipboard->have_owner) + g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id, + g_slist_remove (g_object_steal_qdata (clipboard->user_data, + clipboards_owned_key_id), + clipboard), + clipboard_owner_destroyed); +} + +static gboolean +gtk_clipboard_set_contents (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + gpointer user_data, + gboolean have_owner) +{ + ensure_clipboard_widget (); + + if (gtk_selection_owner_set (clipboard_widget, clipboard->selection, + clipboard_get_timestamp (clipboard))) + { + clipboard->have_selection = TRUE; + + if (!(clipboard->have_owner && have_owner) || + clipboard->user_data != user_data) + { + clipboard_unset (clipboard); + + if (clipboard->get_func) + { + /* Calling unset() caused the clipboard contents to be reset! + * Avoid leaking and return + */ + if (!(clipboard->have_owner && have_owner) || + clipboard->user_data != user_data) + { + (*clear_func) (clipboard, user_data); + return FALSE; + } + else + return TRUE; + } + else + { + clipboard->user_data = user_data; + clipboard->have_owner = have_owner; + if (have_owner) + clipboard_add_owner_notify (clipboard); + } + + } + + clipboard->get_func = get_func; + clipboard->clear_func = clear_func; + + gtk_selection_clear_targets (clipboard_widget, clipboard->selection); + gtk_selection_add_targets (clipboard_widget, clipboard->selection, + targets, n_targets); + + return TRUE; + } + else + return FALSE; +} + +/** + * gtk_clipboard_set_with_data: + * @clipboard: a #GtkClipboard + * @targets: array containing information about the available forms for the + * clipboard data + * @n_targets: number of elements in @targets + * @get_func: function to call to get the actual clipboard data + * @clear_func: when the clipboard contents are set again, this function will + * be called, and get_func will not be subsequently called. + * @user_data: user data to pass to @get_func and @clear_func. + * + * Virtually set the contents of the specified clipboard by providing + * a list of supported formats for the clipboard data and a function + * to call to get the actual data when it is requested. + * + * Return value: %TRUE if setting the clipboard data succeeded. If setting + * the clipboard data failed the provided callback functions + * will be ignored. + **/ +gboolean +gtk_clipboard_set_with_data (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + gpointer user_data) +{ + g_return_val_if_fail (clipboard != NULL, FALSE); + g_return_val_if_fail (targets != NULL, FALSE); + g_return_val_if_fail (get_func != NULL, FALSE); + + return gtk_clipboard_set_contents (clipboard, targets, n_targets, + get_func, clear_func, user_data, + FALSE); +} + +/** + * gtk_clipboard_set_with_owner: + * @clipboard: a #GtkClipboard + * @targets: array containing information about the available forms for the + * clipboard data + * @n_targets: number of elements in @targets + * @get_func: function to call to get the actual clipboard data + * @clear_func: when the clipboard contents are set again, this function will + * be called, and get_func will not be subsequently called. + * @owner: an object that "owns" the data. This object will be passed + * to the callbacks when called. + * + * Virtually set the contents of the specified clipboard by providing + * a list of supported formats for the clipboard data and a function + * to call to get the actual data when it is requested. + * + * The difference between this function and gtk_clipboard_set_with_data + * is that instead of an generic @user_data pointer, a #GObject is passed + * in. Because of this, + * + * Return value: %TRUE if setting the clipboard data succeeded. If setting + * the clipboard data failed the provided callback functions + * will be ignored. + **/ +gboolean +gtk_clipboard_set_with_owner (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + GObject *owner) +{ + g_return_val_if_fail (clipboard != NULL, FALSE); + g_return_val_if_fail (targets != NULL, FALSE); + g_return_val_if_fail (get_func != NULL, FALSE); + g_return_val_if_fail (G_IS_OBJECT (owner), FALSE); + + return gtk_clipboard_set_contents (clipboard, targets, n_targets, + get_func, clear_func, owner, + TRUE); +} + +/** + * gtk_clipboard_get_owner: + * @clipboard: a #GtkClipboard + * + * If the clipboard contents callbacks were set with gtk_clipboard_set_owner(), + * and the gtk_clipboard_set_with_data() or gtk_clipboard_clear() has not + * subsequently called, returns the @owner set by gtk_clipboard_set_owner(). + * + * Return value: the owner of the clipboard, if any; otherwise %NULL. + **/ +GObject * +gtk_clipboard_get_owner (GtkClipboard *clipboard) +{ + g_return_val_if_fail (clipboard != NULL, NULL); + + if (clipboard->have_owner) + return clipboard->user_data; + else + return NULL; +} + +static void +clipboard_unset (GtkClipboard *clipboard) +{ + GtkClipboardClearFunc old_clear_func; + gpointer old_data; + + old_clear_func = clipboard->clear_func; + old_data = clipboard->user_data; + + if (clipboard->have_owner) + { + clipboard_remove_owner_notify (clipboard); + clipboard->have_owner = FALSE; + } + + clipboard->get_func = NULL; + clipboard->clear_func = NULL; + clipboard->user_data = NULL; + + if (old_clear_func) + old_clear_func (clipboard, old_data); +} + +/** + * gtk_clipboard_clear: + * @clipboard: a #GtkClipboard + * + * Clear the contents of the clipboard. Generally this should only + * be called between the time you call gtk_clipboard_set_contents(), + * and when the @clear_func you supplied is called. Otherwise, the + * clipboard may be owned by someone else. + **/ +void +gtk_clipboard_clear (GtkClipboard *clipboard) +{ + g_return_if_fail (clipboard != NULL); + + if (clipboard->have_selection) + gtk_selection_owner_set (NULL, clipboard->selection, + clipboard_get_timestamp (clipboard)); +} + +void +text_get_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + gtk_selection_data_set_text (selection_data, data); +} + +void +text_clear_func (GtkClipboard *clipboard, + gpointer data) +{ + g_free (data); +} + +/** + * gtk_clipboard_set_text: + * @clipboard: a #GtkClipboard object + * @text: a UTF-8 string. + * @len: length of @text, in bytes, or -1, in which case + * the length will be determined with strlen(). + * + * Set the contents of the clipboard to the given UTF-8 string. GTK+ will + * make a copy of the text and take responsibility for responding + * for requests for the text, and for converting the text into + * the requested format. + **/ +void +gtk_clipboard_set_text (GtkClipboard *clipboard, + const gchar *text, + gint len) +{ + static const GtkTargetEntry targets[] = { + { "STRING", 0, TARGET_STRING }, + { "TEXT", 0, TARGET_TEXT }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, + { "UTF8_STRING", 0, TARGET_UTF8_STRING } + }; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (text != NULL); + + if (len < 0) + len = strlen (text); + + gtk_clipboard_set_with_data (clipboard, + targets, G_N_ELEMENTS (targets), + text_get_func, text_clear_func, + g_strndup (text, len)); +} + +static void +set_request_contents_info (GtkWidget *widget, + RequestContentsInfo *info) +{ + if (!request_contents_key_id) + request_contents_key_id = g_quark_from_static_string (request_contents_key); + + gtk_object_set_data_by_id (GTK_OBJECT (widget), + request_contents_key_id, + info); +} + +static RequestContentsInfo * +get_request_contents_info (GtkWidget *widget) +{ + if (!request_contents_key_id) + return NULL; + else + return gtk_object_get_data_by_id (GTK_OBJECT (widget), + request_contents_key_id); +} + +static void +selection_received (GtkWidget *widget, + GtkSelectionData *selection_data, + guint time) +{ + RequestContentsInfo *request_info = get_request_contents_info (widget); + set_request_contents_info (widget, NULL); + + request_info->callback (gtk_clipboard_get (selection_data->selection), + selection_data, + request_info->user_data); + + g_free (request_info); + + if (widget != clipboard_widget) + gtk_widget_destroy (widget); +} + +/** + * gtk_clipboard_request_contents: + * @clipboard: a #GtkClipboard + * @target: an atom representing the form into which the clipboard + * owner should convert the selection. + * @callback: A function to call when the results are received + * (or the retrieval fails.) If the retrieval fails + * the length field of @selection_data will be + * negative. + * @user_data: user data to pass to @callback + * + * Requests the contents of clipboard as the given target. + * When the results of the result are later received the supplied callback + * will be called. + **/ +void +gtk_clipboard_request_contents (GtkClipboard *clipboard, + GdkAtom target, + GtkClipboardReceivedFunc callback, + gpointer user_data) +{ + RequestContentsInfo *info; + GtkWidget *widget; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (target != GDK_NONE); + g_return_if_fail (callback != NULL); + + ensure_clipboard_widget (); + + if (get_request_contents_info (clipboard_widget)) + widget = make_clipboard_widget (FALSE); + else + widget = clipboard_widget; + + info = g_new (RequestContentsInfo, 1); + info->callback = callback; + info->user_data = user_data; + + set_request_contents_info (widget, info); + + gtk_selection_convert (widget, clipboard->selection, target, + clipboard_get_timestamp (clipboard)); +} + +static void +request_text_received_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + RequestTextInfo *info = data; + gchar *result = NULL; + + result = gtk_selection_data_get_text (selection_data); + + if (!result) + { + /* If we asked for UTF8 and didn't get it, try text; if we asked + * for text and didn't get it, try string. If we asked for + * anything else and didn't get it, give up. + */ + if (selection_data->target == gdk_atom_intern ("UTF8_STRING", FALSE)) + { + gtk_clipboard_request_contents (clipboard, + gdk_atom_intern ("TEXT", FALSE), + request_text_received_func, info); + return; + } + else if (selection_data->target == gdk_atom_intern ("TEXT", FALSE)) + { + gtk_clipboard_request_contents (clipboard, + GDK_TARGET_STRING, + request_text_received_func, info); + return; + } + } + + info->callback (clipboard, result, info->user_data); + g_free (info); + g_free (result); +} + +/** + * gtk_clipboard_request_text: + * @clipboard: a #GtkClipboard + * @callback: a function to call when the text is received, + * or the retrieval fails. (It will always be called + * one way or the other.) + * @user_data: user data to pass to @callback. + * + * Requests the contents of the clipboard as text. When the text is + * later received, it will be converted to UTF-8 if necessary, and + * @callback will be called. + * + * The @text parameter to @callback will contain the resulting text if + * the request succeeded, or %NULL if it failed. This could happen for + * various reasons, in particular if the clipboard was empty or if the + * contents of the clipboard could not be converted into text form. + **/ +void +gtk_clipboard_request_text (GtkClipboard *clipboard, + GtkClipboardTextReceivedFunc callback, + gpointer user_data) +{ + RequestTextInfo *info; + + g_return_if_fail (clipboard != NULL); + g_return_if_fail (callback != NULL); + + info = g_new (RequestTextInfo, 1); + info->callback = callback; + info->user_data = user_data; + + gtk_clipboard_request_contents (clipboard, gdk_atom_intern ("UTF8_STRING", FALSE), + request_text_received_func, + info); +} + + +typedef struct +{ + GMainLoop *loop; + gpointer data; +} WaitResults; + +static void +clipboard_received_func (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + WaitResults *results = data; + + if (selection_data->length >= 0) + results->data = gtk_selection_data_copy (selection_data); + + g_main_quit (results->loop); +} + +/** + * gtk_clipboard_wait_for_contents: + * @clipboard: a #GtkClipboard + * @target: an atom representing the form into which the clipboard + * owner should convert the selection. + * + * Requests the contents of the clipboard using the given target. + * This function waits for the data to be received using the main + * loop, so events, timeouts, etc, may be dispatched during the wait. + * + * Return value: a newly allocated #GtkSelectionData object or %NULL + * if retrieving the given target failed. If non-%NULL, + * this value freed with gtk_selection_data_free() when + * you are finished with it. + **/ +GtkSelectionData * +gtk_clipboard_wait_for_contents (GtkClipboard *clipboard, + GdkAtom target) +{ + WaitResults results; + + g_return_val_if_fail (clipboard != NULL, NULL); + g_return_val_if_fail (target != GDK_NONE, NULL); + + results.data = NULL; + results.loop = g_main_new (TRUE); + + gtk_clipboard_request_contents (clipboard, target, + clipboard_received_func, + &results); + + if (g_main_is_running (results.loop)) + g_main_run (results.loop); + + g_main_destroy (results.loop); + + return results.data; +} + +static void +clipboard_text_received_func (GtkClipboard *clipboard, + const gchar *text, + gpointer data) +{ + WaitResults *results = data; + + results->data = g_strdup (text); + g_main_quit (results->loop); +} + + +/** + * gtk_clipboard_wait_for_text: + * @clipboard: a #GtkClipboard + * + * Requests the contents of the clipboard as text and converts + * the result to UTF-8 if necessary. This function waits for + * the data to be received using the main loop, so events, + * timeouts, etc, may be dispatched during the wait. + * + * Return value: a newly allocated UTF-8 string which must + * be freed with g_free(), or %NULL if retrieving + * the selection data failed. (This could happen + * for various reasons, in particular if the + * clipboard was empty or if the contents of the + * clipboard could not be converted into text form.) + **/ +gchar * +gtk_clipboard_wait_for_text (GtkClipboard *clipboard) +{ + WaitResults results; + + g_return_val_if_fail (clipboard != NULL, NULL); + g_return_val_if_fail (clipboard != NULL, NULL); + + results.data = NULL; + results.loop = g_main_new (TRUE); + + gtk_clipboard_request_text (clipboard, + clipboard_text_received_func, + &results); + + if (g_main_is_running (results.loop)) + g_main_run (results.loop); + + g_main_destroy (results.loop); + + return results.data; +} + diff --git a/gtk/gtkclipboard.h b/gtk/gtkclipboard.h new file mode 100644 index 0000000000..f35630e9d8 --- /dev/null +++ b/gtk/gtkclipboard.h @@ -0,0 +1,87 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2000 Red Hat, Inc. + * + * 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 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 library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Global clipboard abstraction. + */ + +#ifndef __GTK_CLIPBOARD_H__ +#define __GTK_CLIPBOARD_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include + +typedef struct _GtkClipboard GtkClipboard; + +typedef void (* GtkClipboardReceivedFunc) (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data); +typedef void (* GtkClipboardTextReceivedFunc) (GtkClipboard *clipboard, + const gchar *text, + gpointer data); + +/* Should these functions have GtkClipboard *clipboard as the first argument? + * right now for ClearFunc, you may have trouble determining _which_ clipboard + * was cleared, if you reuse your ClearFunc for multiple clipboards. + */ +typedef void (* GtkClipboardGetFunc) (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer user_data_or_owner); +typedef void (* GtkClipboardClearFunc) (GtkClipboard *clipboard, + gpointer user_data_or_owner); + +GtkClipboard *gtk_clipboard_get (GdkAtom selection); + +gboolean gtk_clipboard_set_with_data (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + gpointer user_data); +gboolean gtk_clipboard_set_with_owner (GtkClipboard *clipboard, + const GtkTargetEntry *targets, + guint n_targets, + GtkClipboardGetFunc get_func, + GtkClipboardClearFunc clear_func, + GObject *owner); +GObject *gtk_clipboard_get_owner (GtkClipboard *clipboard); +void gtk_clipboard_clear (GtkClipboard *clipboard); +void gtk_clipboard_set_text (GtkClipboard *clipboard, + const gchar *text, + gint len); + +void gtk_clipboard_request_contents (GtkClipboard *clipboard, + GdkAtom target, + GtkClipboardReceivedFunc callback, + gpointer user_data); +void gtk_clipboard_request_text (GtkClipboard *clipboard, + GtkClipboardTextReceivedFunc callback, + gpointer user_data); + +GtkSelectionData *gtk_clipboard_wait_for_contents (GtkClipboard *clipboard, + GdkAtom target); +gchar * gtk_clipboard_wait_for_text (GtkClipboard *clipboard); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif __GTK_CLIPBOARD_H__ diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 1971b7e2ee..dd8fe45fc0 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -66,6 +66,19 @@ typedef enum GTK_CURVE_TYPE_FREE /* free form curve */ } GtkCurveType; +typedef enum { + GTK_DELETE_CHARS, + GTK_DELETE_WORD_ENDS, /* delete only the portion of the word to the + * left/right of cursor if we're in the middle + * of a word */ + GTK_DELETE_WORDS, + GTK_DELETE_DISPLAY_LINES, + GTK_DELETE_DISPLAY_LINE_ENDS, + GTK_DELETE_PARAGRAPH_ENDS, /* like C-k in Emacs (or its reverse) */ + GTK_DELETE_PARAGRAPHS, /* C-k in pico, kill whole line */ + GTK_DELETE_WHITESPACE, /* M-\ in Emacs */ +} GtkDeleteType; + /* Focus movement types */ typedef enum { @@ -128,6 +141,18 @@ typedef enum GTK_CENTIMETERS } GtkMetricType; +typedef enum { + GTK_MOVEMENT_CHARS, /* move by forw/back chars */ + GTK_MOVEMENT_POSITIONS, /* move by left/right chars */ + GTK_MOVEMENT_WORDS, /* move by forward/back words */ + GTK_MOVEMENT_DISPLAY_LINES, /* move up/down lines (wrapped lines) */ + GTK_MOVEMENT_DISPLAY_LINE_ENDS, /* move up/down lines (wrapped lines) */ + GTK_MOVEMENT_PARAGRAPHS, /* move up/down paragraphs (newline-ended lines) */ + GTK_MOVEMENT_PARAGRAPH_ENDS, /* move to either end of a paragraph */ + GTK_MOVEMENT_PAGES, /* move by pages */ + GTK_MOVEMENT_BUFFER_ENDS /* move to ends of the buffer */ +} GtkMovementStep; + /* Orientation for toolbars, etc. */ typedef enum { diff --git a/gtk/gtkinvisible.c b/gtk/gtkinvisible.c index c18c789348..e7d8cc4088 100644 --- a/gtk/gtkinvisible.c +++ b/gtk/gtkinvisible.c @@ -102,7 +102,10 @@ gtk_invisible_destroy (GtkObject *object) GtkWidget* gtk_invisible_new (void) { - return GTK_WIDGET (gtk_type_new (GTK_TYPE_INVISIBLE)); + GtkWidget *result = GTK_WIDGET (gtk_type_new (GTK_TYPE_INVISIBLE)); + gtk_widget_realize (result); + + return result; } static void diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 121beb0985..7a690d2acd 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -33,21 +33,12 @@ #include #include #include -#include "gtkbutton.h" #include "gtkdnd.h" #include "gtkcompat.h" -#include "gtkhscrollbar.h" -#include "gtkhseparator.h" #include "gtkmain.h" -#include "gtkpreview.h" #include "gtkrc.h" -#include "gtkscrolledwindow.h" #include "gtkselection.h" #include "gtksignal.h" -#include "gtktable.h" -#include "gtktext.h" -#include "gtkvbox.h" -#include "gtkvscrollbar.h" #include "gtkwidget.h" #include "gtkwindow.h" #include "gtkprivate.h" @@ -1292,11 +1283,28 @@ GdkEvent* gtk_get_current_event (void) { if (current_events) - return gdk_event_copy ((GdkEvent *) current_events->data); + return gdk_event_copy (current_events->data); else return NULL; } +/** + * gtk_get_current_event_time: + * + * If there is a current event and it has a timestamp, return that + * timestamp, otherwise return %GDK_CURRENT_TIME. + * + * Return value: the timestamp from the current event, or %GDK_CURRENT_TIME. + **/ +guint32 +gtk_get_current_event_time (void) +{ + if (current_events) + return gdk_event_get_time (current_events->data); + else + return GDK_CURRENT_TIME; +} + GtkWidget* gtk_get_event_widget (GdkEvent *event) { @@ -1315,7 +1323,6 @@ gtk_exit_func (void) if (gtk_initialized) { gtk_initialized = FALSE; - gtk_preview_uninit (); } } diff --git a/gtk/gtkmain.h b/gtk/gtkmain.h index ea9829b6b6..7383788564 100644 --- a/gtk/gtkmain.h +++ b/gtk/gtkmain.h @@ -179,7 +179,9 @@ guint gtk_key_snooper_install (GtkKeySnoopFunc snooper, gpointer func_data); void gtk_key_snooper_remove (guint snooper_handler_id); -GdkEvent* gtk_get_current_event (void); +GdkEvent* gtk_get_current_event (void); +guint32 gtk_get_current_event_time (void); + GtkWidget* gtk_get_event_widget (GdkEvent *event); diff --git a/gtk/gtkselection.c b/gtk/gtkselection.c index f2e07c3e32..e09da3d65e 100644 --- a/gtk/gtkselection.c +++ b/gtk/gtkselection.c @@ -313,7 +313,7 @@ gtk_target_list_find (GtkTargetList *list, * results: *************************************************************/ -gint +gboolean gtk_selection_owner_set (GtkWidget *widget, GdkAtom selection, guint32 time) @@ -322,34 +322,26 @@ gtk_selection_owner_set (GtkWidget *widget, GtkWidget *old_owner; GtkSelectionInfo *selection_info = NULL; GdkWindow *window; + + g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE); if (widget == NULL) window = NULL; else - { - if (!GTK_WIDGET_REALIZED (widget)) - gtk_widget_realize (widget); - - window = widget->window; - } - + window = widget->window; + tmp_list = current_selections; while (tmp_list) { - selection_info = (GtkSelectionInfo *)tmp_list->data; - - if (selection_info->selection == selection) - break; + if (((GtkSelectionInfo *)tmp_list->data)->selection == selection) + { + selection_info = tmp_list->data; + break; + } tmp_list = tmp_list->next; } - if (tmp_list == NULL) - selection_info = NULL; - else - if (selection_info->widget == widget) - return TRUE; - if (gdk_selection_owner_set (window, selection, time, TRUE)) { old_owner = NULL; @@ -373,8 +365,8 @@ gtk_selection_owner_set (GtkWidget *widget, selection_info->selection = selection; selection_info->widget = widget; selection_info->time = time; - current_selections = g_list_append (current_selections, - selection_info); + current_selections = g_list_prepend (current_selections, + selection_info); } else { @@ -384,9 +376,9 @@ gtk_selection_owner_set (GtkWidget *widget, } } /* If another widget in the application lost the selection, - * send it a GDK_SELECTION_CLEAR event, unless we're setting - * the owner to None, in which case an event will be sent */ - if (old_owner && (widget != NULL)) + * send it a GDK_SELECTION_CLEAR event. + */ + if (old_owner && old_owner != widget) { GdkEventSelection event; @@ -476,6 +468,43 @@ gtk_selection_target_list_remove (GtkWidget *widget) gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL); } +/** + * gtk_selection_clear_targets: + * @widget: a #GtkWidget + * @selection: an atom representing a selection + * + * Remove all targets registered for the given selection for the + * widget. + **/ +void +gtk_selection_clear_targets (GtkWidget *widget, + GdkAtom selection) +{ + GtkSelectionTargetList *sellist; + GList *tmp_list; + GList *lists; + + lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key); + + tmp_list = lists; + while (tmp_list) + { + sellist = tmp_list->data; + if (sellist->selection == selection) + { + lists = g_list_delete_link (lists, tmp_list); + gtk_target_list_unref (sellist->list); + g_free (sellist); + + break; + } + + tmp_list = tmp_list->next; + } + + gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists); +} + void gtk_selection_add_target (GtkWidget *widget, GdkAtom selection, @@ -505,6 +534,7 @@ gtk_selection_add_targets (GtkWidget *widget, gtk_target_list_add_table (list, targets, ntargets); } + /************************************************************* * gtk_selection_remove_all: * Removes all handlers and unsets ownership of all @@ -728,6 +758,122 @@ gtk_selection_data_set (GtkSelectionData *selection_data, selection_data->length = length; } +static GdkAtom utf8_atom; +static GdkAtom text_atom; +static GdkAtom ctext_atom; + +static void +init_atoms (void) +{ + if (!utf8_atom) + { + utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE); + text_atom = gdk_atom_intern ("TEXT", FALSE); + ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); + } +} + +/** + * gtk_selection_data_set_text: + * @selection_data: a #GtkSelectionData + * @str: a UTF-8 string + * + * Sets the contents of the selection from a UTF-8 encoded string. + * The string is converted to the form determined by + * @selection_data->target. + * + * Return value: %TRUE if the selection was succesfully set, + * otherwise %FALSE. + **/ +gboolean +gtk_selection_data_set_text (GtkSelectionData *selection_data, + const guchar *str) +{ + init_atoms (); + + if (selection_data->target == utf8_atom) + { + gtk_selection_data_set (selection_data, + utf8_atom, + 8, (guchar *)str, strlen (str)); + return TRUE; + } + else if (selection_data->target == GDK_TARGET_STRING) + { + gchar *latin1 = gdk_utf8_to_string_target (str); + + if (latin1) + { + gtk_selection_data_set (selection_data, + GDK_SELECTION_TYPE_STRING, + 8, latin1, strlen (latin1)); + g_free(latin1); + + return TRUE; + } + + } + else if (selection_data->target == ctext_atom || + selection_data->target == text_atom) + { + guchar *text; + GdkAtom encoding; + gint format; + gint new_length; + + if (gdk_utf8_to_compound_text (str, &encoding, &format, &text, &new_length)) + { + gtk_selection_data_set (selection_data, encoding, format, text, new_length); + gdk_free_compound_text (text); + + return TRUE; + } + } + + return FALSE; +} + +/** + * gtk_selection_data_get_text: + * @selection_data: a #GtkSelectionData + * + * Gets the contents of the selection data as a UTF-8 string. + * + * Return value: if the selection data contained a recognized + * text type and it could be converted to UTF-8, a newly allocated + * string containing the converted text, otherwise %NULL. + * If the result is non-%NULL it must be freed with g_free(). + **/ +guchar * +gtk_selection_data_get_text (GtkSelectionData *selection_data) +{ + guchar *result = NULL; + + init_atoms (); + + if (selection_data->length >= 0 && + (selection_data->type == GDK_TARGET_STRING || + selection_data->type == ctext_atom || + selection_data->type == utf8_atom)) + { + gchar **list; + gint i; + gint count = gdk_text_property_to_utf8_list (selection_data->type, + selection_data->format, + selection_data->data, + selection_data->length, + &list); + if (count > 0) + result = list[0]; + + for (i = 1; i < count; i++) + g_free (list[i]); + g_free (list); + } + + return result; +} + /************************************************************* * gtk_selection_init: * Initialize local variables @@ -755,16 +901,13 @@ gtk_selection_init (void) *************************************************************/ gint -gtk_selection_clear (GtkWidget *widget, +gtk_selection_clear (GtkWidget *widget, GdkEventSelection *event) { - /* FIXME: there can be a problem if we change the selection - via gtk_selection_owner_set after another client claims - the selection, but before we get the notification event. - Tk filters based on serial #'s, which aren't retained by - GTK. Filtering based on time's will be inherently - somewhat unreliable. */ - + /* Note that we filter clear events in gdkselection-x11.c, so + * that we only will get here if the clear event actually + * represents a change that we didn't do ourself. + */ GList *tmp_list; GtkSelectionInfo *selection_info = NULL; @@ -782,16 +925,9 @@ gtk_selection_clear (GtkWidget *widget, if (tmp_list) { - if (selection_info->time > event->time) - return FALSE; /* return FALSE to indicate that - * the selection was out of date, - * and this clear should be ignored */ - else - { - current_selections = g_list_remove_link (current_selections, tmp_list); - g_list_free (tmp_list); - g_free (selection_info); - } + current_selections = g_list_remove_link (current_selections, tmp_list); + g_list_free (tmp_list); + g_free (selection_info); } return TRUE; @@ -1585,14 +1721,20 @@ gtk_selection_default_handler (GtkWidget *widget, GtkSelectioData* -gtk_selection_data_copy (GtkSelectionData *data) +gtk_selection_data_copy (GtkSelectionData *selection_data) { GtkSelectionData *new_data; - g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (selection_data != NULL, NULL); new_data = g_new (GtkSelectionData, 1); - *new_data = *data; + *new_data = *selection_data; + + if (selection_data->data) + { + new_data->data = g_malloc (selection_data->length + 1); + memcpy (new_data->data, selection_data->data, selection_data->length + 1); + } return new_data; } @@ -1602,6 +1744,9 @@ gtk_selection_data_free (GtkSelectionData *data) { g_return_if_fail (data != NULL); + if (data->data) + g_free (data->data); + g_free (data); } diff --git a/gtk/gtkselection.h b/gtk/gtkselection.h index 157896f197..668fec4b3a 100644 --- a/gtk/gtkselection.h +++ b/gtk/gtkselection.h @@ -83,28 +83,31 @@ gboolean gtk_target_list_find (GtkTargetList *list, /* Public interface */ -gint gtk_selection_owner_set (GtkWidget *widget, - GdkAtom selection, - guint32 time); -void gtk_selection_add_target (GtkWidget *widget, - GdkAtom selection, - GdkAtom target, - guint info); -void gtk_selection_add_targets (GtkWidget *widget, - GdkAtom selection, - const GtkTargetEntry *targets, - guint ntargets); -gint gtk_selection_convert (GtkWidget *widget, - GdkAtom selection, - GdkAtom target, - guint32 time); - - -void gtk_selection_data_set (GtkSelectionData *selection_data, - GdkAtom type, - gint format, - const guchar *data, - gint length); +gboolean gtk_selection_owner_set (GtkWidget *widget, + GdkAtom selection, + guint32 time); +void gtk_selection_add_target (GtkWidget *widget, + GdkAtom selection, + GdkAtom target, + guint info); +void gtk_selection_add_targets (GtkWidget *widget, + GdkAtom selection, + const GtkTargetEntry *targets, + guint ntargets); +void gtk_selection_clear_targets (GtkWidget *widget, + GdkAtom selection); +gint gtk_selection_convert (GtkWidget *widget, + GdkAtom selection, + GdkAtom target, + guint32 time); +void gtk_selection_data_set (GtkSelectionData *selection_data, + GdkAtom type, + gint format, + const guchar *data, + gint length); +gboolean gtk_selection_data_set_text (GtkSelectionData *selection_data, + const guchar *str); +guchar * gtk_selection_data_get_text (GtkSelectionData *selection_data); /* Called when a widget is destroyed */ diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c index 52f7e664d3..19549efdf9 100644 --- a/gtk/gtktextbuffer.c +++ b/gtk/gtktextbuffer.c @@ -5,14 +5,24 @@ #include +#include "gtkclipboard.h" #include "gtkinvisible.h" -#include "gtkselection.h" #include "gtksignal.h" #include "gtktextbuffer.h" #include "gtktextbtree.h" #include "gtktextiterprivate.h" #include +typedef struct _ClipboardRequest ClipboardRequest; + +struct _ClipboardRequest +{ + GtkTextBuffer *buffer; + gboolean interactive; + gboolean default_editable; + gboolean is_clipboard; +}; + enum { INSERT_TEXT, DELETE_TEXT, @@ -44,7 +54,6 @@ static void gtk_text_buffer_finalize (GObject *object); static void gtk_text_buffer_update_primary_selection (GtkTextBuffer *buffer); -static void gtk_text_buffer_update_clipboard_selection (GtkTextBuffer *buffer); static void gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer, GtkTextIter *iter, const gchar *text, @@ -70,11 +79,6 @@ void gtk_marshal_NONE__INT_POINTER_INT (GtkObject *object, gpointer func_data, GtkArg *args); -static GdkAtom clipboard_atom = GDK_NONE; -static GdkAtom text_atom = GDK_NONE; -static GdkAtom ctext_atom = GDK_NONE; -static GdkAtom utf8_atom = GDK_NONE; - static GtkObjectClass *parent_class = NULL; static guint signals[LAST_SIGNAL] = { 0 }; @@ -238,34 +242,6 @@ gtk_marshal_NONE__INT_POINTER_INT (GtkObject *object, void gtk_text_buffer_init (GtkTextBuffer *buffer) { - static const GtkTargetEntry targets[] = { - { "STRING", 0, TARGET_STRING }, - { "TEXT", 0, TARGET_TEXT }, - { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, - { "UTF8_STRING", 0, TARGET_UTF8_STRING } - }; - static const gint n_targets = sizeof(targets) / sizeof(targets[0]); - - if (!clipboard_atom) - clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); - - if (!text_atom) - text_atom = gdk_atom_intern ("TEXT", FALSE); - - if (!ctext_atom) - ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); - - if (!utf8_atom) - utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE); - - buffer->selection_widget = gtk_invisible_new(); - - gtk_selection_add_targets (buffer->selection_widget, - GDK_SELECTION_PRIMARY, - targets, n_targets); - gtk_selection_add_targets (buffer->selection_widget, - clipboard_atom, - targets, n_targets); } /** @@ -301,9 +277,6 @@ gtk_text_buffer_destroy (GtkObject *object) buffer = GTK_TEXT_BUFFER (object); - gtk_widget_destroy(buffer->selection_widget); - buffer->selection_widget = NULL; - if (buffer->tag_table) { gtk_object_unref(GTK_OBJECT(buffer->tag_table)); @@ -1283,41 +1256,6 @@ gtk_text_buffer_set_modified (GtkTextBuffer *buffer, } -/* - * Clipboard - */ - -static void -set_clipboard_contents_nocopy(GtkTextBuffer *buffer, - gchar *text) -{ - if (text && *text == '\0') - { - g_free(text); - text = NULL; - } - - if (buffer->clipboard_text != NULL) - g_free(buffer->clipboard_text); - - buffer->clipboard_text = text; - - gtk_text_buffer_update_clipboard_selection(buffer); -} - -void -gtk_text_buffer_set_clipboard_contents (GtkTextBuffer *buffer, - const gchar *text) -{ - set_clipboard_contents_nocopy(buffer, text ? g_strdup(text) : NULL); -} - -const gchar* -gtk_text_buffer_get_clipboard_contents (GtkTextBuffer *buffer) -{ - return buffer->clipboard_text; -} - /* * Assorted other stuff */ @@ -1370,449 +1308,198 @@ gtk_text_buffer_get_tags (GtkTextBuffer *buffer, return retval; } -/* - * Selection +/* Called when we lose the primary selection. */ - -static gboolean -have_primary_x_selection(GtkWidget *widget) -{ - return (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == - widget->window); -} - -static gboolean -request_primary_x_selection(GtkWidget *widget, guint32 time) -{ - return gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, time); -} - -static gboolean -release_primary_x_selection(GtkWidget *widget, guint32 time) -{ - if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == - widget->window) - { - gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time); - return TRUE; - } - else - return FALSE; -} - - -static gboolean -have_clipboard_x_selection(GtkWidget *widget) -{ - return (gdk_selection_owner_get (clipboard_atom) == - widget->window); -} - -static gboolean -request_clipboard_x_selection(GtkWidget *widget, guint32 time) -{ - return gtk_selection_owner_set (widget, clipboard_atom, time); -} - -static gboolean -release_clipboard_x_selection(GtkWidget *widget, guint32 time) -{ - if (gdk_selection_owner_get (clipboard_atom) == - widget->window) - { - gtk_selection_owner_set (NULL, clipboard_atom, time); - return TRUE; - } - else - return FALSE; -} - -/* Called when we lose the selection */ -static gint -selection_clear_event(GtkWidget *widget, GdkEventSelection *event, - gpointer data) -{ - GtkTextBuffer *buffer; - - buffer = GTK_TEXT_BUFFER(data); - - /* Let the selection handling code know that the selection - * has been changed, since we've overriden the default handler */ - if (!gtk_selection_clear (widget, event)) - return FALSE; - - buffer->have_selection = FALSE; - - if (event->selection == GDK_SELECTION_PRIMARY) - { - /* Move selection_bound to the insertion point */ - GtkTextIter insert; - GtkTextIter selection_bound; - - gtk_text_buffer_get_iter_at_mark(buffer, &insert, - gtk_text_buffer_get_mark (buffer, "insert")); - gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound, - gtk_text_buffer_get_mark (buffer, "selection_bound")); - - if (!gtk_text_iter_equal(&insert, &selection_bound)) - gtk_text_buffer_move_mark(buffer, - gtk_text_buffer_get_mark (buffer, "selection_bound"), - &insert); - } - else if (event->selection == clipboard_atom) - { - gtk_text_buffer_set_clipboard_contents(buffer, NULL); - } - - return TRUE; -} - -/* Called when we have the selection and someone else wants our - data in order to paste it */ static void -selection_get (GtkWidget *widget, - GtkSelectionData *selection_data, - guint info, - guint time, - gpointer data) +clipboard_clear_cb (GtkClipboard *clipboard, + gpointer data) { - GtkTextBuffer *buffer; + /* Move selection_bound to the insertion point */ + GtkTextIter insert; + GtkTextIter selection_bound; + GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data); + + gtk_text_buffer_get_iter_at_mark(buffer, &insert, + gtk_text_buffer_get_mark (buffer, "insert")); + gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound, + gtk_text_buffer_get_mark (buffer, "selection_bound")); + + if (!gtk_text_iter_equal(&insert, &selection_bound)) + gtk_text_buffer_move_mark(buffer, + gtk_text_buffer_get_mark (buffer, "selection_bound"), + &insert); +} + +/* Called when we have the primary selection and someone else wants our + * data in order to paste it. + */ +static void +clipboard_get_cb (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer data) +{ + GtkTextBuffer *buffer = GTK_TEXT_BUFFER(data); gchar *str; - guint length; + GtkTextIter start, end; - buffer = GTK_TEXT_BUFFER(data); - - if (selection_data->selection == GDK_SELECTION_PRIMARY) + if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) { - GtkTextIter start; - GtkTextIter end; - - if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) - { - /* Extract the selected text */ - str = gtk_text_iter_get_visible_text(&start, &end); - - length = strlen(str); - } - else - return; - } - else - { - const gchar *cstr; - - cstr = gtk_text_buffer_get_clipboard_contents(buffer); - - if (cstr == NULL) - return; - - str = g_strdup(cstr); - - length = strlen (str); - } - - if (str) - { - if (info == TARGET_UTF8_STRING) - { - /* Pass raw UTF8 */ - gtk_selection_data_set (selection_data, - utf8_atom, - 8*sizeof(gchar), (guchar *)str, length); - - } - else if (info == TARGET_STRING || - info == TARGET_TEXT) - { - gchar *latin1; - - latin1 = gtk_text_utf_to_latin1(str, length); - - gtk_selection_data_set (selection_data, - GDK_SELECTION_TYPE_STRING, - 8*sizeof(gchar), latin1, strlen(latin1)); - g_free(latin1); - } - else if (info == TARGET_COMPOUND_TEXT) - { - /* FIXME convert UTF8 directly to current locale, not via - latin1 */ - - guchar *text; - GdkAtom encoding; - gint format; - gint new_length; - gchar *latin1; - - latin1 = gtk_text_utf_to_latin1(str, length); - - gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length); - gtk_selection_data_set (selection_data, encoding, format, text, new_length); - gdk_free_compound_text (text); - - g_free(latin1); - } - + /* Extract the selected text */ + str = gtk_text_iter_get_visible_text(&start, &end); + gtk_selection_data_set_text (selection_data, str); g_free (str); } } -/* Called when we request a paste and receive the data */ +/* Called when we request a paste and receive the data + */ static void -selection_received (GtkWidget *widget, - GtkSelectionData *selection_data, - guint time, - gpointer data) +clipboard_received (GtkClipboard *clipboard, + const gchar *str, + gpointer data) { - GtkTextBuffer *buffer; - gboolean reselect; - GtkTextIter insert_point; - GtkTextMark *paste_point_override; - enum {INVALID, STRING, CTEXT, UTF8} type; + ClipboardRequest *request_data = data; + GtkTextBuffer *buffer = request_data->buffer; - g_return_if_fail (widget != NULL); - - buffer = GTK_TEXT_BUFFER(data); - - if (selection_data->type == GDK_TARGET_STRING) - type = STRING; - else if (selection_data->type == ctext_atom) - type = CTEXT; - else if (selection_data->type == utf8_atom) - type = UTF8; - else - type = INVALID; - - if (type == INVALID || selection_data->length < 0) + if (str) { - /* If we asked for UTF8 and didn't get it, try text; if we asked - for text and didn't get it, try string. If we asked for - anything else and didn't get it, give up. */ - if (selection_data->target == utf8_atom) - gtk_selection_convert (widget, selection_data->selection, - GDK_TARGET_STRING, time); - return; - } + gboolean reselect; + GtkTextIter insert_point; + GtkTextMark *paste_point_override; - paste_point_override = gtk_text_buffer_get_mark (buffer, - "__paste_point_override"); - - if (paste_point_override != NULL) - { - gtk_text_buffer_get_iter_at_mark(buffer, &insert_point, - paste_point_override); - gtk_text_buffer_delete_mark(buffer, - gtk_text_buffer_get_mark (buffer, - "__paste_point_override")); - } - else - { - gtk_text_buffer_get_iter_at_mark(buffer, &insert_point, - gtk_text_buffer_get_mark (buffer, - "insert")); - } - - reselect = FALSE; - - if ((TRUE/* Text is selected FIXME */) && - (!buffer->have_selection || - (selection_data->selection == clipboard_atom))) - { - reselect = TRUE; - - if (buffer->have_selection) - { - /* FIXME Delete currently-selected chars but don't give up X - selection since we'll use the newly-pasted stuff as - a new X selection */ - - } + paste_point_override = gtk_text_buffer_get_mark (buffer, + "__paste_point_override"); + + if (paste_point_override != NULL) + { + gtk_text_buffer_get_iter_at_mark(buffer, &insert_point, + paste_point_override); + gtk_text_buffer_delete_mark(buffer, + gtk_text_buffer_get_mark (buffer, + "__paste_point_override")); + } else - ; /* FIXME Delete selected chars and give up X selection */ + { + gtk_text_buffer_get_iter_at_mark(buffer, &insert_point, + gtk_text_buffer_get_mark (buffer, + "insert")); + } + + reselect = FALSE; + + /* FIXME ALL OF THIS - I think that the "best method" is that when pasting + * with the cursor inside the selection area, you replace the selection + * with the new text, otherwise, you simply insert the new text at + * the point where the click occured, unselecting any selected text. + * + * This probably is best implemented as a "replace_selection" flag in + * ClipboardRequest. + */ +#if 0 + if ((TRUE/* Text is selected FIXME */) && + (!buffer->have_selection || request_data->is_clipboard)) + { + reselect = TRUE; + + if (buffer->have_selection) + { + /* FIXME Delete currently-selected chars but don't give up X + selection since we'll use the newly-pasted stuff as + a new X selection */ + + } + else + ; /* FIXME Delete selected chars and give up X selection */ + } +#endif + + + if (request_data->interactive) + gtk_text_buffer_insert_interactive (buffer, &insert_point, + str, -1, request_data->default_editable); + else + gtk_text_buffer_insert (buffer, &insert_point, + str, -1); + + if (reselect) + ; /* FIXME Select the region of text we just inserted */ } - switch (type) - { - case STRING: - { - gchar *utf; - - utf = gtk_text_latin1_to_utf((const gchar*)selection_data->data, - selection_data->length); - if (buffer->paste_interactive) - gtk_text_buffer_insert_interactive (buffer, &insert_point, - utf, -1, buffer->paste_default_editable); - else - gtk_text_buffer_insert (buffer, &insert_point, - utf, -1); - g_free(utf); - } - break; - - case UTF8: - gtk_text_buffer_insert (buffer, &insert_point, - (const gchar *)selection_data->data, - selection_data->length); - break; - - case CTEXT: - { - gchar **list; - gint count; - gint i; - - count = gdk_text_property_to_text_list (selection_data->type, - selection_data->format, - selection_data->data, - selection_data->length, - &list); - for (i=0; ipaste_interactive) - gtk_text_buffer_insert_interactive (buffer, &insert_point, - utf, -1, buffer->paste_default_editable); - else - gtk_text_buffer_insert (buffer, &insert_point, - utf, -1); - - if (free_utf) - g_free(utf); - } - - if (count > 0) - gdk_free_text_list (list); - } - break; - - case INVALID: /* quiet compiler */ - break; - } - - if (reselect) - ; /* FIXME Select the region of text we just inserted */ - + g_object_unref (G_OBJECT (buffer)); + g_free (request_data); } static void -ensure_handlers(GtkTextBuffer *buffer) +gtk_text_buffer_update_primary_selection (GtkTextBuffer *buffer) { - if (!buffer->selection_handlers_installed) - { - buffer->selection_handlers_installed = TRUE; + static const GtkTargetEntry targets[] = { + { "STRING", 0, TARGET_STRING }, + { "TEXT", 0, TARGET_TEXT }, + { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, + { "UTF8_STRING", 0, TARGET_UTF8_STRING } + }; - gtk_signal_connect(GTK_OBJECT(buffer->selection_widget), - "selection_clear_event", - GTK_SIGNAL_FUNC(selection_clear_event), - buffer); - - gtk_signal_connect(GTK_OBJECT(buffer->selection_widget), - "selection_received", - GTK_SIGNAL_FUNC(selection_received), - buffer); - - gtk_signal_connect(GTK_OBJECT(buffer->selection_widget), - "selection_get", - GTK_SIGNAL_FUNC(selection_get), - buffer); - } -} - -/* FIXME GDK_CURRENT_TIME should probably go away and we should - figure out how to get the events in here */ -static void -gtk_text_buffer_update_primary_selection(GtkTextBuffer *buffer) -{ GtkTextIter start; GtkTextIter end; - ensure_handlers(buffer); - + GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY); + /* Determine whether we have a selection and adjust X selection - accordingly. */ + * accordingly. + */ - if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end)) + if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end)) { - release_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME); - buffer->have_selection = FALSE; + if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (buffer)) + gtk_clipboard_clear (clipboard); } else { /* Even if we already have the selection, we need to update our - timestamp. */ - buffer->have_selection = FALSE; - request_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME); - if (have_primary_x_selection(buffer->selection_widget)) - buffer->have_selection = TRUE; + * timestamp. + */ + if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets), + clipboard_get_cb, clipboard_clear_cb, G_OBJECT (buffer))) + clipboard_clear_cb (clipboard, buffer); } } static void -gtk_text_buffer_update_clipboard_selection(GtkTextBuffer *buffer) -{ - if (buffer->clipboard_text == NULL || - buffer->clipboard_text[0] == '\0') - { - release_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME); - } - else - { - /* Even if we already have the selection, we need to update our - timestamp. */ - request_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME); - } -} - -static void -paste (GtkTextBuffer *buffer, GdkAtom selection, guint32 time, +paste (GtkTextBuffer *buffer, + gboolean is_clipboard, gboolean interactive, gboolean default_editable) { - ensure_handlers(buffer); + ClipboardRequest *data = g_new (ClipboardRequest, 1); - buffer->paste_interactive = interactive; - buffer->paste_default_editable = default_editable; - - gtk_selection_convert (buffer->selection_widget, selection, - utf8_atom, time); + data->buffer = buffer; + g_object_ref (G_OBJECT (buffer)); + data->interactive = interactive; + data->default_editable = default_editable; + + gtk_clipboard_request_text (gtk_clipboard_get (is_clipboard ? GDK_NONE : GDK_SELECTION_PRIMARY), + clipboard_received, data); } void -gtk_text_buffer_paste_primary_selection(GtkTextBuffer *buffer, - GtkTextIter *override_location, - guint32 time, - gboolean interactive, - gboolean default_editable) +gtk_text_buffer_paste_primary (GtkTextBuffer *buffer, + GtkTextIter *override_location, + gboolean default_editable) { if (override_location != NULL) gtk_text_buffer_create_mark(buffer, "__paste_point_override", override_location, FALSE); - paste(buffer, GDK_SELECTION_PRIMARY, time, interactive, default_editable); + paste (buffer, FALSE, TRUE, default_editable); } void -gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer, - guint32 time, - gboolean interactive, - gboolean default_editable) +gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer, + gboolean default_editable) { - paste(buffer, clipboard_atom, time, interactive, default_editable); + paste (buffer, TRUE, TRUE, default_editable); } gboolean @@ -1833,15 +1520,13 @@ gtk_text_buffer_delete_selection (GtkTextBuffer *buffer, gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable); else gtk_text_buffer_delete (buffer, &start, &end); - - gtk_text_buffer_update_primary_selection(buffer); + return TRUE; /* We deleted stuff */ } } static void cut_or_copy(GtkTextBuffer *buffer, - guint32 time, gboolean delete_region_after, gboolean interactive, gboolean default_editable) @@ -1871,11 +1556,12 @@ cut_or_copy(GtkTextBuffer *buffer, if (!gtk_text_iter_equal(&start, &end)) { + GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE); gchar *text; - text = gtk_text_iter_get_visible_text(&start, &end); - - set_clipboard_contents_nocopy(buffer, text); + text = gtk_text_iter_get_visible_text (&start, &end); + gtk_clipboard_set_text (clipboard, text, -1); + g_free (text); if (delete_region_after) { @@ -1888,19 +1574,16 @@ cut_or_copy(GtkTextBuffer *buffer, } void -gtk_text_buffer_cut (GtkTextBuffer *buffer, - guint32 time, - gboolean interactive, - gboolean default_editable) +gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer, + gboolean default_editable) { - cut_or_copy(buffer, time, TRUE, interactive, default_editable); + cut_or_copy (buffer, TRUE, TRUE, default_editable); } void -gtk_text_buffer_copy (GtkTextBuffer *buffer, - guint32 time) +gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer) { - cut_or_copy(buffer, time, FALSE, FALSE, TRUE); + cut_or_copy (buffer, FALSE, TRUE, TRUE); } diff --git a/gtk/gtktextbuffer.h b/gtk/gtktextbuffer.h index 7680294b71..b58feb62c4 100644 --- a/gtk/gtktextbuffer.h +++ b/gtk/gtktextbuffer.h @@ -32,18 +32,8 @@ struct _GtkTextBuffer { GtkTextTagTable *tag_table; GtkTextBTree *btree; - /* Text currently pasted to the clipboard */ - gchar *clipboard_text; - /* Whether the buffer has been modified since last save */ gboolean modified; - - /* We use this for selections */ - GtkWidget *selection_widget; - gboolean have_selection; - gboolean selection_handlers_installed; - gboolean paste_interactive; - gboolean paste_default_editable; }; struct _GtkTextBufferClass { @@ -233,33 +223,22 @@ GSList *gtk_text_buffer_get_tags (GtkTextBuffer *buffer, gboolean gtk_text_buffer_modified (GtkTextBuffer *buffer); void gtk_text_buffer_set_modified (GtkTextBuffer *buffer, gboolean setting); -void gtk_text_buffer_set_clipboard_contents (GtkTextBuffer *buffer, - const gchar *text); -const gchar *gtk_text_buffer_get_clipboard_contents (GtkTextBuffer *buffer); - -void gtk_text_buffer_paste_primary_selection (GtkTextBuffer *buffer, +void gtk_text_buffer_paste_primary (GtkTextBuffer *buffer, GtkTextIter *override_location, - guint32 time, - gboolean interactive, gboolean default_editable); -gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer, - gboolean interactive, +void gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer, gboolean default_editable); -void gtk_text_buffer_cut (GtkTextBuffer *buffer, - guint32 time, - gboolean interactive, - gboolean default_editable); -void gtk_text_buffer_copy (GtkTextBuffer *buffer, - guint32 time); +void gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer); void gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer, - guint32 time, - gboolean interactive, gboolean default_editable); + gboolean gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer, GtkTextIter *start, GtkTextIter *end); - +gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer, + gboolean interactive, + gboolean default_editable); /* This function is not implemented. */ gboolean gtk_text_buffer_find_string(GtkTextBuffer *buffer, diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c index af2b76edb2..901aa92666 100644 --- a/gtk/gtktextlayout.c +++ b/gtk/gtktextlayout.c @@ -2061,6 +2061,60 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout, } } +/** + * gtk_text_layout_move_iter_to_line_end: + * @layout: a #GtkTextLayout + * @direction: if negative, move to beginning of line, otherwise + move to end of line. + * + * Move to the beginning or end of a display line. + **/ +void +gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout, + GtkTextIter *iter, + gint direction) +{ + GtkTextLine *line; + GtkTextLineDisplay *display; + gint line_byte; + gint byte_offset = 0; + GSList *tmp_list; + + g_return_if_fail (layout != NULL); + g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout)); + g_return_if_fail (iter != NULL); + + line = gtk_text_iter_get_text_line (iter); + line_byte = gtk_text_iter_get_line_index (iter); + + display = gtk_text_layout_get_line_display (layout, line, FALSE); + + tmp_list = pango_layout_get_lines (display->layout); + while (tmp_list) + { + PangoLayoutLine *layout_line = tmp_list->data; + + if (line_byte < byte_offset + layout_line->length || !tmp_list->next) + { + gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer), + iter, line, + direction < 0 ? byte_offset : layout_line->length); + + /* FIXME: Move back one position to avoid going to next line + */ + if (direction < 0 && layout_line->length > 0) + gtk_text_iter_prev_char (iter); + + break; + } + + byte_offset += layout_line->length; + tmp_list = tmp_list->next; + } + + gtk_text_layout_free_line_display (layout, display); +} + /** * gtk_text_layout_move_iter_to_x: * @layout: a #GtkTextLayout diff --git a/gtk/gtktextlayout.h b/gtk/gtktextlayout.h index 048b55d771..e06ed4fd3f 100644 --- a/gtk/gtktextlayout.h +++ b/gtk/gtktextlayout.h @@ -245,6 +245,9 @@ gboolean gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout, gint top, gint bottom); +void gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout, + GtkTextIter *iter, + gint direction); void gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout, GtkTextIter *iter); void gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout, @@ -256,7 +259,6 @@ void gtk_text_layout_move_iter_visually (GtkTextLayout *layout, GtkTextIter *iter, gint count); - void gtk_text_layout_spew (GtkTextLayout *layout); #ifdef __cplusplus diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index e97df15f72..55b378c6e9 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -63,13 +63,13 @@ #include enum { - MOVE_INSERT, + MOVE, SET_ANCHOR, - SCROLL_TEXT, - DELETE_TEXT, - CUT_TEXT, - COPY_TEXT, - PASTE_TEXT, + INSERT, + DELETE, + CUT_CLIPBOARD, + COPY_CLIPBOARD, + PASTE_CLIPBOARD, TOGGLE_OVERWRITE, SET_SCROLL_ADJUSTMENTS, LAST_SIGNAL @@ -168,21 +168,22 @@ static void gtk_text_view_set_scroll_adjustments (GtkTextView *text_view, GtkAdjustment *hadj, GtkAdjustment *vadj); -static void gtk_text_view_move_insert (GtkTextView *text_view, - GtkTextViewMovementStep step, - gint count, - gboolean extend_selection); -static void gtk_text_view_set_anchor (GtkTextView *text_view); -static void gtk_text_view_scroll_text (GtkTextView *text_view, - GtkTextViewScrollType type); -static void gtk_text_view_delete_text (GtkTextView *text_view, - GtkTextViewDeleteType type, - gint count); -static void gtk_text_view_cut_text (GtkTextView *text_view); -static void gtk_text_view_copy_text (GtkTextView *text_view); -static void gtk_text_view_paste_text (GtkTextView *text_view); -static void gtk_text_view_toggle_overwrite (GtkTextView *text_view); - +static void gtk_text_view_move (GtkTextView *text_view, + GtkMovementStep step, + gint count, + gboolean extend_selection); +static void gtk_text_view_set_anchor (GtkTextView *text_view); +static void gtk_text_view_scroll_pages (GtkTextView *text_view, + gint count); +static void gtk_text_view_insert (GtkTextView *text_view, + const gchar *str); +static void gtk_text_view_delete (GtkTextView *text_view, + GtkDeleteType type, + gint count); +static void gtk_text_view_cut_clipboard (GtkTextView *text_view); +static void gtk_text_view_copy_clipboard (GtkTextView *text_view); +static void gtk_text_view_paste_clipboard (GtkTextView *text_view); +static void gtk_text_view_toggle_overwrite (GtkTextView *text_view); static void gtk_text_view_validate_onscreen (GtkTextView *text_view); static void gtk_text_view_get_first_para_iter (GtkTextView *text_view, @@ -231,12 +232,6 @@ enum { TARGET_UTF8_STRING }; -static GdkAtom clipboard_atom = GDK_NONE; -static GdkAtom text_atom = GDK_NONE; -static GdkAtom ctext_atom = GDK_NONE; -static GdkAtom utf8_atom = GDK_NONE; - - static GtkTargetEntry target_table[] = { { "UTF8_STRING", 0, TARGET_UTF8_STRING }, { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT }, @@ -245,8 +240,6 @@ static GtkTargetEntry target_table[] = { { "STRING", 0, TARGET_STRING } }; -static guint n_targets = sizeof (target_table) / sizeof (target_table[0]); - static GtkContainerClass *parent_class = NULL; static guint signals[LAST_SIGNAL] = { 0 }; @@ -276,23 +269,23 @@ gtk_text_view_get_type (void) } static void -add_move_insert_binding (GtkBindingSet *binding_set, - guint keyval, - guint modmask, - GtkTextViewMovementStep step, - gint count) +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_insert", 3, + "move", 3, GTK_TYPE_ENUM, step, GTK_TYPE_INT, count, GTK_TYPE_BOOL, FALSE); /* Selection-extending version */ gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK, - "move_insert", 3, + "move", 3, GTK_TYPE_ENUM, step, GTK_TYPE_INT, count, GTK_TYPE_BOOL, TRUE); @@ -331,13 +324,13 @@ gtk_text_view_class_init (GtkTextViewClass *klass) * Signals */ - signals[MOVE_INSERT] = - gtk_signal_new ("move_insert", + signals[MOVE] = + gtk_signal_new ("move", GTK_RUN_LAST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, move_insert), + GTK_SIGNAL_OFFSET (GtkTextViewClass, move), gtk_marshal_NONE__INT_INT_INT, - GTK_TYPE_NONE, 3, GTK_TYPE_ENUM, GTK_TYPE_INT, GTK_TYPE_BOOL); + GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL); signals[SET_ANCHOR] = gtk_signal_new ("set_anchor", @@ -347,43 +340,43 @@ gtk_text_view_class_init (GtkTextViewClass *klass) gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); - signals[SCROLL_TEXT] = - gtk_signal_new ("scroll_text", + signals[INSERT] = + gtk_signal_new ("insert", GTK_RUN_LAST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, scroll_text), - gtk_marshal_NONE__INT, - GTK_TYPE_NONE, 1, GTK_TYPE_ENUM); + GTK_SIGNAL_OFFSET (GtkTextViewClass, insert), + gtk_marshal_NONE__STRING, + GTK_TYPE_NONE, 1, GTK_TYPE_STRING); - signals[DELETE_TEXT] = - gtk_signal_new ("delete_text", + signals[DELETE] = + gtk_signal_new ("delete", GTK_RUN_LAST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_text), + GTK_SIGNAL_OFFSET (GtkTextViewClass, delete), gtk_marshal_NONE__INT_INT, - GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_INT); + GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT); - signals[CUT_TEXT] = - gtk_signal_new ("cut_text", + signals[CUT_CLIPBOARD] = + gtk_signal_new ("cut_clipboard", GTK_RUN_LAST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_text), + GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_clipboard), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); - signals[COPY_TEXT] = - gtk_signal_new ("copy_text", + signals[COPY_CLIPBOARD] = + gtk_signal_new ("copy_clipboard", GTK_RUN_LAST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_text), + GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_clipboard), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); - signals[PASTE_TEXT] = - gtk_signal_new ("paste_text", + signals[PASTE_CLIPBOARD] = + gtk_signal_new ("paste_clipboard", GTK_RUN_LAST | GTK_RUN_ACTION, GTK_CLASS_TYPE (object_class), - GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_text), + GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_clipboard), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); @@ -412,123 +405,142 @@ gtk_text_view_class_init (GtkTextViewClass *klass) binding_set = gtk_binding_set_by_class (klass); /* Moving the insertion point */ - add_move_insert_binding (binding_set, GDK_Right, 0, - GTK_TEXT_MOVEMENT_POSITIONS, 1); + add_move_binding (binding_set, GDK_Right, 0, + GTK_MOVEMENT_POSITIONS, 1); - add_move_insert_binding (binding_set, GDK_Left, 0, - GTK_TEXT_MOVEMENT_POSITIONS, -1); + add_move_binding (binding_set, GDK_Left, 0, + GTK_MOVEMENT_POSITIONS, -1); - add_move_insert_binding (binding_set, GDK_f, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_CHAR, 1); + add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK, + GTK_MOVEMENT_CHARS, 1); - add_move_insert_binding (binding_set, GDK_b, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_CHAR, -1); + add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK, + GTK_MOVEMENT_CHARS, -1); - add_move_insert_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_WORD, 1); + add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK, + GTK_MOVEMENT_WORDS, 1); - add_move_insert_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_WORD, -1); + add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK, + GTK_MOVEMENT_WORDS, -1); /* Eventually we want to move by display lines, not paragraphs */ - add_move_insert_binding (binding_set, GDK_Up, 0, - GTK_TEXT_MOVEMENT_WRAPPED_LINE, -1); + add_move_binding (binding_set, GDK_Up, 0, + GTK_MOVEMENT_DISPLAY_LINES, -1); - add_move_insert_binding (binding_set, GDK_Down, 0, - GTK_TEXT_MOVEMENT_WRAPPED_LINE, 1); + add_move_binding (binding_set, GDK_Down, 0, + GTK_MOVEMENT_DISPLAY_LINES, 1); - add_move_insert_binding (binding_set, GDK_p, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_WRAPPED_LINE, -1); + add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK, + GTK_MOVEMENT_DISPLAY_LINES, -1); - add_move_insert_binding (binding_set, GDK_n, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_WRAPPED_LINE, 1); + add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK, + GTK_MOVEMENT_DISPLAY_LINES, 1); - add_move_insert_binding (binding_set, GDK_a, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_LINE_ENDS, -1); + add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK, + GTK_MOVEMENT_PARAGRAPH_ENDS, -1); - add_move_insert_binding (binding_set, GDK_e, GDK_CONTROL_MASK, - GTK_TEXT_MOVEMENT_LINE_ENDS, 1); + add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK, + GTK_MOVEMENT_PARAGRAPH_ENDS, 1); - add_move_insert_binding (binding_set, GDK_f, GDK_MOD1_MASK, - GTK_TEXT_MOVEMENT_WORD, 1); + add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK, + GTK_MOVEMENT_WORDS, 1); - add_move_insert_binding (binding_set, GDK_b, GDK_MOD1_MASK, - GTK_TEXT_MOVEMENT_WORD, -1); + add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK, + GTK_MOVEMENT_WORDS, -1); - add_move_insert_binding (binding_set, GDK_Home, 0, - GTK_TEXT_MOVEMENT_BUFFER_ENDS, -1); + add_move_binding (binding_set, GDK_Home, 0, + GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1); - add_move_insert_binding (binding_set, GDK_End, 0, - GTK_TEXT_MOVEMENT_BUFFER_ENDS, 1); + add_move_binding (binding_set, GDK_End, 0, + GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1); + + add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK, + GTK_MOVEMENT_BUFFER_ENDS, -1); + + add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK, + GTK_MOVEMENT_BUFFER_ENDS, 1); + + add_move_binding (binding_set, GDK_Page_Up, 0, + GTK_MOVEMENT_PAGES, -1); + + add_move_binding (binding_set, GDK_Page_Down, 0, + GTK_MOVEMENT_PAGES, 1); + /* Setting the cut/paste/copy anchor */ gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK, "set_anchor", 0); - - /* Scrolling around */ - gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0, - "scroll_text", 1, - GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_UP); - - gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0, - "scroll_text", 1, - GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_DOWN); - /* Deleting text */ gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_CHARS, + GTK_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_CHARS, GTK_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_CHARS, GTK_TYPE_INT, -1); gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, + GTK_TYPE_INT, 1); + + gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, GTK_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS, GTK_TYPE_INT, -1); gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_LINE, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS, GTK_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHOLE_LINE, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS, GTK_TYPE_INT, 1); gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE, GTK_TYPE_INT, 1); + gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK, + "insert", 1, + GTK_TYPE_STRING, " "); gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK, - "delete_text", 2, - GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE, + "delete", 2, + GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE, GTK_TYPE_INT, 1); /* Cut/copy/paste */ gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK, - "cut_text", 0); + "cut_clipboard", 0); gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK, - "cut_text", 0); + "cut_clipboard", 0); gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK, - "copy_text", 0); + "copy_clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK, + "paste_clipboard", 0); + gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK, - "paste_text", 0); + "paste_clipboard", 0); /* Overwrite */ gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0, @@ -570,13 +582,13 @@ gtk_text_view_class_init (GtkTextViewClass *klass) widget_class->drag_drop = gtk_text_view_drag_drop; widget_class->drag_data_received = gtk_text_view_drag_data_received; - klass->move_insert = gtk_text_view_move_insert; + klass->move = gtk_text_view_move; klass->set_anchor = gtk_text_view_set_anchor; - klass->scroll_text = gtk_text_view_scroll_text; - klass->delete_text = gtk_text_view_delete_text; - klass->cut_text = gtk_text_view_cut_text; - klass->copy_text = gtk_text_view_copy_text; - klass->paste_text = gtk_text_view_paste_text; + klass->insert = gtk_text_view_insert; + klass->delete = gtk_text_view_delete; + klass->cut_clipboard = gtk_text_view_cut_clipboard; + klass->copy_clipboard = gtk_text_view_copy_clipboard; + klass->paste_clipboard = gtk_text_view_paste_clipboard; klass->toggle_overwrite = gtk_text_view_toggle_overwrite; klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments; } @@ -592,21 +604,9 @@ gtk_text_view_init (GtkTextView *text_view) text_view->wrap_mode = GTK_WRAPMODE_NONE; - if (!clipboard_atom) - clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); - - if (!text_atom) - text_atom = gdk_atom_intern ("TEXT", FALSE); - - if (!ctext_atom) - ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE); - - if (!utf8_atom) - utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE); - gtk_drag_dest_set (widget, GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION, - target_table, n_targets, + target_table, G_N_ELEMENTS (target_table), GDK_ACTION_COPY | GDK_ACTION_MOVE); text_view->virtual_cursor_x = -1; @@ -1667,11 +1667,9 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event) event->x + text_view->xoffset, event->y + text_view->yoffset); - gtk_text_buffer_paste_primary_selection (text_view->buffer, - &iter, - event->time, - TRUE, - text_view->editable); + gtk_text_buffer_paste_primary (text_view->buffer, + &iter, + text_view->editable); return TRUE; } else if (event->button == 3) @@ -1843,61 +1841,77 @@ gtk_text_view_move_iter_by_lines (GtkTextView *text_view, } static void -gtk_text_view_move_insert (GtkTextView *text_view, - GtkTextViewMovementStep step, - gint count, - gboolean extend_selection) +gtk_text_view_move (GtkTextView *text_view, + GtkMovementStep step, + gint count, + gboolean extend_selection) { GtkTextIter insert; GtkTextIter newplace; gint cursor_x_pos = 0; + if (step == GTK_MOVEMENT_PAGES) + { + gtk_text_view_scroll_pages (text_view, count); + return; + } + gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, gtk_text_buffer_get_mark (text_view->buffer, "insert")); newplace = insert; - if (step == GTK_TEXT_MOVEMENT_WRAPPED_LINE) + if (step == GTK_MOVEMENT_DISPLAY_LINES) gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL); switch (step) { - case GTK_TEXT_MOVEMENT_CHAR: + case GTK_MOVEMENT_CHARS: gtk_text_iter_forward_chars (&newplace, count); break; - case GTK_TEXT_MOVEMENT_POSITIONS: + case GTK_MOVEMENT_POSITIONS: gtk_text_layout_move_iter_visually (text_view->layout, &newplace, count); break; - case GTK_TEXT_MOVEMENT_WORD: + case GTK_MOVEMENT_WORDS: if (count < 0) gtk_text_iter_backward_word_starts (&newplace, -count); else if (count > 0) gtk_text_iter_forward_word_ends (&newplace, count); break; - case GTK_TEXT_MOVEMENT_WRAPPED_LINE: + case GTK_MOVEMENT_DISPLAY_LINES: gtk_text_view_move_iter_by_lines (text_view, &newplace, count); gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos); break; - case GTK_TEXT_MOVEMENT_LINE: + case GTK_MOVEMENT_DISPLAY_LINE_ENDS: + if (count > 1) + gtk_text_view_move_iter_by_lines (text_view, &newplace, --count); + else if (count < -1) + gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count); + + if (count != 0) + gtk_text_layout_move_iter_to_line_end (text_view->layout, &newplace, count); + break; + + case GTK_MOVEMENT_PARAGRAPHS: /* This should almost certainly instead be doing the parallel thing to WORD */ /* gtk_text_iter_down_lines (&newplace, count); */ /* FIXME */ break; - case GTK_TEXT_MOVEMENT_LINE_ENDS: + case GTK_MOVEMENT_PARAGRAPH_ENDS: if (count > 0) gtk_text_iter_forward_to_newline (&newplace); else if (count < 0) gtk_text_iter_set_line_offset (&newplace, 0); break; - case GTK_TEXT_MOVEMENT_BUFFER_ENDS: + case GTK_MOVEMENT_BUFFER_ENDS: if (count > 0) gtk_text_buffer_get_last_iter (text_view->buffer, &newplace); else if (count < 0) @@ -1922,7 +1936,7 @@ gtk_text_view_move_insert (GtkTextView *text_view, gtk_text_buffer_get_mark (text_view->buffer, "insert"), 0); - if (step == GTK_TEXT_MOVEMENT_WRAPPED_LINE) + if (step == GTK_MOVEMENT_DISPLAY_LINES) { gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1); } @@ -1942,8 +1956,8 @@ gtk_text_view_set_anchor (GtkTextView *text_view) } static void -gtk_text_view_scroll_text (GtkTextView *text_view, - GtkTextViewScrollType type) +gtk_text_view_scroll_pages (GtkTextView *text_view, + gint count) { gfloat newval; GtkAdjustment *adj; @@ -1958,60 +1972,26 @@ gtk_text_view_scroll_text (GtkTextView *text_view, /* Validate the region that will be brought into view by the cursor motion */ - switch (type) + if (count < 0) { - default: - case GTK_TEXT_SCROLL_TO_TOP: - gtk_text_buffer_get_iter_at_offset (text_view->buffer, &anchor, 0); - y0 = 0; - y1 = adj->page_size; - break; - - case GTK_TEXT_SCROLL_TO_BOTTOM: - gtk_text_buffer_get_last_iter (text_view->buffer, &anchor); - y0 = -adj->page_size; - y1 = adj->page_size; - break; - - case GTK_TEXT_SCROLL_PAGE_DOWN: gtk_text_view_get_first_para_iter (text_view, &anchor); y0 = adj->page_size; - y1 = adj->page_size + adj->page_increment; - break; - - case GTK_TEXT_SCROLL_PAGE_UP: - gtk_text_view_get_first_para_iter (text_view, &anchor); - y0 = - adj->page_increment + adj->page_size; - y1 = 0; - break; + y1 = adj->page_size + count * adj->page_increment; } + else + { + gtk_text_view_get_first_para_iter (text_view, &anchor); + y0 = count * adj->page_increment + adj->page_size; + y1 = 0; + } + gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1); gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos); newval = adj->value; - switch (type) - { - case GTK_TEXT_SCROLL_TO_TOP: - newval = adj->lower; - break; - - case GTK_TEXT_SCROLL_TO_BOTTOM: - newval = adj->upper; - break; - - case GTK_TEXT_SCROLL_PAGE_DOWN: - newval += adj->page_increment; - break; - - case GTK_TEXT_SCROLL_PAGE_UP: - newval -= adj->page_increment; - break; - - default: - break; - } + newval += count * adj->page_increment; cursor_y_pos += newval - adj->value; set_adjustment_clamped (adj, newval); @@ -2059,16 +2039,24 @@ find_whitepace_region (const GtkTextIter *center, } static void -gtk_text_view_delete_text (GtkTextView *text_view, - GtkTextViewDeleteType type, - gint count) +gtk_text_view_insert (GtkTextView *text_view, + const gchar *str) +{ + gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, str, -1, + text_view->editable); +} + +static void +gtk_text_view_delete (GtkTextView *text_view, + GtkDeleteType type, + gint count) { GtkTextIter insert; GtkTextIter start; GtkTextIter end; gboolean leave_one = FALSE; - if (type == GTK_TEXT_DELETE_CHAR) + if (type == GTK_DELETE_CHARS) { /* Char delete deletes the selection, if one exists */ if (gtk_text_buffer_delete_selection (text_view->buffer, TRUE, @@ -2086,27 +2074,27 @@ gtk_text_view_delete_text (GtkTextView *text_view, switch (type) { - case GTK_TEXT_DELETE_CHAR: + case GTK_DELETE_CHARS: gtk_text_iter_forward_chars (&end, count); break; - case GTK_TEXT_DELETE_HALF_WORD: + case GTK_DELETE_WORD_ENDS: if (count > 0) gtk_text_iter_forward_word_ends (&end, count); else if (count < 0) gtk_text_iter_backward_word_starts (&start, 0 - count); break; - case GTK_TEXT_DELETE_WHOLE_WORD: + case GTK_DELETE_WORDS: break; - case GTK_TEXT_DELETE_HALF_WRAPPED_LINE: + case GTK_DELETE_DISPLAY_LINE_ENDS: break; - case GTK_TEXT_DELETE_WHOLE_WRAPPED_LINE: + case GTK_DELETE_DISPLAY_LINES: break; - case GTK_TEXT_DELETE_HALF_LINE: + case GTK_DELETE_PARAGRAPH_ENDS: while (count > 0) { if (!gtk_text_iter_forward_to_newline (&end)) @@ -2119,7 +2107,7 @@ gtk_text_view_delete_text (GtkTextView *text_view, and support that */ break; - case GTK_TEXT_DELETE_WHOLE_LINE: + case GTK_DELETE_PARAGRAPHS: if (count > 0) { gtk_text_iter_set_line_offset (&start, 0); @@ -2138,9 +2126,7 @@ gtk_text_view_delete_text (GtkTextView *text_view, break; - case GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE: - leave_one = TRUE; /* FALL THRU */ - case GTK_TEXT_DELETE_WHITESPACE: + case GTK_DELETE_WHITESPACE: { find_whitepace_region (&insert, &start, &end); } @@ -2168,9 +2154,9 @@ gtk_text_view_delete_text (GtkTextView *text_view, } static void -gtk_text_view_cut_text (GtkTextView *text_view) +gtk_text_view_cut_clipboard (GtkTextView *text_view) { - gtk_text_buffer_cut (text_view->buffer, GDK_CURRENT_TIME, TRUE, text_view->editable); + gtk_text_buffer_cut_clipboard (text_view->buffer, text_view->editable); gtk_text_view_scroll_to_mark (text_view, gtk_text_buffer_get_mark (text_view->buffer, "insert"), @@ -2178,9 +2164,9 @@ gtk_text_view_cut_text (GtkTextView *text_view) } static void -gtk_text_view_copy_text (GtkTextView *text_view) +gtk_text_view_copy_clipboard (GtkTextView *text_view) { - gtk_text_buffer_copy (text_view->buffer, GDK_CURRENT_TIME); + gtk_text_buffer_copy_clipboard (text_view->buffer); gtk_text_view_scroll_to_mark (text_view, gtk_text_buffer_get_mark (text_view->buffer, "insert"), @@ -2188,9 +2174,9 @@ gtk_text_view_copy_text (GtkTextView *text_view) } static void -gtk_text_view_paste_text (GtkTextView *text_view) +gtk_text_view_paste_clipboard (GtkTextView *text_view) { - gtk_text_buffer_paste_clipboard (text_view->buffer, GDK_CURRENT_TIME, TRUE, text_view->editable); + gtk_text_buffer_paste_clipboard (text_view->buffer, text_view->editable); gtk_text_view_scroll_to_mark (text_view, gtk_text_buffer_get_mark (text_view->buffer, "insert"), @@ -2567,16 +2553,14 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view, GdkDragContext *context; GtkTargetList *target_list; - /* FIXME we have to handle more formats for the selection, - and do the conversions to/from UTF8 */ - - /* FIXME not sure how this is memory-managed. */ - target_list = gtk_target_list_new (target_table, n_targets); - + target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table)); + context = gtk_drag_begin (GTK_WIDGET (text_view), target_list, GDK_ACTION_COPY | GDK_ACTION_MOVE, 1, (GdkEvent*)event); + gtk_target_list_unref (target_list); + gtk_drag_set_icon_default (context); /* We're inside the selection, so start without being able @@ -2631,46 +2615,7 @@ gtk_text_view_drag_data_get (GtkWidget *widget, if (str) { - if (info == TARGET_UTF8_STRING) - { - /* Pass raw UTF8 */ - gtk_selection_data_set (selection_data, - utf8_atom, - 8*sizeof (gchar), (guchar *)str, length); - - } - else if (info == TARGET_STRING || - info == TARGET_TEXT) - { - gchar *latin1; - - latin1 = gtk_text_utf_to_latin1(str, length); - - gtk_selection_data_set (selection_data, - GDK_SELECTION_TYPE_STRING, - 8*sizeof (gchar), latin1, strlen (latin1)); - g_free (latin1); - } - else if (info == TARGET_COMPOUND_TEXT) - { - /* FIXME convert UTF8 directly to current locale, not via - latin1 */ - - guchar *text; - GdkAtom encoding; - gint format; - gint new_length; - gchar *latin1; - - latin1 = gtk_text_utf_to_latin1(str, length); - - gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length); - gtk_selection_data_set (selection_data, encoding, format, text, new_length); - gdk_free_compound_text (text); - - g_free (latin1); - } - + gtk_selection_data_set_text (selection_data, str); g_free (str); } } @@ -2792,27 +2737,10 @@ gtk_text_view_drag_data_received (GtkWidget *widget, GtkTextIter drop_point; GtkTextView *text_view; GtkTextMark *drag_target_mark; + gchar *str; - enum {INVALID, STRING, CTEXT, UTF8} type; - text_view = GTK_TEXT_VIEW (widget); - if (selection_data->type == GDK_TARGET_STRING) - type = STRING; - else if (selection_data->type == ctext_atom) - type = CTEXT; - else if (selection_data->type == utf8_atom) - type = UTF8; - else - type = INVALID; - - if (type == INVALID || selection_data->length < 0) - { - /* I think the DND code automatically tries asking - for the various formats. */ - return; - } - drag_target_mark = gtk_text_buffer_get_mark (text_view->buffer, "__drag_target"); @@ -2823,71 +2751,13 @@ gtk_text_view_drag_data_received (GtkWidget *widget, &drop_point, drag_target_mark); - - switch (type) + str = gtk_selection_data_get_text (selection_data); + if (str) { - case STRING: - { - gchar *utf; - - utf = gtk_text_latin1_to_utf ((const gchar*)selection_data->data, - selection_data->length); - gtk_text_buffer_insert_interactive (text_view->buffer, &drop_point, - utf, -1, - text_view->editable); - g_free (utf); - } - break; - - case UTF8: - gtk_text_buffer_insert_interactive (text_view->buffer, &drop_point, - (const gchar *)selection_data->data, - selection_data->length, - text_view->editable); - break; - - case CTEXT: - { - gchar **list; - gint count; - gint i; - - count = gdk_text_property_to_text_list (selection_data->type, - selection_data->format, - selection_data->data, - selection_data->length, - &list); - for (i=0; ibuffer, - &drop_point, utf, -1, - text_view->editable); - - if (free_utf) - g_free(utf); - } - - if (count > 0) - gdk_free_text_list (list); - } - break; - - case INVALID: /* quiet compiler */ - break; + gtk_text_buffer_insert_interactive (text_view->buffer, + &drop_point, str, -1, + text_view->editable); + g_free (str); } } @@ -3029,7 +2899,7 @@ gtk_text_view_commit_handler (GtkIMContext *context, else { if (text_view->overwrite_mode) - gtk_text_view_delete_text (text_view, GTK_TEXT_DELETE_CHAR, 1); + gtk_text_view_delete (text_view, GTK_DELETE_CHARS, 1); gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, str, -1, text_view->editable); } diff --git a/gtk/gtktextview.h b/gtk/gtktextview.h index f20c2f0620..1b32c0386d 100644 --- a/gtk/gtktextview.h +++ b/gtk/gtktextview.h @@ -9,37 +9,6 @@ extern "C" { #endif /* __cplusplus */ -typedef enum { - GTK_TEXT_MOVEMENT_CHAR, /* move by forw/back chars */ - GTK_TEXT_MOVEMENT_POSITIONS, /* move by left/right chars */ - GTK_TEXT_MOVEMENT_WORD, /* move by forward/back words */ - GTK_TEXT_MOVEMENT_WRAPPED_LINE, /* move up/down lines (wrapped lines) */ - GTK_TEXT_MOVEMENT_LINE, /* move up/down paragraphs (newline-ended lines) */ - GTK_TEXT_MOVEMENT_LINE_ENDS, /* move to either end of a paragraph */ - GTK_TEXT_MOVEMENT_BUFFER_ENDS /* move to ends of the buffer */ -} GtkTextViewMovementStep; - -typedef enum { - GTK_TEXT_SCROLL_TO_TOP, - GTK_TEXT_SCROLL_TO_BOTTOM, - GTK_TEXT_SCROLL_PAGE_DOWN, - GTK_TEXT_SCROLL_PAGE_UP -} GtkTextViewScrollType; - -typedef enum { - GTK_TEXT_DELETE_CHAR, - GTK_TEXT_DELETE_HALF_WORD, /* delete only the portion of the word to the - left/right of cursor if we're in the middle - of a word */ - GTK_TEXT_DELETE_WHOLE_WORD, - GTK_TEXT_DELETE_HALF_WRAPPED_LINE, - GTK_TEXT_DELETE_WHOLE_WRAPPED_LINE, - GTK_TEXT_DELETE_HALF_LINE, /* like C-k in Emacs (or its reverse) */ - GTK_TEXT_DELETE_WHOLE_LINE, /* C-k in pico, kill whole line */ - GTK_TEXT_DELETE_WHITESPACE, /* M-\ in Emacs */ - GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE /* M-space in Emacs */ -} GtkTextViewDeleteType; - #define GTK_TYPE_TEXT_VIEW (gtk_text_view_get_type()) #define GTK_TEXT_VIEW(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_VIEW, GtkTextView)) #define GTK_TEXT_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_VIEW, GtkTextViewClass)) @@ -107,17 +76,16 @@ struct _GtkTextViewClass { /* These are all RUN_ACTION signals for keybindings */ /* move insertion point */ - void (* move_insert) (GtkTextView *text_view, GtkTextViewMovementStep step, gint count, gboolean extend_selection); + void (* move) (GtkTextView *text_view, GtkMovementStep step, gint count, gboolean extend_selection); /* move the "anchor" (what Emacs calls the mark) to the cursor position */ void (* set_anchor) (GtkTextView *text_view); - /* Scroll */ - void (* scroll_text) (GtkTextView *text_view, GtkTextViewScrollType type); /* Deletions */ - void (* delete_text) (GtkTextView *text_view, GtkTextViewDeleteType type, gint count); + void (* insert) (GtkTextView *text_view, const gchar *str); + void (* delete) (GtkTextView *text_view, GtkDeleteType type, gint count); /* cut copy paste */ - void (* cut_text) (GtkTextView *text_view); - void (* copy_text) (GtkTextView *text_view); - void (* paste_text) (GtkTextView *text_view); + void (* cut_clipboard) (GtkTextView *text_view); + void (* copy_clipboard) (GtkTextView *text_view); + void (* paste_clipboard) (GtkTextView *text_view); /* overwrite */ void (* toggle_overwrite) (GtkTextView *text_view); void (*set_scroll_adjustments) (GtkTextView *text_view,