{***************************************************************************}
{ TMS IntraWeb Component Pack Pro                                           }
{ for Delphi & C++Builder                                                   }
{                                                                           }
{ written by TMS Software                                                   }
{            copyright © 2002 - 2009                                        }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{                                                                           }
{ The source code is given as is. The author is not responsible             }
{ for any possible damage done due to the use of this code.                 }
{ The component can be freely used in any application. The complete         }
{ source code remains property of the author and may not be distributed,    }
{ published, given or sold in any form as such. No parts of the source      }
{ code can be included in any other component or application without        }
{ written authorization of the author.                                      }
{***************************************************************************}

unit IWDBAdvWebGrid;

{$I TMSDEFS.INC}

interface

uses
  {$IFDEF LINUX}
  QControls, QGraphics, IWJPEG,
  {$ELSE}
  Controls, Windows, Graphics, JPEG, Dialogs,
  {$ENDIF}
  {$IFDEF DELPHI6_LVL}
  Types,
  {$ENDIF}
  Classes,
  IWControl, IWTypes, SysUtils, IWCompButton,
  IWCompEdit, IWCompLabel, IWCompCheckbox, IWExtCtrls, IWHTMLTag,
  IWFont, IWWebGrid, DB, IWTMSBase, OleCtnrs;

const
  MAJ_VER = 3; // Major version nr.
  MIN_VER = 6; // Minor version nr.
  REL_VER = 0; // Release nr.
  BLD_VER = 0; // Build nr.
  DATE_VER = 'May, 2009'; // Month version

type
  TTIWDBAdvWebGrid = class;

  TTIWDBWebGridColumn = class(TTIWWebGridColumn)
  private
    FDataField: string;
    FURLField: string;
    procedure SetDataField(const Value: string);
    procedure SetURLField(const Value: string);
  protected
    function GetDisplayName: string; override;  
  public
    procedure Assign(Source: TPersistent); override;  
  published
    property DataField: string read FDataField write SetDataField;
    property URLTextField: string read FURLField write SetURLField;
  end;

  TIWDBGridTemplateEvent = procedure(Sender: TObject; ColumnIndex, RowIndex: Integer; var DBField:string) of object;

  TTIWDBWebGridColumns = class(TTIWWebGridColumns)
  private
    function GetItem(Index: Integer): TTIWDBWebGridColumn;
    procedure SetItem(Index: Integer; const Value: TTIWDBWebGridColumn);
  public
    function GetItemClass: TCollectionItemClass; override;
    function Add: TTIWDBWebGridColumn;
    function Insert(index: Integer): TTIWDBWebGridColumn;
    property Items[Index: Integer]: TTIWDBWebGridColumn read GetItem write SetItem; default;
  end;

  TTIWCustomDBAdvWebGrid = class(TTIWCustomWebGrid)
  private
    FBookMark: TBookmark;
    FDetailBookMark: TBookmark;
    FDataSource: TDataSource;
    FCurrentRow: Integer;
    FDetailState: TDataSetState;
    FDetailRow: Integer;
    FOnTemplateData: TIWDBGridTemplateEvent;
    FDetailSource: TDataSource;
    FAutoAddAllFields: Boolean;
    FShowRowsAlways: boolean;
    procedure SetDataSource(const Value: TDataSource);
    function GetColumns: TTIWDBWebGridColumns;
    procedure SetColumns(const Value: TTIWDBWebGridColumns);
    function GetCellValues(ACol, ARow: Integer): string;
    //function GetRowIndex(var NumRows: Integer): Integer;
    function RecNo(ADataSource: TDataSource): Integer;
    procedure SetDetailSource(const Value: TDataSource);
  protected
    function GetCellData(ACol,ARow: Integer): string; override;
    function GetCellDetail(ACol,ARow: Integer): string; override;
    function GetCellImage(ACol,ARow: Integer): TPicture; override;
    procedure SetCellData(ACol,ARow: Integer; Value: string;postalways:boolean=false); override;
    function GetLookupValue(ACol:Integer; ACellValue:string): string; override;
    function GetLookupValues(ACol:Integer; ACellValue:string): string; override;
    function GetColumnTitle(ACol: Integer): string; override;
    function GetURLText(ACol,ARow: Integer; AValue: string): string; override;
    function GetGridState: TTIWGridState; override;
    procedure GetRowInfo(var NumRows,RowOffset,PageOffset: Integer); override;
    function GetCurrentRecord: integer; override;
    procedure SaveBookmark; override;
    procedure RestoreBookmark; override;
    procedure RenderPrepareDetail(AGrid: TTIWCustomWebGrid); override;
    function RenderPrepare: Boolean; override;
    procedure RenderDone; override;
    procedure DoEdit; override;
    procedure DoCancel; override;
    function DoPost(postalways:boolean=false): Boolean; override;
    procedure DoInsert; override;
    procedure DoNodeOpen(ARow: Integer); override;
    procedure DoNodeClose(ARow: Integer); override;
    procedure GotoRecord(RecordIndex: Integer); override;
    function NextRecord: boolean; override;
    function IsDBAware: boolean; override;
    function HTMLDBReplace(ACol,ARow: Integer;s:string; DataSet: TDataSet): string;
    function ImageHTMLEx(const ASrc: string; AWidth,AHeight,AFullWidth,AFullHeight: Integer): TIWHTMLTag;
    procedure Notification(AComponent: TComponent; AOperation: TOperation); override;
    procedure SetValue(const AValue: string); override;
    procedure AddAllFields;
    function BlobFieldToFile(DBField: TBlobField; var size:tpoint): string;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function CreateColumns: TTIWWebGridColumns; override;
    function GetVersionNr: Integer; override;
    function GetVersionString: string; override;
    property CellValues[ACol,ARow: Integer]: string read GetCellValues;
  published
    property AutoAddAllFields: Boolean read FAutoAddAllFields write FAutoAddAllFields default false;
    property DataSource: TDataSource read FDataSource write SetDataSource;
    property DetailSource: TDataSource read FDetailSource write SetDetailSource;
    property Color;
    property Columns: TTIWDBWebGridColumns read GetColumns write SetColumns;
    property ShowRowsAlways: boolean read FShowRowsAlways write FShowRowsAlways default false;    
    property OnTemplateData: TIWDBGridTemplateEvent read FOnTemplateData write FOnTemplateData;
  end;

  TTIWDBAdvWebGrid = class(TTIWCustomDBAdvWebGrid)
  end;

  TTIWDBAdvDetailWebGrid = class(TTIWCustomDBAdvWebGrid)
  public
    constructor Create(AOwner: TComponent); override;
  published
    property FullHeight;  
  end;


