/* * Diff text map module * See "dtextmap.h" for the details. * * Copyright INOUE Seiichiro , licensed under the GPL. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "gui.h" #include "dtextmap.h" /* Private function declarations */ static DispLines* displn_new(DispAttr attr, int pos, int len, const char *buf, int buf_lenb, int buf_nl); static void displn_delete(DispLines *displn); static void move_down_nodes(GList *h_node, int i_len); static void move_up_nodes(GList *h_node, int i_len); DTextMap* dtmap_new(int buf_nl) { DTextMap *dtmap; dtmap = g_new(DTextMap, 1); dtmap->total_nl = buf_nl;/* Initial value */ dtmap->displn_list = NULL; dtmap->cur_node = NULL; dtmap->last_node = NULL; dtmap->b_has_o_only_top = FALSE; return dtmap; } void dtmap_delete(DTextMap *dtmap) { GList *list; list = dtmap->displn_list; while (list) { DispLines *displn = list->data; GList *next = list->next; displn_delete(displn); list = next; } g_list_free(dtmap->displn_list); g_free(dtmap); } /** * dtmap_map_b2t: * Mapping a line number from buffer to text. **/ int dtmap_map_b2t(const DTextMap *dtmap, int buf_ln) { GList *node; int bln = 1; int tln = 1; for (node = dtmap->displn_list; node; node = node->next) { const DispLines *displn = node->data; if (displn->attr & DA_BASE) { if (buf_ln < bln + MBUFFER(displn)->nl) {/* found */ tln += (buf_ln - bln); return tln; } bln += MBUFFER(displn)->nl; } if (!(displn->attr & DA_HIDE)) tln += MBUFFER(displn)->nl; } return 0; } /** * dtmap_map_t2b: * Mapping a line number from text to buffer. **/ int dtmap_map_t2b(const DTextMap *dtmap, int text_ln) { GList *node; int tln = 1; int bln = 1; for (node = dtmap->displn_list; node; node = node->next) { const DispLines *displn = node->data; if (text_ln < tln + MBUFFER(displn)->nl) {/* found */ const DispLines *tmp = node->data; if (!(tmp->attr & DA_BASE)) bln--; if (displn->attr & DA_BASE) { bln += (text_ln - tln); return bln; } else {/* return related bln */ return bln; } } if (!(displn->attr & DA_HIDE)) tln += MBUFFER(displn)->nl; if (displn->attr & DA_BASE) bln += MBUFFER(displn)->nl; } return 0; } /** * dtmap_bufln_by_displn: * Return the line number in buf related to @displn; **/ int dtmap_bufln_by_displn(const DTextMap *dtmap, const DispLines *displn) { GList *node; int bln = 1; for (node = dtmap->displn_list; node; node = node->next) { const DispLines *tmp = node->data; if (tmp == displn) return bln; if (tmp->attr & DA_BASE) bln += MBUFFER(tmp)->nl; } return 0; } /** * dtmap_append_displn: * Create DispLines instance and append it to the list. * The arguments are almost related to DispLines member variables. **/ DispLines* dtmap_append_displn(DTextMap *dtmap, DispAttr attr, int pos, int len, const char *buf, int buf_lenb, int buf_nl) { DispLines *displn; displn = displn_new(attr, pos, len, buf, buf_lenb, buf_nl); if (displn == NULL) return NULL; if (displn->attr & DA_O_ONLY_TOP) dtmap->b_has_o_only_top = TRUE; if (dtmap->displn_list == NULL) { g_assert(dtmap->last_node == NULL); dtmap->displn_list = g_list_append(dtmap->displn_list, displn); dtmap->last_node = dtmap->displn_list; dtmap->cur_node = dtmap->displn_list; } else { if (dtmap->last_node) dtmap->last_node = g_list_append(dtmap->last_node, displn)->next; else dtmap->last_node = g_list_append(dtmap->displn_list, displn)->next; } if (!(attr & DA_BASE) && !(attr & DA_HIDE)) dtmap->total_nl += buf_nl; return displn; } static gint compare_for_prepend(gconstpointer a, gconstpointer b) { const DispLines *a1 = a; const DispLines *b1 = b; return a1->pos - b1->pos; } /** * dtmap_insert_displn: * Create DispLines instance and insert(prepend) it into the list. * The inserted position is based on displn->pos. **/ DispLines* dtmap_insert_displn(DTextMap *dtmap, DispAttr attr, int pos, int len, const char *buf, int buf_lenb, int buf_nl) { DispLines *displn; GList *node; displn = displn_new(attr, pos, len, buf, buf_lenb, buf_nl); if (displn == NULL) return NULL; if (displn->attr & DA_O_ONLY_TOP) dtmap->b_has_o_only_top = TRUE; if (dtmap->b_has_o_only_top && (attr & (DA_ONLY_O|DA_MARK)) && pos == 0) {/* kludge */ g_assert(((DispLines*)dtmap->displn_list->data)->pos == 0); dtmap->displn_list = g_list_insert(dtmap->displn_list, displn, 1); } else dtmap->displn_list = g_list_insert_sorted(dtmap->displn_list, displn, compare_for_prepend); if (!(attr & DA_HIDE)) move_down_nodes(node = g_list_find(dtmap->displn_list, displn)->next, len); if (!(attr & DA_BASE) && !(attr & DA_HIDE)) dtmap->total_nl += buf_nl; return displn; } static void displn_remove_by_node(DTextMap *dtmap, GList *node) { DispLines *displn = node->data; GList *n_node = node->next; int len = displn->len; DispAttr attr = displn->attr; dtmap->total_nl -= MBUFFER(displn)->nl; if (dtmap->cur_node == node) dtmap->cur_node = node->next; if (dtmap->last_node == node) dtmap->last_node = node->prev; displn_delete(displn); dtmap->displn_list = g_list_remove_link(dtmap->displn_list, node); g_list_free_1(node); if (!(attr & DA_HIDE)) move_up_nodes(n_node, len); } /** * dtmap_remove_displn: * Remove @displn from the list. * Search is started from @cur_node. * Return TRUE if it was removed. **/ gboolean dtmap_remove_displn(DTextMap *dtmap, DispLines *displn) { GList *node; for (node = dtmap->cur_node; node; node = node->next) { if (node->data == displn) {/* found */ displn_remove_by_node(dtmap, node); return TRUE; } } for (node = dtmap->displn_list; node != dtmap->cur_node; node = node->next) { if (node->data == displn) {/* found */ displn_remove_by_node(dtmap, node); return TRUE; } } return FALSE; } /** * dtmap_first_displn: * Filtering with @attr, return the first node of the list. **/ DispLines* dtmap_first_displn(DTextMap *dtmap, DispAttr attr) { GList *node; for (node = dtmap->displn_list; node; node = node->next) { DispLines *displn = node->data; if (displn->attr & attr) { dtmap->cur_node = node; return displn; } } return NULL; } /** * dtmap_next_displn: * Filtering with @attr, return the next node of the list. * Usually, dtmap_first_displn() should be called before this. **/ DispLines* dtmap_next_displn(DTextMap *dtmap, DispAttr attr) { GList *node; if (dtmap->cur_node == NULL) return NULL; for (node = dtmap->cur_node->next; node; node = node->next) { DispLines *displn = node->data; if (displn->attr & attr) { dtmap->cur_node = node; return displn; } } return NULL; } /** * dtmap_lookup_by_bufln: * Using @buf_ln(line number in buf), look for the related DispLines. **/ DispLines* dtmap_lookup_by_bufln(DTextMap *dtmap, int buf_ln) { GList *node; int bln = 1; if (buf_ln == 0) {/* special. A virtual zero'th line could exist */ DispLines *displn; node = dtmap->displn_list; displn = node ? node->data : NULL; if (displn && displn->pos == 0 && displn->len == 0 && (displn->attr & DA_O_ONLY_TOP)) return displn; } for (node = dtmap->displn_list; node; node = node->next) { DispLines *displn = node->data; if ((displn->attr & DA_BASE) && !(displn->attr & DA_O_ONLY_TOP)) { if (bln == buf_ln)/* found */ return displn; else if (bln > buf_ln) return NULL; bln += MBUFFER(displn)->nl; } } return NULL; } /** * dtmap_lookup_next: * Return the next DispLines to @displn. * Search is started from @cur_node. **/ DispLines* dtmap_lookup_next(DTextMap *dtmap, DispLines *displn) { GList *node; for (node = dtmap->cur_node; node; node = node->next) { DispLines *tmp = node->data; if (tmp == displn) { dtmap->cur_node = node->next; return dtmap->cur_node ? dtmap->cur_node->data : NULL; } } for (node = dtmap->displn_list; node != dtmap->cur_node; node = node->next) { DispLines *tmp = node->data; if (tmp == displn) { dtmap->cur_node = node->next; return dtmap->cur_node ? dtmap->cur_node->data : NULL; } } return NULL; } /** * dtmap_lookup_prev: * Return the previous DispLines to @displn. * Search is started from @cur_node. **/ DispLines* dtmap_lookup_prev(DTextMap *dtmap, DispLines *displn) { GList *node; for (node = dtmap->cur_node; node; node = node->next) { DispLines *tmp = node->data; if (tmp == displn) { dtmap->cur_node = node->prev; return dtmap->cur_node ? dtmap->cur_node->data : NULL; } } for (node = dtmap->displn_list; node != dtmap->cur_node; node = node->next) { DispLines *tmp = node->data; if (tmp == displn) { dtmap->cur_node = node->prev; return dtmap->cur_node ? dtmap->cur_node->data : NULL; } } return NULL; } /** * dtmap_inc_displn: * Increment @len of @displn. Typically, called when line numbers are inserted. * The following lines are moved down. **/ void dtmap_inc_displn(DTextMap *dtmap, DispLines *displn, int i_len) { displn->len += i_len; move_down_nodes(g_list_find(dtmap->displn_list, displn)->next, i_len); } /** * dtmap_dec_displn: * Decrement @len of @displn. Typically, called when line numbers are removed. * The following lines are moved up. **/ void dtmap_dec_displn(DTextMap *dtmap, DispLines *displn, int i_len) { displn->len -= i_len; move_up_nodes(g_list_find(dtmap->displn_list, displn)->next, i_len); } /** * dtmap_show_displn: * Show @displn, so the following lines are move down. * If I was too strict, I have to remove this node once and insert it as a shown node. * That is annonying, so this function is used to skip the process. **/ void dtmap_show_displn(DTextMap *dtmap, DispLines *displn) { displn->attr &= ~DA_HIDE; if (displn->len > 0) { int i_len = displn->len; dtmap->total_nl += MBUFFER(displn)->nl; displn->len = 0; dtmap_inc_displn(dtmap, displn, i_len);/* => displn->len = i_len */ } } /** * dtmap_hide_displn: * Hide @displn, so the following lines are move up. **/ void dtmap_hide_displn(DTextMap *dtmap, DispLines *displn) { displn->attr |= DA_HIDE; if (displn->len > 0) { int i_len = displn->len; dtmap->total_nl -= MBUFFER(displn)->nl; displn->len += i_len; dtmap_dec_displn(dtmap, displn, i_len);/* => disln->len = i_len */ } } /* ---The followings are private functions--- */ static DispLines* displn_new(DispAttr attr, int pos, int len, const char *buf, int buf_lenb, int buf_nl) { DispLines *displn; MBuffer *mbuf; #ifdef DEBUG g_print("displn_new: attr=%x pos=%d len=%d buf_lenb=%d buf_nl=%d\n", attr, pos, len, buf_lenb, buf_nl); #endif /* Special: a virtual zero'th line. The other file has an additional lines at the top */ if (len == 0 && pos == 0 && (attr & DA_O_ONLY || attr & DA_O2_ONLY)) { buf_nl = 0; attr |= DA_O_ONLY_TOP; } else if (len == 0 && !(attr & DA_HIDE)) return NULL; displn = g_new(DispLines, 1); mbuf = MBUFFER(displn); _mbuf_init(mbuf, buf, buf_lenb, buf_nl, FALSE); displn->attr = attr; displn->pos = pos; displn->len = len; return displn; } static void displn_delete(DispLines *displn) { _mbuf_finalize(MBUFFER(displn)); g_free(displn); } /* This implies the displayed lines are moved down. */ static void move_down_nodes(GList *h_node, int i_len) { GList *node; for (node = h_node; node; node = node->next) { DispLines *displn = node->data; displn->pos += i_len; } } /* This implies the displayed lines are moved up. */ static void move_up_nodes(GList *h_node, int i_len) { GList *node; for (node = h_node; node; node = node->next) { DispLines *displn = node->data; displn->pos -= i_len; } }