/* * GTK+ text widget(GtkText) support module * Independent from specific data structure. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "gtktext-support.h" #include "misc.h" /** * gtext_insert_buf: **/ guint gtext_insert_buf(GtkWidget *text, const FontProp *fprop, const char *buf_pt, int lenb) { gtk_text_insert(GTK_TEXT(text), fprop->font, fprop->fg, fprop->bg, buf_pt, lenb); return gtk_text_get_point(GTK_TEXT(text)); } /** * gtext_forward_delete: * @len is character-length. **/ gint gtext_forward_delete(GtkWidget *text, guint len) { return gtk_text_forward_delete(GTK_TEXT(text), len); } /** * gtext_forward_delete_b: * @lenb is byte-length. * On the other hand, gtk_text_forward_delete() requires character-length. * @buf_pt is used to calculate character-length. * @buf_pt == NULL is allowed. If so, I assume string to delete is SBCS. **/ gint gtext_forward_delete_b(GtkWidget *text, const char *buf_pt, guint lenb) { gboolean b_sbcs = !GTK_TEXT(text)->use_wchar;/* Single byte character set. */ if (buf_pt == NULL || b_sbcs == TRUE) { return gtk_text_forward_delete(GTK_TEXT(text), lenb); } else { guint len_chars; len_chars = get_num_chars(buf_pt, lenb, b_sbcs); return gtk_text_forward_delete(GTK_TEXT(text), len_chars); } } /** * gtext_line_from_index: * Get the line number from index. **/ int gtext_line_from_index(GtkWidget *text, int index) { int i; int lenb; int ln = 1; lenb = gtk_text_get_length(GTK_TEXT(text)); g_assert(index <= lenb); for (i = 0; i < index; i++) { if (GTK_TEXT_INDEX(GTK_TEXT(text), i) == '\n') { ln++; } } return ln; } /** * gtext_gotoline_nowrap: * Goto the specified line for non-wrapped text widget. * This is much faster than wrapped case's routine. **/ void gtext_gotoline_nonwrap(GtkWidget *text, int ln, int total) { GtkAdjustment *vadj = GTK_TEXT(text)->vadj; gdouble value; gdouble max; if (total == 0) return; max = vadj->upper - vadj->lower; g_return_if_fail(max != 0); value = ((double)ln / total * max) - (vadj->page_size / 2); gtk_adjustment_set_value(vadj, value); } /** * gtext_gotoline_wrap: * Goto the specified line for line-wrapped text widget. * This is very slow. * Inspired by gnotepad+-1.1.4. **/ void gtext_gotoline_wrap(GtkWidget *text, int ln, int total) { int i; int lenb; /* Call gtext_gotoline_nonwrap() for efficiency. * For even line-wrapped text widget, that works closely(not precisely), * and it is useful for this routine's efficiency.*/ gtext_gotoline_nonwrap(text, ln, total); if (ln == 0) return; lenb = gtk_text_get_length(GTK_TEXT(text)); for (i = 0; i < lenb; i++) { if (GTK_TEXT_INDEX(GTK_TEXT(text), i) == '\n') { ln--; if (ln == 0) { gtk_editable_set_position(GTK_EDITABLE(text), i); return; } } } g_assert_not_reached(); } /* Guessing routines, which are used when moving relative next(or previous) diff part. They work only when text widget is line-wrap-off. */ /** * gtext_guess_visible_top_line: * Guess the top line number of visible part of text widget, and return it. **/ int gtext_guess_visible_top_line(GtkWidget *text, int total) { GtkAdjustment *vadj = GTK_TEXT(text)->vadj; gdouble max = vadj->upper - vadj->lower; int line = 0; if (max) { line = vadj->value / max * total; } return line + 1; } /** * gtext_guess_visible_center_line: * Guess the center line number of visible part of text widget, and return it. **/ int gtext_guess_visible_center_line(GtkWidget *text, int total) { GtkAdjustment *vadj = GTK_TEXT(text)->vadj; gdouble max = vadj->upper - vadj->lower; int line = 0; if (max) { line = (vadj->value + vadj->page_size / 2) / max * total; } return line + 1; } /** * gtext_guess_visible_bottom_line: * Guess the bottom line number of visible part of text widget, and return it. **/ int gtext_guess_visible_bottom_line(GtkWidget *text, int total) { GtkAdjustment *vadj = GTK_TEXT(text)->vadj; gdouble max = vadj->upper - vadj->lower; int line = 0; if (max) { line = (vadj->value + vadj->page_size) / max * total; } return line + 1; } /** * gtext_search_string: * ugly, ugly... many value-result parameters. * Interface is ugly, but what this function does is simple. * Search the string in the specified line on text widget. * This scans only one line for search. * Input: * int ln; line number where search will be executed. * int cached_ln; cached line number. To avoid goto-line every time. * int *cached_point; when @cached_ln is available, this points to the beginning of the line. * int *cached_index; index in the line @ln. If @cached_ln equals to @ln, search will be executed from @cached_index. * Output: * Return value; TRUE if @string is found. * int *cached_point; the beginning of the line @ln. * int *cached_index; When @string is found, return the index of the string in the line @ln. **/ gboolean gtext_search_string(GtkWidget *text, const char *string, int lenb, int ln, int cached_ln, int *cached_point, int *cached_index) { #define STRING_INDEX(sb, wc, index) (b_sbcs ? sb[index] : wc[index]) #define STRING_LEN (b_sbcs ? lenb : wlen) gboolean ret = FALSE; int cur_ln = 1; int cur_point = 0; gboolean b_sbcs = !GTK_TEXT(text)->use_wchar;/* Single byte character set. */ GdkWChar *pwcs = NULL; int wlen = 0; gboolean buf_allocated = FALSE; if (ln == 0) return FALSE; /* Take care of caches. Does this make sense? */ if (cached_ln != 0 && cached_ln <= ln) {/* cache line number is available */ g_assert(*cached_point != 0); cur_ln = cached_ln; cur_point = *cached_point;/* point of the beginning of the line */ } if (cached_ln == ln) { /* If search is for the same line, cache index is available */ cur_point += *cached_index; } else { /* go to the beginning of the line */ while (cur_ln < ln) { if (GTK_TEXT_INDEX(GTK_TEXT(text), cur_point) == '\n') { cur_ln++; if (cur_ln == ln) break; } cur_point++; } cur_point++; *cached_point = cur_point;/* return the beginning of the line as cache */ } if (b_sbcs == FALSE) { pwcs = g_new(GdkWChar, lenb+1); buf_allocated = TRUE; wlen = gdk_mbstowcs(pwcs, string, lenb+1); if (wlen == -1) b_sbcs = TRUE; } /* Search the string _only in this line_ */ while (GTK_TEXT_INDEX(GTK_TEXT(text), cur_point) != '\n') { if (GTK_TEXT_INDEX(GTK_TEXT(text), cur_point) == STRING_INDEX(string, pwcs, 0)) { int i; for (i = 0; i < STRING_LEN; i++) { if (GTK_TEXT_INDEX(GTK_TEXT(text), cur_point + i) != STRING_INDEX(string, pwcs, i)) break; } if (i == STRING_LEN) {/* found */ *cached_index = cur_point - *cached_point;/* return the index as cache */ /* highlight */ gtk_editable_select_region(GTK_EDITABLE(text), cur_point, cur_point + STRING_LEN); ret = TRUE; goto done; } } cur_point++; } done: if (buf_allocated) g_free(pwcs); return ret; }