unit uViewDetallesBase;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, uViewBase, cxStyles, ComCtrls, ToolWin, ActnList, cxCustomData,
  cxGraphics, cxFilter, cxData, cxDataStorage, cxEdit, DB, cxDBData,
  uDADataTable, cxGridLevel, cxClasses, cxControls, cxGridCustomView,
  cxGridCustomTableView, cxGridTableView, cxGridDBTableView, ImgList,
  PngImageList, cxGrid, cxImageComboBox, cxTextEdit, cxMaskEdit, cxCheckBox,
  uGridStatusUtils, uControllerDetallesBase, cxCurrencyEdit,
  ExtCtrls, Grids, DBGrids, StdCtrls,
  ExtActns, StdActns, cxRichEdit, JvExStdCtrls, JvCombobox, JvColorCombo,
  TB2Item, TBX, TB2Dock, TB2Toolbar, uDAInterfaces, cxContainer, 
  Menus, cxGridCustomPopupMenu, cxGridPopupMenu;

type
  IViewDetallesBase = interface(IViewBase)
  ['{852EB860-13B6-4355-A6B0-4542AB16896F}']
    procedure ExpandirTodo;
    procedure ContraerTodo;
    procedure AjustarAncho;

    procedure GotoFirst;
    procedure GotoLast;

    procedure SaveGridStatus;
    procedure RestoreGridStatus;

    procedure SeleccionarTodo;

    procedure BeginUpdate;
    procedure EndUpdate;

    function IsEmpty : Boolean;

    function GetFocusedView : TcxGridDBTableView;
    property _FocusedView : TcxGridDBTableView read GetFocusedView;

    function GetGrid : TcxGrid;
    property _Grid : TcxGrid read GetGrid;
  end;

  TfrViewDetallesBase = class(TfrViewBase, IViewDetallesBase)
    ActionListContenido: TActionList;
    DADataSource: TDADataSource;
    actAnadir: TAction;
    actEliminar: TAction;
    actSubir: TAction;
    actBajar: TAction;
    ContenidoImageList: TPngImageList;
    ToolBar1: TToolBar;
    cxStyleRepository: TcxStyleRepository;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ToolButton4: TToolButton;
    cxGrid: TcxGrid;
    cxGridView: TcxGridDBTableView;
    cxGridViewID: TcxGridDBColumn;
    cxGridViewPOSICION: TcxGridDBColumn;
    cxGridViewTIPO: TcxGridDBColumn;
    cxGridViewDESCRIPCION: TcxGridDBColumn;
    cxGridViewCANTIDAD: TcxGridDBColumn;
    cxGridViewIMPORTEUNIDAD: TcxGridDBColumn;
    cxGridViewIMPORTETOTAL: TcxGridDBColumn;
    cxGridViewVISIBLE: TcxGridDBColumn;
    cxGridLevel: TcxGridLevel;
    RichEditBold1: TRichEditBold;
    RichEditItalic1: TRichEditItalic;
    ToolButton6: TToolButton;
    ToolButton7: TToolButton;
    RichEditUnderline1: TRichEditUnderline;
    RichEditAlignLeft1: TRichEditAlignLeft;
    RichEditAlignRight1: TRichEditAlignRight;
    RichEditAlignCenter1: TRichEditAlignCenter;
    ToolButton8: TToolButton;
    ToolButton9: TToolButton;
    ToolButton10: TToolButton;
    ToolButton11: TToolButton;
    ToolButton12: TToolButton;
    ToolButton13: TToolButton;
    FontEdit1: TFontEdit;
    ToolButton14: TToolButton;
    UpDown1: TUpDown;
    FontSize: TEdit;
    FontName: TJvFontComboBox;
    TBXDock1: TTBXDock;
    TBXToolbar1: TTBXToolbar;
    TBXItem1: TTBXItem;
    TBXItem2: TTBXItem;
    TBXSeparatorItem1: TTBXSeparatorItem;
    TBXItem3: TTBXItem;
    TBXItem4: TTBXItem;
    TBXSeparatorItem2: TTBXSeparatorItem;
    TBXItem5: TTBXItem;
    TBXItem6: TTBXItem;
    TBXItem7: TTBXItem;
    TBXSeparatorItem3: TTBXSeparatorItem;
    TBXItem8: TTBXItem;
    TBXSeparatorItem4: TTBXSeparatorItem;
    TBXItem9: TTBXItem;
    TBXItem10: TTBXItem;
    TBXItem11: TTBXItem;
    cxStyle_SUBTOTAL: TcxStyle;    
    cxStyle_TITULO: TcxStyle;
    actAnchoAutomatico: TAction;
    TBXSeparatorItem5: TTBXSeparatorItem;
    TBXItem13: TTBXItem;
    cxStyle_DESCUENTO: TcxStyle;
    actDetallesCortar: TAction;
    actDetallesCopiar: TAction;
    actDetallesPegar: TAction;
    cxStyle_SOLO_LECTURA: TcxStyle;
    cxStyle_ESPECIALES: TcxStyle;
    cxStyle_NORMAL_PAR: TcxStyle;
    cxStyle_NORMAL_IMPAR: TcxStyle;
    TBXSubmenuItem1: TTBXSubmenuItem;
    TBXSeparatorItem6: TTBXSeparatorItem;
    actAnadirCapitulo: TAction;
    actAnadirTitulo: TAction;
    actAnadirSubtotal: TAction;
    actAnadirDescuento: TAction;
    TBXItem12: TTBXItem;
    TBXItem14: TTBXItem;
    TBXItem15: TTBXItem;
    TBXItem16: TTBXItem;
    TBXSeparatorItem7: TTBXSeparatorItem;
    cxGridPopupMenu: TcxGridPopupMenu;
    GridGeneralPopupMenu: TPopupMenu;
    dsds1: TMenuItem;
    Bajar1: TMenuItem;
    Eliminar1: TMenuItem;
    N1: TMenuItem;
    Copiar1: TMenuItem;
    Copiar2: TMenuItem;
    Pegar1: TMenuItem;
    N3: TMenuItem;
    Aadir2: TMenuItem;
    N4: TMenuItem;
    Anchoautomtico2: TMenuItem;
    actExportar: TAction;
    ExportaraMicrosoftExcel1: TMenuItem;
    actSeleccionarTodo: TAction;
    Seleccionartodo1: TMenuItem;
    cxStyle_TITULO_OPCIONAL: TcxStyle;
    TBXItem17: TTBXItem;
    procedure actAnadirExecute(Sender: TObject);
    procedure actEliminarExecute(Sender: TObject);
    procedure actSubirExecute(Sender: TObject);
    procedure actBajarExecute(Sender: TObject);
    procedure actEliminarUpdate(Sender: TObject);
    procedure actSubirUpdate(Sender: TObject);
    procedure actBajarUpdate(Sender: TObject);
    procedure actAnadirUpdate(Sender: TObject);

    procedure cxGridViewEditValueChanged(Sender: TcxCustomGridTableView; AItem: TcxCustomGridTableItem);
    procedure cxGridViewEditKeyDown(Sender: TcxCustomGridTableView;
      AItem: TcxCustomGridTableItem; AEdit: TcxCustomEdit; var Key: Word; Shift: TShiftState);
    procedure cxGridViewKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

    procedure cxGridViewInitEdit(Sender: TcxCustomGridTableView;
      AItem: TcxCustomGridTableItem; AEdit: TcxCustomEdit);
    procedure FontEdit1BeforeExecute(Sender: TObject);
    procedure FontEdit1Accept(Sender: TObject);

    procedure CustomViewCreate(Sender: TObject);
    procedure CustomViewDestroy(Sender: TObject);
    procedure FontSizeChange(Sender: TObject);
    procedure FontNameChange(Sender: TObject);

    procedure cxGridViewEditing(Sender: TcxCustomGridTableView;
      AItem: TcxCustomGridTableItem; var AAllow: Boolean);
    procedure cxGridViewStylesGetContentStyle(Sender: TcxCustomGridTableView;
      ARecord: TcxCustomGridRecord; AItem: TcxCustomGridTableItem;
      out AStyle: TcxStyle);
    procedure TBXItem13Click(Sender: TObject);
    procedure actAnchoAutomaticoExecute(Sender: TObject);
    procedure actAnchoAutomaticoUpdate(Sender: TObject);
    procedure actAnadirCapituloUpdate(Sender: TObject);
    procedure actAnadirTituloUpdate(Sender: TObject);
    procedure actAnadirSubtotalUpdate(Sender: TObject);
    procedure actAnadirDescuentoUpdate(Sender: TObject);
    procedure actAnadirCapituloExecute(Sender: TObject);
    procedure actAnadirTituloExecute(Sender: TObject);
    procedure actAnadirSubtotalExecute(Sender: TObject);
    procedure actAnadirDescuentoExecute(Sender: TObject);
    procedure actDetallesCopiarExecute(Sender: TObject);
    procedure actDetallesCopiarUpdate(Sender: TObject);
    procedure actDetallesPegarExecute(Sender: TObject);
    procedure actSeleccionarTodoExecute(Sender: TObject);
    procedure actSeleccionarTodoUpdate(Sender: TObject);
    procedure actDetallesPegarUpdate(Sender: TObject);
    procedure actDetallesCortarExecute(Sender: TObject);
    procedure actDetallesCortarUpdate(Sender: TObject);
    procedure cxGridViewFocusedItemChanged(Sender: TcxCustomGridTableView;
      APrevFocusedItem, AFocusedItem: TcxCustomGridTableItem);
    procedure cxGridViewFocusedRecordChanged(Sender: TcxCustomGridTableView;
      APrevFocusedRecord, AFocusedRecord: TcxCustomGridRecord;
      ANewItemRecordFocusingChanged: Boolean);
    procedure actExportarExecute(Sender: TObject);

  private
    FController : IControllerDetallesBase;
    FDetalles: IDAStronglyTypedDataTable;
    FGridStatus: TcxGridStatus;
    CurEdit: TcxRichEdit;
    FUpdating: Boolean;
    function CurrText: TTextAttributes;
