Clipper Support Bulletin #6
LABEL FORM problems and workarounds

Copyright (c) 1991, 1992 Nantucket Corporation.  All rights reserved.


Version:  Clipper 5.0, revisions 1.00 - 1.03
Date:     8th January, 1991
Revised:  22nd May, 1992
Status:   Inactive

================================================================================

This Support Bulletin covers the following topics:

   1. Type mismatch error
   2. Performing a SET MARGIN before a LABEL FORM
   3. Erroneous NOCONSOLE handling
   4. Revised Lblrun.prg code

================================================================================
1. Type mismatch error
   
   PROBLEM: The LABEL FORM command is used, and works correctly.
   However, if used a second time, a "Type Mismatch" error occurs.
   
   REASON:  STATIC variables which were defined and assigned during
   the label routine's initial invocation were later assigned NIL
   values.  This resulted in an error on subsequent invocations,
   because the code was not designed to handle any of the variables
   having a NIL value.
   
   FIX:  Instead of assigning NIL values, the variables are now reset
   to their original values (where appropriate).
   
================================================================================
2. Performing a SET MARGIN before a LABEL FORM
   
   PROBLEM: Issuing a SET MARGIN prior to a LABEL FORM caused labels
   to be printed at a column position equal to three times the margin
   setting.
   
   REASON:  First, the code for the label routine was adding the
   margin twice.  Eliminating the second addition in two separate
   places in the code fixed this problem.
   
   Second, all label lines other than the first one are output using
   QOUT() (the functional equivalent of the ? command).  This
   function moves the print head down after each line and respects
   any SET MARGIN value. This accounts for the third multiple of the
   margin.
   
   FIX:  The problem was solved by temporarily eliminating the margin
   during execution of the label routine and reassigning it upon
   completion.  The current margin setting was retrieved using the
   SET() function and stored in a local variable.  Upon completion of
   the routine, the margin was reset to the value contained in this
   local variable.
   
   At this point anyone reading this might begin to wonder why all
   this trouble was taken.  It may seem easier to just eliminate all
   the internal margin handling and let the global effect upon QOUT()
   deal with the problem.  However, some may recall a Summer '87
   problem where the first line of a label would not respect the
   margin.  This occurred because the first line of a label is output
   with QQOUT() (the functional equivalent of the ?? command), which
   is not affected by SET MARGIN.
   
   This means that this version of the label routine is not Summer
   '87 compatible, as it will be respecting the margin on all printed
   lines.  Some developers may have placed a workaround in their code
   for the Summer '87 behavior.  This is probably in the form of a
   routine which will use printer escape sequences to place the print
   head at the desired margin offset before running the label form.
   Developers with such routines will need to remove that code.
   Failure to remove the Summer '87  workaround code will cause the
   first line to be printed at a column which is twice the margin.
   
================================================================================
3. Erroneous NOCONSOLE handling
   
   PROBLEM: Lines 134-136 of LBLRUN.prg are as follows:
   
   IF lConsole                             // To the screen
     lConsoleOn := SET( _SET_CONSOLE, lConsole )
   ENDIF
   
   lConsole is the NOCONSOLE parameter passed to LBLRUN. The
   preprocessor uses a logify result-marker for this parameter, which
   means that lConsole has a value of True (.T.) if NOCONSOLE was
   specified.  This causes the code listed above to turn the console
   on if NOCONSOLE was specified.
   
   FIX: Change the above lines of code to the following:
   
   IF lConsole                             // To the screen
     lConsoleOn := SET( _SET_CONSOLE, !lConsole )
   ENDIF
   
================================================================================
4. Revised Lblrun.prg code
   
/***
   *
   *   Lblrun.prg
   *   Clipper 5.0 LABEL FORM runtime system
   *   Copyright (c) 1990 Nantucket Corp.  All rights reserved
   *
   
*   Compile:  /m/n/w
*/

#include "lbldef.ch"          // Label array definitions
#include "error.ch"

// Filewide static declarations
STATIC aLabelData   := {}     // Label definition array
STATIC aBandToPrint := {}
STATIC cBlank    := ""
STATIC lOneMoreBand := .T.
STATIC nCurrentCol  := 1      // The current column in the band




