source: editwin.c @ 39cff48

release-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 39cff48 was 8459609, checked in by Nelson Elhage <nelhage@mit.edu>, 15 years ago
editwin: Properly update points inside a replaced region. If the point is inside a region that is replaced, the correct thing to do is to send it to the new end, not the old end. Moreover, if the region shrunk and was near the end of the buffer, the old behavior would even result in e->index > e->bufflen, which could result in a crash.
  • Property mode set to 100644
File size: 30.7 KB
Line 
1#include "owl.h"
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
5#include <ctype.h>
6
7#define VALID_EXCURSION (0x9a2b4729)
8
9typedef struct _owl_editwin_excursion { /*noproto*/
10  int valid;
11  int index;
12  int mark;
13  int goal_column;
14  int lock;
15  struct _owl_editwin_excursion *next;
16} oe_excursion;
17
18struct _owl_editwin { /*noproto*/
19  char *buff;
20  owl_history *hist;
21  int bufflen;
22  int allocated;
23  int index;
24  int mark;
25  char *killbuf;
26  int goal_column;
27  int topindex;
28  int cursorx;
29  int winlines, wincols, fillcol, wrapcol;
30  WINDOW *curswin;
31  int style;
32  int lock;
33  int dotsend;
34  int echochar;
35  oe_excursion *excursions;
36
37  char *command;
38  void (*callback)(struct _owl_editwin*);
39  void *cbdata;
40};
41
42static void oe_reframe(owl_editwin *e);
43static void oe_save_excursion(owl_editwin *e, oe_excursion *x);
44static void oe_release_excursion(owl_editwin *e, oe_excursion *x);
45static void oe_restore_excursion(owl_editwin *e, oe_excursion *x);
46static void oe_restore_mark_only(owl_editwin *e, oe_excursion *x);
47static int oe_count_glyphs(const char *s);
48static int oe_char_width(gunichar c, int column);
49static int oe_region_width(owl_editwin *e, int start, int end, int width);
50static int oe_find_display_line(owl_editwin *e, int *x, int index);
51static void oe_insert_char(owl_editwin *e, gunichar c);
52static int owl_editwin_limit_maxcols(int v, int maxv);
53static int owl_editwin_check_dotsend(owl_editwin *e);
54static int owl_editwin_is_char_in(owl_editwin *e, const char *set);
55static gunichar owl_editwin_get_char_at_point(owl_editwin *e);
56static int owl_editwin_replace_internal(owl_editwin *e, int replace, const char *s);
57static const char *oe_copy_buf(owl_editwin *e, const char *buf, int len);
58static int oe_copy_region(owl_editwin *e);
59static char *oe_chunk(owl_editwin *e, int start, int end);
60
61#define INCR 4096
62
63#define WHITESPACE " \n\t"
64
65owl_editwin *owl_editwin_allocate(void)
66{
67  owl_editwin *e;
68  e = owl_malloc(sizeof(owl_editwin));
69  memset(e, 0, sizeof(*e));
70  return e;
71}
72
73static int oe_count_glyphs(const char *s)
74{
75  int count = 0;
76  const char *p;
77
78  for(p = s; *p != 0; p = g_utf8_find_next_char(p, NULL))
79    if (!g_unichar_ismark(g_utf8_get_char(p)))
80      count++;
81
82  return count;
83}
84
85static inline void oe_set_index(owl_editwin *e, int index)
86{
87  if (index != e->index) {
88    e->goal_column = -1;
89    e->cursorx = -1;
90  }
91  e->index = index;
92}
93
94static inline void oe_set_mark(owl_editwin *e, int mark)
95{
96  e->mark = mark;
97}
98
99void owl_editwin_set_mark(owl_editwin *e)
100{
101  oe_set_mark(e, e->index);
102  /* owl_function_makemsg("Mark set."); */
103}
104
105/* initialize the editwin e.
106 * 'win' is an already initialzed curses window that will be used by editwin
107 */
108void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style, owl_history *hist)
109{
110  e->buff=owl_malloc(INCR);
111  e->buff[0]='\0';
112  e->bufflen=0;
113  e->hist=hist;
114  e->allocated=INCR;
115  oe_set_index(e, 0);
116  oe_set_mark(e, -1);
117  if (e->killbuf != NULL)
118    owl_free(e->killbuf);
119  e->killbuf = NULL;
120  e->goal_column = -1;
121  e->cursorx = -1;
122  e->topindex = 0;
123  e->excursions = NULL;
124  owl_editwin_set_curswin(e, win, winlines, wincols);
125  e->style=style;
126  if ((style!=OWL_EDITWIN_STYLE_MULTILINE) &&
127      (style!=OWL_EDITWIN_STYLE_ONELINE)) {
128    e->style=OWL_EDITWIN_STYLE_MULTILINE;
129  }
130  e->lock=0;
131  e->dotsend=0;
132  e->echochar='\0';
133
134  /* We get initialized multiple times, but we need to hold on to
135     the callbacks, so we can't NULL them here. */
136  /*
137    e->command = NULL;
138    e->callback = NULL;
139    e->cbdata = NULL;
140  */
141  if (win) werase(win);
142}
143
144void owl_editwin_set_curswin(owl_editwin *e, WINDOW *w, int winlines, int wincols)
145{
146  e->curswin=w;
147  e->winlines=winlines;
148  e->wincols=wincols;
149  e->fillcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxfillcols(&g));
150  e->wrapcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxwrapcols(&g));
151}
152
153/* echo the character 'ch' for each normal character keystroke,
154 * excepting locktext.  This is useful for entering passwords etc.  If
155 * ch=='\0' characters are echo'd normally
156 */
157void owl_editwin_set_echochar(owl_editwin *e, int ch)
158{
159  e->echochar=ch;
160}
161
162WINDOW *owl_editwin_get_curswin(owl_editwin *e)
163{
164  return(e->curswin);
165}
166
167owl_history *owl_editwin_get_history(owl_editwin *e)
168{
169  return(e->hist);
170}
171
172void owl_editwin_set_dotsend(owl_editwin *e)
173{
174  e->dotsend=1;
175}
176
177void owl_editwin_set_command(owl_editwin *e, const char *command)
178{
179  if(e->command) owl_free(e->command);
180  e->command = owl_strdup(command);
181}
182
183const char *owl_editwin_get_command(owl_editwin *e)
184{
185  if(e->command) return e->command;
186  return "";
187}
188
189void owl_editwin_set_callback(owl_editwin *e, void (*cb)(owl_editwin*))
190{
191  e->callback = cb;
192}
193
194void (*owl_editwin_get_callback(owl_editwin *e))(owl_editwin*)
195{
196  return e->callback;
197}
198
199void owl_editwin_set_cbdata(owl_editwin *e, void *data)
200{
201  e->cbdata = data;
202}
203
204void *owl_editwin_get_cbdata(owl_editwin *e) {
205  return e->cbdata;
206}
207
208void owl_editwin_do_callback(owl_editwin *e) {
209  void (*cb)(owl_editwin*);
210  cb=owl_editwin_get_callback(e);
211  if(!cb) {
212    owl_function_error("Internal error: No editwin callback!");
213  } else {
214    /* owl_function_error("text: |%s|", owl_editwin_get_text(e)); */
215    cb(e);
216  }
217}
218
219static int owl_editwin_limit_maxcols(int v, int maxv)
220{
221  /* maxv > 5 ? MAX(v, vax) : v */
222  if (maxv > 5 && v > maxv) {
223    return(maxv);
224  } else {
225    return(v);
226  }
227}
228
229/* set text to be 'locked in' at the beginning of the buffer, any
230 * previous text (including locked text) will be overwritten
231 */
232void owl_editwin_set_locktext(owl_editwin *e, const char *text)
233{
234  oe_set_index(e, 0);
235  e->lock = 0;
236  owl_editwin_replace(e, e->bufflen, text);
237  e->buff[e->bufflen] = 0;
238  e->lock=e->bufflen;
239  oe_set_index(e, e->lock);
240  owl_editwin_redisplay(e, 0);
241}
242
243int owl_editwin_get_style(owl_editwin *e)
244{
245  return(e->style);
246}
247
248void owl_editwin_new_style(owl_editwin *e, int newstyle, owl_history *h)
249{
250  e->hist = h;
251
252  if (e->style==newstyle) return;
253
254  if (newstyle==OWL_EDITWIN_STYLE_MULTILINE) {
255    e->style=newstyle;
256  } else if (newstyle==OWL_EDITWIN_STYLE_ONELINE) {
257    e->style=newstyle;
258
259    /* nuke everything after the first line */
260    owl_editwin_move_to_top(e);
261    owl_editwin_move_to_end_of_line(e);
262    owl_editwin_replace(e, oe_count_glyphs(e->buff + e->index),  "");
263  }
264}
265
266/* completly reinitialize the buffer */
267void owl_editwin_fullclear(owl_editwin *e)
268{
269  owl_free(e->buff);
270  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
271}
272
273/* clear all text except for locktext and put the cursor at the
274 * beginning
275 */
276void owl_editwin_clear(owl_editwin *e)
277{
278
279  int lock = e->lock;
280  int dotsend=e->dotsend;
281  char *locktext=NULL;
282  char echochar=e->echochar;
283
284  if (lock > 0) {
285    locktext = owl_malloc(lock+1);
286    strncpy(locktext, e->buff, lock);
287    locktext[lock] = 0;
288  }
289
290  owl_free(e->buff);
291  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
292
293  if (lock > 0) {
294    owl_editwin_set_locktext(e, locktext);
295  }
296  if (dotsend) {
297    owl_editwin_set_dotsend(e);
298  }
299  if (echochar) {
300    owl_editwin_set_echochar(e, echochar);
301  }
302
303  if (locktext)
304    owl_free(locktext);
305
306  oe_set_index(e, lock);
307}
308
309void owl_editwin_recenter(owl_editwin *e)
310{
311  e->topindex = -1;
312}
313
314static void oe_save_excursion(owl_editwin *e, oe_excursion *x)
315{
316  x->index = e->index;
317  x->mark = e->mark;
318  x->goal_column = e->goal_column;
319  x->lock = e->lock;
320
321  x->valid = VALID_EXCURSION;
322  x->next = e->excursions;
323  e->excursions = x;
324}
325
326static void oe_release_excursion(owl_editwin *e, oe_excursion *x)
327{
328  oe_excursion *p;
329
330  x->valid = 0;
331  if (e->excursions == NULL)
332    /* XXX huh. */ ;
333  else if (e->excursions == x)
334    e->excursions = x->next;
335  else {
336    for (p = e->excursions; p->next != NULL; p = p->next)
337      if (p->next == x) {
338        p->next = p->next->next;
339        break;
340      }
341    /* and if we ran off the end? XXX */
342  }
343}
344
345static void oe_restore_excursion(owl_editwin *e, oe_excursion *x)
346{
347  if (x->valid == VALID_EXCURSION) {
348    oe_set_index(e, x->index);
349    e->goal_column = x->goal_column;
350    e->mark = x->mark;
351    e->lock = x->lock;
352
353    oe_release_excursion(e, x);
354  }
355}
356
357static void oe_restore_mark_only(owl_editwin *e, oe_excursion *x)
358{
359  if (x->valid == VALID_EXCURSION) {
360    e->mark = x->mark;
361
362    oe_release_excursion(e, x);
363  }
364}
365
366/* External interface to oe_save_excursion */
367owl_editwin_excursion *owl_editwin_begin_excursion(owl_editwin *e)
368{
369  owl_editwin_excursion *x = owl_malloc(sizeof *x);
370  oe_save_excursion(e, x);
371  return x;
372}
373
374void owl_editwin_end_excursion(owl_editwin *e, owl_editwin_excursion *x)
375{
376  oe_restore_excursion(e, x);
377  owl_free(x);
378}
379
380static inline const char *oe_next_point(owl_editwin *e, const char *p)
381{
382  const char *boundary = e->buff + e->bufflen + 1;
383  const char *q;
384
385  q = g_utf8_find_next_char(p, boundary);
386  while (q && g_unichar_ismark(g_utf8_get_char(q)))
387    q = g_utf8_find_next_char(q, boundary);
388
389  if (q == p)
390    return NULL;
391  return q;
392}
393
394static inline const char *oe_prev_point(owl_editwin *e, const char *p)
395{
396  const char *boundary = e->buff + e->lock;
397
398  p = g_utf8_find_prev_char(boundary, p);
399  while (p && g_unichar_ismark(g_utf8_get_char(p)))
400    p = g_utf8_find_prev_char(boundary, p);
401
402  return p;
403}
404
405static int oe_char_width(gunichar c, int column)
406{
407  int cw;
408
409  if (c == 9) /* TAB */
410    return TABSIZE - column % TABSIZE;
411
412  cw = mk_wcwidth(c);
413
414  if (cw < 0) /* control characters */
415    cw = 0;
416
417  return cw;
418}
419
420static int oe_find_display_line(owl_editwin *e, int *x, int index)
421{
422  int width = 0, cw;
423  gunichar c;
424  const char *p;
425
426  while(1) {
427    /* note the position of the dot */
428    if (x != NULL && index == e->index && width < e->wincols)
429      *x = width;
430
431    /* get the current character */
432    c = g_utf8_get_char(e->buff + index);
433
434    /* figure out how wide it is */
435    cw = oe_char_width(c, width);
436
437    if (width + cw > e->wincols) {
438      if (x != NULL && *x == width)
439        *x = -1;
440      break;
441    }
442    width += cw;
443
444    if (c == '\n') {
445      if (width < e->wincols)
446        ++index; /* skip the newline */
447      break;
448    }
449
450    /* find the next character */
451    p = oe_next_point(e, e->buff + index);
452    if (p == NULL) { /* we ran off the end */
453      if (x != NULL && e->index > index)
454        *x = width + 1;
455      break;
456    }
457    index = p - e->buff;
458
459  }
460  return index;
461}
462
463static void oe_reframe(owl_editwin *e) {
464  oe_excursion x;
465  int goal = e->winlines / 2;
466  int index;
467  int count = 0;
468  int point;
469  int n, i;
470  int last;
471
472  oe_save_excursion(e, &x);
473  /* step back line-by-line through the buffer until we have >= goal lines of
474     display text */
475  e->lock = 0; /* we can (must) tread on the locktext */
476
477  point = e->index;
478  last = -1;
479  while (count < goal) {
480    index = e->index;
481    owl_editwin_move_to_beginning_of_line(e);
482    if (last == e->index)
483      break;
484    last = e->index;
485    for (n = 0, i = e->index; i < index; n++)
486      i = oe_find_display_line(e, NULL, i);
487    count += n == 0 ? 1 : n;
488    if (count < goal)
489      owl_editwin_point_move(e, -1);
490  }
491
492  e->topindex = e->index;
493  /* if we overshot, backtrack */
494  for (n = 0; n < (count - goal); n++)
495    e->topindex = oe_find_display_line(e, NULL, e->topindex);
496
497  oe_restore_excursion(e, &x);
498}
499
500static void oe_addnec(owl_editwin *e, int count)
501{
502  int i;
503
504  for (i = 0; i < count; i++)
505    waddch(e->curswin, e->echochar);
506}
507
508static void oe_mvaddnec(owl_editwin *e, int y, int x, int count)
509{
510  wmove(e->curswin, y, x);
511  oe_addnec(e, count);
512}
513
514/* regenerate the text on the curses window */
515/* if update == 1 then do a doupdate(), otherwise do not */
516void owl_editwin_redisplay(owl_editwin *e, int update)
517{
518  int x = -1, y = -1, t;
519  int line, index, lineindex, times = 0;
520
521  do {
522    werase(e->curswin);
523
524    if (e->topindex == -1 || e->index < e->topindex)
525      oe_reframe(e);
526
527    line = 0;
528    index = e->topindex;
529    while(line < e->winlines) {
530      lineindex = index;
531      t = -1;
532      index = oe_find_display_line(e, &t, lineindex);
533      if (x == -1 && t != -1)
534        x = t, y = line;
535      if (index - lineindex) {
536        if (!e->echochar)
537          mvwaddnstr(e->curswin, line, 0,
538                     e->buff + lineindex,
539                     index - lineindex);
540        else {
541          if(lineindex < e->lock) {
542            mvwaddnstr(e->curswin, line, 0,
543                       e->buff + lineindex,
544                       MIN(index - lineindex,
545                           e->lock - lineindex));
546            if (e->lock < index)
547              oe_addnec(e,
548                        oe_region_width(e, e->lock, index,
549                                        oe_region_width(e, lineindex, e->lock, 0)));
550          } else
551            oe_mvaddnec(e, line, 0, oe_region_width(e, line, index, 0));
552        }
553      }
554      line++;
555    }
556    if (x == -1)
557        e->topindex = -1; /* force a reframe */
558    times++;
559  } while(x == -1 && times < 3);
560
561  wmove(e->curswin, y, x);
562  e->cursorx = x;
563
564  wnoutrefresh(e->curswin);
565  if (update == 1)
566    doupdate();
567}
568
569static inline void oe_fixup(int *target, int start, int end, int change) {
570  if (*target > start) {
571    if (*target <= end)
572      *target = end + change;
573    else
574      *target += change;
575  }
576}
577
578int owl_editwin_replace_region(owl_editwin *e, const char *s)
579{
580  oe_excursion x;
581  int ret;
582  oe_save_excursion(e, &x);
583
584  if(e->index > e->mark) {
585    owl_editwin_exchange_point_and_mark(e);
586  }
587
588  ret = owl_editwin_replace_internal(e, e->mark - e->index, s);
589
590  oe_restore_excursion(e, &x);
591
592  return ret;
593}
594
595/* replace 'replace' characters at the point with s, returning the change in size */
596int owl_editwin_replace(owl_editwin *e, int replace, const char *s)
597{
598  int start, end, i;
599  const char *p;
600
601  if (!g_utf8_validate(s, -1, NULL)) {
602    owl_function_debugmsg("owl_editwin_insert_string: received non-utf-8 string.");
603    return 0;
604  }
605
606  start = e->index;
607  for (i = 0, p = e->buff + start; i < replace && p != NULL; i++)
608    p = oe_next_point(e, p);
609  if (p != NULL)
610    end = p - e->buff;
611  else
612    end = e->bufflen;
613
614  return owl_editwin_replace_internal(e, end - start, s);
615}
616
617static int owl_editwin_replace_internal(owl_editwin *e, int replace, const char *s)
618{
619  int start, end, free, need, size, change;
620  oe_excursion *x;
621  char *p;
622
623  start = e->index;
624  end   = start + replace;
625
626  free = e->allocated - e->bufflen + end - start;
627
628  need = strlen(s) - free;
629  if (need > 0) {
630    size = e->allocated + need + INCR - (need % INCR);
631    p = owl_realloc(e->buff, size);
632    if (p == NULL) {
633      /* XXX signal impending doom somehow and don't do anything */
634      return 0;
635    }
636    e->buff = p;
637    e->allocated = size;
638  }
639
640  memmove(e->buff + start + strlen(s), e->buff + end, e->bufflen + 1 - end);
641  memcpy(e->buff + start, s, strlen(s));
642  change = start - end + strlen(s);
643  e->bufflen += change;
644  e->index += strlen(s);
645
646  /* fix up the mark */
647  oe_fixup(&e->mark, start, end, change);
648  oe_fixup(&e->topindex, start, end, change);
649  /* fix up any saved points after the replaced area */
650  for (x = e->excursions; x != NULL; x = x->next) {
651    oe_fixup(&x->index, start, end, change);
652    oe_fixup(&x->mark, start, end, change);
653  }
654
655  return change;
656}
657
658/* linewrap the word just before the cursor.
659 * returns 0 on success
660 * returns -1 if we could not wrap.
661 */
662static void _owl_editwin_linewrap_word(owl_editwin *e)
663{
664  oe_excursion x;
665  gunichar c;
666
667  oe_save_excursion(e, &x);
668
669  while (owl_editwin_point_move(e, -1)) {
670    c = owl_editwin_get_char_at_point(e);
671    if (owl_util_can_break_after(c) || c == '\n') {
672      if (c != '\n')
673        owl_editwin_replace(e, c != ' ' ? 0 : 1, "\n");
674      break;
675    }
676  }
677
678  oe_restore_excursion(e, &x);
679}
680
681/* delete the character at the current point, following chars
682 * shift left.
683 */
684void owl_editwin_delete_char(owl_editwin *e)
685{
686  owl_editwin_replace(e, 1, "");
687}
688
689/* Swap the character at point with the character at point-1 and
690 * advance the pointer.  If point is at beginning of buffer do
691 * nothing.  If point is after the last character swap point-1 with
692 * point-2.  (Behaves as observed in tcsh and emacs).
693 */
694void owl_editwin_transpose_chars(owl_editwin *e)
695{
696  const char *middle, *end, *start;
697  char *tmp;
698
699  if (e->bufflen == 0) return;
700
701  if (e->index == e->bufflen)
702    owl_editwin_point_move(e, -1);     /* point is after last character */
703
704  if (owl_editwin_at_beginning_of_buffer(e))
705    return;     /* point is at beginning of buffer, do nothing */
706
707  /* Transpose two utf-8 unicode glyphs. */
708  middle = e->buff + e->index;
709
710  end = oe_next_point(e, middle);
711  if (end == NULL)
712    return;
713
714  start = oe_prev_point(e, middle);
715  if (start == NULL)
716    return;
717
718  tmp = owl_malloc((end - start) + 1);
719  tmp[(end - start)] = 0;
720  memcpy(tmp, middle, end - middle);
721  memcpy(tmp + (end - middle), start, middle - start);
722
723  owl_editwin_point_move(e, -1);
724  owl_editwin_replace(e, 2, tmp);
725}
726
727/* insert 'string' at the current point, later text is shifted
728 * right
729 */
730void owl_editwin_insert_string(owl_editwin *e, const char *s)
731{
732  owl_editwin_replace(e, 0, s);
733}
734
735/* We assume index is not set to point to a mid-char */
736static gunichar owl_editwin_get_char_at_point(owl_editwin *e)
737{
738  return g_utf8_get_char(e->buff + e->index);
739}
740
741void owl_editwin_exchange_point_and_mark(owl_editwin *e) {
742  int tmp;
743
744  if (e->mark != -1) {
745    tmp = e->mark;
746    owl_editwin_set_mark(e);
747    oe_set_index(e, tmp);
748  }
749}
750
751int owl_editwin_point_move(owl_editwin *e, int delta)
752{
753  const char *p;
754  int change, d = 0;
755
756  change = MAX(delta, - delta);
757  p = e->buff + e->index;
758
759  while (d < change && p != NULL) {
760    if (delta > 0)
761      p = oe_next_point(e, p);
762    else
763      p = oe_prev_point(e, p);
764    if (p != NULL) {
765      oe_set_index(e, p - e->buff);
766      d++;
767    }
768  }
769
770  return delta > 0 ? d : -d;
771}
772
773int owl_editwin_at_beginning_of_buffer(owl_editwin *e) {
774  if (e->index == e->lock)
775    return 1;
776
777  return 0;
778}
779
780int owl_at_end_of_buffer(owl_editwin *e) {
781  if (e->index == e->bufflen)
782    return 1;
783
784  return 0;
785}
786
787int owl_editwin_at_beginning_of_line(owl_editwin *e) /*noproto*/
788{
789  oe_excursion x;
790  int ret;
791
792  if (owl_editwin_at_beginning_of_buffer(e))
793    return 1;
794
795  oe_save_excursion(e, &x);
796  owl_editwin_point_move(e, -1);
797  ret = (owl_editwin_get_char_at_point(e) == '\n');
798  oe_restore_excursion(e, &x);
799
800  return ret;
801}
802
803static int owl_editwin_is_char_in(owl_editwin *e, const char *set)
804{
805  const char *p;
806
807  for (p = set; *p != 0; p = g_utf8_find_next_char(p, NULL))
808    if (owl_editwin_get_char_at_point(e) == g_utf8_get_char(p))
809      return 1;
810  return 0;
811}
812
813int owl_editwin_move_if_in(owl_editwin *e, int delta, const char *set)
814{
815  int change, distance = 0;
816  while (owl_editwin_is_char_in(e, set)) {
817    change = owl_editwin_point_move(e, delta);
818    distance += change;
819    if (change == 0)
820      break;
821  }
822  return distance;
823}
824
825int owl_editwin_move_if_not_in(owl_editwin *e, int delta, const char *set)
826{
827  int change, distance = 0;
828  while (!owl_editwin_is_char_in(e, set)) {
829    change = owl_editwin_point_move(e, delta);
830    distance += change;
831    if (change == 0)
832      break;
833  }
834  return distance;
835}
836
837int owl_editwin_move_to_beginning_of_line(owl_editwin *e)
838{
839  int distance = 0;
840
841  if (!owl_editwin_at_beginning_of_line(e)) {
842    /* move off the \n if were at the end of a line */
843    distance += owl_editwin_point_move(e, -1);
844    distance += owl_editwin_move_if_not_in(e, -1, "\n");
845    if (distance && !owl_editwin_at_beginning_of_buffer(e))
846      distance += owl_editwin_point_move(e, 1);
847  }
848  e->goal_column = 0; /* subtleties */
849
850  return distance;
851}
852
853int owl_editwin_move_to_end_of_line(owl_editwin *e)
854{
855  return owl_editwin_move_if_not_in(e, 1, "\n");
856}
857
858int owl_editwin_line_move(owl_editwin *e, int delta)
859{
860  int goal_column, change, ll, distance;
861  int count = 0;
862
863  change = MAX(delta, -delta);
864
865  goal_column = e->goal_column;
866  distance = owl_editwin_move_to_beginning_of_line(e);
867  goal_column = goal_column == -1 ? -distance : goal_column;
868
869  while(count < change) {
870    if (delta > 0) {
871      distance += owl_editwin_move_if_not_in(e, 1, "\n");
872      distance += owl_editwin_point_move(e, 1);
873    } else {
874      /* I really want to assert delta < 0 here */
875      distance += owl_editwin_point_move(e, -1); /* to the newline on
876                                                    the previous line */
877      distance += owl_editwin_move_to_beginning_of_line(e);
878    }
879    count++;
880  }
881
882  distance += (ll = owl_editwin_move_to_end_of_line(e));
883  if (ll > goal_column)
884    distance += owl_editwin_point_move(e, goal_column - ll);
885
886  e->goal_column = goal_column;
887
888  return distance;
889}
890
891void owl_editwin_backspace(owl_editwin *e)
892{
893  /* delete the char before the current one
894   * and shift later chars left
895   */
896  if(owl_editwin_point_move(e, -1))
897    owl_editwin_delete_char(e);
898}
899
900void owl_editwin_key_up(owl_editwin *e)
901{
902  owl_editwin_line_move(e, -1);
903}
904
905void owl_editwin_key_down(owl_editwin *e)
906{
907  owl_editwin_line_move(e, 1);
908}
909
910void owl_editwin_key_left(owl_editwin *e)
911{
912  owl_editwin_point_move(e, -1);
913}
914
915void owl_editwin_key_right(owl_editwin *e)
916{
917  owl_editwin_point_move(e, 1);
918}
919
920int owl_editwin_forward_word(owl_editwin *e)
921{
922  int distance;
923  /* if we're starting on a space, find the first non-space */
924  distance = owl_editwin_move_if_in(e, 1, WHITESPACE);
925
926  /* now find the end of this word */
927  distance += owl_editwin_move_if_not_in(e, 1, WHITESPACE);
928
929  return distance;
930}
931
932void owl_editwin_move_to_nextword(owl_editwin *e)
933{
934  owl_editwin_forward_word(e);
935}
936
937/* go backwards to the last non-space character
938 */
939int owl_editwin_backward_word(owl_editwin *e)
940{
941  oe_excursion x;
942  int distance = 0;
943  int further = 0;
944  int beginning;
945  /* if in middle of word, beginning of word */
946
947  /* if at beginning of a word, find beginning of previous word */
948
949  if (owl_editwin_is_char_in(e, WHITESPACE)) {
950    /* if in whitespace past end of word, find a word , the find the beginning*/
951    distance += owl_editwin_move_if_in(e, -1, WHITESPACE); /* leaves us on the last
952                                                              character of the word */
953    oe_save_excursion(e, &x);
954    /* are we at the beginning of a word? */
955    owl_editwin_point_move(e, -1);
956    beginning = owl_editwin_is_char_in(e, WHITESPACE);
957    oe_restore_excursion(e, &x);
958    if (beginning)
959      return distance;
960   } else {
961    /* in the middle of the word; */
962    oe_save_excursion(e, &x);
963    further += owl_editwin_point_move(e, -1);
964    if (owl_editwin_is_char_in(e, WHITESPACE)) { /* we were at the beginning */
965      distance += owl_editwin_backward_word(e); /* previous case */
966      oe_release_excursion(e, &x);
967      return distance + further;
968    } else {
969      oe_restore_excursion(e, &x);
970    }
971  }
972  distance += owl_editwin_move_if_not_in(e, -1, WHITESPACE);
973  /* will go past */
974  if (e->index > e->lock)
975    distance += owl_editwin_point_move(e, 1);
976  return distance;
977}
978
979void owl_editwin_move_to_previousword(owl_editwin *e)
980{
981  owl_editwin_backward_word(e);
982}
983
984void owl_editwin_delete_nextword(owl_editwin *e)
985{
986  oe_excursion x;
987
988  oe_save_excursion(e, &x);
989  oe_set_mark(e, e->index);
990  owl_editwin_forward_word(e);
991  owl_editwin_kill_region(e);
992  oe_restore_mark_only(e, &x);
993}
994
995void owl_editwin_delete_previousword(owl_editwin *e)
996{
997  oe_excursion x;
998
999  oe_save_excursion(e, &x);
1000  oe_set_mark(e, e->index);
1001  owl_editwin_backward_word(e);
1002  owl_editwin_kill_region(e);
1003  oe_restore_mark_only(e, &x);
1004}
1005
1006void owl_editwin_move_to_line_end(owl_editwin *e)
1007{
1008  owl_editwin_move_to_end_of_line(e);
1009}
1010
1011void owl_editwin_delete_to_endofline(owl_editwin *e)
1012{
1013  oe_excursion x;
1014  int distance;
1015
1016  oe_save_excursion(e, &x);
1017  owl_editwin_set_mark(e);
1018  distance = owl_editwin_move_to_end_of_line(e);
1019  if (distance)
1020    owl_editwin_kill_region(e);
1021  else
1022    owl_editwin_replace(e, 1, "");
1023  oe_restore_excursion(e, &x);
1024}
1025
1026void owl_editwin_yank(owl_editwin *e)
1027{
1028  if (e->killbuf != NULL)
1029    owl_editwin_replace(e, 0, e->killbuf);
1030}
1031
1032static const char *oe_copy_buf(owl_editwin *e, const char *buf, int len)
1033{
1034  char *p;
1035
1036  p = owl_malloc(len + 1);
1037
1038  if (p != NULL) {
1039    owl_free(e->killbuf);
1040    e->killbuf = p;
1041    memcpy(e->killbuf, buf, len);
1042    e->killbuf[len] = 0;
1043  }
1044
1045  return p;
1046}
1047
1048static int oe_copy_region(owl_editwin *e)
1049{
1050  const char *p;
1051  int start, end;
1052
1053  if (e->mark == -1)
1054    return 0;
1055
1056  start = MIN(e->index, e->mark);
1057  end = MAX(e->index, e->mark);
1058
1059  p = oe_copy_buf(e, e->buff + start, end - start);
1060  if (p != NULL)
1061    return end - start;
1062  return 0;
1063}
1064
1065void owl_editwin_copy_region_as_kill(owl_editwin *e)
1066{
1067  oe_copy_region(e);
1068}
1069
1070void owl_editwin_kill_region(owl_editwin *e)
1071{
1072  if (e->index > e->mark)
1073    owl_editwin_exchange_point_and_mark(e);
1074
1075  owl_editwin_replace(e, oe_copy_region(e), "");
1076}
1077
1078void owl_editwin_move_to_line_start(owl_editwin *e)
1079{
1080  owl_editwin_move_to_beginning_of_line(e);
1081}
1082
1083void owl_editwin_move_to_end(owl_editwin *e)
1084{
1085  oe_set_index(e, e->bufflen);
1086}
1087
1088void owl_editwin_move_to_top(owl_editwin *e)
1089{
1090  oe_set_index(e, e->lock);
1091}
1092
1093void owl_editwin_backward_paragraph(owl_editwin *e)
1094{
1095  owl_editwin_point_move(e, -1);
1096  for (; e->index >= e->lock; owl_editwin_point_move(e, -1)) {
1097    if (e->index <= e->lock ||
1098        ((e->buff[e->index] == '\n') && (e->buff[e->index - 1]=='\n')))
1099      break;
1100  }
1101}
1102
1103void owl_editwin_forward_paragraph(owl_editwin *e)
1104{
1105  owl_editwin_point_move(e, 1);
1106  /* scan forward to the start of the next paragraph */
1107  for(; e->index < e->bufflen; owl_editwin_point_move(e, 1)) {
1108    if (e->buff[e->index -1] == '\n' && e->buff[e->index] == '\n')
1109      break;
1110  }
1111}
1112
1113int owl_editwin_current_column(owl_editwin *e)
1114{
1115  oe_excursion x;
1116  int lineindex;
1117
1118  oe_save_excursion(e, &x);
1119  owl_editwin_move_to_beginning_of_line(e);
1120  lineindex = e->index;
1121  oe_restore_excursion(e, &x);
1122  return oe_region_width(e, lineindex, e->index, 0);
1123}
1124
1125void owl_editwin_fill_paragraph(owl_editwin *e)
1126{
1127  oe_excursion x;
1128  gunichar ch;
1129  int sentence;
1130
1131  oe_save_excursion(e, &x);
1132
1133  /* Mark the end of the paragraph */
1134  owl_editwin_forward_paragraph(e);
1135  /* Skip the trailing newline */
1136  owl_editwin_point_move(e, -1);
1137  owl_editwin_set_mark(e);
1138
1139  owl_editwin_backward_paragraph(e);
1140
1141  /* Don't mess with the leading newline */
1142  if (owl_editwin_get_char_at_point(e) == '\n')
1143    owl_editwin_point_move(e, 1);
1144
1145  /*
1146   * First pass: Scan forward replacing all series of spaces with ' '
1147   * (or nothing after CJK ideograms)
1148   */
1149  sentence = 0;
1150  for(;e->index < e->mark; owl_editwin_point_move(e, 1)) {
1151    /* bail if we hit a trailing dot on the buffer */
1152    if (strcmp(e->buff + e->index, "\n.") == 0) {
1153      owl_editwin_set_mark(e);
1154      break;
1155    }
1156
1157    ch = owl_editwin_get_char_at_point(e);
1158
1159    if (owl_util_can_break_after(ch) || ch == '\n') {
1160      if (g_unichar_isspace(ch)) {
1161        owl_editwin_replace(e, 1, " ");
1162      }
1163
1164      if (sentence && g_unichar_isspace(owl_editwin_get_char_at_point(e)))
1165        owl_editwin_point_move(e, 1);
1166
1167      while(g_unichar_isspace(owl_editwin_get_char_at_point(e))
1168            && e->index < e->mark) {
1169        owl_editwin_delete_char(e);
1170      }
1171    }
1172
1173    if(ch == '.' || ch == '!' || ch == '?')
1174      sentence = 1;
1175    else
1176      sentence = 0;
1177  }
1178
1179  owl_editwin_backward_paragraph(e);
1180
1181  /* Now go through inserting newlines as needed */
1182  while(e->index < e->mark) {
1183    /* if we've travelled too far, linewrap */
1184    if (owl_editwin_current_column(e) >= e->fillcol)
1185      _owl_editwin_linewrap_word(e);
1186    owl_editwin_point_move(e, 1);
1187  }
1188
1189  oe_restore_excursion(e, &x);
1190}
1191
1192/* returns true if only whitespace remains */
1193int owl_editwin_is_at_end(owl_editwin *e)
1194{
1195  return (only_whitespace(e->buff + e->index));
1196}
1197
1198static int owl_editwin_check_dotsend(owl_editwin *e)
1199{
1200  int zdot = 0;
1201  oe_excursion x;
1202
1203  if (!e->dotsend) return(0);
1204  if (!owl_editwin_is_at_end(e)) return (0);
1205
1206  oe_save_excursion(e, &x);
1207
1208  owl_editwin_point_move(e, -3);
1209
1210  if(strncmp(e->buff + e->index, "\n.\n", 3) == 0) {
1211    owl_editwin_point_move(e, 1);
1212    zdot = 1;
1213  } else if(e->index == e->lock &&
1214            strncmp(e->buff + e->index, ".\n", 2) == 0) {
1215    zdot = 1;
1216  }
1217
1218  if(zdot) {
1219    owl_editwin_set_mark(e);
1220    owl_editwin_move_to_end(e);
1221    owl_editwin_replace_region(e, "");
1222  }
1223
1224  oe_restore_excursion(e, &x);
1225
1226  return zdot;
1227}
1228
1229void owl_editwin_post_process_char(owl_editwin *e, owl_input j)
1230{
1231  /* XXX force a redisplay? */
1232  if ((j.ch==13 || j.ch==10) && owl_editwin_check_dotsend(e)) {
1233    owl_command_edit_done(e);
1234    return;
1235  }
1236  owl_editwin_redisplay(e, 0);
1237}
1238
1239static int oe_region_width(owl_editwin *e, int start, int end, int offset)
1240{
1241  const char *p;
1242  int width = offset;
1243 
1244  for(p = e->buff + start;
1245      p < e->buff + end;
1246      p = g_utf8_find_next_char(p, NULL))
1247    width += oe_char_width(g_utf8_get_char(p), width);
1248
1249  return width - offset;
1250}
1251
1252static void oe_insert_char(owl_editwin *e, gunichar c)
1253{
1254  oe_excursion x;
1255  char tmp[7];
1256  int replaced = -1;
1257
1258  if (c == '\r') /* translate CRs to NLs */
1259    c = '\n';
1260
1261  if (!g_unichar_iscntrl(c) || c == '\n' || c== '\t' ) {
1262    memset(tmp, 0, 7);
1263
1264    if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) {
1265      return;
1266    }
1267
1268    if (e->cursorx != -1 && e->cursorx + oe_char_width(c, e->cursorx) > e->wrapcol) {
1269      /* XXX this is actually wrong:
1270       * + If the line has been been wrapped, we can be past the wrap column but
1271       *   e->cursorx be much smaller.
1272       * + If the user went back and inserted a bunch of stuff in the middle of
1273       *   the line, there may be more than one word past the wrap column.
1274       */
1275      oe_save_excursion(e, &x);
1276
1277      if (c == ' ' || c == '\t') {
1278        owl_editwin_point_move(e, -1);
1279        replaced = -owl_editwin_move_if_in(e, -1, " \t");
1280        if (!replaced) {
1281          c = '\n';
1282          replaced = -1;
1283        }
1284      } else {
1285        while(!owl_editwin_at_beginning_of_line(e)) {
1286          owl_editwin_point_move(e, -1);
1287          if (owl_util_can_break_after(owl_editwin_get_char_at_point(e))) {
1288            replaced = -owl_editwin_move_if_in(e, -1, " \t");
1289            break;
1290          }
1291        }
1292        if (owl_editwin_at_beginning_of_line(e))
1293          replaced = -1;
1294      }
1295      if (replaced && !owl_editwin_at_beginning_of_line(e))
1296        owl_editwin_point_move(e, 1);
1297      if (replaced >= 0) {
1298        owl_editwin_replace(e, replaced, "\n");
1299      }
1300      oe_restore_excursion(e, &x);
1301    }
1302
1303    if (replaced >= 0 && (c == ' ' || c == '\t'))
1304      return; /* our work here is done */
1305
1306    g_unichar_to_utf8(c, tmp);
1307    owl_editwin_replace(e, 0, tmp);
1308  }
1309}
1310
1311void owl_editwin_process_char(owl_editwin *e, owl_input j)
1312{
1313  if (j.ch == ERR)
1314    return;
1315  /* Ignore ncurses control characters. */
1316  if (j.ch < 0x100) {
1317    oe_insert_char(e, j.uch);
1318  }
1319}
1320
1321const char *owl_editwin_get_text(owl_editwin *e)
1322{
1323  return(e->buff+e->lock);
1324}
1325
1326char *owl_editwin_get_region(owl_editwin *e)
1327{
1328  int start, end;
1329  start = e->index;
1330  end   = e->mark;
1331  if(start > end) {
1332    int tmp = end;
1333    end = start;
1334    start = tmp;
1335  }
1336
1337  return oe_chunk(e, start, end);
1338}
1339
1340int owl_editwin_get_echochar(owl_editwin *e)
1341{
1342  return e->echochar;
1343}
1344
1345static char *oe_chunk(owl_editwin *e, int start, int end)
1346{
1347  char *p;
1348 
1349  p = owl_malloc(end - start + 1);
1350  memcpy(p, e->buff + start, end - start);
1351  p[end - start] = 0;
1352
1353  return p;
1354}
1355
1356/*
1357 * The only guarantee made about these values is that comparisons
1358 * between them, as well as comparison between multiple calls to these
1359 * functions without modifying the editwin in-between, are meaningful.
1360 */
1361
1362int owl_editwin_get_point(owl_editwin *e)
1363{
1364  return e->index;
1365}
1366
1367int owl_editwin_get_mark(owl_editwin *e)
1368{
1369  return e->mark;
1370}
1371
1372
1373/*
1374 * Local Variables:
1375 * mode:C
1376 * c-basic-offset:2
1377 * End:
1378 */
Note: See TracBrowser for help on using the repository browser.