//    procedure OnSelectChange(Sender:TObject);

    function GetController: IControllerDetallesBase;
    procedure SetController(const Value: IControllerDetallesBase);
    function GetDetalles: IDAStronglyTypedDataTable;
    procedure SetDetalles(const Value: IDAStronglyTypedDataTable);

    function DarPosicionCAMPO(const Nombre:String): Integer;

    procedure DoPasteText(Sender: TObject; AText: string);
    procedure TratamientoTeclas(Key: Word; Shift: TShiftState; AItem: TcxCustomGridTableItem = nil);

  protected
    function DarListaSeleccionados: TIntegerArray;
    function HayQueRecalcular(AItem: TcxCustomGridTableItem): Boolean; virtual;
    function EsTipoEditable(AItem: TcxCustomGridTableItem): Boolean; virtual;
    function DarTipoConcepto(ARecord: TcxCustomGridRecord): string; virtual;
    function DarFuentePorDefecto(const ATipo : String = TIPO_DETALLE_CONCEPTO): TFont; virtual;
    function GetFocusedView : TcxGridDBTableView; virtual;
    function GetGrid : TcxGrid; virtual;
    procedure SeleccionarFilaActual;

  public
    procedure GotoFirst;
    procedure GotoLast;
    procedure BeginUpdate;
    procedure EndUpdate;
    procedure SaveGridStatus;
    procedure RestoreGridStatus;
    procedure ExpandirTodo;
    procedure ContraerTodo;
    procedure AjustarAncho;
    procedure SeleccionarTodo;
    function IsEmpty : Boolean;
    destructor Destroy; override;
    property _FocusedView : TcxGridDBTableView read GetFocusedView;
    property _Grid : TcxGrid read GetGrid;
    property Controller: IControllerDetallesBase read GetController write SetController;
    property Detalles: IDAStronglyTypedDataTable read GetDetalles write SetDetalles;
  end;

