Sample Source Code
QuickCall: list_dis.c

About the Program

"QuickCall" is the main engine for QuickATM's airport information kiosks. It is written entirely in C, and runs under DOS. Most of the data files are flattened versions of our relational database, which were dumped into a pseudo-filesystem that we created. "QuickCall" ran in over one hundred kiosks nationwide.

In the code, the macro PUB_EXE is used to denote public functions, and PVT_EXE indicates a private (module level) function. These macros allow for quick greps and extractions of public and private function names.

About the Code

This module handles the displaying of list files. A list file is a table in our flattened database, stored on disk as a plain text file, and in memory as a structure containing multiple lists and arrays.

The Code

/* -- list_dis.c: list displaying and history routines. -- */
/* -- Copyright (c) 1995 by QuickATM.  All rights reserved. -- */

#include <ctype.h>
#include "proto.h"
#include "list.h"

extern List *list;

/* --------------------------------------------------------------------------*/
/*  show_hermometer: displays the hermometer (scroll bar).                   */
/* --------------------------------------------------------------------------*/

PVT_EXE void show_hermometer( )
{
    int idx1, idx2;
    int y_base, y_offset, y1, y2;
    long offset;
    rect r;

    /* erase the scrollbar area. */
    start_graphics( );
    BackColor( list->hermom_bg );
    EraseRect( &( list->hermometer ) );
    end_graphics( );

    /* top and bottom of highlighted area -- list entries. */
    idx1 = list_index_of( 1 );
    idx2 = list_index_of( list->n_scroll );

    /* top and bottom of highlighted area -- pixels. */
    y_base   = list->hermometer.Ymin;
    y_offset = list->hermometer.Ymax - list->hermometer.Ymin;
    offset = ((long)(y_offset) * (long)(idx1)) / (long)(list->ni);
    y1 = y_base + (int)offset;
    offset = ((long)(y_offset) * (long)(idx2)) / (long)(list->ni);
    y2 = y_base + (int)offset;

    /* adjust coordinates to make the rectangle slightly bigger. */
    if ( y1 > y_base + 1 ) { y1--; };
    if ( y2 < y_base + y_offset - 1 ) { y2++; };

    /* X coordinates shrink if the scrollbar is wide enough. */
    r.Xmin = list->hermometer.Xmin;
    r.Xmax = list->hermometer.Xmax;
    if ( ( r.Xmax - r.Xmin ) > 10 ) {
        r.Xmin += 3;
        r.Xmax -= 3;
    }

    BackColor( list->hermom_fg );
    start_graphics( );
    if ( y2 >= y1 ) {
        /* normal case: one filled area. */
        r.Ymin = y1;
        r.Ymax = y2;
        EraseRect( &r );
    } else {
        /* deviant case: two filled areas. */
        r.Ymin = list->hermometer.Ymin;
        r.Ymax = y2;
        EraseRect( &r );
        r.Ymin = y1;
        r.Ymax = list->hermometer.Ymax;
        EraseRect( &r );
    }
    end_graphics( );
    BackColor( LTEAL );
}

/* ------------------------------------------------------------------------- */
/*  refresh_list_az_rects: refreshes the scroll button labels for lists.     */
/* ------------------------------------------------------------------------- */

PVT_EXE char atoz( char *c )
{
    char d = *c;
    if ( d == '\0' )  return ' ';
    if ( d == ' ' )   return ' ';
    if ( d <  'A' )   d  = 'A';
    if ( d >= 'a' )   d -= ( 'a' - 'A' );
    if ( d >  'Z' )   d  = 'Z';
    return d;
}

