Mensaje    Asunto: Impresión de un ListView Responder citando

Después de darle muchas vueltas, por fin he terminado una función que imprime un ListView en cualquier impresora.
Antes de nada debo agradecer a escafandra su inestimable ayuda por echarme una mano en traducir un código de delphi sin el cual no hubiese tirado adelante el proyecto

Para llamar a selprint debemos usar esto
Código:
Tselprint *selprint =new Tselprint (this);
selprint->PListView=data;  // (nombre del FListView a imprimir)
selprint->Title="Listado de datos"; // ="" si no queremos titulo
selprint->PageFormat="Página %d"; //="" si no queremos número de página
selprint->Ajust=true;  // se autoajustan las columnas o false
selprint->FicheroIni=directorio+"Mi.ini"; // para guardar el último listado
selprint->ClaveIni="ListadoCli"; // clave del ini para este listado

   try {
  selprint->ShowModal();
   } __finally {
   selprint->Free();
   }


Creo que está claro, si hay dudas preguntar
Hay que incluir el h en el cpp con use unit. Y Poner el form de selprint en available forms de las propiedades del proyecto


ahora sel_print_listview.h
Código:
//---------------------------------------------------------------------------

#ifndef print_listview_selH
#define print_listview_selH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Buttons.hpp>
#include <ComCtrls.hpp>
#include <Dialogs.hpp>
#include <ExtCtrls.hpp>
#include <inifiles.hpp>
//---------------------------------------------------------------------------
class Tselprint : public TForm
{
__published:   // IDE-managed Components
   TSpeedButton *SpeedButton1;
   TSpeedButton *SpeedButton2;
   TGroupBox *GroupBox1;
   TComboBox *printers;
   TEdit *copias;
   TRadioGroup *orienta;
   TUpDown *UpDown1;
   TStaticText *StaticText1;
   TGroupBox *selec;
   TGroupBox *sumar;

   void __fastcall FormPaint(TObject *Sender);
   void __fastcall SpeedButton1Click(TObject *Sender);
   void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
   void __fastcall SpeedButton2Click(TObject *Sender);
   void __fastcall printersChange(TObject *Sender);
   void __fastcall selecClick(TObject *Sender);

protected:
   int       FCurY;
   int       FMaxX;
   int       FMaxY;
   int       FHeight;
   int       *ColStart;
   int       *ColWidth;
   int      *ItemLen;
   double     *SumaCol;
   TCheckBox* col[50];
   TCheckBox* sum[50];

   void NewLine();
   void PrintHeader();
   void PrintLine(TListItem *AItem);
   void PrintRect(const String S, int Column);
   double ADouble(String Valor);

private:   // User declarations

public:      // User declarations

   __fastcall Tselprint(TComponent* Owner);
   TListView *PListView;
   String    Title;
   String    PageFormat;
   bool      Ajust;
   String     FicheroIni;
   String    ClaveIni;   
};
//---------------------------------------------------------------------------
extern PACKAGE Tselprint *selprint;
//---------------------------------------------------------------------------
#endif


y sel_print_listview.cpp
Código:
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "print_listview_sel.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
Tselprint *selprint;
bool selpaint=false;

//---------------------------------------------------------------------------
__fastcall Tselprint::Tselprint(TComponent* Owner)
   : TForm(Owner)
{

}
//---------------------------------------------------------------------------