implementation
{$R *.dfm}

uses
  uDMBase, uCalculosUtils, dxOffice11, uSistemaFunc, uGridClipboardUtils,
  Clipbrd, cxGridExportLink;

type
  TOnPaste = procedure(Sender: TObject; AText: string) of object;

  TcxMyRichEdit = class(TcxRichEdit)
  private
    FOnPaste: TOnPaste;
    FOldWndProc : TWndMethod;
    FGrid: TcxGrid;
    procedure InnerControlWindowProc(var Message: TMessage);
    procedure FInnerRichOnKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  protected
    procedure DoPaste; virtual;
    procedure Initialize; override;
    procedure HookInnerControl;
  public
    procedure PasteFromClipboard; override;
    destructor Destroy; override;
    property _Grid : TcxGrid read FGrid write FGrid;
    property OnPaste: TOnPaste read FOnPaste write FOnPaste;
  end;

  TcxMyRichEditProperties = class(TcxRichEditProperties)
  public
    class function GetContainerClass: TcxContainerClass; override;
  end;


var
  _FRecalculando : Boolean;
  _FRecalcular : Boolean;
  _FPegando : Boolean;

function EnumFontsProc(var LogFont: TLogFont; var TextMetric: TTextMetric;
  FontType: Integer; Data: Pointer): Integer; stdcall;
begin
  TStrings(Data).Add(LogFont.lfFaceName);
  Result := 1;
end;


procedure TfrViewDetallesBase.actAnadirCapituloExecute(Sender: TObject);
var
  bEsMultiSelect : Boolean;
begin
  // Debo quitar el multiselect porque provoca que se quede seleccionado
  // el registro actual y no el nuevo registro que voy a añadir
  bEsMultiSelect := _FocusedView.OptionsSelection.MultiSelect;
  if bEsMultiSelect then
    _FocusedView.OptionsSelection.MultiSelect := False;

  ShowHourglassCursor;
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
    begin
        Controller.Add(FDetalles, TIPO_DETALLE_TITULO);
        Controller.Add(FDetalles, TIPO_DETALLE_CONCEPTO);
        Controller.Add(FDetalles, TIPO_DETALLE_CONCEPTO);
        Controller.Add(FDetalles, TIPO_DETALLE_SUBTOTAL);
//        Controller.add(FDetalles, TIPO_DETALLE_DESCUENTO);
    end;
  finally
    _FocusedView.EndUpdate;
    HideHourglassCursor;

    // Dejo la propiedad MultiSelect como estaba
    if bEsMultiSelect then
       _FocusedView.OptionsSelection.MultiSelect := bEsMultiSelect;
  end;
end;

procedure TfrViewDetallesBase.actAnadirCapituloUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := not ReadOnly;
end;

procedure TfrViewDetallesBase.actAnadirDescuentoExecute(Sender: TObject);
var
  bEsMultiSelect : Boolean;
begin
  // Debo quitar el multiselect porque provoca que se quede seleccionado
  // el registro actual y no el nuevo registro que voy a añadir
  bEsMultiSelect := cxGridView.OptionsSelection.MultiSelect;
  if bEsMultiSelect then
    _FocusedView.OptionsSelection.MultiSelect := False;

  ShowHourglassCursor;
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
        Controller.Add(FDetalles, TIPO_DETALLE_DESCUENTO);
  finally
    _FocusedView.EndUpdate;
    HideHourglassCursor;

    // Dejo la propiedad MultiSelect como estaba
    if bEsMultiSelect then
       _FocusedView.OptionsSelection.MultiSelect := bEsMultiSelect;
  end;
end;

procedure TfrViewDetallesBase.actAnadirDescuentoUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := not ReadOnly;
end;

procedure TfrViewDetallesBase.actAnadirExecute(Sender: TObject);
var
  bEsMultiSelect : Boolean;
begin
  // Debo quitar el multiselect porque provoca que se quede seleccionado
  // el registro actual y no el nuevo registro que voy a añadir
  bEsMultiSelect := cxGridView.OptionsSelection.MultiSelect;
  if bEsMultiSelect then
    _FocusedView.OptionsSelection.MultiSelect := False;

  ShowHourglassCursor;
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
      Controller.Add(FDetalles, TIPO_DETALLE_CONCEPTO);
  finally
    _FocusedView.EndUpdate;
    HideHourglassCursor;
    
    // Dejo la propiedad MultiSelect como estaba
    if bEsMultiSelect then
       _FocusedView.OptionsSelection.MultiSelect := bEsMultiSelect;
  end;
end;

procedure TfrViewDetallesBase.actAnadirSubtotalExecute(Sender: TObject);
var
  bEsMultiSelect : Boolean;
