unit uvisordetabla;
(***************************
Atención!!! cuando utilice este módulo, para que esté creada la instancia
DataModule2, hay que ir a Proyecto->Opciones del proyecto->Forumario e incluir
este entre los creados en forma automática en el arranque de la aplicación.
****************)
{$MODE Delphi}

interface

uses
{$IFDEF FPC-LCL}
  LResources,
{$ENDIF}
  SysUtils, Classes,
{$IFNDEF FPC-LCL}
  ImgList,
{$ENDIF}
  Controls,
  Graphics, Forms,
{$IFDEF WINDOWS}
 Windows,
 {$ELSE}
 types,
 LCLType,
 {$ENDIF}
  Dialogs,
  StdCtrls,
  ExtCtrls,
  clipbrd,
  Buttons;

type

  { TDataModule2 }

  TDataModule2 = class(TDataModule)
    ImageList1: TImageList;
    procedure DataModuleCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DataModule2: TDataModule2;

  //Los colores van como var para poder usar rgb
  clFondoFixedCells: TColor;
  clFontFixedCells: TColor;

  clFondoFilaPar: TColor;
  clFondoFilaImpar: TColor;

  clFondoFilaPar_Activa: TColor;
  clFondoFilaImpar_Activa: TColor;
  clFondoFilaPar_Inactiva: TColor;
  clFondoFilaImpar_Inactiva: TColor;

const

  iid_Borrar= 0;
  iid_BorrarDisabled= 1;
  iid_Editar= 2;
  iid_Ver= 3;
  iid_VerNaranja= 4;
  iid_Bajar= 5;
  iid_BajarDelTodo= 6;
  iid_Configurar= 7;
  iid_Email= 8;
  iid_Info= 9;
  iid_EmailDisabled= 10;
  iid_SemaforoRojo= 11;
  iid_SemaforoVerde= 12;
  iid_SubirDelTodo= 13;
  iid_Subir= 14;
  iid_Lupa= 15;
  iid_checkbox_0=16;
  iid_checkbox_1=17;
  iid_clonar= 18;
  iid_fdownload= 19;
  iid_fupload= 20;
  iid_radiobutton_0= 21;
  iid_radiobutton_1= 22;
  iid_Reset=23;// doble flechas verticales algo combadas
  iid_Lupa2 = 24; // Lupa gruesa
  iid_Editar2 = 25; //Lápiz grueso.
  iid_OrdenadoAZ = 26;
  iid_OrdenadoZA = 27;
  iid_OrdenarPor = 28;



type

  TAlineacionHorizontal = ( CAH_Izquierda, CAH_Centro, CAH_Derecha );
  TAlineacionVertical = (CAV_Abajo, CAV_Centro, CAV_Arriba );

  TCelda = class( TPanel )
  public

    //20150108@dfusco
    //A la celda se le puede asociar cualquier objeto. Por ejemplo el DataCell
    Tag:TObject;

    alinHorizontal: TAlineacionHorizontal;
    alinVertical: TAlineacionVertical;

    procedure agregar( aChild: TControl );

    // calcula el mínimo ancho y alto para alojar el contenido
    // incluyendo el cellpadding.
    procedure InnerWH( var w, h: integer );

    // posicionar y calcular el ancho necesario
    procedure PosicionarControles;
  end;

  TFila = class( TPanel )
  public

    //20150108@dfusco
    Tag:TObject;

    altoFila: integer;
    fgColor: TColor;
    celdas: array of TCelda;
    constructor Create( Owner: TComponent; nCols: integer ); reintroduce;

    // retorna -1 si no encuentra la Celda en la fila.
    function kOf( aCelda: TCelda ): integer;
  end;

  TDescriptorColumna = record
    ancho: integer; // especificado por el usuario -1 es auto
    pxAncho: integer; // asignado al ajustar anchos de todas.
  end;

  TDescriptoresColumnas = array of TDescriptorColumna;

  { TTabla }
  TTabla = class( TPanel )
  public
    nfilas, ncols: integer;
    ancho, alto: integer;
    altoFila: integer;
    descriptoresColumnas: TDescriptoresColumnas;
    filas: array of TFila;
    filas_ocultas: TList; // lista de las filas quitadas y pendientes de eliminación.
    creada: boolean;
    cellpadding, cellspacing: integer;
    anchoborde: integer;
    flg_disable_autoscroll: boolean; // en las respuestas a las acciones de los botones

    constructor Create(
          AOwner: TComponent;
                    Name: String; // nombre del componente.
//                    xpos, ypos: integer;
//                    ancho, alto: integer;
                    cellpadding, cellspacing: integer;
                    anchoborde: integer ); reintroduce;

    // borra el contenido y redimiensiona.
    // llamarlo una vez que se conozca la dimensión de la tabla
    // y en cada Polulate
    procedure ClearRedim( new_nFilas, new_nCols: integer );

    procedure Reposicionar;

    // reescribir este con el llenado de la tabla. No precisa tener esta declaración,
    // puede llamarse de otra manera o tener otra lista de parametros, pero por
    // prolijidad conviene nombrar al método Populate + algo descriptivo.
    // PEj: Populate_DS(ds: TDataSetGenerico) para popular a partir de un TDataSetGenerico
    // Polulate debe ser llamado después del Create y todas las veces que
    // se quiera refrescar la tabla.
    // Dentro de Populate, hay que llamar a ClearRedim una vez que se conozca
    // la dimensión de la tabla.
    // Luego de cargar todo el contenido delas celdas, Populate tiene que llamar
    // a Reposicionar para que se ajusten los anchos y altos de las mismas.

    //procedure Populate; virtual; abstract;

    function GetFila( kfila: integer ): TFila;
    function GetCelda( kfila, kcolumna: integer ): TCelda;
    function FindObj( kfila, kcolumna: integer; id: string ): pointer;

    procedure SetBgColorFila( kfila: integer; bgColor: TColor );
    procedure SetBgColorCelda( kfila, kcolumna: integer; bgColor: TColor );

    procedure SetFontColorFila( kfila: integer; fontColor: TColor );
    procedure SetFontColorCelda( kfila, kcolumna: integer; fontColor: TColor );

    procedure SetFontFila (kFila: integer; fontName: string; fontStyle: TFontStyles;
      fontSize: Integer);

