unit utilidades;

{$MODE Delphi}

interface

uses
{$IFDEF FPC-LCL}
  LCLType, lmessages,
  {$IFDEF LINUX}
  LCLIntf,
  {$ENDIF}
{$ENDIF}
{$IFDEF WINDOWS}
  Windows,
{$ENDIF}
  Grids, SysUtils, StdCtrls, Controls, Classes, Graphics,
  Messages, Forms, ExtCtrls, Dialogs;

const
  leftDef = 0;    //Coordenadas superior e izquerda del formulario principal por defecto
  topDef = 0;
  plusLeft = 15;  //Cuanto se le suma a las coordenadas del formulario hijo
  plusTop = 50;
  plusWidth = 10;
  plusHeight = 4;

  encabezadoBTEditar = 'TC_btEditar';
  encabezadoBTEliminar = 'TC_btEliminar';
  encabezadoBTClonar = 'TC_btClonar';
  encabezadoCheckBox = 'TC_checkBox';
  encabezadoRadioButton = 'TC_radioButton';
  encabezadoColor = 'TC_Color';
  encabezadoDisabled = 'TC_Disabled';
  encabezadoComboBox = 'TC_ComboBox';
  encabezadoBTUp = 'TC_btUp';
  encabezadoBTDown = 'TC_btDown';
  encabezadoTextoEditable = 'encabezadoTextoEditable';

  teclas = [VK_Return, VK_Tab, VK_Left, VK_Up, VK_Right, VK_Down, VK_Home, VK_End];

  teclasPosibleCambioCelda = [VK_Left, VK_Right];
  teclasCambioCelda = [VK_Return, VK_Tab, VK_Up, VK_Down, VK_Home, VK_End];

type
  TID_Icono = (btEdit, btClonar, btEliminar, checkBox_0, checkBox_1,
    radioButton_0, radioButton_1, btUp, btDown);
  TDAOfColores = array of TColor;

  TTipoColumna = (TC_Texto, TC_TextoEditable, TC_btEditar, TC_btEliminar,
    TC_btClonar, TC_checkBox, TC_radioButton, TC_Color, TC_Disabled,
    TC_ComboBox, TC_RaiseException, TC_btUp, TC_btDown);

  TDAOfTTipoColumna = array of TTipoColumna;
  TDAOfTDAOfTTipoColumna = array of TDAOfTTipoColumna;

  TFuncValidarCelda = function(listado: TStringGrid;
    fila, columna: integer): boolean of object;
  TProcCambioValor = procedure(listado: TStringGrid;
    fila, columna: integer) of object;

  TProcSwap = procedure(item1, item2: Pointer) of object;

var
  tiposdeColuma: array of TTipoColumna;
  mouseAbajo: boolean;

  colListado, filaListado, uACol, uAFila: integer;
  editedCol, editedFila: integer;
  uLoQueHabia: string;

resourcestring
  rsEditar = 'Editar';
  rsEliminar = 'Eliminar';
  rsClonar = 'Clonar';
  rsDesmarcar = 'Desmarcar';
  rsMarcar = 'Marcar';
  rsSubir = 'Subir';
  rsBajar = 'Bajar';

//Ajusta el tamaño de las columnas y luego el de la tabla
procedure AutoSizeTypedColsAndTable(grid: TStringGrid; tipos: TDAOfTTipoColumna;
  iconos: TImageList);

//Retornan el ancho total que aumento la tabla
function AutoSizeCol(Grid: TStringGrid; Column: integer): integer;
function AutoSizeTypedCol(Grid: TStringGrid; Column: integer;
  tipo: TTipoColumna; iconos: TImageList): integer;
function AutoSizeColsToMaxCol(Grid: TStringGrid): integer;

function AutosizeTableWidth(Grid: TStringGrid): integer;
(*
function AutoSizeTableHeight(Grid: TStringGrid): integer;
  *)

//Retornan el alto y ancho ocupados por las celdas de una tabla
function altoColumnasYScrollStringGrid(grid: TStringGrid): integer;
function anchoColumnasYScrollStringGrid(grid: TStringGrid): integer;


(*rch
procedure AutoSizeTableBajarControlesEnMismoEspacioVertical
  (contenedor: TWinControl; grid: TStringGrid; maxWidth: integer;
  maxHeigth: integer; deshabilitarScrollHorizontal: boolean);
*)


(***
//Baja todos los controles que esten por debajo de controlBase en cantPixeles
procedure bajarControles_(contenedor: TWinControl; controlBase: TControl;
  cantPixeles: integer);
procedure bajarControlesDebajoDeHIni_(contenedor: TWinControl;
  HIni, cantPixeles: integer);
procedure bajarControlesDebajoDeHIniEntreLeftYRight_(contenedor: TWinControl;
  left, right, HIni, cantPixeles: integer);

  ***)


//Retorna la lista de controles en parent ordenados por top, si hay
//mas de un control con el mismo top se ordenan por left
function ControlsSortedByTopAndLeft(parent: TWinControl): TList {of TControl};

//Retorna la lista de controles en parent ordenados por left, si hay
//mas de un control con el mismo left se ordenan por top
function ControlsSortedByLeftAndTop(parent: TWinControl): TList {of TControl};

//Fija el tabOrder de los controles de parent en orden de arriba hacia
//abajo y de izquierda a derecha
procedure setTabOrderByTopAndLeft(parent: TWinControl);
//Fija el tabOrder de los controles de parent en orden de izquierda a derecha
//y de arriba hacia abajo

procedure setTabOrderByLeftAndTop(parent: TWinControl);

function AutoSizeComboBox(cb: TComboBox): integer;

procedure AgregarFormatoFecha(etiqueta: TLabel);

procedure sgDeleteRow(tabla: TStringGrid; fila: integer);

procedure initListado(listado: TStringGrid; const encabezados: array of string;
  var tiposDeCols: TDAOfTTipoColumna; seleccionarPorFilas: boolean);

//Estos tres métodos deben ser asignados para listados con texto editable
procedure listadoGetEditText(Sender: TObject; ACol, ARow: integer);

procedure listadoValidarCambio(Sender: TObject; const tiposCols: TDAOfTTipoColumna;
  procValidarCelda: TFuncValidarCelda; procCambioValor: TProcCambioValor);

procedure ListadoMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: integer);

procedure ListadoDrawCell(Sender: TObject; ACol, ARow: integer;
  Rect: TRect; State: TGridDrawState; tipoColumna: TTipoColumna;
  colores: TDAOfColores; iconos: TImageList; procValidarCelda: TFuncValidarCelda = nil);

procedure ListadoMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: integer; const tiposdeColumna: array of TTipoColumna); overload;
procedure ListadoMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: integer; const tiposdeColumna: array of TTipoColumna;
  const hintsColumnas: array of string); overload;

function ListadoMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: integer;
  const tiposdeColumna: array of TTipoColumna): TTipoColumna;