begin
  // Debo quitar el multiselect porque provoca que se quede seleccionado
  // el registro actual y no el nuevo registro que voy a añadir
  bEsMultiSelect := cxGridView.OptionsSelection.MultiSelect;
  if bEsMultiSelect then
    _FocusedView.OptionsSelection.MultiSelect := False;

  ShowHourglassCursor;
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
        Controller.Add(FDetalles, TIPO_DETALLE_SUBTOTAL);
  finally
    _FocusedView.EndUpdate;
    HideHourglassCursor;
    
    // Dejo la propiedad MultiSelect como estaba
    if bEsMultiSelect then
       _FocusedView.OptionsSelection.MultiSelect := bEsMultiSelect;
  end;
end;

procedure TfrViewDetallesBase.actAnadirSubtotalUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := not ReadOnly;
end;

procedure TfrViewDetallesBase.actAnadirTituloExecute(Sender: TObject);
var
  bEsMultiSelect : Boolean;
begin
  // Debo quitar el multiselect porque provoca que se quede seleccionado
  // el registro actual y no el nuevo registro que voy a añadir
  bEsMultiSelect := cxGridView.OptionsSelection.MultiSelect;
  if bEsMultiSelect then
    cxGridView.OptionsSelection.MultiSelect := False;

  ShowHourglassCursor;
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
        Controller.Add(FDetalles, TIPO_DETALLE_TITULO);
  finally
    _FocusedView.EndUpdate;
    HideHourglassCursor;

    // Dejo la propiedad MultiSelect como estaba
    if bEsMultiSelect then
       _FocusedView.OptionsSelection.MultiSelect := bEsMultiSelect;
  end;
end;

procedure TfrViewDetallesBase.actAnadirTituloUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := not ReadOnly;
end;

procedure TfrViewDetallesBase.actEliminarExecute(Sender: TObject);
var
  AuxTop, AuxRow:Integer;

begin
  ShowHourglassCursor;
  _FocusedView.BeginUpdate;
  try
    if Assigned(Controller) and Assigned(FDetalles) then
    begin
      AuxTop := cxGridView.Controller.TopRowIndex;
      AuxRow := cxGridView.DataController.FocusedRowIndex;

      Controller.Delete(FDetalles, darListaSeleccionados);

      if (FDetalles.RecordCount > 0) then
      begin
        //Selecciona en el grid el registro siguiente
        if (AuxRow < cxGridView.DataController.RowCount-1) then
           Inc(AuxRow)
        else
           Dec(AuxRow);

        _FocusedView.DataController.SelectRows(AuxRow,AuxRow);
        _FocusedView.Controller.TopRowIndex := AuxTop;
      end;
    end
  finally
    _FocusedView.EndUpdate;
    HideHourglassCursor;
  end;

  SeleccionarFilaActual;
end;

procedure TfrViewDetallesBase.actEliminarUpdate(Sender: TObject);
begin
  if not Assigned(DADataSource.DataTable) then
    (Sender as TAction).Enabled := False
  else
    (Sender as TAction).Enabled := (not ReadOnly)
                                   and (not DADataSource.DataTable.IsEmpty)
end;

procedure TfrViewDetallesBase.actExportarExecute(Sender: TObject);
var
  AFileName : String;

begin
  inherited;
  if PreguntarFicheroExcelExportar(AFileName) then
     ExportGridToExcel(AFileName, cxGrid);
end;

procedure TfrViewDetallesBase.actSubirUpdate(Sender: TObject);
begin
  inherited;
  if not Assigned(_FocusedView.Controller.FocusedRow) then
    (Sender as TAction).Enabled := False
  else
    (Sender as TAction).Enabled := (not ReadOnly)
                                   and (not _FocusedView.Controller.FocusedRow.IsFirst)
end;

procedure TfrViewDetallesBase.AjustarAncho;
begin
  if Assigned(_FocusedView) then
    _FocusedView.ApplyBestFit;
end;

procedure TfrViewDetallesBase.BeginUpdate;
begin
  if Assigned(_FocusedView) then
    _FocusedView.BeginUpdate;
end;

function TfrViewDetallesBase.DarTipoConcepto(ARecord: TcxCustomGridRecord): string;
var
  IndiceCol : Integer;
begin
  IndiceCol := cxGridView.GetColumnByFieldName(CAMPO_TIPO).Index;
  Result := ARecord.DisplayTexts[IndiceCol];
end;

destructor TfrViewDetallesBase.Destroy;
begin
  FController := NIL;
  FDetalles := NIL;  

  if Assigned(FGridStatus) then
    FreeAndNil(FGridStatus);
  inherited;
end;

procedure TfrViewDetallesBase.DoPasteText(Sender: TObject; AText: string);
begin
//  ShowMessage(AText);
end;

procedure TfrViewDetallesBase.ContraerTodo;
begin
  if Assigned(_FocusedView) then
    _FocusedView.ViewData.Collapse(True);
end;

function TfrViewDetallesBase.CurrText: TTextAttributes;
begin
  Result := NIL;
  if Assigned(CurEdit) then
//    if CurEdit.FindSelection then
    if CurEdit.SelLength > 0 then
      Result := CurEdit.SelAttributes
    else
      Result := CurEdit.DefAttributes;
end;

procedure TfrViewDetallesBase.CustomViewCreate(Sender: TObject);
begin
  inherited;
  //Debemos tener la certeza de que las operaciones del grid estan deshabilitadas y que
  //cuando pulsamos teclas de insercion flecha abajo y demas salta nuestra propia lógia del controlador
  cxGridView.OptionsData.Appending := False;
  cxGridView.OptionsData.Deleting := False;
  cxGridView.OptionsData.Inserting := False;

  CurEdit := Nil;
  FUpdating := False;