void __fastcall Tselprint::FormPaint(TObject *Sender)
{
int i,c=0,count;
String Valor;
if(PListView==NULL)   return;
if(selpaint) return;
selpaint=true;
count=PListView->Columns->Count;

// ajustar ventana
count-=16; // las 4 primeras correcto
if(count>0){
      if((count/4)*4!=count) c=4;
      count=(count+c)/4; //lineas que faltan
      count*=20;
      ClientHeight+=count*2;
      Top-=count;
      selec->Height+=count;
      sumar->Height+=count;
      sumar->Top+=count;
      }
// poner las columnas
for(i=0,c=0;i<PListView->Columns->Count;i++,c++){
     if(c>3) c=0;
     col[i] = new TCheckBox(this);
     col[i]->Parent=selec;
     col[i]->Visible=true;
     col[i]->Top =i/4*20+15;
     col[i]->Width=118;
     col[i]->Left=20+c*120;
     col[i]->Checked=PListView->Columns->Items[i]->Width;
     col[i]->Caption=PListView->Columns->Items[i]->Caption;
     col[i]->Name = "col"+IntToStr(i);
     col[i]->OnClick=selecClick;
     }
// poner las columnas a sumar
for(int i=0,c=0;i<PListView->Columns->Count;i++,c++){
     if(c>3) c=0;
     sum[i] = new TCheckBox(this);
     sum[i]->Parent=sumar;
     sum[i]->Visible=true;
     sum[i]->Top =i/4*20+15;
     sum[i]->Width=118;
     sum[i]->Left=20+c*120;
     sum[i]->Checked=false;
     sum[i]->Enabled=PListView->Columns->Items[i]->Alignment==taRightJustify;
     sum[i]->Caption=PListView->Columns->Items[i]->Caption;
     sum[i]->Name = "sum"+IntToStr(i);
   }
// imprsoras
printers->Items->AddStrings(Printer()->Printers);
printers->ItemIndex=Printer()->PrinterIndex;

// abrir ini si existe
if(FicheroIni!=""){
   TIniFile *ini = new TIniFile (FicheroIni);
   Valor=ini->ReadString(ClaveIni,"printer",Printer()->Printers->Strings[Printer()->PrinterIndex]);
   orienta->ItemIndex=ini->ReadInteger(ClaveIni,"orientacion",0);
   for(i=0;i<PListView->Columns->Count;i++){
      sum[i]->Checked=ini->ReadBool(ClaveIni,"suma"+IntToStr(i),false);
      col[i]->Checked=ini->ReadBool(ClaveIni,"Col"+IntToStr(i),true);
      }
   delete (ini);
   printers->ItemIndex=printers->Items->IndexOf(Valor);
    }

}
//---------------------------------------------------------------------------

