Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C Programming


OCT88: C PROGRAMMING

C PROGRAMMING

Screen Control, Programming as Art (?), and C ++

Al Stevens

Last month we began a running "C Column" programming project with a general-purpose video window library for the IBM PC and compatible computers. This issue continues that project with a set of window-oriented software tools using the window functions from last month. So far we haven't made the program do anything; that comes later. The final program will be a communications service driver to be used by subscribers to on-line services.

This month we add a window menu facility and a data entry screen driver. These tools are general enough to use in other projects while you wait for the communications application for which they are intended. The tools are written in C and were compiled with Turbo C, Version 1.5.

A Window Menu

The program that we are building uses a sliding bar menu at the top of the screen with a number of selections displayed horizontally across the bar. A selection can have an associated pop-down menu. To build this capability, we will use a general-purpose menu function driven by tables of structures. The structures are defined outside of the menu functions and describe the menu displays and software that each menu selection executes. Later tools such as the text editor, will employ these menus. So will the application communications program.

Listing One, page 128, is menu.h. This file will be included in programs that must describe and execute menus by using the menu functions. You describe a menu by declaring and initializing an array of the structures that are named by the typedef MENU. Each element in the array describes a selection on the sliding bar menu at the top of the screen. The members of the structure describe the selection. Here is a description of each member:

mname: A pointer to the selection's name as it will be displayed in the sliding bar.

mhlpmsg: A pointer to a longer string that will be displayed on the bottom line of the screen when the selection is highlighted. This string is used to amplify the meaning of the selection by describing it to the user.

mselcs: A pointer to an array of character pointers, each of which points to the text of a selection on the pop-down menu associated with the sliding bar menu. This array must be terminated with a NULL pointer. If a NULL pointer is in the first position, no pop-down menu will appear, and the siding bar menu selection itself will execute a single command.

mshelp: An array of character pointers, each of which points to a help window mnemonic for the associated pop-down menu selection. These will be explained later when we install context-sensitive help windows.

mskeys: A pointer to a character array that contains keystrokes that will execute the pop-down commands. This feature allows a menu to be executed in one of two ways--the positioning of the menu cursor on the selection and the press of the Enter key, or the press of a designated selection key as specified in the mskeys array.

func: An array of function pointers that point to the functions that will be executed by the associated pop-down menu selections.

lastvsel: Used by the menu manager to remember the most recent vertical selection; should be initialized to a zero value.

With an array of MENU structures thus initialized, you call the menu __select function, passing it the address of the structure and an integer that says which of the horizontal selections should be highlighted when the menu is first displayed. Usually the integer has the value 1.

Listing Two, page 128, is menu.c, which contains the library functions that display the menus, get the user's selection, and execute the appropriate functions.

Listing Three, on page 129, is testmenu.c, an example of a simple menu program that shows how the menu software can be used. The example is an abstract of the menu that will be used from within the text editor part of the communications software package. The example doesn't do anything. It just illustrates how to use the menu functions. Compile and link it with the menu and window functions and it will display and navigate the menus. Figure 1, page 111, shows the menus the way they are first displayed. The arrow keys select the pop-down menus and move the cursor bars. The Enter key selects a menu item. The options menu shows an example of how you can use these menus to turn operating modes (toggles) on and off.

Compile and link testmenu.c with menu.c in Listing Two and window.c from last month's C Column. You will need Listing One, menu.h, and window.h from last month.

You can build nested layers of these menus by calling menu_select from within a function that was executed from a higher-level menu. In fact, the editor menu we've simulated in this example will be called from within an editor function that is called from a shell menu.

The functions that are called by the menu manager will receive two integers as parameters. These integers are the horizontal selection from the sliding menu bar (1, 2,.., n) and the vertical selection from the pop-down menu (1, 2,.., n). These values allow a function to determine which menu selection executed it. Each function executed from a menu must return an integer true or false value. If a false value is returned, the menu manager retains control at the selection where the function was called. If a true value is returned, the menu manager returns to the caller of the menu_select function. Although the menu selections in the example don't do anything, you can see this feature work in the Quit selection on the File menu. its function returns a true value, and the menu manager returns. The other functions return false, and the user remains at the menu that executed the function.

