source: editwin.c @ 691d012

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