// funcion para calculos de impresión
void __fastcall Tselprint::SpeedButton1Click(TObject *Sender)
{
int I, FConvX1, FConvX2,resto,ultimo,actual;
double porpunto;
Printer()->Title=Title;
Printer()->Copies=StrToInt(copias->Text);
Printer()->Orientation=orienta->ItemIndex;
FHeight=0;
Printer()->BeginDoc();
__try{
   Printer()->Canvas->Font = PListView->Font;
   FCurY = 0;
   FMaxX = Printer()->PageWidth;
   FMaxY = Printer()->PageHeight;
   FConvX1 = Printer()->Canvas->TextWidth("M");
   FConvX2 = PListView->Canvas->TextWidth("M");
   if(ColStart) delete [] ColStart;
   ColStart = new int[PListView->Columns->Count];
   if(ColWidth) delete [] ColWidth;
   ColWidth = new int[PListView->Columns->Count];
   if(ItemLen) delete [] ItemLen;
   ItemLen = new int[PListView->Columns->Count];
   if(SumaCol) delete [] SumaCol;
   SumaCol = new double[PListView->Columns->Count];

   for (int I = 0; I < PListView->Columns->Count; I++){
      if(I == 0)   ColStart[I] = FConvX1;
      else   ColStart[I] = ColStart[I-1]+ColWidth[I-1]+FConvX1;

      ColWidth[I] = PListView->Columns->Items[I]->Width * FConvX1/FConvX2;
      }
// ajustar ancho al maximo (sobrante)
   // sumar ancho de linea

      for( int c=0;c < PListView->Columns->Count; c++){
         int cuan;
         SumaCol[c]=0;
         ItemLen[c]=0;
         if(col[c]->Checked==false){
               ItemLen[c]=0;
               ColWidth[c]=0;
               ColStart[c]= c==0 ? FConvX1 : 0;
               continue;
               }
         Printer()->Canvas->Font->Style = TFontStyles()<< fsBold;
         if(sum[c]->Checked){
               ItemLen[c]=Printer()->Canvas->TextWidth("99.999.999,99");
               ColWidth[c]=ItemLen[c];
               }
         cuan=Printer()->Canvas->TextWidth(PListView->Columns->Items[c]->Caption+"M ");
         if(ItemLen[c]<cuan) ItemLen[c]=cuan;
         Printer()->Canvas->Font->Style = TFontStyles();
// calculo del maximo de cada item
         for(int I=0;I<PListView->Items->Count;I++){
            String Valor;
            if(!c) Valor=PListView->Items->Item[I]->Caption;
            else Valor=PListView->Items->Item[I]->SubItems->Strings[c-1];
            cuan=Printer()->Canvas->TextWidth(Valor.Trim()+"M ");
            if(ItemLen[c]<cuan)   ItemLen[c]=cuan;
            if(sum[c]->Checked) SumaCol[c]+=ADouble(Valor);
            }
      }
// hacer 2 veces el reparto de la sobra
      for(int i=0; i<4; i++){   
      ultimo=0;
      actual=0;
      for (int I = 0; I < PListView->Columns->Count; I++){
            if(ItemLen[I]<ColWidth[I]) ColWidth[I]=ItemLen[I];
            else ultimo+=ColWidth[I];
            actual+=ColWidth[I];
            }

      resto=FMaxX-actual-FConvX1; // descontar una letra "M"
      porpunto=(double)resto/ultimo;
      for (int I = 0; I < PListView->Columns->Count; I++){

         if(Ajust && ItemLen[I]>ColWidth[I] && porpunto){
               ColWidth[I]+=ColWidth[I]*porpunto;
               if(ItemLen[I]<ColWidth[I]) ColWidth[I]=ItemLen[I];
               }
// reajustar el inicio de cada columna
         if(I!=0) ColStart[I] = ColStart[I - 1] + ColWidth[I - 1];
         // si no cabe en la pag, se quita
         if(ColStart[I]+ColWidth[I]>FMaxX){
                  ItemLen[I]=0;
                  ColWidth[I]=0;
                  ColStart[I]= I==0 ? FConvX1 : 0;
            }
         }
      }
// impresion
   for (int I = 0; I < PListView->Items->Count; I++){
     if(FCurY == 0) PrintHeader();
     PrintLine(PListView->Items->Item[I]);
     }
     // revisar si hay sumacols
   for (I = 0; I < PListView->Columns->Count; I++)
         if(sum[I]->Checked) break;

   if(I<PListView->Columns->Count){
      I=FCurY+4;
      FCurY+=6;
      Printer()->Canvas->Font->Style = TFontStyles()<< fsBold;
      for (int i = 0; i < PListView->Columns->Count; i++)
         if(sum[i]->Checked)
            PrintRect(FloatToStrF(SumaCol[i],ffNumber,10,2), i);
      Printer()->Canvas->Font->Style = TFontStyles();
      }
// poner las lineas
      for (int i = 0; i < PListView->Columns->Count; i++)
         if(sum[i]->Checked){
            int w=Printer()->Canvas->TextWidth(" ");
            Printer()->Canvas->Pen->Width=2;
            Printer()->Canvas->MoveTo(ColStart[i]+w, I);
            Printer()->Canvas->LineTo(ColStart[i]+ColWidth[i]-FConvX1,I);
            }
  }__finally{
   Printer()->EndDoc();
  }

Close();
}
//---------------------------------------------------------------------------

void __fastcall Tselprint::FormClose(TObject *Sender, TCloseAction &Action)
{
int i;
if(FicheroIni!=""){
   TIniFile *ini = new TIniFile (FicheroIni);
   ini->WriteString(ClaveIni,"printer",printers->Text);
   ini->WriteInteger(ClaveIni,"orientacion",orienta->ItemIndex);
   for(i=0;i<PListView->Columns->Count;i++){
      ini->WriteBool(ClaveIni,"suma"+IntToStr(i),sum[i]->Checked);
      ini->WriteBool(ClaveIni,"Col"+IntToStr(i),col[i]->Checked);
      }
   delete (ini);
   }

for(i=0;i<PListView->Columns->Count;i++){
      delete col[i];
      delete sum[i];
      }

selpaint=false;
Action=caFree;
}
//---------------------------------------------------------------------------

