GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libform/frm_driver.c Lines: 0 1092 0.0 %
Date: 2017-11-07 Branches: 0 997 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: frm_driver.c,v 1.12 2017/07/29 07:18:32 florian Exp $	*/
2
/****************************************************************************
3
 * Copyright (c) 1998-2007,2008 Free Software Foundation, Inc.              *
4
 *                                                                          *
5
 * Permission is hereby granted, free of charge, to any person obtaining a  *
6
 * copy of this software and associated documentation files (the            *
7
 * "Software"), to deal in the Software without restriction, including      *
8
 * without limitation the rights to use, copy, modify, merge, publish,      *
9
 * distribute, distribute with modifications, sublicense, and/or sell       *
10
 * copies of the Software, and to permit persons to whom the Software is    *
11
 * furnished to do so, subject to the following conditions:                 *
12
 *                                                                          *
13
 * The above copyright notice and this permission notice shall be included  *
14
 * in all copies or substantial portions of the Software.                   *
15
 *                                                                          *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19
 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22
 * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23
 *                                                                          *
24
 * Except as contained in this notice, the name(s) of the above copyright   *
25
 * holders shall not be used in advertising or otherwise to promote the     *
26
 * sale, use or other dealings in this Software without prior written       *
27
 * authorization.                                                           *
28
 ****************************************************************************/
29
30
/****************************************************************************
31
 *   Author:  Juergen Pfeifer, 1995,1997                                    *
32
 ****************************************************************************/
33
34
#include "form.priv.h"
35
36
MODULE_ID("$Id: frm_driver.c,v 1.12 2017/07/29 07:18:32 florian Exp $")
37
38
/*----------------------------------------------------------------------------
39
  This is the core module of the form library. It contains the majority
40
  of the driver routines as well as the form_driver function.
41
42
  Essentially this module is nearly the whole library. This is because
43
  all the functions in this module depends on some others in the module,
44
  so it makes no sense to split them into separate files because they
45
  will always be linked together. The only acceptable concern is turnaround
46
  time for this module, but now we have all Pentiums or RISCs, so what!
47
48
  The driver routines are grouped into nine generic categories:
49
50
   a)   Page Navigation            ( all functions prefixed by PN_ )
51
        The current page of the form is left and some new page is
52
        entered.
53
   b)   Inter-Field Navigation     ( all functions prefixed by FN_ )
54
        The current field of the form is left and some new field is
55
        entered.
56
   c)   Intra-Field Navigation     ( all functions prefixed by IFN_ )
57
        The current position in the current field is changed.
58
   d)   Vertical Scrolling         ( all functions prefixed by VSC_ )
59
        Essentially this is a specialization of Intra-Field navigation.
60
        It has to check for a multi-line field.
61
   e)   Horizontal Scrolling       ( all functions prefixed by HSC_ )
62
        Essentially this is a specialization of Intra-Field navigation.
63
        It has to check for a single-line field.
64
   f)   Field Editing              ( all functions prefixed by FE_ )
65
        The content of the current field is changed
66
   g)   Edit Mode requests         ( all functions prefixed by EM_ )
67
        Switching between insert and overlay mode
68
   h)   Field-Validation requests  ( all functions prefixed by FV_ )
69
        Perform verifications of the field.
70
   i)   Choice requests            ( all functions prefixed by CR_ )
71
        Requests to enumerate possible field values
72
  --------------------------------------------------------------------------*/
73
74
/*----------------------------------------------------------------------------
75
  Some remarks on the placements of assert() macros :
76
  I use them only on "strategic" places, i.e. top level entries where
77
  I want to make sure that things are set correctly. Throughout subordinate
78
  routines I omit them mostly.
79
  --------------------------------------------------------------------------*/
80
81
/*
82
Some options that may effect compatibility in behavior to SVr4 forms,
83
but they are here to allow a more intuitive and user friendly behavior of
84
our form implementation. This doesn't affect the API, so we feel it is
85
uncritical.
86
87
The initial implementation tries to stay very close with the behavior
88
of the original SVr4 implementation, although in some areas it is quite
89
clear that this isn't the most appropriate way. As far as possible this
90
sources will allow you to build a forms lib that behaves quite similar
91
to SVr4, but now and in the future we will give you better options.
92
Perhaps at some time we will make this configurable at runtime.
93
*/
94
95
/* Implement a more user-friendly previous/next word behavior */
96
#define FRIENDLY_PREV_NEXT_WORD (1)
97
/* Fix the wrong behavior for forms with all fields inactive */
98
#define FIX_FORM_INACTIVE_BUG (1)
99
/* Allow dynamic field growth also when navigating past the end */
100
#define GROW_IF_NAVIGATE (1)
101
102
#if USE_WIDEC_SUPPORT
103
#define myADDNSTR(w, s, n) wadd_wchnstr(w, s, n)
104
#define myINSNSTR(w, s, n) wins_wchnstr(w, s, n)
105
#define myINNSTR(w, s, n)  fix_wchnstr(w, s, n)
106
#define myWCWIDTH(w, y, x) cell_width(w, y, x)
107
#else
108
#define myADDNSTR(w, s, n) waddnstr(w, s, n)
109
#define myINSNSTR(w, s, n) winsnstr(w, s, n)
110
#define myINNSTR(w, s, n)  winnstr(w, s, n)
111
#define myWCWIDTH(w, y, x) 1
112
#endif
113
114
/*----------------------------------------------------------------------------
115
  Forward references to some internally used static functions
116
  --------------------------------------------------------------------------*/
117
static int Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form);
118
static int FN_Next_Field(FORM *form);
119
static int FN_Previous_Field(FORM *form);
120
static int FE_New_Line(FORM *);
121
static int FE_Delete_Previous(FORM *);
122
123
/*----------------------------------------------------------------------------
124
  Macro Definitions.
125
126
  Some Remarks on that: I use the convention to use UPPERCASE for constants
127
  defined by Macros. If I provide a macro as a kind of inline routine to
128
  provide some logic, I use my Upper_Lower case style.
129
  --------------------------------------------------------------------------*/
130
131
/* Calculate the position of a single row in a field buffer */
132
#define Position_Of_Row_In_Buffer(field,row) ((row)*(field)->dcols)
133
134
/* Calculate start address for the fields buffer# N */
135
#define Address_Of_Nth_Buffer(field,N) \
136
  ((field)->buf + (N)*(1+Buffer_Length(field)))
137
138
/* Calculate the start address of the row in the fields specified buffer# N */
139
#define Address_Of_Row_In_Nth_Buffer(field,N,row) \
140
  (Address_Of_Nth_Buffer(field,N) + Position_Of_Row_In_Buffer(field,row))
141
142
/* Calculate the start address of the row in the fields primary buffer */
143
#define Address_Of_Row_In_Buffer(field,row) \
144
  Address_Of_Row_In_Nth_Buffer(field,0,row)
145
146
/* Calculate the start address of the row in the forms current field
147
   buffer# N */
148
#define Address_Of_Current_Row_In_Nth_Buffer(form,N) \
149
   Address_Of_Row_In_Nth_Buffer((form)->current,N,(form)->currow)
150
151
/* Calculate the start address of the row in the forms current field
152
   primary buffer */
153
#define Address_Of_Current_Row_In_Buffer(form) \
154
   Address_Of_Current_Row_In_Nth_Buffer(form,0)
155
156
/* Calculate the address of the cursor in the forms current field
157
   primary buffer */
158
#define Address_Of_Current_Position_In_Nth_Buffer(form,N) \
159
   (Address_Of_Current_Row_In_Nth_Buffer(form,N) + (form)->curcol)
160
161
/* Calculate the address of the cursor in the forms current field
162
   buffer# N */
163
#define Address_Of_Current_Position_In_Buffer(form) \
164
  Address_Of_Current_Position_In_Nth_Buffer(form,0)
165
166
/* Logic to decide whether or not a field is actually a field with
167
   vertical or horizontal scrolling */
168
#define Is_Scroll_Field(field)          \
169
   (((field)->drows > (field)->rows) || \
170
    ((field)->dcols > (field)->cols))
171
172
/* Logic to decide whether or not a field needs to have an individual window
173
   instead of a derived window because it contains invisible parts.
174
   This is true for non-public fields and for scrollable fields. */
175
#define Has_Invisible_Parts(field)     \
176
  (!((field)->opts & O_PUBLIC)      || \
177
   Is_Scroll_Field(field))
178
179
/* Logic to decide whether or not a field needs justification */
180
#define Justification_Allowed(field)        \
181
   (((field)->just != NO_JUSTIFICATION)  && \
182
    (Single_Line_Field(field))           && \
183
    (((field)->dcols == (field)->cols)   && \
184
    ((field)->opts & O_STATIC))             )
185
186
/* Logic to determine whether or not a dynamic field may still grow */
187
#define Growable(field) ((field)->status & _MAY_GROW)
188
189
/* Macro to set the attributes for a fields window */
190
#define Set_Field_Window_Attributes(field,win) \
191
(  wbkgdset((win),(chtype)((field)->pad | (field)->back)), \
192
   wattrset((win),(field)->fore) )
193
194
/* Logic to decide whether or not a field really appears on the form */
195
#define Field_Really_Appears(field)         \
196
  ((field->form)                          &&\
197
   (field->form->status & _POSTED)        &&\
198
   (field->opts & O_VISIBLE)              &&\
199
   (field->page == field->form->curpage))
200
201
/* Logic to determine whether or not we are on the first position in the
202
   current field */
203
#define First_Position_In_Current_Field(form) \
204
  (((form)->currow==0) && ((form)->curcol==0))
205
206
#define Minimum(a,b) (((a)<=(b)) ? (a) : (b))
207
#define Maximum(a,b) (((a)>=(b)) ? (a) : (b))
208
209
/*----------------------------------------------------------------------------
210
  Useful constants
211
  --------------------------------------------------------------------------*/
212
static FIELD_CELL myBLANK = BLANK;
213
static FIELD_CELL myZEROS;
214
215
#ifdef TRACE
216
static void
217
check_pos(FORM *form, int lineno)
218
{
219
  int y, x;
220
221
  if (form && form->w)
222
    {
223
      getyx(form->w, y, x);
224
      if (y != form->currow || x != form->curcol)
225
	{
226
	  T(("CHECKPOS %s@%d have position %d,%d vs want %d,%d",
227
	     __FILE__, lineno,
228
	     y, x,
229
	     form->currow, form->curcol));
230
	}
231
    }
232
}
233
#define CHECKPOS(form) check_pos(form, __LINE__)
234
#else
235
#define CHECKPOS(form)		/* nothing */
236
#endif
237
238
/*----------------------------------------------------------------------------
239
  Wide-character special functions
240
  --------------------------------------------------------------------------*/
241
#if USE_WIDEC_SUPPORT
242
/* like winsnstr */
243
static int
244
wins_wchnstr(WINDOW *w, cchar_t *s, int n)
245
{
246
  int code = ERR;
247
  int y, x;
248
249
  while (n-- > 0)
250
    {
251
      getyx(w, y, x);
252
      if ((code = wins_wch(w, s++)) != OK)
253
	break;
254
      if ((code = wmove(w, y, x + 1)) != OK)
255
	break;
256
    }
257
  return code;
258
}
259
260
/* win_wchnstr is inconsistent with winnstr, since it returns OK rather than
261
 * the number of items transferred.
262
 */
263
static int
264
fix_wchnstr(WINDOW *w, cchar_t *s, int n)
265
{
266
  int x;
267
268
  win_wchnstr(w, s, n);
269
  /*
270
   * This function is used to extract the text only from the window.
271
   * Strip attributes and color from the string so they will not be added
272
   * back when copying the string to the window.
273
   */
274
  for (x = 0; x < n; ++x)
275
    {
276
      RemAttr(s[x], A_ATTRIBUTES);
277
      SetPair(s[x], 0);
278
    }
279
  return n;
280
}
281
282
/*
283
 * Returns the column of the base of the given cell.
284
 */
285
static int
286
cell_base(WINDOW *win, int y, int x)
287
{
288
  int result = x;
289
290
  while (LEGALYX(win, y, x))
291
    {
292
      cchar_t *data = &(win->_line[y].text[x]);
293
294
      if (isWidecBase(CHDEREF(data)) || !isWidecExt(CHDEREF(data)))
295
	{
296
	  result = x;
297
	  break;
298
	}
299
      --x;
300
    }
301
  return result;
302
}
303
304
/*
305
 * Returns the number of columns needed for the given cell in a window.
306
 */
307
static int
308
cell_width(WINDOW *win, int y, int x)
309
{
310
  int result = 1;
311
312
  if (LEGALYX(win, y, x))
313
    {
314
      cchar_t *data = &(win->_line[y].text[x]);
315
316
      if (isWidecExt(CHDEREF(data)))
317
	{
318
	  /* recur, providing the number of columns to the next character */
319
	  result = cell_width(win, y, x - 1);
320
	}
321
      else
322
	{
323
	  result = wcwidth(CharOf(CHDEREF(data)));
324
	}
325
    }
326
  return result;
327
}
328
329
/*
330
 * There is no wide-character function such as wdel_wch(), so we must find
331
 * all of the cells that comprise a multi-column character and delete them
332
 * one-by-one.
333
 */
334
static void
335
delete_char(FORM *form)
336
{
337
  int cells = cell_width(form->w, form->currow, form->curcol);
338
339
  form->curcol = cell_base(form->w, form->currow, form->curcol);
340
  wmove(form->w, form->currow, form->curcol);
341
  while (cells-- > 0)
342
    {
343
      wdelch(form->w);
344
    }
345
}
346
#define DeleteChar(form) delete_char(form)
347
#else
348
#define DeleteChar(form) \
349
	  wmove((form)->w, (form)->currow, (form)->curcol), \
350
	  wdelch((form)->w)