implementation

uses
  {$IFDEF LINUX}
  QImgList,
  {$ELSE}
  ImgList, ShellAPI, CommCtrl,
  {$ENDIF}
  IWApplication, IWServerControllerBase,
  SWStrings, SWSystem, IWAppForm
  {$IFDEF TMSIW7}
  , IWBaseHTMLControl
  {$ENDIF}
  ;


function CheckDataSource(ADataSource: TDataSource): Boolean; overload;
begin
  Result := False;
  if ADataSource <> nil then
  begin
    if ADataSource.Dataset <> nil then
    begin
      Result := ADataSource.Dataset.Active;
    end;
  end;
end;


{ TTIWCustomDBAdvWebGrid }

constructor TTIWCustomDBAdvWebGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FDetailState := dsBrowse;
  FAutoAddAllFields := false;
  DirectPost := False;
  {$IFDEF TMSIW7}
  SupportsPartial := true;
  {$ENDIF}
  FBookmark := nil;
end;

function TTIWCustomDBAdvWebGrid.CreateColumns: TTIWWebGridColumns;
begin
  Result := TTIWDBWebGridColumns.Create(Self);
end;

destructor TTIWCustomDBAdvWebGrid.Destroy;
begin
  //prevent memory leak
  if assigned(DataSource) and assigned(DataSource.DataSet) and assigned(FBookmark) then
    DataSource.DataSet.FreeBookmark(FBookMark);
  if assigned(DetailSource) and assigned(DetailSource.DataSet) and assigned(FDetailBookmark) then
    Detailsource.DataSet.FreeBookmark(FDetailBookMark);
  inherited;
end;

procedure TTIWCustomDBAdvWebGrid.DoCancel;
begin
  inherited;
  if CheckDataSource(DataSource) then
  begin
    DataSource.DataSet.Cancel;
  end;
end;

procedure TTIWCustomDBAdvWebGrid.DoEdit;
begin
  inherited;
  if CheckDataSource(DataSource) then
  begin
    {$IFDEF TMSDEBUG}
    outputdebugstring(pchar('edit record:'+inttostr(FDetailRow)));
    {$ENDIF}  
    DataSource.DataSet.Edit;
    FDetailRow := RecNo(DataSource);
  end;
end;

procedure TTIWCustomDBAdvWebGrid.DoInsert;
begin
  if CheckDataSource(DataSource) then
  begin
    DataSource.DataSet.Insert;
  end;
end;

function TTIWCustomDBAdvWebGrid.DoPost(postalways:boolean=false): Boolean;
begin
  if CheckDataSource(DataSource) then
  begin
    if not (DataSource.DataSet.State = dsEdit) then
      DataSource.DataSet.Edit;
  end;

  Result := inherited DoPost(postalways);

  if Result then
  begin
    if CheckDataSource(DataSource) then
    begin
      try
        DataSource.DataSet.Post;
      except
//        on E : Exception do
//        OutputDebugString(pchar('post exception: ' + E.Message));
      end;
    end;
  end;
end;

procedure TTIWCustomDBAdvWebGrid.AddAllFields;
var
  i,j: Integer;
  NewColumn: TTIWDBWebGridColumn;

begin
  if not CheckDataSource(DataSource) then
    Exit;

  if AutoAddAllFields then
  begin
    j := 0;
    i := 0;
    while (i < Columns.Count) do
    begin
      if Columns[i].ColumnType <> ctNormal then
        inc(j)
      else
        break;
      inc(i);
    end;

    for i := 1 to FDataSource.DataSet.FieldCount do
    begin
      if (Columns.Count > j) then
      begin
        NewColumn := TTIWDBWebGridColumn(Columns[j]);
      end
      else
        NewColumn := TTIWDBWebGridColumn(Columns.Add);

      inc(j);

      NewColumn.DataField := FDataSource.DataSet.Fields[i - 1].FieldName;
      NewColumn.ColumnType := ctNormal;
      if NewColumn.Title = '' then
        NewColumn.Title := FDataSource.DataSet.Fields[i - 1].FieldName;
    end;
  end;
end;

function TTIWCustomDBAdvWebGrid.HTMLDBReplace(ACol,ARow:Integer;s:string; DataSet: TDataSet):string;
var
  BeforeTag,AfterTag,Fld,DBFld:string;
  i,j: Integer;
  DBField: TField;
  TempFileName: string;
  iwt:TIWHTMLTag;
  size: TPoint;

  function VarPos(su,s: string; var Res: Integer): Integer;
  begin
    Res := Pos(su,s);
    Result := Res;
  end;

