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