351
#endif
352
353
/*---------------------------------------------------------------------------
354
|   Facility      :  libnform
355
|   Function      :  static char *Get_Start_Of_Data(char * buf, int blen)
356
|
357
|   Description   :  Return pointer to first non-blank position in buffer.
358
|                    If buffer is empty return pointer to buffer itself.
359
|
360
|   Return Values :  Pointer to first non-blank position in buffer
361
+--------------------------------------------------------------------------*/
362
NCURSES_INLINE static FIELD_CELL *
363
Get_Start_Of_Data(FIELD_CELL *buf, int blen)
364
{
365
  FIELD_CELL *p = buf;
366
  FIELD_CELL *end = &buf[blen];
367
368
  assert(buf && blen >= 0);
369
  while ((p < end) && ISBLANK(*p))
370
    p++;
371
  return ((p == end) ? buf : p);
372
}
373
374
/*---------------------------------------------------------------------------
375
|   Facility      :  libnform
376
|   Function      :  static char *After_End_Of_Data(char * buf, int blen)
377
|
378
|   Description   :  Return pointer after last non-blank position in buffer.
379
|                    If buffer is empty, return pointer to buffer itself.
380
|
381
|   Return Values :  Pointer to position after last non-blank position in
382
|                    buffer.
383
+--------------------------------------------------------------------------*/
384
NCURSES_INLINE static FIELD_CELL *
385
After_End_Of_Data(FIELD_CELL *buf, int blen)
386
{
387
  FIELD_CELL *p = &buf[blen];
388
389
  assert(buf && blen >= 0);
390
  while ((p > buf) && ISBLANK(p[-1]))
391
    p--;
392
  return (p);
393
}
394
395
/*---------------------------------------------------------------------------
396
|   Facility      :  libnform
397
|   Function      :  static char *Get_First_Whitespace_Character(
398
|                                     char * buf, int   blen)
399
|
400
|   Description   :  Position to the first whitespace character.
401
|
402
|   Return Values :  Pointer to first whitespace character in buffer.
403
+--------------------------------------------------------------------------*/
404
NCURSES_INLINE static FIELD_CELL *
405
Get_First_Whitespace_Character(FIELD_CELL *buf, int blen)
406
{
407
  FIELD_CELL *p = buf;
408
  FIELD_CELL *end = &p[blen];
409
410
  assert(buf && blen >= 0);
411
  while ((p < end) && !ISBLANK(*p))
412
    p++;
413
  return ((p == end) ? buf : p);
414
}
415
416
/*---------------------------------------------------------------------------
417
|   Facility      :  libnform
418
|   Function      :  static char *After_Last_Whitespace_Character(
419
|                                     char * buf, int blen)
420
|
421
|   Description   :  Get the position after the last whitespace character.
422
|
423
|   Return Values :  Pointer to position after last whitespace character in
424
|                    buffer.
425
+--------------------------------------------------------------------------*/
426
NCURSES_INLINE static FIELD_CELL *
427
After_Last_Whitespace_Character(FIELD_CELL *buf, int blen)
428
{
429
  FIELD_CELL *p = &buf[blen];
430
431
  assert(buf && blen >= 0);
432
  while ((p > buf) && !ISBLANK(p[-1]))
433
    p--;
434
  return (p);
435
}
436
437
/* Set this to 1 to use the div_t version. This is a good idea if your
438
   compiler has an intrinsic div() support. Unfortunately GNU-C has it
439
   not yet.
440
   N.B.: This only works if form->curcol follows immediately form->currow
441
         and both are of type int.
442
*/
443
#define USE_DIV_T (0)
444
445
/*---------------------------------------------------------------------------
446
|   Facility      :  libnform
447
|   Function      :  static void Adjust_Cursor_Position(
448
|                                       FORM * form, const char * pos)
449
|
450
|   Description   :  Set current row and column of the form to values
451
|                    corresponding to the buffer position.
452
|
453
|   Return Values :  -
454
+--------------------------------------------------------------------------*/
455
NCURSES_INLINE static void
456
Adjust_Cursor_Position(FORM *form, const FIELD_CELL *pos)
457
{
458
  FIELD *field;
459
  int idx;
460
461
  field = form->current;
462
  assert(pos >= field->buf && field->dcols > 0);
463
  idx = (int)(pos - field->buf);
464
#if USE_DIV_T
465
  *((div_t *) & (form->currow)) = div(idx, field->dcols);
466
#else
467
  form->currow = idx / field->dcols;
468
  form->curcol = idx - field->cols * form->currow;
469
#endif
470
  if (field->drows < form->currow)
471
    form->currow = 0;
472
}
473
474
/*---------------------------------------------------------------------------
475
|   Facility      :  libnform
476
|   Function      :  static void Buffer_To_Window(
477
|                                      const FIELD  * field,
478
|                                      WINDOW * win)
479
|
480
|   Description   :  Copy the buffer to the window. If it is a multi-line
481
|                    field, the buffer is split to the lines of the
482
|                    window without any editing.
483
|
484
|   Return Values :  -
485
+--------------------------------------------------------------------------*/
486
static void
487
Buffer_To_Window(const FIELD *field, WINDOW *win)
488
{
489
  int width, height;
490
  int y, x;
491
  int len;
492
  int row;
493
  FIELD_CELL *pBuffer;
494
495
  assert(win && field);
496
497
  getyx(win, y, x);
498
  width = getmaxx(win);
499
  height = getmaxy(win);
500
501
  for (row = 0, pBuffer = field->buf;
502
       row < height;
503
       row++, pBuffer += width)
504
    {
505
      if ((len = (int)(After_End_Of_Data(pBuffer, width) - pBuffer)) > 0)
506
	{
507
	  wmove(win, row, 0);
508
	  myADDNSTR(win, pBuffer, len);
509
	}
510
    }
511
  wmove(win, y, x);
512
}
513
514
/*---------------------------------------------------------------------------
515
|   Facility      :  libnform
516
|   Function      :  static void Window_To_Buffer(
517
|                                          WINDOW * win,
518
|                                          FIELD  * field)
519
|
520
|   Description   :  Copy the content of the window into the buffer.
521
|                    The multiple lines of a window are simply
522
|                    concatenated into the buffer. Pad characters in
523
|                    the window will be replaced by blanks in the buffer.
524
|
525
|   Return Values :  -
526
+--------------------------------------------------------------------------*/
527
static void
528
Window_To_Buffer(WINDOW *win, FIELD *field)
529
{
530
  int pad;
531
  int len = 0;
532
  FIELD_CELL *p;
533
  int row, height;
534
535
  assert(win && field && field->buf);
536
537
  pad = field->pad;
538
  p = field->buf;
539
  height = getmaxy(win);
540
541
  for (row = 0; (row < height) && (row < field->drows); row++)
542
    {
543
      wmove(win, row, 0);
544
      len += myINNSTR(win, p + len, field->dcols);
545
    }
546
  p[len] = myZEROS;
547
548
  /* replace visual padding character by blanks in buffer */
549
  if (pad != C_BLANK)
550
    {
551
      int i;
552
553
      for (i = 0; i < len; i++, p++)
554
	{
555
	  if ((unsigned long)CharOf(*p) == ChCharOf(pad)
556
#if USE_WIDEC_SUPPORT
557
	      && p->chars[1] == 0
558
#endif
559
	    )
560
	    *p = myBLANK;
561
	}
562
    }
563
}
564
565
/*---------------------------------------------------------------------------
566
|   Facility      :  libnform
567
|   Function      :  static void Synchronize_Buffer(FORM * form)
568
|
569
|   Description   :  If there was a change, copy the content of the
570
|                    window into the buffer, so the buffer is synchronized
571
|                    with the windows content. We have to indicate that the
572
|                    buffer needs validation due to the change.
573
|
574
|   Return Values :  -
575
+--------------------------------------------------------------------------*/
576
NCURSES_INLINE static void
577
Synchronize_Buffer(FORM *form)
578
{
579
  if (form->status & _WINDOW_MODIFIED)
580
    {
581
      form->status &= ~_WINDOW_MODIFIED;
582
      form->status |= _FCHECK_REQUIRED;
583
      Window_To_Buffer(form->w, form->current);
584
      wmove(form->w, form->currow, form->curcol);
585
    }
586
}
587
588
/*---------------------------------------------------------------------------
589
|   Facility      :  libnform
590
|   Function      :  static bool Field_Grown( FIELD *field, int amount)
591
|
592
|   Description   :  This function is called for growable dynamic fields
593
|                    only. It has to increase the buffers and to allocate
594
|                    a new window for this field.
595
|                    This function has the side effect to set a new
596
|                    field-buffer pointer, the dcols and drows values
597
|                    as well as a new current Window for the field.
598
|
599
|   Return Values :  TRUE     - field successfully increased
600
|                    FALSE    - there was some error
601
+--------------------------------------------------------------------------*/
602
static bool
603
Field_Grown(FIELD *field, int amount)
604
{
605
  bool result = FALSE;
606
607
  if (field && Growable(field))
608
    {
609
      bool single_line_field = Single_Line_Field(field);
610
      int old_buflen = Buffer_Length(field);
611
      int new_buflen;
612
      int old_dcols = field->dcols;
613
      int old_drows = field->drows;
614
      FIELD_CELL *oldbuf = field->buf;
615
      FIELD_CELL *newbuf;
616
617
      int growth;
618
      FORM *form = field->form;
619
      bool need_visual_update = ((form != (FORM *)0) &&
620
				 (form->status & _POSTED) &&
621
				 (form->current == field));
622
623
      if (need_visual_update)
624
	Synchronize_Buffer(form);
625
626
      if (single_line_field)
627
	{
628
	  growth = field->cols * amount;
629
	  if (field->maxgrow)
630
	    growth = Minimum(field->maxgrow - field->dcols, growth);
631
	  field->dcols += growth;
632
	  if (field->dcols == field->maxgrow)
633
	    field->status &= ~_MAY_GROW;
634
	}
635
      else
636
	{
637
	  growth = (field->rows + field->nrow) * amount;
638
	  if (field->maxgrow)
639
	    growth = Minimum(field->maxgrow - field->drows, growth);
640
	  field->drows += growth;
641
	  if (field->drows == field->maxgrow)
642
	    field->status &= ~_MAY_GROW;
643
	}
644
      /* drows, dcols changed, so we get really the new buffer length */
645
      new_buflen = Buffer_Length(field);
646
      newbuf = (FIELD_CELL *)malloc(Total_Buffer_Size(field));
647
      if (!newbuf)
648
	{
649
	  /* restore to previous state */
650
	  field->dcols = old_dcols;
651
	  field->drows = old_drows;
652
	  if ((single_line_field && (field->dcols != field->maxgrow)) ||
653
	      (!single_line_field && (field->drows != field->maxgrow)))
654
	    field->status |= _MAY_GROW;
655
	}
656
      else
657
	{
658
	  /* Copy all the buffers.  This is the reason why we can't just use
659
	   * realloc().
660
	   */
661
	  int i, j;
662
	  FIELD_CELL *old_bp;
663
	  FIELD_CELL *new_bp;
664
665
	  result = TRUE;	/* allow sharing of recovery on failure */
666
667
	  T((T_CREATE("fieldcell %p"), newbuf));
668
	  field->buf = newbuf;
669
	  for (i = 0; i <= field->nbuf; i++)
670
	    {
671
	      new_bp = Address_Of_Nth_Buffer(field, i);
672
	      old_bp = oldbuf + i * (1 + old_buflen);
673
	      for (j = 0; j < old_buflen; ++j)
674
		new_bp[j] = old_bp[j];
675
	      while (j < new_buflen)
676
		new_bp[j++] = myBLANK;
677
	      new_bp[new_buflen] = myZEROS;
678
	    }
679
680
#if USE_WIDEC_SUPPORT && NCURSES_EXT_FUNCS
681
	  if (wresize(field->working, 1, Buffer_Length(field) + 1) == ERR)
682
	    result = FALSE;
683
#endif
684
685
	  if (need_visual_update && result)
686
	    {
687
	      WINDOW *new_window = newpad(field->drows, field->dcols);
688
689
	      if (new_window != 0)
690
		{
691
		  assert(form != (FORM *)0);
692
		  if (form->w)
693
		    delwin(form->w);
694
		  form->w = new_window;
695
		  Set_Field_Window_Attributes(field, form->w);
696
		  werase(form->w);
697
		  Buffer_To_Window(field, form->w);
698
		  untouchwin(form->w);
699
		  wmove(form->w, form->currow, form->curcol);
700
		}
701
	      else
702
		result = FALSE;
703
	    }
704
705
	  if (result)
706
	    {
707
	      free(oldbuf);
708
	      /* reflect changes in linked fields */
709
	      if (field != field->link)
710
		{
711
		  FIELD *linked_field;
712
713
		  for (linked_field = field->link;
714
		       linked_field != field;
715
		       linked_field = linked_field->link)
716
		    {
717
		      linked_field->buf = field->buf;
718
		      linked_field->drows = field->drows;
719
		      linked_field->dcols = field->dcols;
720
		    }
721
		}
722
	    }
723
	  else
724
	    {
725
	      /* restore old state */
726
	      field->dcols = old_dcols;
727
	      field->drows = old_drows;
728
	      field->buf = oldbuf;
729
	      if ((single_line_field &&
730
		   (field->dcols != field->maxgrow)) ||
731
		  (!single_line_field &&
732
		   (field->drows != field->maxgrow)))
733
		field->status |= _MAY_GROW;
734
	      free(newbuf);
735
	    }
736
	}
737
    }
738
  return (result);
739
}
740
741
#ifdef NCURSES_MOUSE_VERSION
742
/*---------------------------------------------------------------------------
743
|   Facility      :  libnform
744
|   Function      :  int Field_encloses(FIELD *field, int ry, int rx)
745
|
746
|   Description   :  Check if the given coordinates lie within the given field.
747
|
748
|   Return Values :  E_OK              - success
749
|                    E_BAD_ARGUMENT    - invalid form pointer
750
|                    E_SYSTEM_ERROR    - form has no current field or
751
|                                        field-window
752
+--------------------------------------------------------------------------*/
753
static int
754
Field_encloses(FIELD *field, int ry, int rx)
755
{
756
  T((T_CALLED("Field_encloses(%p)"), field));
757
  if (field != 0
758
      && field->frow <= ry
759
      && (field->frow + field->rows) > ry
760
      && field->fcol <= rx
761
      && (field->fcol + field->cols) > rx)
762
    {
763
      RETURN(E_OK);
764
    }
765
  RETURN(E_INVALID_FIELD);
766
}
767
#endif
768
769
/*---------------------------------------------------------------------------
770
|   Facility      :  libnform
771
|   Function      :  int _nc_Position_Form_Cursor(FORM * form)
772
|
773
|   Description   :  Position the cursor in the window for the current
774
|                    field to be in sync. with the currow and curcol
775
|                    values.
776
|
777
|   Return Values :  E_OK              - success
778
|                    E_BAD_ARGUMENT    - invalid form pointer
779
|                    E_SYSTEM_ERROR    - form has no current field or
780
|                                        field-window
781
+--------------------------------------------------------------------------*/
782
NCURSES_EXPORT(int)
783
_nc_Position_Form_Cursor(FORM *form)
784
{
785
  FIELD *field;
786
  WINDOW *formwin;
787
788
  if (!form)
789
    return (E_BAD_ARGUMENT);
790
791
  if (!form->w || !form->current)
792
    return (E_SYSTEM_ERROR);
793
794
  field = form->current;
795
  formwin = Get_Form_Window(form);
796
797
  wmove(form->w, form->currow, form->curcol);
798
  if (Has_Invisible_Parts(field))
799
    {
800
      /* in this case fieldwin isn't derived from formwin, so we have
801
         to move the cursor in formwin by hand... */
802
      wmove(formwin,
803
	    field->frow + form->currow - form->toprow,
804
	    field->fcol + form->curcol - form->begincol);
805
      wcursyncup(formwin);
806
    }
807
  else
808
    wcursyncup(form->w);
809
  return (E_OK);
810
}
811
812
/*---------------------------------------------------------------------------
813
|   Facility      :  libnform
814
|   Function      :  int _nc_Refresh_Current_Field(FORM * form)
815
|
816
|   Description   :  Propagate the changes in the fields window to the
817
|                    window of the form.
818
|
819
|   Return Values :  E_OK              - on success
820
|                    E_BAD_ARGUMENT    - invalid form pointer
821
|                    E_SYSTEM_ERROR    - general error
822
+--------------------------------------------------------------------------*/
823
NCURSES_EXPORT(int)
824
_nc_Refresh_Current_Field(FORM *form)
825
{
826
  WINDOW *formwin;
827
  FIELD *field;
828
829
  T((T_CALLED("_nc_Refresh_Current_Field(%p)"), form));
830
831
  if (!form)
832
    RETURN(E_BAD_ARGUMENT);
833
834
  if (!form->w || !form->current)
835
    RETURN(E_SYSTEM_ERROR);
836
837
  field = form->current;
838
  formwin = Get_Form_Window(form);
839
840
  if (field->opts & O_PUBLIC)
841
    {
842
      if (Is_Scroll_Field(field))
843
	{
844
	  /* Again, in this case the fieldwin isn't derived from formwin,
845
	     so we have to perform a copy operation. */
846
	  if (Single_Line_Field(field))
847
	    {
848
	      /* horizontal scrolling */
849
	      if (form->curcol < form->begincol)
850
		form->begincol = form->curcol;
851
	      else
852
		{
853
		  if (form->curcol >= (form->begincol + field->cols))
854
		    form->begincol = form->curcol - field->cols + 1;
855
		}
856
	      copywin(form->w,
857
		      formwin,
858
		      0,
859
		      form->begincol,
860
		      field->frow,
861
		      field->fcol,
862
		      field->frow,
863
		      field->cols + field->fcol - 1,
864
		      0);
865
	    }
866
	  else
867
	    {
868
	      /* A multi-line, i.e. vertical scrolling field */
869
	      int row_after_bottom, first_modified_row, first_unmodified_row;
870
871
	      if (field->drows > field->rows)
872
		{
873
		  row_after_bottom = form->toprow + field->rows;
874
		  if (form->currow < form->toprow)
875
		    {
876
		      form->toprow = form->currow;
877
		      field->status |= _NEWTOP;
878
		    }
879
		  if (form->currow >= row_after_bottom)
880
		    {
881
		      form->toprow = form->currow - field->rows + 1;
882
		      field->status |= _NEWTOP;
883
		    }
884
		  if (field->status & _NEWTOP)
885
		    {
886
		      /* means we have to copy whole range */
887
		      first_modified_row = form->toprow;
888
		      first_unmodified_row = first_modified_row + field->rows;
889
		      field->status &= ~_NEWTOP;
890
		    }
891
		  else
892
		    {
893
		      /* we try to optimize : finding the range of touched
894
		         lines */
895
		      first_modified_row = form->toprow;
896
		      while (first_modified_row < row_after_bottom)
897
			{
898
			  if (is_linetouched(form->w, first_modified_row))
899
			    break;
900
			  first_modified_row++;
901
			}
902
		      first_unmodified_row = first_modified_row;
903
		      while (first_unmodified_row < row_after_bottom)
904
			{
905
			  if (!is_linetouched(form->w, first_unmodified_row))
906
			    break;
907
			  first_unmodified_row++;
908
			}
909
		    }
910
		}
911
	      else
912
		{
913
		  first_modified_row = form->toprow;
914
		  first_unmodified_row = first_modified_row + field->rows;
915
		}
916
	      if (first_unmodified_row != first_modified_row)
917
		copywin(form->w,
918
			formwin,
919
			first_modified_row,
920
			0,
921
			field->frow + first_modified_row - form->toprow,
922
			field->fcol,
923
			field->frow + first_unmodified_row - form->toprow - 1,
924
			field->cols + field->fcol - 1,
925
			0);
926
	    }
927
	  wsyncup(formwin);
928
	}
929
      else
930
	{
931
	  /* if the field-window is simply a derived window, i.e. contains no
932
	   * invisible parts, the whole thing is trivial
933
	   */
934
	  wsyncup(form->w);
935
	}
936
    }
937
  untouchwin(form->w);
938
  returnCode(_nc_Position_Form_Cursor(form));
939
}
940
941
/*---------------------------------------------------------------------------
942
|   Facility      :  libnform
943
|   Function      :  static void Perform_Justification(
944
|                                        FIELD  * field,
945
|                                        WINDOW * win)
946
|
947
|   Description   :  Output field with requested justification
948
|
949
|   Return Values :  -
950
+--------------------------------------------------------------------------*/
951
static void
952
Perform_Justification(FIELD *field, WINDOW *win)
953
{
954
  FIELD_CELL *bp;
955
  int len;
956
  int col = 0;
957
958
  bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
959
  len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
960
961
  if (len > 0)
962
    {
963
      assert(win && (field->drows == 1) && (field->dcols == field->cols));
964
965
      switch (field->just)
966
	{
967
	case JUSTIFY_LEFT:
968
	  break;
969
	case JUSTIFY_CENTER:
970
	  col = (field->cols - len) / 2;
971
	  break;
972
	case JUSTIFY_RIGHT:
973
	  col = field->cols - len;
974
	  break;
975
	default:
976
	  break;
977
	}
978
979
      wmove(win, 0, col);
980
      myADDNSTR(win, bp, len);
981
    }
982
}
983
984
/*---------------------------------------------------------------------------
985
|   Facility      :  libnform
986
|   Function      :  static void Undo_Justification(
987
|                                     FIELD  * field,
988
|                                     WINDOW * win)
989
|
990
|   Description   :  Display field without any justification, i.e.
991
|                    left justified
992
|
993
|   Return Values :  -
994
+--------------------------------------------------------------------------*/
995
static void
996
Undo_Justification(FIELD *field, WINDOW *win)
997
{
998
  FIELD_CELL *bp;
999
  int len;
1000
1001
  bp = Get_Start_Of_Data(field->buf, Buffer_Length(field));
1002
  len = (int)(After_End_Of_Data(field->buf, Buffer_Length(field)) - bp);
1003
1004
  if (len > 0)
1005
    {
1006
      assert(win);
1007
      wmove(win, 0, 0);
1008
      myADDNSTR(win, bp, len);
1009
    }
1010
}
1011
1012
/*---------------------------------------------------------------------------
1013
|   Facility      :  libnform
1014
|   Function      :  static bool Check_Char(
1015
|                                           FIELDTYPE * typ,
1016
|                                           int ch,
1017
|                                           TypeArgument *argp)
1018
|
1019
|   Description   :  Perform a single character check for character ch
1020
|                    according to the fieldtype instance.
1021
|
1022
|   Return Values :  TRUE             - Character is valid
1023
|                    FALSE            - Character is invalid
1024
+--------------------------------------------------------------------------*/
1025
static bool
1026
Check_Char(FIELDTYPE *typ, int ch, TypeArgument *argp)
1027
{
1028
  if (typ)
1029
    {
1030
      if (typ->status & _LINKED_TYPE)
1031
	{
1032
	  assert(argp);
1033
	  return (
1034
		   Check_Char(typ->left, ch, argp->left) ||
1035
		   Check_Char(typ->right, ch, argp->right));
1036
	}
1037
      else
1038
	{
1039
	  if (typ->ccheck)
1040
	    return typ->ccheck(ch, (void *)argp);
1041
	}
1042
    }
1043
  return (!iscntrl(UChar(ch)) ? TRUE : FALSE);
1044
}
1045
1046
/*---------------------------------------------------------------------------
1047
|   Facility      :  libnform
1048
|   Function      :  static int Display_Or_Erase_Field(
1049
|                                           FIELD * field,
1050
|                                           bool bEraseFlag)
1051
|
1052
|   Description   :  Create a subwindow for the field and display the
1053
|                    buffer contents (apply justification if required)
1054
|                    or simply erase the field.
1055
|
1056
|   Return Values :  E_OK           - on success
1057
|                    E_SYSTEM_ERROR - some error (typical no memory)
1058
+--------------------------------------------------------------------------*/
1059
static int
1060
Display_Or_Erase_Field(FIELD *field, bool bEraseFlag)
1061
{
1062
  WINDOW *win;
1063
  WINDOW *fwin;
1064
1065
  if (!field)
1066
    return E_SYSTEM_ERROR;
1067
1068
  fwin = Get_Form_Window(field->form);
1069
  win = derwin(fwin,
1070
	       field->rows, field->cols, field->frow, field->fcol);
1071
1072
  if (!win)
1073
    return E_SYSTEM_ERROR;
1074
  else
1075
    {
1076
      if (field->opts & O_VISIBLE)
1077
	Set_Field_Window_Attributes(field, win);
1078
      else
1079
	wattrset(win, WINDOW_ATTRS(fwin));
1080
      werase(win);
1081
    }
1082
1083
  if (!bEraseFlag)
1084
    {
1085
      if (field->opts & O_PUBLIC)
1086
	{
1087
	  if (Justification_Allowed(field))
1088
	    Perform_Justification(field, win);
1089
	  else
1090
	    Buffer_To_Window(field, win);
1091
	}
1092
      field->status &= ~_NEWTOP;
1093
    }
1094
  wsyncup(win);
1095
  delwin(win);
1096
  return E_OK;
1097
}
1098
1099
/* Macros to preset the bEraseFlag */
1100
#define Display_Field(field) Display_Or_Erase_Field(field,FALSE)
1101
#define Erase_Field(field)   Display_Or_Erase_Field(field,TRUE)
1102
1103
/*---------------------------------------------------------------------------
1104
|   Facility      :  libnform
1105
|   Function      :  static int Synchronize_Field(FIELD * field)
1106
|
1107
|   Description   :  Synchronize the windows content with the value in
1108
|                    the buffer.
1109
|
1110
|   Return Values :  E_OK                - success
1111
|                    E_BAD_ARGUMENT      - invalid field pointer
1112
|                    E_SYSTEM_ERROR      - some severe basic error
1113
+--------------------------------------------------------------------------*/
1114
static int
1115
Synchronize_Field(FIELD *field)
1116
{
1117
  FORM *form;
1118
  int res = E_OK;
1119
1120
  if (!field)
1121
    return (E_BAD_ARGUMENT);
1122
1123
  if (((form = field->form) != (FORM *)0)
1124
      && Field_Really_Appears(field))
1125
    {
1126
      if (field == form->current)
1127
	{
1128
	  form->currow = form->curcol = form->toprow = form->begincol = 0;
1129
	  werase(form->w);
1130
1131
	  if ((field->opts & O_PUBLIC) && Justification_Allowed(field))
1132
	    Undo_Justification(field, form->w);
1133
	  else
1134
	    Buffer_To_Window(field, form->w);
1135
1136
	  field->status |= _NEWTOP;
1137
	  res = _nc_Refresh_Current_Field(form);
1138
	}
1139
      else
1140
	res = Display_Field(field);
1141
    }
1142
  field->status |= _CHANGED;
1143
  return (res);
1144
}
1145
1146
/*---------------------------------------------------------------------------
1147
|   Facility      :  libnform
1148
|   Function      :  static int Synchronize_Linked_Fields(FIELD * field)
1149
|
1150
|   Description   :  Propagate the Synchronize_Field function to all linked
1151
|                    fields. The first error that occurs in the sequence
1152
|                    of updates is the return value.
1153
|
1154
|   Return Values :  E_OK                - success
1155
|                    E_BAD_ARGUMENT      - invalid field pointer
1156
|                    E_SYSTEM_ERROR      - some severe basic error
1157
+--------------------------------------------------------------------------*/
1158
static int
1159
Synchronize_Linked_Fields(FIELD *field)
1160
{
1161
  FIELD *linked_field;
1162
  int res = E_OK;
1163
  int syncres;
1164
1165
  if (!field)
1166
    return (E_BAD_ARGUMENT);
1167
1168
  if (!field->link)
1169
    return (E_SYSTEM_ERROR);
1170
1171
  for (linked_field = field->link;
1172
       linked_field != field;
1173
       linked_field = linked_field->link)
1174
    {
1175
      if (((syncres = Synchronize_Field(linked_field)) != E_OK) &&
1176
	  (res == E_OK))
1177
	res = syncres;
1178
    }
1179
  return (res);
1180
}
1181
1182
/*---------------------------------------------------------------------------
1183
|   Facility      :  libnform
1184
|   Function      :  int _nc_Synchronize_Attributes(FIELD * field)
1185
|
1186
|   Description   :  If a fields visual attributes have changed, this
1187
|                    routine is called to propagate those changes to the
1188
|                    screen.
1189
|
1190
|   Return Values :  E_OK             - success
1191
|                    E_BAD_ARGUMENT   - invalid field pointer
1192
|                    E_SYSTEM_ERROR   - some severe basic error
1193
+--------------------------------------------------------------------------*/
1194
NCURSES_EXPORT(int)
1195
_nc_Synchronize_Attributes(FIELD *field)
1196
{
1197
  FORM *form;
1198
  int res = E_OK;
1199
  WINDOW *formwin;
1200
1201
  T((T_CALLED("_nc_Synchronize_Attributes(%p)"), field));
1202
1203
  if (!field)
1204
    returnCode(E_BAD_ARGUMENT);
1205
1206
  CHECKPOS(field->form);
1207
  if (((form = field->form) != (FORM *)0)
1208
      && Field_Really_Appears(field))
1209
    {
1210
      if (form->current == field)
1211
	{
1212
	  Synchronize_Buffer(form);
1213
	  Set_Field_Window_Attributes(field, form->w);
1214
	  werase(form->w);
1215
	  wmove(form->w, form->currow, form->curcol);
1216
1217
	  if (field->opts & O_PUBLIC)
1218
	    {
1219
	      if (Justification_Allowed(field))
1220
		Undo_Justification(field, form->w);
1221
	      else
1222
		Buffer_To_Window(field, form->w);
1223
	    }
1224
	  else
1225
	    {
1226
	      formwin = Get_Form_Window(form);
1227
	      copywin(form->w, formwin,
1228
		      0, 0,
1229
		      field->frow, field->fcol,
1230
		      field->rows - 1, field->cols - 1, 0);
1231
	      wsyncup(formwin);
1232
	      Buffer_To_Window(field, form->w);
1233
	      field->status |= _NEWTOP;		/* fake refresh to paint all */
1234
	      _nc_Refresh_Current_Field(form);
1235
	    }
1236
	}
1237
      else
1238
	{
1239
	  res = Display_Field(field);
1240
	}
1241
    }
1242
  CHECKPOS(form);
1243
  returnCode(res);
1244
}
1245
1246
/*---------------------------------------------------------------------------
1247
|   Facility      :  libnform
1248
|   Function      :  int _nc_Synchronize_Options(FIELD * field,
1249
|                                                Field_Options newopts)
1250
|
1251
|   Description   :  If a fields options have changed, this routine is
1252
|                    called to propagate these changes to the screen and
1253
|                    to really change the behavior of the field.
1254
|
1255
|   Return Values :  E_OK                - success
1256
|                    E_BAD_ARGUMENT      - invalid field pointer
1257
|                    E_CURRENT           - field is the current one
1258
|                    E_SYSTEM_ERROR      - some severe basic error
1259
+--------------------------------------------------------------------------*/
1260
NCURSES_EXPORT(int)
1261
_nc_Synchronize_Options(FIELD *field, Field_Options newopts)
1262
{
1263
  Field_Options oldopts;
1264
  Field_Options changed_opts;
1265
  FORM *form;
1266
  int res = E_OK;
1267
1268
  T((T_CALLED("_nc_Synchronize_Options(%p,%#x)"), field, newopts));
1269
1270
  if (!field)
1271
    returnCode(E_BAD_ARGUMENT);
1272
1273
  oldopts = field->opts;
1274
  changed_opts = oldopts ^ newopts;
1275
  field->opts = newopts;
1276
  form = field->form;
1277
1278
  if (form)
1279
    {
1280
      if (form->current == field)
1281
	{
1282
	  field->opts = oldopts;
1283
	  returnCode(E_CURRENT);
1284
	}
1285
1286
      if (form->status & _POSTED)
1287
	{
1288
	  if (form->curpage == field->page)
1289
	    {
1290
	      if (changed_opts & O_VISIBLE)
1291
		{
1292
		  if (newopts & O_VISIBLE)
1293
		    res = Display_Field(field);
1294
		  else
1295
		    res = Erase_Field(field);
1296
		}
1297
	      else
1298
		{
1299
		  if ((changed_opts & O_PUBLIC) &&
1300
		      (newopts & O_VISIBLE))
1301
		    res = Display_Field(field);
1302
		}
1303
	    }
1304
	}
1305
    }
1306
1307
  if (changed_opts & O_STATIC)
1308
    {
1309
      bool single_line_field = Single_Line_Field(field);
1310
      int res2 = E_OK;
1311
1312
      if (newopts & O_STATIC)
1313
	{
1314
	  /* the field becomes now static */
1315
	  field->status &= ~_MAY_GROW;
1316
	  /* if actually we have no hidden columns, justification may
1317
	     occur again */
1318
	  if (single_line_field &&
1319
	      (field->cols == field->dcols) &&
1320
	      (field->just != NO_JUSTIFICATION) &&
1321
	      Field_Really_Appears(field))
1322
	    {
1323
	      res2 = Display_Field(field);
1324
	    }
1325
	}
1326
      else
1327
	{
1328
	  /* field is no longer static */
1329
	  if ((field->maxgrow == 0) ||
1330
	      (single_line_field && (field->dcols < field->maxgrow)) ||
1331
	      (!single_line_field && (field->drows < field->maxgrow)))
1332
	    {
1333
	      field->status |= _MAY_GROW;
1334
	      /* a field with justification now changes its behavior,
1335
	         so we must redisplay it */
1336
	      if (single_line_field &&
1337
		  (field->just != NO_JUSTIFICATION) &&
1338
		  Field_Really_Appears(field))
1339
		{
1340
		  res2 = Display_Field(field);
1341
		}
1342
	    }
1343
	}
1344
      if (res2 != E_OK)
1345
	res = res2;
1346
    }
1347
1348
  returnCode(res);
1349
}
1350
1351
/*---------------------------------------------------------------------------
1352
|   Facility      :  libnform
1353
|   Function      :  int _nc_Set_Current_Field(FORM  * form,
1354
|                                              FIELD * newfield)
1355
|
1356
|   Description   :  Make the newfield the new current field.
1357
|
1358
|   Return Values :  E_OK              - success
1359
|                    E_BAD_ARGUMENT    - invalid form or field pointer
1360
|                    E_SYSTEM_ERROR    - some severe basic error
1361
|                    E_NOT_CONNECTED   - no fields are connected to the form
1362
+--------------------------------------------------------------------------*/
1363
NCURSES_EXPORT(int)
1364
_nc_Set_Current_Field(FORM *form, FIELD *newfield)
1365
{
1366
  FIELD *field;
1367
  WINDOW *new_window;
1368
1369
  T((T_CALLED("_nc_Set_Current_Field(%p,%p)"), form, newfield));
1370
1371
  if (!form || !newfield || !form->current || (newfield->form != form))
1372
    returnCode(E_BAD_ARGUMENT);
1373
1374
  if ((form->status & _IN_DRIVER))
1375
    returnCode(E_BAD_STATE);
1376
1377
  if (!(form->field))
1378
    returnCode(E_NOT_CONNECTED);
1379
1380
  field = form->current;
1381
1382
  if ((field != newfield) ||
1383
      !(form->status & _POSTED))
1384
    {
1385
      if ((form->w) &&
1386
	  (field->opts & O_VISIBLE) &&
1387
	  (field->form->curpage == field->page))
1388
	{
1389
	  _nc_Refresh_Current_Field(form);
1390
	  if (field->opts & O_PUBLIC)
1391
	    {
1392
	      if (field->drows > field->rows)
1393
		{
1394
		  if (form->toprow == 0)
1395
		    field->status &= ~_NEWTOP;
1396
		  else
1397
		    field->status |= _NEWTOP;
1398
		}
1399
	      else
1400
		{
1401
		  if (Justification_Allowed(field))
1402
		    {
1403
		      Window_To_Buffer(form->w, field);
1404
		      werase(form->w);
1405
		      Perform_Justification(field, form->w);
1406
		      wsyncup(form->w);
1407
		    }
1408
		}
1409
	    }
1410
	  delwin(form->w);
1411
	  form->w = (WINDOW *)0;
1412
	}
1413
1414
      field = newfield;
1415
1416
      if (Has_Invisible_Parts(field))
1417
	new_window = newpad(field->drows, field->dcols);
1418
      else
1419
	new_window = derwin(Get_Form_Window(form),
1420
			    field->rows, field->cols, field->frow, field->fcol);
1421
1422
      if (!new_window)
1423
	returnCode(E_SYSTEM_ERROR);
1424
1425
      form->current = field;
1426
1427
      if (form->w)
1428
	delwin(form->w);
1429
      form->w = new_window;
1430
1431
      form->status &= ~_WINDOW_MODIFIED;
1432
      Set_Field_Window_Attributes(field, form->w);
1433
1434
      if (Has_Invisible_Parts(field))
1435
	{
1436
	  werase(form->w);
1437
	  Buffer_To_Window(field, form->w);
1438
	}
1439
      else
1440
	{
1441
	  if (Justification_Allowed(field))
1442
	    {
1443
	      werase(form->w);
1444
	      Undo_Justification(field, form->w);
1445
	      wsyncup(form->w);
1446
	    }
1447
	}
1448
1449
      untouchwin(form->w);
1450
    }
1451
1452
  form->currow = form->curcol = form->toprow = form->begincol = 0;
1453
  returnCode(E_OK);
1454
}
1455
1456
/*----------------------------------------------------------------------------
1457
  Intra-Field Navigation routines
1458
  --------------------------------------------------------------------------*/