begin
  i := 1;
  BeforeTag := '';

  while VarPos('(#',s,i) > 0 do
  begin
    BeforeTag := BeforeTag + Copy(s,1,i - 1); //part prior to the tag
    AfterTag := Copy(s,i,Length(s)); //part after the tag

    j := Pos(')',AfterTag);
    if j = 0 then
      Break;

    Fld := Copy(AfterTag,1,j - 1);

    Delete(Fld,1,2);
    Delete(s,1,i + j - 1);

    DBFld := '';

    if Assigned(DataSet) and (DBFld = '') then
    begin

      if DataSet.Active then
      begin
        DBField := DataSet.FindField(Fld);

        if Assigned(DBField) then
        begin
          if DBField.IsBlob then
          begin
            if (TBlobField(DBField).BlobType = ftGraphic) then
            begin
              if (csDesigning in ComponentState) then
              begin
                Result := '(IMAGE)';
              end
              else
              begin
                TempFileName := BlobFieldToFile(TBlobField(DBField),size);

                iwt := ImageHTML(GetCacheDir(Self) + ExtractFilename(TempFilename),Columns[acol].ImageWidth,Columns[ACol].ImageHeight,True,'');
                DBFld := iwt.Render;
                iwt.Free;
              end;
            end
            else
              DBFld := DBField.AsString;
          end
          else
            DBFld := DBField.AsString;
        end;
      end
      else
        DBfld := '['+fld+']';
    end
    else
      if DBFld='' then
        DBfld := '['+fld+']';

    if  Assigned(FOnTemplateData) then
      FOnTemplateData(Self,ACol,ARow,DBFld);

    BeforeTag := BeforeTag + DBFld;
  end;

  Result := Beforetag + s;
end;

function TTIWCustomDBAdvWebGrid.ImageHTMLEx(const ASrc: string; AWidth,AHeight,AFullWidth,AFullHeight: Integer): TIWHTMLTag;
begin
  Result := TIWHTMLTag.CreateTag('IMG');
  Result.AddStringParam('SRC', ASrc);
  Result.AddStringParam('NAME', Name);
  Result.AddIntegerParam('BORDER', 0);
  Result.AddStringParam('ALT', '');

  if AWidth > 0 then
    Result.AddIntegerParam('WIDTH', AWidth - 8);

  if (DefaultRowHeight > 0) and (AHeight = 0) then
    Result.AddIntegerParam('HEIGHT', DefaultRowHeight)
  else
  begin
    if (AHeight > 0) then
      Result.AddIntegerParam('HEIGHT', AHeight);
  end;
  Result.AddStringParam('ALIGN','top');

  Result.AddStringParam('OnMouseOver','ImgPopup(this,'+IntToStr(AFullWidth)+','+IntToStr(AFullHeight)+',true)');

  Result.AddStringParam('OnMouseOut','ImgPopup(this,'+IntToStr(AWidth - 8)+','+IntToStr(DefaultRowHeight)+',false)');
end;

function TTIWCustomDBAdvWebGrid.IsDBAware: boolean;
begin
  Result := true;
end;

function TTIWCustomDBAdvWebGrid.BlobFieldToFile(DBField: TBlobField; var size:tpoint): string;
var
  s,ms: TMemoryStream;
  sig:word;
  b: byte;
  Ljpg: TJPEGImage;
  ABitmap: TBitmap;
  APicture: TPicture;
  oletype: integer;
  oleoffset: integer;
  i: Integer;

begin
  Result := '';
  size.X := 0;
  size.Y := 0;

  if TBlobField(DBField).BlobType = ftGraphic then
  begin
    s := TMemoryStream.Create;
    try
      DBField.SaveToStream(S);

      oletype := -1;
      oleoffset := 0;

      if s.Size > 2 then
      begin
        // find file type
        S.Position := 0;
        S.Read(sig, 2);

        case sig of
        $1C15: // OLE storage
          begin
            i := 0;
            while (i < 512) do
            begin
              S.Read(b, 1);
              inc(i);
              if (b = $FF) then
              begin
                S.Read(b,1);
                inc(i);
                if b = $D8 then
                begin
                  oletype := 1;
                  oleoffset := i;
                  break;
                end;
              end;
              if (b = $47) then
              begin
                S.Read(b,1);
                inc(i);
                if b = $49 then
                begin
                  oletype := 2;
                  oleoffset := i;
                  break;
                end;
              end;
              if (b = ord('B')) then
              begin
                S.Read(b,1);
                inc(i);
                if (b = ord('M')) then
                begin
                  oletype := 0;
                  oleoffset := i;
                  Break;
                end;
              end;
            end;

            S.Seek(oleoffset,0);

            case oletype of
            0:
              begin
                ABitmap := TBitmap.Create;
                ABitmap.LoadFromStream(S);
                if not ABitmap.Empty then
                begin
                  {$IFDEF TMSIW71}
                  Result := TIWServerControllerBase.NewCacheFile('bmp',true);
                  {$ELSE}
                  Result := TIWServerControllerBase.NewCacheFile('bmp');
                  {$ENDIF}
                  ABitmap.SaveToFile(Result);
                  size.X := ABitmap.Width;
                  size.Y := ABitmap.Height;
                end;
                ABitmap.Free;
              end;
            1:
              begin
                LJPg := TJPEGImage.Create;
                try
                  LJpg.LoadFromStream(S);
                  APicture := TPicture.Create;
                  APicture.Assign(LJpg);

                  if not APicture.Graphic.Empty then
                  begin
                    {$IFDEF TMSIW71}
                    Result := TIWServerControllerBase.NewCacheFile('jpg',true);
                    {$ELSE}
                    Result := TIWServerControllerBase.NewCacheFile('jpg');
                    {$ENDIF}
                    APicture.SaveToFile(Result);
                    size.X := APicture.Graphic.Width;
                    size.Y := APicture.Graphic.Height;
                  end;
                  APicture.Free;
                finally
                  FreeAndNil(LJpg);
                end;
              end;
            2:
              begin
                ms := TMemoryStream.Create;
                ms.CopyFrom(s,s.Size - s.Position);
                {$IFDEF TMSIW71}
                Result := TIWServerControllerBase.NewCacheFile('gif',true);
                {$ELSE}
                Result := TIWServerControllerBase.NewCacheFile('gif');
                {$ENDIF}
                ms.SaveToFile(Result);
                ms.Free;
              end;
            end;
          end;
        $4947: //gif signature
          begin
            ms := TMemoryStream.Create;
            s.Position := 0;
            ms.LoadFromStream(s);
            {$IFDEF TMSIW71}
            Result := TIWServerControllerBase.NewCacheFile('gif',true);
            {$ELSE}
            Result := TIWServerControllerBase.NewCacheFile('gif');
            {$ENDIF}
            ms.SaveToFile(Result);
            ms.Free;
          end;
        $D8FF: //jpeg signature
          begin
            S.Position := 0;
            LJPg := TJPEGImage.Create;
            try
              LJpg.LoadFromStream(S);
              APicture := TPicture.Create;
              APicture.Assign(LJpg);

              if not APicture.Graphic.Empty then
              begin
                {$IFDEF TMSIW71}
                Result := TIWServerControllerBase.NewCacheFile('jpg',true);
                {$ELSE}
                Result := TIWServerControllerBase.NewCacheFile('jpg');
                {$ENDIF}
                APicture.SaveToFile(Result);
                size.X := APicture.Graphic.Width;
                size.Y := APicture.Graphic.Height;
              end;
              APicture.Free;
            finally
              FreeAndNil(LJpg);
            end;
          end
          else
          begin
            APicture := TPicture.Create;
            APicture.Assign(DBField);
            if not APicture.Graphic.Empty then
            begin
              {$IFDEF TMSIW71}
              Result := TIWServerControllerBase.NewCacheFile('bmp',true);
              {$ELSE}
              Result := TIWServerControllerBase.NewCacheFile('bmp');
              {$ENDIF}
              APicture.SaveToFile(Result);
              size.X := APicture.Graphic.Width;
              size.Y := APicture.Graphic.Height;
              APicture.Free;
            end;
          end;
        end;
      end;
    finally
      FreeAndNil(S);
    end;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetCellData(ACol, ARow: Integer): string;
