[vte] widget: Provide a way to copy the selection to clipboard as HTML



commit 91e9c83878ee389ad63f45c7ec654011b75039e5
Author: Christian Persch <chpe src gnome org>
Date:   Sun May 7 13:57:41 2017 +0200

    widget: Provide a way to copy the selection to clipboard as HTML
    
    Currently, copying to HTML is disabled (#if 0) in the code, because
    it will generate the potentially huge selection data in both text and
    HTML formats.
    
    Instead, provide API to tell vte which format to use.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=365121

 bindings/vala/app.ui           |    1 +
 bindings/vala/app.vala         |   12 +-
 doc/reference/vte-sections.txt |    6 +-
 src/vte.cc                     |  325 +++++++++++++++++++++-------------------
 src/vte/vtedeprecated.h        |    4 +
 src/vte/vteenums.h             |   15 ++
 src/vte/vteterminal.h          |    3 +-
 src/vteaccess.cc               |    4 +-
 src/vtegtk.cc                  |   71 +++++++--
 src/vteinternal.hh             |   38 ++----
 10 files changed, 270 insertions(+), 209 deletions(-)
---
diff --git a/bindings/vala/app.ui b/bindings/vala/app.ui
index 8fb9dbb..bfcedd5 100644
--- a/bindings/vala/app.ui
+++ b/bindings/vala/app.ui
@@ -43,6 +43,7 @@
             <property name="receives_default">True</property>
             <property name="tooltip_text" translatable="yes">Copy</property>
             <property name="action_name">win.copy</property>
+            <property name="action_target">"text"</property>
             <property name="focus_on_click">False</property>
             <child>
               <object class="GtkImage" id="image2">
diff --git a/bindings/vala/app.vala b/bindings/vala/app.vala
index 11cd1ff..8663d63 100644
--- a/bindings/vala/app.vala
+++ b/bindings/vala/app.vala
@@ -196,7 +196,7 @@ class Window : Gtk.ApplicationWindow
   };
 
   private const GLib.ActionEntry[] action_entries = {
-    { "copy",        action_copy_cb            },
+    { "copy",        action_copy_cb,       "s" },
     { "copy-match",  action_copy_match_cb, "s" },
     { "paste",       action_paste_cb           },
     { "reset",       action_reset_cb,      "b" },
@@ -564,9 +564,12 @@ class Window : Gtk.ApplicationWindow
 
   /* Callbacks */
 
-  private void action_copy_cb()
+  private void action_copy_cb(GLib.SimpleAction action, GLib.Variant? parameter)
   {
-    terminal.copy_clipboard();
+    size_t len;
+    unowned string str = parameter.get_string(out len);
+    
+    terminal.copy_clipboard_format(str == "html" ? Vte.Format.HTML : Vte.Format.TEXT);
   }
 
   private void action_copy_match_cb(GLib.SimpleAction action, GLib.Variant? parameter)
@@ -625,7 +628,8 @@ class Window : Gtk.ApplicationWindow
       return false;
 
     var menu = new GLib.Menu();
-    menu.append("_Copy", "win.copy");
+    menu.append("_Copy", "win.copy::text");
+    menu.append("Copy As _HTML", "win.copy::html");
 
 #if VALA_0_24
     if (event != null) {
diff --git a/doc/reference/vte-sections.txt b/doc/reference/vte-sections.txt
index 69683dd..9b34536 100644
--- a/doc/reference/vte-sections.txt
+++ b/doc/reference/vte-sections.txt
@@ -5,6 +5,7 @@ VteTerminal
 VteCursorBlinkMode
 VteCursorShape
 VteEraseBinding
+VteFormat
 VteWriteFlags
 VteSelectionFunc
 vte_terminal_new
@@ -13,7 +14,7 @@ vte_terminal_feed_child
 vte_terminal_feed_child_binary
 vte_terminal_select_all
 vte_terminal_unselect_all
-vte_terminal_copy_clipboard
+vte_terminal_copy_clipboard_format
 vte_terminal_paste_clipboard
 vte_terminal_copy_primary
 vte_terminal_paste_primary
@@ -102,6 +103,8 @@ VTE_TYPE_CURSOR_SHAPE
 vte_cursor_shape_get_type
 VTE_TYPE_ERASE_BINDING
 vte_erase_binding_get_type
+VTE_TYPE_FORMAT
+vte_format_get_type
 VTE_TYPE_WRITE_FLAGS
 vte_write_flags_get_type
 VTE_TYPE_TERMINAL
@@ -123,6 +126,7 @@ vte_terminal_get_current_directory_uri
 vte_terminal_get_current_file_uri
 
 <SUBSECTION Deprecated>
+vte_terminal_copy_clipboard
 vte_terminal_match_set_cursor
 vte_terminal_match_add_gregex
 vte_terminal_search_get_gregex
diff --git a/src/vte.cc b/src/vte.cc
index 3cb8343..df1220e 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -1033,10 +1033,10 @@ VteTerminalPrivate::match_contents_refresh()
 {
        match_contents_clear();
        GArray *array = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
-        m_match_contents = get_text_displayed(true /* wrap */,
-                                              false /* include trailing whitespace */,
-                                              array,
-                                              nullptr);
+        auto match_contents = get_text_displayed(true /* wrap */,
+                                                 false /* include trailing whitespace */,
+                                                 array);
+        m_match_contents = g_string_free(match_contents, FALSE);
        m_match_attributes = array;
 }
 
@@ -3845,13 +3845,14 @@ next_match:
                 * by this insertion. */
                if (m_has_selection) {
                         //FIXMEchpe: this is atrocious
-                       char *selection = get_selected_text();
-                       if ((selection == NULL) ||
-                           (m_selection_text[VTE_SELECTION_PRIMARY] == NULL) ||
-                           (strcmp(selection, m_selection_text[VTE_SELECTION_PRIMARY]) != 0)) {
+                       auto selection = get_selected_text();
+                       if ((selection == nullptr) ||
+                           (m_selection[VTE_SELECTION_PRIMARY] == nullptr) ||
+                           (strcmp(selection->str, m_selection[VTE_SELECTION_PRIMARY]->str) != 0)) {
                                deselect_all();
                        }
-                       g_free(selection);
+                        if (selection)
+                                g_string_free(selection, TRUE);
                }
        }
 
@@ -5873,46 +5874,58 @@ clipboard_copy_cb(GtkClipboard *clipboard,
         that->widget_clipboard_requested(clipboard, data, info);
 }
 
+static char*
+text_to_utf16_mozilla(GString* text,
+                      gsize* len_ptr)
+{
+        /* Use g_convert() instead of g_utf8_to_utf16() since the former
+         * adds a BOM which Mozilla requires for text/html format.
+         */
+        return g_convert(text->str, text->len,
+                         "UTF-16", /* conver to UTF-16 */
+                         "UTF-8", /* convert from UTF-8 */
+                         nullptr /* out bytes_read */,
+                         len_ptr,
+                         nullptr);
+}
+
 void
 VteTerminalPrivate::widget_clipboard_requested(GtkClipboard *target_clipboard,
                                                GtkSelectionData *data,
                                                guint info)
 {
-       int sel;
-
-       for (sel = 0; sel < LAST_VTE_SELECTION; sel++) {
+       for (auto sel = 0; sel < LAST_VTE_SELECTION; sel++) {
                if (target_clipboard == m_clipboard[sel] &&
-                    m_selection_text[sel] != nullptr) {
+                    m_selection[sel] != nullptr) {
                        _VTE_DEBUG_IF(VTE_DEBUG_SELECTION) {
                                int i;
-                               g_printerr("Setting selection %d (%" G_GSIZE_FORMAT " UTF-8 bytes.)\n",
-                                       sel,
-                                       strlen(m_selection_text[sel]));
-                               for (i = 0; m_selection_text[sel][i] != '\0'; i++) {
-                                       g_printerr("0x%04x\n",
-                                               m_selection_text[sel][i]);
+                               g_printerr("Setting selection %d (%" G_GSIZE_FORMAT " UTF-8 bytes.) for 
target %s\n",
+                                           sel,
+                                           m_selection[sel]->len,
+                                           gdk_atom_name(gtk_selection_data_get_target(data)));
+                                char const* selection_text = m_selection[sel]->str;
+                                for (i = 0; selection_text[i] != '\0'; i++) {
+                                        g_printerr("0x%04x ", selection_text[i]);
+                                        if ((i & 0x7) == 0x7)
+                                                g_printerr("\n");
                                }
+                                g_printerr("\n");
                        }
                        if (info == VTE_TARGET_TEXT) {
-                                // FIXMEchpe cache the strlen instead of passing -1 here, and below
-                               gtk_selection_data_set_text(data, m_selection_text[sel], -1);
+                               gtk_selection_data_set_text(data,
+                                                            m_selection[sel]->str,
+                                                            m_selection[sel]->len);
                        } else if (info == VTE_TARGET_HTML) {
-#ifdef HTML_SELECTION
                                gsize len;
-                               gchar *selection;
-
-                               /* Mozilla asks that we start our text/html with the Unicode byte order mark 
*/
-                               /* (Comment found in gtkimhtml.c of pidgin fame) */
-                               selection = g_convert(m_selection_html[sel],
-                                       -1, "UTF-16", "UTF-8", NULL, &len, NULL);
+                                auto selection = text_to_utf16_mozilla(m_selection[sel], &len);
                                 // FIXMEchpe this makes yet another copy of the data... :(
-                               gtk_selection_data_set(data,
-                                       gdk_atom_intern("text/html", FALSE),
-                                       16,
-                                       (const guchar *)selection,
-                                       len);
+                                if (selection)
+                                        gtk_selection_data_set(data,
+                                                               gdk_atom_intern_static_string("text/html"),
+                                                               16,
+                                                               (const guchar *)selection,
+                                                               len);
                                g_free(selection);
-#endif
                        } else {
                                 /* Not reached */
                         }
@@ -5950,26 +5963,6 @@ VteTerminalPrivate::rgb_from_index(guint index,
        }
 }
 
-char *
-VteTerminalPrivate::get_text(vte::grid::row_t start_row,
-                             vte::grid::column_t start_col,
-                             vte::grid::row_t end_row,
-                             vte::grid::column_t end_col,
-                             bool block,
-                             bool wrap,
-                             bool include_trailing_spaces,
-                             GArray *attributes,
-                             gsize *ret_len)
-{
-        GString *text = get_text(start_row, start_col,
-                                 end_row, end_col,
-                                 block, wrap, include_trailing_spaces,
-                                 attributes);
-        if (ret_len)
-                *ret_len = text->len;
-        return static_cast<char*>(g_string_free(text, FALSE));
-}
-
 GString*
 VteTerminalPrivate::get_text(vte::grid::row_t start_row,
                              vte::grid::column_t start_col,
@@ -6103,22 +6096,12 @@ VteTerminalPrivate::get_text(vte::grid::row_t start_row,
                        vte_g_array_fill (attributes, &attr, string->len);
                }
        }
+
        /* Sanity check. */
-       g_assert(attributes == NULL || string->len == attributes->len);
-        return string;
-}
+        if (attributes != nullptr)
+                g_assert_cmpuint(string->len, ==, attributes->len);
 
-char *
-VteTerminalPrivate::get_text_displayed(bool wrap,
-                                       bool include_trailing_spaces,
-                                       GArray *attributes,
-                                       gsize *ret_len)
-{
-        GString *text = get_text_displayed(wrap, include_trailing_spaces,
-                                           attributes);
-        if (ret_len)
-                *ret_len = text->len;
-        return static_cast<char*>(g_string_free(text, FALSE));
+        return string;
 }
 
 GString*
@@ -6146,9 +6129,8 @@ VteTerminalPrivate::get_text_displayed_a11y(bool wrap,
                         attributes);
 }
 
-char *
-VteTerminalPrivate::get_selected_text(GArray *attributes,
-                                      gsize *len_ptr)
+GString*
+VteTerminalPrivate::get_selected_text(GArray *attributes)
 {
        return get_text(m_selection_start.row,
                         m_selection_start.col,
@@ -6157,8 +6139,7 @@ VteTerminalPrivate::get_selected_text(GArray *attributes,
                         m_selection_block_mode,
                         true /* wrap */,
                         false /* include trailing whitespace */,
-                        attributes,
-                        len_ptr);
+                        attributes);
 }
 
 /*
@@ -6275,17 +6256,18 @@ VteTerminalPrivate::char_to_cell_attr(VteCharAttributes const* attr) const
  *
  * Returns: (transfer full): a newly allocated text string, or %NULL.
  */
-char *
-VteTerminalPrivate::attributes_to_html(char const* text,
-                                       gsize len,
-                                       GArray *attrs)
+GString*
+VteTerminalPrivate::attributes_to_html(GString* text_string,
+                                       GArray* attrs)
 {
        GString *string;
        guint from,to;
        const VteCellAttr *attr;
        char *escaped, *marked;
 
-       g_assert(len == attrs->len);
+        char const* text = text_string->str;
+        auto len = text_string->len;
+        g_assert_cmpuint(len, ==, attrs->len);
 
        /* Initial size fits perfectly if the text has no attributes and no
         * characters that need to be escaped
@@ -6322,29 +6304,85 @@ VteTerminalPrivate::attributes_to_html(char const* text,
        }
        g_string_append(string, "</pre>");
 
-       return g_string_free(string, FALSE);
+       return string;
+}
+
+static GtkTargetEntry*
+targets_for_format(VteFormat format,
+                   int *n_targets)
+{
+        switch (format) {
+        case VTE_FORMAT_TEXT: {
+                static GtkTargetEntry *text_targets = nullptr;
+                static int n_text_targets;
+
+                if (text_targets == nullptr) {
+                       auto list = gtk_target_list_new (nullptr, 0);
+                       gtk_target_list_add_text_targets (list, VTE_TARGET_TEXT);
+
+                        text_targets = gtk_target_table_new_from_list (list, &n_text_targets);
+                       gtk_target_list_unref (list);
+                }
+
+                *n_targets = n_text_targets;
+                return text_targets;
+        }
+
+        case VTE_FORMAT_HTML: {
+                static GtkTargetEntry *html_targets = nullptr;
+                static int n_html_targets;
+
+                if (html_targets == nullptr) {
+                       auto list = gtk_target_list_new (nullptr, 0);
+                       gtk_target_list_add_text_targets (list, VTE_TARGET_TEXT);
+                        gtk_target_list_add (list,
+                                             gdk_atom_intern_static_string("text/html"),
+                                             0,
+                                             VTE_TARGET_HTML);
+
+                        html_targets = gtk_target_table_new_from_list (list, &n_html_targets);
+                       gtk_target_list_unref (list);
+                }
+
+                *n_targets = n_html_targets;
+                return html_targets;
+        }
+        default:
+                g_assert_not_reached();
+        }
 }
 
 /* Place the selected text onto the clipboard.  Do this asynchronously so that
  * we get notified when the selection we placed on the clipboard is replaced. */
 void
-VteTerminalPrivate::widget_copy(VteSelection sel)
+VteTerminalPrivate::widget_copy(VteSelection sel,
+                                VteFormat format)
 {
-       static GtkTargetEntry *targets = NULL;
-       static gint n_targets = 0;
-       GArray *attributes;
-
-       attributes = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
+        /* Only put HTML on the CLIPBOARD, not PRIMARY */
+        g_assert(sel == VTE_SELECTION_CLIPBOARD || format == VTE_FORMAT_TEXT);
 
        /* Chuck old selected text and retrieve the newly-selected text. */
-       g_free(m_selection_text[sel]);
-        gsize len;
-       m_selection_text[sel] = get_selected_text(attributes, &len);
-#ifdef HTML_SELECTION
-       g_free(m_selection_html[sel]);
-       m_selection_html[sel] = attributes_to_html(m_selection_text[sel], len,
-                                                   attributes);
-#endif
+        GArray *attributes = g_array_new(FALSE, TRUE, sizeof(struct _VteCharAttributes));
+        auto selection = get_selected_text(attributes);
+
+        if (m_selection[sel]) {
+                g_string_free(m_selection[sel], TRUE);
+                m_selection[sel] = nullptr;
+        }
+
+        if (selection == nullptr) {
+                g_array_free(attributes, TRUE);
+                m_has_selection = FALSE;
+                m_selection_owned[sel] = false;
+                return;
+        }
+
+        if (format == VTE_FORMAT_HTML) {
+                m_selection[sel] = attributes_to_html(selection, attributes);
+                g_string_free(selection, TRUE);
+        } else {
+                m_selection[sel] = selection;
+        }
 
        g_array_free (attributes, TRUE);
 
@@ -6352,38 +6390,24 @@ VteTerminalPrivate::widget_copy(VteSelection sel)
                m_has_selection = TRUE;
 
        /* Place the text on the clipboard. */
-       if (m_selection_text[sel] != NULL) {
-               _vte_debug_print(VTE_DEBUG_SELECTION,
-                               "Assuming ownership of selection.\n");
-               if (!targets) {
-                       GtkTargetList *list;
-
-                       list = gtk_target_list_new (NULL, 0);
-                       gtk_target_list_add_text_targets (list, VTE_TARGET_TEXT);
-
-#ifdef HTML_SELECTION
-                       gtk_target_list_add (list,
-                               gdk_atom_intern("text/html", FALSE),
-                               0,
-                               VTE_TARGET_HTML);
-#endif
+        _vte_debug_print(VTE_DEBUG_SELECTION,
+                         "Assuming ownership of selection.\n");
 
-                        targets = gtk_target_table_new_from_list (list, &n_targets);
-                       gtk_target_list_unref (list);
-               }
+        int n_targets;
+        auto targets = targets_for_format(format, &n_targets);
 
-                m_changing_selection = true;
-               gtk_clipboard_set_with_data(m_clipboard[sel],
-                                            targets,
-                                            n_targets,
-                                            clipboard_copy_cb,
-                                            clipboard_clear_cb,
-                                            this);
-                m_changing_selection = false;
+        m_changing_selection = true;
+        gtk_clipboard_set_with_data(m_clipboard[sel],
+                                    targets,
+                                    n_targets,
+                                    clipboard_copy_cb,
+                                    clipboard_clear_cb,
+                                    this);
+        m_changing_selection = false;
 
-               gtk_clipboard_set_can_store(m_clipboard[sel], NULL, 0);
-                m_selection_owned[sel] = true;
-       }
+        gtk_clipboard_set_can_store(m_clipboard[sel], nullptr, 0);
+        m_selection_owned[sel] = true;
+        m_selection_format[sel] = format;
 }
 
 /* Paste from the given clipboard. */
@@ -6505,7 +6529,7 @@ VteTerminalPrivate::maybe_end_selection()
                if (m_has_selection &&
                    !m_selecting_restart &&
                    m_selecting_had_delta) {
-                        widget_copy(VTE_SELECTION_PRIMARY);
+                        widget_copy(VTE_SELECTION_PRIMARY, VTE_FORMAT_TEXT);
                        emit_selection_changed();
                }
                m_selecting = false;
@@ -6977,7 +7001,7 @@ VteTerminalPrivate::select_all()
 
        _vte_debug_print(VTE_DEBUG_SELECTION, "Selecting *all* text.\n");
 
-        widget_copy(VTE_SELECTION_PRIMARY);
+        widget_copy(VTE_SELECTION_PRIMARY, VTE_FORMAT_TEXT);
        emit_selection_changed();
 
        invalidate_all();
@@ -8434,16 +8458,16 @@ VteTerminalPrivate::~VteTerminalPrivate()
         * throw the text onto the clipboard without an owner so that it
         * doesn't just disappear. */
        for (sel = VTE_SELECTION_PRIMARY; sel < LAST_VTE_SELECTION; sel++) {
-               if (m_selection_text[sel] != NULL) {
+               if (m_selection[sel] != nullptr) {
                        if (m_selection_owned[sel]) {
+                                // FIXMEchpe we should check m_selection_format[sel]
+                                // and also put text/html on if it's VTE_FORMAT_HTML
                                gtk_clipboard_set_text(m_clipboard[sel],
-                                                      m_selection_text[sel],
-                                                      -1);
+                                                      m_selection[sel]->str,
+                                                      m_selection[sel]->len);
                        }
-                       g_free(m_selection_text[sel]);
-#ifdef HTML_SELECTION
-                       g_free(m_selection_html[sel]);
-#endif
+                       g_string_free(m_selection[sel], TRUE);
+                        m_selection[sel] = nullptr;
                }
        }
 
@@ -10327,13 +10351,9 @@ VteTerminalPrivate::reset(bool clear_tabstops,
        m_selecting_restart = FALSE;
        m_selecting_had_delta = FALSE;
        for (int sel = VTE_SELECTION_PRIMARY; sel < LAST_VTE_SELECTION; sel++) {
-               if (m_selection_text[sel] != NULL) {
-                       g_free(m_selection_text[sel]);
-                       m_selection_text[sel] = NULL;
-#ifdef HTML_SELECTION
-                       g_free(m_selection_html[sel]);
-                       m_selection_html[sel] = NULL;
-#endif
+               if (m_selection[sel] != nullptr) {
+                       g_string_free(m_selection[sel], TRUE);
+                       m_selection[sel] = nullptr;
                }
                 m_selection_owned[sel] = false;
        }
@@ -10459,7 +10479,7 @@ VteTerminalPrivate::select_text(vte::grid::column_t start_col,
        m_selection_start.row = start_row;
        m_selection_end.col = end_col;
        m_selection_end.row = end_row;
-        widget_copy(VTE_SELECTION_PRIMARY);
+        widget_copy(VTE_SELECTION_PRIMARY, VTE_FORMAT_TEXT);
        emit_selection_changed();
 
        invalidate_region(MIN (start_col, end_col), MAX (start_col, end_col),
@@ -11029,22 +11049,18 @@ VteTerminalPrivate::search_rows(pcre2_match_context_8 *match_context,
                                 vte::grid::row_t end_row,
                                 bool backward)
 {
-       char *row_text;
-        gsize row_text_length;
        int start, end;
        long start_col, end_col;
-       gchar *word;
        VteCharAttributes *ca;
        GArray *attrs;
        gdouble value, page_size;
 
-       row_text = get_text(start_row, 0,
-                            end_row, -1,
-                            false /* block */,
-                            true /* wrap */,
-                            false /* include trailing whitespace */, /* FIXMEchpe maybe do include it since 
the match may depend on it? */
-                            nullptr,
-                            &row_text_length);
+       auto row_text = get_text(start_row, 0,
+                                 end_row, -1,
+                                 false /* block */,
+                                 true /* wrap */,
+                                 false /* include trailing whitespace */, /* FIXMEchpe maybe do include it 
since the match may depend on it? */
+                                 nullptr);
 
         int (* match_fn) (const pcre2_code_8 *,
                           PCRE2_SPTR8, PCRE2_SIZE, PCRE2_SIZE, uint32_t,
@@ -11058,7 +11074,7 @@ VteTerminalPrivate::search_rows(pcre2_match_context_8 *match_context,
                 match_fn = pcre2_match_8;
 
         r = match_fn(_vte_regex_get_pcre(m_search_regex.regex),
-                     (PCRE2_SPTR8)row_text, row_text_length , /* subject, length */
+                     (PCRE2_SPTR8)row_text->str, row_text->len , /* subject, length */
                      0, /* start offset */
                      m_search_regex.match_flags |
                      PCRE2_NO_UTF_CHECK | PCRE2_NOTEMPTY | PCRE2_PARTIAL_SOFT /* FIXME: HARD? */,
@@ -11079,10 +11095,9 @@ VteTerminalPrivate::search_rows(pcre2_match_context_8 *match_context,
 
         start = so;
         end = eo;
-        word = g_strndup(row_text, end - start);
 
        /* Fetch text again, with attributes */
-       g_free (row_text);
+       g_string_free(row_text, TRUE);
        if (!m_search_attrs)
                m_search_attrs = g_array_new (FALSE, TRUE, sizeof (VteCharAttributes));
        attrs = m_search_attrs;
@@ -11091,8 +11106,7 @@ VteTerminalPrivate::search_rows(pcre2_match_context_8 *match_context,
                             false /* block */,
                             true /* wrap */,
                             false /* include trailing whitespace */, /* FIXMEchpe maybe true? */
-                            attrs,
-                            nullptr);
+                            attrs);
 
        ca = &g_array_index (attrs, VteCharAttributes, start);
        start_row = ca->row;
@@ -11101,8 +11115,7 @@ VteTerminalPrivate::search_rows(pcre2_match_context_8 *match_context,
        end_row = ca->row;
        end_col = ca->column;
 
-       g_free (word);
-       g_free (row_text);
+       g_string_free (row_text, TRUE);
 
        select_text(start_col, start_row, end_col, end_row);
        /* Quite possibly the math here should not access adjustment directly... */
diff --git a/src/vte/vtedeprecated.h b/src/vte/vtedeprecated.h
index e6aba8f..d0a0c29 100644
--- a/src/vte/vtedeprecated.h
+++ b/src/vte/vtedeprecated.h
@@ -89,6 +89,10 @@ _VTE_DEPRECATED
 _VTE_PUBLIC
 void vte_pty_close (VtePty *pty) _VTE_GNUC_NONNULL(1);
 
+_VTE_DEPRECATED
+_VTE_PUBLIC
+void vte_terminal_copy_clipboard(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+
 G_END_DECLS
 
 #undef _VTE_DEPRECATED
diff --git a/src/vte/vteenums.h b/src/vte/vteenums.h
index 1006963..0e71715 100644
--- a/src/vte/vteenums.h
+++ b/src/vte/vteenums.h
@@ -137,6 +137,21 @@ typedef enum {
         VTE_REGEX_ERROR_NOT_SUPPORTED = G_MAXINT
 } VteRegexError;
 
+/**
+ * VteFormat:
+ * @VTE_FORMAT_TEXT: Export as plain text
+ * @VTE_FORMAT_HTML: Export as HTML formatted text
+ *
+ * An enumeratio type that can be used to specify the format the selection
+ * should be copied to the clipboard in.
+ *
+ * Since: 0.50
+ */
+typedef enum {
+        VTE_FORMAT_TEXT = 1,
+        VTE_FORMAT_HTML = 2
+} VteFormat;
+
 G_END_DECLS
 
 #endif /* __VTE_VTE_ENUMS_H__ */
diff --git a/src/vte/vteterminal.h b/src/vte/vteterminal.h
index 40cdc4f..cfd5a91 100644
--- a/src/vte/vteterminal.h
+++ b/src/vte/vteterminal.h
@@ -178,7 +178,8 @@ void vte_terminal_feed_child_binary(VteTerminal *terminal,
 /* Copy currently-selected text to the clipboard, or from the clipboard to
  * the terminal. */
 _VTE_PUBLIC
-void vte_terminal_copy_clipboard(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
+void vte_terminal_copy_clipboard_format(VteTerminal *terminal,
+                                        VteFormat format) _VTE_GNUC_NONNULL(1);
 _VTE_PUBLIC
 void vte_terminal_paste_clipboard(VteTerminal *terminal) _VTE_GNUC_NONNULL(1);
 _VTE_PUBLIC
diff --git a/src/vteaccess.cc b/src/vteaccess.cc
index 31eaec1..3bc68f0 100644
--- a/src/vteaccess.cc
+++ b/src/vteaccess.cc
@@ -1422,7 +1422,7 @@ vte_terminal_accessible_get_selection(AtkText *text, gint selection_number,
 
         auto impl = IMPL_FROM_WIDGET(widget);
 
-       if (!impl->m_has_selection)
+       if (!impl->m_has_selection || impl->m_selection[VTE_SELECTION_PRIMARY] == nullptr)
                return NULL;
 
         auto start_sel = impl->m_selection_start;
@@ -1431,7 +1431,7 @@ vte_terminal_accessible_get_selection(AtkText *text, gint selection_number,
        *start_offset = offset_from_xy (priv, start_sel.col, start_sel.row);
        *end_offset = offset_from_xy (priv, end_sel.col, end_sel.row);
 
-       return g_strdup(impl->m_selection_text[VTE_SELECTION_PRIMARY]);
+       return g_strdup(impl->m_selection[VTE_SELECTION_PRIMARY]->str);
 }
 
 static gboolean
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 0f077e2..68a1dce 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -140,7 +140,7 @@ vte_terminal_set_vscroll_policy(VteTerminal *terminal,
 static void
 vte_terminal_real_copy_clipboard(VteTerminal *terminal)
 {
-       IMPL(terminal)->widget_copy(VTE_SELECTION_CLIPBOARD);
+       IMPL(terminal)->widget_copy(VTE_SELECTION_CLIPBOARD, VTE_FORMAT_TEXT);
 }
 
 static void
@@ -1678,6 +1678,9 @@ vte_terminal_new(void)
  *
  * Places the selected text in the terminal in the #GDK_SELECTION_CLIPBOARD
  * selection.
+ *
+ * Deprecated: 0.50: Use vte_terminal_copy_clipboard_format() with %VTE_FORMAT_TEXT
+ *   instead.
  */
 void
 vte_terminal_copy_clipboard(VteTerminal *terminal)
@@ -1687,6 +1690,34 @@ vte_terminal_copy_clipboard(VteTerminal *terminal)
         IMPL(terminal)->emit_copy_clipboard();
 }
 
+
+/**
+ * vte_terminal_copy_clipboard_format:
+ * @terminal: a #VteTerminal
+ * @format: a #VteFormat
+ *
+ * Places the selected text in the terminal in the #GDK_SELECTION_CLIPBOARD
+ * selection in the form specified by @format.
+ *
+ * For all formats, the selection data (see #GtkSelectionData) will include the
+ * text targets (see gtk_target_list_add_text_targets() and
+ * gtk_selection_data_targets_includes_text()). For %VTE_FORMAT_HTML,
+ * the selection will also include the "text/html" target, which when requested,
+ * returns the HTML data in UTF-16 with a U+FEFF BYTE ORDER MARK character at
+ * the start.
+ *
+ * Since: 0.50
+ */
+void
+vte_terminal_copy_clipboard_format(VteTerminal *terminal,
+                                   VteFormat format)
+{
+        g_return_if_fail(VTE_IS_TERMINAL(terminal));
+        g_return_if_fail(format == VTE_FORMAT_TEXT || format == VTE_FORMAT_HTML);
+
+        IMPL(terminal)->widget_copy(VTE_SELECTION_CLIPBOARD, format);
+}
+
 /**
  * vte_terminal_copy_primary:
  * @terminal: a #VteTerminal
@@ -1699,7 +1730,7 @@ vte_terminal_copy_primary(VteTerminal *terminal)
 {
        g_return_if_fail(VTE_IS_TERMINAL(terminal));
        _vte_debug_print(VTE_DEBUG_SELECTION, "Copying to PRIMARY.\n");
-       IMPL(terminal)->widget_copy(VTE_SELECTION_PRIMARY);
+       IMPL(terminal)->widget_copy(VTE_SELECTION_PRIMARY, VTE_FORMAT_TEXT);
 }
 
 /**
@@ -2672,10 +2703,12 @@ vte_terminal_get_text(VteTerminal *terminal,
 {
        g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
         warn_if_callback(is_selected);
-       return IMPL(terminal)->get_text_displayed(true /* wrap */,
-                                                 false /* include trailing whitespace */,
-                                                 attributes,
-                                                 nullptr);
+        auto text = IMPL(terminal)->get_text_displayed(true /* wrap */,
+                                                       false /* include trailing whitespace */,
+                                                       attributes);
+        if (text == nullptr)
+                return nullptr;
+        return (char*)g_string_free(text, FALSE);
 }
 
 /**
@@ -2703,10 +2736,12 @@ vte_terminal_get_text_include_trailing_spaces(VteTerminal *terminal,
 {
        g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
         warn_if_callback(is_selected);
-       return IMPL(terminal)->get_text_displayed(true /* wrap */,
-                                                 true /* include trailing whitespace */,
-                                                 attributes,
-                                                 nullptr);
+        auto text = IMPL(terminal)->get_text_displayed(true /* wrap */,
+                                                       true /* include trailing whitespace */,
+                                                       attributes);
+        if (text == nullptr)
+                return nullptr;
+        return (char*)g_string_free(text, FALSE);
 }
 
 /**
@@ -2742,13 +2777,15 @@ vte_terminal_get_text_range(VteTerminal *terminal,
 {
        g_return_val_if_fail(VTE_IS_TERMINAL(terminal), NULL);
         warn_if_callback(is_selected);
-       return IMPL(terminal)->get_text(start_row, start_col,
-                                       end_row, end_col,
-                                       false /* block */,
-                                       true /* wrap */,
-                                       true /* include trailing whitespace */,
-                                       attributes,
-                                       nullptr);
+        auto text = IMPL(terminal)->get_text(start_row, start_col,
+                                             end_row, end_col,
+                                             false /* block */,
+                                             true /* wrap */,
+                                             true /* include trailing whitespace */,
+                                             attributes);
+        if (text == nullptr)
+                return nullptr;
+        return (char*)g_string_free(text, FALSE);
 }
 
 /**
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 7eea941..677e61a 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -357,11 +357,9 @@ public:
        /* Clipboard data information. */
         // FIXMEchpe check if this can make m_has_selection obsolete!
         bool m_selection_owned[LAST_VTE_SELECTION];
+        VteFormat m_selection_format[LAST_VTE_SELECTION];
         bool m_changing_selection;
-        char *m_selection_text[LAST_VTE_SELECTION];
-#ifdef HTML_SELECTION
-        char *m_selection_html[LAST_VTE_SELECTION];
-#endif
+        GString *m_selection[LAST_VTE_SELECTION];
         GtkClipboard *m_clipboard[LAST_VTE_SELECTION];
 
         ClipboardTextRequestGtk<VteTerminalPrivate> m_paste_request;
@@ -647,7 +645,8 @@ public:
 
 
         void widget_paste(GdkAtom board);
-        void widget_copy(VteSelection sel);
+        void widget_copy(VteSelection sel,
+                         VteFormat format);
         void widget_paste_received(char const* text);
         void widget_clipboard_cleared(GtkClipboard *clipboard);
         void widget_clipboard_requested(GtkClipboard *target_clipboard,
@@ -826,33 +825,17 @@ public:
                           bool block,
                           bool wrap,
                           bool include_trailing_spaces,
-                          GArray *attributes);
-
-        char* get_text(vte::grid::row_t start_row,
-                       vte::grid::column_t start_col,
-                       vte::grid::row_t end_row,
-                       vte::grid::column_t end_col,
-                       bool block,
-                       bool wrap,
-                       bool include_trailing_spaces,
-                       GArray *attributes,
-                       gsize *ret_len);
+                          GArray* attributes = nullptr);
 
         GString* get_text_displayed(bool wrap,
                                     bool include_trailing_spaces,
-                                    GArray *attributes);
-
-        char* get_text_displayed(bool wrap,
-                                 bool include_trailing_spaces,
-                                 GArray *attributes,
-                                 gsize *ret_len);
+                                    GArray* attributes = nullptr);
 
         GString* get_text_displayed_a11y(bool wrap,
                                          bool include_trailing_spaces,
-                                         GArray *attributes);
+                                         GArray* attributes = nullptr);
 
-        char *get_selected_text(GArray *attributes = nullptr,
-                                gsize *len_ptr = nullptr);
+        GString* get_selected_text(GArray* attributes = nullptr);
 
         inline void rgb_from_index(guint index,
                                    vte::color::rgb& color) const;
@@ -874,9 +857,8 @@ public:
                                char const* text) const;
         VteCellAttr const* char_to_cell_attr(VteCharAttributes const* attr) const;
 
-        char *attributes_to_html(char const* text,
-                                 gsize len,
-                                 GArray *attrs);
+        GString* attributes_to_html(GString* text_string,
+                                    GArray* attrs);
 
         void start_selection(long x,
                              long y,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]