GtkTextView paste-clipboard signal

Hi, ALL,
Is there a way to get access to the text being pasted?

I’m trying to create a GtkTextView with the maximum number of characters allowed, but it seems I can’t do that, because I will not be able to cut the pasted text before it will be inserted. And according to the documentation I need g_signal_connect() and not g_signal_connect_after().

Or the only way is to access the Clipboard and get the text from there?

Thank you.

Hi,

If you want to control and limit the pasted text, then the best way would be to create a subclass of GtkTextBuffer and override the virtual function insert_text.

This way, every time text is inserted (by pasting or keyboard or whatever) you will have the possibility to check and trim the content.

Then, when creating the GtkTextView, pass your custom GtkTextBuffer subclass instance instead of using the default one.

@gwillems ,

Thank you for the response.

I took a look at the link to the docs and there is nothing there - no explanation is given to any of the parameters or what this function doe in regards to them.

Can you give me an idea of how to override it so I can achieve what I want?

Or explain what the parameters to that virtual are…

Thank you.

The insert_text virtual function is the class closure for the GtkTextBuffer::insert-text signal, so you’ll find the documentation there.

You subclass GtkTextBuffer and inside the class initialisation function, override the virtual function.

@oneeyeman1 which language are you using?

Here a fully working example in Python:

import sys
import gi
gi.require_version('Gtk', '4.0')
from gi.repository import GLib, Gtk

class MyApp(Gtk.Application):
    def __init__(self):
        Gtk.Application.__init__(self)
        self.connect('activate', self.on_activate)
    def on_activate(self, app):
        self.add_window(MyAppWindow())

class MyAppWindow(Gtk.ApplicationWindow):
    def __init__(self):
        Gtk.ApplicationWindow.__init__(self, default_width=300, default_height=200)
        buffer = MyTextBuffer(20)
        view = Gtk.TextView.new_with_buffer(buffer)
        self.set_child(view)
        self.present()

class MyTextBuffer(Gtk.TextBuffer):
    def __init__(self, max_length):
        Gtk.TextBuffer.__init__(self)
        self.max_length = max_length
    def do_insert_text(self, itr, txt, txtlen):
        # Get buffer size, in characters
        current_length = self.get_char_count()
        # Trim inserted text, if needed
        if current_length + len(txt) > self.max_length:
            insert_length = 1 + self.max_length - current_length - len(txt)
            txt = txt[:insert_length]
        # Let parent's method do the actual insert
        Gtk.TextBuffer.do_insert_text(self, itr, txt, -1)

if __name__ == '__main__':
    sys.exit(MyApp().run(sys.argv))

If you use C, then have a look at the GtkSourceView code, they have a custom override of insert_text.

It should look like this (entirely untested!!!, feedback welcome):

#define MAX_CHARS   20

#define MY_TYPE_BUFFER (my_buffer_get_type())
G_DECLARE_DERIVABLE_TYPE (MyBuffer, my_buffer, MY, BUFFER, GtkTextBuffer)

G_DEFINE_TYPE (MyBuffer, my_buffer, GTK_TYPE_TEXT_BUFFER)

static void
my_buffer_class_init (MyBufferClass *klass)
{
    GObjectClass *object_class;
    GtkTextBufferClass *text_buffer_class;

    object_class = G_OBJECT_CLASS (klass);
    text_buffer_class = GTK_TEXT_BUFFER_CLASS (klass);

    text_buffer_class->insert_text = my_buffer_real_insert_text;
}

static void
my_buffer_real_insert_text (GtkTextBuffer *buffer,
                            GtkTextIter   *iter,
                            const gchar   *text,
                            int            len)
{
    int current_char_count = gtk_text_buffer_get_char_count (buffer);
    int insert_char_count  = (int) g_utf8_strlen (text, len);

    if (current_char_count + insert_char_count > MAX_CHARS)
      {
        int    new_cnt = MAX_CHARS - current_char_count - insert_char_count;
        gchar *new_txt = g_utf8_substring (txt, 0, new_cnt);

        GTK_TEXT_BUFFER_CLASS (my_buffer_parent_class)->insert_text (buffer, iter, new_txt, -1);

        g_free (new_txt);
      }
    else
      {
        GTK_TEXT_BUFFER_CLASS (my_buffer_parent_class)->insert_text (buffer, iter, text, len);
      }
}