var

  DBField: TField;
  TempFilename: string;
  dt: TDateTime;
  IWHTMLTag: TIWHTMLTag;
  tmp:string;
  Size: TPoint;

  function NonZero(a,b: Integer): integer;
  begin
    if a = 0 then
      Result := b
    else
      Result := a;  
  end;

begin
  Result := '';
  if not CheckDataSource(DataSource) then
  begin
    Result := '&nbsp;';
    Exit;
  end;

  if (Columns[ACol].Template <> '') then
  begin
    if Columns.Count > ACol then
    begin
      Result := HTMLDBReplace(ACol,ARow,Columns[ACol].Template ,DataSource.DataSet)
    end
    else
      Result := '&nbsp;';
  end;

  if (TTIWDBWebGridColumn(Columns[ACol]).DataField <> '') then
  begin
    DBField := DataSource.DataSet.FindField(TTIWDBWebGridColumn(Columns[ACol]).DataField);
    if Assigned(DBField) then
    begin
      if DBField.IsBlob then
      begin
        if TBlobField(DBField).BlobType = ftGraphic then
        begin
          if (csDesigning in ComponentState) then
          begin
            Result := '(IMAGE)';
          end
          else
          begin
            TempFileName := BlobFieldToFile(TBlobField(DBField),size);
            if TempFileName <> '' then
            begin
              if Columns[ACol].ColumnType = ctPopupImage then
              begin
                IWHTMLTag := ImageHTMLEx(GetCacheDir(Self) + ExtractFilename(TempFileName),
                  NonZero(Columns[ACol].ImageWidth,Columns[ACol].Width),NonZero(Columns[ACol].ImageHeight,DefaultRowHeight),size.x,size.y);

                  Result := '&nbsp;<DIV style="position:absolute;" onmouseover="this.style.zIndex='#39'10'#39'" onmouseout="this.style.zIndex='#39'0'#39'">'+IWHTMLTag.Render+'</DIV>';

                IWHTMLTag.Free;
              end
              else
              begin
                IWHTMLTag := ImageHTML(GetCacheDir(Self) + ExtractFilename(TempFilename),
                  Columns[ACol].ImageWidth,Columns[ACol].ImageHeight,True,'');
                Result := IWHTMLTag.Render;
                IWHTMLTag.Free;
                if Assigned(OnImageClick) then
                begin
                  Result := '<a href="#" onclick="ImgClick('+HTMLName+'Obj,'+IntToStr(ARow)+','+IntToStr(ACol)+','#39+Columns[ACol].ClickConfirm+#39')">' + Result + '</a>'
                end;
              end;
            end;
          end;
        end
        else
        begin // textual blob field
          Result := HTMLAdapt(DBField.AsString);
        end;
      end
      else

      case DBField.DataType of
      ftDate,ftDateTime:
        begin
          if (TTIWDBWebGridColumn(Columns[ACol]).Editor = edDatePicker) then
          begin
            dt := DBField.AsDateTime;
            if dt = 0 then
              Result := ''
            else
              Result := DateToDateStr(dt);
          end
          else
            Result := HTMLAdapt(DBField.DisplayText);
        end;
       ftFloat,ftCurrency:
        begin
          // use real float value for dyn. text
          if (TTIWDBWebGridColumn(Columns[ACol]).ColumnType in [ctDynEdit,ctDynText]) then
            tmp := Format('%g',[DBField.AsFloat])
          else
            tmp := DBField.DisplayText;
          Result := StringReplace(tmp,DecimalSeparator, DecimalPoint,[rfReplaceAll]);
        end;
      ftMemo:
        begin
          Result := HTMLAdapt(DBField.AsString);
        end;
      else
        Result := HTMLAdapt(DBField.DisplayText);
      end;

      if Result = '' then
        Result := '&nbsp;';

    end
    else
      Result := '&nbsp;';
  end;

  if Assigned(OnGetCellData) then
    OnGetCellData(Self,ARow,ACol,Result);