PVT_EXE void refresh_list_az_rects( )
{
    field f;
    ints *tmp;
    char  buf[20], *font;

    f.fg_color = BLACK;
    f.bg_color = LGRAY;
    f.h_align  = f.v_align = 1;
    f.font     = LARGE_FONT;

    if ( list == NULL ) return;

    font = get_dictionary( STRING, "AZ RECT FONT" );
    if ( set_def_font( font ) == 1 ) {
        f.font = font;
    }

    tmp = get_dictionary( INTS, "UP AZ RECT" );
    if ( tmp != NULL ) {
        /* give the up-scroll button an appropriate name. */
        char c, a;
        c = atoz( list_item_of( -1 ) );
        a = atoz( list_item_of( 1-list->curi ) );
        sprintf( buf, "%c to %c", a, c );
        if ( c <= a )   sprintf( buf, "%c", a );
        if ( c == ' ' ) sprintf( buf, "(top)" );

        Ints2Rect( &(f.disp), tmp );
        clear_text_in_field( &f );
        draw_text_in_field( buf, &f );
    }

    tmp = get_dictionary( INTS, "DOWN AZ RECT" );
    if ( tmp != NULL ) {
        /* give the down-scroll button an appropriate name. */
        char c, z;
        c = atoz( list_item_of( list->n_scroll ) );
        z = atoz( list_item_of( -1-list->curi ) );
        sprintf( buf, "%c to %c", c, z );
        if ( c >= z )   sprintf( buf, "%c", z );
        if ( c == ' ' ) sprintf( buf, "(bottom)" );

        Ints2Rect( &(f.disp), tmp );
        clear_text_in_field( &f );
        draw_text_in_field( buf, &f );
    }

    /* display the alpha buffer. */
    if ( list->control == ALPHA_CTRL ) {
        f.bg_color = WHITE;
        f.font     = LARGE_FONT;
        f.disp     = list->alpha_rect;
        clear_text_in_field( &f );
        draw_text_in_field( list->alpha_buf, &f );
    }
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

PVT_EXE int fg_color_of_label( int but, char *label )
{
    int fg = BLACK;

    /* simple cases. */
    if ( list->display == QPHONE_DISP ) return BLACK;
    if ( list->control == NO_CTRL     ) return BLACK;

    /* see if we should unhighlight this entry. */
    switch ( list->control ) {
    case ALPHA_CTRL:
        if ( list->alpha_nbuf > 0 ) {
            if ( strnicmp( label, list->alpha_buf, list->alpha_nbuf ) != 0 ) {
                fg = DGRAY;
            }
        }
        break;
    case DATE_CTRL:
        if ( strnicmp( list_item_of( but ), list->date_string,
                       strlen( list->date_string ) ) < 0 ) {
            fg = DGRAY;
        }
        break;
    case MAP_CTRL:
        if ( list_index_of( but ) == list->map_best ) {
            fg = DRED;
        }
    }
    return fg;
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

PVT_EXE char *data_for_field( int ch, int but )
{
    char *ret = "";
    char *s;
    int   idx;
    int   min_i, max_i, i;

    /* these are displayed separately; therefore return nothing! */
    if ( ( ch == F_LABEL ) || ( ch == F_HASHMARK ) ) {
        return NULL;
    }

    /* find the bounds of this list entry's data. */
    idx = list_index_of( but );
    min_i = list->ii[ idx ];
    for ( i = min_i + 1; i < list->ns; i++ ) {
        if ( *( list->ss[ i ] ) == L_ITEM ) break;
    }
    max_i = i;

    switch( ch ) {

    case 'P': /* phone number. */
        {
            char *my_air = get_dictionary( STRING, "Q_HOME_AIRPORT" );

            for ( i = min_i; i < max_i; i++ ) {
                s = list->ss[ i ];
                if ( *s == 'p' ) {
                    if ( strnicmp( s + 1, "ANY", 3 ) == 0 ) {
                        if ( *ret == '\0' ) {
                            ret = s + 4;
                        }
                    }
                    if ( strnicmp( s + 1, my_air, 3 ) == 0 ) {
                        ret = s + 4;
                    }
                } else if ( *s == 'P' ) {
                    ret = s + 1;
                }
            }
        }
        break;

    default:
        for ( i = min_i; i < max_i; i++ ) {
            s = list->ss[ i ];
            if ( *s == ch ) {
                ret = s + 1;
            }
        }
    }

    return ret;
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

PVT_EXE void display_row_label( int but )
{
    char  *label;
    field *fp, f;

    /* get and copy a field for this label. */
    /* if there is none, no need to display! */
    fp = get_list_field( F_LABEL, but );
    if ( fp == NULL ) return;
    f = *fp;

    /* find the label to be displayed. */
    label = list_field_of( but, L_TEXT );
    if ( *label == '\0' ) {
        label = list_item_of( but );
    }

    /* change the color and the font as appropriate. */
    if ( list->display == NO_DISP ) {
        f.fg_color = fg_color_of_label( but, label );
        f.font     = get_dictionary( STRING, "LIST FONT" );
    }

    /* draw the label! */
    draw_text_in_field( label, &f );
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

PVT_EXE void display_row_hashmark( int but )
{
    int    idx;
    field *fp;
    char  *hash;
    char   hash2[ 80 ];

    if ( list->enable_hashmarks == FALSE ) return;

    /* get the field for the hashmark. */
    fp = get_list_field( F_HASHMARK, but );
    if ( fp == NULL ) return;

    /* if it's a hash one, add a mark. */
    idx = list_index_of( but );
    if ( 1 || ( idx % list->hashmark_freq == 0 ) ) {
        hash = get_dictionary( STRING, "Hash Mark" );
        if ( ( hash == NULL ) || ( *hash == '\0' ) ) {
            hash = "*";
        }
        sprintf( hash2, "%d", idx );
        draw_text_in_field( hash2, fp );
    }
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

PVT_EXE void clear_row( int but, char *chlist )
{
    char  *ch;
    field *fp;

    ch = chlist;
    while ( *ch ) {
        fp = get_list_field( *ch, but );
        if ( ( fp != NULL ) && ( ! ( isnullrect( &(fp->disp) ) ) ) ) {
            clear_text_in_field( fp );
        }
        ch++;
    }
}

/* ------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

PVT_EXE void display_row( int but, char *chlist )
{
    char  *ch, *data;
    char   dict[ 2 ] = " ";
    field *fp;

    /* fill in the [label] field. */
    display_row_label( but );

    /* fill in the [hashmark] field. */
    display_row_hashmark( but );

    /* now fill in all other fields. */
    ch = chlist;
    while ( *ch ) {
        data = data_for_field( *ch, but );
        if ( data != NULL ) {
            fp = get_list_field( *ch, but );
                if ( ( fp != NULL ) && ( ! ( isnullrect( &(fp->disp) ) ) ) ) {
                draw_text_in_field( data, fp );
            }

            *dict = *ch;
            add_dictionary( STRING, dict, data );
        }
        ch++;
    }
}


/* ------------------------------------------------------------------------- */
/*  display_list: redisplays the list.                                       */
/* --------------------------------------------------------------------------*/

PUB_EXE void display_list( )
{
    int row, idx;
    char *fields;

    list_resolve_curi( );

    for ( row = 0; row < list->n_scroll; row++ ) {
        if ( list->dirty[ row ] == TRUE ) {
            fields = get_list_fields_of_row( row );

            /* clear out this row. */
            clear_row( row, fields );

            /* if this row is in the list bounds, display it. */
            idx = list_index_of( row );
            if ( ( idx >= 0 ) && ( idx < list->ni ) ) {
                display_row( row, fields );
            }
        }
        list->dirty[ row ] = FALSE;
    }

    /* display the hermometer, if there is one. */
    if ( list->enable_hermometer == TRUE ) {
        show_hermometer( );
    }
    refresh_list_az_rects( );
}