1/****************************************************************************
2 * Copyright 2020,2021 Thomas E. Dickey                                     *
3 * Copyright 1998-2010,2012 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
36MODULE_ID("$Id: frm_def.c,v 1.30 2021/03/27 23:49:58 tom Exp $")
37
38/* this can't be readonly */
39static FORM default_form =
40{
41  0,				/* status     */
42  0,				/* rows       */
43  0,				/* cols       */
44  0,				/* currow     */
45  0,				/* curcol     */
46  0,				/* toprow     */
47  0,				/* begincol   */
48  -1,				/* maxfield   */
49  -1,				/* maxpage    */
50  -1,				/* curpage    */
51  ALL_FORM_OPTS,		/* opts       */
52  (WINDOW *)0,			/* win        */
53  (WINDOW *)0,			/* sub        */
54  (WINDOW *)0,			/* w          */
55  (FIELD **)0,			/* field      */
56  (FIELD *)0,			/* current    */
57  (_PAGE *) 0,			/* page       */
58  (char *)0,			/* usrptr     */
59  NULL,				/* forminit   */
60  NULL,				/* formterm   */
61  NULL,				/* fieldinit  */
62  NULL				/* fieldterm  */
63};
64
65FORM_EXPORT_VAR(FORM *) _nc_Default_Form = &default_form;
66
67/*---------------------------------------------------------------------------
68|   Facility      :  libnform
69|   Function      :  static FIELD *Insert_Field_By_Position(
70|                                     FIELD *new_field,
71|                                     FIELD *head )
72|
73|   Description   :  Insert new_field into sorted fieldlist with head "head"
74|                    and return new head of sorted fieldlist. Sorting
75|                    criteria is (row,column). This is a circular list.
76|
77|   Return Values :  New head of sorted fieldlist
78+--------------------------------------------------------------------------*/
79static FIELD *
80Insert_Field_By_Position(FIELD *newfield, FIELD *head)
81{
82  FIELD *current, *newhead;
83
84  assert(newfield);
85
86  if (!head)
87    {				/* empty list is trivial */
88      newhead = newfield->snext = newfield->sprev = newfield;
89    }
90  else
91    {
92      newhead = current = head;
93      while ((current->frow < newfield->frow) ||
94	     ((current->frow == newfield->frow) &&
95	      (current->fcol < newfield->fcol)))
96	{
97	  current = current->snext;
98	  if (current == head)
99	    {			/* We cycled through. Reset head to indicate that */
100	      head = (FIELD *)0;
101	      break;
102	    }
103	}
104      /* we leave the loop with current pointing to the field after newfield */
105      newfield->snext = current;
106      newfield->sprev = current->sprev;
107      newfield->snext->sprev = newfield;
108      newfield->sprev->snext = newfield;
109      if (current == head)
110	newhead = newfield;
111    }
112  return (newhead);
113}
114
115/*---------------------------------------------------------------------------
116|   Facility      :  libnform
117|   Function      :  static void Disconnect_Fields(FORM *form)
118|
119|   Description   :  Break association between form and array of fields.
120|
121|   Return Values :  -
122+--------------------------------------------------------------------------*/
123static void
124Disconnect_Fields(FORM *form)
125{
126  if (form->field)
127    {
128      FIELD **fields;
129
130      for (fields = form->field; *fields; fields++)
131	{
132	  if (form == (*fields)->form)
133	    (*fields)->form = (FORM *)0;
134	}
135
136      form->rows = form->cols = 0;
137      form->maxfield = form->maxpage = -1;
138      form->field = (FIELD **)0;
139      if (form->page)
140	free(form->page);
141      form->page = (_PAGE *) 0;
142    }
143}
144
145/*---------------------------------------------------------------------------
146|   Facility      :  libnform
147|   Function      :  static int Connect_Fields(FORM *form, FIELD **fields)
148|
149|   Description   :  Set association between form and array of fields.
150|
151|   Return Values :  E_OK            - no error
152|                    E_CONNECTED     - a field is already connected
153|                    E_BAD_ARGUMENT  - Invalid form pointer or field array
154|                    E_SYSTEM_ERROR  - not enough memory
155+--------------------------------------------------------------------------*/
156static int
157Connect_Fields(FORM *form, FIELD **fields)
158{
159  int field_cnt, j;
160  int page_nr;
161  _PAGE *pg;
162
163  T((T_CALLED("Connect_Fields(%p,%p)"), (void *)form, (void *)fields));
164
165  assert(form);
166
167  form->field = fields;
168  form->maxfield = 0;
169  form->maxpage = 0;
170
171  if (!fields)
172    RETURN(E_OK);
173
174  page_nr = 0;
175  /* store formpointer in fields and count pages */
176  for (field_cnt = 0; fields[field_cnt]; field_cnt++)
177    {
178      if (fields[field_cnt]->form)
179	RETURN(E_CONNECTED);
180      if (field_cnt == 0 ||
181	  (fields[field_cnt]->status & _NEWPAGE))
182	page_nr++;
183      fields[field_cnt]->form = form;
184    }
185  if (field_cnt == 0 || (short)field_cnt < 0)
186    RETURN(E_BAD_ARGUMENT);
187
188  /* allocate page structures */
189  if ((pg = typeMalloc(_PAGE, page_nr)) != (_PAGE *) 0)
190    {
191      T((T_CREATE("_PAGE %p"), (void *)pg));
192      form->page = pg;
193    }
194  else
195    RETURN(E_SYSTEM_ERROR);
196
197  /* Cycle through fields and calculate page boundaries as well as
198     size of the form */
199  for (j = 0; j < field_cnt; j++)
200    {
201      int maximum_row_in_field;
202      int maximum_col_in_field;
203
204      if (j == 0)
205	pg->pmin = (short)j;
206      else
207	{
208	  if (fields[j]->status & _NEWPAGE)
209	    {
210	      pg->pmax = (short)(j - 1);
211	      pg++;
212	      pg->pmin = (short)j;
213	    }
214	}
215
216      maximum_row_in_field = fields[j]->frow + fields[j]->rows;
217      maximum_col_in_field = fields[j]->fcol + fields[j]->cols;
218
219      if (form->rows < maximum_row_in_field)
220	form->rows = (short)maximum_row_in_field;
221      if (form->cols < maximum_col_in_field)
222	form->cols = (short)maximum_col_in_field;
223    }
224
225  pg->pmax = (short)(field_cnt - 1);
226  form->maxfield = (short)field_cnt;
227  form->maxpage = (short)page_nr;
228
229  /* Sort fields on form pages */
230  for (page_nr = 0; page_nr < form->maxpage; page_nr++)
231    {
232      FIELD *fld = (FIELD *)0;
233
234      for (j = form->page[page_nr].pmin; j <= form->page[page_nr].pmax; j++)
235	{
236	  fields[j]->index = (short)j;
237	  fields[j]->page = (short)page_nr;
238	  fld = Insert_Field_By_Position(fields[j], fld);
239	}
240      if (fld)
241	{
242	  form->page[page_nr].smin = fld->index;
243	  form->page[page_nr].smax = fld->sprev->index;
244	}
245      else
246	{
247	  form->page[page_nr].smin = 0;
248	  form->page[page_nr].smax = 0;
249	}
250    }
251  RETURN(E_OK);
252}
253
254/*---------------------------------------------------------------------------
255|   Facility      :  libnform
256|   Function      :  static int Associate_Fields(FORM *form, FIELD **fields)
257|
258|   Description   :  Set association between form and array of fields.
259|                    If there are fields, position to first active field.
260|
261|   Return Values :  E_OK            - success
262|                    E_BAD_ARGUMENT  - Invalid form pointer or field array
263|                    E_CONNECTED     - a field is already connected
264|                    E_SYSTEM_ERROR  - not enough memory
265+--------------------------------------------------------------------------*/
266NCURSES_INLINE static int
267Associate_Fields(FORM *form, FIELD **fields)
268{
269  int res = Connect_Fields(form, fields);
270
271  if (res == E_OK)
272    {
273      if (form->maxpage > 0)
274	{
275	  form->curpage = 0;
276	  form_driver(form, FIRST_ACTIVE_MAGIC);
277	}
278      else
279	{
280	  form->curpage = -1;
281	  form->current = (FIELD *)0;
282	}
283    }
284  return (res);
285}
286
287/*---------------------------------------------------------------------------
288|   Facility      :  libnform
289|   Function      :  FORM *new_form_sp(SCREEN* sp, FIELD** fields )
290|
291|   Description   :  Create new form with given array of fields.
292|
293|   Return Values :  Pointer to form. NULL if error occurred.
294!                    Set errno:
295|                    E_OK            - success
296|                    E_BAD_ARGUMENT  - Invalid form pointer or field array
297|                    E_CONNECTED     - a field is already connected
298|                    E_SYSTEM_ERROR  - not enough memory
299+--------------------------------------------------------------------------*/
300FORM_EXPORT(FORM *)
301NCURSES_SP_NAME(new_form) (NCURSES_SP_DCLx FIELD **fields)
302{
303  int err = E_SYSTEM_ERROR;
304  FORM *form = (FORM *)0;
305
306  T((T_CALLED("new_form(%p,%p)"), (void *)SP_PARM, (void *)fields));
307
308  if (IsValidScreen(SP_PARM))
309    {
310      form = typeMalloc(FORM, 1);
311
312      if (form)
313	{
314	  T((T_CREATE("form %p"), (void *)form));
315	  *form = *_nc_Default_Form;
316	  /* This ensures win and sub are always non-null,
317	     so we can derive always the SCREEN that this form is
318	     running on. */
319	  form->win = StdScreen(SP_PARM);
320	  form->sub = StdScreen(SP_PARM);
321	  if ((err = Associate_Fields(form, fields)) != E_OK)
322	    {
323	      free_form(form);
324	      form = (FORM *)0;
325	    }
326	}
327    }
328
329  if (!form)
330    SET_ERROR(err);
331
332  returnForm(form);
333}
334
335/*---------------------------------------------------------------------------
336|   Facility      :  libnform
337|   Function      :  FORM* new_form(FIELD** fields )
338|
339|   Description   :  Create new form with given array of fields.
340|
341|   Return Values :  Pointer to form. NULL if error occurred.
342!                    Set errno:
343|                    E_OK            - success
344|                    E_BAD_ARGUMENT  - Invalid form pointer or field array
345|                    E_CONNECTED     - a field is already connected
346|                    E_SYSTEM_ERROR  - not enough memory
347+--------------------------------------------------------------------------*/
348#if NCURSES_SP_FUNCS
349FORM_EXPORT(FORM *)
350new_form(FIELD **fields)
351{
352  return NCURSES_SP_NAME(new_form) (CURRENT_SCREEN, fields);
353}
354#endif
355
356/*---------------------------------------------------------------------------
357|   Facility      :  libnform
358|   Function      :  int free_form( FORM *form )
359|
360|   Description   :  Release internal memory associated with form.
361|
362|   Return Values :  E_OK           - no error
363|                    E_BAD_ARGUMENT - invalid form pointer
364|                    E_POSTED       - form is posted
365+--------------------------------------------------------------------------*/
366FORM_EXPORT(int)
367free_form(FORM *form)
368{
369  T((T_CALLED("free_form(%p)"), (void *)form));
370
371  if (!form)
372    RETURN(E_BAD_ARGUMENT);
373
374  if (form->status & _POSTED)
375    RETURN(E_POSTED);
376
377  Disconnect_Fields(form);
378  if (form->page)
379    free(form->page);
380  free(form);
381
382  RETURN(E_OK);
383}
384
385/*---------------------------------------------------------------------------
386|   Facility      :  libnform
387|   Function      :  int set_form_fields( FORM *form, FIELD **fields )
388|
389|   Description   :  Set a new association of an array of fields to a form
390|
391|   Return Values :  E_OK            - no error
392|                    E_BAD_ARGUMENT  - Invalid form pointer or field array
393|                    E_CONNECTED     - a field is already connected
394|                    E_POSTED        - form is posted
395|                    E_SYSTEM_ERROR  - not enough memory
396+--------------------------------------------------------------------------*/
397FORM_EXPORT(int)
398set_form_fields(FORM *form, FIELD **fields)
399{
400  FIELD **old;
401  int res;
402
403  T((T_CALLED("set_form_fields(%p,%p)"), (void *)form, (void *)fields));
404
405  if (!form)
406    RETURN(E_BAD_ARGUMENT);
407
408  if (form->status & _POSTED)
409    RETURN(E_POSTED);
410
411  old = form->field;
412  Disconnect_Fields(form);
413
414  if ((res = Associate_Fields(form, fields)) != E_OK)
415    Connect_Fields(form, old);
416
417  RETURN(res);
418}
419
420/*---------------------------------------------------------------------------
421|   Facility      :  libnform
422|   Function      :  FIELD **form_fields( const FORM *form )
423|
424|   Description   :  Retrieve array of fields
425|
426|   Return Values :  Pointer to field array
427+--------------------------------------------------------------------------*/
428FORM_EXPORT(FIELD **)
429form_fields(const FORM *form)
430{
431  T((T_CALLED("form_field(%p)"), (const void *)form));
432  returnFieldPtr(Normalize_Form(form)->field);
433}
434
435/*---------------------------------------------------------------------------
436|   Facility      :  libnform
437|   Function      :  int field_count( const FORM *form )
438|
439|   Description   :  Retrieve number of fields
440|
441|   Return Values :  Number of fields, -1 if none are defined
442+--------------------------------------------------------------------------*/
443FORM_EXPORT(int)
444field_count(const FORM *form)
445{
446  T((T_CALLED("field_count(%p)"), (const void *)form));
447
448  returnCode(Normalize_Form(form)->maxfield);
449}
450
451/* frm_def.c ends here */
452