source: editwin.c @ d2a4534

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