/***
*  __LabelForm( ... ) --> NIL
*  Print the specified (.lbl) definition for specified records
*  meeting specified scope and condition
*/
FUNCTION __LabelForm( cLBLName, lPrinter, cAltFile, lConsole, bFor, ;
   bWhile, nNext, nRecord, lRest, lSample )

   LOCAL lPrintOn := .F.            // PRINTER status
   LOCAL lConsoleOn := .T.          // CONSOLE status
   LOCAL cExtraFile, lExtraState    // EXTRA status
   LOCAL xBreakVal, lBroke := .F.
   LOCAL err
   Local OldMargin


   // Resolve parameters
   IF cLBLName == NIL
      err := ErrorNew()
      err:severity := 2
      err:genCode := EG_ARG
      err:subSystem := "FRMLBL"
      Eval(ErrorBlock(), err)
   ELSE
      IF AT( ".", cLBLName ) == 0
         cLBLName := TRIM( cLBLName ) + ".LBL"
      ENDIF
   ENDIF

   IF lPrinter == NIL
      lPrinter := .F.
   ENDIF

   IF lConsole == NIL
      lConsole := .T.
   ENDIF

   IF lSample == NIL

      lSample := .F.
   ENDIF

   // Set output devices
   IF lPrinter           // To the printer
      lPrintOn  := SET( _SET_PRINTER, lPrinter )
   ENDIF
   IF lConsole           // To the screen
      lConsoleOn := SET( _SET_CONSOLE, !lConsole )
   ENDIF

   IF (!Empty(cAltFile))      // To file
      cExtraFile  := SET( _SET_EXTRAFILE, cAltFile )
      lExtraState := SET( _SET_EXTRA, .T. )
   ENDIF

   OldMargin := SET( _SET_MARGIN, 0)

   BEGIN SEQUENCE

      aLabelData := __LblLoad( cLBLName )  // Load the (.lbl) into an array

      // Add to the left margin if a SET MARGIN has been defined
      aLabelData[ LB_LMARGIN ] += OldMargin

      // Size the aBandToPrint array to the number of fields
      ASIZE( aBandToPrint, LEN( aLabelData[ LB_FIELDS ] ) )
      AFILL( aBandToPrint, SPACE( aLabelData[ LB_LMARGIN ] ) )

      // Create enough space for a blank record
      cBlank := SPACE( aLabelData[ LB_WIDTH ] + aLabelData[ LB_SPACES ] )

      // Handle sample labels
      IF lSample
         SampleLabels()
      ENDIF

      // Execute the actual label run based on matching records
      DBEval( { || ExecuteLabel() }, bFor, bWhile, nNext, nRecord, lRest )

      // Print the last band if there is one
      IF lOneMoreBand
         // Print the band
         AEVAL( aBandToPrint, ;
            { | BandLine | ; // PrintIt(SPACE(aLabelData[LB_LMARGIN])+ BandLine);
            PrintIt( BandLine ) } )
      ENDIF


   RECOVER USING xBreakVal

      lBroke := .T.

   END // Sequence



   // Clean up and leave
   aLabelData   := {}       // Recover the space
   aBandToPrint := {}
   nCurrentCol  := 1
   cBlank       := ""
   lOneMoreBand :=.T.

   // Clean up
   SET( _SET_PRINTER, lPrintOn )    // Set the printer back to prior state
   SET( _SET_CONSOLE, lConsoleOn )// Set the console back to prior state

   IF (!Empty(cAltFile))    // Set extrafile back
      SET( _SET_EXTRAFILE, cExtraFile )
      SET( _SET_EXTRA, lExtraState )
   ENDIF

   IF lBroke
      // keep the break value going
      BREAK xBreakVal
   ENDIF

   SET( _SET_MARGIN, OldMargin)

   RETURN NIL