//  cxGridView.OptionsView.RowSeparatorColor := dmBase.StyleManager.Colors.ButtonSeparatorColor;

//  No quiero RTF para la columna de DESCRIPCION
//  cxGridViewDESCRIPCION.PropertiesClass := TcxMyRichEditProperties;
end;

procedure TfrViewDetallesBase.CustomViewDestroy(Sender: TObject);
begin
  inherited;
  CurEdit := Nil;
end;

procedure TfrViewDetallesBase.cxGridViewEditing(Sender: TcxCustomGridTableView;
  AItem: TcxCustomGridTableItem; var AAllow: Boolean);
begin
  AAllow := EsTipoEditable(AItem);
end;

procedure TfrViewDetallesBase.cxGridViewEditKeyDown(
  Sender: TcxCustomGridTableView; AItem: TcxCustomGridTableItem;
  AEdit: TcxCustomEdit; var Key: Word; Shift: TShiftState);
begin
  inherited;
  TratamientoTeclas(Key, Shift, AItem);
end;

procedure TfrViewDetallesBase.cxGridViewEditValueChanged(Sender: TcxCustomGridTableView; AItem: TcxCustomGridTableItem);
begin
  inherited;
  if not _FRecalcular and HayQueRecalcular(AItem) then
    _FRecalcular := True;
end;

procedure TfrViewDetallesBase.cxGridViewFocusedItemChanged(
  Sender: TcxCustomGridTableView; APrevFocusedItem,
  AFocusedItem: TcxCustomGridTableItem);
begin
  inherited;
  if _FRecalcular and not _FRecalculando then //and HayQueRecalcular(APrevFocusedItem) then
  begin
    _FRecalculando := True;
    try
      if Sender.Controller.EditingController.IsEditing then
        Sender.Controller.EditingController.Edit.PostEditValue;
      Controller.ActualizarTotales(Detalles);
      _FRecalcular := False;
    finally
      _FRecalculando := False;
    end;
  end;
end;

procedure TfrViewDetallesBase.cxGridViewFocusedRecordChanged(
  Sender: TcxCustomGridTableView; APrevFocusedRecord,
  AFocusedRecord: TcxCustomGridRecord; ANewItemRecordFocusingChanged: Boolean);
begin
  inherited;

  if _FRecalcular and not _FRecalculando then //and HayQueRecalcular(cxGridView.Controller.FocusedItem) then
  begin
    _FRecalculando := True;
    try
      if Sender.Controller.EditingController.IsEditing then
        Sender.Controller.EditingController.Edit.PostEditValue;
      Controller.ActualizarTotales(Detalles);
      _FRecalcular := False;      
    finally
      _FRecalculando := False;
    end;
  end;
end;

procedure TfrViewDetallesBase.cxGridViewInitEdit(Sender: TcxCustomGridTableView;
  AItem: TcxCustomGridTableItem; AEdit: TcxCustomEdit);
var
  FuentePorDefecto: TFont;
  ARecord: TcxCustomGridRecord;
begin
  inherited;

  if AEdit is TcxMyRichEdit then
  begin
    TcxMyRichEdit(AEdit).OnPaste := DoPasteText;
    TcxMyRichEdit(AEdit)._Grid := _Grid;
  end;

  // Este exit es para que no se genere código RTF para el tamaño de la fuente
  // porque luego sale demasiado grande en los informes impresos. 
  Exit; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  if AEdit is TcxRichEdit then
  begin
    ARecord := AItem.FocusedCellViewInfo.GridRecord;
    FuentePorDefecto := DarFuentePorDefecto(DarTipoConcepto(ARecord));
    try
      //La primera vez que accedemos al grid entra dos veces y perderiamos el editor
      //dando un pete.
      if not Assigned(CurEdit) then
      begin
        CurEdit := TcxRichEdit(AEdit);
        FontEdit1.Enabled := True;
        RichEditBold1.Enabled := True;
        RichEditItalic1.Enabled := True;
        RichEditUnderline1.Enabled := True;
        RichEditAlignLeft1.Enabled := True;
        RichEditAlignRight1.Enabled := True;
        RichEditAlignCenter1.Enabled := True;

        if not VarIsNull(AItem.EditValue) and VarIsType(AItem.EditValue, varString) then
          if Length(AItem.EditValue) = 0 then
             CurEdit.DefAttributes.Assign(FuentePorDefecto);
      end
      else begin
        if not VarIsNull(AItem.EditValue) and VarIsType(AItem.EditValue, varString) then
          if Length(AItem.EditValue) = 0 then
             CurEdit.DefAttributes.Assign(FuentePorDefecto);
      end;
    finally
      FreeAndNil(FuentePorDefecto);
    end;
  end
  else
  begin
    CurEdit := Nil;
    FontEdit1.Enabled := False;
    RichEditBold1.Enabled := False;
    RichEditItalic1.Enabled := False;
    RichEditUnderline1.Enabled := False;
    RichEditAlignLeft1.Enabled := False;
    RichEditAlignRight1.Enabled := False;
    RichEditAlignCenter1.Enabled := False;
  end;
end;

procedure TfrViewDetallesBase.cxGridViewKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  inherited;
  TratamientoTeclas(Key, Shift);
end;

procedure TfrViewDetallesBase.cxGridViewStylesGetContentStyle(
  Sender: TcxCustomGridTableView; ARecord: TcxCustomGridRecord;
  AItem: TcxCustomGridTableItem; out AStyle: TcxStyle);