// escribe el texto en la celda.
    procedure wrTexto( id: string; kfila, kcolumna: integer; Texto: String );

// Escribe un campo Editor
    procedure wrEdit( id: string; kfila, kcolumna: integer; Texto: String; ancho: integer; hint: string );

// Escribe un campo del tipo CheckBox
    procedure wrCheckBox(id: string; kfila, kcolumna: integer; caption: string;
      marcado: boolean; hint: string);


// Escribe un botón común
    procedure wrButton(id: string; kfila, kcolumna: integer; caption: string;
      hint: string);

// Escribe un BitBtn (botón con bitmap )
    procedure wrBitBtn( id: string; kfila, kcolumna: integer; iid: integer; hint: string='-');

// Escribe un campo del tipo ComboBox
    procedure wrComboBox( id: string; kfila, kcolumna, ancho: integer);

// Escribe un array de BitBtn en la misma celda
// los botones son creados con identificador 'btns_' + BaseId + '_' + iidx siendo iidx el
// la constante iid correspondiente del botón
// en la botonera.
    procedure wrBotonera(
      BaseId: string;
      kfila, kcolumna: integer;
      iids: array of integer;//TListActionButton; // ej [ iid_Borrar, iid_BorrarDisabled,  iid_Editar]
      hints: array of string ); // si length( hints ) <> length( iids ) no se muestran hints

    procedure AlinearCelda(
      kfil: integer;
      kCol: integer;
      AlinHorizontal: TAlineacionHorizontal;
      AlinVertical: TAlineacionVertical );

    procedure AlinearColumna(
      kCol: integer;
      AlinHorizontal: TAlineacionHorizontal;
      AlinVertical: TAlineacionVertical );

    procedure dropFila(fila: TFila);

    procedure BitBtn_OnClick( Sender: TObject); virtual;
    procedure CheckBox_OnClick( Sender: TObject); virtual;
    procedure Edit_OnChange( Sender: TObject); virtual;
    procedure Edit_OnKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState ); virtual;
    procedure Button_OnClick( Sender: TObject ); virtual;

    procedure Borrar_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure BorrarDisabled_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Editar_onClick( nidRec: string; kFila: integer ); virtual;
    procedure Ver_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure VerNaranja_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Bajar_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure BajarDelTodo_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Configurar_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Email_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Info_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure EmailDisabled_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure SemaforoRojo_OnClick( nidRec: string; kFila: integer); virtual;
    procedure SemaforoVerde_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure SubirDelTodo_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Subir_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure Lupa_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure checkbox_0_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure checkbox_1_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure clonar_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure fdownload_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure fupload_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure radiobutton_0_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure radiobutton_1_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure reset_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure lupa2_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure editar2_OnClick( nidRec: string; kFila: integer ); virtual;

    procedure OrdenadoAZ_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure OrdenadoZA_OnClick( nidRec: string; kFila: integer ); virtual;
    procedure OrdenarPor_OnClick( nidRec: string; kFila: integer ); virtual;


    // retorna el índice de la fila
    function kOf( aFila: TFila ): integer;

  end;

  TDAOfTTabla = array of TTabla;

