Remove g_convert (moved to glib) and now useless utf_to_latin1()

Thu Sep 14 12:21:12 2000  Owen Taylor  <otaylor@redhat.com>

        * 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
This commit is contained in:
Owen Taylor 2000-09-14 16:41:20 +00:00 committed by Owen Taylor
parent 42e44b9fa5
commit 39339f14f5
20 changed files with 2082 additions and 983 deletions

View File

@ -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,

View File

@ -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;
}

View File

@ -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__ */

View File

@ -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; i<local_count; i++)
{
/* list contains stuff in our default encoding
*/
if (need_conversion)
{
gchar *utf = g_convert (local_list[i], -1,
"UTF-8", charset,
NULL, NULL, &error);
if (utf)
{
if (list)
(*list)[count++] = utf;
else
g_free (utf);
}
else
{
g_warning ("Error converting to UTF-8 from '%s': %s",
charset, error->message);
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)

View File

@ -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);

View File

@ -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))

806
gtk/gtkclipboard.c Normal file
View File

@ -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 <string.h>
#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;
}

87
gtk/gtkclipboard.h Normal file
View File

@ -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 <gtk/gtkselection.h>
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__

View File

@ -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
{

View File

@ -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

View File

@ -33,21 +33,12 @@
#include <stdlib.h>
#include <string.h>
#include <gmodule.h>
#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 ();
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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 */

View File

@ -5,14 +5,24 @@
#include <string.h>
#include "gtkclipboard.h"
#include "gtkinvisible.h"
#include "gtkselection.h"
#include "gtksignal.h"
#include "gtktextbuffer.h"
#include "gtktextbtree.h"
#include "gtktextiterprivate.h"
#include <string.h>
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; i<count; i++)
{
/* list contains stuff in our default encoding */
gboolean free_utf = FALSE;
gchar *utf = NULL;
gchar *charset = NULL;
if (g_get_charset (&charset))
{
utf = g_convert (list[i], -1,
"UTF8", charset,
NULL);
free_utf = TRUE;
}
else
utf = list[i];
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);
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);
}

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -63,13 +63,13 @@
#include <string.h>
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; i<count; i++)
{
/* list contains stuff in our default encoding */
gboolean free_utf = FALSE;
gchar *utf = NULL;
gchar *charset = NULL;
if (g_get_charset (&charset))
{
utf = g_convert (list[i], -1,
"UTF8", charset, NULL);
free_utf = TRUE;
}
else
utf = list[i];
gtk_text_buffer_insert_interactive (text_view->buffer,
&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);
}

View File

@ -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,