source: editwin.c @ bab52da

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