end;

function TTIWCustomDBAdvWebGrid.GetColumnTitle(ACol: Integer): string;
var
  Fld: TField;
begin
  Result := Columns[ACol].Title;
  if (Result = '') and (TTIWDBWebGridColumn(Columns[ACol]).DataField <> '') and CheckDataSource(DataSource) then
  begin
    Fld := DataSource.DataSet.FindField(TTIWDBWebGridColumn(Columns[ACol]).DataField);
    if Assigned(Fld) then
       Result := Fld.DisplayLabel;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetGridState: TTIWGridState;
begin
  Result := gsBrowse;

  if not CheckDataSource(DataSource) then
    Exit;

  case DataSource.DataSet.State of    
  dsInsert: Result := gsInsert;
  dsEdit: Result := gsEdit;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetLookupValue(ACol: Integer;
  ACellValue: string): string;
var
  KFld,Fld : TField;

begin
  Result := ACellValue;
  Fld := nil;

  if (TTIWDBWebGridColumn(Columns[ACol]).DataField <> '') then
    Fld := DataSource.DataSet.FindField(TTIWDBWebGridColumn(Columns[ACol]).DataField);

  if Assigned(Fld) then
  begin
    if Fld.FieldKind = fkLookup then
    begin
      if Assigned(Fld.LookupDataSet) and (Fld.LookupKeyFields <>'') and (Fld.KeyFields <> '') and (Fld.LookupResultField <> '')  then
      begin
        KFld := DataSource.DataSet.FindField(Fld.KeyFields);
        if Assigned(KFld) then
          Result := KFld.AsString;
      end;
    end;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetLookupValues(ACol: Integer;
  ACellValue: string): string;
var
  Fld, LFld : TField;
  k: integer;
  cb: TBookmark;
  fldval,cval: string;
  lookupval:string;
begin
  Result := '';
  Fld := nil;

  if (TTIWDBWebGridColumn(Columns[ACol]).DataField <> '') then
    Fld := DataSource.DataSet.Fields.FindField(TTIWDBWebGridColumn(Columns[ACol]).DataField);

  if Assigned(Fld) then
  begin
    if Fld.FieldKind = fkLookup then
    begin
      if Assigned(Fld.LookupDataSet) and (Fld.LookupKeyFields <>'') and (Fld.KeyFields <> '') and (Fld.LookupResultField <> '')  then
      begin
        cb := Fld.LookupDataSet.GetBookmark;
        {$IFDEF TMSDEBUG}
        outputdebugstring('start');
        {$ENDIF}
        Fld.LookupDataSet.First;

        while not Fld.LookupDataSet.Eof do
        begin
          lookupval := '';
          fldval := '';
          
          LFld := Fld.LookupDataSet.Fields.FindField(Fld.LookupKeyFields);

          if Assigned(LFld) then
            lookupval := LFld.AsString;

          LFld := Fld.LookupDataSet.Fields.FindField(Fld.LookupResultField);
          if Assigned(LFld) then
            fldval := LFld.AsString;

          if (lookupval <> '') and (fldval <> '') then
          begin
            Result := Result + '<OPTION VALUE="'+lookupval+'"';

            if fldval = ACellValue then
              Result := Result + ' SELECTED';
            Result := Result + '>'+fldval;
          end;  

          Fld.LookupDataSet.Next;
        end;

        Fld.LookupDataSet.GotoBookmark(cb);
        Fld.LookupDataSet.FreeBookmark(cb);
      end;
      Exit;
    end;
  end;

  for k := 1 to Columns[ACol].ComboItems.Count do
  begin
    if Columns[ACol].ComboValues.Count  >= k then
      cval := Columns[ACol].ComboValues[k - 1]
    else
      cval := Columns[ACol].ComboItems[k - 1];

    Result := Result + '<OPTION VALUE="'+cval+'"';
    if cval = ACellValue then
      Result := Result + ' SELECTED';

    Result := Result + '>'+Columns[ACol].ComboItems[k - 1];
  end;
end;



function TTIWCustomDBAdvWebGrid.RecNo(ADataSource: TDataSource): Integer;
var
  d: TDataSet;
  bk: TBookmark;
  NumRows: Integer;
  OldState: TDataSetState;
begin
  if not CheckDataSource(DataSource) then
  begin
    Result := 0;
    Exit;
  end;

  d := DataSource.DataSet;

  // get database state
  OldState := d.State;

  bk := d.GetBookmark;

  // get current row index
  Result := d.MoveBy($FFFF) + 1;

  // get nr. of rows
  d.First;
  NumRows := d.MoveBy($FFFF) + 1;

  Result := NumRows - Result;

  d.GotoBookmark(bk);

  if OldState = dsInsert then
    Result := Result + 1;

  // restore edit/insert states
  case OldState of
  dsEdit: d.Edit;
  dsInsert: d.Insert;
  end;

  //prevent memory leak
  d.FreeBookmark(bk);  
end;

{
function TTIWCustomDBAdvWebGrid.GetRowIndex(var NumRows: Integer): Integer;
var
  d: TDataSet;
  bk: TBookmark;
begin

  d := DataSource.DataSet;
  bk := d.GetBookmark;

  // get current row index
  Result := d.MoveBy($FFFF) + 1;

  // get nr. of rows
  d.First;
  NumRows := d.MoveBy($FFFF) + 1;

  FCurrentRow := NumRows - FCurrentRow;

  d.GotoBookMark(bk);
  d.FreeBookmark(bk);
end;
}

function TTIWCustomDBAdvWebGrid.GetCurrentRecord: integer;
var
  d: TDataSet;
  bk: TBookmark;
  Ins: Boolean;
  NumRows: integer;
