ncurses 6.2 - patch 20200425
[ncurses.git] / form / frm_driver.c
index f78a45e6b060853a0534684f8fce1a00c17ca9a6..b9f91e12f26d2a59754ee2f9ae11081acb66755f 100644 (file)
@@ -1,5 +1,6 @@
 /****************************************************************************
- * Copyright (c) 1998-2014,2015 Free Software Foundation, Inc.              *
+ * Copyright 2018-2019,2020 Thomas E. Dickey                                *
+ * Copyright 1998-2016,2017 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            *
@@ -32,7 +33,7 @@
 
 #include "form.priv.h"
 
-MODULE_ID("$Id: frm_driver.c,v 1.117 2015/11/28 20:39:09 tom Exp $")
+MODULE_ID("$Id: frm_driver.c,v 1.129 2020/02/02 23:34:34 tom Exp $")
 
 /*----------------------------------------------------------------------------
   This is the core module of the form library. It contains the majority
@@ -99,9 +100,9 @@ Perhaps at some time we will make this configurable at runtime.
 #define GROW_IF_NAVIGATE (1)
 
 #if USE_WIDEC_SUPPORT
-#define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
-#define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
-#define myINNSTR(w, s, n)  fix_wchnstr(w, s, n)
+#define myADDNSTR(w, s, n) wide_waddnstr(w, s, n)
+#define myINSNSTR(w, s, n) wide_winsnstr(w, s, n)
+#define myINNSTR(w, s, n)  wide_winnstr(w, s, n)
 #define myWCWIDTH(w, y, x) cell_width(w, y, x)
 #else
 #define myADDNSTR(w, s, n) waddnstr(w, s, n)
@@ -239,9 +240,29 @@ check_pos(FORM *form, int lineno)
   Wide-character special functions
   --------------------------------------------------------------------------*/
 #if USE_WIDEC_SUPPORT
-/* like winsnstr */
+/* add like waddnstr, but using cchar_t* rather than char*
+ */
+static int
+wide_waddnstr(WINDOW *w, const cchar_t *s, int n)
+{
+  int rc = OK;
+
+  while (n-- > 0)
+    {
+      if ((rc = wadd_wch(w, s)) != OK)
+       break;
+      ++s;
+    }
+  return rc;
+}
+
+/* insert like winsnstr, but using cchar_t* rather than char*
+ *
+ * X/Open Curses has no close equivalent; inserts are done only with wchar_t
+ * strings.
+ */
 static int
-wins_wchnstr(WINDOW *w, cchar_t *s, int n)
+wide_winsnstr(WINDOW *w, const cchar_t *s, int n)
 {
   int code = ERR;
   int y, x;
@@ -257,11 +278,13 @@ wins_wchnstr(WINDOW *w, cchar_t *s, int n)
   return code;
 }
 
-/* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
- * the number of items transferred.
+/* retrieve like winnstr, but using cchar_t*, rather than char*.
+ *
+ * X/Open Curses' closest equivalent, win_wchnstr(), is inconsistent with
+ * winnstr(), since it returns OK rather than the number of items transferred.
  */
 static int
