C “GtkTextBuffer”;标记“U集”;信号为箭头键或鼠标单击触发回调3-4次

C “GtkTextBuffer”;标记“U集”;信号为箭头键或鼠标单击触发回调3-4次,c,gtk2,C,Gtk2,我正试图使用'mark\u set'信号更新我的GtkTextBuffer中的行:列值。对于测试,我有一个简单的设置,在一个滚动窗口内的一个窗口内设置textview,例如: 我使用一个结构来保存应用程序的各种值,例如: typedef struct { GtkWidget *window; GtkWidget *view; GtkTextBuffer *buffer; GtkTextMark *cursor; gint line; gint co

我正试图使用
'mark\u set'
信号更新我的GtkTextBuffer中的
行:列
值。对于测试,我有一个简单的设置,在一个滚动窗口内的一个窗口内设置textview,例如:

我使用一个结构来保存应用程序的各种值,例如:

typedef struct {
    GtkWidget *window;
    GtkWidget *view;
    GtkTextBuffer *buffer;
    GtkTextMark *cursor;
    gint line;
    gint col;
    gint winwidth;
    gint winheight;
} context;
我正在尝试更新应用程序用于跟踪缓冲区中的行和列位置的struct实例中的当前
line
col
值。在create_window函数中,我初始化
context*app的值
(在
main()
中定义)并将
'mark\u set'
信号连接到\u mark\u set()回调上的
,将结构实例作为数据传递给回调。e、 g:

g_signal_connect (app->buffer, "mark_set",
                  G_CALLBACK (on_mark_set), app);
_mark_set()
回调上的
是:

app->line
app->col
的值在向缓冲区提供输入的每个按键后正确设置(仅一次)。e、 g.在文本视图中输入
'abc'
,结果如下:

$ ./bin/text_mcve
 line:   1 col: 2
 line:   1 col: 3
 line:   1 col: 4
但是,当我使用
箭头键
移动输入光标或使用
鼠标
重新定位输入光标时,会触发三重或四重回调。e、 g.按左箭头备份一个位置,结果如下:

line:   1 col: 3
line:   1 col: 3
line:   1 col: 3
通过单击鼠标重新定位到末端,将导致回调的四次触发:

line:   1 col: 4
line:   1 col: 4
line:   1 col: 4
line:   1 col: 4
无论是否输入数据或是否使用箭头键或鼠标移动光标,如何将\u mark\u set()
回调的执行限制为单个调用?考虑到
“mark\u set”
是唯一可以覆盖输入的信号->光标位置处理,无论定位输入是来自按键还是鼠标单击。我们的目标是利用
'mark_set'
信号来处理所有
行:列
更新,但我必须找到一种方法来防止每次按键或鼠标单击事件触发回调不止一次

在textview小部件中使用
'key\u press\u event'
时,您可以传递create a gboolean回调和GdkEventKey,并手动处理
事件->keyval
以使用键盘(包括箭头键)处理光标重新定位,通过
return
告诉默认的输入处理程序,不需要对任何给定的按键执行进一步的操作,但这不需要,也不能使用鼠标单击。因此,如果我能通过
'mark\u set'
信号完成这一切,那将是我的选择

有什么方法可以对
'mark\u set'
事件执行相同的操作,以确保
on\u mark\u set()
回调只执行一次,而不考虑按键或鼠标单击?我已发布到gtk应用程序开发列表,但尚未收到回复。S.O.在gtk主题上可能比gtk列表本身更活跃。如果您能帮助解决这个难题,我们将不胜感激

用于测试的MCVE

下文提供了用于测试目的的MCVE。使用
gcc-oprogname progname.c$(pkg config--cflags--libs gtk+-2.0)编译

知道当前的
line:col
值后,就可以与新的
line:col
值进行比较,新的
line:col
值将基于
GtkTextIter*iter
值作为参数传递给
on_mark_set()
回调。这提供了当前值与新值的简单比较,允许您仅响应导致
行:col
值发生变化的
标记集
信号:

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 (mark) {}
}
注意:调试
g_print
语句留在上面,为下面的输出提供上下文)进一步注意,
mark
gtk_text_buffer_get_insert(buffer)
之间的比较无法进行,因为
gtk_text_buffer_get_insert(buffer)
仅在进行箭头键或鼠标单击输入时返回匹配值。(对于正常文本输入,比较失败)

现在,当重复原始问题中记录的相同事件序列时(例如,输入
'abc'
,然后用左箭头备份1,然后在末尾左键单击以重新定位光标),显示标记集上的
现在只正确响应更新的
行:col
值:

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 (mark) {}
}
输出

$ ./bin/text_mcve
 line:   1 col: 2
 line:   1 col: 3
 line:   1 col: 4
 line:   1 col: 3
 line:   1 col: 4
Exiting... buffer contained:
abc

每个
iter
位置都有多个标记,没有特定顺序

为了让这些问题成为一个好的参考,如果不是一个好的解决方案,我将在本主题中包含进一步调试的结果。在其他任何地方都找不到关于这个特定主题的信息

进一步的调试说明了为什么在这方面存在困难,以及为什么不能单独使用
标记
gtk_文本_缓冲区_获取_插入(缓冲区)
之间的简单比较来确定是否响应
“标记_设置”
信号。为什么?

每次生成
“标记设置”
信号时,在任何给定的
iter
位置都可能有多个
标记。在正常输入的情况下(例如,
'a'
'b'
等),传递到
上的
标记
不一定是
“insert”
标记,但显然是iter位置上存在的最后一个标记。(在匿名标记下方的每种情况下)任何给定
iter
位置的标记列表可通过
gtk\u text\u iter\u get\u marks(iter)
返回的
GSList
标记找到。(注意:返回列表中的标记没有特定顺序——这可能是整个问题的基础。请参阅:)例如,您可以使用以下调试代码检查标记:<
GtkTextIter iterfirst;
...
app->cursor = gtk_text_buffer_get_insert (app->buffer);
gtk_text_buffer_get_iter_at_mark (app->buffer, &iterfirst, app->cursor);
app->line = gtk_text_iter_get_line (&iterfirst);
app->col = gtk_text_iter_get_line_offset (&iterfirst);
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 (mark) {}
}
$ ./bin/text_mcve
 line:   1 col: 2
 line:   1 col: 3
 line:   1 col: 4
 line:   1 col: 3
 line:   1 col: 4
Exiting... buffer contained:
abc
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) {}
}
$ ./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
  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)
  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)
void user_function (GtkTextBuffer *textbuffer, GtkTextIter *location,
                    GtkTextMark *mark, gpointer user_data)