/* * This is a curses forms wrapper as part of ncurses-ruby * Contributed by Simon Kaczor * Prognosoft Inc. * Copyright 2004 * * Changes: * (C) 2004 Tobias Peters * (C) 2005 Tobias Herzke * * This module is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This module is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this module; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #ifdef HAVE_FORM_H #include "form_wrap.h" #include "ncurses_wrap.h" VALUE mForm; VALUE cFIELD; VALUE cFIELDTYPE; VALUE cFORM; void init_err_codes() { /* The routine succeeded. */ FORM_DEF_CONST(E_OK); /* The field is already connected to a form. */ FORM_DEF_CONST(E_CONNECTED); /* System error occurred (see errno). */ FORM_DEF_CONST(E_SYSTEM_ERROR); /* Routine detected an incorrect or out-of-range argument. */ FORM_DEF_CONST(E_BAD_ARGUMENT); /* The form is already posted. */ FORM_DEF_CONST(E_POSTED); /* Routine was called from an initialization or termination function. */ FORM_DEF_CONST(E_BAD_STATE); /* Form is too large for its window. */ FORM_DEF_CONST(E_NO_ROOM); /* The form has not been posted. */ FORM_DEF_CONST(E_NOT_POSTED); /* The form driver code saw an unknown request code. */ FORM_DEF_CONST(E_UNKNOWN_COMMAND); /* Contents of a field are not valid. */ FORM_DEF_CONST(E_INVALID_FIELD); /* No fields are connected to the form. */ FORM_DEF_CONST(E_NOT_CONNECTED); /* The form driver could not process the request.} */ FORM_DEF_CONST(E_REQUEST_DENIED); } /* * Form driver request characters listed in form_driver(3x) man page */ void init_req_constants() { /* Move to the next page */ FORM_DEF_CONST(REQ_NEXT_PAGE); /* Move to the previous page. */ FORM_DEF_CONST(REQ_PREV_PAGE); /* Move to the first page. */ FORM_DEF_CONST(REQ_FIRST_PAGE); /* Move to the last field. */ FORM_DEF_CONST(REQ_LAST_PAGE); /* Move to the next field. */ FORM_DEF_CONST(REQ_NEXT_FIELD); /* Move to the previous field. */ FORM_DEF_CONST(REQ_PREV_FIELD); /* Move to the first field. */ FORM_DEF_CONST(REQ_FIRST_FIELD); /* Move to the last field. */ FORM_DEF_CONST(REQ_LAST_FIELD); /* Move to the sorted next field. */ FORM_DEF_CONST(REQ_SNEXT_FIELD); /* Move to the sorted previous field. */ FORM_DEF_CONST(REQ_SPREV_FIELD); /* Move to the sorted first field. */ FORM_DEF_CONST(REQ_SFIRST_FIELD); /* Move to the sorted last field. */ FORM_DEF_CONST(REQ_SLAST_FIELD); /* Move left to a field. */ FORM_DEF_CONST(REQ_LEFT_FIELD); /* Move right to a field. */ FORM_DEF_CONST(REQ_RIGHT_FIELD); /* Move up to a field. */ FORM_DEF_CONST(REQ_UP_FIELD); /* Move down to a field. */ FORM_DEF_CONST(REQ_DOWN_FIELD); /* Move to the next char. */ FORM_DEF_CONST(REQ_NEXT_CHAR); /* Move to the previous char. */ FORM_DEF_CONST(REQ_PREV_CHAR); /* Move to the next line. */ FORM_DEF_CONST(REQ_NEXT_LINE); /* Move to the previous line. */ FORM_DEF_CONST(REQ_PREV_LINE); /* Move to the next word. */ FORM_DEF_CONST(REQ_NEXT_WORD); /* Move to the previous word. */ FORM_DEF_CONST(REQ_PREV_WORD); /* Move to the beginning of the field. */ FORM_DEF_CONST(REQ_BEG_FIELD); /* Move to the end of the field. */ FORM_DEF_CONST(REQ_END_FIELD); /* Move to the beginning of the line. */ FORM_DEF_CONST(REQ_BEG_LINE); /* Move to the end of the line. */ FORM_DEF_CONST(REQ_END_LINE); /* Move left in the field. */ FORM_DEF_CONST(REQ_LEFT_CHAR); /* Move right in the field. */ FORM_DEF_CONST(REQ_RIGHT_CHAR); /* Move up in the field. */ FORM_DEF_CONST(REQ_UP_CHAR); /* Move down in the field. */ FORM_DEF_CONST(REQ_DOWN_CHAR); /* Insert or overlay a new line. */ FORM_DEF_CONST(REQ_NEW_LINE); /* Insert a blank at the cursor. */ FORM_DEF_CONST(REQ_INS_CHAR); /* Insert a blank line at the cursor. */ FORM_DEF_CONST(REQ_INS_LINE); /* Delete character at the cursor. */ FORM_DEF_CONST(REQ_DEL_CHAR); /* Delete character before the cursor. */ FORM_DEF_CONST(REQ_DEL_PREV); /* Delete line at the cursor. */ FORM_DEF_CONST(REQ_DEL_LINE); /* Delete blank-delimited word at the cursor. */ FORM_DEF_CONST(REQ_DEL_WORD); /* Clear to end of line from cursor. */ FORM_DEF_CONST(REQ_CLR_EOL); /* Clear to end of field from cursor. */ FORM_DEF_CONST(REQ_CLR_EOF); /* Clear the entire field. */ FORM_DEF_CONST(REQ_CLR_FIELD); /* Enter overlay mode. */ FORM_DEF_CONST(REQ_OVL_MODE); /* Enter insert mode. */ FORM_DEF_CONST(REQ_INS_MODE); /* Scroll the field forward a line. */ FORM_DEF_CONST(REQ_SCR_FLINE); /* Scroll the field backward a line. */ FORM_DEF_CONST(REQ_SCR_BLINE); /* Scroll the field forward a page. */ FORM_DEF_CONST(REQ_SCR_FPAGE); /* Scroll the field backward a page. */ FORM_DEF_CONST(REQ_SCR_BPAGE); /* Scroll the field forward half a page. */ FORM_DEF_CONST(REQ_SCR_FHPAGE); /* Scroll the field backward half a page. */ FORM_DEF_CONST(REQ_SCR_BHPAGE); /* Scroll the field forward a character. */ FORM_DEF_CONST(REQ_SCR_FCHAR); /* Scroll the field backward a character. */ FORM_DEF_CONST(REQ_SCR_BCHAR); /* Horizontal scroll the field forward a line. */ FORM_DEF_CONST(REQ_SCR_HFLINE); /* Horizontal scroll the field backward a line. */ FORM_DEF_CONST(REQ_SCR_HBLINE); /* Horizontal scroll the field forward half a line. */ FORM_DEF_CONST(REQ_SCR_HFHALF); /* Horizontal scroll the field backward half a line. */ FORM_DEF_CONST(REQ_SCR_HBHALF); /* Validate field. */ FORM_DEF_CONST(REQ_VALIDATION); /* Display next field choice. */ FORM_DEF_CONST(REQ_NEXT_CHOICE); /* Display previous field choice. */ FORM_DEF_CONST(REQ_PREV_CHOICE); } /* * field justification constants */ void init_just_constants() { FORM_DEF_CONST(NO_JUSTIFICATION); FORM_DEF_CONST(JUSTIFY_RIGHT); FORM_DEF_CONST(JUSTIFY_LEFT); FORM_DEF_CONST(JUSTIFY_CENTER); } /* * field options constants */ void init_opts_constants() { FORM_DEF_CONST(O_VISIBLE); FORM_DEF_CONST(O_ACTIVE); FORM_DEF_CONST(O_PUBLIC); FORM_DEF_CONST(O_EDIT); FORM_DEF_CONST(O_WRAP); FORM_DEF_CONST(O_BLANK); FORM_DEF_CONST(O_AUTOSKIP); FORM_DEF_CONST(O_NULLOK); FORM_DEF_CONST(O_STATIC); FORM_DEF_CONST(O_PASSOK); } void init_form_opts_constants() { FORM_DEF_CONST(O_NL_OVERLOAD); FORM_DEF_CONST(O_BS_OVERLOAD); } /* * _PAGE wrapper */ /* static VALUE wrap_page(_PAGE* page) */ /* { */ /* if (page == 0) return Qnil; */ /* { */ /* VALUE pages_hash = rb_iv_get(mForm, "@pages_hash"); */ /* VALUE page_adress = INT2NUM((long)(page)); */ /* VALUE rb_page = rb_hash_aref(pages_hash, page_adress); */ /* if (rb_page == Qnil) { */ /* rb_page = Data_Wrap_Struct(cPAGE, 0, 0, page); */ /* rb_iv_set(rb_page, "@destroyed", Qfalse); */ /* rb_hash_aset(pages_hash, page_adress, rb_page); */ /* } */ /* return rb_page; */ /* } */ /* } */ /* static _PAGE* get_page(VALUE rb_page) */ /* { */ /* _PAGE* page; */ /* if (rb_page == Qnil) return 0; */ /* if (rb_iv_get(rb_page, "@destroyed") == Qtrue) { */ /* rb_raise(rb_eRuntimeError, "Attempt to access a destroyed page"); */ /* return 0; */ /* } */ /* Data_Get_Struct(rb_page, _PAGE, page); */ /* return page; */ /* } */ /* * FIELD wrapper */ static VALUE wrap_field(FIELD* field) { if (field == 0) return Qnil; { VALUE fields_hash = rb_iv_get(mForm, "@fields_hash"); VALUE field_adress = INT2NUM((long)(field)); VALUE rb_field = rb_hash_aref(fields_hash, field_adress); if (rb_field == Qnil) { rb_field = Data_Wrap_Struct(cFIELD, 0, 0, field); rb_iv_set(rb_field, "@destroyed", Qfalse); rb_hash_aset(fields_hash, field_adress, rb_field); } return rb_field; } } static FIELD* get_field(VALUE rb_field) { FIELD* field; if (rb_field == Qnil) return 0; if (rb_iv_get(rb_field, "@destroyed") == Qtrue) { rb_raise(rb_eRuntimeError, "Attempt to access a destroyed field"); return 0; } Data_Get_Struct(rb_field, FIELD, field); return field; } /* * FIELDTYPE wrapper */ static VALUE wrap_fieldtype(FIELDTYPE* fieldtype) { if (fieldtype == NULL) return Qnil; { VALUE fieldtypes_hash = rb_iv_get(mForm, "@fieldtypes_hash"); VALUE fieldtype_adress = INT2NUM((long)(fieldtype)); VALUE rb_fieldtype = rb_hash_aref(fieldtypes_hash, fieldtype_adress); if (rb_fieldtype == Qnil) { rb_fieldtype = Data_Wrap_Struct(cFIELDTYPE, 0, 0, fieldtype); rb_iv_set(rb_fieldtype, "@destroyed", Qfalse); rb_hash_aset(fieldtypes_hash, fieldtype_adress, rb_fieldtype); } return rb_fieldtype; } } static FIELDTYPE* get_fieldtype(VALUE rb_fieldtype) { FIELDTYPE* fieldtype; if (rb_fieldtype == Qnil) return 0; if (rb_iv_get(rb_fieldtype, "@destroyed") == Qtrue) { rb_raise(rb_eRuntimeError, "Attempt to access a destroyed fieldtype"); return 0; } Data_Get_Struct(rb_fieldtype, FIELDTYPE, fieldtype); return fieldtype; } /* * FORM wrapper */ static VALUE wrap_form(FORM* form) { if (form == 0) return Qnil; { VALUE forms_hash = rb_iv_get(mForm, "@forms_hash"); VALUE form_adress = INT2NUM((long)(form)); VALUE rb_form = rb_hash_aref(forms_hash, form_adress); if (rb_form == Qnil) { rb_form = Data_Wrap_Struct(cFORM, 0, 0, form); rb_iv_set(rb_form, "@destroyed", Qfalse); rb_hash_aset(forms_hash, form_adress, rb_form); } return rb_form; } } static FORM* get_form(VALUE rb_form) { FORM* form; if (rb_form == Qnil) return 0; if (rb_iv_get(rb_form, "@destroyed") == Qtrue) { rb_raise(rb_eRuntimeError, "Attempt to access a destroyed form"); return 0; } Data_Get_Struct(rb_form, FORM, form); return form; } /* * Proc objects are registered using hashes (one for each type of hook) * The key in the hash is the address of the ncurses "object" and the value is * the Proc object. */ #define FIELD_INIT_HOOK 0 #define FIELD_TERM_HOOK 1 #define FORM_INIT_HOOK 2 #define FORM_TERM_HOOK 3 #define FIELDTYPE_FIELD_CHECK_HOOK 4 #define FIELDTYPE_CHAR_CHECK_HOOK 5 #define FIELDTYPE_NEXT_CHOICE_HOOK 6 #define FIELDTYPE_PREV_CHOICE_HOOK 7 #define FIELDTYPE_ARGS 8 #define PROC_HASHES_COUNT 9 static VALUE get_proc_hash(int hook) { VALUE arr = rb_iv_get(mForm, "@proc_hashes"); VALUE hash = rb_ary_entry(arr, (long)hook); if (hash == Qnil) { rb_raise(rb_eRuntimeError, "Invalid proc hash."); } return hash; } /* * Returns an existing Ruby Proc for a given owning "object" and hook type. * Qnil will be returned if no Proc was associated with the owner */ static VALUE get_proc(void* owner, int hook) { if (owner == 0) return Qnil; { VALUE owner_adress = INT2NUM((long)(owner)); VALUE proc_hash = get_proc_hash(hook); VALUE proc = rb_hash_aref(proc_hash, owner_adress); return proc; } } /* * Registers the Proc object with a given owner "object" and hook type. * If proc is Qnil, the hook is unregistered instead. */ static void reg_proc(void* owner, int hook, VALUE proc) { if (owner == NULL) return; { VALUE proc_hash = get_proc_hash(hook); VALUE owner_address = INT2NUM((long)(owner)); if (proc == Qnil) { rb_hash_delete(proc_hash, owner_address); } else { rb_hash_aset(proc_hash, owner_address, proc); } } } /* * Form creation/destruction functions */ static VALUE rbncurs_m_new_form(VALUE dummy, VALUE rb_field_array) { long n = RARRAY_LEN(rb_field_array); /* Will ncurses free this array? If not, must do it after calling free_form(). */ FIELD** fields = ALLOC_N(FIELD*, (n+1)); long i; for (i=0; i