forked from AuroraMiddleware/gtk
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:
parent
42e44b9fa5
commit
39339f14f5
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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__ */
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
806
gtk/gtkclipboard.c
Normal 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
87
gtk/gtkclipboard.h
Normal 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__
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user