function hintPorDefecto(iid_boton: integer): String;
function SearchComponent( AOwner: TComponent; Name: string ): TComponent;
function RemoveAndFree( AOwner: TComponent; Name: string ): boolean;

function nomChkBox(fila, col: Integer): String;

implementation
{$IFNDEF FPC-LCL}
  {$R *.dfm}
{$ELSE}
  {$R *.lfm}
{$ENDIF}



function hintPorDefecto(iid_boton: integer): String;
begin
  case iid_boton of
    iid_Borrar          : result:= 'Borrar';
    iid_BorrarDisabled  : result:= 'Borrar (Deshabilitado)';
    iid_Editar          : result:= 'Editar';
    iid_Ver             : result:= 'Ver';
    iid_VerNaranja      : result:= 'Ver';
    iid_Bajar           : result:= 'Bajar';
    iid_BajarDelTodo    : result:= 'Bajar del Todo';
    iid_Configurar      : result:= 'Configurar';
    iid_Email           : result:= 'Email';
    iid_Info            : result:= AnsiToUtf8('Información');
    iid_EmailDisabled   : result:= 'Email (Deshabilitado)';
    iid_SemaforoRojo    : result:= 'Semafaro Rojo';
    iid_SemaforoVerde   : result:= 'Semafaro Verde';
    iid_SubirDelTodo    : result:= 'Subir del Todo';
    iid_Subir           : result:= 'Subir';
    iid_Lupa            : result:= 'Lupa';
    iid_checkbox_0      : result:= 'Falso';
    iid_checkbox_1      : result:= 'Verdadero';
    iid_clonar          : result:= 'Clonar';
    iid_fdownload       : result:= 'Descargar';
    iid_fupload         : result:= 'Cargar';
    iid_radiobutton_0   : result:= 'No Seleccionado';
    iid_radiobutton_1   : result:= 'Seleccionado';
    iid_Reset           : result:= 'Reset';
    iid_Lupa2         : result:= 'Ampliar';
    iid_Editar2      : result:= 'Editar';
    iid_OrdenadoAZ  : result:= 'Ordenar decreciente';
    iid_OrdenadoZA  : result:= 'Ordenar creciente';
    iid_OrdenarPor  : result:= 'Ordenar por...';
    else
      result:= '';
  end;
end;


function kSearchComponent( AOwner: TComponent; Name: string ): integer;
var
  ac: TComponent;
  k, res: integer;
begin
  res:= -1;
  for k:= 0 to  AOwner.ComponentCount -1 do
  begin
    ac:= AOwner.Components[k];
    if ac.Name = Name then
    begin
      res:= k;
      break;
    end;
  end;
  result:= res;
end;

function SearchComponent( AOwner: TComponent; Name: string ): TComponent;
var
  k: integer;
begin
  k:= kSearchComponent( AOwner, Name );
  if k >= 0 then
    result:= AOwner.Components[k]
  else
    result:= nil;
end;

function RemoveAndFree( AOwner: TComponent; Name: string ): boolean;
var
  ac: TComponent;
begin
  ac:= SearchComponent( AOwner, Name );
  if ac <> nil then
  begin
    AOwner.RemoveComponent( ac );
    ac.Free;
    result:= true;
  end
  else
    result:= false;
end;

function nomChkBox(fila, col: Integer): String;
begin
  result:= 'chkBox_f' + IntToStr(fila) + '_c' + IntToStr(col);
end;

{ TDataModule2 }

procedure TDataModule2.DataModuleCreate(Sender: TObject);
begin

end;

procedure TTabla.dropFila(fila: TFila);
var
  kFila, i: Integer;
begin
  kFila:= -1;
  for i:= 0 to high(filas) do
    if filas[i] = fila then
    begin
      kFila:= i;
      break;
    end;
  assert(kfila >= 0, 'TTabla.dropFila: no se encuentra la fila');

  for i:= kFila to high(filas) - 1 do
    filas[i]:= filas[i + 1];
  SetLength(filas, length(filas) - 1);
  nfilas:= nfilas - 1;

  fila.Visible:= false;

  filas_ocultas.add( fila );
// no la borro en esta función pues es llamada desde el CLICK del botón elminar
// y entonces al eliminar la fila estoy eliminando el botón que está en la fila
// y da una excepción por borrar el elemento del que estamos atendiendo un evento.
// mejor quitamos la fila y la ponemos en una lista de filas _ocultas
//
//  self.RemoveControl(fila);
//  fila.Free;

  reposicionar;
end;

