source: editwin.c @ ebf0128

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