begin
  if not CheckDataSource(DataSource) then
  begin
    Result := 0;
    Exit;
  end;

  d := DataSource.DataSet;

  Ins := (d.State = dsInsert);

  bk := d.GetBookmark;

  // get current row index
  FCurrentRow := d.MoveBy($FFFF) + 1;

  // get nr. of rows
  d.First;
  if d.Eof then
  begin
    if not ShowRowsAlways then
      NumRows := 0
    else
      NumRows := RowCount;
  end
  else
    NumRows := d.MoveBy($FFFF) + 1;

  if Ins then
    Inc(NumRows);

  FCurrentRow := NumRows - FCurrentRow;

  d.GotoBookMark(bk);
  d.FreeBookmark(bk);

  Result := FCurrentRow;
end;

procedure TTIWCustomDBAdvWebGrid.GetRowInfo(var NumRows, RowOffset, PageOffset: Integer);
var
  d: TDataSet;
  bk: TBookmark;
  Ins: Boolean;
begin

  if not CheckDataSource(DataSource) then
  begin
    if not ShowRowsAlways then
      NumRows := 0  // changed from 1
    else
      NumRows := RowCount;
    PageOffset := 2;
    RowOffset := 0;
    Exit;
  end;

  d := DataSource.DataSet;

  Ins := (d.State = dsInsert);

  //creates memory leak
  bk := d.GetBookmark;

  // get current row index
  FCurrentRow := d.MoveBy($FFFF) + 1;

  // get nr. of rows
  d.First;
  if d.Eof then
  begin
    if not ShowRowsAlways then
      NumRows := 0
    else
      NumRows := RowCount;
  end
  else
    NumRows := d.MoveBy($FFFF) + 1;

  if Ins then
    Inc(NumRows);

  FCurrentRow := NumRows - FCurrentRow;

  d.GotoBookMark(bk);

  //prevent memory leak
  d.FreeBookmark(bk);
  
  bk := d.GetBookmark;

  RowOffset := NumRows - (d.MoveBy($FFFF) + 1);

  if RowCount = 0 then
    PageOffset := RowOffset
  else
    PageOffset := (RowOffset) mod RowCount;

  if FCurrentRow = NumRows - 1 then
    inc(FCurrentRow);

  {$IFDEF TMSDEBUG}
  outputdebugstring(pchar(inttostr(RowOffset)+':'+inttostr(NumRows)+':'+inttostr(PageOffset)+':'+inttostr(fcurrentrow)));
  {$ENDIF}

  d.GotoBookMark(bk);

  RowOffset := RowOffset - PageOffset;

  {$IFDEF TMSDEBUG}
  outputdebugstring(pchar('** rowoffset: ' + inttostr(RowOffset)+' numrows:'+inttostr(NumRows)+' pageoffset:'+inttostr(PageOffset)));
  {$ENDIF}

  d.MoveBy(-PageOffset);

  //prevent memory leak
  d.FreeBookmark(bk);
end;

procedure TTIWCustomDBAdvWebGrid.GotoRecord(RecordIndex: Integer);
begin
  if CheckDataSource(DataSource) then
  begin
    try
      //DataSource.DataSet.Active := false;
      //DataSource.DataSet.Active := true;
      DataSource.DataSet.First;
      DataSource.DataSet.MoveBy(RecordIndex);
    except

    end;
  end;
end;

function TTIWCustomDBAdvWebGrid.NextRecord: boolean;
begin
  Result := false;
  if CheckDataSource(DataSource) then
  begin
    DataSource.DataSet.Next;
    Result := DataSource.DataSet.EOF;
  end;
end;

procedure TTIWCustomDBAdvWebGrid.RenderPrepareDetail(AGrid: TTIWCustomWebGrid);
begin
  inherited;
  if (AGrid is TTIWCustomDBAdvWebGrid) then
  begin
    with (AGrid as TTIWCustomDBAdvWebGrid) do
    begin
      if CheckDataSource(DataSource) then
      begin
        if (DataSource = self.DetailSource) and (AGrid.ID = IDDetail) then
        begin
          {$IFDEF TMSDEBUG}
          outputdebugstring('render for matching detail');
          {$ENDIF}
          if self.FDetailState = dsEdit then
          begin
            DataSource.DataSet.First;
            DataSource.DataSet.MoveBy(FDetailRow);
            DataSource.DataSet.GotoBookmark(FDetailBookMark);
            DataSource.DataSet.Edit;
            // disable further actions from master
            self.DataSource.DataSet.DisableControls;
          end;
        end
          else
          begin
            DataSource.DataSet.First;
            DataSource.DataSet.MoveBy(RowOffset);
          end;
      end;
    end;
  end;
end;

procedure TTIWCustomDBAdvWebGrid.RestoreBookmark;
begin
  if CheckDataSource(DataSource) then
  begin
    DataSource.DataSet.GotoBookmark(FBookMark);
  end;
end;

function TTIWCustomDBAdvWebGrid.RenderPrepare: Boolean;
begin
  Result := CheckDataSource(DataSource);

  if Result then
  begin
    //prevent memory leak
    if Assigned(FBookmark) then
      DataSource.DataSet.FreeBookmark(FBookMark);
    FBookMark := DataSource.DataSet.GetBookmark;

    if CheckDataSource(DetailSource) then
    begin
      // capture current state of the detaildataset
      if assigned(FDetailBookmark) then
        DetailSource.DataSet.FreeBookmark(FDetailBookMark);
      FDetailBookMark := DetailSource.DataSet.GetBookmark;
      FDetailState := DetailSource.DataSet.State;

      {$IFDEF TMSDEBUG}
      case FDetailState of
      dsEdit: outputdebugstring('detail in edit mode');
      dsInsert:outputdebugstring('detail in insert mode');
      end;
      {$ENDIF}
    end;

    if not Assigned(DetailGrid) then
      DataSource.DataSet.DisableControls;
  end;                 

  Result := True;