1459
1460
/*---------------------------------------------------------------------------
1461
|   Facility      :  libnform
1462
|   Function      :  static int IFN_Next_Character(FORM * form)
1463
|
1464
|   Description   :  Move to the next character in the field. In a multi-line
1465
|                    field this wraps at the end of the line.
1466
|
1467
|   Return Values :  E_OK                - success
1468
|                    E_REQUEST_DENIED    - at the rightmost position
1469
+--------------------------------------------------------------------------*/
1470
static int
1471
IFN_Next_Character(FORM *form)
1472
{
1473
  FIELD *field = form->current;
1474
  int step = myWCWIDTH(form->w, form->currow, form->curcol);
1475
1476
  T((T_CALLED("IFN_Next_Character(%p)"), form));
1477
  if ((form->curcol += step) == field->dcols)
1478
    {
1479
      if ((++(form->currow)) == field->drows)
1480
	{
1481
#if GROW_IF_NAVIGATE
1482
	  if (!Single_Line_Field(field) && Field_Grown(field, 1))
1483
	    {
1484
	      form->curcol = 0;
1485
	      returnCode(E_OK);
1486
	    }
1487
#endif
1488
	  form->currow--;
1489
#if GROW_IF_NAVIGATE
1490
	  if (Single_Line_Field(field) && Field_Grown(field, 1))
1491
	    returnCode(E_OK);
1492
#endif
1493
	  form->curcol -= step;
1494
	  returnCode(E_REQUEST_DENIED);
1495
	}
1496
      form->curcol = 0;
1497
    }
1498
  returnCode(E_OK);
1499
}
1500
1501
/*---------------------------------------------------------------------------
1502
|   Facility      :  libnform
1503
|   Function      :  static int IFN_Previous_Character(FORM * form)
1504
|
1505
|   Description   :  Move to the previous character in the field. In a
1506
|                    multi-line field this wraps and the beginning of the
1507
|                    line.
1508
|
1509
|   Return Values :  E_OK                - success
1510
|                    E_REQUEST_DENIED    - at the leftmost position
1511
+--------------------------------------------------------------------------*/
1512
static int
1513
IFN_Previous_Character(FORM *form)
1514
{
1515
  int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1516
  int oldcol = form->curcol;
1517
1518
  T((T_CALLED("IFN_Previous_Character(%p)"), form));
1519
  if ((form->curcol -= amount) < 0)
1520
    {
1521
      if ((--(form->currow)) < 0)
1522
	{
1523
	  form->currow++;
1524
	  form->curcol = oldcol;
1525
	  returnCode(E_REQUEST_DENIED);
1526
	}
1527
      form->curcol = form->current->dcols - 1;
1528
    }
1529
  returnCode(E_OK);
1530
}
1531
1532
/*---------------------------------------------------------------------------
1533
|   Facility      :  libnform
1534
|   Function      :  static int IFN_Next_Line(FORM * form)
1535
|
1536
|   Description   :  Move to the beginning of the next line in the field
1537
|
1538
|   Return Values :  E_OK                - success
1539
|                    E_REQUEST_DENIED    - at the last line
1540
+--------------------------------------------------------------------------*/
1541
static int
1542
IFN_Next_Line(FORM *form)
1543
{
1544
  FIELD *field = form->current;
1545
1546
  T((T_CALLED("IFN_Next_Line(%p)"), form));
1547
  if ((++(form->currow)) == field->drows)
1548
    {
1549
#if GROW_IF_NAVIGATE
1550
      if (!Single_Line_Field(field) && Field_Grown(field, 1))
1551
	returnCode(E_OK);
1552
#endif
1553
      form->currow--;
1554
      returnCode(E_REQUEST_DENIED);
1555
    }
1556
  form->curcol = 0;
1557
  returnCode(E_OK);
1558
}
1559
1560
/*---------------------------------------------------------------------------
1561
|   Facility      :  libnform
1562
|   Function      :  static int IFN_Previous_Line(FORM * form)
1563
|
1564
|   Description   :  Move to the beginning of the previous line in the field
1565
|
1566
|   Return Values :  E_OK                - success
1567
|                    E_REQUEST_DENIED    - at the first line
1568
+--------------------------------------------------------------------------*/
1569
static int
1570
IFN_Previous_Line(FORM *form)
1571
{
1572
  T((T_CALLED("IFN_Previous_Line(%p)"), form));
1573
  if ((--(form->currow)) < 0)
1574
    {
1575
      form->currow++;
1576
      returnCode(E_REQUEST_DENIED);
1577
    }
1578
  form->curcol = 0;
1579
  returnCode(E_OK);
1580
}
1581
1582
/*---------------------------------------------------------------------------
1583
|   Facility      :  libnform
1584
|   Function      :  static int IFN_Next_Word(FORM * form)
1585
|
1586
|   Description   :  Move to the beginning of the next word in the field.
1587
|
1588
|   Return Values :  E_OK             - success
1589
|                    E_REQUEST_DENIED - there is no next word
1590
+--------------------------------------------------------------------------*/
1591
static int
1592
IFN_Next_Word(FORM *form)
1593
{
1594
  FIELD *field = form->current;
1595
  FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1596
  FIELD_CELL *s;
1597
  FIELD_CELL *t;
1598
1599
  T((T_CALLED("IFN_Next_Word(%p)"), form));
1600
1601
  /* We really need access to the data, so we have to synchronize */
1602
  Synchronize_Buffer(form);
1603
1604
  /* Go to the first whitespace after the current position (including
1605
     current position). This is then the starting point to look for the
1606
     next non-blank data */
1607
  s = Get_First_Whitespace_Character(bp, Buffer_Length(field) -
1608
				     (int)(bp - field->buf));
1609
1610
  /* Find the start of the next word */
1611
  t = Get_Start_Of_Data(s, Buffer_Length(field) -
1612
			(int)(s - field->buf));
1613
#if !FRIENDLY_PREV_NEXT_WORD
1614
  if (s == t)
1615
    returnCode(E_REQUEST_DENIED);
1616
  else
1617
#endif
1618
    {
1619
      Adjust_Cursor_Position(form, t);
1620
      returnCode(E_OK);
1621
    }
1622
}
1623
1624
/*---------------------------------------------------------------------------
1625
|   Facility      :  libnform
1626
|   Function      :  static int IFN_Previous_Word(FORM * form)
1627
|
1628
|   Description   :  Move to the beginning of the previous word in the field.
1629
|
1630
|   Return Values :  E_OK             - success
1631
|                    E_REQUEST_DENIED - there is no previous word
1632
+--------------------------------------------------------------------------*/
1633
static int
1634
IFN_Previous_Word(FORM *form)
1635
{
1636
  FIELD *field = form->current;
1637
  FIELD_CELL *bp = Address_Of_Current_Position_In_Buffer(form);
1638
  FIELD_CELL *s;
1639
  FIELD_CELL *t;
1640
  bool again = FALSE;
1641
1642
  T((T_CALLED("IFN_Previous_Word(%p)"), form));
1643
1644
  /* We really need access to the data, so we have to synchronize */
1645
  Synchronize_Buffer(form);
1646
1647
  s = After_End_Of_Data(field->buf, (int)(bp - field->buf));
1648
  /* s points now right after the last non-blank in the buffer before bp.
1649
     If bp was in a word, s equals bp. In this case we must find the last
1650
     whitespace in the buffer before bp and repeat the game to really find
1651
     the previous word! */
1652
  if (s == bp)
1653
    again = TRUE;
1654
1655
  /* And next call now goes backward to look for the last whitespace
1656
     before that, pointing right after this, so it points to the begin
1657
     of the previous word.
1658
   */
1659
  t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1660
#if !FRIENDLY_PREV_NEXT_WORD
1661
  if (s == t)
1662
    returnCode(E_REQUEST_DENIED);
1663
#endif
1664
  if (again)
1665
    {
1666
      /* and do it again, replacing bp by t */
1667
      s = After_End_Of_Data(field->buf, (int)(t - field->buf));
1668
      t = After_Last_Whitespace_Character(field->buf, (int)(s - field->buf));
1669
#if !FRIENDLY_PREV_NEXT_WORD
1670
      if (s == t)
1671
	returnCode(E_REQUEST_DENIED);
1672
#endif
1673
    }
1674
  Adjust_Cursor_Position(form, t);
1675
  returnCode(E_OK);
1676
}
1677
1678
/*---------------------------------------------------------------------------
1679
|   Facility      :  libnform
1680
|   Function      :  static int IFN_Beginning_Of_Field(FORM * form)
1681
|
1682
|   Description   :  Place the cursor at the first non-pad character in
1683
|                    the field.
1684
|
1685
|   Return Values :  E_OK             - success
1686
+--------------------------------------------------------------------------*/
1687
static int
1688
IFN_Beginning_Of_Field(FORM *form)
1689
{
1690
  FIELD *field = form->current;
1691
1692
  T((T_CALLED("IFN_Beginning_Of_Field(%p)"), form));
1693
  Synchronize_Buffer(form);
1694
  Adjust_Cursor_Position(form,
1695
			 Get_Start_Of_Data(field->buf, Buffer_Length(field)));
1696
  returnCode(E_OK);
1697
}
1698
1699
/*---------------------------------------------------------------------------
1700
|   Facility      :  libnform
1701
|   Function      :  static int IFN_End_Of_Field(FORM * form)
1702
|
1703
|   Description   :  Place the cursor after the last non-pad character in
1704
|                    the field. If the field occupies the last position in
1705
|                    the buffer, the cursor is positioned on the last
1706
|                    character.
1707
|
1708
|   Return Values :  E_OK              - success
1709
+--------------------------------------------------------------------------*/
1710
static int
1711
IFN_End_Of_Field(FORM *form)
1712
{
1713
  FIELD *field = form->current;
1714
  FIELD_CELL *pos;
1715
1716
  T((T_CALLED("IFN_End_Of_Field(%p)"), form));
1717
  Synchronize_Buffer(form);
1718
  pos = After_End_Of_Data(field->buf, Buffer_Length(field));
1719
  if (pos == (field->buf + Buffer_Length(field)))
1720
    pos--;
1721
  Adjust_Cursor_Position(form, pos);
1722
  returnCode(E_OK);
1723
}
1724
1725
/*---------------------------------------------------------------------------
1726
|   Facility      :  libnform
1727
|   Function      :  static int IFN_Beginning_Of_Line(FORM * form)
1728
|
1729
|   Description   :  Place the cursor on the first non-pad character in
1730
|                    the current line of the field.
1731
|
1732
|   Return Values :  E_OK         - success
1733
+--------------------------------------------------------------------------*/
1734
static int
1735
IFN_Beginning_Of_Line(FORM *form)
1736
{
1737
  FIELD *field = form->current;
1738
1739
  T((T_CALLED("IFN_Beginning_Of_Line(%p)"), form));
1740
  Synchronize_Buffer(form);
1741
  Adjust_Cursor_Position(form,
1742
			 Get_Start_Of_Data(Address_Of_Current_Row_In_Buffer(form),
1743
					   field->dcols));
1744
  returnCode(E_OK);
1745
}
1746
1747
/*---------------------------------------------------------------------------
1748
|   Facility      :  libnform
1749
|   Function      :  static int IFN_End_Of_Line(FORM * form)
1750
|
1751
|   Description   :  Place the cursor after the last non-pad character in the
1752
|                    current line of the field. If the field occupies the
1753
|                    last column in the line, the cursor is positioned on the
1754
|                    last character of the line.
1755
|
1756
|   Return Values :  E_OK        - success
1757
+--------------------------------------------------------------------------*/
1758
static int
1759
IFN_End_Of_Line(FORM *form)
1760
{
1761
  FIELD *field = form->current;
1762
  FIELD_CELL *pos;
1763
  FIELD_CELL *bp;
1764
1765
  T((T_CALLED("IFN_End_Of_Line(%p)"), form));
1766
  Synchronize_Buffer(form);
1767
  bp = Address_Of_Current_Row_In_Buffer(form);
1768
  pos = After_End_Of_Data(bp, field->dcols);
1769
  if (pos == (bp + field->dcols))
1770
    pos--;
1771
  Adjust_Cursor_Position(form, pos);
1772
  returnCode(E_OK);
1773
}
1774
1775
/*---------------------------------------------------------------------------
1776
|   Facility      :  libnform
1777
|   Function      :  static int IFN_Left_Character(FORM * form)
1778
|
1779
|   Description   :  Move one character to the left in the current line.
1780
|                    This doesn't cycle.
1781
|
1782
|   Return Values :  E_OK             - success
1783
|                    E_REQUEST_DENIED - already in first column
1784
+--------------------------------------------------------------------------*/
1785
static int
1786
IFN_Left_Character(FORM *form)
1787
{
1788
  int amount = myWCWIDTH(form->w, form->currow, form->curcol - 1);
1789
  int oldcol = form->curcol;
1790
1791
  T((T_CALLED("IFN_Left_Character(%p)"), form));
1792
  if ((form->curcol -= amount) < 0)
1793
    {
1794
      form->curcol = oldcol;
1795
      returnCode(E_REQUEST_DENIED);
1796
    }
1797
  returnCode(E_OK);
1798
}
1799
1800
/*---------------------------------------------------------------------------
1801
|   Facility      :  libnform
1802
|   Function      :  static int IFN_Right_Character(FORM * form)
1803
|
1804
|   Description   :  Move one character to the right in the current line.
1805
|                    This doesn't cycle.
1806
|
1807
|   Return Values :  E_OK              - success
1808
|                    E_REQUEST_DENIED  - already in last column
1809
+--------------------------------------------------------------------------*/
1810
static int
1811
IFN_Right_Character(FORM *form)
1812
{
1813
  int amount = myWCWIDTH(form->w, form->currow, form->curcol);
1814
  int oldcol = form->curcol;
1815
1816
  T((T_CALLED("IFN_Right_Character(%p)"), form));
1817
  if ((form->curcol += amount) >= form->current->dcols)
1818
    {
1819
#if GROW_IF_NAVIGATE
1820
      FIELD *field = form->current;
1821
1822
      if (Single_Line_Field(field) && Field_Grown(field, 1))
1823
	returnCode(E_OK);
1824
#endif
1825
      form->curcol = oldcol;
1826
      returnCode(E_REQUEST_DENIED);
1827
    }
1828
  returnCode(E_OK);
1829
}
1830
1831
/*---------------------------------------------------------------------------
1832
|   Facility      :  libnform
1833
|   Function      :  static int IFN_Up_Character(FORM * form)
1834
|
1835
|   Description   :  Move one line up. This doesn't cycle through the lines
1836
|                    of the field.
1837
|
1838
|   Return Values :  E_OK              - success
1839
|                    E_REQUEST_DENIED  - already in last column
1840
+--------------------------------------------------------------------------*/
1841
static int
1842
IFN_Up_Character(FORM *form)
1843
{
1844
  T((T_CALLED("IFN_Up_Character(%p)"), form));
1845
  if ((--(form->currow)) < 0)
1846
    {
1847
      form->currow++;
1848
      returnCode(E_REQUEST_DENIED);
1849
    }
1850
  returnCode(E_OK);
1851
}
1852
1853
/*---------------------------------------------------------------------------
1854
|   Facility      :  libnform
1855
|   Function      :  static int IFN_Down_Character(FORM * form)
1856
|
1857
|   Description   :  Move one line down. This doesn't cycle through the
1858
|                    lines of the field.
1859
|
1860
|   Return Values :  E_OK              - success
1861
|                    E_REQUEST_DENIED  - already in last column
1862
+--------------------------------------------------------------------------*/
1863
static int
1864
IFN_Down_Character(FORM *form)
1865
{
1866
  FIELD *field = form->current;
1867
1868
  T((T_CALLED("IFN_Down_Character(%p)"), form));
1869
  if ((++(form->currow)) == field->drows)
1870
    {
1871
#if GROW_IF_NAVIGATE
1872
      if (!Single_Line_Field(field) && Field_Grown(field, 1))
1873
	returnCode(E_OK);
1874
#endif
1875
      --(form->currow);
1876
      returnCode(E_REQUEST_DENIED);
1877
    }
1878
  returnCode(E_OK);
1879
}
1880
/*----------------------------------------------------------------------------
1881
  END of Intra-Field Navigation routines
1882
  --------------------------------------------------------------------------*/
