+static gboolean
+selection_set_string (GtkSelectionData *selection_data,
+ const gchar *str,
+ gint len)
+{
+ gchar *tmp = g_strndup (str, len);
+ gchar *latin1 = gdk_utf8_to_string_target (tmp);
+ g_free (tmp);
+
+ if (latin1)
+ {
+ gtk_selection_data_set (selection_data,
+ GDK_SELECTION_TYPE_STRING,
+ 8, (guchar *) latin1, strlen (latin1));
+ g_free (latin1);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gboolean
+selection_set_compound_text (GtkSelectionData *selection_data,
+ const gchar *str,
+ gint len)
+{
+ gchar *tmp;
+ guchar *text;
+ GdkAtom encoding;
+ gint format;
+ gint new_length;
+ gboolean result = FALSE;
+
+#ifdef GDK_WINDOWING_X11
+ if (GDK_IS_X11_DISPLAY (selection_data->display))
+ {
+ tmp = g_strndup (str, len);
+ if (gdk_x11_display_utf8_to_compound_text (selection_data->display, tmp,
+ &encoding, &format, &text, &new_length))
+ {
+ gtk_selection_data_set (selection_data, encoding, format, text, new_length);
+ gdk_x11_free_compound_text (text);
+
+ result = TRUE;
+ }
+ g_free (tmp);
+ }
+#endif
+
+ return result;
+}
+
+/* Normalize \r and \n into \r\n
+ */
+static gchar *
+normalize_to_crlf (const gchar *str,
+ gint len)
+{
+ GString *result = g_string_sized_new (len);
+ const gchar *p = str;
+ const gchar *end = str + len;
+
+ while (p < end)
+ {
+ if (*p == '\n')
+ g_string_append_c (result, '\r');
+
+ if (*p == '\r')
+ {
+ g_string_append_c (result, *p);
+ p++;
+ if (p == end || *p != '\n')
+ g_string_append_c (result, '\n');
+ if (p == end)
+ break;
+ }
+
+ g_string_append_c (result, *p);
+ p++;
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+/* Normalize \r and \r\n into \n
+ */
+static gchar *
+normalize_to_lf (gchar *str,
+ gint len)
+{
+ GString *result = g_string_sized_new (len);
+ const gchar *p = str;
+
+ while (1)
+ {
+ if (*p == '\r')
+ {
+ p++;
+ if (*p != '\n')
+ g_string_append_c (result, '\n');
+ }
+
+ if (*p == '\0')
+ break;
+
+ g_string_append_c (result, *p);
+ p++;
+ }
+
+ return g_string_free (result, FALSE);
+}
+
+static gboolean
+selection_set_text_plain (GtkSelectionData *selection_data,
+ const gchar *str,
+ gint len)
+{
+ const gchar *charset = NULL;
+ gchar *result;
+ GError *error = NULL;
+
+ result = normalize_to_crlf (str, len);
+ if (selection_data->target == text_plain_atom)
+ charset = "ASCII";
+ else if (selection_data->target == text_plain_locale_atom)
+ g_get_charset (&charset);
+
+ if (charset)
+ {
+ gchar *tmp = result;
+ result = g_convert_with_fallback (tmp, -1,
+ charset, "UTF-8",
+ NULL, NULL, NULL, &error);
+ g_free (tmp);
+ }
+
+ if (!result)
+ {
+ g_warning ("Error converting from %s to %s: %s",
+ "UTF-8", charset, error->message);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ gtk_selection_data_set (selection_data,
+ selection_data->target,
+ 8, (guchar *) result, strlen (result));
+ g_free (result);
+
+ return TRUE;
+}
+
+static guchar *
+selection_get_text_plain (const GtkSelectionData *selection_data)
+{
+ const gchar *charset = NULL;
+ gchar *str, *result;
+ gsize len;
+ GError *error = NULL;
+
+ str = g_strdup ((const gchar *) selection_data->data);
+ len = selection_data->length;
+
+ if (selection_data->type == text_plain_atom)
+ charset = "ISO-8859-1";
+ else if (selection_data->type == text_plain_locale_atom)
+ g_get_charset (&charset);
+
+ if (charset)
+ {
+ gchar *tmp = str;
+ str = g_convert_with_fallback (tmp, len,
+ "UTF-8", charset,
+ NULL, NULL, &len, &error);
+ g_free (tmp);
+
+ if (!str)
+ {
+ g_warning ("Error converting from %s to %s: %s",
+ charset, "UTF-8", error->message);
+ g_error_free (error);
+
+ return NULL;
+ }
+ }
+ else if (!g_utf8_validate (str, -1, NULL))
+ {
+ g_warning ("Error converting from %s to %s: %s",
+ "text/plain;charset=utf-8", "UTF-8", "invalid UTF-8");
+ g_free (str);
+
+ return NULL;
+ }
+
+ result = normalize_to_lf (str, len);
+ g_free (str);
+
+ return (guchar *) result;
+}
+
+/**
+ * gtk_selection_data_set_text:
+ * @selection_data: a #GtkSelectionData
+ * @str: a UTF-8 string
+ * @len: the length of @str, or -1 if @str is nul-terminated.
+ *
+ * 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 successfully set,
+ * otherwise %FALSE.
+ **/
+gboolean
+gtk_selection_data_set_text (GtkSelectionData *selection_data,
+ const gchar *str,
+ gint len)
+{
+ g_return_val_if_fail (selection_data != NULL, FALSE);
+
+ if (len < 0)
+ len = strlen (str);
+
+ init_atoms ();
+
+ if (selection_data->target == utf8_atom)
+ {
+ gtk_selection_data_set (selection_data,
+ utf8_atom,
+ 8, (guchar *)str, len);
+ return TRUE;
+ }
+ else if (selection_data->target == GDK_TARGET_STRING)
+ {
+ return selection_set_string (selection_data, str, len);
+ }
+ else if (selection_data->target == ctext_atom ||
+ selection_data->target == text_atom)
+ {
+ if (selection_set_compound_text (selection_data, str, len))
+ return TRUE;
+ else if (selection_data->target == text_atom)
+ return selection_set_string (selection_data, str, len);
+ }
+ else if (selection_data->target == text_plain_atom ||
+ selection_data->target == text_plain_utf8_atom ||
+ selection_data->target == text_plain_locale_atom)
+ {
+ return selection_set_text_plain (selection_data, str, len);
+ }
+
+ 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: (type utf8): 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 (const GtkSelectionData *selection_data)
+{
+ guchar *result = NULL;
+
+ g_return_val_if_fail (selection_data != NULL, 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_for_display (selection_data->display,
+ selection_data->type,
+ selection_data->format,
+ selection_data->data,
+ selection_data->length,
+ &list);
+ if (count > 0)
+ result = (guchar *) list[0];
+
+ for (i = 1; i < count; i++)
+ g_free (list[i]);
+ g_free (list);
+ }
+ else if (selection_data->length >= 0 &&
+ (selection_data->type == text_plain_atom ||
+ selection_data->type == text_plain_utf8_atom ||
+ selection_data->type == text_plain_locale_atom))
+ {
+ result = selection_get_text_plain (selection_data);
+ }
+
+ return result;
+}
+
+/**
+ * gtk_selection_data_set_pixbuf:
+ * @selection_data: a #GtkSelectionData
+ * @pixbuf: a #GdkPixbuf
+ *
+ * Sets the contents of the selection from a #GdkPixbuf
+ * The pixbuf is converted to the form determined by
+ * @selection_data->target.
+ *
+ * Return value: %TRUE if the selection was successfully set,
+ * otherwise %FALSE.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gtk_selection_data_set_pixbuf (GtkSelectionData *selection_data,
+ GdkPixbuf *pixbuf)
+{
+ GSList *formats, *f;
+ gchar **mimes, **m;
+ GdkAtom atom;
+ gboolean result;
+ gchar *str, *type;
+ gsize len;
+
+ g_return_val_if_fail (selection_data != NULL, FALSE);
+ g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), FALSE);
+
+ formats = gdk_pixbuf_get_formats ();
+
+ for (f = formats; f; f = f->next)
+ {
+ GdkPixbufFormat *fmt = f->data;
+
+ mimes = gdk_pixbuf_format_get_mime_types (fmt);
+ for (m = mimes; *m; m++)
+ {
+ atom = gdk_atom_intern (*m, FALSE);
+ if (selection_data->target == atom)
+ {
+ str = NULL;
+ type = gdk_pixbuf_format_get_name (fmt);
+ result = gdk_pixbuf_save_to_buffer (pixbuf, &str, &len,
+ type, NULL,
+ ((strcmp (type, "png") == 0) ?
+ "compression" : NULL), "2",
+ NULL);
+ if (result)
+ gtk_selection_data_set (selection_data,
+ atom, 8, (guchar *)str, len);
+ g_free (type);
+ g_free (str);
+ g_strfreev (mimes);
+ g_slist_free (formats);
+
+ return result;
+ }
+ }
+
+ g_strfreev (mimes);
+ }
+
+ g_slist_free (formats);
+
+ return FALSE;
+}
+
+/**
+ * gtk_selection_data_get_pixbuf:
+ * @selection_data: a #GtkSelectionData
+ *
+ * Gets the contents of the selection data as a #GdkPixbuf.
+ *
+ * Return value: (transfer full): if the selection data contained a recognized
+ * image type and it could be converted to a #GdkPixbuf, a
+ * newly allocated pixbuf is returned, otherwise %NULL.
+ * If the result is non-%NULL it must be freed with g_object_unref().
+ *
+ * Since: 2.6
+ **/
+GdkPixbuf *
+gtk_selection_data_get_pixbuf (const GtkSelectionData *selection_data)
+{
+ GdkPixbufLoader *loader;
+ GdkPixbuf *result = NULL;
+
+ g_return_val_if_fail (selection_data != NULL, NULL);
+
+ if (selection_data->length > 0)
+ {
+ loader = gdk_pixbuf_loader_new ();
+
+ gdk_pixbuf_loader_write (loader,
+ selection_data->data,
+ selection_data->length,
+ NULL);
+ gdk_pixbuf_loader_close (loader, NULL);
+ result = gdk_pixbuf_loader_get_pixbuf (loader);
+
+ if (result)
+ g_object_ref (result);
+
+ g_object_unref (loader);
+ }
+
+ return result;
+}
+
+/**
+ * gtk_selection_data_set_uris:
+ * @selection_data: a #GtkSelectionData
+ * @uris: (array zero-terminated=1): a %NULL-terminated array of
+ * strings holding URIs
+ *
+ * Sets the contents of the selection from a list of URIs.
+ * The string is converted to the form determined by
+ * @selection_data->target.
+ *
+ * Return value: %TRUE if the selection was successfully set,
+ * otherwise %FALSE.
+ *
+ * Since: 2.6
+ **/
+gboolean
+gtk_selection_data_set_uris (GtkSelectionData *selection_data,
+ gchar **uris)
+{
+ g_return_val_if_fail (selection_data != NULL, FALSE);
+ g_return_val_if_fail (uris != NULL, FALSE);
+
+ init_atoms ();
+
+ if (selection_data->target == text_uri_list_atom)
+ {
+ GString *list;
+ gint i;
+ gchar *result;
+ gsize length;
+
+ list = g_string_new (NULL);
+ for (i = 0; uris[i]; i++)
+ {
+ g_string_append (list, uris[i]);
+ g_string_append (list, "\r\n");
+ }
+
+ result = g_convert (list->str, list->len,
+ "ASCII", "UTF-8",
+ NULL, &length, NULL);
+ g_string_free (list, TRUE);
+
+ if (result)
+ {
+ gtk_selection_data_set (selection_data,
+ text_uri_list_atom,
+ 8, (guchar *)result, length);
+
+ g_free (result);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * gtk_selection_data_get_uris:
+ * @selection_data: a #GtkSelectionData
+ *
+ * Gets the contents of the selection data as array of URIs.
+ *
+ * Return value: (array zero-terminated=1) (element-type utf8) (transfer full): if
+ * the selection data contains a list of
+ * URIs, a newly allocated %NULL-terminated string array
+ * containing the URIs, otherwise %NULL. If the result is
+ * non-%NULL it must be freed with g_strfreev().
+ *
+ * Since: 2.6
+ **/
+gchar **
+gtk_selection_data_get_uris (const GtkSelectionData *selection_data)
+{
+ gchar **result = NULL;
+
+ g_return_val_if_fail (selection_data != NULL, NULL);
+
+ init_atoms ();
+
+ if (selection_data->length >= 0 &&
+ selection_data->type == text_uri_list_atom)
+ {
+ gchar **list;
+ gint count = gdk_text_property_to_utf8_list_for_display (selection_data->display,
+ utf8_atom,
+ selection_data->format,
+ selection_data->data,
+ selection_data->length,
+ &list);
+ if (count > 0)
+ result = g_uri_list_extract_uris (list[0]);
+
+ g_strfreev (list);
+ }
+
+ return result;
+}
+
+
+/**
+ * gtk_selection_data_get_targets:
+ * @selection_data: a #GtkSelectionData object
+ * @targets: (out) (array length=n_atoms) (transfer container):
+ * location to store an array of targets. The result stored
+ * here must be freed with g_free().
+ * @n_atoms: location to store number of items in @targets.
+ *
+ * Gets the contents of @selection_data as an array of targets.
+ * This can be used to interpret the results of getting
+ * the standard TARGETS target that is always supplied for
+ * any selection.
+ *
+ * Return value: %TRUE if @selection_data contains a valid
+ * array of targets, otherwise %FALSE.
+ **/
+gboolean
+gtk_selection_data_get_targets (const GtkSelectionData *selection_data,
+ GdkAtom **targets,
+ gint *n_atoms)
+{
+ g_return_val_if_fail (selection_data != NULL, FALSE);
+
+ if (selection_data->length >= 0 &&
+ selection_data->format == 32 &&
+ selection_data->type == GDK_SELECTION_TYPE_ATOM)
+ {
+ if (targets)
+ *targets = g_memdup (selection_data->data, selection_data->length);
+ if (n_atoms)
+ *n_atoms = selection_data->length / sizeof (GdkAtom);
+
+ return TRUE;
+ }
+ else
+ {
+ if (targets)
+ *targets = NULL;
+ if (n_atoms)
+ *n_atoms = -1;
+
+ return FALSE;
+ }
+}
+
+/**
+ * gtk_targets_include_text:
+ * @targets: (array length=n_targets): an array of #GdkAtom<!-- -->s
+ * @n_targets: the length of @targets
+ *
+ * Determines if any of the targets in @targets can be used to
+ * provide text.
+ *
+ * Return value: %TRUE if @targets include a suitable target for text,
+ * otherwise %FALSE.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_targets_include_text (GdkAtom *targets,
+ gint n_targets)
+{
+ gint i;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE);
+
+ /* Keep in sync with gtk_target_list_add_text_targets()
+ */
+
+ init_atoms ();
+
+ for (i = 0; i < n_targets; i++)
+ {
+ if (targets[i] == utf8_atom ||
+ targets[i] == text_atom ||
+ targets[i] == GDK_TARGET_STRING ||
+ targets[i] == ctext_atom ||
+ targets[i] == text_plain_atom ||
+ targets[i] == text_plain_utf8_atom ||
+ targets[i] == text_plain_locale_atom)
+ {
+ result = TRUE;
+ break;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * gtk_targets_include_rich_text:
+ * @targets: (array length=n_targets): an array of #GdkAtom<!-- -->s
+ * @n_targets: the length of @targets
+ * @buffer: a #GtkTextBuffer
+ *
+ * Determines if any of the targets in @targets can be used to
+ * provide rich text.
+ *
+ * Return value: %TRUE if @targets include a suitable target for rich text,
+ * otherwise %FALSE.
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_targets_include_rich_text (GdkAtom *targets,
+ gint n_targets,
+ GtkTextBuffer *buffer)
+{
+ GdkAtom *rich_targets;
+ gint n_rich_targets;
+ gint i, j;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (targets != NULL || n_targets == 0, FALSE);
+ g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);