home *** CD-ROM | disk | FTP | other *** search
- /* editrect.c -- Manipulating `rectangles' of text
- Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
-
- This file is part of Jade.
-
- Jade is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- Jade is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with Jade; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "jade.h"
- #include "jade_protos.h"
-
- #include <string.h>
- #include <stdlib.h>
- #include <ctype.h>
-
- #ifdef NEED_MEMORY_H
- # include <memory.h>
- #endif
-
- _PR bool rect_insert(TX *, const u_char *, long, POS *);
- _PR bool rect_delete(TX *, POS *, POS *);
- _PR void order_rect(TX *, POS *, POS *, POS *, POS *);
- _PR VALUE rect_copy(TX *, POS *, POS *);
- _PR void editrect_init(void);
-
- bool
- rect_insert(TX *tx, const u_char *text, long textLen, POS *pos)
- {
- long len, col = pos->pos_Col;
- LINE *line = tx->tx_Lines + pos->pos_Line;
- POS orig;
- while(textLen > 0 && pos->pos_Line < tx->tx_NumLines)
- {
- const u_char *end = memchr(text, '\n', textLen);
- if(!end)
- {
- end = text + textLen;
- len = textLen;
- textLen = 0;
- }
- else
- {
- len = end - text;
- textLen -= len + 1;
- }
- if(!pad_pos(tx, pos))
- goto nomem;
- orig = *pos;
- if(!insert_str_n(tx, text, len, pos))
- goto nomem;
- undo_record_insertion(tx, &orig, pos);
- flag_insertion(tx, &orig, pos);
- pos->pos_Line++;
- pos->pos_Col = col;
- line++;
- text = (*end == '\n') ? end + 1 : end;
- }
- if(textLen > 0 && pos->pos_Line == tx->tx_NumLines)
- {
- /*
- * Now start expanding off the bottom of the file.
- */
- orig = *pos;
- while(textLen > 0)
- {
- const u_char *end = memchr(text, '\n', textLen);
- u_char *new;
- if(!end)
- {
- end = text + textLen;
- len = textLen;
- textLen = 0;
- }
- else
- {
- len = end - text;
- textLen -= len + 1;
- }
- if(!resize_line_list(tx, +1, pos->pos_Line))
- goto nomem;
- if(!(new = str_alloc(col + len + 1)))
- goto nomem;
- memset(new, ' ', col);
- memcpy(new + col, text, len);
- new[col + len] = 0;
- tx->tx_Lines[pos->pos_Line].ln_Strlen = col + len + 1;
- tx->tx_Lines[pos->pos_Line].ln_Line = new;
- adjust_marks_add_y(tx, +1, pos->pos_Line);
- pos->pos_Line++;
- line++;
- text = (*end == '\n') ? end + 1 : end;
- }
- if(len > 0)
- pos->pos_Col += len;
- pos->pos_Line--;
- undo_record_insertion(tx, &orig, pos);
- flag_insertion(tx, &orig, pos);
- }
- return(TRUE);
- nomem:
- mem_error();
- return(FALSE);
- }
-
- bool
- rect_delete(TX *tx, POS *startPos, POS *endPos)
- {
- LINE *line = tx->tx_Lines + startPos->pos_Line;
- while(startPos->pos_Line <= endPos->pos_Line)
- {
- POS start;
- start.pos_Col = char_col(tx, startPos->pos_Col, startPos->pos_Line);
- if(line->ln_Strlen > start.pos_Col)
- {
- long del_len, spc_len;
- POS end;
- long start_glyph = glyph_col(tx, start.pos_Col,
- startPos->pos_Line);
- if((start_glyph != startPos->pos_Col) && (start.pos_Col > 0))
- {
- start.pos_Col--;
- start_glyph = glyph_col(tx, start.pos_Col, startPos->pos_Line);
- }
- end.pos_Col = char_col(tx, endPos->pos_Col, startPos->pos_Line);
- if(end.pos_Col >= line->ln_Strlen)
- end.pos_Col = line->ln_Strlen - 1;
- del_len = end.pos_Col - start.pos_Col;
- spc_len = startPos->pos_Col - start_glyph;
- start.pos_Line = end.pos_Line = startPos->pos_Line;
- undo_record_deletion(tx, &start, &end);
- if(!delete_chars(tx, &start, del_len))
- return(FALSE);
- flag_deletion(tx, &start, &end);
-
- /* May need to insert some spaces to compensate for a deleted
- TAB which crossed the start of the deletion. */
- if(spc_len > 0)
- {
- if(!insert_gap(tx, spc_len, &start))
- return(FALSE);
- memset(line->ln_Line + start.pos_Col, ' ', spc_len);
- end.pos_Col = start.pos_Col + spc_len;
- undo_record_insertion(tx, &start, &end);
- flag_insertion(tx, &start, &end);
- }
- }
- startPos->pos_Line++;
- line++;
- }
- return(TRUE);
- }
-
- /* Makes sure that the corners of a rectangle are how I want them. Converts
- from character positions to glyph positions. */
- void
- order_rect(TX *tx, POS *dstart, POS *dend, POS *sstart, POS *send)
- {
- long start_col = glyph_col(tx, sstart->pos_Col, sstart->pos_Line);
- long end_col = glyph_col(tx, send->pos_Col, send->pos_Line);
- if(start_col < end_col)
- {
- dstart->pos_Col = start_col;
- dend->pos_Col = end_col;
- }
- else
- {
- dstart->pos_Col = end_col;
- dend->pos_Col = start_col;
- }
- if(sstart->pos_Line < send->pos_Line)
- {
- dstart->pos_Line = sstart->pos_Line;
- dend->pos_Line = send->pos_Line;
- }
- else
- {
- dstart->pos_Line = send->pos_Line;
- dend->pos_Line = sstart->pos_Line;
- }
- }
-
- /* Copies a rectangle to a string, newlines are inserted between lines. */
- VALUE
- rect_copy(TX *tx, POS *startPos, POS *endPos)
- {
- VALUE res;
- u_char *buf = str_alloc(128);
- long len = 0, buflen = 128;
- long linenum = startPos->pos_Line;
- LINE *line = tx->tx_Lines + linenum;
- long width = (endPos->pos_Col - startPos->pos_Col) + 1;
- if(!buf)
- return(mem_error());
- while(linenum <= endPos->pos_Line)
- {
- long glyphs;
- if(len + width >= buflen)
- {
- long newlen = buflen * 2;
- u_char *new = str_alloc(newlen);
- if(!new)
- {
- str_free(buf);
- return(mem_error());
- }
- memcpy(new, buf, len);
- str_free(buf);
- buf = new;
- buflen = newlen;
- }
- len += expand_tabs(tx, buf + len, startPos->pos_Col, endPos->pos_Col,
- linenum, &glyphs);
- if(glyphs < width - 1)
- {
- glyphs = width - glyphs - 1;
- memset(buf + len, ' ', glyphs);
- len += glyphs;
- }
- buf[len++] = '\n';
- linenum++;
- line++;
- }
- res = string_dupn(buf, len);
- str_free(buf);
- return(res);
- }
-
- _PR VALUE cmd_insert_rect(VALUE str, VALUE pos, VALUE buff);
- DEFUN_INT("insert-rect", cmd_insert_rect, subr_insert_rect, (VALUE str, VALUE lpos, VALUE buff), V_Subr3, DOC_insert_rect, "sString to insert rectangularly") /*
- ::doc:insert_rect::
- insert-rect STRING [POS] [BUFFER]
-
- Inserts STRING into BUFFER at POS treating it as a ``rectangle'' of
- text -- that is, each separate line in STRING (separated by newlines) is
- inserted at the *same* column in successive lines.
- ::end:: */
- {
- POS pos;
- DECLARE1(str, STRINGP);
- if(POSP(lpos))
- pos = VPOS(lpos);
- else
- pos = curr_vw->vw_CursorPos;
- if(!BUFFERP(buff))
- buff = VAL(curr_vw->vw_Tx);
- if(!read_only(VTX(buff)) && pad_pos(VTX(buff), &pos))
- {
- if(rect_insert(VTX(buff), VSTR(str), STRING_LEN(str), &pos))
- return(sym_t);
- }
- return(sym_nil);
- }
-
- _PR VALUE cmd_delete_rect(VALUE lstart, VALUE lend, VALUE buff);
- DEFUN("delete-rect", cmd_delete_rect, subr_delete_rect, (VALUE lstart, VALUE lend, VALUE buff), V_Subr3, DOC_delete_rect) /*
- ::doc:delete_rect::
- delete-rect START-POS END-POS [BUFFER]
-
- Deletes the rectangle of text from one corner, START-POS, to the opposite
- corner, END-POS.
- ::end:: */
- {
- POS start, end;
- DECLARE1(lstart, POSP);
- DECLARE2(lend, POSP);
- if(!BUFFERP(buff))
- buff = VAL(curr_vw->vw_Tx);
- order_rect(VTX(buff), &start, &end, &VPOS(lstart), &VPOS(lend));
- if(!read_only(VTX(buff)) && check_line(VTX(buff), &end))
- {
- rect_delete(VTX(buff), &start, &end);
- return(sym_t);
- }
- return(sym_nil);
- }
-
- _PR VALUE cmd_copy_rect(VALUE lstart, VALUE lend, VALUE buff);
- DEFUN("copy-rect", cmd_copy_rect, subr_copy_rect, (VALUE lstart, VALUE lend, VALUE buff), V_Subr3, DOC_copy_rect) /*
- ::doc:copy_rect::
- copy-rect START-POS END-POS [BUFFER]
-
- Returns the rectangle of text marked out by START-POS and END-POS.
- ::end:: */
- {
- POS start, end;
- DECLARE1(lstart, POSP);
- DECLARE2(lend, POSP);
- if(!BUFFERP(buff))
- buff = VAL(curr_vw->vw_Tx);
- order_rect(VTX(buff), &start, &end, &VPOS(lstart), &VPOS(lend));
- if(check_line(VTX(buff), &end))
- return(rect_copy(VTX(buff), &start, &end));
- return(sym_nil);
- }
-
- _PR VALUE cmd_cut_rect(VALUE lstart, VALUE lend, VALUE buff);
- DEFUN("cut-rect", cmd_cut_rect, subr_cut_rect, (VALUE lstart, VALUE lend, VALUE buff), V_Subr3, DOC_cut_rect) /*
- ::doc:cut_rect::
- cut-rect START-POS END-POS [BUFFER]
-
- The same as `copy-rect' except that the section of text copied (START-POS
- to END-POS) is deleted from the file after being duplicated.
- ::end:: */
- {
- POS start, end;
- DECLARE1(lstart, POSP);
- DECLARE2(lend, POSP);
- if(!BUFFERP(buff))
- buff = VAL(curr_vw->vw_Tx);
- order_rect(VTX(buff), &start, &end, &VPOS(lstart), &VPOS(lend));
- if(!read_only(VTX(buff)) && check_line(VTX(buff), &end))
- {
- VALUE str = rect_copy(VTX(buff), &start, &end);
- if(str)
- rect_delete(VTX(buff), &start, &end);
- return(str);
- }
- return(sym_nil);
- }
-
- void
- editrect_init(void)
- {
- ADD_SUBR(subr_insert_rect);
- ADD_SUBR(subr_delete_rect);
- ADD_SUBR(subr_copy_rect);
- ADD_SUBR(subr_cut_rect);
- }
-