Logo Search packages:      
Sourcecode: ne version File versions  Download package

clips.c

/* Clip handling functions.

      Copyright (C) 1993-1998 Sebastiano Vigna 
      Copyright (C) 1999-2010 Todd M. Lewis and Sebastiano Vigna

      This file is part of ne, the nice editor.

      This program 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.
      
      This program 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 this program; see the file COPYING.  If not, write to the Free
      Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
      02111-1307, USA.  */


#include "ne.h"


/* A clip is a numbered node in the global clip list. The contents of the clip
      are handled through the stream functions contained in streams.c.

      At creation time, a clip is marked with an encoding. Clips, of course, may
      be pasted only in buffers with a compatible encoding. 

      Note that pasting a clip in an ASCII buffer may change its encoding.
*/


/* Allocates a clip descriptor. */

clip_desc *alloc_clip_desc(int n, int size) {

      clip_desc *cd;

      assert(n >= 0);
      assert(size >= 0);

      if (cd = calloc(1, sizeof(clip_desc))) {
            cd->n = n;
            if (cd->cs = alloc_char_stream(size)) return cd;

            free(cd);
      }
      return NULL;
}



/* Reallocates a clip descriptor of the given size. If cd is NULL, this is
      equivalent to calling alloc_clip_desc. */

clip_desc *realloc_clip_desc(clip_desc *cd, int n, int size) {

      char_stream *cs;

      assert(n >= 0);
      assert(size >= 0);

      if (!cd) return alloc_clip_desc(n, size);

      assert_clip_desc(cd);

      if (cd->n != n) return NULL;

      if (cs = realloc_char_stream(cd->cs, size)) {
            cd->cs = cs;
            return cd;
      }
      
      return NULL;
}


/* Frees a clip descriptor. */

void free_clip_desc(clip_desc *cd) {

      if (!cd) return;

      assert_clip_desc(cd);

      free_char_stream(cd->cs);
      free(cd);
}


/* Scans the global clip list, searching for a specific numbered clip. Returns
      NULL on failure. */

clip_desc *get_nth_clip(int n) {
      clip_desc *cd = (clip_desc *)clips.head;

      while (cd->cd_node.next) {

            assert_clip_desc(cd);

            if (cd->n == n) return cd;
            cd = (clip_desc *)cd->cd_node.next;
      }

      return NULL;
}



/* Copies the characters between the cursor and the block marker of the given
      buffer to the nth clip. If the cut flag is true, the characters are also
      removed from the text. The code scans the text two times: the first time in
      order to determine the exact length of the text, the second time in order to
      actually copy it. */