procedure TTabla.BitBtn_OnClick( Sender: TObject);
var
  aux, nidRec: String;
  tipoBoton: integer;
  i: Integer;
  kFila: integer;
begin
  aux:= TComponent(Sender).Name;
  if pos('btns_', aux ) <> 1 then raise Exception.Create('ERROR, TTable.BitBtn_OnClick(name='+aux+');');
  delete( aux, 1, length( 'btns_' ) );
  i:= Pos('_', aux);
  nidRec:= Copy(aux, 1, i - 1);
  tipoBoton:= StrToInt(Copy(aux, i + 1, length(aux) - i));
  kFila:= kOf( TFila( TControl( Sender ).Parent.Parent ) );
  case tipoBoton of
    iid_Borrar:         Borrar_OnClick( nidRec, kFila );
    iid_BorrarDisabled: BorrarDisabled_OnClick( nidRec, kFila );
    iid_Editar:         Editar_OnClick( nidRec, kFila );
    iid_Ver:            Ver_OnClick( nidRec, kFila );
    iid_VerNaranja:     VerNaranja_OnClick( nidRec, kFila );
    iid_Bajar:          Bajar_OnClick( nidRec, kFila );
    iid_BajarDelTodo:   BajarDelTodo_OnClick( nidRec, kFila );
    iid_Configurar:     Configurar_OnClick( nidRec, kFila );
    iid_Email:          Email_OnClick( nidRec, kFila );
    iid_Info:           Info_OnClick( nidRec, kFila );
    iid_EmailDisabled:  EmailDisabled_OnClick( nidRec, kFila );
    iid_SemaforoRojo:   SemaforoRojo_OnClick( nidRec, kFila );
    iid_SemaforoVerde:  SemaforoVerde_OnClick( nidRec, kFila );
    iid_SubirDelTodo:   SubirDelTodo_OnClick( nidRec, kFila );
    iid_Subir:          Subir_OnClick( nidRec, kFila );
    iid_Lupa:           Lupa_OnClick( nidRec, kFila );
    iid_checkbox_0:     checkbox_0_OnClick( nidRec, kFila );
    iid_checkbox_1:     checkbox_1_OnClick( nidRec, kFila );
    iid_clonar:         clonar_OnClick( nidRec, kFila );
    iid_fdownload:      fdownload_OnClick( nidRec, kFila );
    iid_fupload:        fupload_OnClick( nidRec, kFila );
    iid_radiobutton_0:  radiobutton_0_OnClick( nidRec, kFila );
    iid_radiobutton_1:  radiobutton_1_OnClick( nidRec, kFila );
    iid_Reset:          reset_OnClick( nidRec, kFila );
    iid_Lupa2:        lupa2_OnClick( nidRec, kFila );
    iid_Editar2:     editar2_OnClick( nidRec, kFila );
    iid_OrdenadoAZ  : ordenadoAZ_OnClick( nidRec, kFila );
    iid_OrdenadoZA  : ordenadoZA_OnClick( nidRec, kFila );
    iid_OrdenarPor  : ordenarPor_OnClick( nidRec, kFila );

  end;
end;

procedure TTabla.CheckBox_OnClick( Sender: TObject);
begin
  showMessage( 'Click on: '+ TComponent( Sender ).Name );
end;


procedure TTabla.Edit_OnChange( Sender: TObject);
begin
//  showMessage( 'Change on: '+ TComponent( Sender ).Name );
end;


procedure TTabla.Button_OnClick( Sender: TObject );
begin
  showmessage( 'Button_OnClick Sin escribir.. '  );
end;


procedure TTabla.Edit_OnKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState );
var
  s: string;
