SOLVED - sortof [was Re: "mark_set" callback fires 3 or 4 times when using arrow keys/mouse in textview?}

2015-12-28 Thread David C. Rankin

On 12/22/2015 05:13 PM, David C. Rankin wrote:

   How can I limit the the execution of the 'on_mark_set' callback to a single
call regardless of whether there is data being entered or if cursor is being
moved with the arrow-keys or mouse? Any help will be greatly appreciated.



OK, I have a solution that is probably not as elegant as desired. Delving into 
the "mark_set" signal and multiple firing of the callback opened a small can of 
worms regarding a number of considerations associated with GtkTextMark and 
GtkTextIter relationships.


The problem can be summarized as follows:

(1) the proper callback prototype to handle the "mark_set" signal is:

void on_mark_set (GtkTextBuffer *buffer, GtkTextIter *iter,
  GtkTextMark *mark, context *app)

(2) there are multiple text marks at each iter location.

(3) when responding to the "mark_set" signal, the 'mark' passed to the callback 
can be any of the marks at the "iter" location which are passed in "no 
particular order" and are passed differently depending on whether normal text is 
being entered, arrow-keys are pressed, or whether the mouse is clicked to 
reposition to "insert" cursor mark.


(4) This prevents a simple comparison between the "mark" parameter and 
gtk_text_buffer_get_insert (buffer) alone from being used to determine whether 
to respond to a "mark_set" signal or not.


The short version of a solution to prevent responding to multiple "mark_set" 
signals is as follows. (the callback is still fired, but you can test between 
'current' and 'new' line:col locations responding only when the values differ)


void on_mark_set (GtkTextBuffer *buffer, GtkTextIter *iter,
  GtkTextMark *mark, context *app)
{
gint line, col;

line = gtk_text_iter_get_line (iter);
col = gtk_text_iter_get_line_offset (iter);

if (line == app->line && col == app->col) return;

app->line = line;
app->col = col;

g_print (" line: %3d col: %d\n", app->line + 1, app->col + 1);

if (buffer) {}
if (mark) {}
}

Now for the rest of the story... I am normally reasonably adept at finding this 
type of information myself in the documentation or via the web without having to 
ask, but in this case there simply isn't much in the way of details on how, what 
or in what order "mark_set" callback parameters are passed each time the 
callback is fired. I'll leave what I found here to save the next person a bit of 
time.