begin
  inherited;

  AStyle := NIL;
  if Assigned(AItem) then
  begin
    if Odd(ARecord.Index) then
      AStyle := cxStyle_NORMAL_PAR
    else
      AStyle := cxStyle_NORMAL_IMPAR;
  end;

  if Assigned(ARecord) then
  begin

    if ARecord.Values[3] = TIPO_DETALLE_TITULO then
      AStyle := cxStyle_TITULO;

    if ARecord.Values[3] = TIPO_DETALLE_TITULO_OPCIONAL then
      AStyle := cxStyle_TITULO_OPCIONAL;

    if ARecord.Values[3] = TIPO_DETALLE_SUBTOTAL then
      AStyle := cxStyle_SUBTOTAL;

    if ARecord.Values[3] = TIPO_DETALLE_DESCUENTO then
      AStyle := cxStyle_DESCUENTO;
  end;

end;

function TfrViewDetallesBase.DarFuentePorDefecto(const ATipo: String): TFont;
begin
  Result := TFont.Create;

  if ATipo = TIPO_DETALLE_CONCEPTO then
  begin
    Result.Name := 'Tahoma';
    Result.Size := 11;
    Result.Style := [];
  end;

  if ATipo = TIPO_DETALLE_TITULO then
  begin
    Result.Name := cxStyle_TITULO.Font.Name;
    Result.Color := cxStyle_TITULO.TextColor;
    Result.Size := cxStyle_TITULO.Font.Size;
    Result.Style := cxStyle_TITULO.Font.Style;
  end;

  if ATipo = TIPO_DETALLE_TITULO_OPCIONAL then
  begin
    Result.Name := cxStyle_TITULO_OPCIONAL.Font.Name;
    Result.Color := cxStyle_TITULO_OPCIONAL.TextColor;
    Result.Size := cxStyle_TITULO_OPCIONAL.Font.Size;
    Result.Style := cxStyle_TITULO_OPCIONAL.Font.Style;
  end;

  if ATipo = TIPO_DETALLE_SUBTOTAL then
  begin
    Result.Name := cxStyle_SUBTOTAL.Font.Name;
    Result.Color := cxStyle_SUBTOTAL.TextColor;
    Result.Size := cxStyle_SUBTOTAL.Font.Size;
    Result.Style := cxStyle_SUBTOTAL.Font.Style;
  end;

  if ATipo = TIPO_DETALLE_DESCUENTO then
  begin
    Result.Name := cxStyle_DESCUENTO.Font.Name;
    Result.Color := cxStyle_DESCUENTO.TextColor;
    Result.Size := cxStyle_DESCUENTO.Font.Size;
    Result.Style := cxStyle_DESCUENTO.Font.Style;
  end;
end;

function TfrViewDetallesBase.DarListaSeleccionados: TIntegerArray;
var
  i, j: Integer;
begin
  j := DarPosicionCampo(CAMPO_POSICION);

  with cxGridView.Controller do
       for i:=0 to SelectedRecordCount-1 do
       begin
           SetLength(Result, i+1);
           Result[i] := SelectedRecords[i].Values[j];
       end;
end;

function TfrViewDetallesBase.DarPosicionCAMPO(const Nombre: String): Integer;
var
  i: Integer;
begin
  i:=0;
  while ((cxGridView.Columns[i].DataBinding.FieldName <> Nombre) and
         (i < cxGridView.ColumnCount)) do
    inc(i);

  if (i = cxGridView.ColumnCount) then
    raise Exception.Create('El campo ' + Nombre + ' no se ha encontrado en el grid (uViewDetallesBase)');

  Result := i;
end;

procedure TfrViewDetallesBase.EndUpdate;
begin
  if Assigned(_FocusedView) then
    _FocusedView.EndUpdate;
end;

function TfrViewDetallesBase.EsTipoEditable(AItem: TcxCustomGridTableItem): Boolean;
var
  IndiceCol : Integer;
begin
  Result := True;

  IndiceCol := cxGridView.GetColumnByFieldName(CAMPO_TIPO).Index;
  if (AItem.GridView.Items[IndiceCol].EditValue = TIPO_DETALLE_SALTO) then
  begin
    IndiceCol := cxGridView.GetColumnByFieldName(CAMPO_CONCEPTO).Index;
    if AItem.Index >= IndiceCol then
       Result := False
  end
  else
  begin
    if (AItem.GridView.Items[IndiceCol].EditValue = TIPO_DETALLE_SUBTOTAL)
    or (AItem.GridView.Items[IndiceCol].EditValue = TIPO_DETALLE_TITULO_OPCIONAL)    
    or (AItem.GridView.Items[IndiceCol].EditValue = TIPO_DETALLE_TITULO) then
    begin
      IndiceCol := cxGridView.GetColumnByFieldName(CAMPO_CONCEPTO).Index;
      if AItem.Index > IndiceCol then
         Result := False
    end
  end;
end;

procedure TfrViewDetallesBase.ExpandirTodo;
begin
  if Assigned(_FocusedView) then
    _FocusedView.ViewData.Expand(True);
end;

procedure TfrViewDetallesBase.FontEdit1Accept(Sender: TObject);
begin
  inherited;
  CurrText.Assign(FontEdit1.Dialog.Font);
end;

procedure TfrViewDetallesBase.FontEdit1BeforeExecute(Sender: TObject);
begin
  inherited;
  FontEdit1.Dialog.Font.Assign(CurEdit.SelAttributes);
end;

procedure TfrViewDetallesBase.FontNameChange(Sender: TObject);
begin
  if FUpdating then Exit;
  CurrText.Name := FontName.Items[FontName.ItemIndex];
end;