function cambiarColor(listado: TStringGrid; fila: integer;
  colorDialog: TColorDialog; var colores: TDAOfColores): TColor;
function listadoDblClick(Sender: TObject;
  const tiposCols: array of TTipoColumna): TTipoColumna;
procedure PopUpComboBox(Sender: TObject; cb: TComboBox);

//Si se produce el cambio retornan la fila con la que se intercambio el elemento
//en fila
function listadoClickUp_(listado: TStringGrid; fila: integer; lista: TList;
  Shift: TShiftState; swapProc: TProcSwap; var xModificado: boolean;
  colores: TDAOfColores = nil): integer;

function listadoClickDown_(listado: TStringGrid; fila: integer;
  lista: TList; Shift: TShiftState; swapProc: TProcSwap; var xModificado: boolean;
  colores: TDAOfColores = nil): integer;

function sgBuscarFila(tabla: TStringGrid; columna: integer;
  const contenido: string): integer;
//procedure sgSeleccionarFila(tabla: TStringGrid; fila: Integer);
function sgBuscarYSeleccionarFila(tabla: TStringGrid; columna: integer;
  const contenido: string): integer;
procedure sgLimpiarSeleccion(tabla: TStringGrid);


//Retorna el titulo del archivo
function titulo(nombreArchivo: string): string;


procedure deshabilitarControles(winControl: TWinControl);
procedure habilitarControles(winControl: TWinControl);


// Ventanas con mensajes
procedure MensajeInformacion(const titulo, texto: string);
procedure MensajeError(const titulo, texto: string);
function MensajeSiNo(const titulo, texto: string): boolean;

var
  formatoFecha: string;

implementation

var
  oldXMouseMove, oldYMouseMove: integer;



type

  { TgCG

  Esta clase la defino solo para poder hacer público el método OffsetToColRow
  de TCuastomGrid
  }

  TgCG = class(TCustomGrid)
  public
    function OffsetToColRow(IsCol, Fisical: boolean; Offset: integer;
      out Index, Rest: integer): boolean;
  end;




function gMouseToCell(sg: TgCG; X, Y: integer; var ACol, ARow: longint): boolean;
var
  dummy1, dummy2: integer;
  res: integer;
begin
  res := 0;
  // Do not raise Exception if out of range
  if sg.OffsetToColRow(True, True, X, ACol, dummy1) then
    res := 1;

  if ACol < 0 then
    ARow := -1
  else
  begin
    if sg.OffsetToColRow(False, True, Y, ARow, dummy2) then
      res := res + 2;
    if ARow < 0 then
      ACol := -1;
  end;
  //  writeln( 'x: ', x, ', y: ', y, ', colMouse: ', aCol,', filMouse: ', aRow,', res: ', res,' dummy1: ', dummy1,', dummy2: ', dummy2 );
  if res <> 3 then
  begin
    ARow := -1;
    ACol := -1;
  end;
  Result := res = 3;
end;


function AutoSizeCol(Grid: TStringGrid; Column: integer): integer;
var
  i, W, WMax, sizeIni, posicion: integer;
  texto: string;