Each time the "mark_set" signal is generated, there can be multiple marks at any 
given iter *location*. In the case of normal input (e.g. 'a', 'b', etc...) the 
mark passed to the on_mark_set() callback is not necessarily the "insert" mark, 
but is apparently simply the last of the marks present at that iter *location*. 
(In each case below an anonymous mark is passed as a result of normal text 
input) The list of marks at any given iter position can be found by the GSList 
of marks returned by gtk_text_iter_get_marks (iter). (*note:* the marks in the 
list returned are in *no particular* order -- which is probably the basis for 
this whole issue to begin with. See: 
https://developer.gnome.org/gtk2/stable/GtkTextIter.html#gtk-text-iter-get-marks) For 
example, you can examine the marks with the following debug code:


void on_mark_set (GtkTextBuffer *buffer, GtkTextIter *iter,
GtkTextMark *mark, context *app)
{
gint line, col;

#ifdef DEBUG
g_print ("  mark: %p  - gtbgi (buffer): %p  mark->name: %s\n", mark,
gtk_text_buffer_get_insert (buffer),
gtk_text_mark_get_name (mark));

GSList *marks = gtk_text_iter_get_marks (iter);
GSList *p = marks;
gint i = 0;
while (p) {
const gchar *name = gtk_text_mark_get_name (GTK_TEXT_MARK(p->data));
g_print ("mark[%d] : %p : %s\n", i++, GTK_TEXT_MARK(p->data), 
name);

p = p->next;
}
g_slist_free (marks);
#endif

line = gtk_text_iter_get_line (iter);
col = gtk_text_iter_get_line_offset (iter);

if (line == app->line && col == app->col) return;

app->line = line;
app->col = col;

#ifdef DEBUG
g_print (" line: %3d col: %d\n\n", app->line + 1, app->col + 1);
#endif

if (mark) {}
}

Compiling and then using the same (enter 'abc', then Left-Arrow, then 
*mouse-click* at the end) fires the on_mark_set() callback for each 'abc' entered:


$ ./bin/text_mcve_dbg
  mark: 0x2458880  - gtbgi (buffer): 0x237d600  mark->name: (null)
mark[0] : 0x237d600 : insert
mark[1] : 0x237d620 : selection_bound
mark[2] : 0x237d7a0 : gtk_drag_target
mark[3] : 0x2458880 : (null)
 line:   1 col: 2

  mark: 0x24792c0  - gtbgi (buffer): 0x237d600  mark->name: (null)
mark[0] : 0x237d600 : insert
mark[1] : 0x237d620 : selection_bound
mark[2] : 0x237d7a0 : gtk_drag_target

"mark_set" callback fires 3 or 4 times when using arrow keys/mouse in textview?

2015-12-22 Thread David C. Rankin

All,

  I am using using the 'mark_set' signal to update the row:col values within my 
GtkTextBuffer. I have a simple setup with the textview inside a scrolled window 
inside a window:


  window
scrolled window
  textview

  I use a structure to hold the various persistent values for my application, 
for example:


typedef struct {
GtkWidget *window;
GtkWidget *view;
GtkTextBuffer *buffer;
GtkWidget *entry;
GtkTextMark *cursor;
gint line;/* line number   */
gint col; /* column number */
gint indent;
gint indentlevel;
gint tabsz;
gint winwidth;
gint winheight;
gboolean overwrite;
} context;

  All I am trying to do is update the current 'line' and 'col' values in the 
instance of the struct used with my application ('app'). Within my main window 
create function I initialize the values for 'context app;', create the window, 
scrolled window and textview, and connect the 'mark_set' signal to my 
'on_mark_set' callback, passing the instance of the struct as the data to the 
callback:


g_signal_connect (app->buffer, "mark_set",
  G_CALLBACK (on_mark_set), app);

  The 'on_mark_set' callback (with g_print for debug purposes):

void on_mark_set (GtkTextBuffer *buffer, context *app)
{
GtkTextIter iter;

app->cursor = gtk_text_buffer_get_insert (buffer);

gtk_text_buffer_get_iter_at_mark (buffer, , app->cursor);

app->line = gtk_text_iter_get_line ();
app->col = gtk_text_iter_get_line_offset ();

g_print (" line: %3d col: %d\n", app->line + 1, app->col + 1);
}

  The values for 'app->line' and 'app->col' are correctly set (only once) 
following each keypress where input is being provided to the buffer. e.g. 
inputting 'abc' into the textview results in:


$ ./bin/text_view_fn
 line:   1 col: 2
 line:   1 col: 3
 line:   1 col: 4

  However, when I use the arrow keys to move the input cursor or use the mouse 
to reposition it, the callback tripple-fires or quadruple-fires. e.g. pressing 
the left-arrow to backup one position results in the following:


 line:   1 col: 3
 line:   1 col: 3
 line:   1 col: 3

  or repositioning by clicking the mouse at the beginning results in a 
quadruple-fire of the callback:


 line:   1 col: 1
 line:   1 col: 1
 line:   1 col: 1
 line:   1 col: 1

  How can I limit the the execution of the 'on_mark_set' callback to a single 
call regardless of whether there is data being entered or if cursor is being 
moved with the arrow-keys or mouse? Any help will be greatly appreciated.



--
David C. Rankin, J.D.,P.E.
___
gtk-app-devel-list mailing list
gtk-app-devel-list@gnome.org
https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list