procedure TfrViewDetallesBase.FontSizeChange(Sender: TObject);
begin
  if FUpdating then Exit;
  CurrText.Size := StrToInt(FontSize.Text);
end;

function TfrViewDetallesBase.GetController: IControllerDetallesBase;
begin
  Result := FController;
end;

function TfrViewDetallesBase.GetDetalles: IDAStronglyTypedDataTable;
begin
  Result := FDetalles;
end;

function TfrViewDetallesBase.GetFocusedView: TcxGridDBTableView;
begin
  Result := cxGridView;
end;

function TfrViewDetallesBase.GetGrid: TcxGrid;
begin
  Result := cxGrid;
end;

procedure TfrViewDetallesBase.GotoFirst;
begin
  if Assigned(_FocusedView) then
    _FocusedView.DataController.GotoFirst;
end;

procedure TfrViewDetallesBase.GotoLast;
begin
  if Assigned(_FocusedView) then
    _FocusedView.DataController.GotoLast;
end;

function TfrViewDetallesBase.HayQueRecalcular(AItem: TcxCustomGridTableItem): Boolean;
begin
  Result := (AItem = cxGridViewTIPO)
         or (AItem = cxGridViewCANTIDAD)
         or (AItem = cxGridViewIMPORTEUNIDAD);
end;

function TfrViewDetallesBase.IsEmpty: Boolean;
begin
  Result := (_FocusedView.ViewData.RowCount < 1);
end;

{procedure TfrViewDetallesBase.OnSelectChange(Sender: TObject);
begin
  if (csDestroying in ComponentState) then
    Exit;

  try
    FUpdating := True;
//    FontSize.Text := IntToStr(CurEdit.SelAttributes.Size);
//    FontName.FontName := CurEdit.SelAttributes.Name;
  finally
    FUpdating := False;
  end;
end;}

procedure TfrViewDetallesBase.RestoreGridStatus;
begin
  if Assigned(FGridStatus) and (not IsEmpty) then
    FGridStatus.Restore(_FocusedView);
end;

procedure TfrViewDetallesBase.SaveGridStatus;
begin
  FreeAndNil(FGridStatus);
  if not IsEmpty then
    FGridStatus := TcxGridStatus.Create(_FocusedView);
end;

procedure TfrViewDetallesBase.SeleccionarFilaActual;
begin
  //Quitamos lo que hubiera seleccionado
  cxGrid.ActiveView.DataController.ClearSelection;
  with cxGrid.ActiveView.DataController do
       if RowCount > 0 then
          SelectRows(GetFocusedRowIndex,GetFocusedRowIndex);
end;

procedure TfrViewDetallesBase.SeleccionarTodo;
begin
  if Assigned(_FocusedView) then
  begin
    _FocusedView.Controller.SelectAll;
  end;
end;

procedure TfrViewDetallesBase.SetController(const Value: IControllerDetallesBase);
var
  AListaValores : TStringList;
  AItem : TcxImageComboBoxItem;
  i: integer;
  DC: HDC;
begin
  FController := Value;

  //Rellenamos los tipos de letra que tenemos
  FontName.Items.Clear;
  DC := GetDC(0);
  EnumFonts(DC, nil, @EnumFontsProc, Pointer(FontName.Items));
  ReleaseDC(0, DC);
  FontName.Sorted := True;

  //Rellenamos los tipos de conceptos que hay
  if Assigned(FController) then
  begin
    AListaValores := FController.DarListaTIPOSDETALLE;
    with (cxGridViewTIPO.Properties as TcxImageComboBoxProperties) do
    if Items.Count = 0 then
    begin
      Items.BeginUpdate;
      try
        Items.Clear;
        for i:=0 to AListaValores.Count-1 do
        begin
          AItem := Items.Add;
          AItem.Tag := i;
          AItem.Description := AListaValores.ValueFromIndex[i];
          AItem.Value := AListaValores.Names[i];
        end;
      finally
        DefaultDescription := AListaValores.ValueFromIndex[0];
        FreeAndNil(AListaValores);
        Items.EndUpdate;
      end;
    end;
  end;
end;

procedure TfrViewDetallesBase.SetDetalles(const Value: IDAStronglyTypedDataTable);
begin
  FDetalles := Value;
  if Assigned(FDetalles) then
    DADataSource.DataTable := FDetalles.DataTable
  else
    DADataSource.DataTable := NIL;
end;

procedure TfrViewDetallesBase.TBXItem13Click(Sender: TObject);
begin
  inherited;
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;
end;

procedure TfrViewDetallesBase.TratamientoTeclas(Key: Word; Shift: TShiftState; AItem: TcxCustomGridTableItem = nil);
begin
  cxGridView.BeginUpdate;
  try
    case Key of
    VK_DOWN  : begin
                  //En el caso de ser la última fila hacemos un append nosotros no el grid
                  //ya que se saltaria la lógica del controllerDetallesBase
                  if cxGridView.Controller.IsFinish then
                  begin
                    //Key := 0;
                    if _FocusedView.Controller.EditingController.IsEditing then
                       _FocusedView.Controller.EditingController.Edit.PostEditValue;
                    actAnadir.Execute;
                  end;

                  //Baja los conceptos seleccionados
                  if Shift = [ssAlt] then
                  begin
                     //Key := 0;
                     actBajar.Execute;
                  end;
               end;
    VK_UP    : begin
                  //Sube los conceptos seleccionados
                  if Shift = [ssAlt] then
                  begin
                     //Key := 0;
                     actSubir.Execute;
                  end;
               end;

    VK_RETURN, VK_RIGHT
             : begin
                  //En el caso de ser la última fila hacemos un append nosotros no el grid
                  //ya que se saltaria la lógica del controllerDetallesBase
                  if Assigned(AItem) and
                  cxGridView.Controller.IsFinish and AItem.IsLast then
                  begin
                    //Key := 0;
                    if  _FocusedView.Controller.EditingController.IsEditing then
                       _FocusedView.Controller.EditingController.Edit.PostEditValue;
                    actAnadir.Execute;
                  end;
              end;
    end;
  finally
    cxGridView.EndUpdate;
  end;