int copy_to_clip(buffer *b, int n, int cut) {

      int i, pass, start_pos, end_pos, len, clip_len, y = b->cur_line;
      unsigned char *p;
      clip_desc *cd, *new_cd;
      line_desc *ld = b->cur_line_desc;

      if (!b->marking) return MARK_BLOCK_FIRST;

      if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;

      /* If the mark and the cursor are on the same line and on the same position
      (or both beyond the line length), we can't copy anything. */

      cd = get_nth_clip(n);

      if (y == b->block_start_line &&
            (b->win_x + b->cur_x == b->block_start_col ||
             b->cur_pos >= ld->line_len && b->block_start_col >= calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding))) {

            if (!(new_cd = realloc_clip_desc(cd, n, 0))) return OUT_OF_MEMORY;
            if (!cd) add_head(&clips, &new_cd->cd_node);
            return OK;
      }


      /* We have two different loops for direct or inverse copying. Making this
      conditional code would be cumbersome, awkward, and definitely inefficient. */

      if (y > b->block_start_line || y == b->block_start_line && b->win_x + b->cur_x > b->block_start_col) {

            for(pass = clip_len = 0; pass < 2; pass++) {

                  ld = b->cur_line_desc;

                  for(i = y; i >= b->block_start_line; i--) {

                        start_pos = 0;
                        len = ld->line_len;

                        if (i == b->block_start_line) {
                              start_pos = calc_pos(ld, b->block_start_col, b->opt.tab_size, b->encoding);
                              len -= start_pos;
                        }

                        if (i == y) {
                              len -= (b->cur_pos >= ld->line_len) ? 0 : ld->line_len - b->cur_pos;
                        }

                        if (pass) {
                              assert(!(len != 0 && ld->line == NULL));

                              if (i != y) *--p = 0;
                              p -= len;
                              if (ld->line) memcpy(p, ld->line + start_pos, len);
                        }
                        else clip_len += len + (i != y);

                        ld = (line_desc *)ld->ld_node.prev;
                  }

                  if (pass) {

                        cd->cs->len = clip_len;
                        set_stream_encoding(cd->cs, b->encoding);
                        assert_clip_desc(cd);

                        if (cut) {
                              goto_line(b, b->block_start_line);
                              goto_column(b, b->block_start_col);
                              delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, clip_len);
                              update_syntax_and_lines(b, b->cur_line_desc, NULL);
                        }

                        return OK;
                  }

                  if (!(new_cd = realloc_clip_desc(cd, n, clip_len))) return OUT_OF_MEMORY;
                  if (!cd) add_head(&clips, &new_cd->cd_node);
                  cd = new_cd;
                  p = cd->cs->stream + clip_len;
            }
      }
      else {

            for(pass = clip_len = 0; pass < 2; pass++) {

                  ld = b->cur_line_desc;

                  for(i = y; i <= b->block_start_line; i++) {

                        start_pos = 0;
                        len = ld->line_len;

                        if (i == y) {
                              start_pos = b->cur_pos > ld->line_len ? ld->line_len : b->cur_pos;
                              len -= start_pos;
                        }

                        if (i == b->block_start_line) {
                              end_pos = calc_pos(ld, b->block_start_col, b->opt.tab_size, b->encoding);
                              len -= ld->line_len - end_pos;
                        }

                        if (pass) {
                              assert(!(len != 0 && ld->line == NULL));

                              if (ld->line) memcpy(p, ld->line + start_pos, len);
                              p += len;
                              if (i != y) *(p++) = 0;
                        }
                        else clip_len += len + (i != y);

                        ld = (line_desc *)ld->ld_node.next;
                  }

                  if (pass) {
                        cd->cs->len = clip_len;
                        set_stream_encoding(cd->cs, b->encoding);
                        assert_clip_desc(cd);
                        if (cut) delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, clip_len);
                        update_syntax_and_lines(b, b->cur_line_desc, NULL);
                        return OK;
                  }

                  if (!(new_cd = realloc_clip_desc(cd, n, clip_len))) return OUT_OF_MEMORY;
                  if (!cd) add_head(&clips, &new_cd->cd_node);
                  cd = new_cd;
                  p = cd->cs->stream;
            }
      }
      return OK;
}



/* Simply erases a block, without putting it in a clip. Calls update_syntax_and_lines(). */