Data Entry Screens

Listing Four, page 130, is entry.h and Listing Five, on page 130, is entry.c. These two files use the window library to implement a general-purpose data entry screen. Here's how it works: You establish a window and build an array of data entry field definitions. You write some prompting information into the window and call the data entry software, passing it the address of the field definition array. The data entry software takes over and collects the user's key entries into your buffers. All the good stuff about jumping around from field to field and help windows and such is managed by the data entry library.

The structure in entry.h named FIELD is used to describe data entry fields. You build an array of these structures terminated by a zero value structure. Here is an explanation of each of the members in the structure:

frow: The row number where the field will be displayed in the current window.

Figure 1: Test menu (from testmenu.c)

fcol: The column number of the field. (Note: Rows and columns are relative to one. The upper-leftmost position m a window is row 1, column 1.)

fx: A running column number used by the data-entry software. This value should be initialized to 1.

fbuff: A pointer to the buffer where the data value will be stored when the user keys it in.

fmask: A pointer to a field mask. Masks are strings of underlines and other characters. The underlines correspond to field data character positions. The other characters are displayed with the field to make it more readable. For example, the mask for a telephone number might be as follows: "( ) ______-______"

fhelp: A pointer to the field help window mnemonic, to be explained another time. For now make it NULL.

Listing Six, on page 134 is testentr.c, XXa program that illustrates the use of the data entry screens. It establishes a window with a title and writes some prompting messages into the window. Then it calls the data entry function to collect data values into its buffer. The parameters are the address of the array of FIELD structures, a TRUE value to tell the function to initialize the buffers to null-terminated strings of spaces, and the integer value 1 to tell the function to start with the field cursor on the first field in the array.

