/* r4report.c   (c)Copyright Sequiter Software Inc., 1991-1994.  All rights reserved. */
#include "d4all.h"
#ifdef S4WINDOWS
#include <commdlg.h>
#endif

#define ABS(x)                ((x<0)?-1*(x):x)

void    S4FUNCTION expr4calc_reset( CODE4 S4PTR * ) ;

/* Function: report4index_type()
   PARAMETERS: none
   DESCRIPTION: used by the CR executable to determine the index
   RETURNS: an integer value, defined in r4report.h, specifying the index */

int S4FUNCTION report4index_type()
{
#ifdef S4MDX
  return r4mdx;
#endif

#ifdef S4NDX
  return r4ndx;
#endif

#ifdef S4CLIPPER
  return r4cli;
#endif

#ifdef S4FOX
  return r4fox;
#endif
}

/* FOR A DESCRIPTION SEE THE CODEREPORTER MANUAL */
PREPORT4 S4FUNCTION  report4init( RELATE4 *relate )
{
  REPORT4 *r4 ;
  CODE4 *code_base;
#ifdef S4WINDOWS
  LOGFONT lfont;
#endif

  if( !relate )
  {
    return NULL;
  }

  code_base = relate->code_base;

  /* you can only have one report open at a time */
  if( code_base->num_reports != 0 )
  {
    e4describe( code_base, e4report, E4_REP_ONE, 0, 0 );
    return NULL;
  }

  /* allocate the REPORT4 structure */
  r4 =  (REPORT4 *) u4alloc_free( code_base, sizeof(REPORT4) ) ;
  if ( r4 == 0 )
  {
    e4describe( code_base, e4report, E4_REP_REPALLOC, 0, 0 );
    return NULL ;
  }

  /* reset the list members */
  memset( &r4->groups, 0, sizeof(r4->groups) );
  memset( &r4->styles, 0, sizeof(r4->styles) );
  memset( &r4->active_objects, 0, sizeof(r4->active_objects) );
  memset( &r4->obj_type_info, 0, sizeof(r4->obj_type_info) );

  /* set the code_base, relate, and default_date_format members */
  r4->code_base =  code_base ;
  r4->relate =  relate ;
#ifdef S4WINDOWS
  lstrcpy( r4->default_date_format, code_base->date_format );
#else
  strcpy( r4->default_date_format, code_base->date_format );
#endif

  /* turn off auto_open of index files */
  r4->code_base->auto_open = 0;

  /* set the default margin and page size */
  r4->margin_left = 250;
  r4->margin_right = 250;
  r4->report_width =  8000 ;
  r4->report_height = 11000;

  /* set the default symbols */
  r4->decimal_point =  '.' ;
  r4->thousand_separator = ',' ;
  r4->currency_symbol[0] = '$';
  r4->currency_symbol[1] = '\0';

  /* by default output is to the screen */
  r4->output_code = r4->output_handle = 1;

  /* default units of display are points: applys to the CR executable */
  r4->units =  UNIT4POINTS ;

  /* no grid by default: applys to the CR executable */
  r4->sensitivity_x = r4->sensitivity_y = 0;

  /* by default save report with pathnames */
  r4->pathnames = 1;

  /* create the default style */
#ifdef S4WINDOWS
  /* if not being called by the CR executable */
  if( !code_base->s4cr2 )
  {
    memset( &lfont, 0, sizeof(R4LOGFONT) );
    strcpy(lfont.lfFaceName,"MS Serif" );
#ifdef S4GERMAN
    style4create( (PREPORT4)r4, (PR4LOGFONT)&lfont, (char *)"Normal", (R4COLORREF)NULL, (int)10 );
#else
    style4create( (PREPORT4)r4, (PR4LOGFONT)&lfont, (char *)"Plain Text", (R4COLORREF)NULL, (int)10 );
#endif
    r4->active_style = (PSTYLE4)l4first( &r4->styles );
  }
#else
#ifdef S4GERMAN
  style4create( r4, "Normal", 0, NULL, 0, NULL );
#else
  style4create( r4, "Plain Text", 0, NULL, 0, NULL );
#endif
  r4->active_style = (PSTYLE4)l4first( &r4->styles );
#endif

  /* create the title/summary and page header/footer groups */
  group4create_title_summary( r4 );
  group4create_pgheader_pgfooter( r4 );

  if( r4 )
    r4->code_base->num_reports = 1;
  else
    r4->code_base->num_reports = 0;

  return r4 ;
}

#ifdef S4WINDOWS
/* frees the windows font associated with the object by releasing the
   font from the objects windows device context.  This function is
   only used through the report4free_styles fctn */
void report4set_obj_font_free( OBJ4 *obj )
{
  OBJ4 *obj_on;
  HDC hDC;

  obj_on = (OBJ4 *)l4first( &obj->contained );
  while( obj_on )
  {
    report4set_obj_font_free( obj_on );
    obj_on = (OBJ4 *)l4next( &obj->contained, obj_on );
  }

  hDC = GetDC( obj->hWnd );
  SelectObject( hDC, GetStockObject(SYSTEM_FONT) );
  ReleaseDC( obj->hWnd, hDC );
}

