Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GtkEntry change text on user input

In a gui I want to modify the text a user inserts in a GtkEntry. eg if the user enters 'joHn doe', my gui should see this is not a nicely formatted name and changes this into 'John Doe'.

I connect the a handler to "changed" signal as described in eg GtkEntry text change signal. The problem that occurs is if I change the entry in my signal handler, the "changed" signal is emitted again and again until kingdom comes.

I currently prevent this by doing a string comparison, and I only change the text in the GtkEntryBuffer if the text "namified" version is unequal to the text inside the entry. However I feel like as programmer I should be able to change the the text inside the entry without that the changed handler is called over and over again.

The changed signal handler is:

void nameify_entry ( GtkEditable* editable, gpointer data )
{
    gchar* nameified;
    const gchar *entry_text;

    entry_text = gtk_entry_get_text( GTK_ENTRY(editable) );
    nameified = nameify(entry_text);

    /*is it possible to change the buffer without this using this string
      comparison, without the "change" signal being emitted over and over again?*/
    if ( g_strcmp0(entry_text, nameified) != 0 ){
        GtkEntryBuffer* buf = gtk_entry_get_buffer(GTK_ENTRY(editable) );
        gtk_entry_buffer_set_text( buf, nameified, -1 );
    }
    g_free(nameified);
}

and my nameify function is:

/*removes characters that should not belong to a name*/
gchar*
nameify ( const char* cstr )
{
    const char* c;
    gchar* ret_val;
    GString* s = g_string_new("");

    gboolean uppercase_next = TRUE;
    g_debug( "string = %s", cstr);

    for ( c = cstr; *c != '0'; c = g_utf8_next_char(c) ) {
        gunichar cp = g_utf8_get_char(c); 
        if ( cp == 0 ) break;
        if ( g_unichar_isalpha( cp ) ){
            if ( uppercase_next ){
                g_string_append_unichar( s, g_unichar_toupper(cp) );
                uppercase_next = FALSE;
            }
            else{
                g_string_append_unichar(s,g_unichar_tolower(cp));
            }
        }
        if ( cp == '-' ){
            g_string_append_unichar( s, cp);
            uppercase_next = TRUE;
        }
        if ( cp == ' '){
            g_string_append_unichar( s, cp);
            uppercase_next = TRUE;
        }
    }

    ret_val = s->str;
    g_string_free(s, FALSE);
    return ret_val;
}

any help is most welcome.

like image 297
hetepeperfan Avatar asked May 14 '13 09:05

hetepeperfan


1 Answers

It's not really handy to connect to the 'changed' signal, but more appropriate to connect to the 'insert-text' signal. Even better to have the default 'insert-text' handler update the entry. Than use g_signal_connect_after on the 'insert-text' signal to update the text in the entry this prevents the changed signal to run infinitely. This should also be done to the 'delete-text' signal, because if a user deletes a capital letter, the capital should be removed and the second should be capitalized.

so on creation run:

g_signal_connect_after( entry, "insert-text", G_CALLBACK(name_insert_after), NULL );
g_signal_connect_after( entry, "delete-text", G_CALLBACK(name_delete_after), NULL );

Then you can have these signal handlers:

void
name_insert_after (GtkEditable* edit,
                   gchar* new_text,
                   gint new_length,
                   gpointer position,
                   gpointer data)
{
    /*prevent compiler warnings about unused variables*/
    (void) new_text; (void) new_length; (void) position; (void) data;
    const gchar* content = gtk_entry_get_text( GTK_ENTRY(edit) );
    gchar* modified = nameify( content);
    gtk_entry_set_text(GTK_ENTRY(edit),modified);
    g_free(modified);
}

void
name_delete_after (GtkEditable* edit,
                   gint start_pos,
                   gint end_pos,
                   gpointer data)
{
    /*no op cast to prevent compiler warnings*/
    (void) start_pos; (void) end_pos; (void) data;
    /*get text and modify the entry*/
    int cursor_pos = gtk_editable_get_position(edit);
    const gchar* content = gtk_entry_get_text( GTK_ENTRY(edit) );
    gchar* modified = nameify( content);
    gtk_entry_set_text(GTK_ENTRY(edit),modified);
    gtk_editable_set_position(edit, cursor_pos);
    g_free(modified);
}

and these can than be used with the nameify function in the original post. you might even provide a function pointer at the data instead of 'NULL' to use this one handler with different functions that are able to modify the string in the entry.

like image 58
hetepeperfan Avatar answered Nov 15 '22 09:11

hetepeperfan