source: editwin.c @ bd482c3

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