#endif

void report4free_styles( REPORT4 *report )
{
#ifdef S4WINDOWS
  GROUP4 *group_on;
  AREA4 *area_on;
  OBJ4 *obj_on;
#endif
  STYLE4 *style_on, *style_next;

  /* if this is a report in windows de-select the font from the device context
     of every object */
#ifdef S4WINDOWS
  if( report->page_header_footer )
  {
    area_on = (AREA4 *)l4first( &report->page_header_footer->header_areas );
    while( area_on )
    {
      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
        report4set_obj_font_free( obj_on );
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &report->page_header_footer->header_areas, area_on );
    }

    area_on = (AREA4 *)l4first( &report->page_header_footer->footer_areas );
    while( area_on )
    {
      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
        report4set_obj_font_free( obj_on );
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &report->page_header_footer->footer_areas, area_on );
    }
  }

  group_on = (GROUP4 *)l4first( &report->groups );
  while( group_on )
  {
    area_on = (AREA4 *)l4first( &group_on->header_areas );
    while( area_on )
    {
      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
        report4set_obj_font_free( obj_on );
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &group_on->header_areas, area_on );
    }

    area_on = (AREA4 *)l4first( &group_on->footer_areas );
    while( area_on )
    {
      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
        report4set_obj_font_free( obj_on );
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &group_on->footer_areas, area_on );
    }
    group_on = (GROUP4 *)l4next( &report->groups, group_on );
  }
#endif

  /* free the STYLE4 structures in the style list */
  style_on = (STYLE4 *)l4first( &report->styles );
  while( style_on )
  {
    style_next = (STYLE4 *)l4next( &report->styles, style_on );
    style4free( report, style_on );
    style_on = style_next;
  }
}


/* SEE the CODEREPORTER MANUAL FOR A DESCRIPTION */
void S4FUNCTION   report4free( REPORT4 *r4, int free_relate, int close_files )
{
  GROUP4 *group_on, *group_next;
  RELATE4 *relate;
  POUT4OBJ o4obj;

  if ( r4 == 0 )  return ;

  if( r4->code_base->num_reports != 1 )
  {
    e4describe( r4->code_base, e4report, E4_REP_NONE, 0, 0 ) ;
  }

  r4->code_base->num_reports = 0 ;

  /* free the report name and file_name */
  if( r4->report_name )
    u4free( r4->report_name );

  if( r4->report_file_name )
    u4free( r4->report_file_name );

  /* free the reports styles */
  report4free_styles( r4 );

  /* free the page header/footer group */
  if( r4->page_header_footer )
  {
    group4free( r4->page_header_footer );
    r4->page_header_footer = NULL;
  }

  /* free all the other groups */
  group_on = (GROUP4 *)l4first( &r4->groups );
  while( group_on )
  {
    group_next = (GROUP4 *)l4next( &r4->groups, group_on );
    group4free( group_on );
    group_on = group_next;
  }

  /* free the additional record buffers in the DATA4 structures in the relation
   */
  relate = r4->relate;
  while( relate )
  {
    if( relate->old_record )
    {
      u4free( relate->old_record );
      relate->old_record = NULL;
    }
    relate4next( &relate );
  }

  /* if the free relate flag was set, free the relate */
  if (free_relate)
    relate4free(r4->relate, close_files);

  /* free the printer name */
  if( r4->printer_name )
    u4free( r4->printer_name );

  /* free all calculations/totals in the calc list */
  expr4calc_reset( r4->code_base ) ;

  /* free up the memory pools for totals and calcs */
  mem4release( r4->code_base->total_memory ) ;
  r4->code_base->total_memory =  0 ;
  mem4release( r4->code_base->calc_memory ) ;
  r4->code_base->calc_memory =  0 ;

  /* free the report caption */
  if( r4->report_caption )
  {
    u4free( r4->report_caption );
  }

  /* free the output data file name */
  if( r4->dfile_name )
    u4free( r4->dfile_name );

  /* free any data file output OUT4OBJ structs */
  while( (o4obj = (POUT4OBJ)l4pop(&r4->output_objs)) != NULL )
    u4free( o4obj );

  u4free( r4 ) ;
}

#ifdef S4WINDOWS
/* Get an information context for either the specified or default printer */
HDC S4FUNCTION report4get_printerIC ( REPORT4 *report )
{
  char szPrinter [64] ;
  char *szDevice, *szDriver, *szOutput ;

  if ( report->printer_name == 0 )
  {
    GetProfileString( "windows", "device", ",,,", szPrinter, 80 ) ;

    if ( ! (szDevice = strtok( szPrinter, ",")) )  return 0 ;
    if ( ! (szDriver = strtok( NULL, ", ")) ) return 0 ;
    if ( ! (szOutput = strtok( NULL, ", ")) ) return 0 ;
  }
  else
  {
    GetProfileString( "devices", report->printer_name, "=,", szPrinter, 80) ;
    szDevice = report->printer_name ;
    if ( ! (szDriver = strtok( szPrinter, ", ")) ) return 0 ;
    if ( ! (szOutput = strtok( NULL, ", ")) ) return 0 ;
  }

  return CreateIC (szDriver, szDevice, szOutput, NULL) ;
}