1883
1884
/*----------------------------------------------------------------------------
1885
  Vertical scrolling helper routines
1886
  --------------------------------------------------------------------------*/
1887
1888
/*---------------------------------------------------------------------------
1889
|   Facility      :  libnform
1890
|   Function      :  static int VSC_Generic(FORM *form, int nlines)
1891
|
1892
|   Description   :  Scroll multi-line field forward (nlines>0) or
1893
|                    backward (nlines<0) this many lines.
1894
|
1895
|   Return Values :  E_OK              - success
1896
|                    E_REQUEST_DENIED  - can't scroll
1897
+--------------------------------------------------------------------------*/
1898
static int
1899
VSC_Generic(FORM *form, int nlines)
1900
{
1901
  FIELD *field = form->current;
1902
  int res = E_REQUEST_DENIED;
1903
  int rows_to_go = (nlines > 0 ? nlines : -nlines);
1904
1905
  if (nlines > 0)
1906
    {
1907
      if ((rows_to_go + form->toprow) > (field->drows - field->rows))
1908
	rows_to_go = (field->drows - field->rows - form->toprow);
1909
1910
      if (rows_to_go > 0)
1911
	{
1912
	  form->currow += rows_to_go;
1913
	  form->toprow += rows_to_go;
1914
	  res = E_OK;
1915
	}
1916
    }
1917
  else
1918
    {
1919
      if (rows_to_go > form->toprow)
1920
	rows_to_go = form->toprow;
1921
1922
      if (rows_to_go > 0)
1923
	{
1924
	  form->currow -= rows_to_go;
1925
	  form->toprow -= rows_to_go;
1926
	  res = E_OK;
1927
	}
1928
    }
1929
  return (res);
1930
}
1931
/*----------------------------------------------------------------------------
1932
  End of Vertical scrolling helper routines
1933
  --------------------------------------------------------------------------*/
1934
1935
/*----------------------------------------------------------------------------
1936
  Vertical scrolling routines
1937
  --------------------------------------------------------------------------*/
1938
1939
/*---------------------------------------------------------------------------
1940
|   Facility      :  libnform
1941
|   Function      :  static int Vertical_Scrolling(
1942
|                                           int (* const fct) (FORM *),
1943
|                                           FORM * form)
1944
|
1945
|   Description   :  Performs the generic vertical scrolling routines.
1946
|                    This has to check for a multi-line field and to set
1947
|                    the _NEWTOP flag if scrolling really occurred.
1948
|
1949
|   Return Values :  Propagated error code from low-level driver calls
1950
+--------------------------------------------------------------------------*/
1951
static int
1952
Vertical_Scrolling(int (*const fct) (FORM *), FORM *form)
1953
{
1954
  int res = E_REQUEST_DENIED;
1955
1956
  if (!Single_Line_Field(form->current))
1957
    {
1958
      res = fct(form);
1959
      if (res == E_OK)
1960
	form->current->status |= _NEWTOP;
1961
    }
1962
  return (res);
1963
}
1964
1965
/*---------------------------------------------------------------------------
1966
|   Facility      :  libnform
1967
|   Function      :  static int VSC_Scroll_Line_Forward(FORM * form)
1968
|
1969
|   Description   :  Scroll multi-line field forward a line
1970
|
1971
|   Return Values :  E_OK                - success
1972
|                    E_REQUEST_DENIED    - no data ahead
1973
+--------------------------------------------------------------------------*/
1974
static int
1975
VSC_Scroll_Line_Forward(FORM *form)
1976
{
1977
  T((T_CALLED("VSC_Scroll_Line_Forward(%p)"), form));
1978
  returnCode(VSC_Generic(form, 1));
1979
}
1980
1981
/*---------------------------------------------------------------------------
1982
|   Facility      :  libnform
1983
|   Function      :  static int VSC_Scroll_Line_Backward(FORM * form)
1984
|
1985
|   Description   :  Scroll multi-line field backward a line
1986
|
1987
|   Return Values :  E_OK                - success
1988
|                    E_REQUEST_DENIED    - no data behind
1989
+--------------------------------------------------------------------------*/
1990
static int
1991
VSC_Scroll_Line_Backward(FORM *form)
1992
{
1993
  T((T_CALLED("VSC_Scroll_Line_Backward(%p)"), form));
1994
  returnCode(VSC_Generic(form, -1));
1995
}
1996
1997
/*---------------------------------------------------------------------------
1998
|   Facility      :  libnform
1999
|   Function      :  static int VSC_Scroll_Page_Forward(FORM * form)
2000
|
2001
|   Description   :  Scroll a multi-line field forward a page
2002
|
2003
|   Return Values :  E_OK              - success
2004
|                    E_REQUEST_DENIED  - no data ahead
2005
+--------------------------------------------------------------------------*/
2006
static int
2007
VSC_Scroll_Page_Forward(FORM *form)
2008
{
2009
  T((T_CALLED("VSC_Scroll_Page_Forward(%p)"), form));
2010
  returnCode(VSC_Generic(form, form->current->rows));
2011
}
2012
2013
/*---------------------------------------------------------------------------
2014
|   Facility      :  libnform
2015
|   Function      :  static int VSC_Scroll_Half_Page_Forward(FORM * form)
2016
|
2017
|   Description   :  Scroll a multi-line field forward half a page
2018
|
2019
|   Return Values :  E_OK              - success
2020
|                    E_REQUEST_DENIED  - no data ahead
2021
+--------------------------------------------------------------------------*/
2022
static int
2023
VSC_Scroll_Half_Page_Forward(FORM *form)
2024
{
2025
  T((T_CALLED("VSC_Scroll_Half_Page_Forward(%p)"), form));
2026
  returnCode(VSC_Generic(form, (form->current->rows + 1) / 2));
2027
}
2028
2029
/*---------------------------------------------------------------------------
2030
|   Facility      :  libnform
2031
|   Function      :  static int VSC_Scroll_Page_Backward(FORM * form)
2032
|
2033
|   Description   :  Scroll a multi-line field backward a page
2034
|
2035
|   Return Values :  E_OK              - success
2036
|                    E_REQUEST_DENIED  - no data behind
2037
+--------------------------------------------------------------------------*/
2038
static int
2039
VSC_Scroll_Page_Backward(FORM *form)
2040
{
2041
  T((T_CALLED("VSC_Scroll_Page_Backward(%p)"), form));
2042
  returnCode(VSC_Generic(form, -(form->current->rows)));
2043
}
2044
2045
/*---------------------------------------------------------------------------
2046
|   Facility      :  libnform
2047
|   Function      :  static int VSC_Scroll_Half_Page_Backward(FORM * form)
2048
|
2049
|   Description   :  Scroll a multi-line field backward half a page
2050
|
2051
|   Return Values :  E_OK              - success
2052
|                    E_REQUEST_DENIED  - no data behind
2053
+--------------------------------------------------------------------------*/
2054
static int
2055
VSC_Scroll_Half_Page_Backward(FORM *form)
2056
{
2057
  T((T_CALLED("VSC_Scroll_Half_Page_Backward(%p)"), form));
2058
  returnCode(VSC_Generic(form, -((form->current->rows + 1) / 2)));
2059
}
2060
/*----------------------------------------------------------------------------
2061
  End of Vertical scrolling routines
2062
  --------------------------------------------------------------------------*/
2063
2064
/*----------------------------------------------------------------------------
2065
  Horizontal scrolling helper routines
2066
  --------------------------------------------------------------------------*/
2067
2068
/*---------------------------------------------------------------------------
2069
|   Facility      :  libnform
2070
|   Function      :  static int HSC_Generic(FORM *form, int ncolumns)
2071
|
2072
|   Description   :  Scroll single-line field forward (ncolumns>0) or
2073
|                    backward (ncolumns<0) this many columns.
2074
|
2075
|   Return Values :  E_OK              - success
2076
|                    E_REQUEST_DENIED  - can't scroll
2077
+--------------------------------------------------------------------------*/
2078
static int
2079
HSC_Generic(FORM *form, int ncolumns)
2080
{
2081
  FIELD *field = form->current;
2082
  int res = E_REQUEST_DENIED;
2083
  int cols_to_go = (ncolumns > 0 ? ncolumns : -ncolumns);
2084
2085
  if (ncolumns > 0)
2086
    {
2087
      if ((cols_to_go + form->begincol) > (field->dcols - field->cols))
2088
	cols_to_go = field->dcols - field->cols - form->begincol;
2089
2090
      if (cols_to_go > 0)
2091
	{
2092
	  form->curcol += cols_to_go;
2093
	  form->begincol += cols_to_go;
2094
	  res = E_OK;
2095
	}
2096
    }
2097
  else
2098
    {
2099
      if (cols_to_go > form->begincol)
2100
	cols_to_go = form->begincol;
2101
2102
      if (cols_to_go > 0)
2103
	{
2104
	  form->curcol -= cols_to_go;
2105
	  form->begincol -= cols_to_go;
2106
	  res = E_OK;
2107
	}
2108
    }
2109
  return (res);
2110
}
2111
/*----------------------------------------------------------------------------
2112
  End of Horizontal scrolling helper routines
2113
  --------------------------------------------------------------------------*/
2114
2115
/*----------------------------------------------------------------------------
2116
  Horizontal scrolling routines
2117
  --------------------------------------------------------------------------*/
2118
2119
/*---------------------------------------------------------------------------
2120
|   Facility      :  libnform
2121
|   Function      :  static int Horizontal_Scrolling(
2122
|                                          int (* const fct) (FORM *),
2123
|                                          FORM * form)
2124
|
2125
|   Description   :  Performs the generic horizontal scrolling routines.
2126
|                    This has to check for a single-line field.
2127
|
2128
|   Return Values :  Propagated error code from low-level driver calls
2129
+--------------------------------------------------------------------------*/
2130
static int
2131
Horizontal_Scrolling(int (*const fct) (FORM *), FORM *form)
2132
{
2133
  if (Single_Line_Field(form->current))
2134
    return fct(form);
2135
  else
2136
    return (E_REQUEST_DENIED);
2137
}
2138
2139
/*---------------------------------------------------------------------------
2140
|   Facility      :  libnform
2141
|   Function      :  static int HSC_Scroll_Char_Forward(FORM * form)
2142
|
2143
|   Description   :  Scroll single-line field forward a character
2144
|
2145
|   Return Values :  E_OK                - success
2146
|                    E_REQUEST_DENIED    - no data ahead
2147
+--------------------------------------------------------------------------*/
2148
static int
2149
HSC_Scroll_Char_Forward(FORM *form)
2150
{
2151
  T((T_CALLED("HSC_Scroll_Char_Forward(%p)"), form));
2152
  returnCode(HSC_Generic(form, 1));
2153
}
2154
2155
/*---------------------------------------------------------------------------
2156
|   Facility      :  libnform
2157
|   Function      :  static int HSC_Scroll_Char_Backward(FORM * form)
2158
|
2159
|   Description   :  Scroll single-line field backward a character
2160
|
2161
|   Return Values :  E_OK                - success
2162
|                    E_REQUEST_DENIED    - no data behind
2163
+--------------------------------------------------------------------------*/
2164
static int
2165
HSC_Scroll_Char_Backward(FORM *form)
2166
{
2167
  T((T_CALLED("HSC_Scroll_Char_Backward(%p)"), form));
2168
  returnCode(HSC_Generic(form, -1));
2169
}
2170
2171
/*---------------------------------------------------------------------------
2172
|   Facility      :  libnform
2173
|   Function      :  static int HSC_Horizontal_Line_Forward(FORM* form)
2174
|
2175
|   Description   :  Scroll single-line field forward a line
2176
|
2177
|   Return Values :  E_OK                - success
2178
|                    E_REQUEST_DENIED    - no data ahead
2179
+--------------------------------------------------------------------------*/
2180
static int
2181
HSC_Horizontal_Line_Forward(FORM *form)
2182
{
2183
  T((T_CALLED("HSC_Horizontal_Line_Forward(%p)"), form));
2184
  returnCode(HSC_Generic(form, form->current->cols));
2185
}
2186
2187
/*---------------------------------------------------------------------------
2188
|   Facility      :  libnform
2189
|   Function      :  static int HSC_Horizontal_Half_Line_Forward(FORM* form)
2190
|
2191
|   Description   :  Scroll single-line field forward half a line
2192
|
2193
|   Return Values :  E_OK               - success
2194
|                    E_REQUEST_DENIED   - no data ahead
2195
+--------------------------------------------------------------------------*/
2196
static int
2197
HSC_Horizontal_Half_Line_Forward(FORM *form)
2198
{
2199
  T((T_CALLED("HSC_Horizontal_Half_Line_Forward(%p)"), form));
2200
  returnCode(HSC_Generic(form, (form->current->cols + 1) / 2));
2201
}
2202
2203
/*---------------------------------------------------------------------------
2204
|   Facility      :  libnform
2205
|   Function      :  static int HSC_Horizontal_Line_Backward(FORM* form)
2206
|
2207
|   Description   :  Scroll single-line field backward a line
2208
|
2209
|   Return Values :  E_OK                - success
2210
|                    E_REQUEST_DENIED    - no data behind
2211
+--------------------------------------------------------------------------*/
2212
static int
2213
HSC_Horizontal_Line_Backward(FORM *form)
2214
{
2215
  T((T_CALLED("HSC_Horizontal_Line_Backward(%p)"), form));
2216
  returnCode(HSC_Generic(form, -(form->current->cols)));
2217
}
2218
2219
/*---------------------------------------------------------------------------
2220
|   Facility      :  libnform
2221
|   Function      :  static int HSC_Horizontal_Half_Line_Backward(FORM* form)
2222
|
2223
|   Description   :  Scroll single-line field backward half a line
2224
|
2225
|   Return Values :  E_OK                - success
2226
|                    E_REQUEST_DENIED    - no data behind
2227
+--------------------------------------------------------------------------*/
2228
static int
2229
HSC_Horizontal_Half_Line_Backward(FORM *form)
2230
{
2231
  T((T_CALLED("HSC_Horizontal_Half_Line_Backward(%p)"), form));
2232
  returnCode(HSC_Generic(form, -((form->current->cols + 1) / 2)));
2233
}
2234
2235
/*----------------------------------------------------------------------------
2236
  End of Horizontal scrolling routines
2237
  --------------------------------------------------------------------------*/
2238
2239
/*----------------------------------------------------------------------------
2240
  Helper routines for Field Editing
2241
  --------------------------------------------------------------------------*/
2242
2243
/*---------------------------------------------------------------------------
2244
|   Facility      :  libnform
2245
|   Function      :  static bool Is_There_Room_For_A_Line(FORM * form)
2246
|
2247
|   Description   :  Check whether or not there is enough room in the
2248
|                    buffer to enter a whole line.
2249
|
2250
|   Return Values :  TRUE   - there is enough space
2251
|                    FALSE  - there is not enough space
2252
+--------------------------------------------------------------------------*/
2253
NCURSES_INLINE static bool
2254
Is_There_Room_For_A_Line(FORM *form)
2255
{
2256
  FIELD *field = form->current;
2257
  FIELD_CELL *begin_of_last_line, *s;
2258
2259
  Synchronize_Buffer(form);
2260
  begin_of_last_line = Address_Of_Row_In_Buffer(field, (field->drows - 1));
2261
  s = After_End_Of_Data(begin_of_last_line, field->dcols);
2262
  return ((s == begin_of_last_line) ? TRUE : FALSE);
2263
}
2264
2265
/*---------------------------------------------------------------------------
2266
|   Facility      :  libnform
2267
|   Function      :  static bool Is_There_Room_For_A_Char_In_Line(FORM * form)
2268
|
2269
|   Description   :  Checks whether or not there is room for a new character
2270
|                    in the current line.
2271
|
2272
|   Return Values :  TRUE    - there is room
2273
|                    FALSE   - there is not enough room (line full)
2274
+--------------------------------------------------------------------------*/
2275
NCURSES_INLINE static bool
2276
Is_There_Room_For_A_Char_In_Line(FORM *form)
2277
{
2278
  int last_char_in_line;
2279
2280
  wmove(form->w, form->currow, form->current->dcols - 1);
2281
  last_char_in_line = (int)(winch(form->w) & A_CHARTEXT);
2282
  wmove(form->w, form->currow, form->curcol);
2283
  return (((last_char_in_line == form->current->pad) ||
2284
	   is_blank(last_char_in_line)) ? TRUE : FALSE);
2285
}
2286
2287
#define There_Is_No_Room_For_A_Char_In_Line(f) \
2288
  !Is_There_Room_For_A_Char_In_Line(f)
2289
2290
/*---------------------------------------------------------------------------
2291
|   Facility      :  libnform
2292
|   Function      :  static int Insert_String(
2293
|                                             FORM * form,
2294
|                                             int row,
2295
|                                             char *txt,
2296
|                                             int  len )
2297
|
2298
|   Description   :  Insert the 'len' characters beginning at pointer 'txt'
2299
|                    into the 'row' of the 'form'. The insertion occurs
2300
|                    on the beginning of the row, all other characters are
2301
|                    moved to the right. After the text a pad character will
2302
|                    be inserted to separate the text from the rest. If
2303
|                    necessary the insertion moves characters on the next
2304
|                    line to make place for the requested insertion string.
2305
|
2306
|   Return Values :  E_OK              - success
2307
|                    E_REQUEST_DENIED  -
2308
|                    E_SYSTEM_ERROR    - system error
2309
+--------------------------------------------------------------------------*/
2310
static int
2311
Insert_String(FORM *form, int row, FIELD_CELL *txt, int len)
2312
{
2313
  FIELD *field = form->current;
2314
  FIELD_CELL *bp = Address_Of_Row_In_Buffer(field, row);
2315
  int datalen = (int)(After_End_Of_Data(bp, field->dcols) - bp);
2316
  int freelen = field->dcols - datalen;
2317
  int requiredlen = len + 1;
2318
  FIELD_CELL *split;
2319
  int result = E_REQUEST_DENIED;
2320
2321
  if (freelen >= requiredlen)
2322
    {
2323
      wmove(form->w, row, 0);
2324
      myINSNSTR(form->w, txt, len);
2325
      wmove(form->w, row, len);
2326
      myINSNSTR(form->w, &myBLANK, 1);
2327
      return E_OK;
2328
    }
2329
  else
2330
    {
2331
      /* we have to move characters on the next line. If we are on the
2332
         last line this may work, if the field is growable */
2333
      if ((row == (field->drows - 1)) && Growable(field))
2334
	{
2335
	  if (!Field_Grown(field, 1))
2336
	    return (E_SYSTEM_ERROR);
2337
	  /* !!!Side-Effect : might be changed due to growth!!! */
2338
	  bp = Address_Of_Row_In_Buffer(field, row);
2339
	}
2340
2341
      if (row < (field->drows - 1))
2342
	{
2343
	  split =
2344
	    After_Last_Whitespace_Character(bp,
2345
					    (int)(Get_Start_Of_Data(bp
2346
								    + field->dcols
2347
								    - requiredlen,
2348
								    requiredlen)
2349
						  - bp));
2350
	  /* split points now to the first character of the portion of the
2351
	     line that must be moved to the next line */
2352
	  datalen = (int)(split - bp);	/* + freelen has to stay on this line   */
2353
	  freelen = field->dcols - (datalen + freelen);		/* for the next line */
2354
2355
	  if ((result = Insert_String(form, row + 1, split, freelen)) == E_OK)
2356
	    {
2357
	      wmove(form->w, row, datalen);
2358
	      wclrtoeol(form->w);
2359
	      wmove(form->w, row, 0);
2360
	      myINSNSTR(form->w, txt, len);
2361
	      wmove(form->w, row, len);
2362
	      myINSNSTR(form->w, &myBLANK, 1);
2363
	      return E_OK;
2364
	    }
2365
	}
2366
      return (result);
2367
    }
2368
}
2369
2370
/*---------------------------------------------------------------------------
2371
|   Facility      :  libnform
2372
|   Function      :  static int Wrapping_Not_Necessary_Or_Wrapping_Ok(
2373
|                                             FORM * form)
2374
|
2375
|   Description   :  If a character has been entered into a field, it may
2376
|                    be that wrapping has to occur. This routine checks
2377
|                    whether or not wrapping is required and if so, performs
2378
|                    the wrapping.
2379
|
2380
|   Return Values :  E_OK              - no wrapping required or wrapping
2381
|                                        was successful
2382
|                    E_REQUEST_DENIED  -
2383
|                    E_SYSTEM_ERROR    - some system error
2384
+--------------------------------------------------------------------------*/
2385
static int
2386
Wrapping_Not_Necessary_Or_Wrapping_Ok(FORM *form)
2387
{
2388
  FIELD *field = form->current;
2389
  int result = E_REQUEST_DENIED;
2390
  bool Last_Row = ((field->drows - 1) == form->currow);
2391
2392
  if ((field->opts & O_WRAP) &&	/* wrapping wanted     */
2393
      (!Single_Line_Field(field)) &&	/* must be multi-line  */
2394
      (There_Is_No_Room_For_A_Char_In_Line(form)) &&	/* line is full        */
2395
      (!Last_Row || Growable(field)))	/* there are more lines */
2396
    {
2397
      FIELD_CELL *bp;
2398
      FIELD_CELL *split;
2399
      int chars_to_be_wrapped;
2400
      int chars_to_remain_on_line;
2401
2402
      if (Last_Row)
2403
	{
2404
	  /* the above logic already ensures, that in this case the field
2405
	     is growable */
2406
	  if (!Field_Grown(field, 1))
2407
	    return E_SYSTEM_ERROR;
2408
	}
2409
      bp = Address_Of_Current_Row_In_Buffer(form);
2410
      Window_To_Buffer(form->w, field);
2411
      split = After_Last_Whitespace_Character(bp, field->dcols);
2412
      /* split points to the first character of the sequence to be brought
2413
         on the next line */
2414
      chars_to_remain_on_line = (int)(split - bp);
2415
      chars_to_be_wrapped = field->dcols - chars_to_remain_on_line;
2416
      if (chars_to_remain_on_line > 0)
2417
	{
2418
	  if ((result = Insert_String(form, form->currow + 1, split,
2419
				      chars_to_be_wrapped)) == E_OK)
2420
	    {
2421
	      wmove(form->w, form->currow, chars_to_remain_on_line);
2422
	      wclrtoeol(form->w);
2423
	      if (form->curcol >= chars_to_remain_on_line)
2424
		{
2425
		  form->currow++;
2426
		  form->curcol -= chars_to_remain_on_line;
2427
		}
2428
	      return E_OK;
2429
	    }
2430
	}
2431
      else
2432
	return E_OK;
2433
      if (result != E_OK)
2434
	{
2435
	  DeleteChar(form);
2436
	  Window_To_Buffer(form->w, field);
2437
	  result = E_REQUEST_DENIED;
2438
	}
2439
    }
2440
  else
2441
    result = E_OK;		/* wrapping was not necessary */
2442
  return (result);
2443
}
2444
2445
/*----------------------------------------------------------------------------
2446
  Field Editing routines
2447
  --------------------------------------------------------------------------*/
