rendered paste bodyIndex: WORKxchat/src/fe-gtk/xtext.c
===================================================================
--- WORKxchat/src/fe-gtk/xtext.c (revision 1451)
+++ WORKxchat/src/fe-gtk/xtext.c (working copy)
@@ -66,6 +66,11 @@
#include "mmx_cmod.h"
#endif
+#include "../common/xchat.h"
+#include "../common/fe.h"
+#include "../common/util.h"
+#include "../common/xchatc.h"
+#include "fe-gtk.h"
#include "xtext.h"
#define charlen(str) g_utf8_skip[*(guchar *)(str)]
@@ -89,6 +94,20 @@
static GtkWidgetClass *parent_class = NULL;
+/*
+ * offsets_t is used for retaining search information.
+ * It is stored in the 'data' member of a GList,
+ * as chained from ent->marks. It saves starting and
+ * ending+1 offset of a found occurrence.
+ */
+typedef union offsets_u {
+ struct offsets_s {
+ guint16 start;
+ guint16 end;
+ } o;
+ guint32 u;
+} offsets_t;
+
struct textentry
{
struct textentry *next;
@@ -108,6 +127,7 @@
guchar tag;
guchar pad1;
guchar pad2; /* 32-bit align : 44 bytes total */
+ GList *marks; /* List of found strings */
};
enum
@@ -149,6 +169,14 @@
static unsigned char *
gtk_xtext_strip_color (unsigned char *text, int len, unsigned char *outbuf,
int *newlen, int *mb_ret, int strip_hidden);
+/* Forward declarations for all search functions */
+static int gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off);
+static void gtk_xtext_search_textentry (xtext_buffer *, textentry *, int);
+static void gtk_xtext_search_textentry_del (xtext_buffer *, textentry *);
+static void gtk_xtext_search_textentry_fini (gpointer, gpointer);
+static void gtk_xtext_search_fini (xtext_buffer *);
+static gboolean gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gboolean case_match, gboolean backward);
+textentry * gtk_xtext_search (GtkXText *xtext, const gchar *text, textentry *hint, gboolean case_match, gboolean bwd);
/* some utility functions first */
@@ -1120,18 +1148,6 @@
}
}
-static void
-gtk_xtext_selection_clear_full (xtext_buffer *buf)
-{
- textentry *ent = buf->text_first;
- while (ent)
- {
- ent->mark_start = -1;
- ent->mark_end = -1;
- ent = ent->next;
- }
-}
-
static int
gtk_xtext_selection_clear (xtext_buffer *buf)
{
@@ -2882,6 +2898,54 @@
xtext->nc = 0;
}
+/*
+ * gtk_xtext_search_offset (buf, ent, off) --
+ * Look for arg offset in arg textentry
+ * Return one or more flags:
+ * GTK_MATCH_MID if we are in a match
+ * GTK_MATCH_START if we're at the first byte of it
+ * GTK_MATCH_END if we at the first byte past it
+ * GTK_MATCH_CUR if it is the current match
+ */
+#define GTK_MATCH_START 1
+#define GTK_MATCH_MID 2
+#define GTK_MATCH_END 4
+#define GTK_MATCH_CUR 8
+static int
+gtk_xtext_search_offset (xtext_buffer *buf, textentry *ent, unsigned int off)
+{
+ GList *gl;
+ offsets_t o;
+ int flags = 0;
+
+ for (gl = g_list_first (ent->marks); gl; gl = g_list_next (gl))
+ {
+ o.u = GPOINTER_TO_UINT (gl->data);
+ if (off < o.o.start || off > o.o.end)
+ continue;
+ flags = GTK_MATCH_MID;
+ if (off == o.o.start)
+ flags |= GTK_MATCH_START;
+ if (off == o.o.end)
+ {
+ gl = g_list_next (gl);
+ if (gl)
+ {
+ o.u = GPOINTER_TO_UINT (gl->data);
+ if (off == o.o.start) // If subseq match is adjacent
+ flags |= (gl == buf->curmark)? GTK_MATCH_CUR: 0;
+ else // If subseq match is not adjacent
+ flags |= GTK_MATCH_END;
+ } else // If there is no subseq match
+ flags |= GTK_MATCH_END;
+ } else // If not yet at the end of this match
+ if (gl == buf->curmark)
+ flags |= GTK_MATCH_CUR;
+ break;
+ }
+ return flags;
+}
+
/* render a single line, which WONT wrap, and parse mIRC colors */
static int
@@ -2896,6 +2960,7 @@
int offset;
int mark = FALSE;
int ret = 1;
+ int k;
xtext->in_hilight = FALSE;
@@ -2977,6 +3042,36 @@
}
#endif
+ if (left_only == FALSE && // If not the timestamp call, and
+ (ent->mark_start == -1 || // also if not in a selection:
+ ent->mark_start > offset + i ||
+ ent->mark_end < offset + i))
+ {
+ k = gtk_xtext_search_offset (xtext->buffer, ent, offset + i);
+ if (k)
+ {
+ x += gtk_xtext_render_flush (xtext, x, y, pstr, j, gc, ent->mb);
+ pstr += j;
+ j = 0;
+ xtext->underline = (k & GTK_MATCH_CUR)? TRUE: FALSE;
+ if (k & (GTK_MATCH_START | GTK_MATCH_MID))
+ {
+ xtext_set_bg (xtext, gc, XTEXT_MARK_BG);
+ xtext_set_fg (xtext, gc, XTEXT_MARK_FG);
+ xtext->backcolor = TRUE;
+ mark = TRUE;
+ }
+ if (k & GTK_MATCH_END)
+ {
+ xtext_set_bg (xtext, gc, xtext->col_back);
+ xtext_set_fg (xtext, gc, xtext->col_fore);
+ xtext->backcolor = (xtext->col_back != XTEXT_BG)? TRUE: FALSE;
+ xtext->underline = FALSE;
+ mark = FALSE;
+ }
+ }
+ }
+
if ((xtext->parsing_color && isdigit (str[i]) && xtext->nc < 2) ||
(xtext->parsing_color && str[i] == ',' && isdigit (str[i+1]) && xtext->nc < 3 && !xtext->parsing_backcolor))
{
@@ -4746,6 +4841,7 @@
}
}
+
static void
gtk_xtext_kill_ent (xtext_buffer *buffer, textentry *ent)
{
@@ -4766,6 +4862,9 @@
if (buffer->marker_pos == ent) buffer->marker_pos = NULL;
+ if (ent->marks)
+ gtk_xtext_search_textentry_del (buffer, ent);
+
free (ent);
}
@@ -4780,7 +4879,10 @@
if (!ent)
return;
buffer->num_lines -= ent->lines_taken;
- buffer->pagetop_line -= ent->lines_taken;
+ if (ent == buffer->pagetop_ent)
+ buffer->pagetop_ent = NULL;
+ else
+ buffer->pagetop_line -= ent->lines_taken;
buffer->last_pixel_pos -= (ent->lines_taken * buffer->xtext->fontsize);
buffer->text_first = ent->next;
if (buffer->text_first)
@@ -4848,6 +4950,8 @@
else
{
/* delete all */
+ if (buf->search_found)
+ gtk_xtext_search_fini (buf);
if (buf->xtext->auto_indent)
buf->indent = MARGIN;
buf->scrollbar_down = TRUE;
@@ -4907,86 +5011,212 @@
xtext->buffer->marker_seen = TRUE;
}
-textentry *
-gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *start, gboolean case_match, gboolean backward)
+/* Search a single textentry for occurrence(s) of search arg string */
+static void
+gtk_xtext_search_textentry (xtext_buffer *buf, textentry *ent, int pre)
{
- textentry *ent, *fent;
- int line;
- gchar *str, *nee, *hay; /* needle in haystack */
+ gchar *nee, *hay; /* looking for a needle in a haystack */
+ gchar *str; /* text string to be searched */
+ gchar *text; /* what to look for in it */
+ offsets_t marks;
+ gchar *pos;
+ gint off, len, lhay, lnee;
+ GList *gl = NULL;
- gtk_xtext_selection_clear_full (xtext->buffer);
- xtext->buffer->last_ent_start = NULL;
- xtext->buffer->last_ent_end = NULL;
+ str = ent->str;
- /* set up text comparand for Case Match or Ignore */
- if (case_match)
- nee = g_strdup (text);
+ if ((text = buf->search_text) == NULL)
+ return;
+ /* Save some time by preserving the massaged search string */
+ if (buf->search_nee == NULL)
+ {
+ if (buf->search_match)
+ buf->search_nee = g_strdup (text);
+ else
+ buf->search_nee = g_utf8_casefold (text, strlen(text));
+ }
+ nee = buf->search_nee;
+
+ if (buf->search_match)
+ hay = g_strdup (str);
else
- nee = g_utf8_casefold (text, strlen (text));
+ hay = g_utf8_casefold (str, strlen (str));
+ lhay = strlen (hay);
+ lnee = strlen (nee);
+ off = 0;
- /* Validate that start gives a currently valid ent pointer */
- ent = xtext->buffer->text_first;
- while (ent)
+ for (pos = hay, len = lhay; len; pos = hay + off, len = lhay - off)
{
- if (ent == start)
+ str = g_strstr_len (pos, len, nee);
+ if (str == NULL)
break;
- ent = ent->next;
+ off = str - hay;
+ marks.o.start = off;
+ off += lnee;
+ marks.o.end = off;
+ gl = g_list_append (gl, GUINT_TO_POINTER (marks.u));
}
- if (!ent)
- start = NULL;
+ g_free (hay);
+ ent->marks = gl;
+ if (gl)
+ {
+ buf->search_found = (pre? g_list_prepend: g_list_append) (buf->search_found, ent);
+ if (pre == FALSE && buf->hintsearch == NULL)
+ buf->hintsearch = ent;
+ }
+ return;
+}
- /* Choose first ent to look at */
- if (start)
- ent = backward? start->prev: start->next;
+/* Free all search information for a textentry */
+static void
+gtk_xtext_search_textentry_del (xtext_buffer *buf, textentry *ent)
+{
+ g_list_free (ent->marks);
+ ent->marks = NULL;
+ if (buf->cursearch && buf->cursearch->data == ent)
+ {
+ buf->cursearch = NULL;
+ buf->curmark = NULL;
+ }
+ if (buf->pagetop_ent == ent)
+ buf->pagetop_ent = NULL;
+ buf->search_found = g_list_remove (buf->search_found, ent);
+}
+
+/* Used only by glist_foreach */
+static void
+gtk_xtext_search_textentry_fini (gpointer entp, gpointer bufp)
+{
+ textentry *ent = entp;
+
+ g_list_free (ent->marks);
+ ent->marks = NULL;
+}
+
+/* Free all search information for all textentrys and the xtext_buffer */
+static void
+gtk_xtext_search_fini (xtext_buffer *buf)
+{
+ g_list_foreach (buf->search_found, gtk_xtext_search_textentry_fini, 0);
+ g_list_free (buf->search_found);
+ buf->search_found = NULL;
+ g_free (buf->search_text);
+ buf->search_text = NULL;
+ g_free (buf->search_nee);
+ buf->search_nee = NULL;
+ buf->search_match = 0;
+ buf->cursearch = NULL;
+ buf->curmark = NULL;
+}
+
+/* Returns TRUE if the base search information exists and is still okay to use */
+static gboolean
+gtk_xtext_search_init (xtext_buffer *buf, const gchar *text, gboolean case_match, gboolean backward)
+{
+ if (buf->search_found &&
+ strcmp (buf->search_text, text) == 0 &&
+ buf->search_match == case_match)
+ return TRUE;
+ if (buf->cursearch && buf->cursearch->data)
+ buf->hintsearch = buf->cursearch->data;
else
- ent = backward? xtext->buffer->text_last: xtext->buffer->text_first;
+ buf->hintsearch = NULL;
+ gtk_xtext_search_fini (buf);
+ buf->search_text = g_strdup (text);
+ buf->search_match = case_match;
+ buf->search_bwd = backward;
+ buf->cursearch = NULL;
+ buf->curmark = NULL;
+ return FALSE;
+}
- /* Search from there to one end or the other until found */
- while (ent)
+textentry *
+gtk_xtext_search (GtkXText * xtext, const gchar *text, textentry *hint, gboolean case_match, gboolean bwd)
+{
+ textentry *ent = NULL, *fent;
+ xtext_buffer *buf = xtext->buffer;
+
+ if (text == NULL || text[0] == 0) // Let a null string reset the search results.
{
- /* If Case Ignore, fold before & free after calling strstr */
- if (case_match)
- hay = g_strdup (ent->str);
- else
- hay = g_utf8_casefold (ent->str, strlen (ent->str));
- /* Try to find the needle in this haystack */
- str = g_strstr_len (hay, strlen (hay), nee);
- g_free (hay);
- if (str)
- break;
- ent = backward? ent->prev: ent->next;
+ gtk_xtext_search_fini (buf);
+ goto checkvisible;
}
- fent = ent;
+ if (gtk_xtext_search_init (buf, text, case_match, bwd) == FALSE) // If a new search:
+ {
+ for (ent = buf->text_first; ent; ent = ent->next)
+ gtk_xtext_search_textentry (buf, ent, TRUE);
+ buf->search_found = g_list_reverse (buf->search_found);
+ }
- /* Save distance to start, end of found string */
- if (ent)
+ /* Now base search results are in place. */
+ if (buf->search_found)
{
- ent->mark_start = str - hay;
- ent->mark_end = ent->mark_start + strlen (nee);
-
- /* is the match visible? Might need to scroll */
- if (!gtk_xtext_check_ent_visibility (xtext, ent, 0))
+ if (buf->cursearch == NULL)
{
- ent = xtext->buffer->text_first;
- line = 0;
+ ent = g_list_find (buf->search_found, buf->hintsearch);
while (ent)
+ if (ent->marks == NULL)
+ ent = bwd? ent->prev: ent->next;
+ else
+ break;
+ buf->cursearch = ent;
+ if (buf->cursearch == NULL)
+ buf->cursearch = bwd? g_list_last (buf->search_found)
+ : g_list_first (buf->search_found);
+ ent = buf->cursearch->data;
+ buf->curmark = bwd? g_list_last (ent->marks)
+ : g_list_first (ent->marks);
+ } else
+ {
+ ent = buf->cursearch->data;
+ if (buf->curmark == NULL)
+ buf->curmark = bwd? g_list_last (ent->marks)
+ : g_list_first (ent->marks);
+ else
{
- line += ent->lines_taken;
- ent = ent->next;
- if (ent == fent)
- break;
+ buf->curmark = bwd? g_list_previous (buf->curmark)
+ : g_list_next (buf->curmark);
}
- while (line > xtext->adj->upper - xtext->adj->page_size)
- line--;
- if (backward)
- line -= xtext->adj->page_size - ent->lines_taken;
- xtext->adj->value = line;
- xtext->buffer->scrollbar_down = FALSE;
- gtk_adjustment_changed (xtext->adj);
+ if (buf->curmark == NULL)
+ {
+ /* We've returned all the matches for this text entry. */
+ ent = NULL;
+ buf->cursearch = bwd? g_list_previous (buf->cursearch)
+ : g_list_next (buf->cursearch);
+ if (buf->cursearch)
+ {
+ ent = buf->cursearch->data;
+ buf->curmark = bwd? g_list_last (ent->marks)
+ : g_list_first (ent->marks);
+ }
+ }
}
}
+checkvisible:
+ fent = ent;
+ buf->hintsearch = fent;
+
+ if (!gtk_xtext_check_ent_visibility (xtext, fent, 0))
+ {
+ GtkAdjustment *adj = xtext->adj;
+ int value, subtr;
- g_free (nee);
+ buf->pagetop_ent = NULL;
+ for (value = 0, ent = buf->text_first; ent && ent != fent; ent = ent->next)
+ value += ent->lines_taken;
+ if (value > adj->upper - adj->page_size)
+ value = adj->upper - adj->page_size;
+ else if (bwd && ent)
+ {
+ subtr = adj->page_size - ent->lines_taken + 1;
+ if (value >= subtr)
+ value -= subtr;
+ else
+ value = 0;
+ }
+ gtk_adjustment_set_value (adj, value);
+ }
+
gtk_widget_queue_draw (GTK_WIDGET (xtext));
return fent;
@@ -5053,6 +5283,7 @@
ent->mark_start = -1;
ent->mark_end = -1;
ent->next = NULL;
+ ent->marks = NULL;
if (ent->indent < MARGIN)
ent->indent = MARGIN; /* 2 pixels is the left margin */
@@ -5114,6 +5345,7 @@
if (buf->old_value < 0)
buf->old_value = 0;
}
+ gtk_xtext_search_textentry (buf, ent, FALSE);
}
/* the main two public functions */
@@ -5440,6 +5672,9 @@
if (buf->xtext->selection_buffer == buf)
buf->xtext->selection_buffer = NULL;
+ if (buf->search_found)
+ gtk_xtext_search_fini (buf);
+
ent = buf->text_first;
while (ent)
{
Index: WORKxchat/src/fe-gtk/xtext.h
===================================================================
--- WORKxchat/src/fe-gtk/xtext.h (revision 1451)
+++ WORKxchat/src/fe-gtk/xtext.h (working copy)
@@ -77,6 +77,15 @@
unsigned int grid_dirty:1;
unsigned int marker_seen:1;
unsigned int reset_marker_pos:1;
+
+ GList *search_found; /* list of textentries where search found strings */
+ GList *cursearch; /* GList whose 'data' pts to current textentry */
+ textentry *hintsearch; /* textentry found for last search */
+ GList *curmark; /* current item in ent->marks */
+ gchar *search_text; /* desired text to search for */
+ gchar *search_nee; /* prepared needle to look in haystack for */
+ unsigned int search_match;
+ unsigned int search_bwd;
} xtext_buffer;
struct _GtkXText
Index: WORKxchat/src/fe-gtk/menu.c
===================================================================
--- WORKxchat/src/fe-gtk/menu.c (revision 1451)
+++ WORKxchat/src/fe-gtk/menu.c (working copy)
@@ -1198,6 +1198,33 @@
}
static void
+menu_search_next ()
+{
+ GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext);
+ xtext_buffer *buf = xtext->buffer;
+
+ gtk_xtext_search (xtext, buf->search_text, NULL, buf->search_match, FALSE);
+}
+
+static void
+menu_search_prev ()
+{
+ GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext);
+ xtext_buffer *buf = xtext->buffer;
+
+ gtk_xtext_search (xtext, buf->search_text, NULL, buf->search_match, TRUE);
+}
+
+static void
+menu_search_reset ()
+{
+ GtkXText *xtext = GTK_XTEXT (current_sess->gui->xtext);
+ xtext_buffer *buf = xtext->buffer;
+
+ gtk_xtext_search (xtext, NULL, buf->text_last, buf->search_match, 0);
+}
+
+static void
menu_resetmarker (GtkWidget * wid, gpointer none)
{
gtk_xtext_reset_marker_pos (GTK_XTEXT (current_sess->gui->xtext));
@@ -1648,6 +1675,8 @@
{N_("C_lear Text"), menu_flushbuffer, GTK_STOCK_CLEAR, M_MENUSTOCK, 0, 0, 1, GDK_l},
#define SEARCH_OFFSET 67
{N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f},
+ {N_("Search Next" ), menu_search_next, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_g},
+ {N_("Search Reset" ), menu_search_reset, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_F},
{N_("Save Text..."), menu_savebuffer, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1},
{N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 69 */
@@ -2188,7 +2217,8 @@
mymenu[i].key,
mymenu[i].key == GDK_F1 ? 0 :
mymenu[i].key == GDK_w ? close_mask :
- GDK_CONTROL_MASK,
+ (mymenu[i].key >= GDK_A && mymenu[i].key <= GDK_Z) ?
+ GDK_CONTROL_MASK | GDK_SHIFT_MASK : GDK_CONTROL_MASK,
GTK_ACCEL_VISIBLE);
if (mymenu[i].callback)
g_signal_connect (G_OBJECT (item), "activate",
Index: WORKxchat/src/fe-gtk/search.c
===================================================================
--- WORKxchat/src/fe-gtk/search.c (revision 1451)
+++ WORKxchat/src/fe-gtk/search.c (working copy)
@@ -60,11 +60,22 @@
last = gtk_xtext_search (GTK_XTEXT (sess->gui->xtext), text,
last, case_match, search_backward);
- if (!last)
+ if (!last && text[0] != 0)
fe_message (_("Search hit end, not found."), FE_MSG_ERROR);
}
static void
+search_reset_cb (GtkWidget * button, session * sess)
+{
+ GtkEntry *entry;
+ const gchar *text;
+
+ entry = g_object_get_data (G_OBJECT (button), "e");
+ text = gtk_entry_get_text (entry);
+ search_search (sess, "");
+}
+
+static void
search_find_cb (GtkWidget * button, session * sess)
{
GtkEntry *entry;
@@ -153,6 +164,14 @@
_("_Find"));
g_object_set_data (G_OBJECT (wid), "e", entry);
+ hbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 4);
+ gtk_widget_show (hbox);
+
+ wid = gtkutil_button (hbox, "gtk-reset", 0, search_reset_cb, sess,
+ _("_Reset"));
+ add_tip (wid, "Reset highlighted search items");
+
g_signal_connect (G_OBJECT (win), "key-press-event", G_CALLBACK (search_key_cb), win);
gtk_widget_show (win);