/* * One-pane widget module * * Naming convention: * buf: internal (mmap'd) buffer, which is static. * text: GtkText widget data, which is variable. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "diff.h" #include "gui.h" #include "dtextmap.h" #include "onepane-widget.h" #include "misc.h" #include "gtktext-support.h" #include "linenum.h" #include "style.h" /* Private function declarations */ static void gdiff_onepane_class_init(GdiffOnePaneClass *klass); static void gdiff_onepane_init(GdiffOnePane *onepane); static void gdiff_onepane_finalize(GtkObject *object); static void gdiff_onepane_display(GdiffBasePane *basepane); static void gdiff_onepane_show_linenum(GdiffBasePane *basepane, gboolean to_show); static gboolean gdiff_onepane_toggle_textwrap(GdiffBasePane *basepane); static void gdiff_onepane_set_highlight(GdiffBasePane *basepane, gboolean to_highlight); static void gdiff_onepane_move_diff(GdiffBasePane *basepane, MoveDiff mv_diff); static gboolean gdiff_onepane_search_string(GdiffBasePane *basepane, const char *string, WhichFile whichfile); static void guts_onepane_display(GdiffOnePane *onepane); static void draw_text(GdiffOnePane *onepane); static void show_hide_numbers(GdiffOnePane *onepane, gboolean b_show); static void calc_ln_columns(GdiffOnePane *onepane, int max_nlines); static void guts_move_diff(GdiffOnePane *onepane, MoveDiff mv_diff); static void change_lines_bgcolor(GdiffOnePane *onepane, const DiffLines *dlines, WhichFile whichfile, GdkColor *bg_color); static gboolean guts_search_string_firstfile(GdiffOnePane *onepane, const char *string); static void centering_text(GdiffOnePane *onepane, int tln); static GdiffBasePaneClass *parent_class = NULL; GtkType gdiff_onepane_get_type(void) { static GtkType onepane_type = 0; if (!onepane_type) { static const GtkTypeInfo onepane_info = { "GdiffOnePane", sizeof(GdiffOnePane), sizeof(GdiffOnePaneClass), (GtkClassInitFunc)gdiff_onepane_class_init, (GtkObjectInitFunc)gdiff_onepane_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc)NULL, }; onepane_type = gtk_type_unique(GDIFF_TYPE_BASEPANE, &onepane_info); } return onepane_type; } static void gdiff_onepane_class_init(GdiffOnePaneClass *klass) { GtkObjectClass *object_class; GdiffBasePaneClass *basepane_class; object_class = (GtkObjectClass*)klass; basepane_class = (GdiffBasePaneClass*)klass; parent_class = gtk_type_class(GDIFF_TYPE_BASEPANE); object_class->finalize = gdiff_onepane_finalize; basepane_class->display = gdiff_onepane_display; basepane_class->show_linenum = gdiff_onepane_show_linenum; basepane_class->toggle_textwrap = gdiff_onepane_toggle_textwrap; basepane_class->set_highlight = gdiff_onepane_set_highlight; basepane_class->move_diff = gdiff_onepane_move_diff; basepane_class->search_string = gdiff_onepane_search_string; } static void gdiff_onepane_init(GdiffOnePane *onepane) { GdiffBasePane *basepane; GtkBin *bin; GtkWidget *text; GtkWidget *scrollwin; int n; basepane = GDIFF_BASEPANE(onepane); bin = GTK_BIN(basepane); scrollwin = gtk_scrolled_window_new(NULL, NULL); /* XXX: I can't use horizontal scrollbar, because of the current text widget's limitation. */ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(bin), scrollwin); gtk_widget_show(scrollwin); text = gtk_text_new(NULL, NULL); onepane->text = text; style_set_text(text); gtext_set_editable(text, FALSE); gtext_set_word_wrap(text, FALSE); gtext_set_line_wrap(text, PANE_PREF(onepane).line_wrap); gtk_container_add(GTK_CONTAINER(scrollwin), text); gtk_widget_grab_focus(text); gtk_widget_show(text); PANE_PREF(onepane).view_type = NO_VIEW;/* not determined here */ for (n = 0; n < MAX_NUM_COMPARE_FILES; n++) { onepane->search_ln[n] = 0; } onepane->search_tln = 0; onepane->search_tpoint = 0; onepane->search_tindex = 0; } static void gdiff_onepane_finalize(GtkObject *object) { GdiffOnePane *onepane; g_return_if_fail(object != NULL); g_return_if_fail(GDIFF_IS_ONEPANE(object)); onepane = GDIFF_ONEPANE(object); dtmap_delete(onepane->dtmap); (*GTK_OBJECT_CLASS(parent_class)->finalize)(object); } GtkWidget* gdiff_onepane_new(DiffDir *diffdir, DiffFiles *dfiles) { GdiffOnePane *onepane; const FileInfo *fi1 = dfiles_get_fileinfo(dfiles, FIRST_FILE, TRUE); onepane = gtk_type_new(GDIFF_TYPE_ONEPANE); _gdiff_basepane_set_backend(GDIFF_BASEPANE(onepane), diffdir, dfiles);/*XXX*/ /* One-pane internal data */ onepane->dtmap = dtmap_new(fi1->nlines); onepane->n_col = -1; if (dfiles->is_diff3) PANE_PREF(onepane).view_type = ONEPANE3_VIEW; else PANE_PREF(onepane).view_type = ONEPANE2_VIEW; /* workaround */ gtk_widget_ensure_style(onepane->text); guts_onepane_display(onepane); return GTK_WIDGET(onepane); } /** Interfaces **/ static void gdiff_onepane_display(GdiffBasePane *basepane) { GdiffOnePane *onepane; g_return_if_fail(basepane != NULL); g_return_if_fail(GDIFF_IS_ONEPANE(basepane)); onepane = GDIFF_ONEPANE(basepane); guts_onepane_display(onepane); } /** * gdiff_onepane_show_linenum: * Show(Hide) the line numbers on text widget. * Input: * gboolean to_show; TRUE implies to show. FALSE implies to hide. **/ static void gdiff_onepane_show_linenum(GdiffBasePane *basepane, gboolean to_show) { GdiffOnePane *onepane; g_return_if_fail(basepane != NULL); g_return_if_fail(GDIFF_IS_ONEPANE(basepane)); onepane = GDIFF_ONEPANE(basepane); if (to_show != PANE_PREF(onepane).show_line_num) { show_hide_numbers(onepane, to_show); PANE_PREF(onepane).show_line_num = to_show; } } /** * gdiff_onepane_toggle_textwrap: **/ static gboolean gdiff_onepane_toggle_textwrap(GdiffBasePane *basepane) { GdiffOnePane *onepane; gboolean b_wrap; g_return_val_if_fail(basepane != NULL, FALSE); g_return_val_if_fail(GDIFF_IS_ONEPANE(basepane), FALSE); onepane = GDIFF_ONEPANE(basepane); b_wrap = !(GTK_TEXT(onepane->text)->line_wrap); gtext_set_line_wrap(onepane->text, b_wrap); PANE_PREF(onepane).line_wrap = b_wrap; return b_wrap; } /** * gdiff_onepane_set_highlight: * Take care of the current highlight. * Input: * gboolean to_highlight; TRUE implies to enable highlight. FALSE implies to disable. **/ static void gdiff_onepane_set_highlight(GdiffBasePane *basepane, gboolean to_highlight) { GdiffOnePane *onepane; g_return_if_fail(basepane != NULL); g_return_if_fail(GDIFF_IS_ONEPANE(basepane)); onepane = GDIFF_ONEPANE(basepane); if (to_highlight != PANE_PREF(onepane).highlight) { if (GDIFF_BASEPANE(onepane)->cur_dlines_node) { const DiffLines *dlines = GDIFF_BASEPANE(onepane)->cur_dlines_node->data; GdkColor *bg_color; if (to_highlight == TRUE) { bg_color = &PANE_PREF(onepane).diff_hl[FIRST_FILE]; change_lines_bgcolor(onepane, dlines, FIRST_FILE, bg_color); } else { bg_color = &PANE_PREF(onepane).diff_bg[FIRST_FILE]; change_lines_bgcolor(onepane, dlines, FIRST_FILE, bg_color); } } PANE_PREF(onepane).highlight = to_highlight; } } /** * gdiff_onepane_move_diff: * Move to a difference, such as next, previous, first or last. **/ static void gdiff_onepane_move_diff(GdiffBasePane *basepane, MoveDiff mv_diff) { GdiffOnePane *onepane; g_return_if_fail(basepane != NULL); g_return_if_fail(GDIFF_IS_ONEPANE(basepane)); onepane = GDIFF_ONEPANE(basepane); guts_move_diff(onepane, mv_diff); } /** * gdiff_onepane_search_string: * @string is null-byte-terminated. Return TRUE if found. **/ static gboolean gdiff_onepane_search_string(GdiffBasePane *basepane, const char *string, WhichFile whichfile) { GdiffOnePane *onepane; g_return_val_if_fail(basepane != NULL, FALSE); g_return_val_if_fail(GDIFF_IS_ONEPANE(basepane), FALSE); if (string == NULL || string[0] == '\0') return FALSE; onepane = GDIFF_ONEPANE(basepane); if (whichfile == FIRST_FILE) return guts_search_string_firstfile(onepane, string); else return FALSE;/* not implemented */ } /** Internal functions **/ /** * guts_onepane_display: * Show the diff result in one-pane mode. **/ static void guts_onepane_display(GdiffOnePane *onepane) { draw_text(onepane); if (PANE_PREF(onepane).show_line_num == TRUE) { show_hide_numbers(onepane, TRUE); } } /** * draw_text: * Draw text with coloring different parts. * Two buffers are drawn on the same text. * During drawing, update dtmap. **/ static void draw_text(GdiffOnePane *onepane) { DiffFiles *dfiles = PANE_DFILES(onepane); const FileInfo *fi[MAX_NUM_COMPARE_FILES]; MBuffer *mbuf[MAX_NUM_COMPARE_FILES]; const char *buf_pt[MAX_NUM_COMPARE_FILES]; int buf_ln1; int buf_lenb[MAX_NUM_COMPARE_FILES]; DTextMap *dtmap = onepane->dtmap; GtkWidget *text = onepane->text; GdkFont *font = text->style->font; GList *node;/* node of DiffLines list */ FontProp fprop; GdkColor *fg_color[MAX_NUM_COMPARE_FILES]; GdkColor *bg_color[MAX_NUM_COMPARE_FILES]; int oldpos = 0; int pos; int n; int num_files = PANE_DFILES(onepane)->is_diff3 ? 3 : 2; DispAttr attr = 0; for (n = 0; n < num_files; n++) { fi[n] = dfiles_get_fileinfo(dfiles, n, TRUE); mbuf[n] = PANE_MBUF(onepane, n); fg_color[n] = &PANE_PREF(onepane).diff_fg[n]; bg_color[n] = &PANE_PREF(onepane).diff_bg[n]; mbuf_goto_top(mbuf[n]); } if (fi[0]->buf == NULL && fi[1]->buf == NULL && fi[2]->buf == NULL) /* Maybe, the file has been deleted. */ return; fprop.font = font; gtext_freeze(text); for (node = dfiles->dlines_list; node; node = node->next) { const DiffLines *dlines = node->data; /* Use local variables for readability */ int begin[MAX_NUM_COMPARE_FILES]; int end[MAX_NUM_COMPARE_FILES]; for (n = 0; n < num_files; n++) { begin[n] = dlines->between[n].begin; end[n] = dlines->between[n].end; } fprop.fg = fprop.bg = NULL; buf_ln1 = mbuf[FIRST_FILE]->cur_ln; buf_pt[FIRST_FILE] = mbuf[FIRST_FILE]->cur_pt; buf_lenb[FIRST_FILE] = mbuf_goto_line(mbuf[FIRST_FILE], begin[FIRST_FILE]) - buf_pt[FIRST_FILE]; pos = gtext_insert_buf(text, &fprop, buf_pt[FIRST_FILE], mbuf[FIRST_FILE]->cur_pt - buf_pt[FIRST_FILE]); dtmap_append_displn(dtmap, DA_COMMON, oldpos, pos - oldpos, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE], begin[FIRST_FILE] - buf_ln1); oldpos = pos; buf_pt[FIRST_FILE] = mbuf[FIRST_FILE]->cur_pt; mbuf_goto_line(mbuf[FIRST_FILE], end[FIRST_FILE] + 1); if (dlines->difftype == CHANGE || dlines->difftype & ONLY_CHANGE || dlines->difftype == F12ADD || dlines->difftype == F31ADD) { WhichFile startfile = SECOND_FILE; WhichFile lastfile = num_files; fprop.fg = fg_color[FIRST_FILE]; fprop.bg = bg_color[FIRST_FILE]; buf_lenb[FIRST_FILE] = mbuf[FIRST_FILE]->cur_pt - buf_pt[FIRST_FILE]; pos = gtext_insert_buf(text, &fprop, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE]); dtmap_append_displn(dtmap, DA_CHANGE, oldpos, pos - oldpos, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE], end[FIRST_FILE]+1 - begin[FIRST_FILE]); oldpos = pos; if (dlines->difftype & F1ONLY || dlines->difftype & F2ONLY || dlines->difftype == F12ADD) { /* This should be distinguished. */ startfile = SECOND_FILE; lastfile = SECOND_FILE + 1; } else if (dlines->difftype & F3ONLY || dlines->difftype == F31ADD) { startfile = THIRD_FILE; lastfile = THIRD_FILE + 1; } for (n = startfile; n < lastfile; n++) { fprop.fg = fg_color[n]; fprop.bg = bg_color[n]; buf_pt[n] = mbuf_goto_line(mbuf[n], begin[n]); buf_lenb[n] = mbuf_goto_line(mbuf[n], end[n] + 1) - buf_pt[n]; pos = gtext_insert_buf(text, &fprop, buf_pt[n], buf_lenb[n]); if (n == SECOND_FILE) attr = DA_CHANGE_O; else if (n == THIRD_FILE) attr = DA_CHANGE_O2; dtmap_append_displn(dtmap, attr, oldpos, pos - oldpos, buf_pt[n], buf_lenb[n], end[n]+1 - begin[n]); oldpos = pos; } } else if (dlines->difftype & F1ONLY) {/* ONLY_ADD */ fprop.fg = fg_color[FIRST_FILE]; fprop.bg = bg_color[FIRST_FILE]; buf_lenb[FIRST_FILE] = mbuf[FIRST_FILE]->cur_pt - buf_pt[FIRST_FILE]; pos = gtext_insert_buf(text, &fprop, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE]); dtmap_append_displn(dtmap, DA_ONLY, oldpos, pos - oldpos, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE], end[FIRST_FILE]+1 - begin[FIRST_FILE]); oldpos = pos; } else if (dlines->difftype == F23ADD || ((dlines->difftype & ONLY_ADD) && ((dlines->difftype & F2ONLY) || (dlines->difftype & F3ONLY)))) { WhichFile startfile = SECOND_FILE; WhichFile lastfile = num_files; WhichFile whichfile = dlines->difftype & F3ONLY ? THIRD_FILE : SECOND_FILE;/* XXX */ fprop.fg = fprop.bg = NULL; buf_lenb[FIRST_FILE] = mbuf[FIRST_FILE]->cur_pt - buf_pt[FIRST_FILE]; pos = gtext_insert_buf(text, &fprop, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE]); if (whichfile == SECOND_FILE) attr = DA_O_ONLY; else if (whichfile == THIRD_FILE) attr = DA_O2_ONLY; dtmap_append_displn(dtmap, attr, oldpos, pos - oldpos, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE], end[FIRST_FILE]+1 - begin[FIRST_FILE]); oldpos = pos; if (dlines->difftype & F2ONLY) { startfile = SECOND_FILE; lastfile = SECOND_FILE + 1; } else if (dlines->difftype & F3ONLY) { startfile = THIRD_FILE; lastfile = THIRD_FILE + 1; } for (n = startfile; n < lastfile; n++) { fprop.fg = fg_color[n]; fprop.bg = bg_color[n]; buf_pt[n] = mbuf_goto_line(mbuf[n], begin[n]); buf_lenb[n] = mbuf_goto_line(mbuf[n], end[n] + 1) - buf_pt[n]; pos = gtext_insert_buf(text, &fprop, buf_pt[n], buf_lenb[n]); if (n == SECOND_FILE) attr = DA_ONLY_O; else if (n == THIRD_FILE) attr = DA_ONLY_O2; dtmap_append_displn(dtmap, attr, oldpos, pos - oldpos, buf_pt[n], buf_lenb[n], end[n]+1 - begin[n]); oldpos = pos; } } } /* Draw the remained part */ fprop.fg = fprop.bg = NULL; buf_ln1 = mbuf[FIRST_FILE]->cur_ln; buf_pt[FIRST_FILE] = mbuf[FIRST_FILE]->cur_pt; buf_lenb[FIRST_FILE] = mbuf_goto_line(mbuf[FIRST_FILE], fi[FIRST_FILE]->nlines + 1) - buf_pt[FIRST_FILE]; pos = gtext_insert_buf(text, &fprop, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE]); dtmap_append_displn(dtmap, DA_COMMON, oldpos, pos - oldpos, buf_pt[FIRST_FILE], buf_lenb[FIRST_FILE], fi[FIRST_FILE]->nlines+1 - buf_ln1); gtext_thaw(text); } /** * show_hide_numbers: * Show(Hide) line numbers on the head of each lines. * This takes care of two buffers(merged text) at the same time. **/ static void show_hide_numbers(GdiffOnePane *onepane, gboolean b_show) { DiffFiles *dfiles = PANE_DFILES(onepane); const FileInfo *fi[MAX_NUM_COMPARE_FILES]; DTextMap *dtmap = onepane->dtmap; GtkWidget *text = onepane->text; GdkFont *font = text->style->font; FontProp fprop; GdkColor *fg_color[MAX_NUM_COMPARE_FILES]; GdkColor *bg_color[MAX_NUM_COMPARE_FILES]; LineFormat lformat; int ln_col_size; DispLines *displn; int ln[MAX_NUM_COMPARE_FILES]; int num_files = PANE_DFILES(onepane)->is_diff3 ? 3 : 2; int n; for (n = 0; n < num_files; n++) { ln[n] = 1; fi[n] = dfiles_get_fileinfo(dfiles, n, TRUE); fg_color[n] = &PANE_PREF(onepane).diff_fg[n]; bg_color[n] = &PANE_PREF(onepane).diff_bg[n]; } if (fi[0]->buf == NULL && fi[1]->buf == NULL && fi[2]->buf == NULL) /* Maybe, the file has been deleted. */ return; if (onepane->n_col < 0) calc_ln_columns(onepane, MAX(MAX(fi[0]->nlines, fi[1]->nlines), fi[1]->nlines)); lformat.n_col = onepane->n_col; ln_col_size = lformat.n_col + MARK_LENGTH; fprop.font = font; gtext_freeze(text); for (displn = dtmap_first_displn(dtmap, DA_ANY); displn; displn = dtmap_next_displn(dtmap, DA_ANY)) { if (displn->attr & (DA_COMMON | DA_O_ONLY)) { fprop.fg = fprop.bg = NULL; lformat.format = onepane->format_common; } else if (displn->attr & DA_BASE) { fprop.fg = fg_color[FIRST_FILE]; fprop.bg = bg_color[FIRST_FILE]; lformat.format = onepane->format_diff[FIRST_FILE]; } else if (displn->attr & DA_OTHER) { fprop.fg = fg_color[SECOND_FILE]; fprop.bg = bg_color[SECOND_FILE]; lformat.format = onepane->format_diff[SECOND_FILE]; } else if (displn->attr & DA_OTHER2) { fprop.fg = fg_color[THIRD_FILE]; fprop.bg = bg_color[THIRD_FILE]; lformat.format = onepane->format_diff[THIRD_FILE]; } else { g_assert_not_reached(); } mbuf_goto_top(MBUFFER(displn)); if (displn->attr & DA_BASE) { insert_remove_line_numbers(text, b_show, displn->pos, &fprop, MBUFFER(displn), ln[FIRST_FILE], ln[FIRST_FILE] + MBUFFER(displn)->nl, &lformat); ln[FIRST_FILE] += MBUFFER(displn)->nl; } else if (displn->attr & DA_OTHER) { insert_remove_line_numbers(text, b_show, displn->pos, &fprop, MBUFFER(displn), ln[SECOND_FILE], ln[SECOND_FILE] + MBUFFER(displn)->nl, &lformat); } else if (displn->attr & DA_OTHER2) { insert_remove_line_numbers(text, b_show, displn->pos, &fprop, MBUFFER(displn), ln[THIRD_FILE], ln[THIRD_FILE] + MBUFFER(displn)->nl, &lformat); } if (displn->attr & (DA_COMMON | DA_O_ONLY | DA_OTHER)) { ln[SECOND_FILE] += MBUFFER(displn)->nl; } if (displn->attr & (DA_COMMON | DA_O2_ONLY | DA_OTHER2)) { ln[THIRD_FILE] += MBUFFER(displn)->nl; } if (b_show == TRUE) { dtmap_inc_displn(dtmap, displn, MBUFFER(displn)->nl * ln_col_size); } else { dtmap_dec_displn(dtmap, displn, MBUFFER(displn)->nl * ln_col_size); } } gtext_thaw(text); } /* calculate column size of line numbers, and store it */ static void calc_ln_columns(GdiffOnePane *onepane, int max_nlines) { int n_col; n_col = calc_number_places(max_nlines); onepane->n_col = n_col; g_snprintf(onepane->format_common, sizeof(onepane->format_common), "%%%dd%s", n_col, MARK_COMMON);/* e.g. "%4d " */ g_snprintf(onepane->format_diff[FIRST_FILE], sizeof(onepane->format_diff[FIRST_FILE]), "%%%dd%s", n_col, MARK_FILE1);/* e.g. "%4d< " */ g_snprintf(onepane->format_diff[SECOND_FILE], sizeof(onepane->format_diff[SECOND_FILE]), "%%%dd%s", n_col, MARK_FILE2);/* e.g. "%4d> " */ if (PANE_DFILES(onepane)->is_diff3) g_snprintf(onepane->format_diff[THIRD_FILE], sizeof(onepane->format_diff[THIRD_FILE]), "%%%dd%s", n_col, MARK_FILE3);/* e.g. "%4d^ " */ } /* Routines for move to a difference */ /** * guts_move_diff: **/ static void guts_move_diff(GdiffOnePane *onepane, MoveDiff mv_diff) { DiffFiles *dfiles = PANE_DFILES(onepane); DTextMap *dtmap = onepane->dtmap; GtkWidget *text = onepane->text; int cur_ln; /* current line number */ const GList *dlines_node; const DiffLines *dlines = NULL; if ((PANE_PREF(onepane).highlight == TRUE)/* revert the current one */ && (mv_diff != MOVED_CURRENT || mv_diff != MOVED_CUR_NOSCROLL)) { GdkColor *bg_color; if (GDIFF_BASEPANE(onepane)->cur_dlines_node) { const DiffLines *dlines = GDIFF_BASEPANE(onepane)->cur_dlines_node->data; bg_color = &PANE_PREF(onepane).diff_bg[FIRST_FILE]; change_lines_bgcolor(onepane, dlines, FIRST_FILE, bg_color); } } if (mv_diff == MOVED_REL_NEXT) { /* Relative moves are special features. */ /* Driven by the first file's different position. */ cur_ln = gtext_guess_visible_bottom_line(text, dtmap->total_nl); dlines_node = dfiles_find_rel_nextl(dfiles, FIRST_FILE, cur_ln); dlines = dlines_node ? dlines_node->data : NULL; } else if (mv_diff == MOVED_REL_PREV) { cur_ln = gtext_guess_visible_top_line(text, dtmap->total_nl); dlines_node = dfiles_find_rel_prevl(dfiles, FIRST_FILE, cur_ln); dlines = dlines_node ? dlines_node->data : NULL; } else { /* findfn_table is defined in fileview.c */ int i; for (i = 0; i < NUM_FTABLE; i++) { if (findfn_table[i].mv_diff == mv_diff) break; } g_assert(findfn_table[i].find_fn != NULL); /* find the node */ dlines_node = (findfn_table[i].find_fn)(dfiles, GDIFF_BASEPANE(onepane)->cur_dlines_node); dlines = dlines_node ? dlines_node->data : NULL; GDIFF_BASEPANE(onepane)->cur_dlines_node = dlines_node; } if (mv_diff != MOVED_CUR_NOSCROLL && dlines) { /* Use local variables for readability */ int begin1 = dlines->between[FIRST_FILE].begin; int tbegin1 = dtmap_map_b2t(dtmap, begin1); if (PANE_PREF(onepane).line_wrap == TRUE) { gtext_gotoline_wrap(text, tbegin1, dtmap->total_nl); } else { gtext_gotoline_nonwrap(text, tbegin1, dtmap->total_nl); } } if (PANE_PREF(onepane).highlight == TRUE) { GdkColor *bg_color; if (dlines) { bg_color = &PANE_PREF(onepane).diff_hl[FIRST_FILE]; change_lines_bgcolor(onepane, dlines, FIRST_FILE, bg_color); } } } /* Currently, used for highlight */ static void change_lines_bgcolor(GdiffOnePane *onepane, const DiffLines *dlines, WhichFile whichfile, GdkColor *bg_color) { DTextMap *dtmap = onepane->dtmap; GtkWidget *text = onepane->text; DispLines *displn = dtmap_lookup_by_bufln(dtmap, dlines->between[whichfile].begin); const char *buf_pt; GdkFont *font = text->style->font; FontProp fprop; GdkColor *fg_color = &PANE_PREF(onepane).diff_fg[whichfile]; fprop.font = font; fprop.fg = fg_color; fprop.bg = bg_color; if (displn && !(displn->attr & DA_HIDE)) { gtext_freeze(text); gtext_set_point(text, displn->pos); gtext_forward_delete(text, displn->len); buf_pt = mbuf_goto_top(MBUFFER(displn)); gtext_insert_buf(text, &fprop, buf_pt, mbuf_goto_bottom(MBUFFER(displn)) - buf_pt); if (PANE_PREF(onepane).show_line_num == TRUE) { int ln = dtmap_bufln_by_displn(dtmap, displn); LineFormat lformat; lformat.n_col = onepane->n_col; lformat.format = onepane->format_diff[whichfile]; mbuf_goto_top(MBUFFER(displn)); insert_remove_line_numbers(text, TRUE, displn->pos, &fprop, MBUFFER(displn), ln, ln + MBUFFER(displn)->nl, &lformat); } gtext_thaw(text); } } /** * guts_search_string_firstfile: * See guts_search_string() in twopane-widget.c about details. **/ static gboolean guts_search_string_firstfile(GdiffOnePane *onepane, const char *string) { MBuffer *mbuf = PANE_MBUF(onepane, FIRST_FILE); DTextMap *dtmap = onepane->dtmap; GtkWidget *text = onepane->text; int lenb = strlen(string); int ln; /* line number in buf */ int tln; /* line number in text */ int cached_tindex; int cached_tln; int cached_tpoint; /* At first, search in the current line from the next index. */ cached_tln = onepane->search_tln; cached_tpoint = onepane->search_tpoint; cached_tindex = onepane->search_tindex + 1; if (gtext_search_string(text, string, lenb, cached_tln, cached_tln, &cached_tpoint, &cached_tindex) == TRUE) { onepane->search_tindex = cached_tindex;/* store for the next search */ centering_text(onepane, cached_tln); return TRUE; } /* If not found in the current line, continue searching from the next line */ /* First, search in mbuf */ ln = onepane->search_ln[FIRST_FILE] + 1; ln = mbuf_search_string(mbuf, ln, string, lenb); onepane->search_ln[FIRST_FILE] = ln;/* store for the next search */ if (ln == 0) {/* not found */ gdk_beep(); return FALSE; } else { /* Found in mbuf, try to find it in text widget */ tln = dtmap_map_b2t(dtmap, ln); cached_tindex = 0;/* dummy */ if (gtext_search_string(text, string, lenb, tln, cached_tln, &cached_tpoint, &cached_tindex) == FALSE) g_warning("guts_search_string wrong result."); /* store these for the next search */ onepane->search_tln = tln; onepane->search_tpoint = cached_tpoint; onepane->search_tindex = cached_tindex; centering_text(onepane, tln); return TRUE; } } static void centering_text(GdiffOnePane *onepane, int tln) { DTextMap *dtmap = onepane->dtmap; GtkWidget *text = onepane->text; if (PANE_PREF(onepane).line_wrap == TRUE) { gtext_gotoline_wrap(text, tln, dtmap->total_nl); } else { gtext_gotoline_nonwrap(text, tln, dtmap->total_nl); } }