/* Get the device context for the default printer */
HDC S4FUNCTION report4get_printerDC ( void )
{
  static char szPrinter [80] ;
  char *szDevice, *szDriver, *szOutput ;

  GetProfileString( "windows", "device", ",,,", szPrinter, 80 ) ;

  if ( ! (szDevice = strtok( szPrinter, ",")) )  return 0 ;
  if ( ! (szDriver = strtok( NULL, ", ")) ) return 0 ;
  if ( ! (szOutput = strtok( NULL, ", ")) ) return 0 ;

  return CreateDC (szDriver, szDevice, szOutput, NULL) ;
}

/* Create the output fonts for printing */
void report4output_screen_fonts( REPORT4 *report )
{
  STYLE4 *style_on;
#ifdef S4WIN32
  LOGFONT *plf;
#else
  PR4LOGFONT plf;
#endif
  int tempval;

  style_on = (STYLE4 *)l4first( &report->styles );
  while( style_on )
  {
    if( !style_on->printer_font )
    {
#ifdef S4WIN32
      plf = &( style_on->ntFont ) ;
#else
      plf = &( style_on->lfont ) ;
#endif
      /* due to the different resolution of the printer and screen we have
         to temporarily change the lfHeight member of the LOGFONT struct and
         create a font for printing */
      tempval = plf->lfHeight;
      plf->lfHeight = -MulDiv(style_on->point_size,
                              GetDeviceCaps(report->printerDC,LOGPIXELSY),72);

      style_on->printer_font = CreateFontIndirect((const LOGFONT FAR*)plf);
      plf->lfHeight = tempval;

    }
    style_on = (STYLE4 *)l4next( &report->styles, style_on );
  }
}


/* USED FOR CREATING A DIALOG TEMPLATE ON THE FLY, this is used to create
   the dialog used during printing in windows to allow cancellation of the
   printing process, the only reason this function is necessary is because
   we do not use a resource file with the CodeBase DLL */
GLOBALHANDLE NewDlgTemplate(DWORD Style, WORD X, WORD Y, WORD CX, WORD CY,
                            LPSTR Menu, LPSTR Class, LPSTR Text,
                            LPSTR TypeFace, WORD PtSize)
{
  DlgTemplateHeader far *lpDT;
  LPSTR lpsztemp;
  GLOBALHANDLE hDTemplate;
  long needed_size;

  needed_size = sizeof( DlgTemplateHeader ) + lstrlen(Menu) + 1 +
    lstrlen(Class) + 1 + lstrlen(Text) + 1;

  if( TypeFace[0] )
    needed_size += lstrlen(TypeFace) + 1 + sizeof(short);

  hDTemplate = GlobalAlloc( GMEM_MOVEABLE | GMEM_ZEROINIT, needed_size );

  if( hDTemplate == NULL )
    return hDTemplate;

  lpDT = (DlgTemplateHeader far * )GlobalLock( hDTemplate );

  lpDT->dtStyle = WS_VISIBLE | Style;
  lpDT->dtX = X;
  lpDT->dtY = Y;
  lpDT->dtCX = CX;
  lpDT->dtCY = CY;
  lpsztemp = ((LPSTR)lpDT) + sizeof( *lpDT );
  lpsztemp = (char far *)memcpy( lpsztemp, Menu, ((lstrlen(Menu)>80)?81:lstrlen(Menu)+1) );
  lpsztemp = (char far *)memcpy( lpsztemp, Class, ((lstrlen(Class)>80)?81:lstrlen(Class)+1)  );
  lpsztemp = (char far *)memcpy( lpsztemp, Text, ((lstrlen(Text)>80)?81:lstrlen(Text)+1)  );

  if( TypeFace[0] )
  {
    *((short far *)lpsztemp) = PtSize;
    memcpy( lpsztemp+sizeof(short), TypeFace,((lstrlen(TypeFace)>80)?81:lstrlen(TypeFace)+1)   );
    lpDT->dtStyle |= DS_SETFONT;
  }

  GlobalUnlock( hDTemplate );

  return hDTemplate;
}

/* USED FOR CREATING A DIALOG TEMPLATE ON THE FLY, this is used to create
   the dialog used during printing in windows to allow cancellation of the
   printing process, the only reason this function is necessary is because
   we do not use a resource file with the CodeBase DLL */