@ebassi ,

Are you saying that the insert-text signal handler will call the insert_text virtual?

If that’s the case - why do I need to derive and override?

@gwillems ,

THx for the samples. I will review them.

The insert_text function pointer will be called as part of the signal emission chain, yes, unless something stops the chain from a callback connected with g_signal_connect().

If you’re changing the way the TextBuffer handles text insertion it is generally easier to do so in a derived type, since you can decide whether or not to “chain up” to the default implementation from an overridden virtual function. If you used a signal handler, you’d have to call g_signal_stop_emission_by_name() instead.

@ebassi ,

So basically I can do g_signal_connect_before() with insert-text signal. And then in the handler fix the text and the length. Call g_signal_stop_emission_by_name() if nothing should be inserted, and let it go thru if partial insert can be done, right?

Thank you.
I will see what’s best here.

@ebassi ,

I used the following code:

static void insert_text_callback( GtkTextBuffer *buffer, GtkTextIter *end, gchar *text, , gint len, void *user_data )
{
    int count = gtk_text_buffer_get_char_count( buffer );
    int newTextLen = g_utf8_strlen( text, len );
    if( count + newTextLen == 20 )
    {
        g_signal_stop_emission_by_name( buffer, "insert_text" );
        return;
    }
    if( count + newTextLen > 20 )
    {
        int insertedTextLen = ( count + newTextLen ) - 20;
        text = g_utf8_substring( text, 0, newTextLen );
    }
}

It works when I type on the keyboard, but when pasting the longer text, it still pastes the whole text.

What am I doing wrong?

Checkibng the values under the debugger I see that the “text” variable is correctly truncated, but it still doesn’t do what should be.

Should I explicitly call “insert-text” and then “stop_emission_by_name()”?

I have an additional paste handler - will that prevents it from running?

Thank you.

@ebassi,

There is also another problem.

When I do paste the text with Ctrl+V and everything is good (text is below the maximum length) - the text is pasted twice.

The “paste-clipboard” handler kicks in and insert text second time.

Anyway to avoid that?

Thank you.

g_utf8_substring() allocates a new string in memory, so the original pointer to text stays unchanged and will be passed to the default handler.

By the way, after using g_utf8_substring() you should call g_free() on the string to free it and avoid memory leaks.

Hmm… are you connected to the paste-clipboard signal? If yes, what is done in the callback?

@gwillems

But I’m assigning it to the same “text” pointer.

However I think I found a solution. I just added following code:

gtk_text_buffer_insert( buffer, end, text, -1 );
g_signal_stop_emission_by_name( buffer, "insert_text" );
return;

after g_utf8_substring() call

Yes, will do.

So I just checked and all the code does in responce to the ID_PASTE menu handler is to call “g_signal_emit_by_name( text, “paste-clipboard” );”.

Thank you.

Do you still see the double paste if you call g_signal_stop_emission_by_name before gtk_text_buffer_insert?

g_signal_stop_emission_by_name( buffer, "insert_text" );
gtk_text_buffer_insert( buffer, end, text, -1 );

.

That only changes the text pointer locally in the function.

@gwillems ,

Double insertion happens on the good paste.

Lets say I have 20 as a maximum length and I have a text inside of the "abcd ". If I paste the phrase “First” the control will contain “abcd FirstFirst”.

In this case the code is not going inside that condition but skips it.

I will check anyway.

And yes - I still see the double insertion.

Thank you.

I never tried to manipulate the signal chain like that, so can’t tell what’s exactly going wrong…

I would personally strongly recommend to try to subclass the GtkTextBuffer and override insert_text. Maybe a little more verbose in C, but you’ll have the full and exclusive control on the insert.

This topic was automatically closed 45 days after the last reply. New replies are no longer allowed.