int erase_block(buffer *b) {

      int i, start_pos, end_pos, len, erase_len = 0, y = b->cur_line;
      line_desc *ld = b->cur_line_desc;

      if (!b->marking) return MARK_BLOCK_FIRST;
      if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;

      if (y == b->block_start_line &&
            (b->win_x + b->cur_x == b->block_start_col ||
             b->cur_pos >= ld->line_len && b->block_start_col >= calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding)))
            return OK;

      if (y > b->block_start_line || y == b->block_start_line && b->win_x + b->cur_x > b->block_start_col) {
            for(i = y; i >= b->block_start_line; i--) {
                  start_pos = 0;
                  len = ld->line_len;
                  if (i == b->block_start_line) {
                        start_pos = calc_pos(ld, b->block_start_col, b->opt.tab_size, b->encoding);
                        len -= start_pos;
                  }
                  if (i == y) {
                        len -= (b->cur_pos >= ld->line_len) ? 0 : ld->line_len - b->cur_pos;
                  }
                  erase_len += len + 1;
                  ld = (line_desc *)ld->ld_node.prev;
            }
            goto_line(b, b->block_start_line);
            goto_column(b, b->block_start_col);
      }
      else {
            for(i = y; i <= b->block_start_line; i++) {
                  start_pos = 0;
                  len = ld->line_len;
                  if (i == y) {
                        start_pos = b->cur_pos >= ld->line_len ? ld->line_len : b->cur_pos;
                        len -= start_pos;
                  }
                  if (i == b->block_start_line) {
                        end_pos = calc_pos(ld, b->block_start_col, b->opt.tab_size, b->encoding);
                        len -= ld->line_len - end_pos;
                  }
                  erase_len += len + 1;
                  ld = (line_desc *)ld->ld_node.next;
            }
      }

      delete_stream(b, b->cur_line_desc, b->cur_line, b->cur_pos, erase_len - 1);
      update_syntax_and_lines(b, b->cur_line_desc, NULL);
      return OK;
}




/* Pastes a clip into a buffer. Since clips are streams, the operation is
      definitely straightforward. */

int paste_to_buffer(buffer *b, int n) {

      clip_desc *cd;

      if (!(cd = get_nth_clip(n))) return CLIP_DOESNT_EXIST;

      if (!cd->cs->len) return OK;
      if (cd->cs->encoding == ENC_ASCII || b->encoding == ENC_ASCII || cd->cs->encoding == b->encoding) {
            line_desc * const ld = b->cur_line_desc, * const end_ld = (line_desc *)b->cur_line_desc->ld_node.next;
            if (b->encoding == ENC_ASCII) b->encoding = cd->cs->encoding;

            start_undo_chain(b);
            if (b->cur_pos > ld->line_len) 
                  insert_spaces(b, ld, b->cur_line, ld->line_len, b->win_x + b->cur_x - calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding));

            insert_stream(b, ld, b->cur_line, b->cur_pos, cd->cs->stream, cd->cs->len);
            end_undo_chain(b);

            assert(ld == b->cur_line_desc);
            update_syntax_and_lines(b, ld, end_ld);
            return OK;
      }

      return INCOMPATIBLE_CLIP_ENCODING;
}



/* Works like copy_to_clip(), but the region to copy is the rectangle defined
      by the cursor and the marker. Same comments apply. Note that in case of a
      cut we use start_undo_chain() in order to make the various deletions a
      single undo operation. */