end;

procedure TfrViewDetallesBase.actAnadirUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := not ReadOnly;
end;

procedure TfrViewDetallesBase.actAnchoAutomaticoExecute(Sender: TObject);
begin
  inherited;
  AjustarAncho
end;

procedure TfrViewDetallesBase.actAnchoAutomaticoUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := not IsEmpty;
end;

procedure TfrViewDetallesBase.actBajarExecute(Sender: TObject);
begin
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
       Controller.move(FDetalles, darListaSeleccionados, 1);
  finally
    _FocusedView.EndUpdate;
  end;
end;

procedure TfrViewDetallesBase.actBajarUpdate(Sender: TObject);
begin
  inherited;
  if not Assigned(cxGridView.Controller.FocusedRow) then
    (Sender as TAction).Enabled := False
  else
    (Sender as TAction).Enabled := (not ReadOnly)
                                   and (not cxGridView.Controller.FocusedRow.IsLast)
end;

procedure TfrViewDetallesBase.actDetallesCopiarExecute(Sender: TObject);
begin
  ShowHourglassCursor;
  try
    CopiarSeleccionGridAlPortapapeles(_Grid);
  finally
    HideHourglassCursor;
  end;
end;

procedure TfrViewDetallesBase.actDetallesCopiarUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := Assigned(cxGridView.Controller.FocusedRow);
end;

procedure TfrViewDetallesBase.actDetallesCortarExecute(Sender: TObject);
begin
  ShowHourglassCursor;
  Application.ProcessMessages;
  try
    CortarSeleccionGridAlPortapapeles(_Grid);
  finally
    HideHourglassCursor;
  end;
end;

procedure TfrViewDetallesBase.actDetallesCortarUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := Assigned(cxGridView.Controller.FocusedRow)
    and not ReadOnly ;
end;

procedure TfrViewDetallesBase.actDetallesPegarExecute(Sender: TObject);
begin
  ShowHourglassCursor;
  try
    PegarAlGridDesdePortapapeles(_Grid);
  finally
    HideHourglassCursor;
  end;
end;

procedure TfrViewDetallesBase.actDetallesPegarUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := Assigned(cxGridView.Controller.FocusedRow) 
    and not ReadOnly and HayDatosEnPortapapeles;
end;

procedure TfrViewDetallesBase.actSeleccionarTodoExecute(Sender: TObject);
begin
  inherited;
  SeleccionarTodo;
end;

procedure TfrViewDetallesBase.actSeleccionarTodoUpdate(Sender: TObject);
begin
  inherited;
  (Sender as TAction).Enabled := Assigned(DADataSource.DataTable);
end;

procedure TfrViewDetallesBase.actSubirExecute(Sender: TObject);
begin
  _FocusedView.BeginUpdate;
  try
    if _FocusedView.Controller.EditingController.IsEditing then
       _FocusedView.Controller.EditingController.Edit.PostEditValue;

    if Assigned(Controller) and Assigned(FDetalles) then
      Controller.Move(FDetalles, DarListaSeleccionados, -1);
  finally
    _FocusedView.EndUpdate;
  end;
end;

{ TcxMyRichEdit }

destructor TcxMyRichEdit.Destroy;
begin
  inherited;
end;

procedure TcxMyRichEdit.DoPaste;
begin
  if Assigned(FOnPaste) then
    FOnPaste(Self, Self.Text);
end;

procedure TcxMyRichEdit.FInnerRichOnKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  if ((Shift = [ssShift]) and (Key = VK_INSERT)) then
  begin
    PasteFromClipboard;
    Key := 0;
  end;
end;

procedure TcxMyRichEdit.HookInnerControl;
begin
  { Desviamos los mensajes que recibe el componente RichEdit interno de la columna
    a un procedimiento nuestro para interceptar los mensajes de pegar del portapapeles
    y así en vez de ejecutar el 'paste' por defecto de Windows,
    hacemos nuestro 'paste'.}
  FOldWndProc := InnerRich.WindowProc;
  InnerRich.WindowProc := InnerControlWindowProc;
  InnerRich.OnKeyDown := FInnerRichOnKeyDown;
//  InnerRich.PopupMenu := ;
end;

procedure TcxMyRichEdit.Initialize;
begin
  inherited;
  if Assigned(InnerRich) then
    HookInnerControl;
end;

procedure TcxMyRichEdit.InnerControlWindowProc(var Message: TMessage);
begin
  case Message.msg of
    WM_PASTE: if not _FPegando then
                PasteFromClipboard;
  else
    FOldWndProc(Message);
  end;
end;

procedure TcxMyRichEdit.PasteFromClipboard;
begin
  _FPegando := True;
  try
    //if HayDatosEnPortapapeles(CF_FACTUGES) then
      PegarAlGridDesdePortapapeles(FGrid);
    //else
      //inherited;
    DoPaste;
  finally
    _FPegando := False;
  end;
end;

{ TcxMyRichEditProperties }

class function TcxMyRichEditProperties.GetContainerClass: TcxContainerClass;
begin
  Result := TcxMyRichEdit;
end;

initialization
  _FRecalculando := False;
  _FRecalcular := False;
  _FPegando := False;

end.