end;

procedure TTIWCustomDBAdvWebGrid.RenderDone;
begin
  if CheckDataSource(DataSource) then
  begin
    DataSource.DataSet.GotoBookmark(FBookmark);

    if not Assigned(DetailGrid) then
      DataSource.DataSet.EnableControls;

    if CheckDataSource(DetailSource) then
    begin
      if not Assigned(DetailGrid) then
        DetailSource.DataSet.GotoBookmark(FDetailBookmark);

      case FDetailState of
      dsEdit:
        begin
          DataSource.DataSet.EnableControls;
          DetailSource.DataSet.Edit;
        end;
      dsInsert:
        begin
          DataSource.DataSet.EnableControls;
          DetailSource.DataSet.Insert;
        end;
      end;
    end;

  end;
end;


procedure TTIWCustomDBAdvWebGrid.SaveBookmark;
begin
  if CheckDataSource(DataSource) then
  begin
    if assigned(FBookmark) then DataSource.DataSet.FreeBookmark(FBookMark);
      FBookMark := DataSource.DataSet.GetBookmark;
  end;
end;


procedure TTIWCustomDBAdvWebGrid.SetCellData(ACol, ARow: Integer; Value: string;postalways:boolean=false);
var
  Fld: TField;
  v,err: Integer;
  f: real;
  tmp: string;
begin
  if CheckDataSource(DataSource) then
  begin
    if (DataSource.DataSet.State in [dsEdit,dsInsert]) then
    begin
      if (TTIWDBWebGridColumn(Columns[ACol]).DataField <> '') and (Columns[ACol].Editor <> edNone) then
      begin
        Fld := DataSource.DataSet.Fields.FindField(TTIWDBWebGridColumn(Columns[ACol]).DataField);

        if Assigned(Fld) then
        begin
          if Assigned(OnSetCellData) then
            OnSetCellData(Self,ARow,ACol,Value);

          if Fld.FieldKind = fkLookup then
          begin
            Fld := DataSource.DataSet.Fields.FieldByName(Fld.KeyFields);
          end;

          if  not (Fld.DataType in [ftGraphic,ftBlob,ftFmtMemo]) then
          begin
            if not (DataSource.DataSet.State in [dsInsert,dsEdit]) then
              DataSource.Edit;

            {$IFDEF TMSDEBUG}
            outputdebugstring(pchar('*'+value+'*'));
            {$ENDIF}

            if (Columns[ACol].Editor = edCheckBox) then
            begin
              if (Value = Columns[ACol].CheckTrue) or (Value = 'true') then
                Value := Columns[ACol].CheckTrue
              else
                Value := Columns[ACol].CheckFalse;
            end;

            case Fld.DataType of
            ftDate,ftDateTime:
              if (Columns[ACol].Editor = edDatePicker) then
              begin
                if Value = '' then
                  Fld.AsDateTime := 0
                else
                  Fld.AsDateTime := DateStrToDate(Value);
              end
              else
                Fld.AsString := Value;
            ftInteger,ftSmallInt,ftWord:
              begin
                val(Value,v,err);
                Fld.AsInteger := v;
              end;
            ftFloat,ftCurrency:
              begin
                if DecimalPoint <> '.' then
                  tmp := StringReplace(Value, DecimalPoint, '.',[rfReplaceAll])
                else
                  tmp := Value;  
                val(tmp,f,err);
                Fld.AsFloat := f;
              end;
            else
              Fld.AsString := Value;
            end;
          end;
        end;
      end;
    end;
  end;
end;

procedure TTIWCustomDBAdvWebGrid.SetDataSource(const Value: TDataSource);
begin
  FDataSource := Value;
  Invalidate;
  AddAllFields;
end;

procedure TTIWCustomDBAdvWebGrid.Notification(AComponent: TComponent;
  AOperation: TOperation);
begin
  inherited;
  if (AOperation = opRemove) and (AComponent = DataSource) then
  begin
    DataSource := nil;
  end;                          
  if (AOperation = opRemove) and (AComponent = DetailSource) then
  begin
    DetailSource := nil;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetColumns: TTIWDBWebGridColumns;
begin
  Result := TTIWDBWebGridColumns(inherited Columns);
end;

procedure TTIWCustomDBAdvWebGrid.SetColumns(const Value: TTIWDBWebGridColumns);
begin
  inherited Columns := Value; 
end;

function TTIWCustomDBAdvWebGrid.GetVersionNr: Integer;
begin
  Result := MakeLong(MakeWord(BLD_VER,REL_VER),MakeWord(MIN_VER,MAJ_VER));
end;

function TTIWCustomDBAdvWebGrid.GetVersionString: string;
var
  vn: Integer;
begin
  vn := GetVersionNr;
  Result := IntToStr(Hi(Hiword(vn)))+'.'+IntToStr(Lo(Hiword(vn)))+'.'+IntToStr(Hi(Loword(vn)))+'.'+IntToStr(Lo(Loword(vn)))+' '+DATE_VER;
end;

function TTIWCustomDBAdvWebGrid.GetCellValues(ACol, ARow: Integer): string;
var
  bk: TBookmark;
begin
  if CheckDataSource(DataSource) then
  begin
    bk := DataSource.DataSet.GetBookmark;

    DataSource.DataSet.DisableControls;

    DataSource.DataSet.First;
    if DataSource.DataSet.MoveBy(RowOffset + ARow) < RowOffset + ARow then
      Result := ''
    else
      Result := GetCellData(ACol,ARow);

    DataSource.DataSet.GotoBookMark(bk);
    DataSource.DataSet.FreeBookmark(bk);
    DataSource.DataSet.EnableControls;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetCellDetail(ACol, ARow: Integer): string;
var
  Condition: Boolean;
