source: editwin.c @ 77d4402

release-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 77d4402 was 77d4402, checked in by Nelson Elhage <nelhage@mit.edu>, 15 years ago
cursor location improved; other behavior The cursor locater now handles wrapped lines /better/ but still messes up when the line is the width of the screen and the cursor is at the end (so it should be a the beginning of a blank screen line) C-a set the goal column kill-to-end-of-line will eat a final newline and thus join.
  • Property mode set to 100644
File size: 25.9 KB
RevLine 
[7d4fbcd]1#include "owl.h"
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
[10b866d]5#include <ctype.h>
[7d4fbcd]6
[1aee7d9]7static const char fileIdent[] = "$Id$";
8
[a556caa]9struct _owl_editwin { /*noproto*/
10  char *buff;
11  owl_history *hist;
12  int bufflen;
13  int allocated;
14  int index;
15  int goal_column;
16  int topindex;
17  int winlines, wincols, fillcol, wrapcol;
18  WINDOW *curswin;
19  int style;
20  int lock;
21  int dotsend;
22  int echochar;
23
24  char *command;
25  void (*callback)(struct _owl_editwin*);
26  void *cbdata;
27};
28
29typedef struct { /*noproto*/
30  int index;
31  int goal_column;
[a0fbdee]32  int lock;
33} oe_excursion;
[a556caa]34
[e20d8179]35static int owl_editwin_is_char_in(owl_editwin *e, char *set);
36static void oe_reframe(owl_editwin *e);
37
[7d4fbcd]38#define INCR 5000
39
[a556caa]40#define WHITESPACE " \n\t"
41
42owl_editwin *owl_editwin_allocate(void) {
43  return owl_malloc(sizeof(owl_editwin));
44}
45
[cf83b7a]46/* initialize the editwin e.
47 * 'win' is an already initialzed curses window that will be used by editwin
48 */
[c9334b1]49void owl_editwin_init(owl_editwin *e, WINDOW *win, int winlines, int wincols, int style, owl_history *hist)
50{
[e20d8179]51  e->buff=owl_malloc(INCR);
[7d4fbcd]52  e->buff[0]='\0';
53  e->bufflen=0;
[10b866d]54  e->hist=hist;
[7d4fbcd]55  e->allocated=INCR;
[a556caa]56  e->index = 0;
57  e->goal_column = -1;
58  e->topindex=0;
[7d4fbcd]59  e->winlines=winlines;
60  e->wincols=wincols;
[9a6cc40]61  e->fillcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxfillcols(&g));
62  e->wrapcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxwrapcols(&g));
[7d4fbcd]63  e->curswin=win;
64  e->style=style;
65  if ((style!=OWL_EDITWIN_STYLE_MULTILINE) &&
66      (style!=OWL_EDITWIN_STYLE_ONELINE)) {
67    e->style=OWL_EDITWIN_STYLE_MULTILINE;
68  }
69  e->lock=0;
70  e->dotsend=0;
[c9334b1]71  e->echochar='\0';
[e74c01c]72
[af1920fd]73  /* We get initialized multiple times, but we need to hold on to
74     the callbacks, so we can't NULL them here. */
[e74c01c]75  /*
76    e->command = NULL;
77    e->callback = NULL;
78    e->cbdata = NULL;
79  */
[7d4fbcd]80  if (win) werase(win);
81}
82
[c9334b1]83void owl_editwin_set_curswin(owl_editwin *e, WINDOW *w, int winlines, int wincols)
84{
[7d4fbcd]85  e->curswin=w;
86  e->winlines=winlines;
87  e->wincols=wincols;
[9a6cc40]88  e->fillcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxfillcols(&g));
89  e->wrapcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxwrapcols(&g));
[7d4fbcd]90}
91
[c9334b1]92/* echo the character 'ch' for each normal character keystroke,
93 * excepting locktext.  This is useful for entering passwords etc.  If
94 * ch=='\0' characters are echo'd normally
95 */
96void owl_editwin_set_echochar(owl_editwin *e, int ch)
97{
98  e->echochar=ch;
99}
100
101WINDOW *owl_editwin_get_curswin(owl_editwin *e)
102{
[060b3b4]103  return(e->curswin);
[7d4fbcd]104}
105
[c9334b1]106void owl_editwin_set_history(owl_editwin *e, owl_history *h)
107{
[060b3b4]108  e->hist=h;
[10b866d]109}
110
[c9334b1]111owl_history *owl_editwin_get_history(owl_editwin *e)
112{
[060b3b4]113  return(e->hist);
[10b866d]114}
115
[c9334b1]116void owl_editwin_set_dotsend(owl_editwin *e)
117{
[7d4fbcd]118  e->dotsend=1;
119}
120
[e74c01c]121void owl_editwin_set_command(owl_editwin *e, char *command) {
122  if(e->command) owl_free(e->command);
123  e->command = owl_strdup(command);
124}
125
126char *owl_editwin_get_command(owl_editwin *e) {
127  if(e->command) return e->command;
128  return "";
129}
130
131void owl_editwin_set_callback(owl_editwin *e, void (*cb)(owl_editwin*)) {
132  e->callback = cb;
133}
134
135void (*owl_editwin_get_callback(owl_editwin *e))(owl_editwin*) {
136  return e->callback;
137}
138
139void owl_editwin_set_cbdata(owl_editwin *e, void *data) {
140  e->cbdata = data;
141}
142
[a556caa]143void *owl_editwin_get_cbdata(owl_editwin *e) {
[e74c01c]144  return e->cbdata;
145}
146
147void owl_editwin_do_callback(owl_editwin *e) {
148  void (*cb)(owl_editwin*);
149  cb=owl_editwin_get_callback(e);
150  if(!cb) {
151    owl_function_error("Internal error: No editwin callback!");
152  } else {
[af1920fd]153    /* owl_function_error("text: |%s|", owl_editwin_get_text(e)); */
[e74c01c]154    cb(e);
155  }
156}
157
[c9334b1]158int owl_editwin_limit_maxcols(int v, int maxv)
159{
[d36f2cb]160  if (maxv > 5 && v > maxv) {
[060b3b4]161    return(maxv);
[d36f2cb]162  } else {
[060b3b4]163    return(v);
[d36f2cb]164  }
165}
166
[c9334b1]167/* set text to be 'locked in' at the beginning of the buffer, any
168 * previous text (including locked text) will be overwritten
169 */
170void owl_editwin_set_locktext(owl_editwin *e, char *text)
171{
[a556caa]172  e->index = 0;
[7d4fbcd]173  owl_editwin_overwrite_string(e, text);
[47519e1b]174  owl_editwin_overwrite_char(e, '\0');
[7d4fbcd]175  e->lock=strlen(text);
[a556caa]176  e->index = e->lock;
[7d4fbcd]177  owl_editwin_redisplay(e, 0);
178}
179
[c9334b1]180int owl_editwin_get_style(owl_editwin *e)
181{
[7d4fbcd]182  return(e->style);
183}
184
[c9334b1]185void owl_editwin_new_style(owl_editwin *e, int newstyle, owl_history *h)
186{
[7d4fbcd]187  char *ptr;
[10b866d]188
189  owl_editwin_set_history(e, h);
[7d4fbcd]190  if (e->style==newstyle) return;
191
192  if (newstyle==OWL_EDITWIN_STYLE_MULTILINE) {
193    e->style=newstyle;
194  } else if (newstyle==OWL_EDITWIN_STYLE_ONELINE) {
195    e->style=newstyle;
196
197    /* nuke everything after the first line */
198    if (e->bufflen > 0) {
199      ptr=strchr(e->buff, '\n')-1;
200      if (ptr) {
201        e->bufflen=ptr - e->buff;
202        e->buff[e->bufflen]='\0';
[a556caa]203        e->index = 0;
[7d4fbcd]204      }
205    }
206  }
207}
208
[c9334b1]209/* completly reinitialize the buffer */
210void owl_editwin_fullclear(owl_editwin *e)
211{
[7d4fbcd]212  owl_free(e->buff);
[10b866d]213  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
[7d4fbcd]214}
215
[c9334b1]216/* clear all text except for locktext and put the cursor at the
217 * beginning
218 */
219void owl_editwin_clear(owl_editwin *e)
220{
221
[7d4fbcd]222  int lock;
[10b866d]223  int dotsend=e->dotsend;
[7d4fbcd]224  char *locktext=NULL;
[cc6f009]225  char echochar=e->echochar;
[10b866d]226
[7d4fbcd]227  lock=0;
228  if (e->lock > 0) {
229    lock=1;
230
231    locktext=owl_malloc(e->lock+20);
232    strncpy(locktext, e->buff, e->lock);
233    locktext[e->lock]='\0';
234  }
235
236  owl_free(e->buff);
[10b866d]237  owl_editwin_init(e, e->curswin, e->winlines, e->wincols, e->style, e->hist);
[7d4fbcd]238
239  if (lock > 0) {
240    owl_editwin_set_locktext(e, locktext);
241  }
[10b866d]242  if (dotsend) {
243    owl_editwin_set_dotsend(e);
244  }
[cc6f009]245  if (echochar) {
246    owl_editwin_set_echochar(e, echochar);
247  }
[7d4fbcd]248
249  if (locktext) owl_free(locktext);
250  owl_editwin_adjust_for_locktext(e);
251}
252
[c9334b1]253/* malloc more space for the buffer */
254void _owl_editwin_addspace(owl_editwin *e)
255{
[7d4fbcd]256  e->buff=owl_realloc(e->buff, e->allocated+INCR);
257  if (!e->buff) {
[a556caa]258    /* error *//*XXXXXXXXXXXXXXXX*/
[7d4fbcd]259    return;
260  }
261  e->allocated+=INCR;
262}
263
[c9334b1]264void owl_editwin_recenter(owl_editwin *e)
265{
[a556caa]266  e->topindex = -1;
267}
[e20d8179]268
[a0fbdee]269static void oe_save_excursion(owl_editwin *e, oe_excursion *x)
270{
271  x->index = e->index;
272  x->goal_column = e->goal_column;
273  x->lock = e->lock;
274}
275
276static void oe_restore_excursion(owl_editwin *e, oe_excursion *x)
277{
278  e->index = x->index;
279  e->goal_column = x->goal_column;
280  e->lock = x->lock;
281}
282
[e20d8179]283static inline char *oe_next_point(owl_editwin *e, char *p)
284{
285  char *boundary = e->buff + e->bufflen + 1;
286  char *q;
287
288  q = g_utf8_find_next_char(p, boundary);
289  while (q && g_unichar_ismark(g_utf8_get_char(q)))
290    q = g_utf8_find_next_char(q, boundary);
291
292  if (q == p)
293    return NULL;
294  return q;
295}
296
297static inline char *oe_prev_point(owl_editwin *e, char *p)
298{
299  char *boundary = e->buff + e->lock;
300
301  p = g_utf8_find_prev_char(boundary, p);
302  while (p && g_unichar_ismark(g_utf8_get_char(p)))
303    p = g_utf8_find_prev_char(boundary, p);
304
305  return p;
306}
307
[19f765d]308static int oe_find_display_line(owl_editwin *e, int *x, int index)
309{
310  int width = 0, cw;
311  gunichar c = -1;
312  char *p;
313
[77d4402]314  while(1) {
315    /* note the position of the dot */
[19f765d]316    if (x != NULL && index == e->index)
317      *x = width;
[77d4402]318
[19f765d]319    /* get the current character */
320    c = g_utf8_get_char(e->buff + index);
321
322    /* figure out how wide it is */
323    if (c == 9) /* TAB */
324      cw = TABSIZE - width % TABSIZE;
325    else
326      cw = mk_wcwidth(c);
327    if (cw < 0) /* control characters */
328        cw = 0;
329
[77d4402]330    if (width + cw > e->wincols) {
331      if (x != NULL && *x == width)
332        *x = -1;
[19f765d]333      break;
[77d4402]334    }
[19f765d]335    width += cw;
336
337    if (c == '\n') {
[77d4402]338      if (width < e->wincols)
339        ++index; /* skip the newline */
[19f765d]340      break;
341    }
342
343    /* find the next character */
344    p = oe_next_point(e, e->buff + index);
345    if (p == NULL) { /* we ran off the end */
346      if (x != NULL && e->index > index)
347        *x = width + 1;
348      break;
349    }
350    index = p - e->buff;
[cedc95c]351
[19f765d]352  }
353  return index;
354}
355
[e20d8179]356static void oe_reframe(owl_editwin *e) {
[cedc95c]357  oe_excursion x;
358  int goal = e->winlines / 2;
359  int index;
360  int count = 0;
361  int point;
362  int n, i;
363  int last;
364
365  oe_save_excursion(e, &x);
366  /* step back line-by-line through the buffer until we have >= goal lines of
367     display text */
368  e->lock = 0; /* we can (must) tread on the locktext */
369
370  point = e->index;
371  last = -1;
372  while (count < goal) {
373    index = e->index;
374    owl_editwin_move_to_beginning_of_line(e);
375    if (last == e->index)
376      break;
377    last = e->index;
378    for (n = 0, i = e->index; i < index; n++)
379      i = oe_find_display_line(e, NULL, i);
380    count += n == 0 ? 1 : n;
381    if (count < goal)
382      owl_editwin_point_move(e, -1);
383  }
384  /* if we overshot, backtrack */
385  for (n = 0; n < (count - goal); n++)
386    e->index = oe_find_display_line(e, NULL, e->index);
387
388  e->topindex = e->index;
389
390  oe_restore_excursion(e, &x);
[7d4fbcd]391}
392
[c9334b1]393/* regenerate the text on the curses window */
394/* if update == 1 then do a doupdate(), otherwise do not */
395void owl_editwin_redisplay(owl_editwin *e, int update)
396{
[19f765d]397  int x = -1, y = -1, t;
[77d4402]398  int line, index, lineindex, times = 0;
[7d4fbcd]399
[bd1a1ae]400  do {
[cedc95c]401    werase(e->curswin);
402
[bd1a1ae]403    if (e->topindex == -1 || e->index < e->topindex)
[e20d8179]404      oe_reframe(e);
405
[bd1a1ae]406    line = 0;
407    index = e->topindex;
408    while(line < e->winlines) {
409      lineindex = index;
[19f765d]410      t = -1;
411      index = oe_find_display_line(e, &t, lineindex);
412      if (x == -1 && t != -1)
413        x = t, y = line;
[bd1a1ae]414      if (index - lineindex)
415        mvwaddnstr(e->curswin, line, 0,
416                   e->buff + lineindex,
417                   index - lineindex);
418      line++;
[a556caa]419    }
[19f765d]420    if (x == -1)
[bd1a1ae]421        e->topindex = -1; /* force a reframe */
[77d4402]422    times++;
423  } while(x == -1 && times < 3);
[e20d8179]424
[19f765d]425  wmove(e->curswin, y, x);
[7d4fbcd]426  wnoutrefresh(e->curswin);
[a556caa]427  if (update == 1)
[7d4fbcd]428    doupdate();
429}
430
[47519e1b]431/* Remove n bytes at cursor. */
432void _owl_editwin_remove_bytes(owl_editwin *e, int n) /*noproto*/
433{
[a556caa]434  int i = e->index + n;
[47519e1b]435  for (; i < e->bufflen; i++) {
436    e->buff[i-n] = e->buff[i];
437  }
[e20d8179]438
[47519e1b]439  e->bufflen -= n;
440  e->buff[e->bufflen] = '\0';
441}
[830c36e]442
[47519e1b]443/* Insert n bytes at cursor.*/
444void _owl_editwin_insert_bytes(owl_editwin *e, int n) /*noproto*/
445{
[a556caa]446  int i;
[e9bb404]447
[47519e1b]448  if ((e->bufflen + n) > (e->allocated - 5)) {
449    _owl_editwin_addspace(e);
450  }
451
[a556caa]452  if(e->index != e->bufflen) {
453    for (i = e->bufflen + n - 1; i > e->index; i--) {
[e9bb404]454      e->buff[i] = e->buff[i - n];
455    }
[47519e1b]456  }
[e9bb404]457
458  e->bufflen += n;
459  e->buff[e->bufflen] = '\0';
460
[47519e1b]461}
462
[7d4fbcd]463
[c9334b1]464/* linewrap the word just before the cursor.
465 * returns 0 on success
466 * returns -1 if we could not wrap.
467 */
468int _owl_editwin_linewrap_word(owl_editwin *e)
469{
[84027015]470  int i;
471  char *ptr1, *start;
472  gunichar c;
[7d4fbcd]473
[84027015]474  start = e->buff + e->lock;
475
[a556caa]476  ptr1 = e->buff + e->index;
[84027015]477  ptr1 = g_utf8_find_prev_char(start, ptr1);
478
479  while (ptr1) {
480    c = g_utf8_get_char(ptr1);
481    if (owl_util_can_break_after(c)) {
482      if (c != ' ') {
483        i = ptr1 - e->buff;
[a556caa]484        e->index = i;
[b2c1bd4]485        _owl_editwin_insert_bytes(e, 1);
486        /* _owl_editwin_insert_bytes may move e->buff. */
[84027015]487        ptr1 = e->buff + i;
488      }
489      *ptr1 = '\n';
490      return 0;
[7d4fbcd]491    }
[2d4ff14]492    else if (c == '\n') {
493      return 0;
494    }
[84027015]495    ptr1 = g_utf8_find_prev_char(start, ptr1);
[7d4fbcd]496  }
[84027015]497  return -1;
[7d4fbcd]498}
499
[c9334b1]500/* insert a character at the current point (shift later
501 * characters over)
502 */
[47519e1b]503void owl_editwin_insert_char(owl_editwin *e, gunichar c)
[c9334b1]504{
[a556caa]505  int i, ret, len;
[47519e1b]506  char tmp[6];
507  memset(tmp, '\0', 6);
[830c36e]508
[7d4fbcd]509  /* \r is \n */
[47519e1b]510  if (c == '\r') {
511    c = '\n';
[7d4fbcd]512  }
513
[47519e1b]514  if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) {
[7d4fbcd]515    /* perhaps later this will change some state that allows the string
516       to be read */
517    return;
518  }
519
[47519e1b]520  g_unichar_to_utf8(c, tmp);
521  len = strlen(tmp);
[830c36e]522
[7d4fbcd]523  /* make sure there is enough memory for the new text */
[47519e1b]524  if ((e->bufflen + len) > (e->allocated - 5)) {
[7d4fbcd]525    _owl_editwin_addspace(e);
526  }
527
528  /* If we're going to insert at the last column do word wrapping, unless it's a \n */
[a556caa]529#if 0 /* XXX */
[47519e1b]530  if ((e->buffx + 1 == e->wrapcol) && (c != '\n')) {
531    ret = _owl_editwin_linewrap_word(e);
532    if (ret == -1) {
[7d4fbcd]533      /* we couldn't wrap, insert a hard newline instead */
534      owl_editwin_insert_char(e, '\n');
535    }
536  }
[a556caa]537#endif
[7d4fbcd]538
539  /* shift all the other characters right */
[e9bb404]540  _owl_editwin_insert_bytes(e, len);
[7d4fbcd]541
[47519e1b]542  /* insert the new character */
543  for(i = 0; i < len; i++) {
[a556caa]544    e->buff[e->index + i] = tmp[i];
[47519e1b]545  }
[7d4fbcd]546
547  /* advance the cursor */
[a556caa]548  e->index += len;
[7d4fbcd]549}
550
[c9334b1]551/* overwrite the character at the current point with 'c' */
[47519e1b]552void owl_editwin_overwrite_char(owl_editwin *e, gunichar c)
[c9334b1]553{
[a556caa]554  int oldlen, newlen, i;
555  char tmp[6], *t;
[47519e1b]556  memset(tmp, '\0', 6);
[830c36e]557
[7d4fbcd]558  /* \r is \n */
[47519e1b]559  if (c == '\r') {
560    c = '\n';
[7d4fbcd]561  }
[e20d8179]562
[47519e1b]563  if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) {
[7d4fbcd]564    /* perhaps later this will change some state that allows the string
565       to be read */
566    return;
567  }
568
[47519e1b]569  g_unichar_to_utf8(c, tmp);
570  newlen = strlen(tmp);
[7d4fbcd]571
[a556caa]572  t = g_utf8_find_next_char(e->buff + e->index, NULL);
573  oldlen = (t ? (t - (e->buff + e->index)) : 0);
[7d4fbcd]574
[830c36e]575  /* only if we are at the end of the buffer do we create new space here */
[a556caa]576  if (e->index == e->bufflen) {
[830c36e]577    if ((e->bufflen+newlen) > (e->allocated-5)) {
578      _owl_editwin_addspace(e);
579    }
[7d4fbcd]580  }
[e20d8179]581  /* if not at the end of the buffer, adjust based in char size difference. */
[830c36e]582  else if (oldlen > newlen) {
[47519e1b]583    _owl_editwin_remove_bytes(e, oldlen-newlen);
584  }
585  else /* oldlen < newlen */ {
586    _owl_editwin_insert_bytes(e, newlen-oldlen);
[7d4fbcd]587  }
[47519e1b]588  /* Overwrite the old char*/
589  for (i = 0; i < newlen; i++) {
[a556caa]590    e->buff[e->index + i] = tmp[i];
[47519e1b]591  }
[e20d8179]592
[47519e1b]593  /* housekeeping */
[a556caa]594  if (e->index == e->bufflen) {
[830c36e]595    e->bufflen += newlen;
596    e->buff[e->bufflen] = '\0';
597  }
[e20d8179]598
[47519e1b]599  /* advance the cursor */
[a556caa]600  e->index += newlen;
[7d4fbcd]601}
602
[c9334b1]603/* delete the character at the current point, following chars
604 * shift left.
[e20d8179]605 */
[c9334b1]606void owl_editwin_delete_char(owl_editwin *e)
607{
[47519e1b]608  char *p1, *p2;
609  gunichar c;
[7d4fbcd]610
[47519e1b]611  if (e->bufflen == 0) return;
[e20d8179]612
[a556caa]613  if (e->index == e->bufflen) return;
[7d4fbcd]614
[a556caa]615  p1 = e->buff + e->index;
[47519e1b]616  p2 = g_utf8_next_char(p1);
617  c = g_utf8_get_char(p2);
618  while (g_unichar_ismark(c)) {
619    p2 = g_utf8_next_char(p2);
620    c = g_utf8_get_char(p2);
[7d4fbcd]621  }
[47519e1b]622  _owl_editwin_remove_bytes(e, p2-p1);
[7d4fbcd]623}
624
[c9334b1]625/* Swap the character at point with the character at point-1 and
626 * advance the pointer.  If point is at beginning of buffer do
627 * nothing.  If point is after the last character swap point-1 with
[e20d8179]628 * point-2.  (Behaves as observed in tcsh and emacs).
[c9334b1]629 */
630void owl_editwin_transpose_chars(owl_editwin *e)
631{
[47519e1b]632  char *p1, *p2, *p3, *tmp;
[f2e36b5]633
[47519e1b]634  if (e->bufflen == 0) return;
[e20d8179]635
[a556caa]636  if (e->index == e->bufflen) {
[f2e36b5]637    /* point is after last character */
[a556caa]638    e->index--;
[e20d8179]639  }
[f2e36b5]640
[a556caa]641  if (e->index - 1 < e->lock) {
[f2e36b5]642    /* point is at beginning of buffer, do nothing */
643    return;
644  }
645
[47519e1b]646  /* Transpose two utf-8 unicode glyphs. */
[a556caa]647  p1 = e->buff + e->index;
[47519e1b]648
[e20d8179]649  p2 = oe_next_point(e, p1); /* XXX make sure we can't transpose past the end
650                                of the buffer */
651  if (p2 == NULL)
652    return;
[47519e1b]653
[e20d8179]654  p3 = oe_prev_point(e, p1);
655  if (p3 == NULL)
656    return;
[47519e1b]657
658  tmp = owl_malloc(p2 - p3 + 5);
659  *tmp = '\0';
660  strncat(tmp, p1, p2 - p1);
661  strncat(tmp, p3, p1 - p3);
662  strncpy(p3, tmp, p2 - p3);
663  owl_free(tmp);
[a556caa]664  e->index = p3 - e->buff;
[f2e36b5]665}
666
[c9334b1]667/* insert 'string' at the current point, later text is shifted
668 * right
669 */
670void owl_editwin_insert_string(owl_editwin *e, char *string)
671{
[47519e1b]672  char *p;
673  gunichar c;
674  if (!g_utf8_validate(string, -1, NULL)) {
675    owl_function_debugmsg("owl_editwin_insert_string: received non-utf-8 string.");
676    return;
677  }
678  p = string;
679  c = g_utf8_get_char(p);
680  while (c) {
[fac5463]681    _owl_editwin_process_char(e, c);
[47519e1b]682    p = g_utf8_next_char(p);
683    c = g_utf8_get_char(p);
[7d4fbcd]684  }
685}
686
[c9334b1]687/* write 'string' at the current point, overwriting text that is
688 * already there
689 */
690
691void owl_editwin_overwrite_string(owl_editwin *e, char *string)
692{
[47519e1b]693  char *p;
694  gunichar c;
[7d4fbcd]695
[47519e1b]696  if (!g_utf8_validate(string, -1, NULL)) {
697    owl_function_debugmsg("owl_editwin_overwrite_string: received non-utf-8 string.");
698    return;
699  }
700  p = string;
701  c = g_utf8_get_char(p);
702  while (c) {
703    owl_editwin_overwrite_char(e, c);
704    p = g_utf8_next_char(p);
705    c = g_utf8_get_char(p);
[7d4fbcd]706  }
707}
708
[a556caa]709/* We assume index is not set to point to a mid-char */
710gunichar _owl_editwin_get_char_at_point(owl_editwin *e)
[c9334b1]711{
[a556caa]712  return g_utf8_get_char(e->buff + e->index);
713}
[7d4fbcd]714
[a556caa]715void owl_editwin_adjust_for_locktext(owl_editwin *e)
716{
717  /* if we happen to have the cursor over locked text
718   * move it to be out of the locktext region */
719  if (e->index < e->lock) {
720    e->index = e->lock;
[7d4fbcd]721  }
722}
723
[e20d8179]724int owl_editwin_point_move(owl_editwin *e, int delta)
[84027015]725{
[e20d8179]726  char *p;
[19f765d]727  int change, d = 0;
[a556caa]728
729  change = MAX(delta, - delta);
730  p = e->buff + e->index;
731
732  while (d < change && p != NULL) {
[e20d8179]733    if (delta > 0)
734      p = oe_next_point(e, p);
735    else
736      p = oe_prev_point(e, p);
737    if (p != NULL) {
738      e->index = p - e->buff;
739      d++;
[a556caa]740    }
741  }
742
743  if (delta)
744    e->goal_column = -1;
745
746  return delta > 0 ? d : -d;
[84027015]747}
748
[a556caa]749int owl_editwin_at_beginning_of_buffer(owl_editwin *e) {
750  if (e->index == e->lock)
751    return 1;
[84027015]752
[a556caa]753  return 0;
754}
755
756int owl_at_end_of_buffer(owl_editwin *e) {
757  if (e->index == e->bufflen)
758    return 1;
759
760  return 0;
761}
762
763int owl_editwin_at_beginning_of_line(owl_editwin *e) /*noproto*/
[c9334b1]764{
[a0fbdee]765  oe_excursion x;
[a556caa]766  int ret;
[47519e1b]767
[a556caa]768  if (owl_editwin_at_beginning_of_buffer(e))
769    return 1;
[47519e1b]770
[a0fbdee]771  oe_save_excursion(e, &x);
[a556caa]772  owl_editwin_point_move(e, -1);
773  ret = (_owl_editwin_get_char_at_point(e) == '\n');
[a0fbdee]774  oe_restore_excursion(e, &x);
[a556caa]775
776  return ret;
777}
778
[e20d8179]779static int owl_editwin_is_char_in(owl_editwin *e, char *set)
[a556caa]780{
781  char *p;
782  /* It would be awfully nice if we could do UTF-8 comparisons */
783  for (p = set; *p != 0; p++)
784    if (_owl_editwin_get_char_at_point(e) == *p)
785      return 1;
786  return 0;
787}
788
[e20d8179]789int owl_editwin_move_if_in(owl_editwin *e, int delta, char *set)
[a556caa]790{
791  int change, distance = 0;
[e20d8179]792  while (owl_editwin_is_char_in(e, set)) {
[a556caa]793    change = owl_editwin_point_move(e, delta);
794    distance += change;
795    if (change == 0)
796      break;
[47519e1b]797  }
[a556caa]798  return distance;
799}
800
[e20d8179]801int owl_editwin_move_if_not_in(owl_editwin *e, int delta, char *set)
[a556caa]802{
803  int change, distance = 0;
[e20d8179]804  while (!owl_editwin_is_char_in(e, set)) {
[a556caa]805    change = owl_editwin_point_move(e, delta);
806    distance += change;
807    if (change == 0)
808      break;
[7d4fbcd]809  }
[a556caa]810  return distance;
[7d4fbcd]811}
812
[e20d8179]813int owl_editwin_move_to_beginning_of_line(owl_editwin *e)
[47519e1b]814{
[a556caa]815  int distance = 0;
[47519e1b]816
[a556caa]817  if (!owl_editwin_at_beginning_of_line(e)) {
818    /* move off the \n if were at the end of a line */
819    distance += owl_editwin_point_move(e, -1);
820    distance += owl_editwin_move_if_not_in(e, -1, "\n");
821    if (distance && !owl_editwin_at_beginning_of_buffer(e))
822      distance += owl_editwin_point_move(e, 1);
[47519e1b]823  }
[77d4402]824  e->goal_column = 0; /* subtleties */
[84027015]825
[a556caa]826  return distance;
827}
[e20d8179]828
829int owl_editwin_move_to_end_of_line(owl_editwin *e)
[a556caa]830{
831  return owl_editwin_move_if_not_in(e, 1, "\n");
[47519e1b]832}
833
[e20d8179]834int owl_editwin_line_move(owl_editwin *e, int delta)
[c9334b1]835{
[d7043b4]836  int goal_column, change, ll, distance;
837  int count = 0;
[a556caa]838
839  change = MAX(delta, -delta);
840
[d7043b4]841  goal_column = e->goal_column;
842  distance = owl_editwin_move_to_beginning_of_line(e);
843  goal_column = goal_column == -1 ? -distance : goal_column;
[a556caa]844
845  while(count < change) {
846    if (delta > 0) {
847      distance += owl_editwin_move_if_not_in(e, 1, "\n");
848      distance += owl_editwin_point_move(e, 1);
849    } else {
850      /* I really want to assert delta < 0 here */
851      distance += owl_editwin_point_move(e, -1); /* to the newline on
852                                                    the previous line */
853      distance += owl_editwin_move_to_beginning_of_line(e);
854    }
855    count++;
[7d4fbcd]856  }
[a556caa]857
858  distance += (ll = owl_editwin_move_to_end_of_line(e));
859  if (ll > goal_column)
860    distance += owl_editwin_point_move(e, goal_column - ll);
861
862  e->goal_column = goal_column;
863
864  return distance;
[7d4fbcd]865}
866
[c9334b1]867void owl_editwin_backspace(owl_editwin *e)
868{
[7d4fbcd]869  /* delete the char before the current one
870   * and shift later chars left
871   */
[a556caa]872  if(owl_editwin_point_move(e, -1))
[7d4fbcd]873    owl_editwin_delete_char(e);
874}
875
[c9334b1]876void owl_editwin_key_up(owl_editwin *e)
877{
[a556caa]878  owl_editwin_line_move(e, -1);
[7d4fbcd]879}
880
[c9334b1]881void owl_editwin_key_down(owl_editwin *e)
882{
[a556caa]883  owl_editwin_line_move(e, 1);
[7d4fbcd]884}
885
[c9334b1]886void owl_editwin_key_left(owl_editwin *e)
887{
[a556caa]888  owl_editwin_point_move(e, -1);
[7d4fbcd]889}
890
[c9334b1]891void owl_editwin_key_right(owl_editwin *e)
892{
[a556caa]893  owl_editwin_point_move(e, 1);
[7d4fbcd]894}
895
[c9334b1]896void owl_editwin_move_to_nextword(owl_editwin *e)
897{
[7d4fbcd]898  /* if we're starting on a space, find the first non-space */
[a556caa]899  owl_editwin_move_if_in(e, 1, WHITESPACE);
[7d4fbcd]900
[a556caa]901  /* now find the end of this word */
902  owl_editwin_move_if_not_in(e, 1, WHITESPACE);
[7d4fbcd]903}
904
[c9334b1]905/* go backwards to the last non-space character
906 */
907void owl_editwin_move_to_previousword(owl_editwin *e)
908{
[a0fbdee]909  oe_excursion x;
[a556caa]910  int beginning;
911  /* if in middle of word, beginning of word */
912
913  /* if at beginning of a word, find beginning of previous word */
914
[e20d8179]915  if (owl_editwin_is_char_in(e, WHITESPACE)) {
[a556caa]916    /* if in whitespace past end of word, find a word , the find the beginning*/
917    owl_editwin_move_if_in(e, -1, WHITESPACE); /* leaves us on the last
918                                                    character of the word */
[a0fbdee]919    oe_save_excursion(e, &x);
[a556caa]920    /* are we at the beginning of a word? */
921    owl_editwin_point_move(e, -1);
[e20d8179]922    beginning = owl_editwin_is_char_in(e, WHITESPACE);
[a0fbdee]923    oe_restore_excursion(e, &x);
[a556caa]924    if (beginning)
925      return;
926   } else {
927    /* in the middle of the word; */
[a0fbdee]928    oe_save_excursion(e, &x);
[a556caa]929    owl_editwin_point_move(e, -1);
[e20d8179]930    if (owl_editwin_is_char_in(e, WHITESPACE)) { /* we were at the beginning */
[a556caa]931      owl_editwin_move_to_previousword(e); /* previous case */
932      return;
933    } else {
[a0fbdee]934      oe_restore_excursion(e, &x);
[7d4fbcd]935    }
936  }
[a556caa]937  owl_editwin_move_if_not_in(e, -1, WHITESPACE);
938  /* will go past */
939  if (e->index > e->lock)
940    owl_editwin_point_move(e, 1);
[7d4fbcd]941}
942
[c9334b1]943void owl_editwin_delete_nextword(owl_editwin *e)
944{
[a0fbdee]945  oe_excursion x;
[a556caa]946  int end;
947
[a0fbdee]948  oe_save_excursion(e, &x);
[a556caa]949  owl_editwin_move_to_nextword(e);
950  end = e->index;
[a0fbdee]951  oe_restore_excursion(e, &x);
[a556caa]952  _owl_editwin_remove_bytes(e, end - e->index);
[7d4fbcd]953}
954
[c9334b1]955void owl_editwin_delete_previousword(owl_editwin *e)
956{
[b68f9cd]957  /* go backwards to the last non-space character, then delete chars */
[84027015]958  int startpos, endpos;
[b68f9cd]959
[a556caa]960  startpos = e->index;
[b68f9cd]961  owl_editwin_move_to_previousword(e);
[a556caa]962  endpos = e->index;
[84027015]963  _owl_editwin_remove_bytes(e, startpos-endpos);
[b68f9cd]964}
965
[a556caa]966void owl_editwin_move_to_line_end(owl_editwin *e)
[c9334b1]967{
[a556caa]968  owl_editwin_move_to_end_of_line(e);
[7d4fbcd]969}
970
[a556caa]971void owl_editwin_delete_to_endofline(owl_editwin *e)
[c9334b1]972{
[a0fbdee]973  oe_excursion x;
[a556caa]974  int end;
975
[a0fbdee]976  oe_save_excursion(e, &x);
[a556caa]977  owl_editwin_move_to_line_end(e);
978  end = e->index;
[a0fbdee]979  oe_restore_excursion(e, &x);
[77d4402]980  if (end - e->index)
981    _owl_editwin_remove_bytes(e, end - e->index);
982  else if (end < e->bufflen)
983    _owl_editwin_remove_bytes(e, 1);
[7d4fbcd]984}
985
[c9334b1]986void owl_editwin_move_to_line_start(owl_editwin *e)
987{
[a556caa]988  owl_editwin_move_to_beginning_of_line(e);
[7d4fbcd]989}
990
[c9334b1]991void owl_editwin_move_to_end(owl_editwin *e)
992{
[a556caa]993  e->index = e->bufflen;
[7d4fbcd]994}
995
[c9334b1]996void owl_editwin_move_to_top(owl_editwin *e)
997{
[a556caa]998  e->index = e->lock;
[7d4fbcd]999}
1000
[c9334b1]1001void owl_editwin_fill_paragraph(owl_editwin *e)
1002{
[a556caa]1003#if 0 /* XXX */
[a0fbdee]1004  oe_excursion x;
[7d4fbcd]1005  int i, save;
1006
1007  /* save our starting point */
[a0fbdee]1008  oe_save_excursion(e, &x);
[a556caa]1009
1010  save = e->index;
[7d4fbcd]1011
1012  /* scan back to the beginning of this paragraph */
1013  for (i=save; i>=e->lock; i--) {
1014    if ( (i<=e->lock) ||
1015         ((e->buff[i]=='\n') && (e->buff[i-1]=='\n'))) {
[a556caa]1016      e->index = i + 1;
[7d4fbcd]1017      break;
1018    }
1019  }
1020
1021  /* main loop */
1022  while (1) {
[47519e1b]1023    i = _owl_editwin_get_index_from_xy(e);
[7d4fbcd]1024
1025    /* bail if we hit the end of the buffer */
[47519e1b]1026    if (i >= e->bufflen || e->buff[i] == '\0') break;
[7d4fbcd]1027
1028    /* bail if we hit the end of the paragraph */
[47519e1b]1029    if (e->buff[i] == '\n' && e->buff[i+1] == '\n') break;
[7d4fbcd]1030
[50e671c]1031    /* bail if we hit a trailing dot on the buffer */
1032    if (e->buff[i] == '\n' && e->buff[i+1] == '.'
1033        && ((i+2) >= e->bufflen || e->buff[i+2] == '\0'))
1034      break;
1035
[7d4fbcd]1036    /* if we've travelled too far, linewrap */
1037    if ((e->buffx) >= e->fillcol) {
[b2c1bd4]1038      int len = e->bufflen;
[7d4fbcd]1039      _owl_editwin_linewrap_word(e);
[b2c1bd4]1040      /* we may have added a character. */
1041      if (i < save) save += e->bufflen - len;
[a556caa]1042      e->index = i;
[7d4fbcd]1043    }
1044
1045    /* did we hit the end of a line too soon? */
[84027015]1046    /* asedeno: Here we replace a newline with a space. We may want to
1047       consider removing the space if the characters to either side
1048       are CJK ideograms.*/
[47519e1b]1049    i = _owl_editwin_get_index_from_xy(e);
1050    if (e->buff[i] == '\n' && e->buffx < e->fillcol - 1) {
[7d4fbcd]1051      /* ********* we need to make sure we don't pull in a word that's too long ***********/
1052      e->buff[i]=' ';
1053    }
[b2c1bd4]1054
[7d4fbcd]1055    /* fix spacing */
[47519e1b]1056    i = _owl_editwin_get_index_from_xy(e);
1057    if (e->buff[i] == ' ' && e->buff[i+1] == ' ') {
1058      if (e->buff[i-1] == '.' || e->buff[i-1] == '!' || e->buff[i-1] == '?') {
[7d4fbcd]1059        owl_editwin_key_right(e);
1060      } else {
1061        owl_editwin_delete_char(e);
[84027015]1062        /* if we did this ahead of the save point, adjust it. Changing
1063           by one is fine here because we're only removing an ASCII
1064           space. */
[47519e1b]1065        if (i < save) save--;
[7d4fbcd]1066      }
1067    } else {
1068      owl_editwin_key_right(e);
1069    }
1070  }
1071
1072  /* put cursor back at starting point */
[a0fbdee]1073  oe_restore_excursion(e, &x);
[a556caa]1074#endif
[7d4fbcd]1075}
1076
[cf83b7a]1077/* returns true if only whitespace remains */
[c9334b1]1078int owl_editwin_is_at_end(owl_editwin *e)
1079{
[a556caa]1080  return (only_whitespace(e->buff + e->index));
[217a43e]1081}
1082
[c9334b1]1083int owl_editwin_check_dotsend(owl_editwin *e)
1084{
[47519e1b]1085  char *p, *p_n, *p_p;
1086  gunichar c;
[7d4fbcd]1087
1088  if (!e->dotsend) return(0);
[47519e1b]1089
1090  p = g_utf8_find_prev_char(e->buff, e->buff + e->bufflen);
1091  p_n = g_utf8_find_next_char(p, NULL);
1092  p_p = g_utf8_find_prev_char(e->buff, p);
1093  c = g_utf8_get_char(p);
1094  while (p != NULL) {
1095    if (*p == '.'
1096        && p_p != NULL && (*p_p == '\n' || *p_p == '\r')
1097        && p_n != NULL && (*p_n == '\n' || *p_n == '\r')) {
1098      e->bufflen = p - e->buff;
1099      e->buff[e->bufflen] = '\0';
[7d4fbcd]1100      return(1);
1101    }
[47519e1b]1102    if (c != '\0' && !g_unichar_isspace(c)) return(0);
1103    p_n = p;
1104    p = p_p;
1105    c = g_utf8_get_char(p);
1106    p_p = g_utf8_find_prev_char(e->buff, p);
[7d4fbcd]1107  }
1108  return(0);
1109}
1110
[fac5463]1111void owl_editwin_post_process_char(owl_editwin *e, owl_input j)
[c9334b1]1112{
[a556caa]1113  /* XXX force a redisplay? */
[fac5463]1114  if ((j.ch==13 || j.ch==10) && owl_editwin_check_dotsend(e)) {
[7d4fbcd]1115    owl_command_editmulti_done(e);
1116    return;
1117  }
[e20d8179]1118  owl_editwin_redisplay(e, 0);
[7d4fbcd]1119}
1120
[fac5463]1121void _owl_editwin_process_char(owl_editwin *e, gunichar j)
[c9334b1]1122{
[bd1a1ae]1123  if (!(g_unichar_iscntrl(j) && (j != 10) && (j != 13)) || j==9 ) {
[7d4fbcd]1124    owl_editwin_insert_char(e, j);
1125  }
1126}
1127
[fac5463]1128
1129void owl_editwin_process_char(owl_editwin *e, owl_input j)
1130{
1131  if (j.ch == ERR) return;
1132  /* Ignore ncurses control characters. */
[e20d8179]1133  if (j.ch < 0x100) {
[fac5463]1134    _owl_editwin_process_char(e, j.uch);
1135  }
1136}
1137
[c9334b1]1138char *owl_editwin_get_text(owl_editwin *e)
1139{
[7d4fbcd]1140  return(e->buff+e->lock);
1141}
1142
[c9334b1]1143int owl_editwin_get_numlines(owl_editwin *e)
1144{
[7d4fbcd]1145  return(owl_text_num_lines(e->buff));
1146}
1147
[a556caa]1148int owl_editwin_get_echochar(owl_editwin *e) {
1149  return e->echochar;
1150}
[e20d8179]1151
1152void owl_editwin_set_goal_column(owl_editwin *e, int column)
1153{
1154  e->goal_column = column;
1155}
1156
1157int owl_editwin_get_goal_column(owl_editwin *e)
1158{
1159  return e->goal_column;
1160}
Note: See TracBrowser for help on using the repository browser.