/***
*  ExecuteLabel() --> NIL
*  Process the label array using the current record
*/
STATIC FUNCTION ExecuteLabel
   LOCAL nField, nMoreLines, aBuffer := {}, cBuffer

   // Load the current record into aBuffer
   FOR nField := 1 TO LEN( aLabelData[ LB_FIELDS ] )
      cBuffer := PADR(TRANSFORM( EVAL( aLabelData[ LB_FIELDS,nField,;
         LF_EXP ] ), ;
         REPLICATE( "X", aLabelData[ LB_WIDTH ] ) ), ;
         aLabelData[ LB_WIDTH ] + aLabelData[ LB_SPACES ])
      IF aLabelData[ LB_FIELDS, nField, LF_BLANK ]
         IF RTRIM( cBuffer ) == ""     // If it's a blank field
         ELSE
            AADD( aBuffer, cBuffer )
         ENDIF
      ELSE
         AADD( aBuffer, cBuffer )
      ENDIF
   NEXT
   ASIZE( aBuffer, LEN( aLabelData[ LB_FIELDS ] ) )


   // Add aBuffer to aBandToPrint
   FOR nField := 1 TO LEN( aLabelData[ LB_FIELDS ] )
      IF aBuffer[ nField ] == NIL
         aBandToPrint[ nField ] += cBlank
      ELSE
         aBandToPrint[ nField ] += aBuffer[ nField ]
      ENDIF
   NEXT

   IF nCurrentCol == aLabelData[ LB_ACROSS ]
      lOneMoreBand := .F.
      nCurrentCol  := 1

      // Print the band
      AEVAL( aBandToPrint, ;
         { | BandLine | ; //PrintIt(SPACE(aLabelData[LB_LMARGIN])+ BandLine);
         PrintIt( BandLine ) } )
      nMoreLines := aLabelData[ LB_HEIGHT ] - LEN( aBandToPrint )
      IF nMoreLines > 0
         FOR nField := 1 TO nMoreLines
            PrintIt()
         NEXT
      ENDIF
      IF aLabelData[ LB_LINES ] > 0

         // Add the spaces between the label lines
         FOR nField := 1 TO aLabelData[ LB_LINES ]
            PrintIt()
         NEXT

      ENDIF

      // Clear out the band
      AFILL( aBandToPrint, SPACE( aLabelData[ LB_LMARGIN ] ) )
   ELSE
      lOneMoreBand := .T.
      nCurrentCol++
   ENDIF

   RETURN NIL




/***
*  SampleLabels() --> NIL
*  Print sample labels
*/
STATIC FUNCTION SampleLabels
   LOCAL nGetKey, lMoreSamples := .T., nField
   LOCAL aBand := {}

   // Create the sample label row
   ASIZE( aBand, aLabelData[ LB_HEIGHT ] )

   AFILL( aBand, SPACE( aLabelData[ LB_LMARGIN ] ) +;
      REPLICATE( REPLICATE( "*", ;
      aLabelData[ LB_WIDTH ] ) + ;
      SPACE( aLabelData[ LB_SPACES ] ), ;
      aLabelData[ LB_ACROSS ] ) )

   // Prints sample labels
   DO WHILE lMoreSamples

      // Print the samples
      AEVAL( aBand, ;
         { | BandLine | PrintIt( BandLine ) } )
      IF aLabelData[ LB_LINES ] > 0
         // Add the spaces between the label lines
         FOR nField := 1 TO aLabelData[ LB_LINES ]
            PrintIt()
         NEXT
      ENDIF

      // Prompt for more
      @ ROW(), 0 SAY "Do you want more samples? (Y/N)"
      nGetKey := INKEY(0)
      @ ROW(), COL() SAY CHR(nGetKey)
      IF ROW() == MAXROW()
         SCROLL( 0, 0, MAXROW(), MAXCOL(), 1 )
         @ MAXROW(), 0 SAY ""
      ELSE
         @ ROW()+1, 0 SAY ""
      ENDIF
      IF UPPER(CHR(nGetKey)) == "N"
         lMoreSamples := .F.
      ENDIF
   ENDDO
   RETURN NIL




/***
*  PrintIt( <cString> ) --> NIL
*  Print a string, then send a CRLF
*/
STATIC FUNCTION PrintIt( cString )
   IF cString == NIL
      cString := ""
   ENDIF
   QQOUT( cString )
   QOUT()
   RETURN NIL

                                        
                                      * * *