int copy_vert_to_clip(buffer *b, int n, int cut) {

      int i, pass, start_pos, len, clip_len, y = b->cur_line, start_x, end_x;
      char *p;
      clip_desc *cd, *new_cd;
      line_desc *ld = b->cur_line_desc;

      if (!b->marking) return MARK_BLOCK_FIRST;
      if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;

      cd = get_nth_clip(n);

      if (b->win_x + b->cur_x == b->block_start_col ||
            y == b->block_start_line && b->cur_pos >= ld->line_len && b->block_start_col >= calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding)) {

            if (!(new_cd = realloc_clip_desc(cd, n, 0))) return OUT_OF_MEMORY;
            set_stream_encoding(new_cd->cs, ENC_ASCII);
            if (!cd) add_head(&clips, &new_cd->cd_node);
            return OK;
      }

      start_x = b->block_start_col;
      end_x = b->win_x + b->cur_x;

      if (end_x < start_x) {
            start_x = b->win_x + b->cur_x;
            end_x = b->block_start_col + 1;
      }

      if (cut) start_undo_chain(b);

      if (y > b->block_start_line) {

            for(pass = clip_len = 0; pass < 2; pass++) {

                  ld = b->cur_line_desc;

                  for(i = y; i >= b->block_start_line; i--) {

                     start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
                     len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding) - start_pos;

                        if (pass) {
                              *--p = 0;
                              p -= len;
                              if (len) memcpy(p, ld->line + start_pos, len);
                              if (cut) delete_stream(b, ld, i, start_pos, len);
                        }

                        else clip_len += len + 1;
                        ld = (line_desc *)ld->ld_node.prev;
                  }

                  if (pass) {
                        cd->cs->len = clip_len;
                        set_stream_encoding(cd->cs, b->encoding);
                        assert_clip_desc(cd);
                        if (cut) {
                              update_syntax_and_lines(b, (line_desc *)ld->ld_node.next, b->cur_line_desc);
                              goto_line(b, min(b->block_start_line, b->cur_line));
                              goto_column(b, min(b->block_start_col, b->win_x + b->cur_x));
                              end_undo_chain(b);
                        }
                        return OK;
                  }

                  if (!(new_cd = realloc_clip_desc(cd, n, clip_len))) return OUT_OF_MEMORY;
                  if (!cd) add_head(&clips, &new_cd->cd_node);
                  cd = new_cd;
                  p = cd->cs->stream + clip_len;
            }
      }
      else {

            for(pass = clip_len = 0; pass < 2; pass++) {

                  ld = b->cur_line_desc;

                  for(i = y; i <= b->block_start_line; i++) {

                        start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
                        len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding) - start_pos;

                        if (pass) {
                              if (len) memcpy(p, ld->line + start_pos, len);
                              p += len;
                              *(p++) = 0;
                              if (cut) delete_stream(b, ld, i, start_pos, len);
                        }

                        else clip_len += len + 1;
                        ld = (line_desc *)ld->ld_node.next;
                  }

                  if (pass) {
                        cd->cs->len = clip_len;
                        set_stream_encoding(cd->cs, b->encoding);
                        assert_clip_desc(cd);
                        if (cut) {
                              update_syntax_and_lines(b, b->cur_line_desc, (line_desc *)ld->ld_node.prev);
                              goto_line(b, min(b->block_start_line, b->cur_line));
                              goto_column(b, min(b->block_start_col, b->win_x + b->cur_x));
                              end_undo_chain(b);
                        }
                        return OK;
                  }

                  if (!(new_cd = realloc_clip_desc(cd, n, clip_len))) return OUT_OF_MEMORY;
                  if (!cd) add_head(&clips, &new_cd->cd_node);
                  cd = new_cd;
                  p = cd->cs->stream;
            }
      }

      if (cut) end_undo_chain(b);
      return OK;
}

/* Simply erases a vertical block, without putting it in a clip. Calls update_syntax_and_lines(). */

int erase_vert_block(buffer *b) {

      int i, start_pos, len, y = b->cur_line, start_x, end_x;
      line_desc *ld = b->cur_line_desc;

      if (!b->marking) return MARK_BLOCK_FIRST;
      if (b->block_start_line >= b->num_lines) return MARK_OUT_OF_BUFFER;

      if (b->win_x + b->cur_x == b->block_start_col ||
            y == b->block_start_line && b->cur_pos >= ld->line_len && b->block_start_col >= calc_width(ld, ld->line_len, b->opt.tab_size, b->encoding)) 
            return OK;

      start_x = b->block_start_col;
      end_x = b->win_x + b->cur_x;

      if (end_x < start_x) {
            start_x = b->win_x + b->cur_x;
            end_x = b->block_start_col + 1;
      }

      start_undo_chain(b);

      if (y > b->block_start_line) {
            for(i = y; i >= b->block_start_line; i--) {
                  start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
                  len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding) - start_pos;
                  delete_stream(b, ld, i, start_pos, len);
                  ld = (line_desc *)ld->ld_node.prev;
            }
            update_syntax_and_lines(b, (line_desc *)ld->ld_node.next, b->cur_line_desc);
      }
      else {
            for(i = y; i <= b->block_start_line; i++) {
                  start_pos = calc_pos(ld, start_x, b->opt.tab_size, b->encoding);
                  len = calc_pos(ld, end_x, b->opt.tab_size, b->encoding)-start_pos;
                  delete_stream(b, ld, i, start_pos, len);
                  ld = (line_desc *)ld->ld_node.next;
            }
            update_syntax_and_lines(b, b->cur_line_desc, (line_desc *)ld->ld_node.prev);
      }

      end_undo_chain(b);

      goto_line(b, min(b->block_start_line, b->cur_line));
      goto_column(b, min(b->block_start_col, b->win_x + b->cur_x));

      return OK;
}



