SOLVED - sortof [was Re: "mark_set" callback fires 3 or 4 times when using arrow keys/mouse in textview?}
- From: "David C. Rankin" <drankinatty suddenlinkmail com>
- To: gtk-app-devel-list gnome org
- Subject: SOLVED - sortof [was Re: "mark_set" callback fires 3 or 4 times when using arrow keys/mouse in textview?}
- Date: Mon, 28 Dec 2015 19:45:28 -0600
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[3] : 0x24792c0 : (null)
line: 1 col: 3
mark: 0x24797a0 - gtbgi (buffer): 0x237d600 mark->name: (null)
mark[0] : 0x237d600 : insert
mark[1] : 0x237d620 : selection_bound
mark[2] : 0x237d7a0 : gtk_drag_target
mark[3] : 0x24797a0 : (null)
line: 1 col: 4
Examining, there are 4-marks at each iter location and the mark passed by the
callback is mark[3] even though all 4 are actually present at the iter location.
When the Left-Arrow key is pressed, the callback fires 3 times with each of the
marks present each time:
mark: 0x237d600 - gtbgi (buffer): 0x237d600 mark->name: insert
mark[0] : 0x237d600 : insert
mark[1] : 0x237d620 : selection_bound
line: 1 col: 3
mark: 0x237d620 - gtbgi (buffer): 0x237d600 mark->name: selection_bound
mark[0] : 0x237d600 : insert
mark[1] : 0x237d620 : selection_bound
mark: 0x2479700 - gtbgi (buffer): 0x237d600 mark->name: (null)
mark[0] : 0x237d600 : insert
mark[1] : 0x237d620 : selection_bound
mark[2] : 0x2479700 : (null)
For the first firing of the callback, the "insert" mark is passed, the second
firing the "selection_bound" mark is passed and lastly, the anonymous 'null'
marks is passed. Essentially, the callback fires once for each mark at the iter
location when the Left-Arrow key is pressed.
When the mouse is clicked to position the insert point at the end of the buffer,
the callback fires 4 times as follows:
mark: 0x237d600 - gtbgi (buffer): 0x237d600 mark->name: insert
mark[0] : 0x237d7a0 : gtk_drag_target
mark[1] : 0x237d600 : insert
mark[2] : 0x237d620 : selection_bound
line: 1 col: 4
mark: 0x237d620 - gtbgi (buffer): 0x237d600 mark->name: selection_bound
mark[0] : 0x237d7a0 : gtk_drag_target
mark[1] : 0x237d600 : insert
mark[2] : 0x237d620 : selection_bound
mark: 0x24792a0 - gtbgi (buffer): 0x237d600 mark->name: (null)
mark[0] : 0x237d7a0 : gtk_drag_target
mark[1] : 0x237d600 : insert
mark[2] : 0x237d620 : selection_bound
mark[3] : 0x24792a0 : (null)
mark: 0x2479200 - gtbgi (buffer): 0x237d600 mark->name: (null)
mark[0] : 0x237d7a0 : gtk_drag_target
mark[1] : 0x237d600 : insert
mark[2] : 0x237d620 : selection_bound
mark[3] : 0x2479200 : (null)
mark[4] : 0x24792a0 : (null)
where there is a 'gtk_drag_target' mark included when the mouse is clicked, but
otherwise, aside from the additional mark and additional anonymous mark, it
behaves like the Left-Arrow key press.
So the bottom line, since the "insert" mark is included in every firing as one
of the marks at the location, but is *Not* passed as the parameter mark to the
callback on normal text input, then there isn't a way to prevent the callback
firing multiple times in any case. The best that can be done is to efficiently
determine if the callback needs to respond to a "mark_set" signal. In that case,
checking whether the "insert" mark is present and whether there is any change in
the line:col location is about as good as it gets.
The other alternative is split responsibility for updating the line:col location
between the on_mark_set() callback and an input-handler callback and to have
your input-handler update line:col for normal text input and on_mark_set() only
respond when the "insert" mark is passed as a parameter. However, I'm not sure
that is any better a solution.
Hopefully this will help someone in the future. If you have a better way of
validating the "mark" passed to the callback following the "mark_set" generation
for a textview buffer that will work in a like manner, please let me know.
--
David C. Rankin, J.D.,P.E.
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]