void __fastcall Tselprint::SpeedButton2Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
void Tselprint::NewLine()
{
  FCurY += FHeight;
  if (FCurY > FMaxY - 2 * FHeight){
   FCurY = 0;
   Printer()->NewPage();
  }
}
//---------------------------------------------------------------------------
void Tselprint::PrintHeader()
{
  TRect R;
  if (Title != "" || PageFormat != ""){
   Printer()->Canvas->Font->Size = Printer()->Canvas->Font->Size + 2;
   __try{
     FHeight = Printer()->Canvas->TextHeight(Title + PageFormat + "1234567890");
     R = Rect(ColStart[0], FCurY, FMaxX-Printer()->Canvas->TextWidth("M"), FCurY + FHeight);
     if(Title != "")
      DrawText(Printer()->Canvas->Handle, Title.c_str(), -1, &R, DT_LEFT | DT_TOP | DT_SINGLELINE);
     if (PageFormat != "")
      DrawText(Printer()->Canvas->Handle, Format(PageFormat, ARRAYOFCONST((Printer()->PageNumber))).c_str(),
             -1, &R, DT_RIGHT | DT_TOP | DT_SINGLELINE);
     NewLine();
   }__finally{
     Printer()->Canvas->Font->Size = Printer()->Canvas->Font->Size - 2;
   }
   NewLine();
  }
  FHeight = 0;
  Printer()->Canvas->Font->Style = TFontStyles()<< fsBold;
  try{
   for(int I = 0; I< PListView->Columns->Count; I++)
      PrintRect(PListView->Columns->Items[I]->Caption,I);
      NewLine();
  }__finally{
   Printer()->Canvas->Font->Style = TFontStyles();
  }
}
//---------------------------------------------------------------------------
void Tselprint::PrintLine(TListItem *AItem)
{
  String AStr;

  FHeight = 0;
  for(int I= 0; I <= AItem->SubItems->Count; I++){

   if(I==0) AStr = AItem->Caption;
   else  AStr = AItem->SubItems->Strings[I - 1];

   PrintRect(AStr,I);
   }
  NewLine();
}
//---------------------------------------------------------------------------
void Tselprint::PrintRect(const String S, int Column)
{
  static int AlignFlags[] = {DT_LEFT,DT_RIGHT  ,DT_CENTER };

  int CurHeight;
  int TwoEms;
  TRect R;
  CurHeight = Printer()->Canvas->TextHeight(S);
  TwoEms = Printer()->Canvas->TextWidth("M");
  if(CurHeight > FHeight)
   FHeight = CurHeight + 2;
  R = TRect(ColStart[Column], FCurY,
         ColStart[Column] + ColWidth[Column]-TwoEms, FCurY + CurHeight);
  if(R.Left > FMaxX || R.Right - R.Left < TwoEms)  return; //Exit;
  if(R.Right > FMaxX) R.Right = FMaxX;
  DrawText(Printer()->Canvas->Handle, S.c_str(), -1, &R,
         AlignFlags[PListView->Columns->Items[Column]->Alignment] | DT_TOP | DT_SINGLELINE);
}
//---------------------------------------------------------------------------
double Tselprint::ADouble(String Valor)
{
String Valor2;
double numero;
Valor2=StringReplace(Valor,".","",TReplaceFlags() << rfReplaceAll);
Valor=StringReplace(Valor2,",",".",TReplaceFlags() << rfReplaceAll);
numero=atof(Valor.c_str());
return (numero);
}
void __fastcall Tselprint::printersChange(TObject *Sender)
{
Printer()->PrinterIndex=printers->ItemIndex;
}
//---------------------------------------------------------------------------

// si se desactiva una columna, la suma tambien
void __fastcall Tselprint::selecClick(TObject *Sender)
{
if(!selpaint) return;
for(int i=0; i<PListView->Columns->Count ; i++){
   if(col[i]->Checked==false){
      sum[i]->Checked=false;
      sum[i]->Enabled=false;
      }
   else
   sum[i]->Enabled=PListView->Columns->Items[i]->Alignment==taRightJustify;
   }
}
//---------------------------------------------------------------------------

Si alguien quiere el proyecto completo con el dfm y los archivos cpp y h que lo diga. Por si el foro "se come" cosas.

El sistema imprime todos los items del listview referenciado, y permite seleccionar las columnas a imprimir así como hacer la suma al final si están justificados a la derecha

Esto quería pasarlo a un componente para poder usarlo con mayor facilidad, pero no he podido, o mejor dicho, no he sabido
También creo que se puede mejorar. Estoy abierto a ello

Saludos, Bosoft