ncurses 5.4
[ncurses.git] / c++ / cursesf.h
1 // * This makes emacs happy -*-Mode: C++;-*-
2 /****************************************************************************
3  * Copyright (c) 1998-2003,2004 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, 1997                                          *
32  ****************************************************************************/
33
34 // $Id: cursesf.h,v 1.19 2004/01/15 00:43:46 tom Exp $
35
36 #ifndef NCURSES_CURSESF_H_incl
37 #define NCURSES_CURSESF_H_incl 1
38
39 #include <cursesp.h>
40 #include <string.h>
41
42 extern "C" {
43 #  include <form.h>
44 }
45 //
46 // -------------------------------------------------------------------------
47 // The abstract base class for buitin and user defined Fieldtypes.
48 // -------------------------------------------------------------------------
49 //
50 class NCURSES_IMPEXP NCursesFormField; // forward declaration
51
52 // Class to represent builtin field types as well as C++ written new
53 // fieldtypes (see classes UserDefineFieldType...
54 class NCURSES_IMPEXP NCursesFieldType {
55   friend class NCursesFormField;
56
57 protected:
58   FIELDTYPE* fieldtype;
59
60   inline void OnError(int err) const THROWS(NCursesFormException) {
61     if (err!=E_OK)
62       THROW(new NCursesFormException (err));
63   }
64
65   NCursesFieldType(FIELDTYPE *f) : fieldtype(f) {
66   }
67
68   virtual ~NCursesFieldType() {}
69
70   // Set the fields f fieldtype to this one.
71   virtual void set(NCursesFormField& f) = 0;
72
73 public:
74   NCursesFieldType() : fieldtype((FIELDTYPE*)0) {
75   }
76 };
77
78 //
79 // -------------------------------------------------------------------------
80 // The class representing a forms field, wrapping the lowlevel FIELD struct
81 // -------------------------------------------------------------------------
82 //
83 class NCURSES_IMPEXP NCursesFormField {
84   friend class NCursesForm;
85
86 protected:
87   FIELD *field;              // lowlevel structure
88   NCursesFieldType* ftype;   // Associated field type
89
90   // Error handler
91   inline void OnError (int err) const THROWS(NCursesFormException) {
92     if (err != E_OK)
93       THROW(new NCursesFormException (err));
94   }
95
96 public:
97   // Create a 'Null' field. Can be used to delimit a field list
98   NCursesFormField()
99     : field((FIELD*)0), ftype((NCursesFieldType*)0) {
100   }
101
102   // Create a new field
103   NCursesFormField (int rows,
104                     int cols,
105                     int first_row = 0,
106                     int first_col = 0,
107                     int offscreen_rows = 0,
108                     int additional_buffers = 0)
109     : ftype((NCursesFieldType*)0) {
110       field = ::new_field(rows,cols,first_row,first_col,
111                           offscreen_rows, additional_buffers);
112       if (!field)
113         OnError(errno);
114   }
115
116   virtual ~NCursesFormField ();
117
118   // Duplicate the field at a new position
119   inline NCursesFormField* dup(int first_row, int first_col) {
120     NCursesFormField* f = new NCursesFormField();
121     if (!f)
122       OnError(E_SYSTEM_ERROR);
123     else {
124       f->ftype = ftype;
125       f->field = ::dup_field(field,first_row,first_col);
126       if (!f->field)
127         OnError(errno);
128     }
129     return f;
130   }
131
132   // Link the field to a new location
133   inline NCursesFormField* link(int first_row, int first_col) {
134     NCursesFormField* f = new NCursesFormField();
135     if (!f)
136       OnError(E_SYSTEM_ERROR);
137     else {
138       f->ftype = ftype;
139       f->field = ::link_field(field,first_row,first_col);
140       if (!f->field)
141         OnError(errno);
142     }
143     return f;
144   }
145
146   // Get the lowlevel field representation
147   inline FIELD* get_field() const {
148     return field;
149   }
150
151   // Retrieve info about the field
152   inline void info(int& rows, int& cols,
153                    int& first_row, int& first_col,
154                    int& offscreen_rows, int& additional_buffers) const {
155     OnError(::field_info(field, &rows, &cols,
156                          &first_row, &first_col,
157                          &offscreen_rows, &additional_buffers));
158   }
159
160   // Retrieve info about the fields dynamic properties.
161   inline void dynamic_info(int& dynamic_rows, int& dynamic_cols,
162                            int& max_growth) const {
163     OnError(::dynamic_field_info(field, &dynamic_rows, &dynamic_cols,
164                                  &max_growth));
165   }
166
167   // For a dynamic field you may set the maximum growth limit.
168   // A zero means unlimited growth.
169   inline void set_maximum_growth(int growth = 0) {
170     OnError(::set_max_field(field,growth));
171   }
172
173   // Move the field to a new position
174   inline void move(int row, int col) {
175     OnError(::move_field(field,row,col));
176   }
177
178   // Mark the field to start a new page
179   inline void new_page(bool pageFlag = FALSE) {
180     OnError(::set_new_page(field,pageFlag));
181   }
182
183   // Retrieve whether or not the field starts a new page.
184   inline bool is_new_page() const {
185     return ::new_page(field);
186   }
187
188   // Set the justification for the field
189   inline void set_justification(int just) {
190     OnError(::set_field_just(field,just));
191   }
192
193   // Retrieve the fields justification
194   inline int justification() const {
195     return ::field_just(field);
196   }
197   // Set the foreground attribute for the field
198   inline void set_foreground(chtype fore) {
199     OnError(::set_field_fore(field,fore));
200   }
201
202   // Retrieve the fields foreground attribute
203   inline chtype fore() const {
204     return ::field_fore(field);
205   }
206
207   // Set the background attribute for the field
208   inline void set_background(chtype back) {
209     OnError(::set_field_back(field,back));
210   }
211
212   // Retrieve the fields background attribute
213   inline chtype back() const {
214     return ::field_back(field);
215   }
216
217   // Set the padding character for the field
218   inline void set_pad_character(int pad) {
219     OnError(::set_field_pad(field,pad));
220   }
221
222   // Retrieve the fields padding character
223   inline int pad() const {
224     return ::field_pad(field);
225   }
226
227   // Switch on the fields options
228   inline void options_on (Field_Options options) {
229     OnError (::field_opts_on (field, options));
230   }
231
232   // Switch off the fields options
233   inline void options_off (Field_Options options) {
234     OnError (::field_opts_off (field, options));
235   }
236
237   // Retrieve the fields options
238   inline Field_Options options () const {
239     return ::field_opts (field);
240   }
241
242   // Set the fields options
243   inline void set_options (Field_Options options) {
244     OnError (::set_field_opts (field, options));
245   }
246
247   // Mark the field as changed
248   inline void set_changed(bool changeFlag = TRUE) {
249     OnError(::set_field_status(field,changeFlag));
250   }
251
252   // Test whether or not the field is marked as changed
253   inline bool changed() const {
254     return ::field_status(field);
255   }
256
257   // Return the index of the field in the field array of a form
258   // or -1 if the field is not associated to a form
259   inline int (index)() const {
260     return ::field_index(field);
261   }
262
263   // Store a value in a fields buffer. The default buffer is nr. 0
264   inline void set_value(const char *val, int buffer = 0) {
265     OnError(::set_field_buffer(field,buffer,val));
266   }
267
268   // Retrieve the value of a fields buffer. The default buffer is nr. 0
269   inline char* value(int buffer = 0) const {
270     return ::field_buffer(field,buffer);
271   }
272
273   // Set the validation type of the field.
274   inline void set_fieldtype(NCursesFieldType& f) {
275     ftype = &f;
276     f.set(*this); // A good friend may do that...
277   }
278
279   // Retrieve the validation type of the field.
280   inline NCursesFieldType* fieldtype() const {
281     return ftype;
282   }
283
284 };
285
286 //
287 // -------------------------------------------------------------------------
288 // The class representing a form, wrapping the lowlevel FORM struct
289 // -------------------------------------------------------------------------
290 //
291 class NCURSES_IMPEXP NCursesForm : public NCursesPanel {
292 protected:
293   FORM* form;  // the lowlevel structure
294
295 private:
296   NCursesWindow* sub;   // the subwindow object
297   bool b_sub_owner;     // is this our own subwindow?
298   bool b_framed;        // has the form a border?
299   bool b_autoDelete;    // Delete fields when deleting form?
300
301   NCursesFormField** my_fields; // The array of fields for this form
302
303   // This structure is used for the form's user data field to link the
304   // FORM* to the C++ object and to provide extra space for a user pointer.
305   typedef struct {
306     void*              m_user;      // the pointer for the user's data
307     const NCursesForm* m_back;      // backward pointer to C++ object
308     const FORM*        m_owner;
309   } UserHook;
310
311   // Get the backward pointer to the C++ object from a FORM
312   static inline NCursesForm* getHook(const FORM *f) {
313     UserHook* hook = (UserHook*)::form_userptr(f);
314     assert(hook != 0 && hook->m_owner==f);
315     return (NCursesForm*)(hook->m_back);
316   }
317
318   // This are the built-in hook functions in this C++ binding. In C++ we use
319   // virtual member functions (see below On_..._Init and On_..._Termination)
320   // to provide this functionality in an object oriented manner.
321   static void frm_init(FORM *);
322   static void frm_term(FORM *);
323   static void fld_init(FORM *);
324   static void fld_term(FORM *);
325
326   // Calculate FIELD* array for the menu
327   FIELD** mapFields(NCursesFormField* nfields[]);
328
329 protected:
330   // internal routines
331   inline void set_user(void *user) {
332     UserHook* uptr = (UserHook*)::form_userptr (form);
333     assert (uptr != 0 && uptr->m_back==this && uptr->m_owner==form);
334     uptr->m_user = user;
335   }
336
337   inline void *get_user() {
338     UserHook* uptr = (UserHook*)::form_userptr (form);
339     assert (uptr != 0 && uptr->m_back==this && uptr->m_owner==form);
340     return uptr->m_user;
341   }
342
343   void InitForm (NCursesFormField* Fields[],
344                  bool with_frame,
345                  bool autoDeleteFields);
346
347   inline void OnError (int err) const THROWS(NCursesFormException) {
348     if (err != E_OK)
349       THROW(new NCursesFormException (err));
350   }
351
352   // this wraps the form_driver call.
353   virtual int driver (int c) ;
354
355   // 'Internal' constructor, builds an object without association to a
356   // field array.
357   NCursesForm( int  lines,
358                int  cols,
359                int  begin_y = 0,
360                int  begin_x = 0)
361     : NCursesPanel(lines,cols,begin_y,begin_x),
362       form ((FORM*)0) {
363   }
364
365 public:
366   // Create form for the default panel.
367   NCursesForm (NCursesFormField* Fields[],
368                bool with_frame=FALSE,         // reserve space for a frame?
369                bool autoDelete_Fields=FALSE)  // do automatic cleanup?
370     : NCursesPanel() {
371     InitForm(Fields, with_frame, autoDelete_Fields);
372   }
373
374   // Create a form in a panel with the given position and size.
375   NCursesForm (NCursesFormField* Fields[],
376                int  lines,
377                int  cols,
378                int  begin_y,
379                int  begin_x,
380                bool with_frame=FALSE,        // reserve space for a frame?
381                bool autoDelete_Fields=FALSE) // do automatic cleanup?
382     : NCursesPanel(lines, cols, begin_y, begin_x) {
383       InitForm(Fields, with_frame, autoDelete_Fields);
384   }
385
386   virtual ~NCursesForm();
387
388   // Set the default attributes for the form
389   virtual void setDefaultAttributes();
390
391   // Retrieve current field of the form.
392   inline NCursesFormField* current_field() const {
393     return my_fields[::field_index(::current_field(form))];
394   }
395
396   // Set the forms subwindow
397   void setSubWindow(NCursesWindow& sub);
398
399   // Set these fields for the form
400   inline void setFields(NCursesFormField* Fields[]) {
401     OnError(::set_form_fields(form,mapFields(Fields)));
402   }
403
404   // Remove the form from the screen
405   inline void unpost (void) {
406     OnError (::unpost_form (form));
407   }
408
409   // Post the form to the screen if flag is true, unpost it otherwise
410   inline void post(bool flag = TRUE) {
411     OnError (flag ? ::post_form(form) : ::unpost_form (form));
412   }
413
414   // Decorations
415   inline void frame(const char *title=NULL, const char* btitle=NULL) {
416     if (b_framed)
417       NCursesPanel::frame(title,btitle);
418     else
419       OnError(E_SYSTEM_ERROR);
420   }
421
422   inline void boldframe(const char *title=NULL, const char* btitle=NULL) {
423     if (b_framed)
424       NCursesPanel::boldframe(title,btitle);
425     else
426       OnError(E_SYSTEM_ERROR);
427   }
428
429   inline void label(const char *topLabel, const char *bottomLabel) {
430     if (b_framed)
431       NCursesPanel::label(topLabel,bottomLabel);
432     else
433       OnError(E_SYSTEM_ERROR);
434   }
435
436   // -----
437   // Hooks
438   // -----
439
440   // Called after the form gets repositioned in its window.
441   // This is especially true if the form is posted.
442   virtual void On_Form_Init();
443
444   // Called before the form gets repositioned in its window.
445   // This is especially true if the form is unposted.
446   virtual void On_Form_Termination();
447
448   // Called after the field became the current field
449   virtual void On_Field_Init(NCursesFormField& field);
450
451   // Called before this field is left as current field.
452   virtual void On_Field_Termination(NCursesFormField& field);
453
454   // Calculate required window size for the form.
455   void scale(int& rows, int& cols) const {
456     OnError(::scale_form(form,&rows,&cols));
457   }
458
459   // Retrieve number of fields in the form.
460   int count() const {
461     return ::field_count(form);
462   }
463
464   // Make the page the current page of the form.
465   void set_page(int page) {
466     OnError(::set_form_page(form,page));
467   }
468
469   // Retrieve current page number
470   int page() const {
471     return ::form_page(form);
472   }
473
474   // Switch on the forms options
475   inline void options_on (Form_Options options) {
476     OnError (::form_opts_on (form, options));
477   }
478
479   // Switch off the forms options
480   inline void options_off (Form_Options options) {
481     OnError (::form_opts_off (form, options));
482   }
483
484   // Retrieve the forms options
485   inline Form_Options options () const {
486     return ::form_opts (form);
487   }
488
489   // Set the forms options
490   inline void set_options (Form_Options options) {
491     OnError (::set_form_opts (form, options));
492   }
493
494   // Are there more data in the current field after the data shown
495   inline bool data_ahead() const {
496     return ::data_ahead(form);
497   }
498
499   // Are there more data in the current field before the data shown
500   inline bool data_behind() const {
501     return ::data_behind(form);
502   }
503
504   // Position the cursor to the current field
505   inline void position_cursor () {
506     OnError (::pos_form_cursor (form));
507   }
508   // Set the current field
509   inline void set_current(NCursesFormField& F) {
510     OnError (::set_current_field(form, F.field));
511   }
512
513   // Provide a default key virtualization. Translate the keyboard
514   // code c into a form request code.
515   // The default implementation provides a hopefully straightforward
516   // mapping for the most common keystrokes and form requests.
517   virtual int virtualize(int c);
518
519   // Operators
520   inline NCursesFormField* operator[](int i) const {
521     if ( (i < 0) || (i >= ::field_count (form)) )
522       OnError (E_BAD_ARGUMENT);
523     return my_fields[i];
524   }
525
526   // Perform the menu's operation
527   // Return the field where you left the form.
528   virtual NCursesFormField* operator()(void);
529
530   // Exception handlers. The default is a Beep.
531   virtual void On_Request_Denied(int c) const;
532   virtual void On_Invalid_Field(int c) const;
533   virtual void On_Unknown_Command(int c) const;
534
535 };
536
537 //
538 // -------------------------------------------------------------------------
539 // This is the typical C++ typesafe way to allow to attach
540 // user data to a field of a form. Its assumed that the user
541 // data belongs to some class T. Use T as template argument
542 // to create a UserField.
543 // -------------------------------------------------------------------------
544 template<class T> class NCURSES_IMPEXP NCursesUserField : public NCursesFormField
545 {
546 public:
547   NCursesUserField (int rows,
548                     int cols,
549                     int first_row = 0,
550                     int first_col = 0,
551                     const T* p_UserData = (T*)0,
552                     int offscreen_rows = 0,
553                     int additional_buffers = 0)
554     : NCursesFormField (rows, cols,
555                         first_row, first_col,
556                         offscreen_rows, additional_buffers) {
557       if (field)
558         OnError(::set_field_userptr(field,(void *)p_UserData));
559   }
560
561   virtual ~NCursesUserField() {};
562
563   inline const T* UserData (void) const {
564     return (const T*)::field_userptr (field);
565   }
566
567   inline virtual void setUserData(const T* p_UserData) {
568     if (field)
569       OnError (::set_field_userptr (field, (void *)p_UserData));
570   }
571 };
572 //
573 // -------------------------------------------------------------------------
574 // The same mechanism is used to attach user data to a form
575 // -------------------------------------------------------------------------
576 //
577 template<class T> class NCURSES_IMPEXP NCursesUserForm : public NCursesForm
578 {
579 protected:
580   // 'Internal' constructor, builds an object without association to a
581   // field array.
582   NCursesUserForm( int  lines,
583                    int  cols,
584                    int  begin_y = 0,
585                    int  begin_x = 0,
586                    const T* p_UserData = (T*)0)
587     : NCursesForm(lines,cols,begin_y,begin_x) {
588       if (form)
589         set_user ((void *)p_UserData);
590   }
591
592 public:
593   NCursesUserForm (NCursesFormField Fields[],
594                    const T* p_UserData = (T*)0,
595                    bool with_frame=FALSE,
596                    bool autoDelete_Fields=FALSE)
597     : NCursesForm (Fields, with_frame, autoDelete_Fields) {
598       if (form)
599         set_user ((void *)p_UserData);
600   };
601
602   NCursesUserForm (NCursesFormField Fields[],
603                    int lines,
604                    int cols,
605                    int begin_y = 0,
606                    int begin_x = 0,
607                    const T* p_UserData = (T*)0,
608                    bool with_frame=FALSE,
609                    bool autoDelete_Fields=FALSE)
610     : NCursesForm (Fields, lines, cols, begin_y, begin_x,
611                    with_frame, autoDelete_Fields) {
612       if (form)
613         set_user ((void *)p_UserData);
614   };
615
616   virtual ~NCursesUserForm() {
617   };
618
619   inline T* UserData (void) const {
620     return (T*)get_user ();
621   };
622
623   inline virtual void setUserData (const T* p_UserData) {
624     if (form)
625       set_user ((void *)p_UserData);
626   }
627
628 };
629 //
630 // -------------------------------------------------------------------------
631 // Builtin Fieldtypes
632 // -------------------------------------------------------------------------
633 //
634 class NCURSES_IMPEXP Alpha_Field : public NCursesFieldType {
635 private:
636   int min_field_width;
637
638   void set(NCursesFormField& f) {
639     OnError(::set_field_type(f.get_field(),fieldtype,min_field_width));
640   }
641
642 public:
643   Alpha_Field(int width)
644     : NCursesFieldType(TYPE_ALPHA),
645       min_field_width(width) {
646   }
647 };
648
649 class NCURSES_IMPEXP Alphanumeric_Field : public NCursesFieldType {
650 private:
651   int min_field_width;
652
653   void set(NCursesFormField& f) {
654     OnError(::set_field_type(f.get_field(),fieldtype,min_field_width));
655   }
656
657 public:
658   Alphanumeric_Field(int width)
659     : NCursesFieldType(TYPE_ALNUM),
660       min_field_width(width) {
661   }
662 };
663
664 class NCURSES_IMPEXP Integer_Field : public NCursesFieldType {
665 private:
666   int precision;
667   long lower_limit, upper_limit;
668
669   void set(NCursesFormField& f) {
670     OnError(::set_field_type(f.get_field(),fieldtype,
671                              precision,lower_limit,upper_limit));
672   }
673
674 public:
675   Integer_Field(int prec, long low=0L, long high=0L)
676     : NCursesFieldType(TYPE_INTEGER),
677       precision(prec), lower_limit(low), upper_limit(high) {
678   }
679 };
680
681 class NCURSES_IMPEXP Numeric_Field : public NCursesFieldType {
682 private:
683   int precision;
684   double lower_limit, upper_limit;
685
686   void set(NCursesFormField& f) {
687     OnError(::set_field_type(f.get_field(),fieldtype,
688                              precision,lower_limit,upper_limit));
689   }
690
691 public:
692   Numeric_Field(int prec, double low=0.0, double high=0.0)
693     : NCursesFieldType(TYPE_NUMERIC),
694       precision(prec), lower_limit(low), upper_limit(high) {
695   }
696 };
697
698 class NCURSES_IMPEXP Regular_Expression_Field : public NCursesFieldType {
699 private:
700   char* regex;
701
702   void set(NCursesFormField& f) {
703     OnError(::set_field_type(f.get_field(),fieldtype,regex));
704   }
705
706 public:
707   Regular_Expression_Field(const char *expr)
708     : NCursesFieldType(TYPE_REGEXP) {
709       regex = new char[1 + ::strlen(expr)];
710       (::strcpy)(regex,expr);
711   }
712
713   ~Regular_Expression_Field() {
714     delete[] regex;
715   }
716 };
717
718 class NCURSES_IMPEXP Enumeration_Field : public NCursesFieldType {
719 private:
720   const char** list;
721   int case_sensitive;
722   int non_unique_matches;
723
724   void set(NCursesFormField& f) {
725     OnError(::set_field_type(f.get_field(),fieldtype,
726                              list,case_sensitive,non_unique_matches));
727   }
728 public:
729   Enumeration_Field(const char* enums[],
730                     bool case_sens=FALSE,
731                     bool non_unique=FALSE)
732     : NCursesFieldType(TYPE_ENUM),
733       list(enums),
734       case_sensitive(case_sens?-1:0),
735       non_unique_matches(non_unique?-1:0) {
736   }
737 };
738
739 class NCURSES_IMPEXP IPV4_Address_Field : public NCursesFieldType {
740 private:
741   void set(NCursesFormField& f) {
742     OnError(::set_field_type(f.get_field(),fieldtype));
743   }
744
745 public:
746   IPV4_Address_Field() : NCursesFieldType(TYPE_IPV4) {
747   }
748 };
749 //
750 // -------------------------------------------------------------------------
751 // Abstract base class for User-Defined Fieldtypes
752 // -------------------------------------------------------------------------
753 //
754 class NCURSES_IMPEXP UserDefinedFieldType : public NCursesFieldType {
755   friend class UDF_Init; // Internal helper to set up statics
756 private:
757   // For all C++ defined fieldtypes we need only one generic lowlevel
758   // FIELDTYPE* element.
759   static FIELDTYPE* generic_fieldtype;
760
761 protected:
762   // This are the functions required by the low level libforms functions
763   // to construct a fieldtype.
764   static bool fcheck(FIELD *, const void*);
765   static bool ccheck(int c, const void *);
766   static void* makearg(va_list*);
767
768   void set(NCursesFormField& f) {
769     OnError(::set_field_type(f.get_field(),fieldtype,&f));
770   }
771
772 protected:
773   // Redefine this function to do a field validation. The argument
774   // is a reference to the field you should validate.
775   virtual bool field_check(NCursesFormField& f) = 0;
776
777   // Redefine this function to do a character validation. The argument
778   // is the character to be validated.
779   virtual bool char_check (int c) = 0;
780
781 public:
782   UserDefinedFieldType() : NCursesFieldType(generic_fieldtype) {
783   }
784 };
785 //
786 // -------------------------------------------------------------------------
787 // Abstract base class for User-Defined Fieldtypes with Choice functions
788 // -------------------------------------------------------------------------
789 //
790 class NCURSES_IMPEXP UserDefinedFieldType_With_Choice : public UserDefinedFieldType {
791   friend class UDF_Init; // Internal helper to set up statics
792 private:
793   // For all C++ defined fieldtypes with choice functions we need only one
794   // generic lowlevel FIELDTYPE* element.
795   static FIELDTYPE* generic_fieldtype_with_choice;
796
797   // This are the functions required by the low level libforms functions
798   // to construct a fieldtype with choice functions.
799   static bool next_choice(FIELD*, const void *);
800   static bool prev_choice(FIELD*, const void *);
801
802 protected:
803   // Redefine this function to do the retrieval of the next choice value.
804   // The argument is a reference to the field tobe examined.
805   virtual bool next    (NCursesFormField& f) = 0;
806
807   // Redefine this function to do the retrieval of the previous choice value.
808   // The argument is a reference to the field tobe examined.
809   virtual bool previous(NCursesFormField& f) = 0;
810
811 public:
812   UserDefinedFieldType_With_Choice() {
813     fieldtype = generic_fieldtype_with_choice;
814   }
815 };
816
817 #endif // NCURSES_CURSESF_H_incl
818