begin
  if ((ssCtrl in Shift) AND (Key = ord('V'))or ( key = 45) ) then
  begin
    if Clipboard.HasFormat(CF_TEXT) then
    begin
      s:= Clipboard.AsText;
      s:= stringReplace( s, #13, '', [rfReplaceAll]  );
      s:= stringReplace( s, #10, '; ', [rfReplaceAll ] );
      s:= stringReplace( s, #9, '; ', [rfReplaceAll ] );
      s:= trim( s );
//      if s[length( s ) ] = ';' then delete( s, length(s ), 1 );
      Clipboard.AsText:= s;
    end;
  end;
end;

constructor TFila.Create( Owner: TComponent; nCols: integer );
var
  ac: TCelda;
  kCelda: Integer;
begin
  inherited Create( Owner );
  self.ParentColor:= false;

  setlength( celdas, nCols );
  for kCelda:= 0 to nCols-1 do
  begin
    ac:= TCelda.Create( self );
    ac.ParentColor:= true;
    ac.alinHorizontal:= CAH_Derecha;
    ac.alinVertical:= CAV_Abajo;

//    ac.BorderWidth:= TTabla( parent ).anchoborde;
    celdas[kCelda]:= ac;
    ac.Parent:= self;
//    self.InsertControl( ac );
  end;
end;


function TFila.kOf( aCelda: TCelda ): integer;
var
  res, k: integer;
begin
  res:= -1;
  for k:= 0 to high( self.celdas ) do
    if ( celdas[k] = aCelda ) then
    begin
      res:= k;
      break;
    end;
  result:= res;
end;



constructor TTabla.Create(
  AOwner: TComponent;
  Name: String; cellpadding,
  cellspacing: integer; anchoborde: integer);
// ac: TComponent;
begin
  inherited Create( AOwner );
  ParentColor:= true;
  flg_disable_autoscroll:= false;


{  self.Top:= ypos;
  self.Left:= xpos;
  self.ancho:= ancho;
  self.alto:= alto;
  self.width:= ancho;
  self.Height:= alto;}
//  self.Left:= AOwner.ClientOrigin.X;
//  self.Top:= AOwner.ClientOrigin.Y;
  self.Left:= 0;
  self.Top:= 0;
  self.Width:= 100;
  self.Height:= 100;
  self.Name:= Name;
  self.Caption:= '';

  self.ancho:= 100;
  self.alto:= 100;


  self.nfilas:= 0;
  self.ncols:= 0;

  self.cellpadding:= cellpadding;
  self.cellspacing:= cellspacing;
  self.anchoborde:= anchoborde;

  setlength( descriptoresColumnas, 0 );
  setlength( filas, 0 );
  filas_ocultas:= TList.Create;

  // esto lo tuve que agregar para Lazarus
  parent:= TWinControl( AOwner );
end;


procedure TTabla.ClearRedim( new_nFilas, new_nCols: integer );
var
  k :integer;
  af: TFila;
begin
  if nFilas <> 0  then
  begin
    for k:= 0 to nFilas-1 do
    begin
      RemoveControl( filas[k] );
      filas[k].Free;
    end;
  end;

  if filas_ocultas.Count > 0 then
  begin
    for k:= 0 to filas_ocultas.Count -1 do
    begin
      RemoveControl( TFila(filas_ocultas.Items[k]) );
      TFila(filas_ocultas[k]).Free;
    end;
    filas_ocultas.Clear;
  end;

  nFilas:= new_nFilas;
  nCols:= new_nCols;

  setlength( descriptoresColumnas, ncols );
  setlength( filas, nfilas );
  for k:= 0 to ncols-1 do
    descriptoresColumnas[k].ancho:= -1;

  for k:= 0 to nfilas-1 do
  begin
    af:= TFila.Create( self, ncols );
    filas[k]:= af;
    af.Parent:= self;
  end;
end;


function TTabla.GetFila( kfila: integer ): TFila;
begin
  result:= filas[kfila];
end;

function TTabla.GetCelda( kfila, kcolumna: integer ): TCelda;
begin
  result:= filas[kfila].celdas[kcolumna];
end;


function TTabla.FindObj( kfila, kcolumna: integer; id: string ): pointer;
begin
  result:= SearchComponent( GetCelda(kfila, kcolumna), id );
end;


procedure TTabla.SetBgColorFila( kfila: integer; bgColor: TColor );
var
  af: TFila;
begin
  af:= getFila( kfila );
  af.ParentColor:= false;
  af.Color:= bgColor;
end;

procedure TTabla.SetBgColorCelda( kfila, kcolumna: integer; bgColor: TColor );
var
  ac: TCelda;
begin
  ac:= getCelda( kfila, kcolumna );
  ac.ParentColor:= false;
  ac.Color:= bgColor;
end;

procedure TTabla.SetFontColorFila( kfila: integer; fontColor: TColor );
var
  ac: TPanel;
begin
  ac:= getFila( kfila );
  ac.Font.Color:= fontColor;
end;

procedure TTabla.SetFontColorCelda( kfila, kcolumna: integer; fontColor: TColor );
var
  ac: TPanel;
begin
  ac:= getCelda( kfila, kcolumna );
  ac.Font.Color:= fontColor;
end;

procedure TTabla.SetFontFila(kFila: integer; fontName: string;
  fontStyle: TFontStyles; fontSize: Integer);
var
  af: TFila;
  i,j: Integer;
  ac: TCelda;
begin
  af:= getFila( kfila );
  for i:=0 to Length( af.celdas )-1 do
  begin
    ac:= af.celdas[i];
    for j := 0 to ac.ControlCount-1 do
      if ac.Controls[j] is TLabel then
      begin
        ac.Controls[j].Font.Name:=fontName;
        ac.Controls[j].Font.Style:=fontStyle;
        ac.Controls[j].Font.Size:=fontSize;
      end;
    end;
end;

procedure TTabla.AlinearCelda(
      kfil: integer;
      kCol: integer;
      AlinHorizontal: TAlineacionHorizontal;
      AlinVertical: TAlineacionVertical );
var
  ac: TCelda;
begin
  ac:= getCelda( kfil, kcol );
  ac.alinHorizontal:= ALinHorizontal;
  ac.alinVertical:= AlinVertical;
  ac.PosicionarControles;
end;

procedure TTabla.AlinearColumna(
      kCol: integer;
      AlinHorizontal: TAlineacionHorizontal;
      AlinVertical: TAlineacionVertical );
var
  k: integer;
begin
  for k:= 0 to high( filas ) do
    AlinearCelda( k, kCol, AlinHorizontal, AlinVertical );
end;

// escribe el texto en la celda.
procedure TTabla.wrTexto( id: string; kfila, kcolumna: integer; Texto: String );
var
  ac: TCelda;
  alabel: TLabel;
  ts: TSize;
begin
  ac:= getCelda( kfila, kcolumna );
  alabel:= TLabel.Create( ac );
  alabel.Name:= id;
  alabel.caption:= texto;
  ac.agregar( alabel );

  // agregado para lazarus. Esto tiene que estar después del ac.agregar
  // para que alabel tenga definido el "parent" sino no funciona.
  ts:= alabel.Canvas.TextExtent( texto );
  alabel.width:= ts.cx;
  alabel.Height:= ts.cy;

end;

// Escribe un campo Editor
procedure TTabla.wrEdit(id: string; kfila, kcolumna: integer; Texto: String;
  ancho: integer; hint: string);
var
  ac: TCelda;
  c: TEdit;
  s: string;
  k: Integer;
begin
  ac:= getCelda( kfila, kcolumna );
  c:= TEdit.Create( ac );
  c.Name:= id;
  for k:= 0 to ancho -1 do s:= s+'M';
  c.Width:= ancho *12;
  c.Text:= s;
  c.text:= texto;
  c.Hint:= Hint;
  c.ShowHint:= Hint <> '';

  c.OnChange:= Edit_OnChange;
  c.OnKeyDown:= Edit_OnKeyDown;
  ac.agregar( c );

  {$IFDEF LCL}
  c.Alignment:=taRightJustify;
  {$ELSE}
  c.Align:= alRight;
  {$ENDIF}
end;

// Escribe un campo del tipo CheckBox
procedure TTabla.wrCheckBox(
  id: string;
  kfila, kcolumna: integer;
  caption: string;
  marcado: boolean; hint: string );
var
  ac: TCelda;
  c: TCheckBox;
begin
  ac:= getCelda( kfila, kcolumna );
  c:= TCheckBox.Create( ac );
  c.Name:= id;
  c.Caption:= caption;
  c.Checked:= marcado;
  c.Width:= 22;

  if hint = '-' then
  begin
    if marcado then
      c.Hint:= hintPorDefecto(iid_checkbox_1)
    else
      c.Hint:= hintPorDefecto(iid_checkbox_0);
  end
  else
    c.Hint:= Hint;
  c.ShowHint:= Hint <> '';
  c.OnClick:= CheckBox_OnClick;
  ac.agregar( c );
end;


// Escribe Boton común
procedure TTabla.wrButton(
  id: string;
  kfila, kcolumna: integer;
  caption: string; hint: string );
var
  ac: TCelda;
  c: TButton;
begin
  ac:= getCelda( kfila, kcolumna );
  c:= TButton.Create( ac );
  c.Name:= id;
  c.Caption:= caption;
  c.Width:= 12 * length( caption );
  c.Hint:= Hint;
  c.ShowHint:= Hint <> '';
  c.OnClick:= Button_OnClick;
  ac.agregar( c );
end;


// Escribe un campo del tipo BitBtn (botón con ícono)
procedure TTabla.wrBitBtn(id: string; kfila, kcolumna: integer; iid: integer;
  hint: string);
var
  ac: TCelda;
  c: TBitBtn;
begin
  ac:= getCelda( kfila, kcolumna );
  c:= TBitBtn.Create( ac );
  c.Name:= id;
  c.Caption:= '';
  if hint = '-' then
    c.hint:= hintPorDefecto(iid)
  else
    c.Hint:= hint;
  c.ShowHint:= Hint <> '';

  DataModule2.ImageList1.GetBitmap( iid,  c.Glyph );
  c.Spacing:=1;
  c.Width:= 22;
  c.Height:= 22;
  c.OnClick:= BitBtn_OnClick;
  ac.agregar( c );
end;


// Escribe un campo del tipo ComboBox
procedure TTabla.wrComboBox( id: string; kfila, kcolumna, ancho: integer);
var
  ac: TCelda;
  c: TComboBox;
begin
  ac:= getCelda( kfila, kcolumna );
  c:= TComboBox.Create( ac );
  c.Name:= id;
  {$IFDEF LCL}
  c.Caption:= '';
  {$ENDIF}
  c.Width:= ancho;
  c.Height:= 22;
  ac.agregar( c );
end;


procedure TTabla.wrBotonera(
      BaseId: string;
      kfila, kcolumna: integer;
      iids: array of integer;//TListActionButton; // ej [ iid_Borrar, iid_BorrarDisabled,  iid_Editar]
      hints: array of string ); // si length( hints ) <> length( iids ) no se muestran hints
var
  k: integer;
  mostrarHints: boolean;
  sid, shint: string;
  i: Integer;
begin

  mostrarHints:= Length(iids ) = length( hints );
  for k:= 0 to high( iids ) do
  begin
    sid:= 'btns_' + BaseId + '_' + IntToStr( iids[k] );
    if mostrarHints then
      shint:= hints[k]
    else
      shint:= '';
    wrBitBtn( sid, kfila, kcolumna, iids[k], shint );
  end;

end;

procedure TTabla.Reposicionar;
var
  kFil, kCol: integer;
  af: TFila;
  ac: TCelda;
  maxw, maxh: array of integer;
  mw, mh: integer;
  ypos, xpos: integer;
begin
  Self.Width:= ancho;
  self.Height:= alto;
  Self.Visible:= false;
  setlength( maxw, ncols );
  setlength( maxh, nfilas );
  for kFil:= 0 to NFilas-1 do
  begin
    af:= getFila( kfil );
    for kCol:= 0 to NCols -1 do
    begin
      ac:= af.celdas[ kCol ];
      ac.InnerWH( mw, mh );
      if mw> maxw[kCol] then
        maxw[kCol]:= mw;
      if mh > maxh[kfil] then
        maxh[kfil]:= mh;
    end;
    af.altoFila:= maxh[ kfil ];
    af.Height:= af.altoFila;
  end;

  ypos:= Self.cellspacing + Self.anchoborde;
  for kFil:= 0 to NFilas-1 do
  begin
    af:= getFila( kfil );
    af.Top:= ypos;
    af.BorderWidth:= 0;
    af.BorderStyle:= bsNone;
    ypos:= ypos + af.height + Self.cellspacing + Self.anchoborde;
    xpos:= Self.cellspacing + Self.anchoborde;
    for kCol:= 0 to NCols -1 do
    begin
      ac:= getCelda(kfil, kcol);
      ac.Width:= maxw[kCol];
      ac.Height:= maxh[kFil];
      ac.Left:= xpos;
      ac.PosicionarControles;
      ac.BorderWidth:= 0; //anchoBorde;
      ac.BorderStyle:= bsNone;
      xpos:= xpos + ac.Width+Self.cellspacing+Self.anchoborde;
    end;
    af.Width:= xpos;
  end;

//  self.AutoSize:= true;

(*
  Self.Width:= xpos + 2 * Integer(Self.BorderWidth) + Padding.Top + Padding.Bottom;
  self.Height:= ypos + 2 * Integer(Self.BorderWidth) + Padding.Left + Padding.Right;
  *)


  Self.Width:= xpos + 2 * ( self.anchoBorde + self.cellPadding );
  self.Height:= ypos + 2 *( self.anchoBorde + self.cellPadding );

  setlength( maxw, 0 );
  self.Visible:= true;

  // rch@20121107 puse esto acá para que no se auto-scrolee luego de los refresh de
  // la tabla. No estoy seguro de que sea la mejor solución.
  if flg_disable_autoscroll and (self.Parent is TScrollBox) then
  begin
    ( Parent as TScrollBox ).AutoScroll:= false;
    flg_disable_autoscroll:= false;
  end;

end;




procedure TCelda.agregar( aChild: TControl );
begin
  aChild.Parent:= Self;
//  self.InsertControl( aChild);
end;

procedure TCelda.InnerWH( var w, h: integer );
var
  k: integer;
  ac: TControl;
  dw: integer;
begin
  h:= 0;
  w:= 0;
  dw:= TTabla(parent.parent).cellpadding;
  for k:= 0 to self.ControlCount-1 do
  begin
    ac:= Controls[ k ];
    if ac.Visible then
    begin
      if ac.Height > h then h:= ac.Height;
      w:= w + ac.Width;
    end;
  end;
  h:= h+ 2*dw;
  w:= w+ 2*dw;
end;

procedure TCelda.PosicionarControles;
var
  xpos{, ypos}: integer;
  {w, }hmax: integer;
  k: integer;
  ac: TControl;
  dw: integer;
  mw, mh: integer;
  deltaw{, deltah}: integer;
begin
  hmax:= 0;
  dw:= TTabla(parent.parent).cellpadding;

  Self.InnerWH( mw, mh);

  case AlinHorizontal of
    CAH_Izquierda : deltaw:= 0;
    CAH_Centro    : deltaw:= ( self.Width - mw ) div 2;
    CAH_Derecha   : deltaw:= self.Width - mw;
    else            deltaw:= 0;
  end;

  xpos:= dw + deltaw;

  for k:= 0 to self.ControlCount-1 do
  begin
    ac:=  Controls[ k ];
    if ac.Height > hmax then
      hmax:= ac.Height;
    ac.Left:= xpos;
    xpos:= xpos + ac.width;
  end;

  for k:= 0 to self.ControlCount-1 do
  begin
    ac:= Controls[ k ];
    case AlinVertical of
      CAV_Abajo: ac.Top:= self.Height - dw - ac.Height;
      CAV_Centro: ac.Top:= (self.Height - ac.Height) div 2;
      CAV_Arriba: ac.Top:= dw;
    end;
  end;
end;


function TTabla.kOf( aFila: TFila ): integer;
var
  res, k: integer;
begin
  res:= -1;
  for k:= 0 to high( self.filas ) do
    if ( filas[k] = aFila ) then
    begin
      res:= k;
      break;
    end;
  result:= res;
end;



procedure TTabla.Borrar_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Borrar_OnClick Sin escribir..' + nidRec );
  dropFila( filas[kFila] );
end;

procedure TTabla.BorrarDisabled_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'BorrarDisabled_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Editar_onClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Editar_onClick Sin escribir..' + nidRec );
end;
procedure TTabla.Ver_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Ver_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.VerNaranja_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'VerNaranja_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Bajar_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Bajar_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.BajarDelTodo_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'BajarDelTodo_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Configurar_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Configurar_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Email_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Email_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Info_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Info_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.EmailDisabled_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'EmailDisabled_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.SemaforoRojo_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'SemaforoRojo_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.SemaforoVerde_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'SemaforoVerde_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.SubirDelTodo_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'SubirDelTodo_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Subir_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Subir_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.Lupa_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'Lupa_OnClick..' + nidRec );
end;
procedure TTabla.checkbox_0_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'checkbox_0_OnClick..' + nidRec );
end;
procedure TTabla.checkbox_1_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'checkbox_1_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.clonar_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'clonar_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.fdownload_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'fdownload_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.fupload_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'fupload_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.radiobutton_0_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'radiobutton_0_OnClick Sin escribir..' + nidRec );
end;
procedure TTabla.radiobutton_1_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'radiobutton_1_OnClick Sin escribir.. ' + nidRec );
end;