BOOL AddDlgItem( GLOBALHANDLE DlgTmp, DWORD Style, WORD X, WORD Y, WORD CX,
                WORD CY, LPSTR Class, LPSTR Text, BYTE DataLen, LPSTR Data,
                int id )
{
  DlgTemplateHeader far *lpDT;
  DlgItemTemplateHeader far *lpDIT;
  LPSTR lpsztemp;
  GLOBALHANDLE hDTemplate;
  unsigned new_size, BlockSize;

  BlockSize = TemplateSize( DlgTmp );

  new_size = BlockSize + sizeof(DlgItemTemplateHeader) +
    lstrlen(Class) + 1 + lstrlen(Text) + 1 + 1;

  hDTemplate = GlobalReAlloc( DlgTmp, new_size, GMEM_ZEROINIT | GMEM_MOVEABLE );

  if( hDTemplate == NULL )
    return FALSE;

  lpDT = (DlgTemplateHeader far * )GlobalLock( hDTemplate );

  lpDIT = (DlgItemTemplateHeader far *)(((LPSTR)lpDT)+BlockSize);
  lpDIT->dtilX = X;
  lpDIT->dtilY = Y;
  lpDIT->dtilCX = (CX < lpDT->dtCX)?CX:lpDT->dtCX;
  lpDIT->dtilCY = (CY < lpDT->dtCY)?CY:lpDT->dtCY;
  lpDIT->dtilID = id;
  lpDIT->dtilStyle = WS_VISIBLE | WS_CHILD | Style;

  lpsztemp = (char far *)(lpDIT)+sizeof(*lpDIT);
  lpsztemp = (char far *)memcpy( lpsztemp, Class, ((lstrlen(Class)>80)?81:lstrlen(Class)+1)  );
  lpsztemp = (char far *)memcpy( lpsztemp, Text, ((lstrlen(Text)>80)?81:lstrlen(Text)+1)  );

  if( DataLen )
    memcpy( lpsztemp, Data, DataLen );

  lpDT->dtItemCount++;

  GlobalUnlock( hDTemplate );

  return TRUE;
}

/* USED FOR CREATING A DIALOG TEMPLATE ON THE FLY, this is used to create
   the dialog used during printing in windows to allow cancellation of the
   printing process, the only reason this function is necessary is because
   we do not use a resource file with the CodeBase DLL */
int TemplateSize( GLOBALHANDLE hDlgTmp )
{
  DlgTemplateHeader far *lpDT;
  LPSTR lpstr, lpbase;
  int isize;
  int i;

  lpDT = (DlgTemplateHeader far *)GlobalLock( hDlgTmp );
  lpbase = (LPSTR)lpDT;
  lpstr = (LPSTR)( lpDT + 1 );
  isize = sizeof( *lpDT );
  lpstr = lpbase+(isize+=(lstrlen(lpstr)+1));
  lpstr = lpbase+(isize+=(lstrlen(lpstr)+1));
  lpstr = lpbase+(isize+=(lstrlen(lpstr)+1));

  if((lpDT->dtStyle)&DS_SETFONT)
  {
    isize += sizeof(short);
    lpstr += sizeof(short);
    lpstr = lpbase+(isize+=(lstrlen(lpstr)+1));
  }

  for( i = 0; i < lpDT->dtItemCount; i++ )
  {
    lpstr = lpbase+(isize+=sizeof(DlgItemTemplateHeader));
    lpstr = lpbase+(isize+=(lstrlen(lpstr)+1));
    lpstr = lpbase+(isize+=(lstrlen(lpstr)+1));
    if( *lpstr )
    {
      isize += *lpstr;
      lpstr += *lpstr;
    }
    isize++;
  }

  GlobalUnlock( hDlgTmp );
  return isize;
}

/**************************************************************************
 * registers window classes for the output window and the page window
 **************************************************************************/
int report4register_classes( PREPORT4 report )
{
  WNDCLASS wc;
  HINSTANCE reginst;

  if( report->hWnd == NULL )
  {
#ifdef S4RDB
    MessageBox( NULL, "No parentwindow in report structure.", "Error", MB_OK );
#endif
    return -1;
  }
#ifdef S4WIN32
  reginst = (HINSTANCE)GetWindowLong( report->hWnd, GWL_HINSTANCE );
#else
  reginst = (HINSTANCE)GetWindowWord( report->hWnd, GWW_HINSTANCE );
#endif
  wc.cbClsExtra     = 0;
  wc.hInstance      = reginst;
  wc.hCursor        = LoadCursor(NULL,IDC_WAIT);
  wc.hbrBackground  = GetStockObject(LTGRAY_BRUSH);
  wc.lpszMenuName   = NULL;
  wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  wc.lpfnWndProc = PreViewProc;
  wc.cbWndExtra = 18;
  wc.hIcon = NULL;
  wc.lpszClassName = "PreViewWin";
#ifdef S4RDB
  if( (RegisterClass( &wc )) == NULL )
  {
    MessageBox( NULL, "Failed to register PreViewWin class", "ERROR", MB_OK );
    return -1;
  }
#else
  RegisterClass( &wc );
#endif

  wc.hbrBackground = GetStockObject( WHITE_BRUSH );
  wc.lpfnWndProc   = OutputPageProc;
  wc.lpszClassName = "OutputPageWin";
#ifdef S4RDB
  if( (RegisterClass( &wc )) == NULL )
  {
    MessageBox( NULL, "Failed to register OutputPageWin class", "ERROR", MB_OK );
    return -1;
  }
#else
  RegisterClass( &wc );
#endif

  wc.lpszClassName = "MouseEat";
  wc.lpfnWndProc   = MouseEatProc;
#ifdef S4RDB
  if( (RegisterClass( &wc )) == NULL )
  {
    MessageBox( NULL, "Failed to register MouseEat class", "ERROR", MB_OK );
    return -1;
  }
#else
  RegisterClass( &wc );
#endif

  return 0;
}

