source: editwin.c @ d427f08

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