ncurses 5.7 - patch 20090704
[ncurses.git] / ncurses / widechar / lib_add_wch.c
index 93b41bb4930cdd38bf8f0339ee683716c7791e33..c1c565c13436b757fca7ea344365ad0f35087ac6 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * Copyright (c) 2004,2006 Free Software Foundation, Inc.                   *
+ * Copyright (c) 2004-2006,2009 Free Software Foundation, Inc.              *
  *                                                                          *
  * Permission is hereby granted, free of charge, to any person obtaining a  *
  * copy of this software and associated documentation files (the            *
 
 #include <curses.priv.h>
 
-MODULE_ID("$Id: lib_add_wch.c,v 1.6 2006/12/02 21:19:17 tom Exp $")
+#if HAVE_WCTYPE_H
+#include <wctype.h>
+#endif
 
-NCURSES_EXPORT(int)
-wadd_wch(WINDOW *win, const cchar_t *wch)
+MODULE_ID("$Id: lib_add_wch.c,v 1.7 2009/07/04 21:59:25 tom Exp $")
+
+/* clone/adapt lib_addch.c */
+static const cchar_t blankchar = NewChar(BLANK_TEXT);
+
+/*
+ * Ugly microtweaking alert.  Everything from here to end of module is
+ * likely to be speed-critical -- profiling data sure says it is!
+ * Most of the important screen-painting functions are shells around
+ * wadd_wch().  So we make every effort to reduce function-call overhead
+ * by inlining stuff, even at the cost of making wrapped copies for
+ * export.  Also we supply some internal versions that don't call the
+ * window sync hook, for use by string-put functions.
+ */
+
+/* Return bit mask for clearing color pair number if given ch has color */
+#define COLOR_MASK(ch) (~(attr_t)((ch) & A_COLOR ? A_COLOR : 0))
+
+static NCURSES_INLINE cchar_t
+render_char(WINDOW *win, cchar_t ch)
+/* compute a rendition of the given char correct for the current context */
 {
-    PUTC_DATA;
-    int n;
-    int code = ERR;
+    attr_t a = WINDOW_ATTRS(win);
+    int pair = GetPair(ch);
 
-    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wadd_wch(%p, %s)"), win,
-                                     _tracech_t(wch)));
-
-    if (win != 0) {
-       PUTC_INIT;
-       for (PUTC_i = 0; PUTC_i < CCHARW_MAX; ++PUTC_i) {
-           attr_t attrs = (wch->attr & A_ATTRIBUTES);
-
-           if ((PUTC_ch = wch->chars[PUTC_i]) == L'\0')
-               break;
-           if ((PUTC_n = wcrtomb(PUTC_buf, PUTC_ch, &PUT_st)) <= 0) {
-               code = ERR;
-               if (is8bits(PUTC_ch))
-                   code = waddch(win, UChar(PUTC_ch) | attrs);
-               break;
+    if (ISBLANK(ch)
+       && AttrOf(ch) == A_NORMAL
+       && pair == 0) {
+       /* color/pair in attrs has precedence over bkgrnd */
+       ch = win->_nc_bkgd;
+       SetAttr(ch, a | AttrOf(win->_nc_bkgd));
+       if ((pair = GET_WINDOW_PAIR(win)) == 0)
+           pair = GetPair(win->_nc_bkgd);
+       SetPair(ch, pair);
+    } else {
+       /* color in attrs has precedence over bkgrnd */
+       a |= AttrOf(win->_nc_bkgd) & COLOR_MASK(a);
+       /* color in ch has precedence */
+       if (pair == 0) {
+           if ((pair = GET_WINDOW_PAIR(win)) == 0)
+               pair = GetPair(win->_nc_bkgd);
+       }
+#if 0
+       if (pair > 255) {
+           cchar_t fixme = ch;
+           SetPair(fixme, pair);
+       }
+#endif
+       AddAttr(ch, (a & COLOR_MASK(AttrOf(ch))));
+       SetPair(ch, pair);
+    }
+
+    TR(TRACE_VIRTPUT,
+       ("render_char bkg %s (%d), attrs %s (%d) -> ch %s (%d)",
+       _tracech_t2(1, CHREF(win->_nc_bkgd)),
+       GetPair(win->_nc_bkgd),
+       _traceattr(WINDOW_ATTRS(win)),
+       GET_WINDOW_PAIR(win),
+       _tracech_t2(3, CHREF(ch)),
+       GetPair(ch)));
+
+    return (ch);
+}
+
+/* check if position is legal; if not, return error */
+#ifndef NDEBUG                 /* treat this like an assertion */
+#define CHECK_POSITION(win, x, y) \
+       if (y > win->_maxy \
+        || x > win->_maxx \
+        || y < 0 \
+        || x < 0) { \
+               TR(TRACE_VIRTPUT, ("Alert! Win=%p _curx = %d, _cury = %d " \
+                                  "(_maxx = %d, _maxy = %d)", win, x, y, \
+                                  win->_maxx, win->_maxy)); \
+               return(ERR); \
+       }
+#else
+#define CHECK_POSITION(win, x, y)      /* nothing */
+#endif
+
+static bool
+newline_forces_scroll(WINDOW *win, NCURSES_SIZE_T * ypos)
+{
+    bool result = FALSE;
+
+    if (*ypos >= win->_regtop && *ypos == win->_regbottom) {
+       *ypos = win->_regbottom;
+       result = TRUE;
+    } else {
+       *ypos += 1;
+    }
+    return result;
+}
+
+/*
+ * The _WRAPPED flag is useful only for telling an application that we've just
+ * wrapped the cursor.  We don't do anything with this flag except set it when
+ * wrapping, and clear it whenever we move the cursor.  If we try to wrap at
+ * the lower-right corner of a window, we cannot move the cursor (since that
+ * wouldn't be legal).  So we return an error (which is what SVr4 does). 
+ * Unlike SVr4, we can successfully add a character to the lower-right corner
+ * (Solaris 2.6 does this also, however).
+ */
+static int
+wrap_to_next_line(WINDOW *win)
+{
+    win->_flags |= _WRAPPED;
+    if (newline_forces_scroll(win, &(win->_cury))) {
+       win->_curx = win->_maxx;
+       if (!win->_scroll)
+           return (ERR);
+       scroll(win);
+    }
+    win->_curx = 0;
+    return (OK);
+}
+
+static int wadd_wch_literal(WINDOW *, cchar_t);
+/*
+ * Fill the given number of cells with blanks using the current background
+ * rendition.  This saves/restores the current x-position.
+ */
+static void
+fill_cells(WINDOW *win, int count)
+{
+    cchar_t blank = blankchar;
+    int save_x = win->_curx;
+    int save_y = win->_cury;
+
+    while (count-- > 0) {
+       if (wadd_wch_literal(win, blank) == ERR)
+           break;
+    }
+    win->_curx = save_x;
+    win->_cury = save_y;
+}
+
+static int
+wadd_wch_literal(WINDOW *win, cchar_t ch)
+{
+    int x;
+    int y;
+    struct ldat *line;
+
+    x = win->_curx;
+    y = win->_cury;
+
+    CHECK_POSITION(win, x, y);
+
+    ch = render_char(win, ch);
+
+    line = win->_line + y;
+
+    CHANGED_CELL(line, x);
+
+    /*
+     * Non-spacing characters are added to the current cell.
+     *
+     * Spacing characters that are wider than one column require some display
+     * adjustments.
+     */
+    {
+       int len = wcwidth(CharOf(ch));
+       int i;
+       int j;
+       wchar_t *chars;
+
+       if (len == 0) {         /* non-spacing */
+           if ((x > 0 && y >= 0)
+               || (win->_maxx >= 0 && win->_cury >= 1)) {
+               if (x > 0 && y >= 0)
+                   chars = (win->_line[y].text[x - 1].chars);
+               else
+                   chars = (win->_line[y - 1].text[win->_maxx].chars);
+               for (i = 0; i < CCHARW_MAX; ++i) {
+                   if (chars[i] == 0) {
+                       TR(TRACE_VIRTPUT,
+                          ("added non-spacing %d: %x",
+                           x, (int) CharOf(ch)));
+                       chars[i] = CharOf(ch);
+                       break;
+                   }
+               }
            }
-           for (n = 0; n < PUTC_n; n++) {
-               if ((code = waddch(win, UChar(PUTC_buf[n]) | attrs)) == ERR) {
+           goto testwrapping;
+       } else if (len > 1) {   /* multi-column characters */
+           /*
+            * Check if the character will fit on the current line.  If it does
+            * not fit, fill in the remainder of the line with blanks.  and
+            * move to the next line.
+            */
+           if (len > win->_maxx + 1) {
+               TR(TRACE_VIRTPUT, ("character will not fit"));
+               return ERR;
+           } else if (x + len > win->_maxx + 1) {
+               int count = win->_maxx + 1 - x;
+               TR(TRACE_VIRTPUT, ("fill %d remaining cells", count));
+               fill_cells(win, count);
+               if (wrap_to_next_line(win) == ERR)
+                   return ERR;
+               x = win->_curx;
+               y = win->_cury;
+               line = win->_line + y;
+           }
+           /*
+            * Check for cells which are orphaned by adding this character, set
+            * those to blanks.
+            *
+            * FIXME: this actually could fill j-i cells, more complicated to
+            * setup though.
+            */
+           for (i = 0; i < len; ++i) {
+               if (isWidecBase(win->_line[y].text[x + i])) {
+                   break;
+               } else if (isWidecExt(win->_line[y].text[x + i])) {
+                   for (j = i; x + j <= win->_maxx; ++j) {
+                       if (!isWidecExt(win->_line[y].text[x + j])) {
+                           TR(TRACE_VIRTPUT, ("fill %d orphan cells", j));
+                           fill_cells(win, j);
+                           break;
+                       }
+                   }
                    break;
                }
            }
-           if (code == ERR)
-               break;
+           /*
+            * Finally, add the cells for this character.
+            */
+           for (i = 0; i < len; ++i) {
+               cchar_t value = ch;
+               SetWidecExt(value, i);
+               TR(TRACE_VIRTPUT, ("multicolumn %d:%d (%d,%d)",
+                                  i + 1, len,
+                                  win->_begy + y, win->_begx + x));
+               line->text[x] = value;
+               CHANGED_CELL(line, x);
+               ++x;
+           }
+           goto testwrapping;
        }
     }
 
-    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
-    return (code);
+    /*
+     * Single-column characters.
+     */
+    line->text[x++] = ch;
+    /*
+     * This label is used only for wide-characters.
+     */
+  testwrapping:
+
+    TR(TRACE_VIRTPUT, ("cell (%ld, %ld..%d) = %s",
+                      (long) win->_cury, (long) win->_curx, x - 1,
+                      _tracech_t(CHREF(ch))));
+
+    if (x > win->_maxx) {
+       return wrap_to_next_line(win);
+    }
+    win->_curx = x;
+    return OK;
 }
 
-NCURSES_EXPORT(int)
-wecho_wchar(WINDOW *win, const cchar_t *wch)
+static NCURSES_INLINE int
+wadd_wch_nosync(WINDOW *win, cchar_t ch)
+/* the workhorse function -- add a character to the given window */
 {
-    PUTC_DATA;
-    int n;
-    int code = ERR;
+    NCURSES_SIZE_T x, y;
+    wchar_t *s;
+    int tabsize = 8;
 
-    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wecho_wchar(%p, %s)"), win,
-                                     _tracech_t(wch)));
-
-    if (win != 0) {
-       PUTC_INIT;
-       for (PUTC_i = 0; PUTC_i < CCHARW_MAX; ++PUTC_i) {
-           attr_t attrs = (wch->attr & A_ATTRIBUTES);
-
-           if ((PUTC_ch = wch->chars[PUTC_i]) == L'\0')
-               break;
-           if ((PUTC_n = wcrtomb(PUTC_buf, PUTC_ch, &PUT_st)) <= 0) {
-               code = ERR;
-               if (is8bits(PUTC_ch))
-                   code = waddch(win, UChar(PUTC_ch) | attrs);
-               break;
+    /*
+     * If we are using the alternate character set, forget about locale.
+     * Otherwise, if the locale * claims the code is printable, treat it that
+     * way.
+     */
+    if ((AttrOf(ch) & A_ALTCHARSET)
+       || iswprint(CharOf(ch)))
+       return wadd_wch_literal(win, ch);
+
+    /*
+     * Handle carriage control and other codes that are not printable, or are
+     * known to expand to more than one character according to unctrl().
+     */
+    x = win->_curx;
+    y = win->_cury;
+
+    switch (CharOf(ch)) {
+    case '\t':
+#if USE_REENTRANT
+       tabsize = sp->_TABSIZE;
+#else
+       tabsize = TABSIZE;
+#endif
+       x += (tabsize - (x % tabsize));
+       /*
+        * Space-fill the tab on the bottom line so that we'll get the
+        * "correct" cursor position.
+        */
+       if ((!win->_scroll && (y == win->_regbottom))
+           || (x <= win->_maxx)) {
+           cchar_t blank = blankchar;
+           AddAttr(blank, AttrOf(ch));
+           while (win->_curx < x) {
+               if (wadd_wch_literal(win, blank) == ERR)
+                   return (ERR);
            }
-           for (n = 0; n < PUTC_n; n++) {
-               if ((code = waddch(win, UChar(PUTC_buf[n]) | attrs)) == ERR) {
-                   break;
+           break;
+       } else {
+           wclrtoeol(win);
+           win->_flags |= _WRAPPED;
+           if (newline_forces_scroll(win, &y)) {
+               x = win->_maxx;
+               if (win->_scroll) {
+                   scroll(win);
+                   x = 0;
                }
+           } else {
+               x = 0;
+           }
+       }
+       break;
+    case '\n':
+       wclrtoeol(win);
+       if (newline_forces_scroll(win, &y)) {
+           if (win->_scroll)
+               scroll(win);
+           else
+               return (ERR);
+       }
+       /* FALLTHRU */
+    case '\r':
+       x = 0;
+       win->_flags &= ~_WRAPPED;
+       break;
+    case '\b':
+       if (x == 0)
+           return (OK);
+       x--;
+       win->_flags &= ~_WRAPPED;
+       break;
+    default:
+       if ((s = wunctrl(&ch)) != 0) {
+           while (*s) {
+               cchar_t sch;
+               SetChar(sch, *s++, AttrOf(ch));
+               if_EXT_COLORS(SetPair(sch, GetPair(ch)));
+               if (wadd_wch_literal(win, sch) == ERR)
+                   return ERR;
            }
-           if (code == ERR)
-               break;
+           return OK;
        }
-       wrefresh(win);
+       return ERR;
     }
 
+    win->_curx = x;
+    win->_cury = y;
+
+    return OK;
+}
+
+/*
+ * The versions below call _nc_synchook().  We wanted to avoid this in the
+ * version exported for string puts; they'll call _nc_synchook once at end
+ * of run.
+ */
+
+/* These are actual entry points */
+
+NCURSES_EXPORT(int)
+wadd_wch(WINDOW *win, const cchar_t *wch)
+{
+    int code = ERR;
+
+    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wadd_wch(%p, %s)"), win,
+                                     _tracecchar_t(wch)));
+
+    if (win && (wadd_wch_nosync(win, *wch) != ERR)) {
+       _nc_synchook(win);
+       code = OK;
+    }
+
+    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
+    return (code);
+}
+
+NCURSES_EXPORT(int)
+wecho_wchar(WINDOW *win, const cchar_t *wch)
+{
+    int code = ERR;
+
+    TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_CALLED("wechochar(%p, %s)"), win,
+                                     _tracecchar_t(wch)));
+
+    if (win && (wadd_wch_nosync(win, *wch) != ERR)) {
+       bool save_immed = win->_immed;
+       win->_immed = TRUE;
+       _nc_synchook(win);
+       win->_immed = save_immed;
+       code = OK;
+    }
     TR(TRACE_VIRTPUT | TRACE_CCALLS, (T_RETURN("%d"), code));
     return (code);
 }