2448
2449
/*---------------------------------------------------------------------------
2450
|   Facility      :  libnform
2451
|   Function      :  static int Field_Editing(
2452
|                                    int (* const fct) (FORM *),
2453
|                                    FORM * form)
2454
|
2455
|   Description   :  Generic routine for field editing requests. The driver
2456
|                    routines are only called for editable fields, the
2457
|                    _WINDOW_MODIFIED flag is set if editing occurred.
2458
|                    This is somewhat special due to the overload semantics
2459
|                    of the NEW_LINE and DEL_PREV requests.
2460
|
2461
|   Return Values :  Error code from low level drivers.
2462
+--------------------------------------------------------------------------*/
2463
static int
2464
Field_Editing(int (*const fct) (FORM *), FORM *form)
2465
{
2466
  int res = E_REQUEST_DENIED;
2467
2468
  /* We have to deal here with the specific case of the overloaded
2469
     behavior of New_Line and Delete_Previous requests.
2470
     They may end up in navigational requests if we are on the first
2471
     character in a field. But navigation is also allowed on non-
2472
     editable fields.
2473
   */
2474
  if ((fct == FE_Delete_Previous) &&
2475
      (form->opts & O_BS_OVERLOAD) &&
2476
      First_Position_In_Current_Field(form))
2477
    {
2478
      res = Inter_Field_Navigation(FN_Previous_Field, form);
2479
    }
2480
  else
2481
    {
2482
      if (fct == FE_New_Line)
2483
	{
2484
	  if ((form->opts & O_NL_OVERLOAD) &&
2485
	      First_Position_In_Current_Field(form))
2486
	    {
2487
	      res = Inter_Field_Navigation(FN_Next_Field, form);
2488
	    }
2489
	  else
2490
	    /* FE_New_Line deals itself with the _WINDOW_MODIFIED flag */
2491
	    res = fct(form);
2492
	}
2493
      else
2494
	{
2495
	  /* From now on, everything must be editable */
2496
	  if (form->current->opts & O_EDIT)
2497
	    {
2498
	      res = fct(form);
2499
	      if (res == E_OK)
2500
		form->status |= _WINDOW_MODIFIED;
2501
	    }
2502
	}
2503
    }
2504
  return res;
2505
}
2506
2507
/*---------------------------------------------------------------------------
2508
|   Facility      :  libnform
2509
|   Function      :  static int FE_New_Line(FORM * form)
2510
|
2511
|   Description   :  Perform a new line request. This is rather complex
2512
|                    compared to other routines in this code due to the
2513
|                    rather difficult to understand description in the
2514
|                    manuals.
2515
|
2516
|   Return Values :  E_OK               - success
2517
|                    E_REQUEST_DENIED   - new line not allowed
2518
|                    E_SYSTEM_ERROR     - system error
2519
+--------------------------------------------------------------------------*/
2520
static int
2521
FE_New_Line(FORM *form)
2522
{
2523
  FIELD *field = form->current;
2524
  FIELD_CELL *bp, *t;
2525
  bool Last_Row = ((field->drows - 1) == form->currow);
2526
2527
  T((T_CALLED("FE_New_Line(%p)"), form));
2528
  if (form->status & _OVLMODE)
2529
    {
2530
      if (Last_Row &&
2531
	  (!(Growable(field) && !Single_Line_Field(field))))
2532
	{
2533
	  if (!(form->opts & O_NL_OVERLOAD))
2534
	    returnCode(E_REQUEST_DENIED);
2535
	  wmove(form->w, form->currow, form->curcol);
2536
	  wclrtoeol(form->w);
2537
	  /* we have to set this here, although it is also
2538
	     handled in the generic routine. The reason is,
2539
	     that FN_Next_Field may fail, but the form is
2540
	     definitively changed */
2541
	  form->status |= _WINDOW_MODIFIED;
2542
	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2543
	}
2544
      else
2545
	{
2546
	  if (Last_Row && !Field_Grown(field, 1))
2547
	    {
2548
	      /* N.B.: due to the logic in the 'if', LastRow==TRUE
2549
	         means here that the field is growable and not
2550
	         a single-line field */
2551
	      returnCode(E_SYSTEM_ERROR);
2552
	    }
2553
	  wmove(form->w, form->currow, form->curcol);
2554
	  wclrtoeol(form->w);
2555
	  form->currow++;
2556
	  form->curcol = 0;
2557
	  form->status |= _WINDOW_MODIFIED;
2558
	  returnCode(E_OK);
2559
	}
2560
    }
2561
  else
2562
    {
2563
      /* Insert Mode */
2564
      if (Last_Row &&
2565
	  !(Growable(field) && !Single_Line_Field(field)))
2566
	{
2567
	  if (!(form->opts & O_NL_OVERLOAD))
2568
	    returnCode(E_REQUEST_DENIED);
2569
	  returnCode(Inter_Field_Navigation(FN_Next_Field, form));
2570
	}
2571
      else
2572
	{
2573
	  bool May_Do_It = !Last_Row && Is_There_Room_For_A_Line(form);
2574
2575
	  if (!(May_Do_It || Growable(field)))
2576
	    returnCode(E_REQUEST_DENIED);
2577
	  if (!May_Do_It && !Field_Grown(field, 1))
2578
	    returnCode(E_SYSTEM_ERROR);
2579
2580
	  bp = Address_Of_Current_Position_In_Buffer(form);
2581
	  t = After_End_Of_Data(bp, field->dcols - form->curcol);
2582
	  wmove(form->w, form->currow, form->curcol);
2583
	  wclrtoeol(form->w);
2584
	  form->currow++;
2585
	  form->curcol = 0;
2586
	  wmove(form->w, form->currow, form->curcol);
2587
	  winsertln(form->w);
2588
	  myADDNSTR(form->w, bp, (int)(t - bp));
2589
	  form->status |= _WINDOW_MODIFIED;
2590
	  returnCode(E_OK);
2591
	}
2592
    }
2593
}
2594
2595
/*---------------------------------------------------------------------------
2596
|   Facility      :  libnform
2597
|   Function      :  static int FE_Insert_Character(FORM * form)
2598
|
2599
|   Description   :  Insert blank character at the cursor position
2600
|
2601
|   Return Values :  E_OK
2602
|                    E_REQUEST_DENIED
2603
+--------------------------------------------------------------------------*/
2604
static int
2605
FE_Insert_Character(FORM *form)
2606
{
2607
  FIELD *field = form->current;
2608
  int result = E_REQUEST_DENIED;
2609
2610
  T((T_CALLED("FE_Insert_Character(%p)"), form));
2611
  if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2612
    {
2613
      bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
2614
2615
      if (There_Is_Room ||
2616
	  ((Single_Line_Field(field) && Growable(field))))
2617
	{
2618
	  if (!There_Is_Room && !Field_Grown(field, 1))
2619
	    result = E_SYSTEM_ERROR;
2620
	  else
2621
	    {
2622
	      winsch(form->w, (chtype)C_BLANK);
2623
	      result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form);
2624
	    }
2625
	}
2626
    }
2627
  returnCode(result);
2628
}
2629
2630
/*---------------------------------------------------------------------------
2631
|   Facility      :  libnform
2632
|   Function      :  static int FE_Insert_Line(FORM * form)
2633
|
2634
|   Description   :  Insert a blank line at the cursor position
2635
|
2636
|   Return Values :  E_OK               - success
2637
|                    E_REQUEST_DENIED   - line can not be inserted
2638
+--------------------------------------------------------------------------*/
2639
static int
2640
FE_Insert_Line(FORM *form)
2641
{
2642
  FIELD *field = form->current;
2643
  int result = E_REQUEST_DENIED;
2644
2645
  T((T_CALLED("FE_Insert_Line(%p)"), form));
2646
  if (Check_Char(field->type, (int)C_BLANK, (TypeArgument *)(field->arg)))
2647
    {
2648
      bool Maybe_Done = (form->currow != (field->drows - 1)) &&
2649
      Is_There_Room_For_A_Line(form);
2650
2651
      if (!Single_Line_Field(field) &&
2652
	  (Maybe_Done || Growable(field)))
2653
	{
2654
	  if (!Maybe_Done && !Field_Grown(field, 1))
2655
	    result = E_SYSTEM_ERROR;
2656
	  else
2657
	    {
2658
	      form->curcol = 0;
2659
	      winsertln(form->w);
2660
	      result = E_OK;
2661
	    }
2662
	}
2663
    }
2664
  returnCode(result);
2665
}
2666
2667
/*---------------------------------------------------------------------------
2668
|   Facility      :  libnform
2669
|   Function      :  static int FE_Delete_Character(FORM * form)
2670
|
2671
|   Description   :  Delete character at the cursor position
2672
|
2673
|   Return Values :  E_OK    - success
2674
+--------------------------------------------------------------------------*/
2675
static int
2676
FE_Delete_Character(FORM *form)
2677
{
2678
  T((T_CALLED("FE_Delete_Character(%p)"), form));
2679
  DeleteChar(form);
2680
  returnCode(E_OK);
2681
}
2682
2683
/*---------------------------------------------------------------------------
2684
|   Facility      :  libnform
2685
|   Function      :  static int FE_Delete_Previous(FORM * form)
2686
|
2687
|   Description   :  Delete character before cursor. Again this is a rather
2688
|                    difficult piece compared to others due to the overloading
2689
|                    semantics of backspace.
2690
|                    N.B.: The case of overloaded BS on first field position
2691
|                          is already handled in the generic routine.
2692
|
2693
|   Return Values :  E_OK                - success
2694
|                    E_REQUEST_DENIED    - Character can't be deleted
2695
+--------------------------------------------------------------------------*/
2696
static int
2697
FE_Delete_Previous(FORM *form)
2698
{
2699
  FIELD *field = form->current;
2700
2701
  T((T_CALLED("FE_Delete_Previous(%p)"), form));
2702
  if (First_Position_In_Current_Field(form))
2703
    returnCode(E_REQUEST_DENIED);
2704
2705
  if ((--(form->curcol)) < 0)
2706
    {
2707
      FIELD_CELL *this_line, *prev_line, *prev_end, *this_end;
2708
      int this_row = form->currow;
2709
2710
      form->curcol++;
2711
      if (form->status & _OVLMODE)
2712
	returnCode(E_REQUEST_DENIED);
2713
2714
      prev_line = Address_Of_Row_In_Buffer(field, (form->currow - 1));
2715
      this_line = Address_Of_Row_In_Buffer(field, (form->currow));
2716
      Synchronize_Buffer(form);
2717
      prev_end = After_End_Of_Data(prev_line, field->dcols);
2718
      this_end = After_End_Of_Data(this_line, field->dcols);
2719
      if ((int)(this_end - this_line) >
2720
	  (field->cols - (int)(prev_end - prev_line)))
2721
	returnCode(E_REQUEST_DENIED);
2722
      wmove(form->w, form->currow, form->curcol);
2723
      wdeleteln(form->w);
2724
      Adjust_Cursor_Position(form, prev_end);
2725
      /*
2726
       * If we did not really move to the previous line, help the user a
2727
       * little.  It is however a little inconsistent.  Normally, when
2728
       * backspacing around the point where text wraps to a new line in a
2729
       * multi-line form, we absorb one keystroke for the wrapping point.  That
2730
       * is consistent with SVr4 forms.  However, SVr4 does not allow typing
2731
       * into the last column of the field, and requires the user to enter a
2732
       * newline to move to the next line.  Therefore it can consistently eat
2733
       * that keystroke.  Since ncurses allows the last column, it wraps
2734
       * automatically (given the proper options).  But we cannot eat the
2735
       * keystroke to back over the wrapping point, since that would put the
2736
       * cursor past the end of the form field.  In this case, just delete the
2737
       * character at the end of the field.
2738
       */
2739
      if (form->currow == this_row && this_row > 0)
2740
	{
2741
	  form->currow -= 1;
2742
	  form->curcol = field->dcols - 1;
2743
	  DeleteChar(form);
2744
	}
2745
      else
2746
	{
2747
	  wmove(form->w, form->currow, form->curcol);
2748
	  myADDNSTR(form->w, this_line, (int)(this_end - this_line));
2749
	}
2750
    }
2751
  else
2752
    {
2753
      DeleteChar(form);
2754
    }
2755
  returnCode(E_OK);
2756
}
2757
2758
/*---------------------------------------------------------------------------
2759
|   Facility      :  libnform
2760
|   Function      :  static int FE_Delete_Line(FORM * form)
2761
|
2762
|   Description   :  Delete line at cursor position.
2763
|
2764
|   Return Values :  E_OK  - success
2765
+--------------------------------------------------------------------------*/
2766
static int
2767
FE_Delete_Line(FORM *form)
2768
{
2769
  T((T_CALLED("FE_Delete_Line(%p)"), form));
2770
  form->curcol = 0;
2771
  wdeleteln(form->w);
2772
  returnCode(E_OK);
2773
}
2774
2775
/*---------------------------------------------------------------------------
2776
|   Facility      :  libnform
2777
|   Function      :  static int FE_Delete_Word(FORM * form)
2778
|
2779
|   Description   :  Delete word at cursor position
2780
|
2781
|   Return Values :  E_OK               - success
2782
|                    E_REQUEST_DENIED   - failure
2783
+--------------------------------------------------------------------------*/
2784
static int
2785
FE_Delete_Word(FORM *form)
2786
{
2787
  FIELD *field = form->current;
2788
  FIELD_CELL *bp = Address_Of_Current_Row_In_Buffer(form);
2789
  FIELD_CELL *ep = bp + field->dcols;
2790
  FIELD_CELL *cp = bp + form->curcol;
2791
  FIELD_CELL *s;
2792
2793
  T((T_CALLED("FE_Delete_Word(%p)"), form));
2794
  Synchronize_Buffer(form);
2795
  if (ISBLANK(*cp))
2796
    returnCode(E_REQUEST_DENIED);	/* not in word */
2797
2798
  /* move cursor to begin of word and erase to end of screen-line */
2799
  Adjust_Cursor_Position(form,
2800
			 After_Last_Whitespace_Character(bp, form->curcol));
2801
  wmove(form->w, form->currow, form->curcol);
2802
  wclrtoeol(form->w);
2803
2804
  /* skip over word in buffer */
2805
  s = Get_First_Whitespace_Character(cp, (int)(ep - cp));
2806
  /* to begin of next word    */
2807
  s = Get_Start_Of_Data(s, (int)(ep - s));
2808
  if ((s != cp) && !ISBLANK(*s))
2809
    {
2810
      /* copy remaining line to window */
2811
      myADDNSTR(form->w, s, (int)(s - After_End_Of_Data(s, (int)(ep - s))));
2812
    }
2813
  returnCode(E_OK);
2814
}
2815
2816
/*---------------------------------------------------------------------------
2817
|   Facility      :  libnform
2818
|   Function      :  static int FE_Clear_To_End_Of_Line(FORM * form)
2819
|
2820
|   Description   :  Clear to end of current line.
2821
|
2822
|   Return Values :  E_OK   - success
2823
+--------------------------------------------------------------------------*/
2824
static int
2825
FE_Clear_To_End_Of_Line(FORM *form)
2826
{
2827
  T((T_CALLED("FE_Clear_To_End_Of_Line(%p)"), form));
2828
  wmove(form->w, form->currow, form->curcol);
2829
  wclrtoeol(form->w);
2830
  returnCode(E_OK);
2831
}
2832
2833
/*---------------------------------------------------------------------------
2834
|   Facility      :  libnform
2835
|   Function      :  static int FE_Clear_To_End_Of_Field(FORM * form)
2836
|
2837
|   Description   :  Clear to end of field.
2838
|
2839
|   Return Values :  E_OK   - success
2840
+--------------------------------------------------------------------------*/
2841
static int
2842
FE_Clear_To_End_Of_Field(FORM *form)
2843
{
2844
  T((T_CALLED("FE_Clear_To_End_Of_Field(%p)"), form));
2845
  wmove(form->w, form->currow, form->curcol);
2846
  wclrtobot(form->w);
2847
  returnCode(E_OK);
2848
}
2849
2850
/*---------------------------------------------------------------------------
2851
|   Facility      :  libnform
2852
|   Function      :  static int FE_Clear_Field(FORM * form)
2853
|
2854
|   Description   :  Clear entire field.
2855
|
2856
|   Return Values :  E_OK   - success
2857
+--------------------------------------------------------------------------*/
2858
static int
2859
FE_Clear_Field(FORM *form)
2860
{
2861
  T((T_CALLED("FE_Clear_Field(%p)"), form));
2862
  form->currow = form->curcol = 0;
2863
  werase(form->w);
2864
  returnCode(E_OK);
2865
}
2866
/*----------------------------------------------------------------------------
2867
  END of Field Editing routines
2868
  --------------------------------------------------------------------------*/
2869
2870
/*----------------------------------------------------------------------------
2871
  Edit Mode routines
2872
  --------------------------------------------------------------------------*/
2873
2874
/*---------------------------------------------------------------------------
2875
|   Facility      :  libnform
2876
|   Function      :  static int EM_Overlay_Mode(FORM * form)
2877
|
2878
|   Description   :  Switch to overlay mode.
2879
|
2880
|   Return Values :  E_OK   - success
2881
+--------------------------------------------------------------------------*/
2882
static int
2883
EM_Overlay_Mode(FORM *form)
2884
{
2885
  T((T_CALLED("EM_Overlay_Mode(%p)"), form));
2886
  form->status |= _OVLMODE;
2887
  returnCode(E_OK);
2888
}
2889
2890
/*---------------------------------------------------------------------------
2891
|   Facility      :  libnform
2892
|   Function      :  static int EM_Insert_Mode(FORM * form)
2893
|
2894
|   Description   :  Switch to insert mode
2895
|
2896
|   Return Values :  E_OK   - success
2897
+--------------------------------------------------------------------------*/
2898
static int
2899
EM_Insert_Mode(FORM *form)
2900
{
2901
  T((T_CALLED("EM_Insert_Mode(%p)"), form));
2902
  form->status &= ~_OVLMODE;
2903
  returnCode(E_OK);
2904
}
2905
2906
/*----------------------------------------------------------------------------
2907
  END of Edit Mode routines
2908
  --------------------------------------------------------------------------*/
2909
2910
/*----------------------------------------------------------------------------
2911
  Helper routines for Choice Requests
2912
  --------------------------------------------------------------------------*/