procedure TTabla.reset_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'reset_OnClick Sin escribir.. ' + nidRec );
end;


procedure TTabla.lupa2_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'lupa2_OnClick Sin escribir.. nidRec:' + nidRec +', kFila: '+IntToStr( kFila ) );
end;

procedure TTabla.editar2_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'editar2_OnClick Sin escribir.. ' + nidRec+', kFila: '+IntToStr( kFila ) );
end;

procedure TTabla.OrdenadoAZ_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'OrdenadoAZ_OnClick Sin escribir.. ' + nidRec+', kFila: '+IntToStr( kFila ) );
end;

procedure TTabla.OrdenadoZA_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'OrdenadoZA_OnClick Sin escribir.. ' + nidRec+', kFila: '+IntToStr( kFila ) );
end;

procedure TTabla.OrdenarPor_OnClick( nidRec: string; kFila: integer );
begin
  showmessage( 'OrdenarPor_OnClick Sin escribir.. ' + nidRec+', kFila: '+IntToStr( kFila ) );
end;




{$IFNDEF WINDOWS}
function rgb( r, g, b: word ): TColor;
begin
  result:= RGBToColor( r, g, b );
end;
{$ENDIF}

initialization
  clFondoFixedCells:= clNavy;
  clFontFixedCells:= clYellow;
  clFondoFilaPar:= rgb( $FF, $FF, $C0 );
  clFondoFilaImpar:= rgb( $F5, $E0, $C0 );
  clFondoFilaPar_Activa:= clFondoFilaPar;
  clFondoFilaImpar_Activa:= clFondoFilaImpar;
  clFondoFilaPar_Inactiva:= rgb( $FF, $FF - $60, $C0  - $60);
  clFondoFilaImpar_Inactiva:= rgb( $FF, $E0 - $60, $C0  - $60);
end.