#endif

/****************************************************************************
 * recursively calculates the position (x,y) and size (w,h) of an object and
 * all the contained objects in device coordinates
 ****************************************************************************/

#ifdef S4WINDOWS
#ifdef __cplusplus
void report4obj_to_dev(OBJ4 *obj, HDC hDC )
#else
  void report4obj_to_dev(obj, hDC )
  OBJ4 *obj ;
  HDC hDC  ;
#endif
#else
#ifdef __cplusplus
  void report4obj_to_dev(OBJ4 *obj )
#else
  void report4obj_to_dev(obj )
  OBJ4 *obj  ;
#endif
#endif
{
  OBJ4 *obj_on;
#ifdef S4WINDOWS
  POINT pt[2];

  /* use the device context */
  pt[0].x = (int)obj->x;
  pt[0].y = (int)obj->y;
  pt[1].x = (int)obj->w;
  pt[1].y = (int)obj->h;
  LPtoDP( hDC, pt, 2 );
  obj->dev_x = pt[0].x;
  obj->dev_y = ABS(pt[0].y);
  obj->dev_w = pt[1].x;
  obj->dev_h = ABS(pt[1].y);
#else
  /* in the non-windows case we simply transfer the 1000's of an inch
     values */
  obj->dev_x = (obj->x);
  obj->dev_y = (obj->y);
  obj->dev_w = (obj->w);
  obj->dev_h = (obj->h);
#endif

  obj_on = (OBJ4 *)l4first( &obj->contained );
  while( obj_on )
  {
#ifdef S4WINDOWS
    report4obj_to_dev( obj_on, hDC );
#else
    report4obj_to_dev( obj_on );
#endif
    obj_on = (OBJ4 *)l4next( &obj->contained, obj_on );
  }
  return;
}

/**************************************************************************
 * calculates group and object sizes in device coordinates
 ***************************************************************************/
#ifdef S4WINDOWS
#ifdef __cplusplus
void report4calc_obj_dev(REPORT4 *report, HDC hDC )
#else
  void report4calc_obj_dev(report, hDC )
  REPORT4 *report ;
  HDC hDC  ;
#endif
#else
#ifdef __cplusplus
  void report4calc_obj_dev(REPORT4 *report )
#else
  void report4calc_obj_dev(report )
  REPORT4 *report  ;
#endif
#endif
{
  GROUP4 *group_on;
  AREA4 *area_on;
  OBJ4 *obj_on;
#ifdef S4WINDOWS
  POINT pt[2];

  SetMapMode( hDC, MM_HIENGLISH );
  SetMapMode( hDC, MM_ANISOTROPIC );

  if( report->output_code == 0 )
  {
    pt[0].x = (int)(report->margin_left - report->hard_margin_left);
    pt[0].y = (int)(report->margin_top - report->hard_margin_top);
    pt[1].x = (int)(report->margin_right - report->hard_margin_right);
    pt[1].y = (int)(report->margin_bottom - report->hard_margin_bottom);
  }
  else
  {
    pt[0].x = (int)(report->margin_left);
    pt[0].y = (int)(report->margin_top);
    pt[1].x = (int)(report->margin_right);
    pt[1].y = (int)(report->margin_bottom);
  }
  LPtoDP( hDC, pt, 2 );
  report->dev_margin_left = pt[0].x;
  report->dev_margin_top  = ABS(pt[0].y);
  report->dev_margin_right = pt[1].x;
  report->dev_margin_bottom = ABS(pt[1].y);
#else
  report->dev_margin_left = (report->margin_left);
  report->dev_margin_right = (report->margin_right);
  report->dev_margin_top = (report->margin_top);
  report->dev_margin_bottom = (report->margin_bottom);
#endif

  group_on = report->page_header_footer;
  if( group_on )
  {
    area_on = (AREA4 *)l4first( &group_on->header_areas );
    while( area_on )
    {
#ifdef S4WINDOWS
      pt[0].x = 0;
      pt[0].y = (UINT)area_on->height;
      LPtoDP( hDC, pt, 1 );
      area_on->height_dev = ABS(pt[0].y);
#else
      area_on->height_dev = (area_on->height);
#endif
      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
#ifdef S4WINDOWS
        report4obj_to_dev( obj_on, hDC );
#else
        report4obj_to_dev( obj_on );
#endif
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &group_on->header_areas, area_on );
    }

    area_on = (AREA4 *)l4first( &group_on->footer_areas );
    while( area_on )
    {
#ifdef S4WINDOWS
      pt[0].x = 0;
      pt[0].y = (UINT)area_on->height;
      LPtoDP( hDC, pt, 1 );
      area_on->height_dev = ABS(pt[0].y);
#else
      area_on->height_dev = (area_on->height);
#endif

      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
#ifdef S4WINDOWS
        report4obj_to_dev( obj_on, hDC );
#else
        report4obj_to_dev( obj_on );
#endif
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &group_on->footer_areas, area_on );
    }
  }

  group_on = (GROUP4 *)l4first( &report->groups );
  while( group_on )
  {
    area_on = (AREA4 *)l4first( &group_on->header_areas );
    while( area_on )
    {
#ifdef S4WINDOWS
      pt[0].x = 0;
      pt[0].y = (UINT)area_on->height;
      LPtoDP( hDC, pt, 1 );
      area_on->height_dev = ABS(pt[0].y);
#else
      area_on->height_dev = (area_on->height);
#endif

      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
#ifdef S4WINDOWS
        report4obj_to_dev( obj_on, hDC );
#else
        report4obj_to_dev( obj_on );
#endif
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &group_on->header_areas, area_on );
    }

    area_on = (AREA4 *)l4first( &group_on->footer_areas );
    while( area_on )
    {
#ifdef S4WINDOWS
      pt[0].x = 0;
      pt[0].y = (UINT)area_on->height;
      LPtoDP( hDC, pt, 1 );
      area_on->height_dev = ABS(pt[0].y);
#else
      area_on->height_dev = (area_on->height);
#endif

      obj_on = (OBJ4 *)l4first( &area_on->objects );
      while( obj_on )
      {
#ifdef S4WINDOWS
        report4obj_to_dev( obj_on, hDC );
#else
        report4obj_to_dev( obj_on );
#endif
        obj_on = (OBJ4 *)l4next( &area_on->objects, obj_on );
      }
      area_on = (AREA4 *)l4next( &group_on->footer_areas, area_on );
    }
    group_on = (GROUP4 *)l4next( &report->groups, group_on );
  }
}


