ncurses 5.6 - patch 20080907
[ncurses.git] / c++ / cursesf.cc
1 // * this is for making emacs happy: -*-Mode: C++;-*-
2 /****************************************************************************
3  * Copyright (c) 1998-2003,2005 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 #include "internal.h"
35 #include "cursesf.h"
36 #include "cursesapp.h"
37
38 MODULE_ID("$Id: cursesf.cc,v 1.21 2005/08/13 18:09:06 tom Exp $")
39
40 NCursesFormField::~NCursesFormField ()
41 {
42   if (field)
43     OnError(::free_field (field));
44 }
45
46 /* Construct a FIELD* array from an array of NCursesFormField
47  * objects.
48  */
49 FIELD**
50 NCursesForm::mapFields(NCursesFormField* nfields[])
51 {
52   int fieldCount = 0,lcv;
53   FIELD** old_fields;
54
55   assert(nfields != 0);
56
57   for (lcv=0; nfields[lcv]->field; ++lcv)
58     ++fieldCount;
59
60   FIELD** fields = new FIELD*[fieldCount + 1];
61
62   for (lcv=0;nfields[lcv]->field;++lcv) {
63     fields[lcv] = nfields[lcv]->field;
64   }
65   fields[lcv] = NULL;
66
67   my_fields = nfields;
68
69   if (form && (old_fields = ::form_fields(form))) {
70     ::set_form_fields(form, static_cast<FIELD**>(0));
71     delete[] old_fields;
72   }
73   return fields;
74 }
75
76 void NCursesForm::setDefaultAttributes()
77 {
78   NCursesApplication* S = NCursesApplication::getApplication();
79
80   int n = count();
81   if (n > 0) {
82     for(int i=0; i<n; i++) {
83       NCursesFormField* f = (*this)[i];
84       if ((f->options() & (O_EDIT|O_ACTIVE))==(O_EDIT|O_ACTIVE)) {
85         if (S) {
86           f->set_foreground(S->foregrounds());
87           f->set_background(S->backgrounds());
88         }
89         f->set_pad_character('_');
90       }
91       else {
92         if (S)
93           f->set_background(S->labels());
94       }
95     }
96   }
97
98   if (S) {
99     bkgd(' '|S->dialog_backgrounds());
100     if (sub)
101       sub->bkgd(' '|S->dialog_backgrounds());
102   }
103 }
104
105 void
106 NCursesForm::InitForm(NCursesFormField* nfields[],
107                       bool with_frame,
108                       bool autoDelete_Fields)
109 {
110   int mrows, mcols;
111
112   keypad(TRUE);
113   meta(TRUE);
114
115   b_framed = with_frame;
116   b_autoDelete = autoDelete_Fields;
117
118   form = static_cast<FORM*>(0);
119   form = ::new_form(mapFields(nfields));
120   if (!form)
121     OnError (E_SYSTEM_ERROR);
122
123   UserHook* hook = new UserHook;
124   hook->m_user   = NULL;
125   hook->m_back   = this;
126   hook->m_owner  = form;
127   ::set_form_userptr(form, reinterpret_cast<void*>(hook));
128
129   ::set_form_init  (form, _nc_xx_frm_init);
130   ::set_form_term  (form, _nc_xx_frm_term);
131   ::set_field_init (form, _nc_xx_fld_init);
132   ::set_field_term (form, _nc_xx_fld_term);
133
134   scale(mrows, mcols);
135   ::set_form_win(form, w);
136
137   if (with_frame) {
138     if ((mrows > height()-2) || (mcols > width()-2))
139       OnError(E_NO_ROOM);
140     sub = new NCursesWindow(*this,mrows,mcols,1,1,'r');
141     ::set_form_sub(form, sub->w);
142     b_sub_owner = TRUE;
143   }
144   else {
145     sub = static_cast<NCursesWindow*>(0);
146     b_sub_owner = FALSE;
147   }
148   options_on(O_NL_OVERLOAD);
149   setDefaultAttributes();
150 }
151
152 NCursesForm::~NCursesForm()
153 {
154   UserHook* hook = reinterpret_cast<UserHook*>(::form_userptr(form));
155   delete hook;
156   if (b_sub_owner) {
157     delete sub;
158     ::set_form_sub(form, static_cast<WINDOW *>(0));
159   }
160   if (form) {
161     FIELD** fields = ::form_fields(form);
162     int cnt = count();
163
164     OnError(::set_form_fields(form, static_cast<FIELD**>(0)));
165
166     if (b_autoDelete) {
167       if (cnt>0) {
168         for (int i=0; i <= cnt; i++)
169           delete my_fields[i];
170       }
171       delete[] my_fields;
172     }
173
174     ::free_form(form);
175     // It's essential to do this after free_form()
176     delete[] fields;
177   }
178 }
179
180 void
181 NCursesForm::setSubWindow(NCursesWindow& nsub)
182 {
183   if (!isDescendant(nsub))
184     OnError(E_SYSTEM_ERROR);
185   else {
186     if (b_sub_owner)
187       delete sub;
188     sub = &nsub;
189     ::set_form_sub(form,sub->w);
190   }
191 }
192
193 /* Internal hook functions. They will route the hook
194  * calls to virtual methods of the NCursesForm class,
195  * so in C++ providing a hook is done simply by
196  * implementing a virtual method in a derived class
197  */
198 void
199 _nc_xx_frm_init(FORM *f)
200 {
201   NCursesForm::getHook(f)->On_Form_Init();
202 }
203
204 void
205 _nc_xx_frm_term(FORM *f)
206 {
207   NCursesForm::getHook(f)->On_Form_Termination();
208 }
209
210 void
211 _nc_xx_fld_init(FORM *f)
212 {
213   NCursesForm* F = NCursesForm::getHook(f);
214   F->On_Field_Init (*(F->current_field ()));
215 }
216
217 void
218 _nc_xx_fld_term(FORM *f)
219 {
220   NCursesForm* F = NCursesForm::getHook(f);
221   F->On_Field_Termination (*(F->current_field ()));
222 }
223
224 void
225 NCursesForm::On_Form_Init()
226 {
227 }
228
229 void
230 NCursesForm::On_Form_Termination()
231 {
232 }
233
234 void
235 NCursesForm::On_Field_Init(NCursesFormField& field)
236 {
237 }
238
239 void
240 NCursesForm::On_Field_Termination(NCursesFormField& field)
241 {
242 }
243
244 // call the form driver and do basic error checking.
245 int
246 NCursesForm::driver (int c)
247 {
248   int res = ::form_driver (form, c);
249   switch (res) {
250   case E_OK:
251   case E_REQUEST_DENIED:
252   case E_INVALID_FIELD:
253   case E_UNKNOWN_COMMAND:
254     break;
255   default:
256     OnError (res);
257   }
258   return (res);
259 }
260
261 void NCursesForm::On_Request_Denied(int c) const
262 {
263   ::beep();
264 }
265
266 void NCursesForm::On_Invalid_Field(int c) const
267 {
268   ::beep();
269 }
270
271 void NCursesForm::On_Unknown_Command(int c) const
272 {
273   ::beep();
274 }
275
276 static const int CMD_QUIT = MAX_COMMAND + 1;
277
278 NCursesFormField*
279 NCursesForm::operator()(void)
280 {
281   int drvCmnd;
282   int err;
283   int c;
284
285   post();
286   show();
287   refresh();
288
289   while (((drvCmnd = virtualize((c=getKey()))) != CMD_QUIT)) {
290     switch((err=driver(drvCmnd))) {
291     case E_REQUEST_DENIED:
292       On_Request_Denied(c);
293       break;
294     case E_INVALID_FIELD:
295       On_Invalid_Field(c);
296       break;
297     case E_UNKNOWN_COMMAND:
298       On_Unknown_Command(c);
299       break;
300     case E_OK:
301       break;
302     default:
303       OnError(err);
304     }
305   }
306
307   unpost();
308   hide();
309   refresh();
310   return my_fields[::field_index (::current_field (form))];
311 }
312
313 // Provide a default key virtualization. Translate the keyboard
314 // code c into a form request code.
315 // The default implementation provides a hopefully straightforward
316 // mapping for the most common keystrokes and form requests.
317 int
318 NCursesForm::virtualize(int c)
319 {
320   switch(c) {
321
322   case KEY_HOME      : return(REQ_FIRST_FIELD);
323   case KEY_END       : return(REQ_LAST_FIELD);
324
325   case KEY_DOWN      : return(REQ_DOWN_CHAR);
326   case KEY_UP        : return(REQ_UP_CHAR);
327   case KEY_LEFT      : return(REQ_PREV_CHAR);
328   case KEY_RIGHT     : return(REQ_NEXT_CHAR);
329
330   case KEY_NPAGE     : return(REQ_NEXT_PAGE);
331   case KEY_PPAGE     : return(REQ_PREV_PAGE);
332
333   case KEY_BACKSPACE : return(REQ_DEL_PREV);
334   case KEY_ENTER     : return(REQ_NEW_LINE);
335   case KEY_CLEAR     : return(REQ_CLR_FIELD);
336
337   case CTRL('X')     : return(CMD_QUIT);        // eXit
338
339   case CTRL('F')     : return(REQ_NEXT_FIELD);  // Forward
340   case CTRL('B')     : return(REQ_PREV_FIELD);  // Backward
341   case CTRL('L')     : return(REQ_LEFT_FIELD);  // Left
342   case CTRL('R')     : return(REQ_RIGHT_FIELD); // Right
343   case CTRL('U')     : return(REQ_UP_FIELD);    // Up
344   case CTRL('D')     : return(REQ_DOWN_FIELD);  // Down
345
346   case CTRL('W')     : return(REQ_NEXT_WORD);
347   case CTRL('T')     : return(REQ_PREV_WORD);
348
349   case CTRL('A')     : return(REQ_BEG_FIELD);
350   case CTRL('E')     : return(REQ_END_FIELD);
351
352   case CTRL('I')     : return(REQ_INS_CHAR);
353   case CTRL('M')     :
354   case CTRL('J')     : return(REQ_NEW_LINE);
355   case CTRL('O')     : return(REQ_INS_LINE);
356   case CTRL('V')     : return(REQ_DEL_CHAR);
357   case CTRL('H')     : return(REQ_DEL_PREV);
358   case CTRL('Y')     : return(REQ_DEL_LINE);
359   case CTRL('G')     : return(REQ_DEL_WORD);
360   case CTRL('K')     : return(REQ_CLR_EOF);
361
362   case CTRL('N')     : return(REQ_NEXT_CHOICE);
363   case CTRL('P')     : return(REQ_PREV_CHOICE);
364
365   default:
366     return(c);
367   }
368 }
369 //
370 // -------------------------------------------------------------------------
371 // User Defined Fieldtypes
372 // -------------------------------------------------------------------------
373 //
374 bool _nc_xx_fld_fcheck(FIELD *f, const void *u)
375 {
376   NCursesFormField* F = reinterpret_cast<NCursesFormField*>(const_cast<void *>(u));
377   assert(F != 0);
378   UserDefinedFieldType* udf = reinterpret_cast<UserDefinedFieldType*>(F->fieldtype());
379   assert(udf != 0);
380   return udf->field_check(*F);
381 }
382
383 bool _nc_xx_fld_ccheck(int c, const void *u)
384 {
385   NCursesFormField* F = reinterpret_cast<NCursesFormField*>(const_cast<void *>(u));
386   assert(F != 0);
387   UserDefinedFieldType* udf =
388     reinterpret_cast<UserDefinedFieldType*>(F->fieldtype());
389   assert(udf != 0);
390   return udf->char_check(c);
391 }
392
393 void* _nc_xx_fld_makearg(va_list* va)
394 {
395   return va_arg(*va,NCursesFormField*);
396 }
397
398 FIELDTYPE* UserDefinedFieldType::generic_fieldtype =
399   ::new_fieldtype(_nc_xx_fld_fcheck,
400                   _nc_xx_fld_ccheck);
401
402 FIELDTYPE* UserDefinedFieldType_With_Choice::generic_fieldtype_with_choice =
403   ::new_fieldtype(_nc_xx_fld_fcheck,
404                   _nc_xx_fld_ccheck);
405
406 bool _nc_xx_next_choice(FIELD *f, const void *u)
407 {
408   NCursesFormField* F = reinterpret_cast<NCursesFormField*>(const_cast<void *>(u));
409   assert(F != 0);
410   UserDefinedFieldType_With_Choice* udf =
411     reinterpret_cast<UserDefinedFieldType_With_Choice*>(F->fieldtype());
412   assert(udf != 0);
413   return udf->next(*F);
414 }
415
416 bool _nc_xx_prev_choice(FIELD *f, const void *u)
417 {
418   NCursesFormField* F = reinterpret_cast<NCursesFormField*>(const_cast<void *>(u));
419   assert(F != 0);
420   UserDefinedFieldType_With_Choice* udf =
421     reinterpret_cast<UserDefinedFieldType_With_Choice*>(F->fieldtype());
422   assert(udf != 0);
423   return udf->previous(*F);
424 }
425
426 class UDF_Init
427 {
428 private:
429   int code;
430   static UDF_Init* I;
431
432 public:
433   UDF_Init()
434     : code(0)
435   {
436     code = ::set_fieldtype_arg(UserDefinedFieldType::generic_fieldtype,
437                                _nc_xx_fld_makearg,
438                                NULL,
439                                NULL);
440     if (code==E_OK)
441       code = ::set_fieldtype_arg
442         (UserDefinedFieldType_With_Choice::generic_fieldtype_with_choice,
443          _nc_xx_fld_makearg,
444          NULL,
445          NULL);
446     if (code==E_OK)
447       code = ::set_fieldtype_choice
448         (UserDefinedFieldType_With_Choice::generic_fieldtype_with_choice,
449          _nc_xx_next_choice,
450          _nc_xx_prev_choice);
451   }
452 };
453
454 UDF_Init* UDF_Init::I = new UDF_Init();