source: editwin.c @ 98f1e69

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