/* make a call to the relate4query_set() function */
int S4FUNCTION report4querySet( PREPORT4 report, char S4PTR *expr )
{
  return relate4query_set( report->relate, expr );
}

/* make a call to the relate4sort_set() function */
int S4FUNCTION report4sortSet( PREPORT4 report, char S4PTR *expr )
{
  return relate4sort_set( report->relate, expr );
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4caption( PREPORT4 report, char S4PTR *caption )
{
  if( report == NULL )
  {
    return -1;
  }

  if( report->report_caption )
    u4free( report->report_caption );

  report->report_caption = NULL;

  if( !caption )
    return 0;

  report->report_caption = (char *)u4alloc_free( report->code_base, strlen(caption)+1 );
  if( !report->report_caption )
  {
    e4describe( report->code_base, e4report, E4_REP_CAPALLOC, 0, 0 );
    return -1;
  }

  strcpy( report->report_caption, caption );

  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4groupHardResets( PREPORT4 report, int reset_flag )
{
  int temp;

  if( !report )
    return -1;

  if( reset_flag < 0 )
  {
    e4describe( report->code_base, e4parm, E4_REP_BADRESET, 0, 0 );
    return -1;
  }

  temp = report->hard_resets_flag;
  report->hard_resets_flag = reset_flag;

  return temp;
}

#ifdef S4WINDOWS
/* Calls the printer setup/seletion common dialog in Windows */
void S4FUNCTION report4printerSelect( PREPORT4 report )
{
  static PRINTDLG pd;

  memset( &pd, 0, sizeof(PRINTDLG) );
  pd.lStructSize = sizeof( PRINTDLG );
  pd.hwndOwner = report->hWnd;
  pd.Flags = PD_RETURNDC | PD_PRINTSETUP;
  pd.hDevMode = NULL;
  pd.hDevNames = NULL;

  if( PrintDlg( &pd ) != 0 )
  {
    report->printerDC = pd.hDC;
  }

}

#endif

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4output( PREPORT4 report, int output_handle, int use_styles )
{
  if( ! report )
  {
    return -1;
  }

  report->output_handle = output_handle;
  report->use_styles    = use_styles;

  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4pageSize( PREPORT4 report, long pg_height, long pg_width, int unit_type )
{
  long pgh = pg_height, pgw = pg_width;

  if( !report )
  {
    return -1;
  }

  if( unit_type )
  {
    pgw = (long)( pg_width * 100 );
    pgh = (long)(((float)pg_height * 1000.0)/6.0 + 0.5 );
  }

  report->report_width = pgw- report->margin_left - report->margin_right;
  report->report_height = pgh - report->margin_top - report->margin_bottom;

  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4margins( PREPORT4 report, long left, long right, long top, long bottom, int unit_type )
{
  long rm = right, lm = left, tm = top, bm = bottom;

  if( !report )
  {
    /*error message; */
    return -1;
  }

  if( unit_type )
  {
    rm = right * 100;
    lm = left * 100;
    tm = (int)(((float)top*1000.0)/6.0);
    bm = (int)(((float)bottom*1000.0)/6.0);
  }

  report->report_width = report->report_width + report->margin_left + report->margin_right;
  report->report_height = report->report_height + report->margin_top + report->margin_bottom;

  report->margin_right = rm;
  report->margin_left  = lm;
  report->margin_top   = tm;
  report->margin_bottom = bm;

  report->report_width = report->report_width - rm - lm;
  report->report_height = report->report_height - tm - bm;

  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4currency( PREPORT4 report, char *currency )
{
  if( !report )
  {
    return -1;
  }

  if( !currency )
    report->currency_symbol[0] = '\0';
  else
    u4ncpy( report->currency_symbol, currency, sizeof(report->currency_symbol) );
  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4decimal( PREPORT4 report, char decimal )
{
  if( !report )
  {
    return -1;
  }

  report->decimal_point = decimal;
  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4separator( PREPORT4 report, char separator )
{
  if( !report )
  {
    return -1;
  }

  report->thousand_separator = separator;
  return 0;
}


/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4titlePage( PREPORT4 report, int title_page )
{
  int temp;

  if( !report )
  {
    return -1;
  }

  if( title_page < 0 )
  {
    e4describe( report->code_base, e4parm, E4_REP_BADTITLE, 0, 0 );
    return -1;
  }

  temp = report->pgbrk_title;
  report->pgbrk_title = title_page;

  return temp;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4toScreen( PREPORT4 report, int to_screen )
{
  if( !report )
  {
    return -1;
  }

  if( to_screen < 0 )
  {
    e4describe( report->code_base, e4parm, E4_REP_BADSCREEN, 0, 0 );
    return -1;
  }

  if( to_screen > 0 )
    report->output_code = to_screen;
  else
    report->output_code = 0;

  return 0;
}

#ifdef S4WINDOWS
/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4parent( PREPORT4 report, HWND parent )
{
  if( !report )
  {
    return -1;
  }

  report->hWnd = parent;
  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
HDC S4FUNCTION report4printerDC( PREPORT4 report, HDC hDC )
{
  HDC temp;

  if( !report )
  {
    return (HDC)-1;
  }

  temp = report->printerDC;
  report->printerDC = hDC;

  return temp;
}
#endif

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4dateFormat( PREPORT4 report, char *date_format  )
{
  if( !report || !date_format )
  {
    return -1;
  }

  u4ncpy( report->default_date_format, date_format, sizeof(report->default_date_format) );
  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4pageSizeGet( PREPORT4 report, long *page_width,
                                  long *page_height )
{
  if( !report )
  {
    return -1;
  }

  *page_width = report->dev_page_width;
  *page_height = report->dev_page_height;

  return 0;
}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4pageMarginsGet( PREPORT4 report, long S4PTR *margin_left,
                                     long S4PTR *margin_right, long S4PTR *margin_top, long S4PTR *margin_bottom )
{
  if( !report )
  {
    return -1;
  }

  *margin_right = report->dev_margin_right;
  *margin_left = report->dev_margin_left;
  *margin_bottom = report->dev_margin_bottom;
  *margin_top = report->dev_margin_top;

  return 0;

}

/* SEE THE CODEREPORTER MANUAL */
int S4FUNCTION report4dataDo( PREPORT4 report )
{
  char *f4info;
  FIELD4INFO *f4infoi;
  DATA4 *dfile;
  POUT4OBJ oobj;
  long bsize = 8198;
  int  rc, i, trl;
  PGROUP4 group;

  if( !report->output_group || !report->dfile_name || report->output_objs.n_link <= 0 )
  {
    e4describe( report->code_base, e4rep_data, E4_REP_NODSETUP, (char *)0, (char *)0 );
    return -1;
  }

  /* allocate an array of FIELD4INFO structures */
  f4info = (char *)u4alloc_er( report->code_base, (report->output_objs.n_link+1)*sizeof(FIELD4INFO) );
  if( !f4info )
  {
    e4describe( report->code_base, e4rep_data, E4_REP_NODCREATE, report->dfile_name, (char *)0 );
    return -1;
  }

  /* fill the FIELD4INFO structures */
  oobj = NULL;
  for( i = 0; i < report->output_objs.n_link; i++ )
  {
    f4infoi = (FIELD4INFO *)(f4info + i * sizeof(FIELD4INFO) );
    oobj = (POUT4OBJ)l4next( &report->output_objs, oobj );
    f4infoi->name = (char *)u4alloc_free( report->code_base, strlen(oobj->obj->field_name)+1 );
    strcpy( f4infoi->name, oobj->obj->field_name );
    f4infoi->type = oobj->obj->field_type;
    f4infoi->len = oobj->obj->field_len;
    f4infoi->dec = oobj->obj->field_dec;
  }

  trl = report->code_base->read_only;
  report->code_base->read_only = 0;

  /* create the new data file */
  dfile = d4create( report->code_base, report->dfile_name, (FIELD4INFO *)f4info, NULL );
  if( !dfile )
  {
    e4describe( report->code_base, e4rep_data, E4_REP_NODCREATE, report->dfile_name, (char *)0 );
    u4free( f4info );
    report->code_base->read_only = trl;
    return -1;
  }
  report->code_base->read_only = trl;

  /* associate the fields in the new data file with the output objects in the
     report */
  oobj = NULL;
  for( i = 0; i < report->output_objs.n_link; i++ )
  {
    oobj = (POUT4OBJ)l4next( &report->output_objs, oobj );
    oobj->obj->field = d4field( dfile, oobj->obj->field_name );
  }

  /* temporarily remove all the internal group flags */
  group = (PGROUP4) l4first( &report->groups );
  while( group )
  {
    group->trepeat_header = group->repeat_header;
    group->tswap_header = group->swap_header;
    group->tswap_footer = group->swap_footer;
    group->treset_page = group->reset_page;
    group->treset_pagenum = group->reset_pagenum;

    group->repeat_header = 0;
    group->swap_header   = 0;
    group->swap_footer   = 0;
    group->reset_page    = 0;
    group->reset_pagenum = 0;
    group = (PGROUP4)l4next( &report->groups, group );
  }

  report->for_dbf = 1;
  report->data_file = dfile;
  report->rcount = 0;
  report->dfile_buf = NULL;

  /* allocate as large a buffer as you can */
  while( report->dfile_buf == NULL )
  {
    report->dfile_buf = (char *)u4alloc_free( report->code_base, bsize );
    if( report->dfile_buf == NULL )
      bsize -= 1000;
    if( bsize <= 0 )
      break;
  }
  if( report->dfile_buf == NULL )
#ifndef __SC__
    return 0;
#else
  return 0;
#endif

  /* set up the new datafile for fast writing */
  file4seq_write_init( &report->dfile_seq, &dfile->file, (long)d4record_position(dfile,1L),
                      report->dfile_buf, (unsigned)bsize );
  d4append_start( dfile, 0 );
  if( f4info )
  {
    for( i = 0; i < report->output_objs.n_link; i++ )
    {
      f4infoi = (FIELD4INFO *)(f4info + i * sizeof(FIELD4INFO) );
      u4free( f4infoi->name );
    }
    u4free( f4info );
  }

  /* start the report */
  report4pageInit( report );
#ifdef S4WINDOWS
  if( report->hWnd2 )
  {
    ReleaseCapture();
    DestroyWindow( report->hWnd2 );
    report->hWnd2 = NULL;
  }
  while( (rc = report4generatePage( report, 0 )) >= 0 )
#else
    while( (rc = report4generatePage( report )) >= 0 )
#endif
    {
      if( rc == 2 )
        break;
    }
  /* clean up after the report */
  report4pageFree( report );

  report->for_dbf = 0;

  /* flush the buffer to the new data file */
  file4seq_write_flush( &report->dfile_seq );
  report->data_file->file.is_exclusive = 1;
  report->data_file->num_recs = report->rcount;
  d4update_header( report->data_file, 1, 1 );
  d4close( report->data_file );

  if( report->dfile_buf )
  {
    u4free( report->dfile_buf );
    report->dfile_buf = NULL;
  }

  group = (PGROUP4) l4first( &report->groups );
  while( group )
  {
    group->repeat_header = group->trepeat_header;
    group->swap_header   = group->tswap_header;
    group->swap_footer   = group->tswap_footer;
    group->reset_page    = group->treset_page;
    group->reset_pagenum = group->treset_pagenum;

    group = (PGROUP4)l4next( &report->groups, group );
  }

  oobj = NULL;
  for( i = 0; i < report->output_objs.n_link; i++ )
  {
    oobj = (POUT4OBJ)l4next( &report->output_objs, oobj );
    oobj->obj->field = NULL;
  }


  return 0;
}

#ifdef S4VBASIC

int S4FUNCTION report4decimal_v( PREPORT4 report, char *decimal )
{
  if ( c4parm_check( report->code_base, 1, "report4decimal():" ) ) return -1 ;

#ifndef S4VB_DOS
  return report4decimal( report, (char) decimal[0] ) ;
#else
  return report4decimal( report, (char) c4str(decimal)[0] ) ;
#endif
}

int S4FUNCTION report4separator_v( PREPORT4 report, char *separator )
{
  if ( c4parm_check( report->code_base, 1, "report4decimal():" ) ) return -1 ;

#ifndef S4VB_DOS
  return report4separator( report, (char) separator[0] ) ;
#else
  return report4separator( report, (char) c4str(separator)[0] ) ;
#endif
}

#ifdef S4VB_DOS

int S4FUNCTION report4caption_v ( PREPORT4 report, char *caption )
{
  return report4caption( report, c4str(caption) ) ;
}

int S4FUNCTION report4currency_v ( PREPORT4 report, char *currency )
{
  return report4currency( report, c4str(currency) ) ;
}

int S4FUNCTION report4dateFormat_v( PREPORT4 report, char *format )
{
  return report4dateFormat( report, c4str(format) ) ;
}

int S4FUNCTION report4querySet_v( PREPORT4 report, char *query_expr )
{
  return report4querySet( report, c4str(query_expr) );
}


int S4FUNCTION report4sortSet_v( PREPORT4 report, char *sort_expr )
{
  return report4sortSet( report, c4str(sort_expr) );
}

#endif  /* S4VB_DOS */
#endif  /* S4VBASIC */