begin
  Condition := True;
  if (DetailRowShow in [dsServerOpen, dsServerOneOpen]) then
  begin
    Condition := DetailStates[ARow];
  end;

  if Condition then
  begin
    if not CheckDataSource(DataSource) then
      Exit;

    if (Columns[ACol].DetailTemplate <> '') then
    begin
      if Columns.Count > ACol then
        Result := HTMLDBReplace(ACol,ARow,Columns[ACol].DetailTemplate ,DataSource.DataSet)
    end
    else
    begin
      if Assigned(OnGetCellDetail) then
        OnGetCellDetail(Self,ARow,ACol, Result);
    end;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetCellImage(ACol, ARow: Integer): TPicture;
var
  bmp: TBitmap;
  idx,err: integer;
  pic: TPicture;
  DBFIeld: TField;

begin
  Result := nil;
  bmp := nil;

  if (Columns[ACol].ColumnType = ctImage) and Assigned(Images) and (Columns[ACol].ImageIndex >=0) then
  begin
    if (Columns[ACol].ImageIndex < Images.Count) then
    begin
      bmp := TBitmap.Create;
      Images.GetBitmap(Columns[ACol].ImageIndex,bmp);
    end;
  end;

  if (Columns[ACol].ColumnType = ctDataImage) and Assigned(Images) then
  begin
    val(GetCellData(ACol,ARow),idx,err);

    if (err=0) then
    begin
      if (idx >= 0) and (idx < Images.Count) then
      begin
        bmp := TBitmap.Create;
        Images.GetBitmap(idx,bmp);
      end;
    end;
  end;

  if (Columns[ACol].ColumnType in  [ctDataImage,ctImage]) then
    if Assigned(bmp) then
    begin
      pic := TPicture.Create;
      pic.Assign(bmp);
      bmp.Free;
      Result := pic;
    end;

  if (TTIWDBWebGridColumn(Columns[ACol]).DataField <> '') then
  begin
    DBField := DataSource.DataSet.FindField(TTIWDBWebGridColumn(Columns[ACol]).DataField);
    if Assigned(DBField) then
    begin
      if DBField.IsBlob then
      begin
        if TBlobField(DBField).BlobType = ftGraphic then
        begin
          pic := TPicture.Create;
          pic.Assign(DBField);
          Result := pic;
        end;
      end;
    end;
  end;
end;

function TTIWCustomDBAdvWebGrid.GetURLText(ACol, ARow: Integer;
  AValue: string): string;
var
  DBField: TField;
begin
  if Columns.Count > ACol then
  begin
    Result := '';

    if not CheckDataSource(DataSource) then
      Result := '&nbsp;'
    else
    begin
      if (TTIWDBWebGridColumn(Columns[ACol]).URLTextField <> '') then
      begin
        DBField := DataSource.DataSet.FindField(TTIWDBWebGridColumn(Columns[ACol]).URLTextField);
        if Assigned(DBField) then
          Result := DBField.DisplayText;

        if Result = '' then
          Result := '&nbsp;';
      end
      else
        Result := '&nbsp;';
    end;    
  end;
  Result := inherited GetURLText(ACol,ARow,Result);
end;

procedure TTIWCustomDBAdvWebGrid.SetValue(const AValue: string);
begin
  inherited;
  if CheckDataSource(DataSource) then
    FDetailState := DataSource.DataSet.State;
end;

procedure TTIWCustomDBAdvWebGrid.SetDetailSource(const Value: TDataSource);
begin
  FDetailSource := Value;
end;

procedure TTIWCustomDBAdvWebGrid.DoNodeClose(ARow: Integer);
begin
  inherited;
end;

procedure TTIWCustomDBAdvWebGrid.DoNodeOpen(ARow: Integer);
begin
  inherited;
  if (DetailRowShow in [dsServerOpen,dsServerOneOpen]) then
  begin
    if CheckDataSource(DataSource) then
    begin
      DataSource.DataSet.First;
      DataSource.DataSet.MoveBy(ARow + RowOffset);
    end;
  end;
end;

{ TTIWDBWebGridColumn }

function TTIWDBWebGridColumn.GetDisplayName: string;
begin
  Result := inherited GetDisplayName + ' ' + DataField;
end;

procedure TTIWDBWebGridColumn.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  FDataField := (Source as TTIWDBWebGridColumn).DataField;
  FURLField := (Source as TTIWDBWebGridColumn).URLTextField;
end;

procedure TTIWDBWebGridColumn.SetDataField(const Value: string);
begin
  FDataField := Value;
  if Title = '' then
    Title := Value;
  Changed;
end;

procedure TTIWDBWebGridColumn.SetURLField(const Value: string);
begin
  FURLField := Value;
  Changed;
end;

{ TTIWDBWebGridColumns }

function TTIWDBWebGridColumns.Add: TTIWDBWebGridColumn;
begin
  Result := TTIWDBWebGridColumn(inherited Add);
end;

function TTIWDBWebGridColumns.GetItem(Index: Integer): TTIWDBWebGridColumn;
begin
  Result := TTIWDBWebGridColumn(inherited Items[Index]);
end;

function TTIWDBWebGridColumns.GetItemClass: TCollectionItemClass;
begin
  Result := TTIWDBWebGridColumn;
end;

function TTIWDBWebGridColumns.Insert(index: Integer): TTIWDBWebGridColumn;
begin
  Result := TTIWDBWebGridColumn(inherited Insert(Index));
end;

procedure TTIWDBWebGridColumns.SetItem(Index: Integer;
  const Value: TTIWDBWebGridColumn);
begin
  inherited Items[Index] := Value;
end;


{ TTIWDBAdvDetailWebGrid }

constructor TTIWDBAdvDetailWebGrid.Create(AOwner: TComponent);
begin
  inherited;
  IsDetail := True;
end;

end.

