ncurses 6.2 - patch 20211018
[ncurses.git] / c++ / cursesf.h
1 // * This makes emacs happy -*-Mode: C++;-*-
2 // vile:cppmode
3 /****************************************************************************
4  * Copyright 2019-2020,2021 Thomas E. Dickey                                *
5  * Copyright 1998-2012,2014 Free Software Foundation, Inc.                  *
6  *                                                                          *
7  * Permission is hereby granted, free of charge, to any person obtaining a  *
8  * copy of this software and associated documentation files (the            *
9  * "Software"), to deal in the Software without restriction, including      *
10  * without limitation the rights to use, copy, modify, merge, publish,      *
11  * distribute, distribute with modifications, sublicense, and/or sell       *
12  * copies of the Software, and to permit persons to whom the Software is    *
13  * furnished to do so, subject to the following conditions:                 *
14  *                                                                          *
15  * The above copyright notice and this permission notice shall be included  *
16  * in all copies or substantial portions of the Software.                   *
17  *                                                                          *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
21  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
24  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
25  *                                                                          *
26  * Except as contained in this notice, the name(s) of the above copyright   *
27  * holders shall not be used in advertising or otherwise to promote the     *
28  * sale, use or other dealings in this Software without prior written       *
29  * authorization.                                                           *
30  ****************************************************************************/
31
32 /****************************************************************************
33  *   Author: Juergen Pfeifer, 1997                                          *
34  ****************************************************************************/
35
36 // $Id: cursesf.h,v 1.38 2021/04/17 18:11:08 tom Exp $
37
38 #ifndef NCURSES_CURSESF_H_incl
39 #define NCURSES_CURSESF_H_incl 1
40
41 #include <cursesp.h>
42
43 #ifndef __EXT_QNX
44 #include <string.h>
45 #endif
46
47 extern "C" {
48 #  include <form.h>
49 }
50 //
51 // -------------------------------------------------------------------------
52 // The abstract base class for builtin and user defined Fieldtypes.
53 // -------------------------------------------------------------------------
54 //
55 class NCURSES_CXX_IMPEXP NCursesFormField; // forward declaration
56
57 // Class to represent builtin field types as well as C++ written new
58 // fieldtypes (see classes UserDefineFieldType...
59 class NCURSES_CXX_IMPEXP NCursesFieldType
60 {
61   friend class NCursesFormField;
62
63 protected:
64   FIELDTYPE* fieldtype;
65
66   inline void OnError(int err) const THROW2(NCursesException const, NCursesFormException) {
67     if (err!=E_OK)
68       THROW(new NCursesFormException (err));
69   }
70
71   NCursesFieldType(FIELDTYPE *f) : fieldtype(f) {
72   }
73
74   virtual ~NCursesFieldType() {}
75
76   // Set the fields f fieldtype to this one.
77   virtual void set(NCursesFormField& f) = 0;
78
79 public:
80   NCursesFieldType()
81     : fieldtype(STATIC_CAST(FIELDTYPE*)(0))
82   {
83   }
84
85   NCursesFieldType& operator=(const NCursesFieldType& rhs)
86   {
87     if (this != &rhs) {
88       *this = rhs;
89     }
90     return *this;
91   }
92
93   NCursesFieldType(const NCursesFieldType& rhs)
94     : fieldtype(rhs.fieldtype)
95   {
96   }
97
98 };
99
100 //
101 // -------------------------------------------------------------------------
102 // The class representing a forms field, wrapping the lowlevel FIELD struct
103 // -------------------------------------------------------------------------
104 //
105 class NCURSES_CXX_IMPEXP NCursesFormField
106 {
107   friend class NCursesForm;
108
109 protected:
110   FIELD *field;              // lowlevel structure
111   NCursesFieldType* ftype;   // Associated field type
112
113   // Error handler
114   inline void OnError (int err) const THROW2(NCursesException const, NCursesFormException) {
115     if (err != E_OK)
116       THROW(new NCursesFormException (err));
117   }
118
119 public:
120   // Create a 'Null' field. Can be used to delimit a field list
121   NCursesFormField()
122     : field(STATIC_CAST(FIELD*)(0)),
123       ftype(STATIC_CAST(NCursesFieldType*)(0))
124   {
125   }
126
127   // Create a new field
128   NCursesFormField (int rows,
129                     int ncols,
130                     int first_row = 0,
131                     int first_col = 0,
132                     int offscreen_rows = 0,
133                     int additional_buffers = 0)
134     : field(0),
135       ftype(STATIC_CAST(NCursesFieldType*)(0))
136   {
137       field = ::new_field(rows, ncols, first_row, first_col,
138                           offscreen_rows, additional_buffers);
139       if (!field)
140         OnError(errno);
141   }
142
143   NCursesFormField& operator=(const NCursesFormField& rhs)
144   {
145     if (this != &rhs) {
146       *this = rhs;
147     }
148     return *this;
149   }
150
151   NCursesFormField(const NCursesFormField& rhs)
152     : field(rhs.field), ftype(rhs.ftype)
153   {
154   }
155
156   virtual ~NCursesFormField () THROWS(NCursesException);
157
158   // Duplicate the field at a new position
159   inline NCursesFormField* dup(int first_row, int first_col)
160   {
161     NCursesFormField* f = new NCursesFormField();
162     if (!f)
163       OnError(E_SYSTEM_ERROR);
164     else {
165       f->ftype = ftype;
166       f->field = ::dup_field(field,first_row,first_col);
167       if (!f->field)
168         OnError(errno);
169     }
170     return f;
171   }
172
173   // Link the field to a new location
174   inline NCursesFormField* link(int first_row, int first_col) {
175     NCursesFormField* f = new NCursesFormField();
176     if (!f)
177       OnError(E_SYSTEM_ERROR);
178     else {
179       f->ftype = ftype;
180       f->field = ::link_field(field,first_row,first_col);
181       if (!f->field)
182         OnError(errno);
183     }
184     return f;
185   }
186
187   // Get the lowlevel field representation
188   inline FIELD* get_field() const {
189     return field;
190   }
191
192   // Retrieve info about the field
193   inline void info(int& rows, int& ncols,
194                    int& first_row, int& first_col,
195                    int& offscreen_rows, int& additional_buffers) const {
196     OnError(::field_info(field, &rows, &ncols,
197                          &first_row, &first_col,
198                          &offscreen_rows, &additional_buffers));
199   }
200
201   // Retrieve info about the fields dynamic properties.
202   inline void dynamic_info(int& dynamic_rows, int& dynamic_cols,
203                            int& max_growth) const {
204     OnError(::dynamic_field_info(field, &dynamic_rows, &dynamic_cols,
205                                  &max_growth));
206   }
207
208   // For a dynamic field you may set the maximum growth limit.
209   // A zero means unlimited growth.
210   inline void set_maximum_growth(int growth = 0) {
211     OnError(::set_max_field(field,growth));
212   }
213
214   // Move the field to a new position
215   inline void move(int row, int col) {
216     OnError(::move_field(field,row,col));
217   }
218
219   // Mark the field to start a new page
220   inline void new_page(bool pageFlag = FALSE) {
221     OnError(::set_new_page(field,pageFlag));
222   }
223
224   // Retrieve whether or not the field starts a new page.
225   inline bool is_new_page() const {
226     return ::new_page(field);
227   }
228
229   // Set the justification for the field
230   inline void set_justification(int just) {
231     OnError(::set_field_just(field,just));
232   }
233
234   // Retrieve the fields justification
235   inline int justification() const {
236     return ::field_just(field);
237   }
238   // Set the foreground attribute for the field
239   inline void set_foreground(chtype foreground) {
240     OnError(::set_field_fore(field,foreground));
241   }
242
243   // Retrieve the fields foreground attribute
244   inline chtype fore() const {
245     return ::field_fore(field);
246   }
247
248   // Set the background attribute for the field
249   inline void set_background(chtype background) {
250     OnError(::set_field_back(field,background));
251   }
252
253   // Retrieve the fields background attribute
254   inline chtype back() const {
255     return ::field_back(field);
256   }
257
258   // Set the padding character for the field
259   inline void set_pad_character(int padding) {
260     OnError(::set_field_pad(field, padding));
261   }
262
263   // Retrieve the fields padding character
264   inline int pad() const {
265     return ::field_pad(field);
266   }
267
268   // Switch on the fields options
269   inline void options_on (Field_Options opts) {
270     OnError (::field_opts_on (field, opts));
271   }
272
273   // Switch off the fields options
274   inline void options_off (Field_Options opts) {
275     OnError (::field_opts_off (field, opts));
276   }
277
278   // Retrieve the fields options
279   inline Field_Options options () const {
280     return ::field_opts (field);
281   }
282
283   // Set the fields options
284   inline void set_options (Field_Options opts) {
285     OnError (::set_field_opts (field, opts));
286   }
287
288   // Mark the field as changed
289   inline void set_changed(bool changeFlag = TRUE) {
290     OnError(::set_field_status(field,changeFlag));
291   }
292
293   // Test whether or not the field is marked as changed
294   inline bool changed() const {
295     return ::field_status(field);
296   }
297
298   // Return the index of the field in the field array of a form
299   // or -1 if the field is not associated to a form
300   inline int (index)() const {
301     return ::field_index(field);
302   }
303
304   // Store a value in a fields buffer. The default buffer is nr. 0
305   inline void set_value(const char *val, int buffer = 0) {
306     OnError(::set_field_buffer(field,buffer,val));
307   }
308
309   // Retrieve the value of a fields buffer. The default buffer is nr. 0
310   inline char* value(int buffer = 0) const {
311     return ::field_buffer(field,buffer);
312   }
313
314   // Set the validation type of the field.
315   inline void set_fieldtype(NCursesFieldType& f) {
316     ftype = &f;
317     f.set(*this); // A good friend may do that...
318   }
319
320   // Retrieve the validation type of the field.
321   inline NCursesFieldType* fieldtype() const {
322     return ftype;
323   }
324
325 };
326
327   // This are the built-in hook functions in this C++ binding. In C++ we use
328   // virtual member functions (see below On_..._Init and On_..._Termination)
329   // to provide this functionality in an object oriented manner.
330 extern "C" {
331   void _nc_xx_frm_init(FORM *);
332   void _nc_xx_frm_term(FORM *);
333   void _nc_xx_fld_init(FORM *);
334   void _nc_xx_fld_term(FORM *);
335 }
336
337 //
338 // -------------------------------------------------------------------------
339 // The class representing a form, wrapping the lowlevel FORM struct
340 // -------------------------------------------------------------------------
341 //
342 class NCURSES_CXX_IMPEXP NCursesForm : public NCursesPanel
343 {
344 protected:
345   FORM* form;  // the lowlevel structure
346
347 private:
348   NCursesWindow* sub;   // the subwindow object
349   bool b_sub_owner;     // is this our own subwindow?
350   bool b_framed;        // has the form a border?
351   bool b_autoDelete;    // Delete fields when deleting form?
352
353   NCursesFormField** my_fields; // The array of fields for this form
354
355   // This structure is used for the form's user data field to link the
356   // FORM* to the C++ object and to provide extra space for a user pointer.
357   typedef struct {
358     void*              m_user;      // the pointer for the user's data
359     const NCursesForm* m_back;      // backward pointer to C++ object
360     const FORM*        m_owner;
361   } UserHook;
362
363   // Get the backward pointer to the C++ object from a FORM
364   static inline NCursesForm* getHook(const FORM *f) {
365     UserHook* hook = reinterpret_cast<UserHook*>(::form_userptr(f));
366     assert(hook != 0 && hook->m_owner==f);
367     return const_cast<NCursesForm*>(hook->m_back);
368   }
369
370   friend void _nc_xx_frm_init(FORM *);
371   friend void _nc_xx_frm_term(FORM *);
372   friend void _nc_xx_fld_init(FORM *);
373   friend void _nc_xx_fld_term(FORM *);
374
375   // Calculate FIELD* array for the menu
376   FIELD** mapFields(NCursesFormField* nfields[]);
377
378 protected:
379   // internal routines
380   inline void set_user(void *user) {
381     UserHook* uptr = reinterpret_cast<UserHook*>(::form_userptr (form));
382     assert (uptr != 0 && uptr->m_back==this && uptr->m_owner==form);
383     uptr->m_user = user;
384   }
385
386   inline void *get_user() {
387     UserHook* uptr = reinterpret_cast<UserHook*>(::form_userptr (form));
388     assert (uptr != 0 && uptr->m_back==this && uptr->m_owner==form);
389     return uptr->m_user;
390   }
391
392   void InitForm (NCursesFormField* Fields[],
393                  bool with_frame,
394                  bool autoDeleteFields);
395
396   inline void OnError (int err) const THROW2(NCursesException const, NCursesFormException) {
397     if (err != E_OK)
398       THROW(new NCursesFormException (err));
399   }
400
401   // this wraps the form_driver call.
402   virtual int driver (int c) ;
403
404   // 'Internal' constructor, builds an object without association to a
405   // field array.
406   NCursesForm( int  nlines,
407                int  ncols,
408                int  begin_y = 0,
409                int  begin_x = 0)
410     : NCursesPanel(nlines, ncols, begin_y, begin_x),
411       form (STATIC_CAST(FORM*)(0)),
412       sub(0),
413       b_sub_owner(0),
414       b_framed(0),
415       b_autoDelete(0),
416       my_fields(0)
417   {
418   }
419
420 public:
421   // Create form for the default panel.
422   NCursesForm (NCursesFormField* Fields[],
423                bool with_frame=FALSE,         // reserve space for a frame?
424                bool autoDelete_Fields=FALSE)  // do automatic cleanup?
425     : NCursesPanel(),
426       form(0),
427       sub(0),
428       b_sub_owner(0),
429       b_framed(0),
430       b_autoDelete(0),
431       my_fields(0)
432   {
433     InitForm(Fields, with_frame, autoDelete_Fields);
434   }
435
436   // Create a form in a panel with the given position and size.
437   NCursesForm (NCursesFormField* Fields[],
438                int  nlines,
439                int  ncols,
440                int  begin_y,
441                int  begin_x,
442                bool with_frame=FALSE,        // reserve space for a frame?
443                bool autoDelete_Fields=FALSE) // do automatic cleanup?
444     : NCursesPanel(nlines, ncols, begin_y, begin_x),
445       form(0),
446       sub(0),
447       b_sub_owner(0),
448       b_framed(0),
449       b_autoDelete(0),
450       my_fields(0)
451   {
452       InitForm(Fields, with_frame, autoDelete_Fields);
453   }
454
455   NCursesForm& operator=(const NCursesForm& rhs)
456   {
457     if (this != &rhs) {
458       *this = rhs;
459       NCursesPanel::operator=(rhs);
460     }
461     return *this;
462   }
463
464   NCursesForm(const NCursesForm& rhs)
465     : NCursesPanel(rhs),
466       form(rhs.form),
467       sub(rhs.sub),
468       b_sub_owner(rhs.b_sub_owner),
469       b_framed(rhs.b_framed),
470       b_autoDelete(rhs.b_autoDelete),
471       my_fields(rhs.my_fields)
472   {
473   }
474
475   virtual ~NCursesForm() THROWS(NCursesException);
476
477   // Set the default attributes for the form
478   virtual void setDefaultAttributes();
479
480   // Retrieve current field of the form.
481   inline NCursesFormField* current_field() const {
482     return my_fields[::field_index(::current_field(form))];
483   }
484
485   // Set the forms subwindow
486   void setSubWindow(NCursesWindow& sub);
487
488   // Set these fields for the form
489   inline void setFields(NCursesFormField* Fields[]) {
490     OnError(::set_form_fields(form,mapFields(Fields)));
491   }
492
493   // Remove the form from the screen
494   inline void unpost (void) {
495     OnError (::unpost_form (form));
496   }
497
498   // Post the form to the screen if flag is true, unpost it otherwise
499   inline void post(bool flag = TRUE) {
500     OnError (flag ? ::post_form(form) : ::unpost_form (form));
501   }
502
503   // Decorations
504   inline void frame(const char *title=NULL, const char* btitle=NULL) {
505     if (b_framed)
506       NCursesPanel::frame(title,btitle);
507     else
508       OnError(E_SYSTEM_ERROR);
509   }
510
511   inline void boldframe(const char *title=NULL, const char* btitle=NULL) {
512     if (b_framed)
513       NCursesPanel::boldframe(title,btitle);
514     else
515       OnError(E_SYSTEM_ERROR);
516   }
517
518   inline void label(const char *topLabel, const char *bottomLabel) {
519     if (b_framed)
520       NCursesPanel::label(topLabel,bottomLabel);
521     else
522       OnError(E_SYSTEM_ERROR);
523   }
524
525   // -----
526   // Hooks
527   // -----
528
529   // Called after the form gets repositioned in its window.
530   // This is especially true if the form is posted.
531   virtual void On_Form_Init();
532
533   // Called before the form gets repositioned in its window.
534   // This is especially true if the form is unposted.
535   virtual void On_Form_Termination();
536
537   // Called after the field became the current field
538   virtual void On_Field_Init(NCursesFormField& field);
539
540   // Called before this field is left as current field.
541   virtual void On_Field_Termination(NCursesFormField& field);
542
543   // Calculate required window size for the form.
544   void scale(int& rows, int& ncols) const {
545     OnError(::scale_form(form,&rows,&ncols));
546   }
547
548   // Retrieve number of fields in the form.
549   int count() const {
550     return ::field_count(form);
551   }
552
553   // Make the page the current page of the form.
554   void set_page(int pageNum) {
555     OnError(::set_form_page(form, pageNum));
556   }
557
558   // Retrieve current page number
559   int page() const {
560     return ::form_page(form);
561   }
562
563   // Switch on the forms options
564   inline void options_on (Form_Options opts) {
565     OnError (::form_opts_on (form, opts));
566   }
567
568   // Switch off the forms options
569   inline void options_off (Form_Options opts) {
570     OnError (::form_opts_off (form, opts));
571   }
572
573   // Retrieve the forms options
574   inline Form_Options options () const {
575     return ::form_opts (form);
576   }
577
578   // Set the forms options
579   inline void set_options (Form_Options opts) {
580     OnError (::set_form_opts (form, opts));
581   }
582
583   // Are there more data in the current field after the data shown
584   inline bool data_ahead() const {
585     return ::data_ahead(form);
586   }
587
588   // Are there more data in the current field before the data shown
589   inline bool data_behind() const {
590     return ::data_behind(form);
591   }
592
593   // Position the cursor to the current field
594   inline void position_cursor () {
595     OnError (::pos_form_cursor (form));
596   }
597   // Set the current field
598   inline void set_current(NCursesFormField& F) {
599     OnError (::set_current_field(form, F.field));
600   }
601
602   // Provide a default key virtualization. Translate the keyboard
603   // code c into a form request code.
604   // The default implementation provides a hopefully straightforward
605   // mapping for the most common keystrokes and form requests.
606   virtual int virtualize(int c);
607
608   // Operators
609   inline NCursesFormField* operator[](int i) const {
610     if ( (i < 0) || (i >= ::field_count (form)) )
611       OnError (E_BAD_ARGUMENT);
612     return my_fields[i];
613   }
614
615   // Perform the menu's operation
616   // Return the field where you left the form.
617   virtual NCursesFormField* operator()(void);
618
619   // Exception handlers. The default is a Beep.
620   virtual void On_Request_Denied(int c) const;
621   virtual void On_Invalid_Field(int c) const;
622   virtual void On_Unknown_Command(int c) const;
623
624 };
625
626 //
627 // -------------------------------------------------------------------------
628 // This is the typical C++ typesafe way to allow to attach
629 // user data to a field of a form. Its assumed that the user
630 // data belongs to some class T. Use T as template argument
631 // to create a UserField.
632 // -------------------------------------------------------------------------
633 template<class T> class NCURSES_CXX_IMPEXP NCursesUserField : public NCursesFormField
634 {
635 public:
636   NCursesUserField (int rows,
637                     int ncols,
638                     int first_row = 0,
639                     int first_col = 0,
640                     const T* p_UserData = STATIC_CAST(T*)(0),
641                     int offscreen_rows = 0,
642                     int additional_buffers = 0)
643     : NCursesFormField (rows, ncols,
644                         first_row, first_col,
645                         offscreen_rows, additional_buffers) {
646       if (field)
647         OnError(::set_field_userptr(field, STATIC_CAST(void *)(p_UserData)));
648   }
649
650   virtual ~NCursesUserField() THROWS(NCursesException) {};
651
652   inline const T* UserData (void) const {
653     return reinterpret_cast<const T*>(::field_userptr (field));
654   }
655
656   inline virtual void setUserData(const T* p_UserData) {
657     if (field)
658       OnError (::set_field_userptr (field, STATIC_CAST(void *)(p_UserData)));
659   }
660 };
661 //
662 // -------------------------------------------------------------------------
663 // The same mechanism is used to attach user data to a form
664 // -------------------------------------------------------------------------
665 //
666 template<class T> class NCURSES_CXX_IMPEXP NCursesUserForm : public NCursesForm
667 {
668 protected:
669   // 'Internal' constructor, builds an object without association to a
670   // field array.
671   NCursesUserForm( int  nlines,
672                    int  ncols,
673                    int  begin_y = 0,
674                    int  begin_x = 0,
675                    const T* p_UserData = STATIC_CAST(T*)(0))
676     : NCursesForm(nlines,ncols,begin_y,begin_x) {
677       if (form)
678         set_user (const_cast<void *>(reinterpret_cast<const void*>
679                                      (p_UserData)));
680   }
681
682 public:
683   NCursesUserForm (NCursesFormField* Fields[],
684                    const T* p_UserData = STATIC_CAST(T*)(0),
685                    bool with_frame=FALSE,
686                    bool autoDelete_Fields=FALSE)
687     : NCursesForm (Fields, with_frame, autoDelete_Fields) {
688       if (form)
689         set_user (const_cast<void *>(reinterpret_cast<const void*>(p_UserData)));
690   };
691
692   NCursesUserForm (NCursesFormField* Fields[],
693                    int nlines,
694                    int ncols,
695                    int begin_y = 0,
696                    int begin_x = 0,
697                    const T* p_UserData = STATIC_CAST(T*)(0),
698                    bool with_frame=FALSE,
699                    bool autoDelete_Fields=FALSE)
700     : NCursesForm (Fields, nlines, ncols, begin_y, begin_x,
701                    with_frame, autoDelete_Fields) {
702       if (form)
703         set_user (const_cast<void *>(reinterpret_cast<const void*>
704                                      (p_UserData)));
705   };
706
707   virtual ~NCursesUserForm() THROWS(NCursesException) {
708   };
709
710   inline T* UserData (void) {
711     return reinterpret_cast<T*>(get_user ());
712   };
713
714   inline virtual void setUserData (const T* p_UserData) {
715     if (form)
716       set_user (const_cast<void *>(reinterpret_cast<const void*>(p_UserData)));
717   }
718
719 };
720 //
721 // -------------------------------------------------------------------------
722 // Builtin Fieldtypes
723 // -------------------------------------------------------------------------
724 //
725 class NCURSES_CXX_IMPEXP Alpha_Field : public NCursesFieldType
726 {
727 private:
728   int min_field_width;
729
730   void set(NCursesFormField& f) {
731     OnError(::set_field_type(f.get_field(),fieldtype,min_field_width));
732   }
733
734 public:
735   explicit Alpha_Field(int width)
736     : NCursesFieldType(TYPE_ALPHA),
737       min_field_width(width) {
738   }
739 };
740
741 class NCURSES_CXX_IMPEXP Alphanumeric_Field : public NCursesFieldType
742 {
743 private:
744   int min_field_width;
745
746   void set(NCursesFormField& f) {
747     OnError(::set_field_type(f.get_field(),fieldtype,min_field_width));
748   }
749
750 public:
751   explicit Alphanumeric_Field(int width)
752     : NCursesFieldType(TYPE_ALNUM),
753       min_field_width(width) {
754   }
755 };
756
757 class NCURSES_CXX_IMPEXP Integer_Field : public NCursesFieldType
758 {
759 private:
760   int precision;
761   long lower_limit, upper_limit;
762
763   void set(NCursesFormField& f) {
764     OnError(::set_field_type(f.get_field(),fieldtype,
765                              precision,lower_limit,upper_limit));
766   }
767
768 public:
769   Integer_Field(int prec, long low=0L, long high=0L)
770     : NCursesFieldType(TYPE_INTEGER),
771       precision(prec), lower_limit(low), upper_limit(high) {
772   }
773 };
774
775 class NCURSES_CXX_IMPEXP Numeric_Field : public NCursesFieldType
776 {
777 private:
778   int precision;
779   double lower_limit, upper_limit;
780
781   void set(NCursesFormField& f) {
782     OnError(::set_field_type(f.get_field(),fieldtype,
783                              precision,lower_limit,upper_limit));
784   }
785
786 public:
787   Numeric_Field(int prec, double low=0.0, double high=0.0)
788     : NCursesFieldType(TYPE_NUMERIC),
789       precision(prec), lower_limit(low), upper_limit(high) {
790   }
791 };
792
793 class NCURSES_CXX_IMPEXP Regular_Expression_Field : public NCursesFieldType
794 {
795 private:
796   char* regex;
797
798   void set(NCursesFormField& f) {
799     OnError(::set_field_type(f.get_field(),fieldtype,regex));
800   }
801
802   void copy_regex(const char *source)
803   {
804     regex = new char[1 + ::strlen(source)];
805     (::strcpy)(regex, source);
806   }
807
808 public:
809   explicit Regular_Expression_Field(const char *expr)
810     : NCursesFieldType(TYPE_REGEXP),
811       regex(NULL)
812   {
813     copy_regex(expr);
814   }
815
816   Regular_Expression_Field& operator=(const Regular_Expression_Field& rhs)
817   {
818     if (this != &rhs) {
819       *this = rhs;
820       copy_regex(rhs.regex);
821       NCursesFieldType::operator=(rhs);
822     }
823     return *this;
824   }
825
826   Regular_Expression_Field(const Regular_Expression_Field& rhs)
827     : NCursesFieldType(rhs),
828       regex(NULL)
829   {
830     copy_regex(rhs.regex);
831   }
832
833   ~Regular_Expression_Field() {
834     delete[] regex;
835   }
836 };
837
838 class NCURSES_CXX_IMPEXP Enumeration_Field : public NCursesFieldType
839 {
840 private:
841   const char** list;
842   int case_sensitive;
843   int non_unique_matches;
844
845   void set(NCursesFormField& f) {
846     OnError(::set_field_type(f.get_field(),fieldtype,
847                              list,case_sensitive,non_unique_matches));
848   }
849 public:
850   Enumeration_Field(const char* enums[],
851                     bool case_sens=FALSE,
852                     bool non_unique=FALSE)
853     : NCursesFieldType(TYPE_ENUM),
854       list(enums),
855       case_sensitive(case_sens ? -1 : 0),
856       non_unique_matches(non_unique ? -1 : 0) {
857   }
858
859   Enumeration_Field& operator=(const Enumeration_Field& rhs)
860   {
861     if (this != &rhs) {
862       *this = rhs;
863       NCursesFieldType::operator=(rhs);
864     }
865     return *this;
866   }
867
868   Enumeration_Field(const Enumeration_Field& rhs)
869     : NCursesFieldType(rhs),
870       list(rhs.list),
871       case_sensitive(rhs.case_sensitive),
872       non_unique_matches(rhs.non_unique_matches)
873   {
874   }
875 };
876
877 class NCURSES_CXX_IMPEXP IPV4_Address_Field : public NCursesFieldType
878 {
879 private:
880   void set(NCursesFormField& f) {
881     OnError(::set_field_type(f.get_field(),fieldtype));
882   }
883
884 public:
885   IPV4_Address_Field() : NCursesFieldType(TYPE_IPV4) {
886   }
887 };
888
889 extern "C" {
890   bool _nc_xx_fld_fcheck(FIELD *, const void*);
891   bool _nc_xx_fld_ccheck(int c, const void *);
892   void* _nc_xx_fld_makearg(va_list*);
893 }
894
895 //
896 // -------------------------------------------------------------------------
897 // Abstract base class for User-Defined Fieldtypes
898 // -------------------------------------------------------------------------
899 //
900 class NCURSES_CXX_IMPEXP UserDefinedFieldType : public NCursesFieldType
901 {
902   friend class UDF_Init; // Internal helper to set up statics
903 private:
904   // For all C++ defined fieldtypes we need only one generic lowlevel
905   // FIELDTYPE* element.
906   static FIELDTYPE* generic_fieldtype;
907
908 protected:
909   // This are the functions required by the low level libforms functions
910   // to construct a fieldtype.
911   friend bool _nc_xx_fld_fcheck(FIELD *, const void*);
912   friend bool _nc_xx_fld_ccheck(int c, const void *);
913   friend void* _nc_xx_fld_makearg(va_list*);
914
915   void set(NCursesFormField& f) {
916     OnError(::set_field_type(f.get_field(),fieldtype,&f));
917   }
918
919 protected:
920   // Redefine this function to do a field validation. The argument
921   // is a reference to the field you should validate.
922   virtual bool field_check(NCursesFormField& f) = 0;
923
924   // Redefine this function to do a character validation. The argument
925   // is the character to be validated.
926   virtual bool char_check (int c) = 0;
927
928 public:
929   UserDefinedFieldType();
930 };
931
932 extern "C" {
933   bool _nc_xx_next_choice(FIELD*, const void *);
934   bool _nc_xx_prev_choice(FIELD*, const void *);
935 }
936
937 //
938 // -------------------------------------------------------------------------
939 // Abstract base class for User-Defined Fieldtypes with Choice functions
940 // -------------------------------------------------------------------------
941 //
942 class NCURSES_CXX_IMPEXP UserDefinedFieldType_With_Choice : public UserDefinedFieldType
943 {
944   friend class UDF_Init; // Internal helper to set up statics
945 private:
946   // For all C++ defined fieldtypes with choice functions we need only one
947   // generic lowlevel FIELDTYPE* element.
948   static FIELDTYPE* generic_fieldtype_with_choice;
949
950   // This are the functions required by the low level libforms functions
951   // to construct a fieldtype with choice functions.
952   friend bool _nc_xx_next_choice(FIELD*, const void *);
953   friend bool _nc_xx_prev_choice(FIELD*, const void *);
954
955 protected:
956   // Redefine this function to do the retrieval of the next choice value.
957   // The argument is a reference to the field tobe examined.
958   virtual bool next    (NCursesFormField& f) = 0;
959
960   // Redefine this function to do the retrieval of the previous choice value.
961   // The argument is a reference to the field tobe examined.
962   virtual bool previous(NCursesFormField& f) = 0;
963
964 public:
965   UserDefinedFieldType_With_Choice();
966 };
967
968 #endif /* NCURSES_CURSESF_H_incl */