-fix_wchnstr(WINDOW *w, cchar_t *s, int n)
+wide_winnstr(WINDOW *w, cchar_t *s, int n)
 {
   int x;
 
@@ -844,11 +867,13 @@ _nc_Position_Form_Cursor(FORM *form)
 |                    E_BAD_ARGUMENT    - invalid form pointer
 |                    E_SYSTEM_ERROR    - general error
 +--------------------------------------------------------------------------*/
+static bool move_after_insert = TRUE;
 NCURSES_EXPORT(int)
 _nc_Refresh_Current_Field(FORM *form)
 {
   WINDOW *formwin;
   FIELD *field;
+  bool is_public;
 
   T((T_CALLED("_nc_Refresh_Current_Field(%p)"), (void *)form));
 
@@ -861,102 +886,105 @@ _nc_Refresh_Current_Field(FORM *form)
   field = form->current;
   formwin = Get_Form_Window(form);
 
-  if (Field_Has_Option(field, O_PUBLIC))
+  is_public = Field_Has_Option(field, O_PUBLIC);
+
+  if (Is_Scroll_Field(field))
     {
-      if (Is_Scroll_Field(field))
+      /* Again, in this case the fieldwin isn't derived from formwin,
+         so we have to perform a copy operation. */
+      if (Single_Line_Field(field))
        {
-         /* Again, in this case the fieldwin isn't derived from formwin,
-            so we have to perform a copy operation. */
-         if (Single_Line_Field(field))
-           {
-             /* horizontal scrolling */
-             if (form->curcol < form->begincol)
-               form->begincol = form->curcol;
-             else
-               {
-                 if (form->curcol >= (form->begincol + field->cols))
-                   form->begincol = form->curcol - field->cols + 1;
-               }
-             copywin(form->w,
-                     formwin,
-                     0,
-                     form->begincol,
-                     field->frow,
-                     field->fcol,
-                     field->frow,
-                     field->cols + field->fcol - 1,
-                     0);
-           }
+         /* horizontal scrolling */
+         if (form->curcol < form->begincol)
+           form->begincol = form->curcol;
          else
            {
-             /* A multi-line, i.e. vertical scrolling field */
-             int row_after_bottom, first_modified_row, first_unmodified_row;
+             if (form->curcol >= (form->begincol + field->cols))
+               form->begincol = form->curcol - field->cols
+                 + (move_after_insert ? 1 : 0);
+           }
+         if (is_public)
+           copywin(form->w,
+                   formwin,
+                   0,
+                   form->begincol,
+                   field->frow,
+                   field->fcol,
+                   field->frow,
+                   field->cols + field->fcol - 1,
+                   0);
+       }
+      else
+       {
+         /* A multi-line, i.e. vertical scrolling field */
+         int row_after_bottom, first_modified_row, first_unmodified_row;
 
-             if (field->drows > field->rows)
+         if (field->drows > field->rows)
+           {
+             row_after_bottom = form->toprow + field->rows;
+             if (form->currow < form->toprow)
                {
-                 row_after_bottom = form->toprow + field->rows;
-                 if (form->currow < form->toprow)
-                   {
-                     form->toprow = form->currow;
-                     SetStatus(field, _NEWTOP);
-                   }
-                 if (form->currow >= row_after_bottom)
-                   {
-                     form->toprow = form->currow - field->rows + 1;
-                     SetStatus(field, _NEWTOP);
-                   }
-                 if (field->status & _NEWTOP)
-                   {
-                     /* means we have to copy whole range */
-                     first_modified_row = form->toprow;
-                     first_unmodified_row = first_modified_row + field->rows;
-                     ClrStatus(field, _NEWTOP);
-                   }
-                 else
-                   {
-                     /* we try to optimize : finding the range of touched
-                        lines */
-                     first_modified_row = form->toprow;
-                     while (first_modified_row < row_after_bottom)
-                       {
-                         if (is_linetouched(form->w, first_modified_row))
-                           break;
-                         first_modified_row++;
-                       }
-                     first_unmodified_row = first_modified_row;
-                     while (first_unmodified_row < row_after_bottom)
-                       {
-                         if (!is_linetouched(form->w, first_unmodified_row))
-                           break;
-                         first_unmodified_row++;
-                       }
-                   }
+                 form->toprow = form->currow;
+                 SetStatus(field, _NEWTOP);
                }
-             else
+             if (form->currow >= row_after_bottom)
                {
+                 form->toprow = form->currow - field->rows + 1;
+                 SetStatus(field, _NEWTOP);
+               }
+             if (field->status & _NEWTOP)
+               {
+                 /* means we have to copy whole range */
                  first_modified_row = form->toprow;
                  first_unmodified_row = first_modified_row + field->rows;
+                 ClrStatus(field, _NEWTOP);
+               }
+             else
+               {
+                 /* we try to optimize : finding the range of touched
+                    lines */
+                 first_modified_row = form->toprow;
+                 while (first_modified_row < row_after_bottom)
+                   {
+                     if (is_linetouched(form->w, first_modified_row))
+                       break;
+                     first_modified_row++;
+                   }
+                 first_unmodified_row = first_modified_row;
+                 while (first_unmodified_row < row_after_bottom)
+                   {
+                     if (!is_linetouched(form->w, first_unmodified_row))
+                       break;
+                     first_unmodified_row++;
+                   }
                }
-             if (first_unmodified_row != first_modified_row)
-               copywin(form->w,
-                       formwin,
-                       first_modified_row,
-                       0,
-                       field->frow + first_modified_row - form->toprow,
-                       field->fcol,
-                       field->frow + first_unmodified_row - form->toprow - 1,
-                       field->cols + field->fcol - 1,
-                       0);
            }
-         wsyncup(formwin);
-       }
-      else
-       {
-         /* if the field-window is simply a derived window, i.e. contains no
-          * invisible parts, the whole thing is trivial
-          */
-         wsyncup(form->w);
+         else
+           {
+             first_modified_row = form->toprow;
+             first_unmodified_row = first_modified_row + field->rows;
+           }
+         if (first_unmodified_row != first_modified_row && is_public)
+           copywin(form->w,
+                   formwin,
+                   first_modified_row,
+                   0,
+                   field->frow + first_modified_row - form->toprow,
+                   field->fcol,
+                   field->frow + first_unmodified_row - form->toprow - 1,
+                   field->cols + field->fcol - 1,
+                   0);
        }
+      if (is_public)
+       wsyncup(formwin);
+    }
+  else
+    {
+      /* if the field-window is simply a derived window, i.e. contains no
+       * invisible parts, the whole thing is trivial
+       */
+      if (is_public)
+       wsyncup(form->w);
     }
   untouchwin(form->w);
   returnCode(_nc_Position_Form_Cursor(form));
@@ -1023,8 +1051,11 @@ static void
 Undo_Justification(FIELD *field, WINDOW *win)
 {
   FIELD_CELL *bp;
+  int y, x;
   int len;
 
+  getyx(win, y, x);
+
   bp = (Field_Has_Option(field, O_NO_LEFT_STRIP)
        ? field->buf
        : Get_Start_Of_Data(field->buf, Buffer_Length(field)));
@@ -1036,6 +1067,7 @@ Undo_Justification(FIELD *field, WINDOW *win)
       wmove(win, 0, 0);
       myADDNSTR(win, bp, len);
     }
+  wmove(win, y, x);
 }
 
 /*---------------------------------------------------------------------------
@@ -1275,7 +1307,8 @@ _nc_Synchronize_Attributes(FIELD *field)
              copywin(form->w, formwin,
                      0, 0,
                      field->frow, field->fcol,
-                     field->rows - 1, field->cols - 1, 0);
+                     field->frow + field->rows - 1,
+                     field->fcol + field->cols - 1, 0);
              wsyncup(formwin);
              Buffer_To_Window(field, form->w);
              SetStatus(field, _NEWTOP);        /* fake refresh to paint all */
@@ -1395,6 +1428,57 @@ _nc_Synchronize_Options(FIELD *field, Field_Options newopts)
   returnCode(res);
 }
 
