home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************************
- ** 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 <<<----
- ---->>> 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;
-
- // 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);
- break;
- 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;
- }
-