source: editwin.c @ 460fbe8

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