2913
2914
/*---------------------------------------------------------------------------
2915
|   Facility      :  libnform
2916
|   Function      :  static bool Next_Choice(
2917
|                                            FIELDTYPE * typ,
2918
|                                            FIELD * field,
2919
|                                            TypeArgument *argp)
2920
|
2921
|   Description   :  Get the next field choice. For linked types this is
2922
|                    done recursively.
2923
|
2924
|   Return Values :  TRUE    - next choice successfully retrieved
2925
|                    FALSE   - couldn't retrieve next choice
2926
+--------------------------------------------------------------------------*/
2927
static bool
2928
Next_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2929
{
2930
  if (!typ || !(typ->status & _HAS_CHOICE))
2931
    return FALSE;
2932
2933
  if (typ->status & _LINKED_TYPE)
2934
    {
2935
      assert(argp);
2936
      return (
2937
	       Next_Choice(typ->left, field, argp->left) ||
2938
	       Next_Choice(typ->right, field, argp->right));
2939
    }
2940
  else
2941
    {
2942
      assert(typ->next);
2943
      return typ->next(field, (void *)argp);
2944
    }
2945
}
2946
2947
/*---------------------------------------------------------------------------
2948
|   Facility      :  libnform
2949
|   Function      :  static bool Previous_Choice(
2950
|                                                FIELDTYPE * typ,
2951
|                                                FIELD * field,
2952
|                                                TypeArgument *argp)
2953
|
2954
|   Description   :  Get the previous field choice. For linked types this
2955
|                    is done recursively.
2956
|
2957
|   Return Values :  TRUE    - previous choice successfully retrieved
2958
|                    FALSE   - couldn't retrieve previous choice
2959
+--------------------------------------------------------------------------*/
2960
static bool
2961
Previous_Choice(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
2962
{
2963
  if (!typ || !(typ->status & _HAS_CHOICE))
2964
    return FALSE;
2965
2966
  if (typ->status & _LINKED_TYPE)
2967
    {
2968
      assert(argp);
2969
      return (
2970
	       Previous_Choice(typ->left, field, argp->left) ||
2971
	       Previous_Choice(typ->right, field, argp->right));
2972
    }
2973
  else
2974
    {
2975
      assert(typ->prev);
2976
      return typ->prev(field, (void *)argp);
2977
    }
2978
}
2979
/*----------------------------------------------------------------------------
2980
  End of Helper routines for Choice Requests
2981
  --------------------------------------------------------------------------*/
2982
2983
/*----------------------------------------------------------------------------
2984
  Routines for Choice Requests
2985
  --------------------------------------------------------------------------*/
2986
2987
/*---------------------------------------------------------------------------
2988
|   Facility      :  libnform
2989
|   Function      :  static int CR_Next_Choice(FORM * form)
2990
|
2991
|   Description   :  Get the next field choice.
2992
|
2993
|   Return Values :  E_OK              - success
2994
|                    E_REQUEST_DENIED  - next choice couldn't be retrieved
2995
+--------------------------------------------------------------------------*/
2996
static int
2997
CR_Next_Choice(FORM *form)
2998
{
2999
  FIELD *field = form->current;
3000
3001
  T((T_CALLED("CR_Next_Choice(%p)"), form));
3002
  Synchronize_Buffer(form);
3003
  returnCode((Next_Choice(field->type, field, (TypeArgument *)(field->arg)))
3004
	     ? E_OK
3005
	     : E_REQUEST_DENIED);
3006
}
3007
3008
/*---------------------------------------------------------------------------
3009
|   Facility      :  libnform
3010
|   Function      :  static int CR_Previous_Choice(FORM * form)
3011
|
3012
|   Description   :  Get the previous field choice.
3013
|
3014
|   Return Values :  E_OK              - success
3015
|                    E_REQUEST_DENIED  - prev. choice couldn't be retrieved
3016
+--------------------------------------------------------------------------*/
3017
static int
3018
CR_Previous_Choice(FORM *form)
3019
{
3020
  FIELD *field = form->current;
3021
3022
  T((T_CALLED("CR_Previous_Choice(%p)"), form));
3023
  Synchronize_Buffer(form);
3024
  returnCode((Previous_Choice(field->type, field, (TypeArgument *)(field->arg)))
3025
	     ? E_OK
3026
	     : E_REQUEST_DENIED);
3027
}
3028
/*----------------------------------------------------------------------------
3029
  End of Routines for Choice Requests
3030
  --------------------------------------------------------------------------*/
3031
3032
/*----------------------------------------------------------------------------
3033
  Helper routines for Field Validations.
3034
  --------------------------------------------------------------------------*/
3035
3036
/*---------------------------------------------------------------------------
3037
|   Facility      :  libnform
3038
|   Function      :  static bool Check_Field(
3039
|                                            FIELDTYPE * typ,
3040
|                                            FIELD * field,
3041
|                                            TypeArgument * argp)
3042
|
3043
|   Description   :  Check the field according to its fieldtype and its
3044
|                    actual arguments. For linked fieldtypes this is done
3045
|                    recursively.
3046
|
3047
|   Return Values :  TRUE       - field is valid
3048
|                    FALSE      - field is invalid.
3049
+--------------------------------------------------------------------------*/
3050
static bool
3051
Check_Field(FIELDTYPE *typ, FIELD *field, TypeArgument *argp)
3052
{
3053
  if (typ)
3054
    {
3055
      if (field->opts & O_NULLOK)
3056
	{
3057
	  FIELD_CELL *bp = field->buf;
3058
3059
	  assert(bp);
3060
	  while (ISBLANK(*bp))
3061
	    {
3062
	      bp++;
3063
	    }
3064
	  if (CharOf(*bp) == 0)
3065
	    return TRUE;
3066
	}
3067
3068
      if (typ->status & _LINKED_TYPE)
3069
	{
3070
	  assert(argp);
3071
	  return (
3072
		   Check_Field(typ->left, field, argp->left) ||
3073
		   Check_Field(typ->right, field, argp->right));
3074
	}
3075
      else
3076
	{
3077
	  if (typ->fcheck)
3078
	    return typ->fcheck(field, (void *)argp);
3079
	}
3080
    }
3081
  return TRUE;
3082
}
3083
3084
/*---------------------------------------------------------------------------
3085
|   Facility      :  libnform
3086
|   Function      :  bool _nc_Internal_Validation(FORM * form )
3087
|
3088
|   Description   :  Validate the current field of the form.
3089
|
3090
|   Return Values :  TRUE  - field is valid
3091
|                    FALSE - field is invalid
3092
+--------------------------------------------------------------------------*/
3093
NCURSES_EXPORT(bool)
3094
_nc_Internal_Validation(FORM *form)
3095
{
3096
  FIELD *field;
3097
3098
  field = form->current;
3099
3100
  Synchronize_Buffer(form);
3101
  if ((form->status & _FCHECK_REQUIRED) ||
3102
      (!(field->opts & O_PASSOK)))
3103
    {
3104
      if (!Check_Field(field->type, field, (TypeArgument *)(field->arg)))
3105
	return FALSE;
3106
      form->status &= ~_FCHECK_REQUIRED;
3107
      field->status |= _CHANGED;
3108
      Synchronize_Linked_Fields(field);
3109
    }
3110
  return TRUE;
3111
}
3112
/*----------------------------------------------------------------------------
3113
  End of Helper routines for Field Validations.
3114
  --------------------------------------------------------------------------*/
3115
3116
/*----------------------------------------------------------------------------
3117
  Routines for Field Validation.
3118
  --------------------------------------------------------------------------*/
3119
3120
/*---------------------------------------------------------------------------
3121
|   Facility      :  libnform
3122
|   Function      :  static int FV_Validation(FORM * form)
3123
|
3124
|   Description   :  Validate the current field of the form.
3125
|
3126
|   Return Values :  E_OK             - field valid
3127
|                    E_INVALID_FIELD  - field not valid
3128
+--------------------------------------------------------------------------*/
3129
static int
3130
FV_Validation(FORM *form)
3131
{
3132
  T((T_CALLED("FV_Validation(%p)"), form));
3133
  if (_nc_Internal_Validation(form))
3134
    returnCode(E_OK);
3135
  else
3136
    returnCode(E_INVALID_FIELD);
3137
}
3138
/*----------------------------------------------------------------------------
3139
  End of routines for Field Validation.
3140
  --------------------------------------------------------------------------*/
3141
3142
/*----------------------------------------------------------------------------
3143
  Helper routines for Inter-Field Navigation
3144
  --------------------------------------------------------------------------*/
3145
3146
/*---------------------------------------------------------------------------
3147
|   Facility      :  libnform
3148
|   Function      :  static FIELD *Next_Field_On_Page(FIELD * field)
3149
|
3150
|   Description   :  Get the next field after the given field on the current
3151
|                    page. The order of fields is the one defined by the
3152
|                    fields array. Only visible and active fields are
3153
|                    counted.
3154
|
3155
|   Return Values :  Pointer to the next field.
3156
+--------------------------------------------------------------------------*/
3157
NCURSES_INLINE static FIELD *
3158
Next_Field_On_Page(FIELD *field)
3159
{
3160
  FORM *form = field->form;
3161
  FIELD **field_on_page = &form->field[field->index];
3162
  FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3163
  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3164
3165
  do
3166
    {
3167
      field_on_page =
3168
	(field_on_page == last_on_page) ? first_on_page : field_on_page + 1;
3169
      if (Field_Is_Selectable(*field_on_page))
3170
	break;
3171
    }
3172
  while (field != (*field_on_page));
3173
  return (*field_on_page);
3174
}
3175
3176
/*---------------------------------------------------------------------------
3177
|   Facility      :  libnform
3178
|   Function      :  FIELD* _nc_First_Active_Field(FORM * form)
3179
|
3180
|   Description   :  Get the first active field on the current page,
3181
|                    if there are such. If there are none, get the first
3182
|                    visible field on the page. If there are also none,
3183
|                    we return the first field on page and hope the best.
3184
|
3185
|   Return Values :  Pointer to calculated field.
3186
+--------------------------------------------------------------------------*/
3187
NCURSES_EXPORT(FIELD *)
3188
_nc_First_Active_Field(FORM *form)
3189
{
3190
  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3191
  FIELD *proposed = Next_Field_On_Page(*last_on_page);
3192
3193
  if (proposed == *last_on_page)
3194
    {
3195
      /* there might be the special situation, where there is no
3196
         active and visible field on the current page. We then select
3197
         the first visible field on this readonly page
3198
       */
3199
      if (Field_Is_Not_Selectable(proposed))
3200
	{
3201
	  FIELD **field = &form->field[proposed->index];
3202
	  FIELD **first = &form->field[form->page[form->curpage].pmin];
3203
3204
	  do
3205
	    {
3206
	      field = (field == last_on_page) ? first : field + 1;
3207
	      if (((*field)->opts & O_VISIBLE))
3208
		break;
3209
	    }
3210
	  while (proposed != (*field));
3211
3212
	  proposed = *field;
3213
3214
	  if ((proposed == *last_on_page) && !(proposed->opts & O_VISIBLE))
3215
	    {
3216
	      /* This means, there is also no visible field on the page.
3217
	         So we propose the first one and hope the very best...
3218
	         Some very clever user has designed a readonly and invisible
3219
	         page on this form.
3220
	       */
3221
	      proposed = *first;
3222
	    }
3223
	}
3224
    }
3225
  return (proposed);
3226
}
3227
3228
/*---------------------------------------------------------------------------
3229
|   Facility      :  libnform
3230
|   Function      :  static FIELD *Previous_Field_On_Page(FIELD * field)
3231
|
3232
|   Description   :  Get the previous field before the given field on the
3233
|                    current page. The order of fields is the one defined by
3234
|                    the fields array. Only visible and active fields are
3235
|                    counted.
3236
|
3237
|   Return Values :  Pointer to the previous field.
3238
+--------------------------------------------------------------------------*/
3239
NCURSES_INLINE static FIELD *
3240
Previous_Field_On_Page(FIELD *field)
3241
{
3242
  FORM *form = field->form;
3243
  FIELD **field_on_page = &form->field[field->index];
3244
  FIELD **first_on_page = &form->field[form->page[form->curpage].pmin];
3245
  FIELD **last_on_page = &form->field[form->page[form->curpage].pmax];
3246
3247
  do
3248
    {
3249
      field_on_page =
3250
	(field_on_page == first_on_page) ? last_on_page : field_on_page - 1;
3251
      if (Field_Is_Selectable(*field_on_page))
3252
	break;
3253
    }
3254
  while (field != (*field_on_page));
3255
3256
  return (*field_on_page);
3257
}
3258
3259
/*---------------------------------------------------------------------------
3260
|   Facility      :  libnform
3261
|   Function      :  static FIELD *Sorted_Next_Field(FIELD * field)
3262
|
3263
|   Description   :  Get the next field after the given field on the current
3264
|                    page. The order of fields is the one defined by the
3265
|                    (row,column) geometry, rows are major.
3266
|
3267
|   Return Values :  Pointer to the next field.
3268
+--------------------------------------------------------------------------*/
3269
NCURSES_INLINE static FIELD *
3270
Sorted_Next_Field(FIELD *field)
3271
{
3272
  FIELD *field_on_page = field;
3273
3274
  do
3275
    {
3276
      field_on_page = field_on_page->snext;
3277
      if (Field_Is_Selectable(field_on_page))
3278
	break;
3279
    }
3280
  while (field_on_page != field);
3281
3282
  return (field_on_page);
3283
}
3284
3285
/*---------------------------------------------------------------------------
3286
|   Facility      :  libnform
3287
|   Function      :  static FIELD *Sorted_Previous_Field(FIELD * field)
3288
|
3289
|   Description   :  Get the previous field before the given field on the
3290
|                    current page. The order of fields is the one defined
3291
|                    by the (row,column) geometry, rows are major.
3292
|
3293
|   Return Values :  Pointer to the previous field.
3294
+--------------------------------------------------------------------------*/
3295
NCURSES_INLINE static FIELD *
3296
Sorted_Previous_Field(FIELD *field)
3297
{
3298
  FIELD *field_on_page = field;
3299
3300
  do
3301
    {
3302
      field_on_page = field_on_page->sprev;
3303
      if (Field_Is_Selectable(field_on_page))
3304
	break;
3305
    }
3306
  while (field_on_page != field);
3307
3308
  return (field_on_page);
3309
}
3310
3311
/*---------------------------------------------------------------------------
3312
|   Facility      :  libnform
3313
|   Function      :  static FIELD *Left_Neighbor_Field(FIELD * field)
3314
|
3315
|   Description   :  Get the left neighbor of the field on the same line
3316
|                    and the same page. Cycles through the line.
3317
|
3318
|   Return Values :  Pointer to left neighbor field.
3319
+--------------------------------------------------------------------------*/
3320
NCURSES_INLINE static FIELD *
3321
Left_Neighbor_Field(FIELD *field)
3322
{
3323
  FIELD *field_on_page = field;
3324
3325
  /* For a field that has really a left neighbor, the while clause
3326
     immediately fails and the loop is left, positioned at the right
3327
     neighbor. Otherwise we cycle backwards through the sorted field list
3328
     until we enter the same line (from the right end).
3329
   */
3330
  do
3331
    {
3332
      field_on_page = Sorted_Previous_Field(field_on_page);
3333
    }
3334
  while (field_on_page->frow != field->frow);
3335
3336
  return (field_on_page);
3337
}
3338
3339
/*---------------------------------------------------------------------------
3340
|   Facility      :  libnform
3341
|   Function      :  static FIELD *Right_Neighbor_Field(FIELD * field)
3342
|
3343
|   Description   :  Get the right neighbor of the field on the same line
3344
|                    and the same page.
3345
|
3346
|   Return Values :  Pointer to right neighbor field.
3347
+--------------------------------------------------------------------------*/
3348
NCURSES_INLINE static FIELD *
3349
Right_Neighbor_Field(FIELD *field)
3350
{
3351
  FIELD *field_on_page = field;
3352
3353
  /* See the comments on Left_Neighbor_Field to understand how it works */
3354
  do
3355
    {
3356
      field_on_page = Sorted_Next_Field(field_on_page);
3357
    }
3358
  while (field_on_page->frow != field->frow);
3359
3360
  return (field_on_page);
3361
}
3362
3363
/*---------------------------------------------------------------------------
3364
|   Facility      :  libnform
3365
|   Function      :  static FIELD *Upper_Neighbor_Field(FIELD * field)
3366
|
3367
|   Description   :  Because of the row-major nature of sorting the fields,
3368
|                    it is more difficult to define whats the upper neighbor
3369
|                    field really means. We define that it must be on a
3370
|                    'previous' line (cyclic order!) and is the rightmost
3371
|                    field laying on the left side of the given field. If
3372
|                    this set is empty, we take the first field on the line.
3373
|
3374
|   Return Values :  Pointer to the upper neighbor field.
3375
+--------------------------------------------------------------------------*/
3376
static FIELD *
3377
Upper_Neighbor_Field(FIELD *field)
3378
{
3379
  FIELD *field_on_page = field;
3380
  int frow = field->frow;
3381
  int fcol = field->fcol;
3382
3383
  /* Walk back to the 'previous' line. The second term in the while clause
3384
     just guarantees that we stop if we cycled through the line because
3385
     there might be no 'previous' line if the page has just one line.
3386
   */
3387
  do
3388
    {
3389
      field_on_page = Sorted_Previous_Field(field_on_page);
3390
    }
3391
  while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3392
3393
  if (field_on_page->frow != frow)
3394
    {
3395
      /* We really found a 'previous' line. We are positioned at the
3396
         rightmost field on this line */
3397
      frow = field_on_page->frow;
3398
3399
      /* We walk to the left as long as we are really right of the
3400
         field. */
3401
      while (field_on_page->frow == frow && field_on_page->fcol > fcol)
3402
	field_on_page = Sorted_Previous_Field(field_on_page);
3403
3404
      /* If we wrapped, just go to the right which is the first field on
3405
         the row */
3406
      if (field_on_page->frow != frow)
3407
	field_on_page = Sorted_Next_Field(field_on_page);
3408
    }
3409
3410
  return (field_on_page);
3411
}
3412
3413
/*---------------------------------------------------------------------------
3414
|   Facility      :  libnform
3415
|   Function      :  static FIELD *Down_Neighbor_Field(FIELD * field)
3416
|
3417
|   Description   :  Because of the row-major nature of sorting the fields,
3418
|                    its more difficult to define whats the down neighbor
3419
|                    field really means. We define that it must be on a
3420
|                    'next' line (cyclic order!) and is the leftmost
3421
|                    field laying on the right side of the given field. If
3422
|                    this set is empty, we take the last field on the line.
3423
|
3424
|   Return Values :  Pointer to the upper neighbor field.
3425
+--------------------------------------------------------------------------*/
3426
static FIELD *
3427
Down_Neighbor_Field(FIELD *field)
3428
{
3429
  FIELD *field_on_page = field;
3430
  int frow = field->frow;
3431
  int fcol = field->fcol;
3432
3433
  /* Walk forward to the 'next' line. The second term in the while clause
3434
     just guarantees that we stop if we cycled through the line because
3435
     there might be no 'next' line if the page has just one line.
3436
   */
3437
  do
3438
    {
3439
      field_on_page = Sorted_Next_Field(field_on_page);
3440
    }
3441
  while (field_on_page->frow == frow && field_on_page->fcol != fcol);
3442
3443
  if (field_on_page->frow != frow)
3444
    {
3445
      /* We really found a 'next' line. We are positioned at the rightmost
3446
         field on this line */
3447
      frow = field_on_page->frow;
3448
3449
      /* We walk to the right as long as we are really left of the
3450
         field. */
3451
      while (field_on_page->frow == frow && field_on_page->fcol < fcol)
3452
	field_on_page = Sorted_Next_Field(field_on_page);
3453
3454
      /* If we wrapped, just go to the left which is the last field on
3455
         the row */
3456
      if (field_on_page->frow != frow)
3457
	field_on_page = Sorted_Previous_Field(field_on_page);
3458
    }
3459
3460
  return (field_on_page);
3461
}
3462
3463
/*----------------------------------------------------------------------------
3464
  Inter-Field Navigation routines
3465
  --------------------------------------------------------------------------*/
3466
3467
/*---------------------------------------------------------------------------
3468
|   Facility      :  libnform
3469
|   Function      :  static int Inter_Field_Navigation(
3470
|                                           int (* const fct) (FORM *),
3471
|                                           FORM * form)
3472
|
3473
|   Description   :  Generic behavior for changing the current field, the
3474
|                    field is left and a new field is entered. So the field
3475
|                    must be validated and the field init/term hooks must
3476
|                    be called.
3477
|
3478
|   Return Values :  E_OK                - success
3479
|                    E_INVALID_FIELD     - field is invalid
3480
|                    some other          - error from subordinate call
3481
+--------------------------------------------------------------------------*/
3482
static int
3483
Inter_Field_Navigation(int (*const fct) (FORM *), FORM *form)
3484
{
3485
  int res;
3486
3487
  if (!_nc_Internal_Validation(form))
3488
    res = E_INVALID_FIELD;
3489
  else
3490
    {
3491
      Call_Hook(form, fieldterm);
3492
      res = fct(form);
3493
      Call_Hook(form, fieldinit);
3494
    }
3495
  return res;
3496
}
3497
3498
/*---------------------------------------------------------------------------
3499
|   Facility      :  libnform
3500
|   Function      :  static int FN_Next_Field(FORM * form)
3501
|
3502
|   Description   :  Move to the next field on the current page of the form
3503
|
3504
|   Return Values :  E_OK                 - success
3505
|                    != E_OK              - error from subordinate call
3506
+--------------------------------------------------------------------------*/
3507
static int
3508
FN_Next_Field(FORM *form)
3509
{
3510
  T((T_CALLED("FN_Next_Field(%p)"), form));
3511
  returnCode(_nc_Set_Current_Field(form,
3512
				   Next_Field_On_Page(form->current)));
3513
}
3514
3515
/*---------------------------------------------------------------------------
3516
|   Facility      :  libnform
3517
|   Function      :  static int FN_Previous_Field(FORM * form)
3518
|
3519
|   Description   :  Move to the previous field on the current page of the
3520
|                    form
3521
|
3522
|   Return Values :  E_OK                 - success
3523
|                    != E_OK              - error from subordinate call
3524
+--------------------------------------------------------------------------*/
3525
static int
3526
FN_Previous_Field(FORM *form)
3527
{
3528
  T((T_CALLED("FN_Previous_Field(%p)"), form));
3529
  returnCode(_nc_Set_Current_Field(form,
3530
				   Previous_Field_On_Page(form->current)));
3531
}
3532
3533
/*---------------------------------------------------------------------------
3534
|   Facility      :  libnform
3535
|   Function      :  static int FN_First_Field(FORM * form)
3536
|
3537
|   Description   :  Move to the first field on the current page of the form
3538
|
3539
|   Return Values :  E_OK                 - success
3540
|                    != E_OK              - error from subordinate call
3541
+--------------------------------------------------------------------------*/
3542
static int
3543
FN_First_Field(FORM *form)
3544
{
3545
  T((T_CALLED("FN_First_Field(%p)"), form));
3546
  returnCode(_nc_Set_Current_Field(form,
3547
				   Next_Field_On_Page(form->field[form->page[form->curpage].pmax])));
3548
}
3549
3550
/*---------------------------------------------------------------------------
3551
|   Facility      :  libnform
3552
|   Function      :  static int FN_Last_Field(FORM * form)
3553
|
3554
|   Description   :  Move to the last field on the current page of the form
3555
|
3556
|   Return Values :  E_OK                 - success
3557
|                    != E_OK              - error from subordinate call
3558
+--------------------------------------------------------------------------*/
3559
static int
3560
FN_Last_Field(FORM *form)
3561
{
3562
  T((T_CALLED("FN_Last_Field(%p)"), form));
3563
  returnCode(
3564
	      _nc_Set_Current_Field(form,
3565
				    Previous_Field_On_Page(form->field[form->page[form->curpage].pmin])));
3566
}
3567
3568
/*---------------------------------------------------------------------------
3569
|   Facility      :  libnform
3570
|   Function      :  static int FN_Sorted_Next_Field(FORM * form)
3571
|
3572
|   Description   :  Move to the sorted next field on the current page
3573
|                    of the form.
3574
|
3575
|   Return Values :  E_OK            - success
3576
|                    != E_OK         - error from subordinate call
3577
+--------------------------------------------------------------------------*/
3578
static int
3579
FN_Sorted_Next_Field(FORM *form)
3580
{
3581
  T((T_CALLED("FN_Sorted_Next_Field(%p)"), form));
3582
  returnCode(_nc_Set_Current_Field(form,
3583
				   Sorted_Next_Field(form->current)));
3584
}
3585
3586
/*---------------------------------------------------------------------------
3587
|   Facility      :  libnform
3588
|   Function      :  static int FN_Sorted_Previous_Field(FORM * form)
3589
|
3590
|   Description   :  Move to the sorted previous field on the current page
3591
|                    of the form.
3592
|
3593
|   Return Values :  E_OK            - success
3594
|                    != E_OK         - error from subordinate call
3595
+--------------------------------------------------------------------------*/
3596
static int
3597
FN_Sorted_Previous_Field(FORM *form)
3598
{
3599
  T((T_CALLED("FN_Sorted_Previous_Field(%p)"), form));
3600
  returnCode(_nc_Set_Current_Field(form,
3601
				   Sorted_Previous_Field(form->current)));
3602
}
3603
3604
/*---------------------------------------------------------------------------
3605
|   Facility      :  libnform
3606
|   Function      :  static int FN_Sorted_First_Field(FORM * form)
3607
|
3608
|   Description   :  Move to the sorted first field on the current page
3609
|                    of the form.
3610
|
3611
|   Return Values :  E_OK            - success
3612
|                    != E_OK         - error from subordinate call
3613
+--------------------------------------------------------------------------*/
3614
static int
3615
FN_Sorted_First_Field(FORM *form)
3616
{
3617
  T((T_CALLED("FN_Sorted_First_Field(%p)"), form));
3618
  returnCode(_nc_Set_Current_Field(form,
3619
				   Sorted_Next_Field(form->field[form->page[form->curpage].smax])));
3620
}
3621
3622
/*---------------------------------------------------------------------------
3623
|   Facility      :  libnform
3624
|   Function      :  static int FN_Sorted_Last_Field(FORM * form)
3625
|
3626
|   Description   :  Move to the sorted last field on the current page
3627
|                    of the form.
3628
|
3629
|   Return Values :  E_OK            - success
3630
|                    != E_OK         - error from subordinate call
3631
+--------------------------------------------------------------------------*/
3632
static int
3633
FN_Sorted_Last_Field(FORM *form)
3634
{
3635
  T((T_CALLED("FN_Sorted_Last_Field(%p)"), form));
3636
  returnCode(_nc_Set_Current_Field(form,
3637
				   Sorted_Previous_Field(form->field[form->page[form->curpage].smin])));
3638
}
3639
3640
/*---------------------------------------------------------------------------
3641
|   Facility      :  libnform
3642
|   Function      :  static int FN_Left_Field(FORM * form)
3643
|
3644
|   Description   :  Get the field on the left of the current field on the
3645
|                    same line and the same page. Cycles through the line.
3646
|
3647
|   Return Values :  E_OK            - success
3648
|                    != E_OK         - error from subordinate call
3649
+--------------------------------------------------------------------------*/
3650
static int
3651
FN_Left_Field(FORM *form)
3652
{
3653
  T((T_CALLED("FN_Left_Field(%p)"), form));
3654
  returnCode(_nc_Set_Current_Field(form,
3655
				   Left_Neighbor_Field(form->current)));
3656
}
3657
3658
/*---------------------------------------------------------------------------
3659
|   Facility      :  libnform
3660
|   Function      :  static int FN_Right_Field(FORM * form)
3661
|
3662
|   Description   :  Get the field on the right of the current field on the
3663
|                    same line and the same page. Cycles through the line.
3664
|
3665
|   Return Values :  E_OK            - success
3666
|                    != E_OK         - error from subordinate call
3667
+--------------------------------------------------------------------------*/
3668
static int
3669
FN_Right_Field(FORM *form)
3670
{
3671
  T((T_CALLED("FN_Right_Field(%p)"), form));
3672
  returnCode(_nc_Set_Current_Field(form,
3673
				   Right_Neighbor_Field(form->current)));
3674
}
3675
3676
/*---------------------------------------------------------------------------
3677
|   Facility      :  libnform
3678
|   Function      :  static int FN_Up_Field(FORM * form)
3679
|
3680
|   Description   :  Get the upper neighbor of the current field. This
3681
|                    cycles through the page. See the comments of the
3682
|                    Upper_Neighbor_Field function to understand how
3683
|                    'upper' is defined.
3684
|
3685
|   Return Values :  E_OK            - success
3686
|                    != E_OK         - error from subordinate call
3687
+--------------------------------------------------------------------------*/
3688
static int
3689
FN_Up_Field(FORM *form)
3690
{
3691
  T((T_CALLED("FN_Up_Field(%p)"), form));
3692
  returnCode(_nc_Set_Current_Field(form,
3693
				   Upper_Neighbor_Field(form->current)));
3694
}
3695
3696
/*---------------------------------------------------------------------------
3697
|   Facility      :  libnform
3698
|   Function      :  static int FN_Down_Field(FORM * form)
3699
|
3700
|   Description   :  Get the down neighbor of the current field. This
3701
|                    cycles through the page. See the comments of the
3702
|                    Down_Neighbor_Field function to understand how
3703
|                    'down' is defined.
3704
|
3705
|   Return Values :  E_OK            - success
3706
|                    != E_OK         - error from subordinate call
3707
+--------------------------------------------------------------------------*/
3708
static int
3709
FN_Down_Field(FORM *form)
3710
{
3711
  T((T_CALLED("FN_Down_Field(%p)"), form));
3712
  returnCode(_nc_Set_Current_Field(form,
3713
				   Down_Neighbor_Field(form->current)));
3714
}
3715
/*----------------------------------------------------------------------------
3716
  END of Field Navigation routines
3717
  --------------------------------------------------------------------------*/
3718
3719
/*----------------------------------------------------------------------------
3720
  Helper routines for Page Navigation
3721
  --------------------------------------------------------------------------*/
3722
3723
/*---------------------------------------------------------------------------
3724
|   Facility      :  libnform
3725
|   Function      :  int _nc_Set_Form_Page(FORM * form,
3726
|                                          int page,
3727
|                                          FIELD * field)
3728
|
3729
|   Description   :  Make the given page number the current page and make
3730
|                    the given field the current field on the page. If
3731
|                    for the field NULL is given, make the first field on
3732
|                    the page the current field. The routine acts only
3733
|                    if the requested page is not the current page.
3734
|
3735
|   Return Values :  E_OK                - success
3736
|                    != E_OK             - error from subordinate call
3737
|                    E_BAD_ARGUMENT      - invalid field pointer
3738
|                    E_SYSTEM_ERROR      - some severe basic error
3739
+--------------------------------------------------------------------------*/
3740
NCURSES_EXPORT(int)
3741
_nc_Set_Form_Page(FORM *form, int page, FIELD *field)
3742
{
3743
  int res = E_OK;
3744
3745
  if ((form->curpage != page))
3746
    {
3747
      FIELD *last_field, *field_on_page;
3748
3749
      werase(Get_Form_Window(form));
3750
      form->curpage = page;
3751
      last_field = field_on_page = form->field[form->page[page].smin];
3752
      do
3753
	{
3754
	  if (field_on_page->opts & O_VISIBLE)
3755
	    if ((res = Display_Field(field_on_page)) != E_OK)
3756
	      return (res);
3757
	  field_on_page = field_on_page->snext;
3758
	}
3759
      while (field_on_page != last_field);
3760
3761
      if (field)
3762
	res = _nc_Set_Current_Field(form, field);
3763
      else
3764
	/* N.B.: we don't encapsulate this by Inter_Field_Navigation(),
3765
	   because this is already executed in a page navigation
3766
	   context that contains field navigation
3767
	 */
3768
	res = FN_First_Field(form);
3769
    }
3770
  return (res);
3771
}
3772
3773
/*---------------------------------------------------------------------------
3774
|   Facility      :  libnform
3775
|   Function      :  static int Next_Page_Number(const FORM * form)
3776
|
3777
|   Description   :  Calculate the page number following the current page
3778
|                    number. This cycles if the highest page number is
3779
|                    reached.
3780
|
3781
|   Return Values :  The next page number
3782
+--------------------------------------------------------------------------*/
3783
NCURSES_INLINE static int
3784
Next_Page_Number(const FORM *form)
3785
{
3786
  return (form->curpage + 1) % form->maxpage;
3787
}
3788
3789
/*---------------------------------------------------------------------------
3790
|   Facility      :  libnform
3791
|   Function      :  static int Previous_Page_Number(const FORM * form)
3792
|
3793
|   Description   :  Calculate the page number before the current page
3794
|                    number. This cycles if the first page number is
3795
|                    reached.
3796
|
3797
|   Return Values :  The previous page number
3798
+--------------------------------------------------------------------------*/
3799
NCURSES_INLINE static int
3800
Previous_Page_Number(const FORM *form)
3801
{
3802
  return (form->curpage != 0 ? form->curpage - 1 : form->maxpage - 1);
3803
}
3804
3805
/*----------------------------------------------------------------------------
3806
  Page Navigation routines
3807
  --------------------------------------------------------------------------*/
3808
3809
/*---------------------------------------------------------------------------
3810
|   Facility      :  libnform
3811
|   Function      :  static int Page_Navigation(
3812
|                                               int (* const fct) (FORM *),
3813
|                                               FORM * form)
3814
|
3815
|   Description   :  Generic behavior for changing a page. This means
3816
|                    that the field is left and a new field is entered.
3817
|                    So the field must be validated and the field init/term
3818
|                    hooks must be called. Because also the page is changed,
3819
|                    the forms init/term hooks must be called also.
3820
|
3821
|   Return Values :  E_OK                - success
3822
|                    E_INVALID_FIELD     - field is invalid
3823
|                    some other          - error from subordinate call
3824
+--------------------------------------------------------------------------*/
3825
static int
3826
Page_Navigation(int (*const fct) (FORM *), FORM *form)
3827
{
3828
  int res;
3829
3830
  if (!_nc_Internal_Validation(form))
3831
    res = E_INVALID_FIELD;
3832
  else
3833
    {
3834
      Call_Hook(form, fieldterm);
3835
      Call_Hook(form, formterm);
3836
      res = fct(form);
3837
      Call_Hook(form, forminit);
3838
      Call_Hook(form, fieldinit);
3839
    }
3840
  return res;
3841
}
3842
3843
/*---------------------------------------------------------------------------
3844
|   Facility      :  libnform
3845
|   Function      :  static int PN_Next_Page(FORM * form)
3846
|
3847
|   Description   :  Move to the next page of the form
3848
|
3849
|   Return Values :  E_OK                - success
3850
|                    != E_OK             - error from subordinate call
3851
+--------------------------------------------------------------------------*/
3852
static int
3853
PN_Next_Page(FORM *form)
3854
{
3855
  T((T_CALLED("PN_Next_Page(%p)"), form));
3856
  returnCode(_nc_Set_Form_Page(form, Next_Page_Number(form), (FIELD *)0));
3857
}
3858
3859
/*---------------------------------------------------------------------------
3860
|   Facility      :  libnform
3861
|   Function      :  static int PN_Previous_Page(FORM * form)
3862
|
3863
|   Description   :  Move to the previous page of the form
3864
|
3865
|   Return Values :  E_OK              - success
3866
|                    != E_OK           - error from subordinate call
3867
+--------------------------------------------------------------------------*/
3868
static int
3869
PN_Previous_Page(FORM *form)
3870
{
3871
  T((T_CALLED("PN_Previous_Page(%p)"), form));
3872
  returnCode(_nc_Set_Form_Page(form, Previous_Page_Number(form), (FIELD *)0));
3873
}
3874
3875
/*---------------------------------------------------------------------------
3876
|   Facility      :  libnform
3877
|   Function      :  static int PN_First_Page(FORM * form)
3878
|
3879
|   Description   :  Move to the first page of the form
3880
|
3881
|   Return Values :  E_OK              - success
3882
|                    != E_OK           - error from subordinate call
3883
+--------------------------------------------------------------------------*/
3884
static int
3885
PN_First_Page(FORM *form)
3886
{
3887
  T((T_CALLED("PN_First_Page(%p)"), form));
3888
  returnCode(_nc_Set_Form_Page(form, 0, (FIELD *)0));
3889
}
3890
3891
/*---------------------------------------------------------------------------
3892
|   Facility      :  libnform
3893
|   Function      :  static int PN_Last_Page(FORM * form)
3894
|
3895
|   Description   :  Move to the last page of the form
3896
|
3897
|   Return Values :  E_OK              - success
3898
|                    != E_OK           - error from subordinate call
3899
+--------------------------------------------------------------------------*/
3900
static int
3901
PN_Last_Page(FORM *form)
3902
{
3903
  T((T_CALLED("PN_Last_Page(%p)"), form));
3904
  returnCode(_nc_Set_Form_Page(form, form->maxpage - 1, (FIELD *)0));
3905
}
3906
3907
/*----------------------------------------------------------------------------
3908
  END of Field Navigation routines
3909
  --------------------------------------------------------------------------*/
3910
3911
/*----------------------------------------------------------------------------
3912
  Helper routines for the core form driver.
3913
  --------------------------------------------------------------------------*/
3914
3915
/*---------------------------------------------------------------------------
3916
|   Facility      :  libnform
3917
|   Function      :  static int Data_Entry(FORM * form,int c)
3918
|
3919
|   Description   :  Enter character c into at the current position of the
3920
|                    current field of the form.
3921
|
3922
|   Return Values :  E_OK              - success
3923
|                    E_REQUEST_DENIED  - driver could not process the request
3924
|                    E_SYSTEM_ERROR    -
3925
+--------------------------------------------------------------------------*/
3926
static int
3927
Data_Entry(FORM *form, int c)
3928
{
3929
  FIELD *field = form->current;
3930
  int result = E_REQUEST_DENIED;
3931
3932
  T((T_CALLED("Data_Entry(%p,%s)"), form, _tracechtype((chtype)c)));
3933
  if ((field->opts & O_EDIT)
3934
#if FIX_FORM_INACTIVE_BUG
3935
      && (field->opts & O_ACTIVE)
3936
#endif
3937
    )
3938
    {
3939
      if ((field->opts & O_BLANK) &&
3940
	  First_Position_In_Current_Field(form) &&
3941
	  !(form->status & _FCHECK_REQUIRED) &&
3942
	  !(form->status & _WINDOW_MODIFIED))
3943
	werase(form->w);
3944
3945
      if (form->status & _OVLMODE)
3946
	{
3947
	  waddch(form->w, (chtype)c);
3948
	}
3949
      else
3950
	/* no _OVLMODE */
3951
	{
3952
	  bool There_Is_Room = Is_There_Room_For_A_Char_In_Line(form);
3953
3954
	  if (!(There_Is_Room ||
3955
		((Single_Line_Field(field) && Growable(field)))))
3956
	    RETURN(E_REQUEST_DENIED);
3957
3958
	  if (!There_Is_Room && !Field_Grown(field, 1))
3959
	    RETURN(E_SYSTEM_ERROR);
3960
3961
	  winsch(form->w, (chtype)c);
3962
	}
3963
3964
      if ((result = Wrapping_Not_Necessary_Or_Wrapping_Ok(form)) == E_OK)
3965
	{
3966
	  bool End_Of_Field = (((field->drows - 1) == form->currow) &&
3967
			       ((field->dcols - 1) == form->curcol));
3968
3969
	  form->status |= _WINDOW_MODIFIED;
3970
	  if (End_Of_Field && !Growable(field) && (field->opts & O_AUTOSKIP))
3971
	    result = Inter_Field_Navigation(FN_Next_Field, form);
3972
	  else
3973
	    {
3974
	      if (End_Of_Field && Growable(field) && !Field_Grown(field, 1))
3975
		result = E_SYSTEM_ERROR;
3976
	      else
3977
		{
3978
#if USE_WIDEC_SUPPORT
3979
		  /*
3980
		   * We have just added a byte to the form field.  It may have
3981
		   * been part of a multibyte character.  If it was, the
3982
		   * addch_used field is nonzero and we should not try to move
3983
		   * to a new column.
3984
		   */
3985
		  if (WINDOW_EXT(form->w, addch_used) == 0)
3986
		    IFN_Next_Character(form);
3987
#else
3988
		  IFN_Next_Character(form);
3989
#endif
3990
		  result = E_OK;
3991
		}
3992
	    }
3993
	}
3994
    }
3995
  RETURN(result);
3996
}
3997
3998
/* Structure to describe the binding of a request code to a function.
3999
   The member keycode codes the request value as well as the generic
4000
   routine to use for the request. The code for the generic routine
4001
   is coded in the upper 16 Bits while the request code is coded in
4002
   the lower 16 bits.
4003
4004
   In terms of C++ you might think of a request as a class with a
4005
   virtual method "perform". The different types of request are
4006
   derived from this base class and overload (or not) the base class
4007
   implementation of perform.
4008
*/
4009
typedef struct
4010
{
4011
  int keycode;			/* must be at least 32 bit: hi:mode, lo: key */
4012
  int (*cmd) (FORM *);		/* low level driver routine for this key     */
4013
}
4014
Binding_Info;
4015
4016
/* You may see this is the class-id of the request type class */
4017
#define ID_PN    (0x00000000)	/* Page navigation           */
4018
#define ID_FN    (0x00010000)	/* Inter-Field navigation    */
4019
#define ID_IFN   (0x00020000)	/* Intra-Field navigation    */
4020
#define ID_VSC   (0x00030000)	/* Vertical Scrolling        */
4021
#define ID_HSC   (0x00040000)	/* Horizontal Scrolling      */
4022
#define ID_FE    (0x00050000)	/* Field Editing             */
4023
#define ID_EM    (0x00060000)	/* Edit Mode                 */
4024
#define ID_FV    (0x00070000)	/* Field Validation          */
4025
#define ID_CH    (0x00080000)	/* Choice                    */
4026
#define ID_Mask  (0xffff0000)
4027
#define Key_Mask (0x0000ffff)
4028
#define ID_Shft  (16)
4029
4030
/* This array holds all the Binding Infos */
4031
/* *INDENT-OFF* */
4032
static const Binding_Info bindings[MAX_FORM_COMMAND - MIN_FORM_COMMAND + 1] =
4033
{
4034
  { REQ_NEXT_PAGE    |ID_PN  ,PN_Next_Page},
4035
  { REQ_PREV_PAGE    |ID_PN  ,PN_Previous_Page},
4036
  { REQ_FIRST_PAGE   |ID_PN  ,PN_First_Page},
4037
  { REQ_LAST_PAGE    |ID_PN  ,PN_Last_Page},
4038
4039
  { REQ_NEXT_FIELD   |ID_FN  ,FN_Next_Field},
4040
  { REQ_PREV_FIELD   |ID_FN  ,FN_Previous_Field},
4041
  { REQ_FIRST_FIELD  |ID_FN  ,FN_First_Field},
4042
  { REQ_LAST_FIELD   |ID_FN  ,FN_Last_Field},
4043
  { REQ_SNEXT_FIELD  |ID_FN  ,FN_Sorted_Next_Field},
4044
  { REQ_SPREV_FIELD  |ID_FN  ,FN_Sorted_Previous_Field},
4045
  { REQ_SFIRST_FIELD |ID_FN  ,FN_Sorted_First_Field},
4046
  { REQ_SLAST_FIELD  |ID_FN  ,FN_Sorted_Last_Field},
4047
  { REQ_LEFT_FIELD   |ID_FN  ,FN_Left_Field},
4048
  { REQ_RIGHT_FIELD  |ID_FN  ,FN_Right_Field},
4049
  { REQ_UP_FIELD     |ID_FN  ,FN_Up_Field},
4050
  { REQ_DOWN_FIELD   |ID_FN  ,FN_Down_Field},
4051
4052
  { REQ_NEXT_CHAR    |ID_IFN ,IFN_Next_Character},
4053
  { REQ_PREV_CHAR    |ID_IFN ,IFN_Previous_Character},
4054
  { REQ_NEXT_LINE    |ID_IFN ,IFN_Next_Line},
4055
  { REQ_PREV_LINE    |ID_IFN ,IFN_Previous_Line},
4056
  { REQ_NEXT_WORD    |ID_IFN ,IFN_Next_Word},
4057
  { REQ_PREV_WORD    |ID_IFN ,IFN_Previous_Word},
4058
  { REQ_BEG_FIELD    |ID_IFN ,IFN_Beginning_Of_Field},
4059
  { REQ_END_FIELD    |ID_IFN ,IFN_End_Of_Field},
4060
  { REQ_BEG_LINE     |ID_IFN ,IFN_Beginning_Of_Line},
4061
  { REQ_END_LINE     |ID_IFN ,IFN_End_Of_Line},
4062
  { REQ_LEFT_CHAR    |ID_IFN ,IFN_Left_Character},
4063
  { REQ_RIGHT_CHAR   |ID_IFN ,IFN_Right_Character},
4064
  { REQ_UP_CHAR      |ID_IFN ,IFN_Up_Character},
4065
  { REQ_DOWN_CHAR    |ID_IFN ,IFN_Down_Character},
4066
4067
  { REQ_NEW_LINE     |ID_FE  ,FE_New_Line},
4068
  { REQ_INS_CHAR     |ID_FE  ,FE_Insert_Character},
4069
  { REQ_INS_LINE     |ID_FE  ,FE_Insert_Line},
4070
  { REQ_DEL_CHAR     |ID_FE  ,FE_Delete_Character},
4071
  { REQ_DEL_PREV     |ID_FE  ,FE_Delete_Previous},
4072
  { REQ_DEL_LINE     |ID_FE  ,FE_Delete_Line},
4073
  { REQ_DEL_WORD     |ID_FE  ,FE_Delete_Word},
4074
  { REQ_CLR_EOL      |ID_FE  ,FE_Clear_To_End_Of_Line},
4075
  { REQ_CLR_EOF      |ID_FE  ,FE_Clear_To_End_Of_Field},
4076
  { REQ_CLR_FIELD    |ID_FE  ,FE_Clear_Field},
4077
4078
  { REQ_OVL_MODE     |ID_EM  ,EM_Overlay_Mode},
4079
  { REQ_INS_MODE     |ID_EM  ,EM_Insert_Mode},
4080
4081
  { REQ_SCR_FLINE    |ID_VSC ,VSC_Scroll_Line_Forward},
4082
  { REQ_SCR_BLINE    |ID_VSC ,VSC_Scroll_Line_Backward},
4083
  { REQ_SCR_FPAGE    |ID_VSC ,VSC_Scroll_Page_Forward},
4084
  { REQ_SCR_BPAGE    |ID_VSC ,VSC_Scroll_Page_Backward},
4085
  { REQ_SCR_FHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Forward},
4086
  { REQ_SCR_BHPAGE   |ID_VSC ,VSC_Scroll_Half_Page_Backward},
4087
4088
  { REQ_SCR_FCHAR    |ID_HSC ,HSC_Scroll_Char_Forward},
4089
  { REQ_SCR_BCHAR    |ID_HSC ,HSC_Scroll_Char_Backward},
4090
  { REQ_SCR_HFLINE   |ID_HSC ,HSC_Horizontal_Line_Forward},
4091
  { REQ_SCR_HBLINE   |ID_HSC ,HSC_Horizontal_Line_Backward},
4092
  { REQ_SCR_HFHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Forward},
4093
  { REQ_SCR_HBHALF   |ID_HSC ,HSC_Horizontal_Half_Line_Backward},
4094
4095
  { REQ_VALIDATION   |ID_FV  ,FV_Validation},
4096
4097
  { REQ_NEXT_CHOICE  |ID_CH  ,CR_Next_Choice},
4098
  { REQ_PREV_CHOICE  |ID_CH  ,CR_Previous_Choice}
4099
};
4100
/* *INDENT-ON* */
4101
4102
/*---------------------------------------------------------------------------
4103
|   Facility      :  libnform
4104
|   Function      :  int form_driver(FORM * form,int  c)
4105
|
4106
|   Description   :  This is the workhorse of the forms system. It checks
4107
|                    to determine whether the character c is a request or
4108
|                    data. If it is a request, the form driver executes
4109
|                    the request and returns the result. If it is data
4110
|                    (printable character), it enters the data into the
4111
|                    current position in the current field. If it is not
4112
|                    recognized, the form driver assumes it is an application
4113
|                    defined command and returns E_UNKNOWN_COMMAND.
4114
|                    Application defined command should be defined relative
4115
|                    to MAX_FORM_COMMAND, the maximum value of a request.
4116
|
4117
|   Return Values :  E_OK              - success
4118
|                    E_SYSTEM_ERROR    - system error
4119
|                    E_BAD_ARGUMENT    - an argument is incorrect
4120
|                    E_NOT_POSTED      - form is not posted
4121
|                    E_INVALID_FIELD   - field contents are invalid
4122
|                    E_BAD_STATE       - called from inside a hook routine
4123
|                    E_REQUEST_DENIED  - request failed
4124
|                    E_NOT_CONNECTED   - no fields are connected to the form
4125
|                    E_UNKNOWN_COMMAND - command not known
4126
+--------------------------------------------------------------------------*/
4127
NCURSES_EXPORT(int)
4128
form_driver(FORM *form, int c)
4129
{
4130
  const Binding_Info *BI = (Binding_Info *) 0;
4131
  int res = E_UNKNOWN_COMMAND;
4132
4133
  T((T_CALLED("form_driver(%p,%d)"), form, c));
4134
4135
  if (!form)
4136
    RETURN(E_BAD_ARGUMENT);
4137
4138
  if (!(form->field))
4139
    RETURN(E_NOT_CONNECTED);
4140
4141
  assert(form->page);
4142
4143
  if (c == FIRST_ACTIVE_MAGIC)
4144
    {
4145
      form->current = _nc_First_Active_Field(form);
4146
      RETURN(E_OK);
4147
    }
4148
4149
  assert(form->current &&
4150
	 form->current->buf &&
4151
	 (form->current->form == form)
4152
    );
4153
4154
  if (form->status & _IN_DRIVER)
4155
    RETURN(E_BAD_STATE);
4156
4157
  if (!(form->status & _POSTED))
4158
    RETURN(E_NOT_POSTED);
4159
4160
  if ((c >= MIN_FORM_COMMAND && c <= MAX_FORM_COMMAND) &&
4161
      ((bindings[c - MIN_FORM_COMMAND].keycode & Key_Mask) == c))
4162
    BI = &(bindings[c - MIN_FORM_COMMAND]);
4163
4164
  if (BI)
4165
    {
4166
      typedef int (*Generic_Method) (int (*const) (FORM *), FORM *);
4167
      static const Generic_Method Generic_Methods[] =
4168
      {
4169
	Page_Navigation,	/* overloaded to call field&form hooks */
4170
	Inter_Field_Navigation,	/* overloaded to call field hooks      */
4171
	NULL,			/* Intra-Field is generic              */
4172
	Vertical_Scrolling,	/* Overloaded to check multi-line      */
4173
	Horizontal_Scrolling,	/* Overloaded to check single-line     */
4174
	Field_Editing,		/* Overloaded to mark modification     */
4175
	NULL,			/* Edit Mode is generic                */
4176
	NULL,			/* Field Validation is generic         */
4177
	NULL			/* Choice Request is generic           */
4178
      };
4179
      size_t nMethods = (sizeof(Generic_Methods) / sizeof(Generic_Methods[0]));
4180
      size_t method = (BI->keycode >> ID_Shft) & 0xffff;	/* see ID_Mask */
4181
4182
      if ((method >= nMethods) || !(BI->cmd))
4183
	res = E_SYSTEM_ERROR;
4184
      else
4185
	{
4186
	  Generic_Method fct = Generic_Methods[method];
4187
4188
	  if (fct)
4189
	    res = fct(BI->cmd, form);
4190
	  else
4191
	    res = (BI->cmd) (form);
4192
	}
4193
    }
4194
#ifdef NCURSES_MOUSE_VERSION
4195
  else if (KEY_MOUSE == c)
4196
    {
4197
      MEVENT event;
4198
      WINDOW *win = form->win ? form->win : stdscr;
4199
      WINDOW *sub = form->sub ? form->sub : win;
4200
4201
      getmouse(&event);
4202
      if ((event.bstate & (BUTTON1_CLICKED |
4203
			   BUTTON1_DOUBLE_CLICKED |
4204
			   BUTTON1_TRIPLE_CLICKED))
4205
	  && wenclose(win, event.y, event.x))
4206
	{			/* we react only if the click was in the userwin, that means
4207
				 * inside the form display area or at the decoration window.
4208
				 */
4209
	  int ry = event.y, rx = event.x;	/* screen coordinates */
4210
4211
	  res = E_REQUEST_DENIED;
4212
	  if (mouse_trafo(&ry, &rx, FALSE))
4213
	    {			/* rx, ry are now "curses" coordinates */
4214
	      if (ry < sub->_begy)
4215
		{		/* we clicked above the display region; this is
4216
				 * interpreted as "scroll up" request
4217
				 */
4218
		  if (event.bstate & BUTTON1_CLICKED)
4219
		    res = form_driver(form, REQ_PREV_FIELD);
4220
		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4221
		    res = form_driver(form, REQ_PREV_PAGE);
4222
		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4223
		    res = form_driver(form, REQ_FIRST_FIELD);
4224
		}
4225
	      else if (ry > sub->_begy + sub->_maxy)
4226
		{		/* we clicked below the display region; this is
4227
				 * interpreted as "scroll down" request
4228
				 */
4229
		  if (event.bstate & BUTTON1_CLICKED)
4230
		    res = form_driver(form, REQ_NEXT_FIELD);
4231
		  else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
4232
		    res = form_driver(form, REQ_NEXT_PAGE);
4233
		  else if (event.bstate & BUTTON1_TRIPLE_CLICKED)
4234
		    res = form_driver(form, REQ_LAST_FIELD);
4235
		}
4236
	      else if (wenclose(sub, event.y, event.x))
4237
		{		/* Inside the area we try to find the hit item */
4238
		  int i;
4239
4240
		  ry = event.y;
4241
		  rx = event.x;
4242
		  if (wmouse_trafo(sub, &ry, &rx, FALSE))
4243
		    {
4244
		      int min_field = form->page[form->curpage].pmin;
4245
		      int max_field = form->page[form->curpage].pmax;
4246
4247
		      for (i = min_field; i <= max_field; ++i)
4248
			{
4249
			  FIELD *field = form->field[i];
4250
4251
			  if (Field_Is_Selectable(field)
4252
			      && Field_encloses(field, ry, rx) == E_OK)
4253
			    {
4254
			      res = _nc_Set_Current_Field(form, field);
4255
			      if (res == E_OK)
4256
				res = _nc_Position_Form_Cursor(form);
4257
			      if (res == E_OK
4258
				  && (event.bstate & BUTTON1_DOUBLE_CLICKED))
4259
				res = E_UNKNOWN_COMMAND;
4260
			      break;
4261
			    }
4262
			}
4263
		    }
4264
		}
4265
	    }
4266
	}
4267
      else
4268
	res = E_REQUEST_DENIED;
4269
    }
4270
#endif /* NCURSES_MOUSE_VERSION */
4271
  else if (!(c & (~(int)MAX_REGULAR_CHARACTER)))
4272
    {
4273
      /*
4274
       * If we're using 8-bit characters, iscntrl+isprint cover the whole set.
4275
       * But with multibyte characters, there is a third possibility, i.e.,
4276
       * parts of characters that build up into printable characters which are
4277
       * not considered printable.
4278
       *
4279
       * FIXME: the wide-character branch should also use Check_Char().
4280
       */
4281
#if USE_WIDEC_SUPPORT
4282
      if (!iscntrl(UChar(c)))
4283
#else
4284
      if (isprint(UChar(c)) &&
4285
	  Check_Char(form->current->type, c,
4286
		     (TypeArgument *)(form->current->arg)))
4287
#endif
4288
	res = Data_Entry(form, c);
4289
    }
4290
  _nc_Refresh_Current_Field(form);
4291
  RETURN(res);
4292
}
4293
4294
/*----------------------------------------------------------------------------
4295
  Field-Buffer manipulation routines.
4296
  The effects of setting a buffer are tightly coupled to the core of the form
4297
  driver logic. This is especially true in the case of growable fields.
4298
  So I don't separate this into a separate module.
4299
  --------------------------------------------------------------------------*/
4300
4301
/*---------------------------------------------------------------------------
4302
|   Facility      :  libnform
4303
|   Function      :  int set_field_buffer(FIELD *field,
4304
|                                         int buffer, char *value)
4305
|
4306
|   Description   :  Set the given buffer of the field to the given value.
4307
|                    Buffer 0 stores the displayed content of the field.
4308
|                    For dynamic fields this may grow the fieldbuffers if
4309
|                    the length of the value exceeds the current buffer
4310
|                    length. For buffer 0 only printable values are allowed.
4311
|                    For static fields, the value needs not to be zero ter-
4312
|                    minated. It is copied up to the length of the buffer.
4313
|
4314
|   Return Values :  E_OK            - success
4315
|                    E_BAD_ARGUMENT  - invalid argument
4316
|                    E_SYSTEM_ERROR  - system error
4317
+--------------------------------------------------------------------------*/
4318
NCURSES_EXPORT(int)
4319
set_field_buffer(FIELD *field, int buffer, const char *value)
4320
{
4321
  FIELD_CELL *p;
4322
  int res = E_OK;
4323
  unsigned int i;
4324
  unsigned int len;
4325
4326
#if USE_WIDEC_SUPPORT
4327
  FIELD_CELL *widevalue = 0;
4328
#endif
4329
4330
  T((T_CALLED("set_field_buffer(%p,%d,%s)"), field, buffer, _nc_visbuf(value)));
4331
4332
  if (!field || !value || ((buffer < 0) || (buffer > field->nbuf)))
4333
    RETURN(E_BAD_ARGUMENT);
4334
4335
  len = Buffer_Length(field);
4336
4337
  if (Growable(field))
4338
    {
4339
      /* for a growable field we must assume zero terminated strings, because
4340
         somehow we have to detect the length of what should be copied.
4341
       */
4342
      unsigned int vlen = strlen(value);
4343
4344
      if (vlen > len)
4345
	{
4346
	  if (!Field_Grown(field,
4347
			   (int)(1 + (vlen - len) / ((field->rows + field->nrow)
4348
						     * field->cols))))
4349
	    RETURN(E_SYSTEM_ERROR);
4350
4351
#if !USE_WIDEC_SUPPORT
4352
	  len = vlen;
4353
#endif
4354
	}
4355
    }
4356
4357
  p = Address_Of_Nth_Buffer(field, buffer);
4358
4359
#if USE_WIDEC_SUPPORT
4360
  /*
4361
   * Use addstr's logic for converting a string to an array of cchar_t's.
4362
   * There should be a better way, but this handles nonspacing characters
4363
   * and other special cases that we really do not want to handle here.
4364
   */
4365
#if NCURSES_EXT_FUNCS
4366
  if (wresize(field->working, field->drows, field->dcols) == ERR)
4367
#endif
4368
    {
4369
      delwin(field->working);
4370
      field->working = newpad(field->drows, field->dcols);
4371
    }
4372
  len = Buffer_Length(field);
4373
  wclear(field->working);
4374
  mvwaddstr(field->working, 0, 0, value);
4375
4376
  if ((widevalue = typeCalloc(FIELD_CELL, len + 1)) == 0)
4377
    {
4378
      RETURN(E_SYSTEM_ERROR);
4379
    }
4380
  else
4381
    {
4382
      for (i = 0; i < (unsigned)field->drows; ++i)
4383
	{
4384
	  mvwin_wchnstr(field->working, i, 0,
4385
			widevalue + (i * field->dcols),
4386
			field->dcols);
4387
	}
4388
      for (i = 0; i < len; ++i)
4389
	{
4390
	  if (CharEq(myZEROS, widevalue[i]))
4391
	    {
4392
	      while (i < len)
4393
		p[i++] = myBLANK;
4394
	      break;
4395
	    }
4396
	  p[i] = widevalue[i];
4397
	}
4398
      free(widevalue);
4399
    }
4400
#else
4401
  for (i = 0; i < len; ++i)
4402
    {
4403
      if (value[i] == '\0')
4404
	{
4405
	  while (i < len)
4406
	    p[i++] = myBLANK;
4407
	  break;
4408
	}
4409
      p[i] = value[i];
4410
    }
4411
#endif
4412
4413
  if (buffer == 0)
4414
    {
4415
      int syncres;
4416
4417
      if (((syncres = Synchronize_Field(field)) != E_OK) &&
4418
	  (res == E_OK))
4419
	res = syncres;
4420
      if (((syncres = Synchronize_Linked_Fields(field)) != E_OK) &&
4421
	  (res == E_OK))
4422
	res = syncres;
4423
    }
4424
  RETURN(res);
4425
}
4426
4427
/*---------------------------------------------------------------------------
4428
|   Facility      :  libnform
4429
|   Function      :  char *field_buffer(const FIELD *field,int buffer)
4430
|
4431
|   Description   :  Return the address of the buffer for the field.
4432
|
4433
|   Return Values :  Pointer to buffer or NULL if arguments were invalid.
4434
+--------------------------------------------------------------------------*/
4435
NCURSES_EXPORT(char *)
4436
field_buffer(const FIELD *field, int buffer)
4437
{
4438
  char *result = 0;
4439
4440
  T((T_CALLED("field_buffer(%p,%d)"), field, buffer));
4441
4442
  if (field && (buffer >= 0) && (buffer <= field->nbuf))
4443
    {
4444
#if USE_WIDEC_SUPPORT
4445
      FIELD_CELL *data = Address_Of_Nth_Buffer(field, buffer);
4446
      unsigned need = 0;
4447
      int size = Buffer_Length(field);
4448
      int n;
4449
4450
      /* determine the number of bytes needed to store the expanded string */
4451
      for (n = 0; n < size; ++n)
4452
	{
4453
	  if (!isWidecExt(data[n]))
4454
	    {
4455
	      mbstate_t state;
4456
	      size_t next;
4457
4458
	      init_mb(state);
4459
	      next = _nc_wcrtomb(0, data[n].chars[0], &state);
4460
	      if (!isEILSEQ(next))
4461
		need += next;
4462
	    }
4463
	}
4464
4465
      /* allocate a place to store the expanded string */
4466
      if (field->expanded[buffer] != 0)
4467
	free(field->expanded[buffer]);
4468
      field->expanded[buffer] = typeMalloc(char, need + 1);
4469
4470
      /* expand the multibyte data */
4471
      if ((result = field->expanded[buffer]) != 0)
4472
	{
4473
	  wclear(field->working);
4474
	  mvwadd_wchnstr(field->working, 0, 0, data, size);
4475
	  mvwinnstr(field->working, 0, 0, result, (int)need);
4476
	}
4477
#else
4478
      result = Address_Of_Nth_Buffer(field, buffer);
4479
#endif
4480
    }
4481
  returnPtr(result);
4482
}
4483
4484
#if USE_WIDEC_SUPPORT
4485
4486
/* FIXME: see lib_get_wch.c */
4487
#if HAVE_MBTOWC && HAVE_MBLEN
4488
#define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
4489
#define count_mbytes(buffer,length,state) mblen(buffer,length)
4490
#define trans_mbytes(wch,buffer,length,state) \
4491
	(int) mbtowc(&wch, buffer, length)
4492
#elif HAVE_MBRTOWC && HAVE_MBRLEN
4493
#define NEED_STATE
4494
#define reset_mbytes(state) init_mb(state)
4495
#define count_mbytes(buffer,length,state) mbrlen(buffer,length,&state)
4496
#define trans_mbytes(wch,buffer,length,state) \
4497
	(int) mbrtowc(&wch, buffer, length, &state)
4498
#else
4499
make an error
4500
#endif
4501
4502
/*---------------------------------------------------------------------------
4503
| Convert a multibyte string to a wide-character string.  The result must be
4504
| freed by the caller.
4505
+--------------------------------------------------------------------------*/
4506
NCURSES_EXPORT(wchar_t *)
4507
_nc_Widen_String(char *source, int *lengthp)
4508
{
4509
  wchar_t *result = 0;
4510
  wchar_t wch;
4511
  size_t given = strlen(source);
4512
  size_t tries;
4513
  int pass;
4514
  int status;
4515
4516
#ifdef NEED_STATE
4517
  mbstate_t state;
4518
#endif
4519
4520
  for (pass = 0; pass < 2; ++pass)
4521
    {
4522
      unsigned need = 0;
4523
      size_t passed = 0;
4524
4525
      while (passed < given)
4526
	{
4527
	  bool found = FALSE;
4528
4529
	  for (tries = 1, status = 0; tries <= (given - passed); ++tries)
4530
	    {
4531
	      int save = source[passed + tries];
4532
4533
	      source[passed + tries] = 0;
4534
	      reset_mbytes(state);
4535
	      status = trans_mbytes(wch, source + passed, tries, state);
4536
	      source[passed + tries] = save;
4537
4538
	      if (status > 0)
4539
		{
4540
		  found = TRUE;
4541
		  break;
4542
		}
4543
	    }
4544
	  if (found)
4545
	    {
4546
	      if (pass)
4547
		{
4548
		  result[need] = wch;
4549
		}
4550
	      passed += status;
4551
	      ++need;
4552
	    }
4553
	  else
4554
	    {
4555
	      if (pass)
4556
		{
4557
		  result[need] = source[passed];
4558
		}
4559
	      ++need;
4560
	      ++passed;
4561
	    }
4562
	}
4563
4564
      if (!pass)
4565
	{
4566
	  if (!need)
4567
	    break;
4568
	  result = typeCalloc(wchar_t, need);
4569
4570
	  *lengthp = need;
4571
	  if (result == 0)
4572
	    break;
4573
	}
4574
    }
4575
4576
  return result;
4577
}
4578
#endif
4579
4580
/* frm_driver.c ends here */