mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-10-28 14:15:38 +00:00
Fix replies targeting the wrong message
This commit is contained in:
14 files changed
+291
-54
No files matched your search
@@ -23,6 +23,8 @@
|
||||
#ifndef ZOITECHAT_FE_H
|
||||
#define ZOITECHAT_FE_H
|
||||
|
||||
struct reply_item;
|
||||
|
||||
/* for storage of /menu entries */
|
||||
typedef struct
|
||||
{
|
||||
@@ -110,6 +112,8 @@ void fe_progressbar_start (struct session *sess);
|
||||
void fe_progressbar_end (struct server *serv);
|
||||
void fe_print_text (struct session *sess, char *text, time_t stamp,
|
||||
gboolean no_activity);
|
||||
void fe_print_text_with_message (struct session *sess, char *text, time_t stamp,
|
||||
gboolean no_activity, const struct reply_item *message);
|
||||
void fe_userlist_insert (struct session *sess, struct User *newuser, gboolean sel);
|
||||
int fe_userlist_remove (struct session *sess, struct User *user);
|
||||
void fe_userlist_rehash (struct session *sess, struct User *user);
|
||||
|
||||
+27
-22
@@ -313,6 +313,20 @@ inbound_make_idtext (server *serv, char *idtext, int max, int id)
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass message identity alongside the formatted event, outside plugin output.
|
||||
* Private messages displayed in another channel have no reply destination here. */
|
||||
static void
|
||||
inbound_emit_message (int event, session *sess, char *from, char *text,
|
||||
char *c, char *d, const message_tags_data *tags_data)
|
||||
{
|
||||
reply_item message = {tags_data->msgid, from, text, tags_data->timestamp};
|
||||
gboolean can_reply = reply_msgid_valid (message.msgid) &&
|
||||
event != XP_TE_PRIVMSG && event != XP_TE_PRIVACTION;
|
||||
|
||||
text_emit_with_message (event, sess, from, text, c, d, tags_data->timestamp,
|
||||
can_reply ? &message : NULL);
|
||||
}
|
||||
|
||||
void
|
||||
inbound_privmsg (server *serv, char *from, char *ip, char *text, int id,
|
||||
const message_tags_data *tags_data)
|
||||
@@ -362,11 +376,9 @@ inbound_privmsg (server *serv, char *from, char *ip, char *text, int id,
|
||||
inbound_make_idtext (serv, idtext, sizeof (idtext), id);
|
||||
|
||||
if (sess->type == SESS_DIALOG && !nodiag)
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
|
||||
tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, tags_data);
|
||||
else
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, 0,
|
||||
tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, tags_data);
|
||||
}
|
||||
|
||||
/* used for Alerts section. Masks can be separated by commas and spaces. */
|
||||
@@ -608,24 +620,21 @@ inbound_action (session *sess, char *chan, char *from, char *ip, char *text,
|
||||
{
|
||||
if (is_hilight (from, text, sess, serv))
|
||||
{
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANACTION, sess, from, text, nickchar,
|
||||
idtext, 0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_HCHANACTION, sess, from, text, nickchar,
|
||||
idtext, tags_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (fromme)
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_UACTION, sess, from, text, nickchar, idtext,
|
||||
0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_UACTION, sess, from, text, nickchar, idtext, tags_data);
|
||||
else if (!privaction)
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANACTION, sess, from, text, nickchar,
|
||||
idtext, 0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_CHANACTION, sess, from, text, nickchar,
|
||||
idtext, tags_data);
|
||||
else if (sess->type == SESS_DIALOG)
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL,
|
||||
0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL, tags_data);
|
||||
else
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, 0,
|
||||
tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, tags_data);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -679,8 +688,7 @@ inbound_chanmsg (server *serv, session *sess, char *chan, char *from,
|
||||
(!tags_data->timestamp || serv->have_echo_message))
|
||||
sess->server->p_set_back (sess->server);
|
||||
reply_context_print (sess, tags_data);
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL,
|
||||
0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL, tags_data);
|
||||
reply_cache_add (sess, tags_data->msgid, from, text, tags_data->timestamp);
|
||||
return;
|
||||
}
|
||||
@@ -693,14 +701,11 @@ inbound_chanmsg (server *serv, session *sess, char *chan, char *from,
|
||||
reply_context_print (sess, tags_data);
|
||||
|
||||
if (sess->type == SESS_DIALOG)
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
|
||||
tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, tags_data);
|
||||
else if (hilight)
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext,
|
||||
0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext, tags_data);
|
||||
else
|
||||
EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMSG, sess, from, text, nickchar, idtext,
|
||||
0, tags_data->timestamp);
|
||||
inbound_emit_message (XP_TE_CHANMSG, sess, from, text, nickchar, idtext, tags_data);
|
||||
|
||||
reply_cache_add (sess, tags_data->msgid, from, text, tags_data->timestamp);
|
||||
}
|
||||
|
||||
+20
-6
@@ -834,8 +834,9 @@ G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
PrintTextTimeStamp (session *sess, char *text, time_t timestamp)
|
||||
static void
|
||||
PrintTextTimeStampWithMessage (session *sess, char *text, time_t timestamp,
|
||||
const reply_item *message)
|
||||
{
|
||||
if (!sess)
|
||||
{
|
||||
@@ -856,10 +857,16 @@ PrintTextTimeStamp (session *sess, char *text, time_t timestamp)
|
||||
|
||||
log_write (sess, text, timestamp);
|
||||
scrollback_save (sess, text, timestamp);
|
||||
fe_print_text (sess, text, timestamp, FALSE);
|
||||
fe_print_text_with_message (sess, text, timestamp, FALSE, message);
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
void
|
||||
PrintTextTimeStamp (session *sess, char *text, time_t timestamp)
|
||||
{
|
||||
PrintTextTimeStampWithMessage (sess, text, timestamp, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
PrintText (session *sess, char *text)
|
||||
{
|
||||
@@ -1962,7 +1969,7 @@ display_event_string (session *sess, int event, char **args, char *format,
|
||||
|
||||
static void
|
||||
display_event (session *sess, int event, char **args,
|
||||
unsigned int stripcolor_args, time_t timestamp)
|
||||
unsigned int stripcolor_args, time_t timestamp, const reply_item *message)
|
||||
{
|
||||
char o[4096];
|
||||
char *format;
|
||||
@@ -1992,7 +1999,7 @@ display_event (session *sess, int event, char **args,
|
||||
}
|
||||
|
||||
if (o[0])
|
||||
PrintTextTimeStamp (sess, o, timestamp);
|
||||
PrintTextTimeStampWithMessage (sess, o, timestamp, message);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -2210,6 +2217,13 @@ text_color_of (char *name)
|
||||
void
|
||||
text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
|
||||
time_t timestamp)
|
||||
{
|
||||
text_emit_with_message (index, sess, a, b, c, d, timestamp, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
text_emit_with_message (int index, session *sess, char *a, char *b, char *c, char *d,
|
||||
time_t timestamp, const reply_item *message)
|
||||
{
|
||||
char *word[PDIWORDS];
|
||||
int i;
|
||||
@@ -2305,7 +2319,7 @@ text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
|
||||
|
||||
if (!prefs.hex_away_omit_alerts || !sess->server->is_away)
|
||||
sound_play_event (index);
|
||||
display_event (sess, index, word, stripcolor_args, timestamp);
|
||||
display_event (sess, index, word, stripcolor_args, timestamp, message);
|
||||
}
|
||||
|
||||
char *
|
||||
|
||||
@@ -56,6 +56,8 @@ int text_color_of (char *name);
|
||||
char **text_color_event_names (int color, int *count);
|
||||
void text_emit (int index, session *sess, char *a, char *b, char *c, char *d,
|
||||
time_t timestamp);
|
||||
void text_emit_with_message (int index, session *sess, char *a, char *b, char *c, char *d,
|
||||
time_t timestamp, const reply_item *message);
|
||||
int text_emit_by_name (char *name, session *sess, time_t timestamp,
|
||||
char *a, char *b, char *c, char *d);
|
||||
gchar *text_convert_invalid (const gchar* text, gssize len, GIConv converter, const gchar *fallback, gsize *len_out);
|
||||
|
||||
+9
-2
@@ -994,13 +994,20 @@ fe_progressbar_end (server *serv)
|
||||
void
|
||||
fe_print_text (struct session *sess, char *text, time_t stamp,
|
||||
gboolean no_activity)
|
||||
{
|
||||
fe_print_text_with_message (sess, text, stamp, no_activity, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
fe_print_text_with_message (struct session *sess, char *text, time_t stamp,
|
||||
gboolean no_activity, const reply_item *message)
|
||||
{
|
||||
/* image links become a "[view image]" placeholder with a hidden URL */
|
||||
char *filtered = inline_image_filter_text (text);
|
||||
|
||||
PrintTextRaw (sess->res->buffer,
|
||||
PrintTextRawWithMessage (sess->res->buffer,
|
||||
(unsigned char *)(filtered ? filtered : text),
|
||||
prefs.hex_text_indent, stamp);
|
||||
prefs.hex_text_indent, stamp, message);
|
||||
g_free (filtered);
|
||||
|
||||
if (no_activity || !sess->gui->is_tab)
|
||||
|
||||
+23
-4
@@ -3865,10 +3865,25 @@ mg_update_topicbar_layout (session_gui *gui)
|
||||
|
||||
/* check if a word is clickable */
|
||||
|
||||
static session *
|
||||
mg_xtext_session (GtkWidget *widget)
|
||||
{
|
||||
GSList *list;
|
||||
|
||||
for (list = sess_list; list; list = list->next)
|
||||
{
|
||||
session *sess = list->data;
|
||||
|
||||
if (sess->res && sess->res->buffer == GTK_XTEXT (widget)->buffer)
|
||||
return sess;
|
||||
}
|
||||
return current_sess;
|
||||
}
|
||||
|
||||
static int
|
||||
mg_word_check (GtkWidget * xtext, char *word)
|
||||
{
|
||||
session *sess = current_sess;
|
||||
session *sess = mg_xtext_session (xtext);
|
||||
int ret;
|
||||
|
||||
ret = url_check_word (word);
|
||||
@@ -3883,7 +3898,7 @@ mg_word_check (GtkWidget * xtext, char *word)
|
||||
static void
|
||||
mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
|
||||
{
|
||||
session *sess = current_sess;
|
||||
session *sess = mg_xtext_session (xtext);
|
||||
int word_type = 0, start, end;
|
||||
char *tmp;
|
||||
|
||||
@@ -3963,7 +3978,9 @@ mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
|
||||
case WORD_NICK:
|
||||
word[end] = 0;
|
||||
word += start;
|
||||
menu_nickmenu (sess, even, word, FALSE);
|
||||
menu_nickmenu_with_message (sess, even, word, FALSE,
|
||||
gtk_xtext_entry_get_message (GTK_XTEXT (xtext),
|
||||
gtk_xtext_get_clicked_entry (GTK_XTEXT (xtext))));
|
||||
break;
|
||||
case WORD_CHANNEL:
|
||||
word[end] = 0;
|
||||
@@ -3978,7 +3995,9 @@ mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
|
||||
g_free (tmp);
|
||||
break;
|
||||
case WORD_DIALOG:
|
||||
menu_nickmenu (sess, even, sess->channel, FALSE);
|
||||
menu_nickmenu_with_message (sess, even, sess->channel, FALSE,
|
||||
gtk_xtext_entry_get_message (GTK_XTEXT (xtext),
|
||||
gtk_xtext_get_clicked_entry (GTK_XTEXT (xtext))));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+76
-12
@@ -790,26 +790,73 @@ fe_userlist_update (session *sess, struct User *user)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
menu_reply_to_latest_cb (GtkWidget *wid, gpointer data)
|
||||
typedef struct
|
||||
{
|
||||
reply_item *item;
|
||||
session *sess;
|
||||
char *target;
|
||||
char *msgid;
|
||||
char *nick;
|
||||
char *text;
|
||||
gboolean latest;
|
||||
} reply_menu_data;
|
||||
|
||||
item = reply_cache_latest_from (current_sess, str_copy);
|
||||
if (!item)
|
||||
static void
|
||||
menu_reply_data_free (gpointer data)
|
||||
{
|
||||
reply_menu_data *reply = data;
|
||||
g_free (reply->target);
|
||||
g_free (reply->msgid);
|
||||
g_free (reply->nick);
|
||||
g_free (reply->text);
|
||||
g_free (reply);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_reply_cb (GtkWidget *wid, gpointer data)
|
||||
{
|
||||
reply_menu_data *reply = data;
|
||||
session *sess = reply->sess;
|
||||
reply_item selected = {reply->msgid, reply->nick, reply->text, 0};
|
||||
const reply_item *item = &selected;
|
||||
GtkWidget *menu = g_object_ref (gtk_widget_get_parent (wid));
|
||||
|
||||
/* The menu may outlive a closed or retargeted tab. Never reply in whatever
|
||||
* session happens to be current when the menu item is activated. */
|
||||
if (!is_session (sess) ||
|
||||
sess->server->p_cmp (sess->channel, reply->target))
|
||||
goto done;
|
||||
if (sess != current_sess)
|
||||
{
|
||||
PrintText (current_sess, _("No recent message to reply to.\n"));
|
||||
return;
|
||||
fe_ctrl_gui (sess, FE_GUI_FOCUS, 0);
|
||||
if (!is_session (sess) || sess != current_sess ||
|
||||
sess->server->p_cmp (sess->channel, reply->target))
|
||||
goto done;
|
||||
}
|
||||
if (reply->latest)
|
||||
item = reply_cache_latest_from (sess, reply->nick);
|
||||
if (!item || !item->msgid)
|
||||
{
|
||||
PrintText (sess, _("No recent message to reply to.\n"));
|
||||
goto done;
|
||||
}
|
||||
|
||||
reply_state_set (current_sess, item->msgid, current_sess->channel, item->nick, item->text);
|
||||
mg_reply_update (current_sess);
|
||||
if (current_sess->gui && current_sess->gui->input_box)
|
||||
gtk_widget_grab_focus (current_sess->gui->input_box);
|
||||
reply_state_set (sess, item->msgid, reply->target, item->nick, item->text);
|
||||
mg_reply_update (sess);
|
||||
if (sess->gui && sess->gui->input_box)
|
||||
gtk_widget_grab_focus (sess->gui->input_box);
|
||||
done:
|
||||
g_object_unref (menu);
|
||||
}
|
||||
|
||||
void
|
||||
menu_nickmenu (session *sess, GdkEventButton *event, char *nick, int num_sel)
|
||||
{
|
||||
menu_nickmenu_with_message (sess, event, nick, num_sel, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
menu_nickmenu_with_message (session *sess, GdkEventButton *event, char *nick,
|
||||
int num_sel, const reply_item *message)
|
||||
{
|
||||
char buf[512];
|
||||
struct User *user;
|
||||
@@ -853,7 +900,24 @@ menu_nickmenu (session *sess, GdkEventButton *event, char *nick, int num_sel)
|
||||
|
||||
if (num_sel <= 1)
|
||||
{
|
||||
menu_quick_item_with_callback (menu_reply_to_latest_cb, _("Reply"), menu, 0);
|
||||
/* A transcript click must identify that exact message. A user-list
|
||||
* click has no message row and retains the reply-to-latest behaviour.
|
||||
* Copy row data now: messages may arrive or be trimmed while open. */
|
||||
reply_menu_data *reply = g_new0 (reply_menu_data, 1);
|
||||
|
||||
reply->sess = sess;
|
||||
reply->target = g_strdup (sess->channel);
|
||||
reply->latest = num_sel != 0;
|
||||
if (reply->latest)
|
||||
reply->nick = g_strdup (nick);
|
||||
else if (message)
|
||||
{
|
||||
reply->msgid = g_strdup (message->msgid);
|
||||
reply->nick = g_strdup (message->nick);
|
||||
reply->text = g_strdup (message->text);
|
||||
}
|
||||
g_object_set_data_full (G_OBJECT (menu), "reply-message", reply, menu_reply_data_free);
|
||||
menu_quick_item_with_callback (menu_reply_cb, _("Reply"), menu, reply);
|
||||
menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ void menu_chanmenu (session *sess, GdkEventButton * event, char *chan);
|
||||
void menu_addfavoritemenu (server *serv, GtkWidget *menu, char *channel, gboolean istree);
|
||||
void menu_addconnectmenu (server *serv, GtkWidget *menu);
|
||||
void menu_nickmenu (session *sess, GdkEventButton * event, char *nick, int num_sel);
|
||||
void menu_nickmenu_with_message (session *sess, GdkEventButton *event, char *nick,
|
||||
int num_sel, const reply_item *message);
|
||||
void menu_middlemenu (session *sess, GdkEventButton *event);
|
||||
void userlist_button_cb (GtkWidget * button, char *cmd);
|
||||
void nick_command_parse (session *sess, char *cmd, char *nick, char *allnick);
|
||||
|
||||
@@ -137,6 +137,49 @@ static void test_null_empty_prefix (Fixture *f, gconstpointer data)
|
||||
cairo_surface_destroy (snapshot (f, FALSE));
|
||||
}
|
||||
|
||||
static void test_message_identity (Fixture *f, gconstpointer data)
|
||||
{
|
||||
xtext_buffer *buf = f->xtext->buffer;
|
||||
xtext_buffer *other;
|
||||
textentry *first, *second;
|
||||
char msgid[] = "message-one";
|
||||
char nick[] = "same-nick";
|
||||
char text[] = "identical text";
|
||||
reply_item message = {msgid, nick, text, 1234};
|
||||
const reply_item *saved;
|
||||
|
||||
/* ZNC playback can repeat the same sender, text and server timestamp. */
|
||||
gtk_xtext_append_wrap_with_message (buf, (guchar *)text, -1, 1234, 0, &message);
|
||||
first = buf->text_last;
|
||||
message.msgid = "message-two";
|
||||
gtk_xtext_append_indent_with_message (buf, (guchar *)nick, -1,
|
||||
(guchar *)text, -1, 1234, &message);
|
||||
second = buf->text_last;
|
||||
msgid[0] = nick[0] = text[0] = 'X';
|
||||
saved = gtk_xtext_entry_get_message (f->xtext, first);
|
||||
g_assert_cmpstr (saved->msgid, ==, "message-one");
|
||||
g_assert_cmpstr (saved->nick, ==, "same-nick");
|
||||
g_assert_cmpstr (saved->text, ==, "identical text");
|
||||
g_assert_cmpint (saved->timestamp, ==, 1234);
|
||||
g_assert_cmpstr (gtk_xtext_entry_get_message (f->xtext, second)->msgid, ==, "message-two");
|
||||
|
||||
/* Other output must not inherit a message ID, including after reflow. */
|
||||
append (f, "a plugin or status message");
|
||||
g_assert_null (gtk_xtext_entry_get_message (f->xtext, buf->text_last));
|
||||
g_assert_true (gtk_xtext_set_font (f->xtext, "monospace 18"));
|
||||
g_assert_cmpstr (gtk_xtext_entry_get_message (f->xtext, first)->msgid, ==, "message-one");
|
||||
g_assert_null (gtk_xtext_entry_get_message (f->xtext, NULL));
|
||||
|
||||
/* Entries from another tab, and removed entries, are never dereferenced. */
|
||||
other = gtk_xtext_buffer_new (f->xtext);
|
||||
gtk_xtext_buffer_show (f->xtext, other, TRUE);
|
||||
g_assert_null (gtk_xtext_entry_get_message (f->xtext, first));
|
||||
gtk_xtext_buffer_show (f->xtext, buf, TRUE);
|
||||
gtk_xtext_buffer_free (other);
|
||||
gtk_xtext_clear (buf, 0);
|
||||
g_assert_null (gtk_xtext_entry_get_message (f->xtext, first));
|
||||
}
|
||||
|
||||
static void test_wrapping (Fixture *f, gconstpointer data)
|
||||
{
|
||||
xtext_buffer *buf = f->xtext->buffer;
|
||||
@@ -1138,6 +1181,7 @@ int main (int argc, char **argv)
|
||||
ADD ("append/empty-bounded", test_empty_and_bounded_append);
|
||||
ADD ("append/utf8-limits", test_utf8_limits);
|
||||
ADD ("append/null-empty-prefix", test_null_empty_prefix);
|
||||
ADD ("reply/message-identity", test_message_identity);
|
||||
ADD ("layout/wrapping", test_wrapping);
|
||||
ADD ("layout/font-isolation", test_font_isolation);
|
||||
ADD ("layout/wide-metrics", test_wide_metrics);
|
||||
|
||||
+17
-8
@@ -69,7 +69,8 @@ xtext_get_stamp_str (time_t tim, char **ret)
|
||||
}
|
||||
|
||||
static void
|
||||
PrintTextLine (xtext_buffer *xtbuf, unsigned char *text, int len, int indent, time_t timet)
|
||||
PrintTextLine (xtext_buffer *xtbuf, unsigned char *text, int len, int indent, time_t timet,
|
||||
const reply_item *message)
|
||||
{
|
||||
unsigned char *tab, *new_text;
|
||||
int leftlen;
|
||||
@@ -92,10 +93,11 @@ PrintTextLine (xtext_buffer *xtbuf, unsigned char *text, int len, int indent, ti
|
||||
memcpy (new_text, stamp, stamp_size);
|
||||
g_free (stamp);
|
||||
memcpy (new_text + stamp_size, text, len);
|
||||
gtk_xtext_append_wrap (xtbuf, new_text, len + stamp_size, timet, stamp_size);
|
||||
gtk_xtext_append_wrap_with_message (xtbuf, new_text, len + stamp_size,
|
||||
timet, stamp_size, message);
|
||||
g_free (new_text);
|
||||
} else
|
||||
gtk_xtext_append (xtbuf, text, len, timet);
|
||||
gtk_xtext_append_wrap_with_message (xtbuf, text, len, timet, 0, message);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -103,14 +105,21 @@ PrintTextLine (xtext_buffer *xtbuf, unsigned char *text, int len, int indent, ti
|
||||
if (tab && tab < (text + len))
|
||||
{
|
||||
leftlen = tab - text;
|
||||
gtk_xtext_append_indent (xtbuf,
|
||||
text, leftlen, tab + 1, len - (leftlen + 1), timet);
|
||||
gtk_xtext_append_indent_with_message (xtbuf, text, leftlen,
|
||||
tab + 1, len - (leftlen + 1), timet, message);
|
||||
} else
|
||||
gtk_xtext_append_indent (xtbuf, 0, 0, text, len, timet);
|
||||
gtk_xtext_append_indent_with_message (xtbuf, 0, 0, text, len, timet, message);
|
||||
}
|
||||
|
||||
void
|
||||
PrintTextRaw (void *xtbuf, unsigned char *text, int indent, time_t stamp)
|
||||
{
|
||||
PrintTextRawWithMessage (xtbuf, text, indent, stamp, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
PrintTextRawWithMessage (void *xtbuf, unsigned char *text, int indent, time_t stamp,
|
||||
const reply_item *message)
|
||||
{
|
||||
char *last_text = text;
|
||||
int len = 0;
|
||||
@@ -122,10 +131,10 @@ PrintTextRaw (void *xtbuf, unsigned char *text, int indent, time_t stamp)
|
||||
switch (*text)
|
||||
{
|
||||
case 0:
|
||||
PrintTextLine (xtbuf, last_text, len, indent, stamp);
|
||||
PrintTextLine (xtbuf, last_text, len, indent, stamp, message);
|
||||
return;
|
||||
case '\n':
|
||||
PrintTextLine (xtbuf, last_text, len, indent, stamp);
|
||||
PrintTextLine (xtbuf, last_text, len, indent, stamp, message);
|
||||
text++;
|
||||
if (*text == 0)
|
||||
return;
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
#ifndef ZOITECHAT_TEXTGUI_H
|
||||
#define ZOITECHAT_TEXTGUI_H
|
||||
|
||||
struct reply_item;
|
||||
|
||||
void PrintTextRaw (void *xtbuf, unsigned char *text, int indent, time_t stamp);
|
||||
void PrintTextRawWithMessage (void *xtbuf, unsigned char *text, int indent, time_t stamp,
|
||||
const struct reply_item *message);
|
||||
void pevent_dialog_show (void);
|
||||
|
||||
#endif
|
||||
@@ -90,6 +90,7 @@ struct textentry
|
||||
GdkPixbuf *image; /* inline image shown below the text, or NULL */
|
||||
GdkPixbuf *image_full; /* larger version for the image viewer */
|
||||
char *image_url; /* the URL the image was loaded from */
|
||||
reply_item *message; /* original IRC message, independent of the reply cache */
|
||||
};
|
||||
|
||||
/* inline image display */
|
||||
@@ -287,6 +288,7 @@ static char *gtk_xtext_selection_get_text (GtkXText *xtext, int *len_ret);
|
||||
static void gtk_xtext_clipboard_get (GtkClipboard *, GtkSelectionData *, guint, gpointer);
|
||||
static void gtk_xtext_clipboard_clear (GtkClipboard *, gpointer);
|
||||
static textentry *gtk_xtext_nth (GtkXText *xtext, int line, int *subline);
|
||||
static void gtk_xtext_entry_set_message (textentry *ent, const reply_item *message);
|
||||
static void gtk_xtext_adjustment_changed (GtkAdjustment * adj,
|
||||
GtkXText * xtext);
|
||||
static void gtk_xtext_scroll_adjustments (GtkXText *xtext, GtkAdjustment *hadj,
|
||||
@@ -362,6 +364,13 @@ gtk_xtext_entry_free (GtkXText *xtext, textentry *ent)
|
||||
g_clear_pointer (&ent->sublines, g_array_unref);
|
||||
g_list_free (ent->marks);
|
||||
gtk_xtext_ent_free_image (ent);
|
||||
if (ent->message)
|
||||
{
|
||||
g_free (ent->message->msgid);
|
||||
g_free (ent->message->nick);
|
||||
g_free (ent->message->text);
|
||||
g_free (ent->message);
|
||||
}
|
||||
g_free (ent);
|
||||
}
|
||||
|
||||
@@ -5413,6 +5422,15 @@ gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
unsigned char *left_text, int left_len,
|
||||
unsigned char *right_text, int right_len,
|
||||
time_t stamp)
|
||||
{
|
||||
gtk_xtext_append_indent_with_message (buf, left_text, left_len,
|
||||
right_text, right_len, stamp, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_xtext_append_indent_with_message (xtext_buffer *buf,
|
||||
unsigned char *left_text, int left_len, unsigned char *right_text, int right_len,
|
||||
time_t stamp, const reply_item *message)
|
||||
{
|
||||
textentry *ent;
|
||||
unsigned char *str;
|
||||
@@ -5489,6 +5507,7 @@ gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
}
|
||||
}
|
||||
|
||||
gtk_xtext_entry_set_message (ent, message);
|
||||
gtk_xtext_append_entry (buf, ent, stamp);
|
||||
}
|
||||
|
||||
@@ -5502,6 +5521,13 @@ gtk_xtext_append (xtext_buffer *buf, unsigned char *text, int len, time_t stamp)
|
||||
|
||||
void
|
||||
gtk_xtext_append_wrap (xtext_buffer *buf, unsigned char *text, int len, time_t stamp, int stamp_len)
|
||||
{
|
||||
gtk_xtext_append_wrap_with_message (buf, text, len, stamp, stamp_len, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_xtext_append_wrap_with_message (xtext_buffer *buf, unsigned char *text,
|
||||
int len, time_t stamp, int stamp_len, const reply_item *message)
|
||||
{
|
||||
textentry *ent;
|
||||
|
||||
@@ -5521,6 +5547,7 @@ gtk_xtext_append_wrap (xtext_buffer *buf, unsigned char *text, int len, time_t s
|
||||
ent->left_len = -1;
|
||||
ent->stamp_len = (stamp_len > 0 && stamp_len < ent->str_len) ? stamp_len : 0;
|
||||
|
||||
gtk_xtext_entry_set_message (ent, message);
|
||||
gtk_xtext_append_entry (buf, ent, stamp);
|
||||
}
|
||||
|
||||
@@ -5854,6 +5881,28 @@ gtk_xtext_buffer_free (xtext_buffer *buf)
|
||||
/* ========== INLINE IMAGE API ============= */
|
||||
/* ========================================= */
|
||||
|
||||
/* Each rendered line owns its message identity, including continuation lines.
|
||||
* Do not infer identity from nick, text or timestamp: ZNC playback and repeated
|
||||
* messages can have identical values for all three. */
|
||||
static void
|
||||
gtk_xtext_entry_set_message (textentry *ent, const reply_item *message)
|
||||
{
|
||||
if (!message || !message->msgid || !*message->msgid)
|
||||
return;
|
||||
|
||||
ent->message = g_new0 (reply_item, 1);
|
||||
ent->message->msgid = g_strdup (message->msgid);
|
||||
ent->message->nick = g_strdup (message->nick);
|
||||
ent->message->text = g_strdup (message->text);
|
||||
ent->message->timestamp = message->timestamp;
|
||||
}
|
||||
|
||||
const reply_item *
|
||||
gtk_xtext_entry_get_message (GtkXText *xtext, textentry *ent)
|
||||
{
|
||||
return gtk_xtext_buffer_contains (xtext, ent) ? ent->message : NULL;
|
||||
}
|
||||
|
||||
/* the entry the last word_click signal was emitted for; only valid while
|
||||
handling that signal */
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
typedef struct _GtkXText GtkXText;
|
||||
typedef struct _GtkXTextClass GtkXTextClass;
|
||||
struct reply_item;
|
||||
|
||||
#define GTK_TYPE_XTEXT (gtk_xtext_get_type ())
|
||||
#define GTK_XTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_XTEXT, GtkXText))
|
||||
@@ -286,6 +287,11 @@ void gtk_xtext_append_indent (xtext_buffer *buf,
|
||||
unsigned char *left_text, int left_len,
|
||||
unsigned char *right_text, int right_len,
|
||||
time_t stamp);
|
||||
void gtk_xtext_append_wrap_with_message (xtext_buffer *buf, unsigned char *text,
|
||||
int len, time_t stamp, int stamp_len, const struct reply_item *message);
|
||||
void gtk_xtext_append_indent_with_message (xtext_buffer *buf,
|
||||
unsigned char *left_text, int left_len, unsigned char *right_text, int right_len,
|
||||
time_t stamp, const struct reply_item *message);
|
||||
int gtk_xtext_set_font (GtkXText *xtext, char *name);
|
||||
void gtk_xtext_set_background (GtkXText * xtext, cairo_surface_t *surface);
|
||||
void gtk_xtext_set_palette (GtkXText * xtext, const XTextColor *palette);
|
||||
@@ -318,6 +324,7 @@ xtext_buffer *gtk_xtext_buffer_new (GtkXText *xtext);
|
||||
void gtk_xtext_buffer_free (xtext_buffer *buf);
|
||||
void gtk_xtext_buffer_show (GtkXText *xtext, xtext_buffer *buf, int render);
|
||||
void gtk_xtext_copy_selection (GtkXText *xtext);
|
||||
const struct reply_item *gtk_xtext_entry_get_message (GtkXText *xtext, textentry *ent);
|
||||
|
||||
/* inline image support. A textentry can have one image attached, which is
|
||||
* rendered below its text. Loading is asynchronous: begin() registers the
|
||||
|
||||
@@ -406,6 +406,13 @@ fe_print_text (struct session *sess, char *text, time_t stamp,
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
fe_print_text_with_message (struct session *sess, char *text, time_t stamp,
|
||||
gboolean no_activity, const reply_item *message)
|
||||
{
|
||||
fe_print_text (sess, text, stamp, no_activity);
|
||||
}
|
||||
|
||||
void
|
||||
fe_timeout_remove (int tag)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user