home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
c
/
cpicture.zip
/
PARAPICT.CPP
next >
Wrap
C/C++ Source or Header
|
1992-08-11
|
24KB
|
750 lines
/*************************************************************************
** Paradox-Like Picture Field Input Processing
**************************************************************************
** **
** Copyright (c) 1992 Flexible Information Systems, Inc. **
** **
** This code may be freely used by any programmer **
** including royalty free inclusion in any commercial **
** application, but any commercial rights to the source **
** code or object files of this code is are reserved. **
** **
** This code is supplied strictly as-is, and FIS, Inc. and the **
** author assume no responsibility for the accuracy, use or fitness **
** for a particular purpose **
** **
** **
** Author: Ken Vogel **
** CIS Id: 74007,564 **
** Filename: parapict.cpp **
** Prefix: PPIC_ **
** Date: 24-Mar-92 **
** **
** Description: A set of recursive C++ classes which process
** data entry with Paradox-like picture formats.
**
** The classes in this file do not actually perform
** any keyboard or display functions, but should be
** integrated into a data entry system. This allows
** them to be used in TurboVision, Windows, etc.
**
** **
**************************************************************************/
#if 0
---->>> Revision History <<<----
1 PARAPICT.CPP 18-Apr-92,14:55:12,`KEN' Paradox-like picture processing
Not tied to any particular input system
2 PARAPICT.CPP 12-Jun-92,13:36:10,`KEN' Fix minor bug
3 PARAPICT.CPP 11-Aug-92,11:52:04,`KEN' Fix bug in \r handling
---->>> Revision History <<<----
#endif
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#define Uses_parapict
#include "parapict.hpp"
void tbg_ResetOneElement (PPIC_ElementClass *element)
// Reset a single element of a list of PPIC_ElementClass *
{
element->ResetState();
}
////////////////////// PPIC_ElementListClass \\\\\\\\\\\\\\\\\\\\\\\\\
PPIC_ElementListClass::PPIC_ElementListClass()
: dElementsCPP (0)
, dCount (0)
{
}
PPIC_ElementListClass::~PPIC_ElementListClass()
{
PPIC_ElementClass **cursor = dElementsCPP;
while (dCount-- > 0)
delete (*cursor++);
delete dElementsCPP;
}
PPIC_ElementClass *PPIC_ElementListClass::at (int index)
{
return (index < dCount) ? dElementsCPP[ index ] : 0;
}
void PPIC_ElementListClass::insert (PPIC_ElementClass *newElement)
{
PPIC_ElementClass **NewElementsCPP = new PPIC_ElementClass *[dCount + 1];
if (dCount > 0)
memcpy (NewElementsCPP, dElementsCPP, dCount * sizeof (*dElementsCPP));
delete dElementsCPP;
dElementsCPP = NewElementsCPP;
dElementsCPP[dCount++] = newElement;
}
void PPIC_ElementListClass::forEach ( void (*actionFun)(PPIC_ElementClass *))
{
for (int index = 0; index < dCount; index++)
actionFun (dElementsCPP[ index ]);
}
////////////////////// PPIC_SequenceClass \\\\\\\\\\\\\\\\\\\\\\\\\
PPIC_SequenceClass::PPIC_SequenceClass ( PPIC_ElementClass *ElementCP)
: dElementListCP (new PPIC_ElementListClass())
, dCurrentEl(0)
// Constructor to create a sequence class from a single element
{
dElementListCP->insert (ElementCP);
}
PPIC_SequenceClass::~PPIC_SequenceClass()
{
delete dElementListCP;
}
void PPIC_SequenceClass::AddElement (PPIC_ElementClass *ElementCP)
// Add an element to a sequence class
{
dElementListCP->insert (ElementCP);
}
void PPIC_SequenceClass::ResetState( void )
// Reset state of entry system
{
dElementListCP->forEach (tbg_ResetOneElement);
dCurrentEl = 0;
}
int PPIC_SequenceClass::ProcessLetter( char Letter
, PPIC_FlagsType *FlagsP
, Boolean IsKeypress
, char *DestP
, int MaxLength )
{
int Result;
if (dCurrentEl >= dElementListCP->getCount())
{
*FlagsP = PPIC_cOverflow;
return 0;
}
// Loop in case they are done
while (1)
{
PPIC_ElementClass *CurrentElP = dElementListCP->at (dCurrentEl);
*FlagsP = 0;
Result = CurrentElP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
if (*FlagsP & PPIC_cOverflow)
{
if (++dCurrentEl >= dElementListCP->getCount())
return 0; // Keep overflow flag
continue;
}
if (dCurrentEl < dElementListCP->getCount() - 1)
*FlagsP = 0; // Cannot be Full if not in last element
return Result;
} // Loop over elements when done
} // PPIC_SequenceClass::ProcessLetter
////////////////////// PPIC_AlternateClass \\\\\\\\\\\\\\\\\\\\\\\\\
PPIC_AlternateClass::PPIC_AlternateClass ( PPIC_ElementClass *ElementCP)
: dAlternativeListCP (new PPIC_ElementListClass ())
, dSelectedEl (-1)
// Constructor to create a sequence class from a single element
{
dAlternativeListCP->insert (ElementCP);
}
PPIC_AlternateClass::~PPIC_AlternateClass()
{
delete dAlternativeListCP;
}
void PPIC_AlternateClass::AddElement (PPIC_ElementClass *ElementCP)
// Add an element to a sequence class
{
dAlternativeListCP->insert (ElementCP);
}
void PPIC_AlternateClass::ResetState( void )
// Reset state of entry system
{
dAlternativeListCP->forEach (tbg_ResetOneElement);
dSelectedEl = -1;
}
int PPIC_AlternateClass::ProcessLetter( char Letter
, PPIC_FlagsType *FlagsP
, Boolean IsKeypress
, char *DestP
, int MaxLength )
{
int Result;
PPIC_ElementClass *CurrentElP;
if (dSelectedEl == -1)
{
for (dSelectedEl = 0; dSelectedEl < dAlternativeListCP->getCount();
dSelectedEl++)
{
CurrentElP = dAlternativeListCP->at (dSelectedEl);
Result = CurrentElP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
if (Result > 0)
{
// Have a selection -- dSelectedEl is correct
return Result;
}
}
// No-one was able to handle it
dSelectedEl = -1;
return 0;
} // Select an alternate
else
{
CurrentElP = dAlternativeListCP->at (dSelectedEl);
return CurrentElP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
} // Use existing selection
} // PPIC_AlternateClass::ProcessLetter
//////////////////////// PPIC_OptionalClass \\\\\\\\\\\\\\\\\\\\\\\\\\\
PPIC_OptionalClass::PPIC_OptionalClass ( PPIC_ElementClass *LeftCP
, PPIC_ElementClass *RightCP )
: dLeftElementCP (LeftCP)
, dRightElementCP (RightCP)
, dState (PPIC_fStarting)
// Construct an optional selection from two elements (first is the optional
// one)
{
}
PPIC_OptionalClass::~PPIC_OptionalClass()
{
delete dLeftElementCP;
delete dRightElementCP;
}
void PPIC_OptionalClass::ResetState ()
{
dLeftElementCP->ResetState();
if (dRightElementCP)
dRightElementCP->ResetState();
dState = PPIC_fStarting;
}
int PPIC_OptionalClass::ProcessLetter( char Letter
, PPIC_FlagsType *FlagsP
, Boolean IsKeypress
, char *DestP
, int MaxLength )
{
int Result;
if (dState == PPIC_fStarting || dState == PPIC_fLeftActive)
{
if (dState == PPIC_fStarting && Letter == '\r')
{
// Optional, so they can end it now only if right element says
// it's ok
*FlagsP = PPIC_cOverflow;
Result = 0;
// Then let the right element have a crack at it
}
else
{
// Try processing with left
Result = dLeftElementCP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
if ((*FlagsP & PPIC_cOverflow)
|| (Result == 0 && dState == PPIC_fStarting))
dState = PPIC_fRightActive;
else
{
dState = PPIC_fLeftActive;
return Result;
}
}
} // Check the left element
// Only gets here if right active
if (dRightElementCP)
{
return dRightElementCP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
}
else
{
*FlagsP = PPIC_cOverflow;
return 0;
}
} // PPIC_OptionalClass::ProcessLetter
//////////////////// PPIC_FormatClass \\\\\\\\\\\\\\\\\\\\\\\\\\\\
PPIC_FormatClass::PPIC_FormatClass (const char *FormatStrP)
{
dFormatStringOP = new char [strlen (FormatStrP) + 1];
if (dFormatStringOP)
strcpy (dFormatStringOP, FormatStrP);
dCursorP = dFormatStringOP;
}
void PPIC_FormatClass::~PPIC_FormatClass()
{
delete dFormatStringOP;
}
void PPIC_FormatClass::ResetState()
{
dCursorP = dFormatStringOP;
}
int PPIC_FormatClass::ProcessLetter( char Letter
, PPIC_FlagsType *FlagsP
, Boolean IsKeypress
, char *DestP
, int MaxLength )
{
int Result;
if (*dCursorP == 0)
{
*FlagsP = PPIC_cOverflow;
return 0;
}
*FlagsP = 0;
if (Letter == '\r')
return 0;
// Process a single letter
if (*dCursorP <= PPIC_cMaxFormatChar)
{
Boolean Good = False;
// A formatting character
switch (*dCursorP)
{
case PPIC_cDigit:
Good = Boolean (isdigit (Letter));
break;
case PPIC_cUpperLetter:
Letter = toupper (Letter);
case PPIC_cLetter:
Good = Boolean (isalpha (Letter));
break;
case PPIC_cUpperAny:
Letter = toupper (Letter);
case PPIC_cAny:
default:
Good = True;
break;
} // Switch on cursor
// Now process
if (!Good)
return 0;
}
else
{
// A literal -- check if it matches
if ( (!IsKeypress || Letter != ' ')
&& toupper (*dCursorP) != toupper (Letter))
return 0;
Letter = *dCursorP;
}
// Store this letter no matter what
dCursorP++;
Result = 1;
MaxLength--;
if (IsKeypress && DestP)
{
*DestP++ = Letter;
*DestP = 0;
}
// Check for auto expand of literals after this letter
while (MaxLength-- > 0 && *dCursorP > PPIC_cMaxFormatChar)
{
*DestP++ = *dCursorP++;
Result++;
}
*DestP = 0;
// Check for full
if (*dCursorP == 0)
*FlagsP = PPIC_cFull;
return Result;
} // FormatClass::ProcessLetter
//////////////////// PPIC_RepetitionClass \\\\\\\\\\\\\\\\\\\\\\\\\\\\
PPIC_RepetitionClass::PPIC_RepetitionClass (int Count
, PPIC_ElementClass *ElementCP)
: dRepeatCount (Count)
, dRepsLeft (Count)
, dElementCP (ElementCP)
, dInProcess (False)
{
}
PPIC_RepetitionClass::~PPIC_RepetitionClass()
{
delete dElementCP;
}
void PPIC_RepetitionClass::ResetState()
{
dElementCP->ResetState();
dRepsLeft = dRepeatCount;
dInProcess = False;
}
int PPIC_RepetitionClass::ProcessLetter( char Letter
, PPIC_FlagsType *FlagsP
, Boolean IsKeypress
, char *DestP
, int MaxLength )
{
if (dRepsLeft == 0 || (!dInProcess && dRepsLeft == -1 && Letter == '\r') )
{
*FlagsP = PPIC_cOverflow;
return 0;
}
int Result = dElementCP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
if (*FlagsP & PPIC_cOverflow)
{
if (dRepsLeft != -1)
if (--dRepsLeft == 0)
return 0;
dInProcess = False;
dElementCP->ResetState();
Result = dElementCP->ProcessLetter (Letter, FlagsP, IsKeypress
, DestP, MaxLength);
}
if (!dInProcess && Result == 0 && dRepsLeft == -1)
*FlagsP = PPIC_cOverflow;
if (Result != 0)
dInProcess = True;
// Cannot be full unless all reps are used
if (dRepsLeft != 0)
*FlagsP &= ~PPIC_cFull;
return Result;
} // PPIC_RepetitionClass::ProcessLetter
//////////////////////// The recursive parser \\\\\\\\\\\\\\\\\\\\\\\\\\
// parses an input picture in an element
// Get one character from the input string, process to control
// codes accordingly. Returns the next character & updates the pointer
// Does NOT handle control codes!
char ppic_NextChar (const char *& CursorP)
{
// The character translation set. The index into this string is the
// "internal" representation of the formatting character.
char FromCharA[] = "*[]{},#?&@!";
char Result = *CursorP;
if (Result == 0)
return 0;
CursorP++;
if (Result == ';')
{
if (0 != (Result = *CursorP))
CursorP++;
}
else
{
char *SpecialP = strchr (FromCharA, Result);
if (SpecialP != 0)
Result = (char)(SpecialP - FromCharA + 1);
}
return Result;
} // ppic_NextChar
// Forward declaration for recursion
PPIC_ElementClass *PPIC_Parse ( const char *&CursorP);
PPIC_ElementClass *PPIC_ElementParse ( const char *&CursorP)
// Parse an element of a picture string
// PictureP is the input picture
// Modifies PictureP to point to last char afer input
{
PPIC_ElementClass *ResultCP;
char *StoreCursorP;
const char *BeforePeekP = CursorP;
char NextChar = ppic_NextChar (CursorP);
if (NextChar == 0)
return NULL;
if (NextChar > PPIC_cMaxSpecialChar)
{
// Formatting string -- get all chars possible
// Get the length for allocation
int Length = 1;
while (ppic_NextChar (CursorP) > PPIC_cMaxSpecialChar)
Length++;
char *FormatOP = new char [Length + 1];
if (FormatOP == 0)
return 0;
// Copy the formatting characters into this
StoreCursorP = FormatOP;
CursorP = BeforePeekP;
while ( Length--)
*StoreCursorP++ = ppic_NextChar (CursorP);
*StoreCursorP = 0;
CursorP = CursorP;
ResultCP = new PPIC_FormatClass (FormatOP);
delete FormatOP;
} // A formatting character
else
{
char BufferA[10];
int Counter, Count;
PPIC_ElementClass *NextElementCP, *LeftCP;
switch (NextChar)
{
case PPIC_cRepetition:
// Get up to 5 digits
StoreCursorP = BufferA;
Counter = 5;
while (Counter-- && isdigit (*CursorP))
*StoreCursorP++ = *CursorP++;
*StoreCursorP = 0;
if (*BufferA == 0)
Count = -1;
else
{
Count = atoi (BufferA);
if (Count < 0)
Count = -1;
}
// Now get the next element
BeforePeekP = CursorP;
NextChar = ppic_NextChar (CursorP);
if (NextChar <= PPIC_cMaxSpecialChar)
{
// A complex element. If it's an option, we only want
// to process the beginning
if (NextChar == PPIC_cOptionOpen)
{
NextElementCP = PPIC_Parse (CursorP);
NextChar = ppic_NextChar (CursorP);
if (NextChar != PPIC_cOptionClose)
{
delete NextElementCP;
NextElementCP = 0;
return 0;
}
else
{
NextElementCP
= new PPIC_OptionalClass (NextElementCP, 0);
}
} // Process option repetition
else
{
CursorP = BeforePeekP;
NextElementCP = PPIC_ElementParse (CursorP);
}
} // Have a special character
else
{
// A single character
BufferA[0] = NextChar;
BufferA[1] = 0;
NextElementCP = new PPIC_FormatClass (BufferA);
}
if (NextElementCP == 0)
ResultCP = 0;
else
ResultCP = new PPIC_RepetitionClass (Count, NextElementCP);
break; // A repetition
case PPIC_cOptionOpen:
LeftCP = PPIC_Parse (CursorP);
if (ppic_NextChar (CursorP) != PPIC_cOptionClose)
{
delete LeftCP;
ResultCP = 0;
}
else
{
NextElementCP = PPIC_Parse (CursorP);
ResultCP = new PPIC_OptionalClass (LeftCP, NextElementCP);
}
break;
case PPIC_cGroupOpen:
ResultCP = PPIC_Parse (CursorP);
if (ppic_NextChar (CursorP) != PPIC_cGroupClose)
{
delete ResultCP;
ResultCP = 0;
}
break;
case PPIC_cAlternate:
case PPIC_cOptionClose:
case PPIC_cGroupClose:
default:
CursorP = BeforePeekP;
ResultCP = 0;
break;
} // Switch NextChar
} // A special character
return ResultCP;
} // PPIC_ElementParse
PPIC_ElementClass *PPIC_Parse ( const char *&CursorP)
// Parse an entire picture string
{
PPIC_SequenceClass *SequenceCP = 0;
PPIC_AlternateClass *AlternateCP = 0;
PPIC_ElementClass *ElementCP;
const char *BeforeP;
char NextChar;
// Loop over the expressions we can get
while (1)
{
ElementCP = PPIC_ElementParse (CursorP);
if (ElementCP != 0 && SequenceCP != 0)
{
// Add to end of sequence
SequenceCP->AddElement (ElementCP);
ElementCP = SequenceCP;
}
BeforeP = CursorP;
NextChar = ppic_NextChar (CursorP);
if (ElementCP == 0
|| NextChar == 0
|| NextChar == PPIC_cOptionClose
|| NextChar == PPIC_cGroupClose)
{
// The end
if (AlternateCP != 0)
{
AlternateCP->AddElement (ElementCP);
ElementCP = AlternateCP;
}
CursorP = BeforeP;
return ElementCP;
}
else if (NextChar == PPIC_cAlternate)
{
if (AlternateCP != 0)
AlternateCP->AddElement (ElementCP);
else
AlternateCP = new PPIC_AlternateClass (ElementCP);
// Use the element in alternate -- erase any sequence
SequenceCP = 0;
}
else
{
// A sequence of elements (if exists, we already added to end)
if (SequenceCP == 0)
SequenceCP = new PPIC_SequenceClass (ElementCP);
CursorP = BeforeP;
}
} // Loop over parts of expression (terminates with return)
} // PPIC_Parse
PPIC_PictureClass::PPIC_PictureClass(const char *PictureP)
: thePicture (0)
{
thePicture = PPIC_Parse (PictureP);
valid = Boolean (thePicture != 0);
}
PPIC_PictureClass::~PPIC_PictureClass()
{
delete thePicture;
}
int PPIC_PictureClass::ProcessLetter( char Letter
, PPIC_FlagsType *FlagsP
, Boolean IsKeypress
, char *DestP
, int MaxLength )
{
int result = 0;
if (thePicture)
{
result = thePicture->ProcessLetter ( Letter, FlagsP, IsKeypress
, DestP, MaxLength);
// Overflow of outermost picture is an error (unless c/r)
if ((*FlagsP & PPIC_cOverflow) && Letter != '\r')
*FlagsP = 0;
}
return result;
}
void PPIC_PictureClass::ResetState( void )
{
if (thePicture)
thePicture->ResetState();
}
char *PPIC_PictureClass::ReprocessString ( char *StringP
, PPIC_FlagsType *FlagsP)
{
*FlagsP = 0;
if (thePicture)
{
thePicture->ResetState();
char *CursorP = StringP;
while (*CursorP)
{
if (!thePicture->ProcessLetter ( *CursorP, FlagsP, False, 0, 0)
|| (*FlagsP & PPIC_cOverflow))
return CursorP;
CursorP++;
}
}
return 0;
}