+/*
+ * Removes the focus from the current field of the form.
+ */
+void
+_nc_Unset_Current_Field(FORM *form)
+{
+  FIELD *field = form->current;
+
+  _nc_Refresh_Current_Field(form);
+  if (Field_Has_Option(field, O_PUBLIC))
+    {
+      if (field->drows > field->rows)
+       {
+         if (form->toprow == 0)
+           ClrStatus(field, _NEWTOP);
+         else
+           SetStatus(field, _NEWTOP);
+       }
+      else
+       {
+         if (Justification_Allowed(field))
+           {
+             Window_To_Buffer(form, field);
+             werase(form->w);
+             Perform_Justification(field, form->w);
+             if (Field_Has_Option(field, O_DYNAMIC_JUSTIFY) &&
+                 (form->w->_parent == 0))
+               {
+                 copywin(form->w,
+                         Get_Form_Window(form),
+                         0,
+                         0,
+                         field->frow,
+                         field->fcol,
+                         field->frow,
+                         field->cols + field->fcol - 1,
+                         0);
+                 wsyncup(Get_Form_Window(form));
+               }
+             else
+               {
+                 wsyncup(form->w);
+               }
+           }
+       }
+    }
+  delwin(form->w);
+  form->w = (WINDOW *)0;
+  form->current = 0;
+}
+
 /*---------------------------------------------------------------------------
 |   Facility      :  libnform
 |   Function      :  int _nc_Set_Current_Field(FORM  * form,
@@ -1415,7 +1499,7 @@ _nc_Set_Current_Field(FORM *form, FIELD *newfield)
 
   T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), (void *)form, (void *)newfield));
 
-  if (!form || !newfield || !form->current || (newfield->form != form))
+  if (!form || !newfield || (newfield->form != form))
     returnCode(E_BAD_ARGUMENT);
 
   if ((form->status & _IN_DRIVER))
@@ -1429,51 +1513,10 @@ _nc_Set_Current_Field(FORM *form, FIELD *newfield)
   if ((field != newfield) ||
       !(form->status & _POSTED))
     {
-      if ((form->w) &&
+      if (field && (form->w) &&
          (Field_Has_Option(field, O_VISIBLE)) &&
          (field->form->curpage == field->page))
-       {
-         _nc_Refresh_Current_Field(form);
-         if (Field_Has_Option(field, O_PUBLIC))
-           {
-             if (field->drows > field->rows)
-               {
-                 if (form->toprow == 0)
-                   ClrStatus(field, _NEWTOP);
-                 else
-                   SetStatus(field, _NEWTOP);
-               }
-             else
-               {
-                 if (Justification_Allowed(field))
-                   {
-                     Window_To_Buffer(form, field);
-                     werase(form->w);
-                     Perform_Justification(field, form->w);
-                     if (Field_Has_Option(field, O_DYNAMIC_JUSTIFY) &&
-                         (form->w->_parent == 0))
-                       {
-                         copywin(form->w,
-                                 Get_Form_Window(form),
-                                 0,
-                                 0,
-                                 field->frow,
-                                 field->fcol,
-                                 field->frow,
-                                 field->cols + field->fcol - 1,
-                                 0);
-                         wsyncup(Get_Form_Window(form));
-                       }
-                     else
-                       {
-                         wsyncup(form->w);
-                       }
-                   }
-               }
-           }
-         delwin(form->w);
-         form->w = (WINDOW *)0;
-       }
+       _nc_Unset_Current_Field(form);
 
       field = newfield;
 
@@ -4034,7 +4077,7 @@ Data_Entry_w(FORM *form, wchar_t c)
       cchar_t temp_ch;
 
       given[0] = c;
-      given[1] = 1;
+      given[1] = 0;
       setcchar(&temp_ch, given, 0, 0, (void *)0);
       if ((Field_Has_Option(field, O_BLANK)) &&
          First_Position_In_Current_Field(form) &&
@@ -4147,6 +4190,12 @@ Data_Entry(FORM *form, int c)
          bool End_Of_Field = (((field->drows - 1) == form->currow) &&
                               ((field->dcols - 1) == form->curcol));
 
+         if (Field_Has_Option(field, O_EDGE_INSERT_STAY))
+           move_after_insert = !!(form->curcol
+                                  - form->begincol
+                                  - field->cols
+                                  + 1);
+
          SetStatus(form, _WINDOW_MODIFIED);
          if (End_Of_Field && !Growable(field) && (Field_Has_Option(field, O_AUTOSKIP)))
            result = Inter_Field_Navigation(FN_Next_Field, form);
@@ -4311,12 +4360,14 @@ form_driver(FORM *form, int c)
   const Binding_Info *BI = (Binding_Info *) 0;
   int res = E_UNKNOWN_COMMAND;
 
+  move_after_insert = TRUE;
+
   T((T_CALLED("form_driver(%p,%d)"), (void *)form, c));
 
   if (!form)
     RETURN(E_BAD_ARGUMENT);
 
-  if (!(form->field))
+  if (!(form->field) || !(form->current))
     RETURN(E_NOT_CONNECTED);
 
   assert(form->page);