/* Performs a vertical paste. It has to be done via an insert_stream() for each
      string of the clip. Again, the undo linking feature makes all these
      operations a single undo step. */

int paste_vert_to_buffer(buffer *b, int n) {

      int len, stream_len, i, x, line;
      unsigned char *p;
      clip_desc *cd;
      line_desc * ld = b->cur_line_desc;
      unsigned char *stream;

      if (!(cd = get_nth_clip(n))) return CLIP_DOESNT_EXIST;

      if (!cd->cs->len) return OK;

      if (cd->cs->encoding != ENC_ASCII && b->encoding != ENC_ASCII && cd->cs->encoding != b->encoding) return INCOMPATIBLE_CLIP_ENCODING;
      if (b->encoding == ENC_ASCII) b->encoding = cd->cs->encoding;

      p = stream = cd->cs->stream;
      stream_len = cd->cs->len;
      line = b->cur_line;
      x = b->cur_x + b->win_x;

      start_undo_chain(b);

      while(p - stream < stream_len) {
            if (!ld->ld_node.next) {
                  insert_one_line(b, (line_desc *)ld->ld_node.prev, line - 1, ((line_desc *)ld->ld_node.prev)->line_len);
                        ld = (line_desc *)ld->ld_node.prev;
            }

            if (len = strnlen_ne(p, stream_len - (p - stream))) {
                  for(n = i=0; i < ld->line_len && n < x; i = next_pos(ld->line, i, b->encoding)) {
                        if (ld->line[i] == '\t') n += b->opt.tab_size - n % b->opt.tab_size;
                        else n += get_char_width(&ld->line[i], b->encoding);
                  }
                  
                  if (i == ld->line_len && n < x) {
                        /* We miss x - n characters after the end of the line. */
                        insert_spaces(b, ld, line, ld->line_len, x - n);
                        insert_stream(b, ld, line, ld->line_len, p, len);
                  }
                  else insert_stream(b, ld, line, i, p, len);
            }
                  
            p += len + 1;
            ld = (line_desc *)ld->ld_node.next;
            line++;
      }
            
      end_undo_chain(b);
      update_syntax_and_lines(b, b->cur_line_desc, ld);           
      return OK;
}


/* Loads a clip. It is just a load_stream, plus an insertion in the clip
      list. If preserve_cr is TRUE, CRs are preserved. */

int load_clip(int n, const char *name, const int preserve_cr, const int binary) {
      int error;

      clip_desc *cd = get_nth_clip(n);

      if (!cd) {
            if (!(cd = alloc_clip_desc(n, 0))) return OUT_OF_MEMORY;
            add_head(&clips, &cd->cd_node);
      }

      error = load_stream(cd->cs, name, preserve_cr, binary) ? OK : CANT_OPEN_FILE;

      if (error == OK) set_stream_encoding(cd->cs, ENC_ASCII);

      return error;
}


/* Saves a clip to a file. If CRLF is true, the clip is saved with CR/LF pairs
 as line terminators. */

int save_clip(int n, const char *name, const int CRLF, const int binary) {

      clip_desc *cd = get_nth_clip(n);

      if (!cd) return CLIP_DOESNT_EXIST;
      return save_stream(cd->cs, name, CRLF, binary);
}

Generated by  Doxygen 1.6.0   Back to index