Figure 2, page 113, shows the screen that is displayed by the example after some data values have been keyed. To use the screen (which does nothing except collect data entry into a buffer( you simply type in some data values. The Ins key toggles the insert/overwrite mode. The arrow keys move the cursor. Ctrl-right or left arrows skip over words. Home goes to the beginning of a field and End goes to the end. Backspace deletes a character to the left, and Del deletes the character under the cursor. Any of the function keys or the Esc key will terminate the data entry and return the terminating key value to the caller of the data entry function.

Compile and link testentr.c with entry.c in Listing Five and window.c from last month's C Column. You will need Listing Four, entry.h, and window.h from last month.

You can keep testmenu.c and testentr.c as examples and to test other menus and data entry screens that you might design. They will not, however, be a part of the C Column programming project other than as examples. The other source files are keepers.

Next month we will add the window text editor and the context-sensitive help window library. The menu and data entry definition structures include members that point to help window mnemonics. So far, we have not used them. Later we will put help window names (string pointers) in these members and help window text into a file, and the help function will become automatic. Press the help key while the cursor is on a menu selection or a data entry field and a context-sensitive help window will pop up.

Crotchet Number 5: The Great Debate

I will now remind you that I use this column to vent my opinions on matters that are vexing. These issues are called crotchets, and they usually relate to C, although this month's crotchet spans the practice of programming without regard to any particular language.

Is programming an art or a science? In "Programming Paradigms," (DDJ, June 1988) Michael Swaine quotes Chuck Moore who said, "Programming is an art; we might hope it becomes a craft; it will never be a science." Later Michael dismisses programming as an art and calls it a discipline. This debate has been getting knocked around for years and I have always kept. my mouth shut about it. No more--here's my nickel's worth for this month's crotchet.

Figure 2: A test data entry screen (from testentr.c)

               Personal Data
Name:             Cuthbert J. Twilly
Account #:        1234-567898
Password:         intheflesh
Phone:            (407) 555-1212 ext.0414

Is programming an art? Webster's stresses the human side of art, assigning its first definition to the "skill in performance acquired by experience, study, or observation." Note that the activity itself is not so much art as is the skill required for the practice of the activity. The next definition deals with branches of learning such as liberal and medical arts. Next comes knowledge, and then comes the creative production of aesthetic objects. Only in this last definition does the creative activity itself take part.

A computer program is the product of creative activity, but is it an aesthetic object? That depends, I suppose, on the audience. To me a work of art is something of beauty created by a skilled person to be enjoyed by the rest of us, skilled and unskilled alike. It is meant to be appreciated by the masses and is not just for kindred artists. Programs are rarely read just for the beauty of their expression and then only by other skilled programmers. Accept this notion and you must conclude that the audience for program appreciation is just too small to qualify most programs as aesthetic objects.

Is programming a science? Are programmers scientists? Scientists wear smocks, work in laboratories, and do research. They search for answers to questions and use their skills in a systematic quest for truth. They go to school for a long time to learn how to do that. Don't they? We programmers don't do that.

Is programming a discipline? Not the way I do it.

Opinions follow.

The discoveries of new formulae for concrete and paint are sciences. Architecture and portraiture are arts. Carpentry and house painting are crafts. Girder walking is a discipline.

Discovery of a new sort algorithm is science. Its publication is art. Its use is craft. Its design methodologies are disciplines, always discarded when deadlines draw near.

Perhaps many people who are not artistically gifted wish they were and thus call what they do art. My doctor does that, but there is a scar on my stomach above where my gall bladder used to be that defies aesthetic appreciation. Likewise, we call ourselves scientists to elevate our importance several levels. If, however, we allow ourselves to be classed with house painters and drywall hangers, we tend to think we've lost caste and given up the mystique that accrues to special people. Not so.

So now you know. Programmers are craftsmen and good ol' boys (and girls, too). Pass me another Pabst Blue Ribbon.

C+ +

Every ten years or so another new method of programming comes along and we are told that this new wave will swamp the old ways and we had better get on the surf board or be left behind, sob and unemployed. In the early sixties we were dragged kicking and screaming into Cobol and Fortran, all the time clutching desperately to that last beloved Autocoder listing. How we clung to those word marks, address registers, and index registers, believing that without them, programming would be nothing short of impossible. Just as our kicks atrophied and our screams faded and we succumbed, they wrested away our go to, invented the while, and shoved something called structured programming in our faces. Eventually, we came to love it--who wouldn't--but those changes were not easy to swallow because they required us to think of code structures in ways foreign to what we had come to practice as second nature.

Today we are told that the next tidal wave is "object-oriented programming," and I am trying to figure it out. The problem is whenever I read an explanation of what it means, the examples aren't real. Nowhere have I seen concrete examples of what objects are supposed to be. Good examples might be out there but I haven't found them yet. I can understand the given examples for what they are, but I can't yet transfer that understanding into what I design. Now, as I develop a new program, I ask myself which of the data aggregates should be objects and which should not and why. I ask but I do not answer.

We had better get this straight because something tells me that before long everyone else is going to be coding object-oriented languages and we are going to be left out. I was reluctant to admit this weakness in character and knowledge to a devoted readership until reading in Michael Swaine's column that the experts don't understand it either. Allow me to admit that my research has been restricted to magazine articles (going back several years to a Byte magazine feature issue on Small-talk) and some product literature. If I work this enigma out, the C Column will try to explain to the rest of us what a few seem to already know. If this thing is truly the answer to a maiden's prayer, it sure could use a plain English explanation.

You might wonder why this column Is the forum for such a wade in the dark through a murky new subject. I should be writing about things I already know about, right? Well, maybe a lot of you are facing the same confusion, and maybe a few of you are farther along. Perhaps the confused will benefit from this tentative entry into the unknown. Perhaps the aware will help.

Soon I will get a copy of Bjarne Stroustrup's book The C++ Programming Language, the definitive work on C++. C++ is a superset dialect of C that includes the constructs of object-oriented programming. You can define new classes of data in a manner similar to giving a typedef to a structure, but then you can bind functions to the new classes. Instances of a data class are called objects. New classes are derived from and inherit the attributes of old classes when you include the old class inside the new class. This feature is similar to the standard C ability to have one structure as a member of a higher structure.

The neat thing about it is that you can define operators that work with the objects. For example, if you need to add and subtract objects, you define that operator and provide the function that performs the operation. I'm not sure how much this feature is different from or better than the standard C facility for passing the addresses of two structures to a function. This faint explanation would be clearer if I could tell you what an obvious object was.

One feature of C ++ is going to cause some trouble. C ++ allows you to give the same name to several functions within a class. The compiler determines which one you want to call by matching the classes of the parameters in each call of the common name with the function prototypes. On the surface this seems to fly in the face of the strong typing claimed for C ++, but then again I don't fully understand it yet.

Until recently C ++ was implemented as a preprocessor that compiled into C language, which could then be compiled into executable code. This approach is how RATFOR was preprocessed into Fortran. Now a native C ++ compiler for MS-DOS has been released. It is called the Zortech C ++ Compiler and it is an offshoot of the recently disappearing Datalight Optimum C Compiler. The Datalight compiler was a respectable product that outperformed the competition with its speedy compile times until it was swept under by Turbo C and, later, QuickC.

Now, the talent that went into the Datalight development has been directed to the C ++ world. I have a copy of the first release of the Zortech C ++ Compiler, and I plan to use it to learn C ++ and object-oriented programming if I can ever figure out what an object is. The Zortech manual has examples. Objects are toasters into which you can insert bread, set the darkness, and test for popped up toast. See what I mean? Sheesh! Another crotchet in the making. As I proceed with this quest for truth (computer science?( I will post my progress in the C Column. Write if you see an object that I might recognize.

_C PROGRAMMING_ by Al Stevens

[LISTING ONE]

<a name="01d4_000a">


/* ----------- menu.h ---------- */

typedef struct w_menu {
    char *mname;            /* menu bar selection names                     */
    char *mhlpmsg;          /* menu bar prompting messages                  */
    char **mselcs;          /* the pop-down menu selections                 */
    char **mshelp;          /* help mnemonics for the pop-down selections   */
    char *mskeys;           /* key strokes that accompany the selections    */
    int (**func)(int,int);  /* the functions to execute for the selections  */
    int lastvsel;           /* most recent vertical selection               */
} MENU;

void menu_select(MENU *, int);
char *display_menubar(MENU *);
void restore_menubar(char *);


<a name="01d4_000b"><a name="01d4_000b">
<a name="01d4_000c">
[LISTING TWO]
<a name="01d4_000c">

/* ------------ menu.c ------------ */

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "window.h"
#include "menu.h"

#define ON 1
#define OFF 0

static int getvmn(void);
static void haccent(int);
static void dimension(char **,int *,int *);
static void light(int);
static int vlook(int,int);

int hsel = 1;        /* horizontal selection */
MENU *mn;            /* active menu          */

/* ------------- display & process a menu ----------- */
void menu_select(MENU *mnn, int sel)
{
    int hs, sx, sy, vsel, holdhsel, frtn = FALSE;
    char *mb;

    if (sel)
        hsel = sel;
    mn = mnn;
    sx = wherex();
    sy = wherey();
    mb = display_menubar(mn);
    light(ON);
    while (!frtn && ((vsel = getvmn()) != 0))    {
        light(OFF);
        holdhsel = hsel;
        hs = hsel;
        hsel = 1;
        frtn = (*(mn+hs-1)->func [vsel-1]) ?
               (*(mn+hs-1)->func [vsel-1])(hs,vsel) : FALSE;
        hsel = holdhsel;
        mn = mnn;
        light(ON);
    }
    light(OFF);
    gotoxy(sx, sy);
    restore_menubar(mb);
}

/* --- display the menu bar with no selections chosen ---- */
char *display_menubar(MENU *mn)
{
    int i = 0;
    char *mb;

    if ((mb = malloc(160)) != NULL)
        gettext(1,1,80,1,mb);
    window(1,1,80,25);
    gotoxy(1,1);
    textcolor(MENUFG);
    textbackground(MENUBG);
    cprintf("    ");
    while ((mn+i)->mname)
        cprintf(" %-10.10s ", (mn+i++)->mname);
    while (i++ < 6)
        cprintf("            ");
    cprintf("    ");
    hidecursor();
    return mb;
}

/* ------------ restore the menu bar line --------------- */
void restore_menubar(char *mb)
{
    if (mb)    {
        puttext(1,1,80,1,mb);
        free(mb);
    }
}

/* ---------pop down a vertical menu --------- */
static int getvmn()
{
    int ht, wd, vx, sel;

    while (TRUE)    {
        dimension((mn+hsel-1)->mselcs, &ht, &wd);
        if (!(mn+hsel-1)->lastvsel)
            (mn+hsel-1)->lastvsel = 1;
        if (ht > 0)    {
            vx = 5+(hsel-1)*12;
            establish_window(vx, 2, vx+wd+1, ht+3,
                MENUFG, MENUBG,TRUE);
            text_window((mn+hsel-1)->mselcs, 1);
            sel = select_window((mn+hsel-1)->lastvsel,
                SELECTFG, SELECTBG, vlook);
            delete_window();
            if (sel == FWD || sel == BS)
                haccent(sel);
            else
                return ((mn+hsel-1)->lastvsel = sel);
        }
        else    {
            if ((sel = getkey()) == *(mn+hsel-1)->mskeys)
                return 1;
            switch (sel)    {
                case FWD:
                case BS:    haccent(sel);
                            break;
                case '\r':    return 1;
                case ESC:    return 0;
                default:    putch(BELL);
                            break;
            }
        }
    }
}

/* ---- if vertical menu user types FWD, BS,
               or a menu key, return it ---- */
static int vlook(ch, sel)
{
    char *cs, *ks;

    if (ch == FWD || ch == BS)    {
        (mn+hsel-1)->lastvsel = sel;
        return ch;
    }
    ks = (mn+hsel-1)->mskeys;
    if ((cs = memchr(ks, tolower(ch), strlen(ks))) == NULL)
        return ERROR;
    return ((mn+hsel-1)->lastvsel = cs-ks+1);
}

/* ----- manage the horizontal menu selection accent ----- */
static void haccent(int sel)
{
    switch (sel)    {
        case FWD:
            light(OFF);
            if ((mn+hsel)->mname)
                hsel++;
            else
                hsel = 1;
            light(ON);
            break;
        case BS:
            light(OFF);
            if (hsel == 1)
                while ((mn+hsel)->mname)
                    hsel++;
            else
                --hsel;
            light(ON);
            break;
        default:
            break;
    }
}

/* ---------- compute a menu's height & width --------- */
static void dimension(char *sl[], int *ht, int *wd)
{
    *ht = *wd = 0;

    while (sl && sl [*ht])    {
        *wd = max(*wd, (unsigned) strlen(sl [*ht]));
        (*ht)++;
    }
}

/* --------- accent a horizontal menu selection ---------- */
static void light(int onoff)
{
    extern struct wn wkw;
    extern char spaces[];
    int ln;

    window(1,1,80,25);
     textcolor(onoff ? SELECTFG : MENUFG);
     textbackground(onoff ? SELECTBG : MENUBG);
     gotoxy((hsel-1)*12+6, 1);
    cprintf((mn+hsel-1)->mname);
    textcolor(TEXTFG);
    textbackground(TEXTBG);
    if ((mn+hsel-1)->mhlpmsg)    {
        if (onoff)    {
            ln = strlen((mn+hsel-1)->mhlpmsg);
            gotoxy((80-ln)/2,25);
            cprintf((mn+hsel-1)->mhlpmsg);
        }
        else    {
            gotoxy(1,25);
            cprintf(spaces);
        }
    }
    current_window();
    hidecursor();
}




<a name="01d4_000d"><a name="01d4_000d">
<a name="01d4_000e">
[LISTING THREE]
<a name="01d4_000e">


/* --------- testmenu.c ------------ */
#include <stdio.h>
#include <string.h>
#include "window.h"
#include "menu.h"

/* ---------- menu tables --------- */
static char *fselcs[] = {
    "Load",
    "Save",
    "New",
    "Quit   [Esc]",
    NULL
};

static char *eselcs[] = {
    "Move       [F3]",
    "Copy       [F4]",
    "Delete     [F8]",
    "Find       [F7]",
    NULL
};

static char *oselcs[] = {
    "Auto Paragraph Reformat       ",
    "Insert  [Ins]                 ",
    NULL
};

static int quit(int,int);
static int reform(int, int);
static int insert(int, int);
static void set_toggles(void);

static char forced[] = {F3,F4,F8,F7};

static char options[] = {'a', INS};

static int (*ffuncs[])()={NULL,NULL,NULL,quit};
static int (*efuncs[])()={NULL,NULL,NULL,NULL};
static int (*ofuncs[])()={reform,insert};

static char filehelp[] =
   "Load a file, Save current buffer, Type a new file, Quit";
static char edithelp[] =
   "Move, Copy, Delete blocks, Find a string";
static char optnhelp[] =
   "Toggle editor options";

MENU emn [] = {
    {"File",    filehelp, fselcs, NULL,   "",      ffuncs, 0},
    {"Edit",    edithelp, eselcs, NULL,   forced,  efuncs, 0},
    {"Options", optnhelp, oselcs, NULL,   options, ofuncs, 0},
    {NULL}
};

void main()
{
    set_toggles();
    clear_screen();
    menu_select(emn, 1);
    clear_screen();
}

/* ---- illustrates toggled menu mode selectors -------- */
int reforming, inserting;  /* these are mode variables */

static int reform(hs,vs)
{
    reforming ^= TRUE;
    set_toggles();
    return FALSE;
}

static int insert(hs,vs)
{
    inserting ^= TRUE;
    set_toggles();
    return FALSE;
}

static void set_toggles()
{
    strcpy(&oselcs[0][24], reforming ? "(on) " : "(off)");
    strcpy(&oselcs[1][24], inserting ? "(on) " : "(off)");
}

/* ---- when TRUE is returned, the menu system exits ----- */
static int quit(hs,vs)
{
    return TRUE;
}




<a name="01d4_000f"><a name="01d4_000f">
<a name="01d4_0010">
[LISTING FOUR]
<a name="01d4_0010">


/* --------- entry.h ---------- */

typedef struct field {      /* data entry field description*/
    int frow;               /* field row                   */
    int fcol;               /* field column position       */
    int fx;                 /* running column              */
    char *fbuff;            /* field buffer                */
    char *fmask;            /* field data entry mask       */
    char *fhelp;            /* field help window mnemonic  */
} FIELD;

void field_tally(void);
int data_entry(FIELD *, int, int);
void clear_template(void);
void insert_line(void);

#define INSERTING TRUE      /* initial Insert mode */





<a name="01d4_0011"><a name="01d4_0011">
<a name="01d4_0012">
[LISTING FIVE]
<a name="01d4_0012">

/* --------- entry.c ---------- */

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "window.h"
#include "entry.h"

#define FIELDCHAR '_'
int inserting = INSERTING;      /* insert mode, TRUE/FALSE */

extern struct wn wkw;

/* -------- local prototypes -------- */
static void addfield(FIELD *);
static void disp_field(FIELD *, char *, char *);
static int read_field(int);
static void data_value(FIELD *);
static int endstroke(int);
static void home_cursor(void);
static int backspace(void);
static void end_cursor(void);
static void forward(void);
static int fore_word(void);
static int back_word(void);
static void delete_char(void);
static void delete_word(void);

static FIELD *fhead;
static FIELD *ftail;
FIELD *fld;

/* -------- display a data field ------ */
static void disp_field(FIELD *fldv, char *bf, char *msk)
{
    char cl[80], *cp = cl;

    while (*msk)    {
        *cp++ = (*msk != FIELDCHAR ? *msk : *bf++);
        msk++;
    }
    *cp = '\0';
    writeline(fldv->fx + fldv->fcol - 1,
               fldv->frow, cl);
}

/* ------- display the data value in a field ------ */
static void data_value(FIELD *fldv)
{
    gotoxy(fldv->fcol, fldv->frow);
    fldv->fx = 1;
    disp_field(fldv, fldv->fbuff, fldv->fmask);
}

/* ------ display all the fields in a window ------- */
void field_tally()
{
    FIELD *fldt;

    fldt = fhead;
    while (fldt != ftail)    {
        data_value(fldt);
        fldt++;
    }
}

/* ------- clear a template to all blanks ------ */
void clear_template()
{
    FIELD *fldc;
    char *bf, *msk;

    fldc = fhead;
    while (fldc != ftail)    {
        bf = fldc->fbuff;
        msk = fldc->fmask;
        while (*msk)    {
            if (*msk == FIELDCHAR)
                *bf++ = ' ';
            msk++;
        }
        fldc++;
    }
    *bf = '\0';
    field_tally();
}

char *mask, *buff;

/* ------- read a field from the keyboard ------------- */
static int read_field(c)
{
    int done = FALSE, first = TRUE;

    home_cursor();
    while (TRUE)    {
        gotoxy(fld->fx+fld->fcol-1, fld->frow);
        if (!c || !first)
            c = getkey();
        first = FALSE;
        switch (c)    {
            case CTRL_D:
                delete_word();
                break;
            case CTRL_FWD:
                done = !fore_word();
                break;
            case CTRL_BS:
                done = !back_word();
                break;
            case HOME:
                fld->fx = 1;
                home_cursor();
                break;
            case END:
                end_cursor();
                break;
            case FWD:
                forward();
                break;
            case BS:
                backspace();
                break;
            case '\b':
                if (!backspace())
                    break;
                gotoxy(fld->fx+fld->fcol-1, fld->frow);
            case DEL:
                delete_char();
                disp_field(fld, buff, mask);
                break;
            case INS:
                inserting ^= TRUE;
                insert_line();
                break;
            default:
                if (endstroke(c))    {
                    done = TRUE;
                    break;
                }
                if (inserting)    {
                    memmove(buff+1, buff, strlen(buff)-1);
                    disp_field(fld, buff, mask);
                    gotoxy(fld->fcol+fld->fx-1, fld->frow);
                }
                *buff = (char) c;
                putch(c);
                forward();
                if (!*mask)
                    c = FWD;
                break;
        }
        if (done || !*mask)
            break;
    }
    wkw.wx = fld->fx+1;
    return c;
}

/* -------- home the cursor in the field -------- */
static void home_cursor()
{
    buff = fld->fbuff+fld->fx-1;
    mask = fld->fmask+fld->fx-1;
    while (*mask != FIELDCHAR)    {
        fld->fx++;
        mask++;
    }
}

/* ------- move the cursor to the end of the field ------ */
static void end_cursor()
{
    fld->fx += strlen(mask)-1;
    buff += strlen(buff)-1;
    mask += strlen(mask)-1;
    while (*buff == ' ')
        if (!backspace())
            break;
    forward();
}

/* ------ move the cursor forward one character -------- */
static void forward()
{
    do    {
        fld->fx++;
        mask++;
    } while (*mask && *mask != FIELDCHAR);
    buff++;
}

/* ------- move back one character ------------ */
static int backspace()
{
    if (buff != fld->fbuff)    {
        --buff;
        do    {
            --mask;
            --(fld->fx);
        } while (*mask != FIELDCHAR);
        return TRUE;
    }
    return FALSE;
}

/* ------- move forward one word ------- */
static int fore_word()
{
    int ct = 2, test = *buff == ' ';

    while (ct--)    {
        while ((*buff == ' ') == test && *mask)
            forward();
        if (!*mask)
            return FALSE;
        if (test)
            break;
        test = !test;
    }
    return TRUE;
}

/* ------- move backward one word ------- */
static int back_word()
{
    int test;

    if (buff == fld->fbuff)
        return FALSE;
    if (*(buff-1) == ' ')
        backspace();
    test = *buff == ' ';
    while ((*buff == ' ') == test && buff != fld->fbuff)
        backspace();
    if (test)
        while (*buff != ' ' && buff != fld->fbuff)
            backspace();
    if (*buff == ' ')
        forward();
    return TRUE;
}

/* --------- delete a character ----------- */
static void delete_char()
{
    memmove(buff, buff+1, strlen(buff));
    *(buff+strlen(buff)) = ' ';
}

/* ----------- delete a word ---------- */
static void delete_word()
{
    int test = *buff == ' ';
    int ln = strlen(buff);

    while ((*buff == ' ') == test && ln--)
        delete_char();
    if (!test)
        delete_char();
    disp_field(fld, buff, mask);
}

/* ---------- test c for an ending keystroke ----------- */
static int endstroke(int c)
{
    switch (c)    {
        case '\r':
        case '\n':
        case '\t':
        case ESC:
        case F1:
        case F2:
        case F3:
        case F4:
        case F5:
        case F6:
        case F7:
        case F8:
        case F9:
        case F10:
        case PGUP:
        case PGDN:
        case HOME:
        case END:
        case CTRL_FWD:
        case CTRL_BS:
        case CTRL_HOME:
        case CTRL_END:
        case UP:
        case DN:
            return TRUE;
        default:
            return FALSE;
    }
}

/* ----- Process data entry for a screen template. ---- */
int data_entry(FIELD *fldin, int init, int firstfld)
{
    int exitcode = 0, done = FALSE;

    insert_line();
    fhead = ftail = fld = fldin;
    while (ftail->frow)
        ftail++;
    while (firstfld-- && fld != ftail)
        fld++;
    --fld;
    if (init)
        clear_template();
    field_tally();
    /* ---- collect data from keyboard into screen ---- */
    while (done == FALSE)    {
        gotoxy(fld->fcol, fld->frow);
        textcolor(FIELDFG);
        textbackground(FIELDBG);
        data_value(fld);
        gotoxy(fld->fcol, fld->frow);
        fld->fx = 1;
        exitcode = read_field(0);
        textcolor(ENTRYFG);
        textbackground(ENTRYBG);
        data_value(fld);
        switch (exitcode)    {
            case DN:
            case '\r':
            case '\t':
            case CTRL_FWD:
            case FWD:    done = (ftail == fhead+1);
                        fld++;
                        if (fld == ftail)
                            fld = fhead;
                        break;
            case CTRL_BS:
            case UP:    if (fld == fhead)
                            fld = ftail;
                        --fld;
                        break;
            case CTRL_HOME:
                        fld = fhead;
                        break;
            case CTRL_END:
                        fld = ftail-1;
                        break;
            default:    done = endstroke(exitcode);
                        break;
        }
    }
    fld = NULL;
    return (exitcode);
}

/* ---------- set insert/exchange cursor shape ----------- */
void insert_line()
{
    set_cursor_type(inserting ? 0x0106 : 0x0607);
}



<a name="01d4_0013"><a name="01d4_0013">
<a name="01d4_0014">
[LISTING SIX]
<a name="01d4_0014">

/* ----------- testentr.c --------- */
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include "window.h"
#include "entry.h"

struct config {
    char name[36];
    char acctno[21];
    char password[11];
    char phone[15];
} cfg;

static char mask[] = "___________________________________";
static char phmask[] = "(___) ___-____ ext.____";

FIELD pers_template[] = {
    {3, 14, 1, cfg.name,    mask,    NULL},
    {4, 14, 1, cfg.acctno,  mask+15, NULL},
    {5, 14, 1, cfg.password,mask+25, NULL},
    {6, 14, 1, cfg.phone,   phmask,  NULL},
    {0}
};

void main()
{
    establish_window(15,5,65,12,ENTRYFG,ENTRYBG,TRUE);
    window_title(" Personal Data ");
    gotoxy(3,3);
    cputs("Name:");
    gotoxy(3,4);
    cputs("Account #:");
    gotoxy(3,5);
    cputs("Password:");
    gotoxy(3,6);
    cputs("Phone:");
    data_entry(pers_template, TRUE, 1);
    delete_window();
    return FALSE;
}









Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.