begin
  WMax := 0;
  sizeIni := Grid.Width;
  for i := 0 to (Grid.RowCount - 1) do
  begin
    posicion := pos(#13, Grid.Cells[column, i]);
    if posicion <> 0 then
    begin
      texto := Grid.Cells[column, i] + #13;
      repeat
        W := Grid.Canvas.TextWidth(copy(texto, 1, posicion - 1));
        if W > WMax then
          WMax := W;
        Delete(texto, 1, posicion);
        posicion := pos(#13, texto);
      until Posicion = 0;
    end
    else
    begin
      W := Grid.Canvas.TextWidth(Grid.Cells[Column, i]);
      if W > WMax then
        WMax := W;
    end;
  end;
  WMax := WMax + plusWidth;
  Grid.Width := Grid.Width + (WMax - Grid.ColWidths[column]);
  Grid.ColWidths[Column] := WMax;
  Result := Grid.Width - sizeIni;
end;

function AutoSizeTypedCol(Grid: TStringGrid; Column: integer;
  tipo: TTipoColumna; iconos: TImageList): integer;
var
  iniWidth, aux: integer;
begin
  case tipo of
    TC_Texto, TC_TextoEditable, TC_ComboBox:
      Result := AutoSizeCol(grid, Column);
    TC_checkBox:
    begin
      iniWidth := Grid.Width;
      aux := AutoSizeCol(Grid, Column);
      if grid.ColWidths[Column] < iconos.Width then
      begin
        grid.Width := grid.Width + (iconos.Width - grid.ColWidths[Column]);
        grid.ColWidths[Column] := iconos.Width;
        Result := Grid.Width - iniWidth;
      end
      else
        Result := aux;
    end;
    TC_btEditar, TC_btEliminar, TC_btClonar, TC_radioButton, TC_Color,
    TC_Disabled, TC_btUp, TC_btDown:
    begin
      iniWidth := Grid.Width;
      grid.Width := grid.Width + (iconos.Width - Grid.ColWidths[Column]);
      grid.ColWidths[Column] := iconos.Width;
      Result := Grid.Width - iniWidth;
    end;
    else
      Result := 0;
  end;
end;

function AutoSizeColsToMaxCol(Grid: TStringGrid): integer;
var
  i, j, W, WMax, SizeIni, posicion: integer;
  texto: string;
begin
  WMax := 0;
  SizeIni := Grid.Width;
  for j := 0 to Grid.ColCount - 1 do
  begin
    for i := 0 to (Grid.RowCount - 1) do
    begin
      posicion := pos(#13, Grid.Cells[j, i]);
      if posicion <> 0 then
      begin
        texto := Grid.Cells[j, i] + #13;
        repeat
          W := Grid.Canvas.TextWidth(copy(texto, 1, posicion - 1));
          if W > WMax then
            WMax := W;
          Delete(texto, 1, posicion);
          posicion := pos(#13, texto);
        until Posicion = 0;
      end
      else
      begin
        W := Grid.Canvas.TextWidth(Grid.Cells[j, i]);
        if W > WMax then
          WMax := W;
      end;
    end;
  end;
  WMax := WMax + plusWidth;
  for i := 0 to Grid.ColCount - 1 do
  begin
    Grid.ColWidths[i] := WMax;
  end;
  grid.Width := grid.ColCount * (WMax + grid.GridLineWidth) + grid.GridLineWidth + 2;
  Grid.DefaultColWidth := WMax;
  Result := Grid.Width - SizeIni;
end;


function anchoColumnasYScrollStringGrid(grid: TStringGrid): integer;
var
  res, i: integer;
begin
{$IFNDEF FPC-LCL}
  res := grid.GridLineWidth * (grid.ColCount + 1) + 2;
{$ELSE}
  res := 0;
{$ENDIF}
  for i := 0 to grid.ColCount - 1 do
    res := res + grid.ColWidths[i];

  //Si se ve el scroll vertical
  if (GetWindowlong(Grid.Handle, GWL_STYLE) and WS_VSCROLL) <> 0 then
    res := res + GetSystemMetrics(SM_CXVSCROLL) + 1;
  Result := res;
end;

function AutosizeTableWidth(Grid: TStringGrid): integer;
var
  SizeIni: integer;
begin
  SizeIni := Grid.Width;
  grid.Width := anchoColumnasYScrollStringGrid(Grid);
  Result := Grid.Width - SizeIni;
end;

function altoColumnasYScrollStringGrid(grid: TStringGrid): integer;
var
  res, i: integer;
begin
{$IFNDEF FPC-LCL}
  res := grid.GridLineWidth * (grid.RowCount + 1) + 2;
{$ELSE}
  res := 0;
{$ENDIF}
  for i := 0 to grid.RowCount - 1 do
    Inc(res, Grid.RowHeights[i]);

  //Si se ve el scroll horizontal
  if (GetWindowlong(Grid.Handle, GWL_STYLE) and WS_HSCROLL) <> 0 then
    res := res + GetSystemMetrics(SM_CYHSCROLL) + 1;
  Result := res;
end;


function AutoSizeTableHeight(Grid: TStringGrid): integer;
var
  SizeIni: integer;
begin
  SizeIni := Grid.Height;
  Grid.Height := altoColumnasYScrollStringGrid(Grid);
  Result := Grid.Height - SizeIni;
end;

procedure AutoSizeTableSinBajarControles(grid: TStringGrid; maxWidth: integer;
  maxHeigth: integer; deshabilitarScrollHorizontal: boolean);
var
  i, nColsConUnPixelMas, Width, heigth, anchoScrollBarV, altoScrollBarH: integer;
  anchoPorColumna, anchoRestante: integer;
  nColsSinAcomodar, oldNColsSinAcomodar: integer;
  colsSinAcomodar: array of integer;
  scrollVertical: boolean;
  anchoDeLinea: integer;
begin
  anchoDeLinea := grid.GridLineWidth;
  Width := 4; //Hay 2 px de cada lado de la tabla
  if grid.ColCount = 1 then
    grid.ScrollBars := ssVertical; //Chanchada, hay que arreglar
  heigth := Width;

  //Lazarus cuenta la línea dentro del ancho de la columna, delphi no
  //Por lo tanto en delphi las columnas ocupan GridLineWidth mas px que en lazarus
  anchoScrollBarV := GetSystemMetrics(SM_CXVSCROLL);
  altoScrollBarH := GetSystemMetrics(SM_CYHSCROLL);

  scrollVertical := False;
  for i := 0 to Grid.RowCount - 1 do
  begin
    Inc(heigth, (Grid.RowHeights[i] + anchoDeLinea));
    if heigth > maxHeigth then
    begin
      heigth := maxHeigth;
      Inc(Width, anchoScrollBarV);
      scrollVertical := True;
      break;
    end;
  end;

  if not deshabilitarScrollHorizontal then
  begin
    for i := 0 to Grid.ColCount - 1 do
    begin
      Width := Width + (Grid.ColWidths[i] + anchoDeLinea);
      if Width > maxWidth then
      begin
        Width := maxWidth;
        Inc(heigth, altoScrollBarH);
        break;
      end;
    end;
  end
  else
  begin
    for i := 0 to grid.ColCount - 1 do
      Width := Width + (Grid.ColWidths[i] + anchoDeLinea);
    if Width > maxWidth then
    begin
      if scrollVertical then
      begin
        Width := maxWidth - anchoScrollBarV;
        anchoRestante := Width - grid.GridLineWidth * 3 - anchoScrollBarV;
      end
      else
      begin
        Width := maxWidth;
        anchoRestante := Width - grid.GridLineWidth * 3;
      end;
      nColsSinAcomodar := grid.ColCount;
      SetLength(colsSinAcomodar, nColsSinAcomodar);
      for i := 0 to grid.ColCount - 1 do
        colsSinAcomodar[i] := i;

      repeat
        anchoPorColumna := anchoRestante div nColsSinAcomodar;
        oldNColsSinAcomodar := nColsSinAcomodar;

        nColsSinAcomodar := 0;
        for i := 0 to oldNColsSinAcomodar - 1 do
          if (grid.ColWidths[colsSinAcomodar[i]] + anchoDeLinea) >
            anchoPorColumna then
          begin
            colsSinAcomodar[nColsSinAcomodar] := colsSinAcomodar[i];
            nColsSinAcomodar := nColsSinAcomodar + 1;
          end
          else
            anchoRestante := anchoRestante -
              (Grid.ColWidths[colsSinAcomodar[i]] + anchoDeLinea);
      until nColsSinAcomodar = oldNColsSinAcomodar;

      nColsConUnPixelMas := anchoRestante mod nColsSinAcomodar - 1;

      for i := 0 to nColsConUnPixelMas - 1 do
        grid.ColWidths[colsSinAcomodar[i]] :=
          anchoPorColumna + 1 - anchoDeLinea;
      for i := nColsConUnPixelMas to nColsSinAcomodar - 1 do
        Grid.ColWidths[colsSinAcomodar[i]] := anchoPorColumna - anchoDeLinea;
    end;
  end;

  Grid.Height := heigth;
  Grid.Width := Width;
end;

function LeftMostTopMost(control1, control2: Pointer): integer;
begin
  if TControl(control1).Top < TControl(control2).Top then
    Result := -1
  else if TControl(control1).Top = TControl(control2).Top then
  begin
    if TControl(control1).Left < TControl(control2).Left then
      Result := -1
    else if TControl(control1).Left = TControl(control2).Left then
      Result := 0
    else
      Result := 1;
  end
  else
    Result := 1;
end;

function ControlsSortedByTopAndLeft(parent: TWinControl): TList {of TControl};
var
  res: TList;
  i: integer;
begin
  res := TList.Create;
  res.Capacity := parent.ControlCount;
  for i := 0 to parent.ControlCount - 1 do
    res.Add(parent.Controls[i]);
  res.Sort(LeftMostTopMost);
  Result := res;
end;

function TopMostLeftMost(control1, control2: Pointer): integer;
begin
  if TControl(control1).Left < TControl(control2).Left then
    Result := -1
  else if TControl(control1).Left = TControl(control2).Left then
  begin
    if TControl(control1).Top < TControl(control2).Top then
      Result := -1
    else if TControl(control1).Top = TControl(control2).Top then
      Result := 0
    else
      Result := 1;
  end
  else
    Result := 1;
end;

function ControlsSortedByLeftAndTop(parent: TWinControl): TList {of TControl};
var
  res: TList;
  i: integer;
begin
  res := TList.Create;
  res.Capacity := parent.ControlCount;
  for i := 0 to parent.ControlCount - 1 do
    res.Add(parent.Controls[i]);
  res.Sort(TopMostLeftMost);
  Result := res;
end;

procedure setTabOrderByTopAndLeft(parent: TWinControl);
var
  tabOrder: TTabOrder;
  controlesEnOrden: TList;
begin
  controlesEnOrden := ControlsSortedByTopAndLeft(parent);
  for tabOrder := 0 to controlesEnOrden.Count - 1 do
  begin
    if TControl(controlesEnOrden[tabOrder]) is TWinControl then
    begin
      TWinControl(controlesEnOrden[tabOrder]).TabOrder := tabOrder;
      if TWinControl(controlesEnOrden[tabOrder]).ControlCount > 0 then
        setTabOrderByTopAndLeft(TWinControl(controlesEnOrden[tabOrder]));
    end;
  end;
  controlesEnOrden.Free;
end;

procedure setTabOrderByLeftAndTop(parent: TWinControl);
var
  tabOrder: TTabOrder;
  controlesEnOrden: TList;
begin
  controlesEnOrden := ControlsSortedByLeftAndTop(parent);
  for tabOrder := 0 to controlesEnOrden.Count - 1 do
  begin
    if TControl(controlesEnOrden[tabOrder]) is TWinControl then
    begin
      TWinControl(controlesEnOrden[tabOrder]).TabOrder := tabOrder;
      if TWinControl(controlesEnOrden[tabOrder]).ControlCount > 0 then
        setTabOrderByLeftAndTop(TWinControl(controlesEnOrden[tabOrder]));
    end;
  end;
  controlesEnOrden.Free;
end;



procedure AutoSizeTypedColsAndTable(grid: TStringGrid;
  tipos: TDAOfTTipoColumna; iconos: TImageList);
var
  i: integer;
begin
  for i := 0 to grid.ColCount - 1 do
    AutoSizeTypedCol(grid, i, tipos[i], iconos);
end;


function AutoSizeComboBox(cb: TComboBox): integer;
var
  i, MaximoAncho, iniSize: integer;
begin
  iniSize := cb.Width;
  if cb.Items.Count > 0 then
  begin
    MaximoAncho := cb.Canvas.TextWidth(cb.items[0]);
    for i := 1 to cb.Items.Count - 1 do
      if cb.Canvas.TextWidth(cb.items[i]) > MaximoAncho then
        MaximoAncho := cb.Canvas.TextWidth(cb.items[i]);
    cb.Width := MaximoAncho + plusWidth;
  end;
  Result := cb.Width - iniSize;
end;

procedure AgregarFormatoFecha(etiqueta: TLabel);
begin
  etiqueta.Caption := etiqueta.Caption + formatoFecha;
end;

procedure sgDeleteRow(tabla: TStringGrid; fila: integer);
var
  i, j: integer;
begin
  for j := 0 to tabla.ColCount - 1 do
    for i := fila to tabla.RowCount - 2 do
      tabla.Cells[j, i] := tabla.Cells[j, i + 1];

  tabla.RowCount := tabla.RowCount - 1;
end;

procedure sgLimpiarSeleccion(tabla: TStringGrid);
var
  rectSeleccion: TGridRect;
begin
  with rectSeleccion do
  begin
    Top := -1;
    Left := -1;
    Right := -1;
    Bottom := -1;
  end;
  tabla.Selection := rectSeleccion;
end;


procedure initListado(listado: TStringGrid; const encabezados: array of string;
  var tiposDeCols: TDAOfTTipoColumna; seleccionarPorFilas: boolean);
var
  i: integer;
begin
  if seleccionarPorFilas then
  begin
    listado.Options := listado.Options + [goRowSelect];
    listado.Options := listado.Options - [goRangeSelect];
  end
  else
  begin
    listado.Options := listado.Options - [goRowSelect];
    listado.Options := listado.Options + [goRangeSelect];
  end;
  listado.ColCount := Length(encabezados);
  SetLength(tiposDeCols, Length(encabezados));
  for i := 0 to high(encabezados) do
  begin
    if encabezados[i] = encabezadoBTEditar then
      tiposDeCols[i] := TC_btEditar
    else if encabezados[i] = encabezadoBTEliminar then
      tiposDeCols[i] := TC_btEliminar
    else if encabezados[i] = encabezadoBTClonar then
      tiposDeCols[i] := TC_btClonar
    else if pos(encabezadoCheckBox, encabezados[i]) > 0 then
    begin
      tiposDeCols[i] := TC_checkBox;
      listado.Cells[i, 0] := StringReplace(encabezados[i], encabezadoCheckBox,
        '', [rfReplaceAll]);
    end
    else if encabezados[i] = encabezadoRadioButton then
      tiposDeCols[i] := TC_radioButton
    else if encabezados[i] = encabezadoColor then
      tiposDeCols[i] := TC_Color
    else if encabezados[i] = encabezadoDisabled then
      tiposDeCols[i] := TC_Disabled
    else if encabezados[i] = encabezadoComboBox then
      tiposDeCols[i] := TC_ComboBox
    else if encabezados[i] = encabezadoBTUp then
      tiposDeCols[i] := TC_btUp
    else if encabezados[i] = encabezadoBTDown then
      tiposDeCols[i] := TC_btDown
    else if pos(encabezadoTextoEditable, encabezados[i]) > 0 then
    begin
      tiposDeCols[i] := TC_TextoEditable;
      listado.Cells[i, 0] := StringReplace(encabezados[i],
        encabezadoTextoEditable, '', [rfReplaceAll]);
      if goRowSelect in listado.Options then
        raise Exception.Create(
          'utilidades.initListado: Creo un llistado con seleccion por filas y'+
          ' al menos una columna de tipo TextoEditable');
    end
    else
    begin
      tiposDeCols[i] := TC_Texto;
      listado.Cells[i, 0] := encabezados[i];
    end;
  end;
  if not seleccionarPorFilas or (listado.RowCount = 1) then
    sgLimpiarSeleccion(listado);
end;

procedure listadoGetEditText(Sender: TObject; ACol, ARow: integer);
begin
  uLoQueHabia := TStringGrid(Sender).Cells[ACol, ARow];
  editedFila := ARow;
  editedCol := ACol;
end;

function iAnteriorColumnaEditable(listado: TStringGrid; iDesde: integer;
  const tiposCols: TDAOfTTipoColumna): integer;
var
  i: integer;
begin
  Result := -1;
  for i := iDesde downto listado.FixedCols do
    if tiposCols[i] = TC_TextoEditable then
    begin
      Result := i;
      break;
    end;
end;

function iProximaColumnaEditable(listado: TStringGrid; iDesde: integer;
  const tiposCols: TDAOfTTipoColumna): integer;
var
  i: integer;
begin
  Result := -1;
  for i := iDesde to listado.ColCount - 1 do
    if tiposCols[i] = TC_TextoEditable then
    begin
      Result := i;
      break;
    end;
end;

procedure listadoValidarCambio(Sender: TObject; const tiposCols: TDAOfTTipoColumna;
  procValidarCelda: TFuncValidarCelda; procCambioValor: TProcCambioValor);
begin
  if (editedCol >= 0) and (tiposCols[utilidades.editedCol] = TC_TextoEditable) and
    (uLoQueHabia <> TStringGrid(Sender).Cells[editedCol, editedFila]) and
    procValidarCelda(TStringGrid(Sender), editedFila, editedCol) then
  begin
    procCambioValor(TStringGrid(Sender), editedFila, editedCol);
    editedCol := -1;
    editedFila := -1;
  end;
end;

procedure ListadoMouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: integer);
begin
  case Button of
    mbLeft:
    begin
      gMouseToCell(TgCG(Sender), X, Y, colListado, filaListado);
      MouseAbajo := True;
    end;
  end;
end;


procedure ListadoMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: integer; const tiposdeColumna: array of TTipoColumna;
  const hintsColumnas: array of string); overload;
var
  colMouse, filaMouse: integer;
  SenderAsGrid: TStringGrid;
begin
  if (oldXMouseMove <> X) and (oldYMouseMove <> Y) then
  begin
    oldXMouseMove := X;
    oldYMouseMove := Y;

    SenderAsGrid := TStringGrid(Sender);
    SenderAsGrid.ShowHint := False;



    if gMouseToCell(TgCG(SenderAsGrid), X, Y, colMouse, filaMouse) and
      MouseAbajo then
    begin
      if (colMouse <> colListado) or (filaMouse <> filaListado) then
      begin
        MouseAbajo := False;
        SenderAsGrid.Invalidate;
      end
      else
      begin
        exit;
      end;
    end;


    if (filaMouse < SenderAsGrid.FixedRows) then
    begin
      if (colMouse >= 0) and (colMouse < Length(hintsColumnas)) then
      begin
        SenderAsGrid.ShowHint := True;
        SenderAsGrid.Hint := hintsColumnas[colMouse];
      end;
    end
    else
    begin
      SenderAsGrid.ShowHint := True;
      case tiposdeColumna[colMouse] of
        TC_checkBox:
        begin
          if SenderAsGrid.Cells[colMouse, filaMouse] = '1' then
            SenderAsGrid.Hint := rsDesmarcar
          else
            SenderAsGrid.Hint := rsMarcar;
        end;
        TC_btEditar:
          SenderAsGrid.Hint := rsEditar;
        TC_btEliminar:
          SenderAsGrid.Hint := rsEliminar;
        TC_btClonar:
          SenderAsGrid.Hint := rsClonar;
        TC_btUp:
          SenderAsGrid.Hint := rsSubir;
        TC_btDown:
          SenderAsGrid.Hint := rsBajar;
        TC_Texto, TC_TextoEditable:
        begin
          if (SenderAsGrid.Canvas.TextWidth(SenderAsGrid.Cells[colMouse,
            filaMouse]) > SenderAsGrid.ColWidths[colMouse]) then
            SenderAsGrid.Hint := SenderAsGrid.Cells[colMouse, filaMouse]
          else if length(hintsColumnas) > 0 then
            SenderAsGrid.Hint := hintsColumnas[colMouse]
          else
            SenderAsGrid.Hint := SenderAsGrid.Cells[colMouse, 0];
        end;
        else
          SenderAsGrid.ShowHint := False;
      end;
    end;

    if (colMouse <> uACol) or (filaMouse <> uAFila) then
      SenderAsGrid.ShowHint := False;

    uACol := colMouse;
    uAFila := filaMouse;
  end;
end;

procedure ListadoMouseMove(Sender: TObject; Shift: TShiftState;
  X, Y: integer; const tiposdeColumna: array of TTipoColumna); overload;
begin
  ListadoMouseMove(Sender, Shift, X, Y, tiposdeColumna, []);
end;

procedure ListadoDrawCell(Sender: TObject; ACol, ARow: integer;
  Rect: TRect; State: TGridDrawState; tipoColumna: TTipoColumna;
  colores: TDAOfColores; iconos: TImageList; procValidarCelda: TFuncValidarCelda = nil);

var
  SenderAsGrid: TStringGrid;
  xRow: integer;

  procedure DibujarColor(colores: TDAOfColores);
  var
    SenderAsGrid: TStringGrid;
  begin
    SenderAsGrid := TStringGrid(Sender);
    if (ARow < SenderAsGrid.FixedRows) or (ACol < SenderAsGrid.fixedCols) or
      ((SenderAsGrid.FixedRows = 0) and (ARow = 0) and
      ((SenderAsGrid.RowCount <> 1) or (SenderAsGrid.ColCount <> 1))) then
      exit;

    xRow:= ARow - SenderAsGrid.FixedRows;
    if ( xRow >= 0 ) and ( xRow < length( colores ) ) and ( colores[ xRow ] <> clDefault ) then
    begin
      if SenderAsGrid.Cells[ACol, ARow] <> '' then
        SenderAsGrid.Cells[ACol, ARow] := '';
      SenderAsGrid.Canvas.Brush.Color := colores[ARow - SenderAsGrid.FixedRows];
      SenderAsGrid.Canvas.FillRect(Rect);
    end
    else
    begin
      if SenderAsGrid.Cells[ACol, ARow] <> 'Auto' then
        SenderAsGrid.Cells[ACol, ARow] := 'Auto';
      SenderAsGrid.Canvas.Brush.Color := clWhite;
      SenderAsGrid.Canvas.FillRect(Rect);
      SenderAsGrid.Canvas.Font.Color := clBlack;
      //    SenderAsGrid.Canvas.Pen.Color:= clBlack;
      SenderAsGrid.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2,
        SenderAsGrid.Cells[ACol, ARow]);
    end;

    //  SenderAsGrid.Canvas.Brush.Color := colores[ARow - 1];
    //  SenderAsGrid.Canvas.FillRect(Rect);
  end;

  procedure DibujarBoton(kicono: TID_Icono; apretable: boolean; iconos: TImageList);
  var
    SenderAsGrid: TDrawGrid;
  begin
    SenderAsGrid := TDrawGrid(Sender);
    if (ARow < SenderAsGrid.FixedRows) or (SenderAsGrid.FixedRows = 0) or
      (ACol < SenderAsGrid.fixedCols) then
      exit;
    with SenderAsGrid do
    begin
      Canvas.Brush.Color := clWhite;
      Canvas.FillRect(Rect);
      iconos.Draw(Canvas, Rect.Left, Rect.Top, Ord(kicono));
      if gdFocused in State then
        Canvas.DrawFocusRect(Rect);

      if apretable and mouseAbajo and ((ARow = filaListado) and (ACol = Col)) then
      begin
        Canvas.Pen.Color := clBlack;
        Canvas.Pen.Width := 2;
        Canvas.Polyline([Point(Rect.Left + 2, Rect.bottom - 2),
          Point(Rect.left + 2, Rect.Top + 2), Point(Rect.right - 2,
          Rect.Top + 2)]);
      end;
    end;
  end;

  procedure dibujarDisabled(Sender: TObject; Col, Row: integer;
    Rect: TRect; State: TGridDrawState);
  begin
    with TStringGrid(Sender).Canvas do
    begin
      Brush.Color := TStringGrid(Sender).FixedColor;
      FillRect(Rect);
      Pen.Style := psSolid;
      Pen.Width := 1;
      Pen.Color := clBlack;
      Polyline([point(rect.left - 1, rect.bottom + 1),
        Point(Rect.TopLeft.X - 1, Rect.TopLeft.Y - 1),
        Point(Rect.right + 1, Rect.Top - 1)]);
      Pen.Color := clBtnHighlight;
      Polyline([point(rect.Left, rect.Bottom - 1), rect.TopLeft,
        point(rect.Right, rect.Top)]);
      Pen.Color := clBtnShadow;
      Polyline([Point(Rect.left + 1, Rect.bottom - 1),
        Point(Rect.right - 1, Rect.bottom - 1), Point(Rect.right - 1, Rect.Top)]);
    end;
  end;

  procedure dibujarTexto(Sender: TObject; Col, Row: integer; Rect: TRect;
    State: TGridDrawState; procValidarCelda: TFuncValidarCelda = nil);
  var
    Texto, linea: string;
    Indice: integer;
    Posicion: integer;
    grid: TStringGrid;
    pintarFondoDeColor: boolean;
    error: boolean;
  begin
    grid := TStringGrid(Sender);

    if Assigned(procValidarCelda) then
      error := not procValidarCelda(grid, row, col)
    else
      error := False;

    pintarFondoDeColor := (Row >= grid.FixedRows) and (Col >= grid.FixedCols) and
      (grid.Row <> Row) or (not (goRowSelect in grid.Options)) or error;
    if pintarFondoDeColor then
    begin
      if error then
        grid.Canvas.Brush.Color := clRed
      else if row mod 2 = grid.FixedRows mod 2 then
        grid.Canvas.Brush.Color := clWhite
      else
        grid.Canvas.Brush.Color := RGB(244, 244, 244);
      grid.Canvas.FillRect(Rect);
    end;

    if Pos(#13, grid.Cells[Col, Row]) <> 0 then
    begin
      Texto := grid.Cells[Col, Row] + #13;
      grid.Canvas.FillRect(Rect);
      Indice := 0;
      repeat
        Posicion := Pos(#13, Texto);
        linea := Copy(Texto, 1, Posicion - 1);
        with grid.Canvas do
          TextOut(Rect.left + 2, Rect.Top + (Indice * TextHeight(linea)) + 2,
            linea);
        Inc(Indice);
        Delete(Texto, 1, Posicion);
      until Posicion = 0;
      grid.RowHeights[Row] := (Indice - 1) * grid.Canvas.TextHeight
        (grid.Cells[Col, Row]) + utilidades.plusHeight + 1;
    end
    else if pintarFondoDeColor then
      grid.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2, grid.Cells[Col, Row]);
  end;

begin
  SenderAsGrid := TStringGrid(Sender);
  //Celdas fijas
  //  if ( ACol >=  0 ) and ( ACol < 1 )
  if (ARow < SenderAsGrid.FixedRows) or
    ((ARow = 0) and ((SenderAsGrid.RowCount <> 1) or (SenderAsGrid.ColCount <> 1))) then
  begin
    SenderAsGrid.Canvas.Pen.Color := clBlack;
    SenderAsGrid.Canvas.Brush.Color := SenderAsGrid.FixedColor;
    SenderAsGrid.Canvas.FillRect(Rect);
    SenderAsGrid.Canvas.Pen.Style := psSolid;
    SenderAsGrid.Canvas.Pen.Width := 1;
    SenderAsGrid.Canvas.Pen.Color := clBlack;
    SenderAsGrid.Canvas.Polyline([point(rect.left - 1, rect.bottom + 1),
      Point(Rect.TopLeft.X - 1, Rect.TopLeft.Y - 1),
      Point(Rect.right + 1, Rect.Top - 1)]);
    SenderAsGrid.Canvas.Pen.Color := clBtnHighlight;
    SenderAsGrid.Canvas.Polyline([point(rect.Left, rect.Bottom - 1),
      rect.TopLeft, point(rect.Right, rect.Top)]);
    SenderAsGrid.Canvas.Pen.Color := clBtnShadow;
    SenderAsGrid.Canvas.Polyline([point(rect.Left + 1, rect.Bottom - 1),
      Point(Rect.right - 1, Rect.bottom - 1), Point(Rect.right - 1, Rect.Top)]);
    SenderAsGrid.Canvas.TextOut(Rect.Left + 2, Rect.Top + 2,
      SenderAsGrid.cells[ACol, Arow]);
  end
  else
  begin
    case tipoColumna of
      TC_Texto, TC_TextoEditable:
        dibujarTexto(Sender, ACol, ARow, Rect, State, procValidarCelda);
      TC_btEditar:
        DibujarBoton(btEdit, True, iconos);
      TC_btEliminar:
        DibujarBoton(btEliminar, True, iconos);
      TC_btClonar:
        DibujarBoton(btClonar, True, iconos);
      TC_Disabled:
        dibujarDisabled(Sender, ACol, ARow, Rect, State);
      TC_checkBox:
        if SenderAsGrid.Cells[ACol, ARow] = '0' then
          DibujarBoton(checkBox_0, False, iconos)
        else
          DibujarBoton(checkBox_1, False, iconos);
      //      TC_ComboBox                  : nada
      TC_radioButton:
        if SenderAsGrid.Cells[ACol, ARow] = '0' then
          DibujarBoton(radioButton_0, False, iconos)
        else
          DibujarBoton(radioButton_1, False, iconos);
      TC_Color:
        DibujarColor(colores);
      TC_btUp:
        DibujarBoton(btUp, True, iconos);
      TC_btDown:
        DibujarBoton(btDown, True, iconos);
    end; // del case
  end;
end;

function ListadoMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: integer;
  const tiposdeColumna: array of TTipoColumna): TTipoColumna;
var
  aFila, aCol: integer;
  res: TTipoColumna;
  SenderAsGrid: TStringGrid;
begin
  SenderAsGrid := Sender as TStringGrid;

  case Button of
    mbLeft:
      gMouseToCell(TgCG(SenderAsGrid), X, Y, colListado, filaListado);
  end;

  if not MouseAbajo then
  begin
    Result := TC_Texto;
    exit;
  end;

  MouseAbajo := False;

  gMouseToCell(TgCG(SenderAsGrid), X, Y, aCol, aFila);

  if (aFila <> filaListado) or (aCol <> colListado) then
  begin
    Result := TC_Texto;
    exit;
  end;

  if (aFila < SenderAsGrid.FixedRows) or ((aFila = 0) and
    ((SenderAsGrid.RowCount <> 1) or (SenderAsGrid.ColCount <> 1))) then
  begin
    Result := TC_Disabled;
    exit;
  end;

  gMouseToCell(TgCG(SenderAsGrid), X, Y, aCol, aFila);
  if (aFila > SenderAsGrid.FixedRows - 1) and (aCol = colListado) and
    (aFila = filaListado) then
  begin
    case tiposdeColumna[aCol] of
      TC_Texto:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_Texto;
      end;
      TC_TextoEditable:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options + [goEditing];
        res := TC_TextoEditable;
      end;
      TC_btEditar:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_btEditar;
      end;
      TC_btEliminar:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_btEliminar;
      end;
      TC_btClonar:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_btClonar;
      end;
      TC_checkBox:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        if SenderAsGrid.cells[aCol, aFila] = '1' then
          SenderAsGrid.cells[aCol, aFila] := ''
        else
          SenderAsGrid.cells[aCol, aFila] := '1';
        res := TC_checkBox;
      end;
      TC_radioButton:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        for aFila := 1 to SenderAsGrid.RowCount - 1 do
          SenderAsGrid.cells[aCol, aFila] := '';
        SenderAsGrid.cells[aCol, aFila] := '1';
        res := TC_radioButton;
      end;
      TC_Color:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_Color;
      end;
      TC_Disabled:
      begin
        SenderAsGrid.ShowHint := True;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_Disabled;
      end;
      TC_ComboBox:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_ComboBox;
      end;
      TC_btUp:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_btUp;
      end;
      TC_btDown:
      begin
        SenderAsGrid.ShowHint := False;
        SenderAsGrid.Options := SenderAsGrid.Options - [goEditing];
        res := TC_btDown;
      end;
      else
        raise Exception.Create('ListadoMouseUp, res llegó con valor no asignado');
    end; //del case
  end    //del if
  else
    res := TC_Disabled;
  if SenderAsGrid.LeftCol + SenderAsGrid.VisibleColCount > SenderAsGrid.ColCount then
    SenderAsGrid.LeftCol := SenderAsGrid.ColCount - SenderAsGrid.VisibleColCount;
  if SenderAsGrid.TopRow + SenderAsGrid.VisibleRowCount > SenderAsGrid.RowCount then
    SenderAsGrid.TopRow := SenderAsGrid.RowCount - SenderAsGrid.VisibleRowCount;
  Result := res;
end;

function cambiarColor(listado: TStringGrid; fila: integer;
  colorDialog: TColorDialog; var colores: TDAOfColores): TColor;
begin
  if colorDialog.Execute then
  begin
    colores[fila - listado.FixedRows] := colorDialog.Color;
    listado.Invalidate;
  end;
  Result := colorDialog.Color;
end;

function listadoDblClick(Sender: TObject;
  const tiposCols: array of TTipoColumna): TTipoColumna;
begin
  if (colListado >= TStringGrid(Sender).FixedCols) and
    (filaListado >= TStringGrid(Sender).FixedRows) then
    Result := tiposCols[colListado]
  else
    Result := TC_Disabled;
end;

procedure listadoSelectCell(Sender: TObject; ACol, ARow: integer;
  var CanSelect: boolean; const tiposCol: array of TTipoColumna);
begin
  if tiposCol[ACol] = TC_Disabled then
    CanSelect := False;
end;

procedure PopUpComboBox(Sender: TObject; cb: TComboBox);
var
  R: TRect;
  org: TPoint;
begin
  with Sender as TStringGrid do
  begin
    if (Col >= FixedCols) and (Row >= FixedRows) then
    begin
      // entered the column associated to the combobox
      // get grid out of selection mode
      Perform(WM_CANCELMODE, 0, 0);
      // position the control on top of the cell
      R := CellRect(col, row);
      //        org:= Self.ScreenToClient( ClientToScreen( R.topleft ));
      org := R.TopLeft;
      with cb do
      begin
        setbounds(org.X, org.Y, r.right - r.left, Height);
        ItemIndex := Items.IndexOf(Cells[Col, Row]);
        Show;
        BringTofront;
        // focus the combobox and drop down the list
        SetFocus;
        DroppedDown := True;
      end;
    end;
  end;
end;

function listadoClickUp_(listado: TStringGrid; fila: integer; lista: TList;
  Shift: TShiftState; swapProc: TProcSwap; var xModificado: boolean;
  colores: TDAOfColores = nil): integer;
var
  swap: string;
  filaSwap: array of string;
  i, j, filaDestino: integer;
  iFilaEnLaLista: integer;

  topLeftCelda: TRect;
  nuevaMousePos: TPoint;

  procedure swapcolores(i1, i2: integer);
  var
    aux: TColor;
  begin
    if colores <> nil then
    begin
      aux := colores[i1];
      colores[i1] := colores[i2];
      colores[i2] := aux;
    end;
  end;

begin
  if fila <> listado.FixedRows then
  begin
    if ssShift in Shift then
    begin
      filaDestino := listado.FixedRows;
      SetLength(filaSwap, listado.ColCount);
      for i := 0 to listado.ColCount - 1 do
        filaSwap[i] := listado.Cells[i, fila];

      for j := fila downto filaDestino + 1 do
      begin
        for i := 0 to listado.ColCount - 1 do
          listado.Cells[i, j] := listado.Cells[i, j - 1];

        iFilaEnLaLista := j - listado.FixedRows;
        //desplazamos la cantidad de filas que haya en el encabezado del listado
        if lista <> nil then
        begin
          lista.Exchange(iFilaEnLaLista, iFilaEnLaLista - 1);
          if Assigned(swapProc) then
            swapProc(lista[iFilaEnLaLista - 1], lista[iFilaEnLaLista]);
        end;
        swapcolores(iFilaEnLaLista - 1, iFilaEnLaLista);
      end;

      for i := 0 to listado.ColCount - 1 do
        listado.Cells[i, filaDestino] := filaSwap[i];
    end
    else
    begin
      filaDestino := fila - 1;
      for i := 0 to listado.ColCount - 1 do
      begin
        swap := listado.Cells[i, filaDestino];
        listado.Cells[i, filaDestino] := listado.Cells[i, fila];
        listado.Cells[i, fila] := swap;
      end;
      if lista <> nil then
      begin
        lista.Exchange(fila - 1, filaDestino - 1);
        if Assigned(swapProc) then
          swapProc(lista[filaDestino - 1], lista[fila - 1]);
      end;
      swapcolores(filaDestino - 1, fila - 1);
    end;

    listado.Row := filaDestino;

    topLeftCelda := listado.CellRect(colListado, filaDestino);
    nuevaMousePos.X := topLeftCelda.Left + listado.ColWidths[colListado] div 2;
    nuevaMousePos.Y := topLeftCelda.Top + listado.RowHeights[filaDestino] div 2;
    nuevaMousePos := listado.ClientToScreen(nuevaMousePos);
    mouse.CursorPos := nuevaMousePos;

    Result := filaDestino;
    xModificado := True;
  end
  else
    Result := -1;
end;

function listadoClickDown_(listado: TStringGrid; fila: integer;
  lista: TList; Shift: TShiftState; swapProc: TProcSwap; var xModificado: boolean;
  colores: TDAOfColores = nil): integer;
var
  swap: string;
  filaSwap: array of string;
  i, j, filaDestino: integer;
  iFilaEnLaLista: integer;

  topLeftCelda: TRect;
  nuevaMousePos: TPoint;

  procedure swapcolores(i1, i2: integer);
  var
    aux: TColor;
  begin
    if colores <> nil then
    begin
      aux := colores[i1];
      colores[i1] := colores[i2];
      colores[i2] := aux;
    end;
  end;

begin
  if fila <> listado.RowCount - 1 then
  begin
    if ssShift in Shift then
    begin
      filaDestino := listado.RowCount - 1;
      SetLength(filaSwap, listado.ColCount);
      for i := 0 to listado.ColCount - 1 do
        filaSwap[i] := listado.Cells[i, fila];

      for j := fila to filaDestino - 1 do
      begin
        for i := 0 to listado.ColCount - 1 do
          listado.Cells[i, j] := listado.Cells[i, j + 1];

        iFilaEnLaLista := j - listado.FixedRows;
        //desplazamos la cantidad de filas que haya en el encabezado del listado
        if lista <> nil then
        begin
          lista.Exchange(iFilaEnLaLista, iFilaEnLaLista + 1);
          if Assigned(swapProc) then
            swapProc(lista[iFilaEnLaLista + 1], lista[iFilaEnLaLista]);
        end;
        swapcolores(iFilaEnLaLista + 1, iFilaEnLaLista);
      end;

      for i := 0 to listado.ColCount - 1 do
        listado.Cells[i, filaDestino] := filaSwap[i];
    end
    else
    begin
      filaDestino := fila + 1;
      for i := 0 to listado.ColCount - 1 do
      begin
        swap := listado.Cells[i, filaDestino];
        listado.Cells[i, filaDestino] := listado.Cells[i, fila];
        listado.Cells[i, fila] := swap;
      end;
      if lista <> nil then
      begin
        lista.Exchange(fila - 1, filaDestino - 1);
        if Assigned(swapProc) then
          swapProc(lista[filaDestino - 1], lista[fila - 1]);
      end;
      swapcolores(filaDestino - 1, fila - 1);
    end;

    listado.Row := filaDestino;

    topLeftCelda := listado.CellRect(colListado, filaDestino);
    nuevaMousePos.X := topLeftCelda.Left + listado.ColWidths[colListado] div 2;
    nuevaMousePos.Y := topLeftCelda.Top + listado.RowHeights[filaDestino] div 2;
    nuevaMousePos := listado.ClientToScreen(nuevaMousePos);
    mouse.CursorPos := nuevaMousePos;

    Result := filaDestino;
    xModificado := True;
  end
  else
    Result := -1;
end;

function sgBuscarFila(tabla: TStringGrid; columna: integer;
  const contenido: string): integer;
var
  i, res: integer;
begin
  res := -1;
  for i := tabla.FixedRows to tabla.RowCount - 1 do
    if tabla.Cells[columna, i] = contenido then
    begin
      res := i;
      break;
    end;
  Result := res;
end;

procedure sgSeleccionarFila(tabla: TStringGrid; fila: integer);
var
  rectSeleccion: TGridRect;
begin
  with rectSeleccion do
  begin
    Top := fila;
    Left := tabla.FixedCols;
    Right := tabla.ColCount - 1;
    Bottom := Top;
  end;
  tabla.Selection := rectSeleccion;
  tabla.Row := fila;
end;

function sgBuscarYSeleccionarFila(tabla: TStringGrid; columna: integer;
  const contenido: string): integer;
var
  fila: integer;
begin
  fila := sgBuscarFila(tabla, columna, contenido);
  if (fila >= tabla.FixedRows) and (fila < tabla.RowCount) then
    tabla.Row := fila;
  //sgSeleccionarFila(tabla, fila);
  Result := fila;
end;

(*
procedure centrar1Boton(contenedor: TWinControl; boton: TButton);
begin
  boton.Left := contenedor.ClientWidth div 2 - boton.Width div 2;
end;

procedure centrar2Botones(contenedor: TWinControl; boton1, boton2: TButton);
begin
  boton1.Left := contenedor.ClientWidth div 4 - boton1.Width div 2;
  boton2.Left := contenedor.ClientWidth * 3 div 4 - boton2.Width div 2;
end;

*)
function titulo(nombreArchivo: string): string;
var
  i, posUltimoPunto: integer;
begin
  posUltimoPunto := Length(nombreArchivo);
  for i := Length(nombreArchivo) - 1 downto 0 do
    if nombreArchivo[i] = '.' then
    begin
      posUltimoPunto := i;
      break;
    end;
  Result := copy(nombreArchivo, 0, posUltimoPunto - 1);
end;


procedure deshabilitarControles(winControl: TWinControl);
var
  i: integer;
begin
  for i := 0 to winControl.ControlCount - 1 do
    if winControl.Controls[i] is TWinControl then
      winControl.Controls[i].Enabled := False;
end;

procedure habilitarControles(winControl: TWinControl);
var
  i: integer;
begin
  for i := 0 to winControl.ControlCount - 1 do
    if winControl.Controls[i] is TWinControl then
      winControl.Controls[i].Enabled := True;
end;

procedure MensajeInformacion(const titulo, texto: string);
begin
  Application.MessageBox(PChar(texto), PChar(titulo), (MB_OK + MB_ICONINFORMATION));
end;

procedure MensajeError(const titulo, texto: string);
begin
  Application.MessageBox(PChar(texto), PChar(titulo), (MB_OK + MB_ICONERROR));
end;

function MensajeSiNo(const titulo, texto: string): boolean;
begin
  if Application.MessageBox(PChar(texto), PChar(titulo),
    (MB_YESNO + MB_ICONQUESTION)) = idYes then
    Result := True
  else
    Result := False;
end;

{ TgCG }

function TgCG.OffsetToColRow(IsCol, Fisical: boolean; Offset: integer; out
  Index, Rest: integer): boolean;
begin
  Result := inherited OffsetToColRow(IsCol, Fisical, Offset, Index, Rest);
end;


initialization

  begin
    formatoFecha := ' (' + SysUtils.ShortDateFormat + ' ' +
      SysUtils.ShortTimeFormat + ')';
    editedCol := -1;
    editedFila := -1;
  end;

end.
