unit ULBrowu;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, Grids,
  PropUtl, {v0.22}{/v0.22 UlanGlob, UlanType,} MyType,
  ULRecTyp, ULRecUtl, ULObju, ULObjDes, ExtCtrls, Menus
  {v0.13},TVType, ShowMsg{/v0.13}
  {$IFDEF DEBUG} {v0.22}{/v0.22,DebugFrm}{$ENDIF}
  {v0.18},Clipbrd, {v0.24}
  XStringGrid, XStringGridRegister,
  XSAbout, CECheckList, CEForm, CEFormRegister, CEButton
  {/v0.24 StrGrdEx}{/v0.18}{v0.22},ActnList
  {/v0.22}{v0.24},ExeLogu{/v0.24};

type
  {v0.24}

  TCellEditorItem = class(TObject)
  private
    FCellEditor: TCellEditor; { pointer to celleditor owned by window }
    FRecID: TULRecID; { for which RecID was the cell editor created }
    FULIndex: integer; { for which field the RecID object was the celleditor created }
  public
    constructor Create(AForm: TForm; AObj: TULObj; AFieldIndex: integer);reintroduce;
    property CellEditor: TCellEditor read FCellEditor;
    property RecID: TULRecID read FRecID;
    property ULIndex: integer read FULIndex;
  end;

  TCellEditors = class(TList)
    { maintainer of CellEditorItems for the window }
  private
    FForm: TForm;
  protected
    function Find(ARecID:TULRecID; AFieldIndex: integer; var ACellEditor: TCellEditor): boolean;
    function AddCellEditor(AObj:TULObj; AFieldIndex:integer): TCellEditor;
  public
    constructor Create(AForm: TForm);
    function Get(AObj: TULObj; AFieldIndex: integer): TCellEditor;
  end;

  TBrowseChilds = class(TList)
    {holds just pointers to ULObj, does not own them, index here corresponds
     to Row index (for FixedRows nil child assigned) }
  protected
    function GetChild(Index: integer):TULObj;
  public
    property Childs[Index: integer]: TULObj read GetChild; default;
  end;
  {/v0.24}

  TBrowseForm = class(TForm)
    Grid: TXStringGrid;
    Panel: TPanel;
    NewRecBtn: TBitBtn;
    EditRecBtn: TBitBtn;
    DelRecBtn: TBitBtn;
    PgDnBtn: TBitBtn;
    DownBtn: TBitBtn;         {getclientrect screentoclient}
    UpBtn: TBitBtn;
    PgUpBtn: TBitBtn;
    FirstRecBtn: TBitBtn;
    PopupMenu: TPopupMenu;
    a1: TMenuItem;
    AllDetailsCheckBox: TCheckBox;
    InternalValuesCheckBox: TCheckBox;
    LocalMenu: TPopupMenu;
    procedure FormCreate(Sender: TObject);
    procedure GridEnter(Sender: TObject);
    procedure GridClick(Sender: TObject);
    procedure EditRecBtnClick(Sender: TObject);
    procedure NewRecBtnClick(Sender: TObject);
    procedure a1Click(Sender: TObject);
    procedure DelRecBtnClick(Sender: TObject);
    procedure GridKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormDestroy(Sender: TObject);
    procedure GridGetEditText(Sender: TObject; ACol, ARow: Integer;
      var Value: String);
    procedure GridSetEditText(Sender: TObject; ACol, ARow: Integer;
      const Value: String);
    procedure GridSelectCell(Sender: TObject; ACol, ARow: Integer;
      var CanSelect: Boolean);
    procedure GridExit(Sender: TObject);
    procedure AllDetailsCheckBoxClick(Sender: TObject);
    procedure InternalValuesCheckBoxClick(Sender: TObject);
    procedure GridMouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure GridKeyUp(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormActivate(Sender: TObject);
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
  private
    { Private declarations }
    FObj: TULObj;
    {v0.22}
    FCurChild: TULObj;
    {/v0.22 CurChild: TULObj;}
      { Currently selected child (row) }
    FModified: boolean;
    LCol, LRow: integer;
    LRecID: TULRecID;
      { what was the last active record's id (for redrawing captions) }
    IsInHandleCommand:boolean;

    FLastEditCol, FLastEditRow: integer;
    FLastEditChild: TULObj;
    FLastEditValue: string;

    FSavingText:  boolean;
    FAddingChild: boolean;
{    FDeletingChild: boolean;}
    FEditEnabled: boolean;
    FMaxColWidths: TULBrowseColWidths;
    FMaxColCaptionWidths: TULBrowseColWidths;
    FFixedRows: integer;
    FFixedCols: integer;
    FInternalValues: boolean;
    {v0.22}
    {FHeaderAction: TAction;
    FEditFocusedAction : TAction;
    FBrowseFocusedAction : TAction;
    FCopySelectedAction : TAction;
    FDeleteSelectedAction : TAction;
    FDeleteAllAction : TAction;
    FPasteAction: TAction;
    FAutoSizeCols: boolean;}
    {/v0.22}
    {v0.24}
    FBrowseChilds: TBrowseChilds;
      { list of childs of Obj selected for showing in browser }
    FBrowseMode: TULBrowseMode;
    FCellEditors: TCellEditors;
    {/v0.24}
    procedure WMCommand(var Msg:TWMCommand);message WM_COMMAND;
  protected
    procedure MenuItemClick(Sender: TObject);
    procedure WMAppMessage(var Msg: TMessage); message WM_APPMESSAGE;
    procedure SaveText(AChild: TULObj; ACol,ARow:integer; const Value:string);
    procedure CheckSaveText(ACol, ARow:integer; const Value: string);
    procedure SetBrowseMode(bm:TULBrowseMode);
    {v0.18}
    procedure GridSelToObj{UpdateSelection};{called (e.g. before copy to clipboard) to
      reflect grid's selected region to FObj.Childs }
    {/v0.18}
    {v0.22}
    function GetCurChild: TULObj;
    procedure SetCurChild(AObj:TULObj);
    procedure SetObj(AObj: TULObj);
    {/v0.22}
    {v0.23}
    procedure UpdateBrowseMode;
    {/v0.23}
    {v0.24}
    function GetBrowseChilds: TBrowseChilds;
    function GetCellEditors: TCellEditors;
    procedure OnGetDrawState(Sender: TObject; ACol, ARow: Longint;
      var AState: TGridDrawState);
    procedure OnGetColor(Sender: TObject; ACol, ARow: Longint;
      AState: TGridDrawState; var ATextColor: TColor; var ABackColor: TColor);
    procedure OnSetSelected(Sender: TObject; ACol, ARow: Longint; IsSelected: boolean);
    {/v0.24}
  public
    { Public declarations }
    procedure UpdateGrid;
    function HandleCommand(cm: integer):boolean;
    procedure AddNewRec(i:integer);
      { Create new child record of RecID that has index in allowed child
        RecIDs array = i }
    procedure NewRec;
    function EditRec: integer;
    procedure DelRec;
    property CurChild: TULObj read GetCurChild write SetCurChild;
    {v0.22}
    property Obj: TULObj read FObj write SetObj;
    {/v0.22}
    {v0.24}
    property BrowseChilds: TBrowseChilds read GetBrowseChilds;
    property CellEditors: TCellEditors read GetCellEditors;
    {/v0.24}
  end;

var
  BrowseForm: TBrowseForm;

const
  ULBrowDebLog: boolean = true;
{v0.22 moved from ulanglob}
  AutoSizeBrowseCols: boolean = true;
{/v0.22}

implementation

{$R *.DFM}

{v0.24}
{TBrowseChilds}
function TBrowseChilds.GetChild(Index: integer):TULObj;
begin
  if Index >= Count then
    Result := nil
  else
    Result := TULObj(Items[Index]);
end;
{/TBrowseChilds}


{TCellEditorItem}
constructor TCellEditorItem.Create(AForm: TForm; AObj: TULObj; AFieldIndex: integer);
begin
  inherited Create;
  if AForm = nil then
    raise Exception.Create('TCellEditorItem.Create AForm nil');
  if AObj = nil then
    raise Exception.Create('TCellEditorItem.Create AObj nil');
  FCellEditor := AObj.Fields[AFieldIndex].FldDesc.CreateCellEditor(AForm, AObj);
  FRecID := AObj.RecID;
  FULIndex := AFieldIndex;
end;
{/TCellEditorItem}

{TCellEditors}
constructor TCellEditors.Create(AForm: TForm);
begin
  inherited Create;
  if AForm = nil then
    raise Exception.Create('TCellEditors.Create AForm nil');
  FForm := AForm;
end;

function TCellEditors.Find(ARecID:TULRecID; AFieldIndex: integer; var ACellEditor: TCellEditor): boolean;
var
  ci: TCellEditorItem;
  i: integer;
begin
  Result := false;
  for i := 0 to Count - 1 do begin
    ci := TCellEditorItem(Items[i]);
    if (ci.RecID = ARecID) and (ci.ULIndex = AFieldIndex) then begin
      ACellEditor := ci.CellEditor;
      Result := true;
      exit;
    end;
  end;
end;

function TCellEditors.AddCellEditor(AObj:TULObj; AFieldIndex:integer): TCellEditor;
var ci: TCellEditorItem;
begin
  ci := TCellEditorItem.Create(FForm, AObj, AFieldIndex);
  try
    Add(ci);
  except
    ci.Free;
  end;
  Result := ci.CellEditor;
end;

function TCellEditors.Get(AObj: TULObj; AFieldIndex: integer): TCellEditor;
var ce: TCellEditor;
begin
  ce := nil;
  if not Find(AObj.RecID, AFIeldIndex, ce) then
    ce := AddCellEditor(AObj, AFieldIndex);
  Result := ce;
end;

{/TCellEditors}
{/v0.24}

procedure TBrowseForm.FormCreate(Sender: TObject);
{v0.22}
begin
  if CurULObj = nil then
    raise EEmptyCurULObj.Create('TBrowseForm.FormCreate');
  Obj := CurULObj;{ulobju uldpobju}
  {v0.24}
  Grid.OnGetDrawState := OnGetDrawState;
  Grid.OnGetCellColor := OnGetColor;
  Grid.OnSetSelected := OnSetSelected;
  {/v0.24}
end;

{v0.24}
procedure TBrowseForm.OnGetDrawState(Sender: TObject; ACol, ARow: Longint;
  var AState: TGridDrawState);
var
  o: TULObj;
begin
  o := BrowseChilds[ARow];
  if (o <> nil) and (o.IsFlagSet(rfSelected)) then
    AState := AState + [gdSelected]
  else
    AState := AState - [gdSelected];
end;

procedure TBrowseForm.OnGetColor(Sender: TObject; ACol, ARow: Longint;
  AState: TGridDrawState; var ATextColor: TColor; var ABackColor: TColor);
begin

end;

procedure TBrowseForm.OnSetSelected(Sender: TObject; ACol, ARow: Longint;
  IsSelected: boolean);
var
  o: TULObj;
begin
  o := Browsechilds[ARow];
  if o <> nil then begin
    if o.IsFlagSet(rfSelected) <> IsSelected then
      o.SetFlag(rfSelected, IsSelected);
  end;
end;

function TBrowseForm.GetCellEditors: TCellEditors;
begin
  if FCellEditors = nil then
    FCellEditors := TCellEditors.Create(Self);
  Result := FCellEditors;
end;

function TBrowseForm.GetBrowseChilds: TBrowseChilds;
begin
  if FBrowseChilds = nil then
    FBrowseChilds := TBrowseChilds.Create;
  Result := FBrowseChilds;
end;
{/v0.24}

procedure TBrowseForm.SetObj(AObj: TULObj);
{/v0.22}
var
  s: string;
  i: integer;
  o: TULObj;
begin
  {v0.22}
  if FObj = AObj then
    exit;
  if FObj <> nil then begin
    CurChild := nil;
    FObj.UserUnregister(Self);
    FObj := nil;
  end;
  if AObj = nil then
    exit;
  FObj := AObj;
  {/v0.22
  FObj := CurULObj;
  if FObj = nil then
    raise EEmptyCurULObj.Create('TBrowseForm.FormCreate');}

  FObj.UserRegister(Self);
  s := FObj.ObjDesc.Caption;
  o := TULObj(FObj.Owner);
  while (o <> nil) and (o is TULObj) do begin
    if o.RecID = ULFID then
      s := o.RelFileName + ':' + s
    else
      s := o.ObjDesc.Caption + '\' + s;
    o := TULObj(o.Owner);
  end;
  FFixedRows := Grid.FixedRows;
  FFixedCols := Grid.FixedCols;
  {v0.24}
  BrowseChilds.Clear;
  {/v0.24}
  Caption := s;
  CurULObj := nil;
  FModified := true;
  LRow := -1;
  LCol := -1;
  FLastEditCol:= -1;
  FLastEditRow:= -1;
  LRecID := 0;
  for i := low(FMaxColCaptionWidths) to high(FMaxColCaptionWidths) do
    FMaxColCaptionWidths[i] :=  Canvas.TextWidth('MMMM');
  if UserMode = umSysOp then begin
    FEditEnabled := true;
    InternalValuesCheckBox.Visible := true;
    Grid.Options := Grid.Options + [goEditing];
  end else begin
    FEditEnabled := false;
    InternalValuesCheckBox.Visible := false;
  end;
  FInternalValues := InternalValuesCheckBox.Checked;
  {v0.23}
  UpdateBrowseMode;
  {/v0.23
  FModified := true;}
  UpdateGrid;
end;

procedure TBrowseForm.SetBrowseMode(bm:TULBrowseMode);
begin
  {v0.24}
  FBrowseMode := bm;
  {/v0.24
  if FObj.ChildWithFlagFirst(rfIsBrowseChild) then
  repeat
    FObj.ActiveChild.ObjDesc.BrowseMode := bm;
  until not FObj.ChildWithFlagNext(rfIsBrowseChild);}
  FModified := true;
  LRecID := 0;
  UpdateGrid;
end;

procedure TBrowseForm.UpdateGrid;
var
  childCnt, rowCnt: integer;
  o: TULObj;
  oi: TULObjDesc;
  curind, i, j, ind: integer;
{  n: shortstring;
  v: AnsiString;}
  curChildSet:boolean;
  maxfldcnt:integer;

  curcol, currow, w: integer;
  widthChanged:boolean;
begin
  if FObj = nil then
    exit;
  maxfldcnt := 0;
  widthChanged := false;

  childCnt := FObj.ChildWithFlagCount({v0.23}rfIsBrowseChild{/v0.23 rfVisible});
  rowCnt := Grid.RowCount - FFixedRows;

  curind := Grid.Row - FFixedRows;
  curChildSet := false;

  if (rowCnt <> childCnt) or FModified then begin
    {v0.24}
    BrowseChilds.Clear;
    for i := 0 to FFixedRows - 1 do begin
      BrowseChilds.Add(nil);
    end;
    {/v0.24}
    if AutoSizeBrowseCols then
      FMaxColWidths := FMaxColCaptionWidths;
    LCol := -1;
      { .. force updating of header }
    if rowCnt <> childCnt then begin
      if childCnt = 0 then begin
        Grid.RowCount := 0;
      end else begin
        Grid.RowCount := childCnt + FFixedRows;
        Grid.FixedRows := FFixedRows;
      end;
    end;

    i := 0;

    if FObj.ChildWithFlagFirst({v0.23}rfIsBrowseChild{/v0.23 rfVisible}) then
    begin
      { scan all child records: }
      repeat
        o := FObj.ActiveChild;
        {v0.24}
        BrowseChilds.Add(o);
        {/v0.24}
        oi := o.ObjDesc;
        {v0.24}
        oi.BrowseMode := FBrowseMode;
        {/v0.24}
        if oi.BrowseFieldCount + FFixedCols > Grid.ColCount then begin
          { make sure that there is enough of columns for all childs: }
          Grid.ColCount := oi.BrowseFieldCount + FFixedCols;
          Grid.FixedCols := FFixedCols;
        end else begin
          { find out what is the maximal number of childs' fields }
          if oi.BrowseFieldCount > maxfldcnt then
            maxfldcnt := oi.BrowseFieldCount;
        end;

        currow := i + FFixedRows;
        {v0.20}
        Grid.SetSelectRow(currow, o.IsFlagSet(rfSelected));
        {/v0.20}
        for j := 0 to oi.BrowseFieldCount - 1 do begin
          { scan all fields of current row child: }
          ind := oi.BrowseFields[j].ULIndex;
          curcol := j + FFixedCols;
          if (curcol <> FLastEditCol) or (currow <> FLastEditRow) or
             (FLastEditChild <> o)
          then begin
            {.. do not disturb eventually changing call value }
            if FInternalValues then begin
              Grid.Cells[curcol, currow] := trim(o.Fields[ind].AsString);
            end else begin
              Grid.Cells[curcol, currow] := trim(o.Fields[ind].AsUsrString);
            end;
          end;

          if AutoSizeBrowseCols then begin
            if curcol < MaxULBrowseCols then begin
              w := Canvas.TextWidth(Grid.Cells[curcol, currow]+'  ');
              if w > FMaxColWidths[curcol] then begin
                FMaxColWidths[curcol] := w;
                widthChanged := true;
              end;
            end;
          end;

        end;

        if FFixedCols > 0 then begin
          {v0.09}
          if FInternalValues then begin
            Grid.Cells[0, currow] := trim(o.RecIDStr);
          end else
          {/v0.09}
          begin
            Grid.Cells[0, currow] := trim(oi.Caption);
          end;

          if AutoSizeBrowseCols then begin
            w := Canvas.TextWidth(Grid.Cells[0, currow]+'  ');
            if w > FMaxColWidths[0{curcol}] then begin
              FMaxColWidths[0{curcol}] := w;
              FMaxColCaptionWidths[0] := w;
              widthChanged := true;
            end;
          end;

        end;

        if i = curind then begin
          curChildSet := true;
          {v0.22}
          CurChild := o;
          {/v0.22 FCurChild := o;}
        end;
        inc(i);
      until not FObj.ChildWithFlagNext({v0.23}rfIsBrowseChild{/v0.23 rfVisible});
      {v0.22}
      CurChild := CurChild;
      {/v0.22}
    end else begin
      { no childs: }
      CurChild := nil;
      LRecID := 0;
      Grid.RowCount := 0;
      {if Grid.RowCount > 0 then begin
        for j := 0 to Grid.ColCount - 1 do
          Grid.Cells[j, 0] := '';
      end;}
    end;

    if AutoSizeBrowseCols then begin
      if (maxfldcnt + FFixedCols) < Grid.ColCount then begin
        if maxfldcnt = 0 then begin
          Grid.ColCount := 0;
        end else begin
          Grid.ColCount := maxfldcnt + FFixedCols;
          Grid.FixedCols := FFixedCols;
        end;
      end;
    end;

    {if i <> Grid.RowCount then
      raise EULBrowse.Create('ULBrowse row/child count mismatch ' +
        IntToStr(i) + ' x ' + IntToStr(Grid.RowCount));}

    {
    while i < rowCnt do begin
      for j := Grid.FixedCols to Grid.ColCount - 1 do begin
        Grid.Cells[j, i + Grid.FixedRows] := '';
      end;
      if Grid.FixedCols > 0 then
        Grid.Cells[0, i + Grid.FixedRows] := '';
      inc(i);
    end;
    }
  end;

  if (LCol <> Grid.Col) or (LRow <> Grid.Row) {v0.10} or FModified{/v0.10} then
  begin
    if (childCnt > 0) and (curind < childCnt) then begin

      if not curChildSet then begin
        i := 0;
        if FObj.ChildWithFlagFirst({v0.23}rfIsBrowseChild{/v0.23 rfVisible}) then
        repeat
          if i = curind then
            break;
          inc(i);
        until not FObj.ChildWithFlagNext({v0.23}rfIsBrowseChild{/v0.23 rfVisible});
        CurChild := FObj.ActiveChild;
      end;

      if (LRecID <> CurChild.RecID) {v0.10}or FModified{/v0.10} then begin
        oi := CurChild.ObjDesc;
        if oi.BrowseFieldCount + FFixedCols > Grid.ColCount then begin
          Grid.ColCount := oi.BrowseFieldCount + FFixedCols;
          Grid.FixedCols := FFixedCols;
        end;
        j := 0;
        while j < oi.BrowseFieldCount do begin
          curcol := j + FFixedCols;
          if FInternalValues then begin
            Grid.Cells[curcol, 0] := trim(oi.BrowseFields[j].Name);
          end else begin
            Grid.Cells[curcol, 0] := trim(oi.BrowseFields[j].Caption);
          end;

          if AutoSizeBrowseCols then begin
            w := Canvas.TextWidth(Grid.Cells[curcol, 0]+'  ');
            if w > FMaxColWidths[curcol] then begin
              FMaxColWidths[curcol] := w;
              FMaxColCaptionWidths[curcol] := w;
              widthChanged := true;
            end;
          end;

          inc(j);
        end;

        j := oi.BrowseFieldCount + FFixedCols;
        while j < Grid.ColCount do begin
          Grid.Cells[j, 0] := '';
          inc(j);
        end;
        LRecID := CurChild.RecID;
      end;

    end else begin
      CurChild := nil;
      LRecID := 0;
    end;
    LCol := Grid.Col;
    LRow := Grid.Row;
    {Invalidate;}
  end;

  if AutoSizeBrowseCols and widthChanged then begin
    for j := 0 to Grid.ColCount - 1 do begin
      w := FMaxColWidths[j];
      if w <> 0 then
        Grid.ColWidths[j] := w;
    end;
  end;
end;

{v0.18}
procedure TBrowseForm.GridSelToObj;{UpdateSelection;}
var
  o: TULObj;
  i: integer;
  currow: integer;
begin
  {v0.24}
  exit;
  {/v0.24}
  if FObj = nil then
    exit;
  if FObj.ChildWithFlagFirst({v0.23}rfIsBrowseChild{/v0.23 rfVisible}) then
  begin
    { scan all child records: }
    i := 0;
    FObj.DoChangeLock;
    repeat
      o := FObj.ActiveChild;
      currow := i + FFixedRows;
      {v0.20}
      o.SetFlag(rfSelected, Grid.GetSelectRow(currow));
      {/v0.20
      o.SetFlag(rfSelected, (currow >= Grid.Selection.Top) and (currow <= Grid.Selection.Bottom));}
      inc(i);
    until not FObj.ChildWithFlagNext({v0.23}rfIsBrowseChild{/v0.23 rfVisible});
    FObj.DoChangeUnlock;
  end;
end;
{/v0.18}

procedure TBrowseForm.GridEnter(Sender: TObject);
begin
  UpdateGrid;
end;

procedure TBrowseForm.GridClick(Sender: TObject);
begin
  UpdateGrid;
end;

procedure TBrowseForm.EditRecBtnClick(Sender: TObject);
begin
  EditRec;
end;

procedure TBrowseForm.NewRecBtnClick(Sender: TObject);
begin
  NewRec;
end;

procedure TBrowseForm.NewRec;
var
 i: integer;
 r: TULRecID;
 {v0.23}
 cnt:integer;
 {/v0.23}
 rd: PULRecDesc;
 mi: TMenuItem;
 cp,sp:TPoint;
begin
  if FObj = nil then
    exit;
  if FObj.ObjDesc.ChildRecIDCount = 0 then
    exit;
  if FObj.ObjDesc.ChildRecIDCount = 1 then begin
    r := FObj.ObjDesc.ChildRecIDs[0];
    {v0.23}
    if FObj.CanAddChildInBrowser(r, rd) then
      AddNewRec(0);
    {/v0.23
    rd := FObj.GetULRecDescOf(r);
    if (rd = nil) or ((rd^.Flags and rfVisible) <> 0) then
      AddNewRec(0); }
  end else begin
    while PopupMenu.Items.Count > 0 do begin
      PopupMenu.Items[PopupMenu.Items.Count - 1].Free;
    end;
    {v0.23}
    cnt := 0;
    for i := 0 to FObj.ObjDesc.ChildRecIDCount - 1 do begin
      r := FObj.ObjDesc.ChildRecIDs[i];
      if FObj.CanAddChildInBrowser(r, rd) then begin
        mi := TMenuItem.Create(Self);
        if rd = nil then
          mi.Caption := ULRecIDToStrStrip(r)
        else
          mi.Caption := rd^.Caption;
        mi.OnClick := MenuItemClick;
        PopupMenu.Items.Add(mi);
        inc(cnt);
      end;
    end;
    if cnt = 0 then
      exit;
    if cnt = 1 then begin
      AddNewRec(0);
      exit;
    end;
    {/v0.23
    for i := 0 to FObj.ObjDesc.ChildRecIDCount - 1 do begin
      r := FObj.ObjDesc.ChildRecIDs[i];

      rd := FObj.GetULRecDescOf(r);
      if (rd = nil) or ((rd^.Flags and rfVisible) <> 0) then begin
        mi := TMenuItem.Create(Self);
        if rd = nil then
          mi.Caption := ULRecIDToStr(r)
        else
          mi.Caption := rd^.Caption;
        mi.OnClick := MenuItemClick;
        PopupMenu.Items.Add(mi);
      end;
    end; }
    {v0.22}
    cp.X := 0;
    cp.Y := 0;
    sp := NewRecBtn.ClientToScreen(cp);
    PopupMenu.Popup(sp.X, sp.Y);
    {/v0.22
    PopupMenu.Popup(Left + NewRecBtn.Left, Top + NewRecBtn.Top + NewRecBtn.Height);}
  end;
end;

function TBrowseForm.HandleCommand(cm: integer):boolean;
var i:integer;
begin
  Result := false;
  if IsInHandleCommand then
    exit;
  try
    IsInHandleCommand := true;
    for i := 0 to PopupMenu.Items.Count - 1 do begin
      if cm = PopupMenu.Items[i].Command then begin
        AddNewRec(i);
        Result := true;
        break;
      end;
    end;
  finally
    IsInHandleCommand := false;
  end;
end;

procedure TBrowseForm.WMCommand(var Msg:TWMCommand);
begin
 if not HandleCommand(Msg.ItemID) then
   inherited;
end;

procedure TBrowseForm.WMAppMessage(var Msg: TMessage);
begin
  case Msg.wParam of

    cmULObjCanDestroy: begin
      if TULObj(Msg.lParam) = FObj then begin
        {v0.22 it can be destroyed, the browser will just be closed }
        {/v0.22
        Msg.Result := 1;}
      end;
    end;

    cmULObjUpdated: begin
      if (TULObj(Msg.lParam) = FObj) then
      begin
        if (not FSavingText) and (not FAddingChild){ and (not FDeletingChild)} then begin
          UpdateGrid;
        end;
      end;
    end;

    cmULObjBrowseFormBringToFront: begin
      if (TULObj(Msg.lParam) = FObj) then
      begin
        Msg.Result := 1;
        BringToFront;
      end;
    end;

    cmULObjAfterEdit: begin

    end;

    cmULObjDestroyed: begin
      if TULObj(Msg.lParam) = FObj then begin
        {v0.22}
        Obj := nil;
        {/v0.22
        FObj := nil;
        CurChild := nil;}
        Release;
      end;
      if TULObj(Msg.lParam) = CurChild then
        CurChild := nil;
    end;

    {v0.18}
    cmClipCopy: begin
      {v0.22}
      if FObj <> nil then
        FObj.Actions.CopySelected(Self);
      {/v0.22
      if FObj <> nil then begin
        GridSelToObj;
        Clipboard.Assign(FObj);
      end; }
    end;

    cmClipPaste: begin
      {v0.22}
      FObj.Actions.Paste(Self);
      {/v0.22
      if FObj <> nil then begin
        FObj.Assign(Clipboard);
        UpdateGrid;
      end;}
    end;

    cmClipCut: begin
      if FObj <> nil then begin
        GridSelToObj;
        Clipboard.Assign(FObj);
        FObj.ChildsDelete(0, rfSelected);
        UpdateGrid;
      end;
    end;
    {v0.18}
  end;
end;

procedure TBrowseForm.a1Click(Sender: TObject);
begin
  HandleCommand(TMenuItem(Sender).Command);
end;

procedure TBrowseForm.MenuItemClick(Sender: TObject);
begin
  HandleCommand(TMenuItem(Sender).Command);
end;

procedure TBrowseForm.AddNewRec(i: integer);
var
  cnt, j: integer;
  r: TULRecID;
  rd: PULRecDesc;
begin
  if FObj = nil then
    exit;
  cnt := 0;
  for j := 0 to FObj.ObjDesc.ChildRecIDCount - 1 do begin
    r := FObj.ObjDesc.ChildRecIDs[j];
    {v0.23}
    if FObj.CanAddChildInBrowser(r, rd) then
    {/v0.23
    rd := FObj.GetULRecDescOf(r);
    if (rd = nil) or ((rd^.Flags and rfVisible) <> 0) then
    }
    begin
      if cnt = i then begin
        FAddingChild:= true;
        try
          CurChild := FObj.Add(r);
          CurChild.JustCreated := true;
        finally
          FAddingChild := false;
        end;
        if EditRec <> mrOK then
          DelRec;
        break;
      end;
      inc(cnt);
    end;
  end;
end;

function TBrowseForm.EditRec: integer;
{v0.24}
var mr:TModalResult;
{/v0.24}
begin
  Result := mrCancel;
  if CurChild <> nil then begin
    if CurChild.IsFlagSet({v0.23}rfIsBrowseChild{/v0.23 rfVisible}) and CurChild.IsFlagSet(rfEnabled) then
    begin
      if CurChild.IsFlagSet(rfBrowseOnEdit) then begin
        CurChild.Browse;
        UpdateGrid;
        Result := mrOK;
      end else begin    {formstate ulstringgrid}
        {v0.24}
        if fsModal in FormState then
          mr := CurChild.EditModal
        else
          mr := CurChild.Edit;
        if mr = mrOK then
        {/v0.24
        if CurChild.Edit = mrOK then}
        begin
          FModified := true;
          UpdateGrid;
          Result := mrOK;
        end;
      end;
    end;
  end;
end;

procedure TBrowseForm.DelRecBtnClick(Sender: TObject);
begin
  DelRec;
end;

procedure TBrowseForm.DelRec;
begin
  if CurChild <> nil then begin
    {v0.13}
    if CurChild.IsFlagSet(rfCantDelete) or (not CurChild.CanDestroy) then begin
      if UserMode = umSysOp then begin
        if ShowMessage(GetTxt({#}'Really destroy object used by other objects (GPF can follow)?'), smYesNo, 0) <> cmYes then
          exit;
      end else begin
        exit;
      end;
    end else begin
      if ShowMessage(GetTxt({#}'Really delete the current row?'), smNoYes, 0) <> cmYes then
        exit;
    end;
    CurChild.Free;
    SendMessage(Application.MainForm.Handle, WM_APPMESSAGE, cmULObjAfterBrowseEdit,
      longint(FObj));
    {/v0.13
    if not CurChild.IsFlagSet(rfCantDelete) or (UserMode = umSysOp) then begin
      if (not CurChild.CanDestroy) and (UserMode <> umSysOp) then
        exit;
      CurChild.Free;
      SendMessage(Application.MainForm.Handle, WM_APPMESSAGE, cmULObjAfterBrowseEdit,
        longint(FObj));
    end;}
  end;
end;

procedure TBrowseForm.GridKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  case Key of
    vk_Insert: NewRec;
    vk_Delete: DelRec;
    {v0.24}{/v0.24
    vk_Return: EditRec;}
  else
    exit;
  end;
  Key := 0;
end;

procedure TBrowseForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

procedure TBrowseForm.FormDestroy(Sender: TObject);
{v0.24}
var i: integer;
{/v0.24}
begin
  if FObj <> nil then begin
    {v0.22}
    Obj := nil;
    {/v0.22
    FObj.UserUnregister(Self);}
  end;
  {v0.24}
  for i := 0 to Grid.ColCount - 1 do begin
    Grid.Columns[i].Editor := nil;
  end;
  FBrowseChilds.Free;
  FCellEditors.Free;
  {/v0.24}
end;

procedure TBrowseForm.GridGetEditText(Sender: TObject; ACol, ARow: Integer;
  var Value: String);
{var
  j: integer;
  n: shortstring;
  v: AnsiString;
  oi: TULObjDesc;}
begin
  {$IFDEF DEBUG}
  if ULBrowDebLog then begin
    {DebLog('OnGetText ' + IntToStr(ACol) + ',' + IntToStr(ARow) + ' :' + Value + ':');}
  end;
  {$ENDIF}
  CheckSaveText(ACol, ARow, Value);

  FLastEditCol := ACol;
  FLastEditRow := ARow;
  FLastEditChild := CurChild;
  FLastEditValue := Value;
end;

procedure TBrowseForm.GridSetEditText(Sender: TObject; ACol, ARow: Integer;
  const Value: String);
begin
  {$IFDEF DEBUG}
  if ULBrowDebLog then
    {DebLog('OnSetText ' + IntToStr(ACol) + ',' + IntToStr(ARow) + ' :' + Value + ':');}
  {$ENDIF}
  FLastEditValue := Value;
  FLastEditCol := ACol;
  FLastEditRow := ARow;
{
  if CurChild <> nil then begin
    if (FLastEditRow = ARow) and (FLastEditCol = ACol) then begin
    end;
  end;
  }
  CheckSaveText(ACol, ARow, Value);
end;

procedure TBrowseForm.CheckSaveText(ACol, ARow:integer; const Value: string);
begin
  if (ACol <> FLastEditCol) or (ARow <> FLastEditRow) then begin
    if FLastEditRow >= 0 then
      SaveText(FLastEditChild, FLastEditCol, FLastEditRow, FLastEditValue);
  end else begin
    FLastEditValue := Value;
  end;
end;

procedure TBrowseForm.SaveText(AChild: TULObj; ACol,ARow:integer; const Value:string);
var
  j: integer;
{  n: shortstring;
  v: AnsiString;}
{  oi: TULObjDesc;}
begin
  if (AChild = nil) then
    exit;
{  oi := AChild.ObjDesc;}
  j := ACol - FFixedCols;
  {if ClassGetPropStr(AChild, j + oi.FirstPropIndex, n, v) then}
  begin
    FSavingText := true;
    try
      if j < AChild.ObjDesc.BrowseFieldCount then begin
        if FInternalValues then begin
          AChild.Fields[AChild.ObjDesc.BrowseFields[j].ULIndex].AsString := Value;
        end else begin
          AChild.Fields[AChild.ObjDesc.BrowseFields[j].ULIndex].AsUsrString := Value;
        end;
        SendMessage(Application.MainForm.Handle, WM_APPMESSAGE, cmULObjAfterBrowseEdit,
          longint(AChild));
        SendMessage(Application.MainForm.Handle, WM_APPMESSAGE, cmULObjAfterBrowseEdit,
          longint(FObj));
      end;
      {ClassSetPropStr(AChild, n, Value);}
    finally
      FSavingText := false;
    end;
  end;

  FLastEditRow := -1;
  FLastEditCol := -1;
  FLastEditValue := '';
  FLastEditChild := nil;
end;

procedure TBrowseForm.GridSelectCell(Sender: TObject; ACol, ARow: Integer;
  var CanSelect: Boolean);
begin
  CheckSaveText(ACol, ARow, '');
end;

procedure TBrowseForm.GridExit(Sender: TObject);
begin
  {v0.24}
  {/v0.24
    CheckSaveText(-1, -1, '');}
end;

{v0.23}
procedure TBrowseForm.UpdateBrowseMode;
begin
  FModified := true;
  if AllDetailsCheckBox.Checked then
    SetBrowseMode(bmFull)
  else
    SetBrowseMode(bmShort);
end;
{/v0.23}

procedure TBrowseForm.AllDetailsCheckBoxClick(Sender: TObject);
begin
  {v0.23}
  UpdateBrowseMode;
  {/v0.23
  FModified := true;
  if AllDetailsCheckBox.Checked then
    SetBrowseMode(bmFull)
  else
    SetBrowseMode(bmShort)}
end;

procedure TBrowseForm.InternalValuesCheckBoxClick(Sender: TObject);
begin
  FInternalValues := InternalValuesCheckBox.Checked;
  FModified := true;
  UpdateGrid;
end;

procedure TBrowseForm.GridMouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  cp, sp: TPoint;
begin
  {v0.18}
  GridSelToObj;{UpdateSelection; ulrectyp}
  {/v0.18}
  {v0.22}
  if Button = mbRight then begin
    cp.x := X;
    cp.y := Y;
    sp := Grid.ClientToScreen(cp);
    FObj.Actions.LocalMenuBuild(Self, LocalMenu);
    LocalMenu.Popup(sp.x, sp.y);
  end;
  {/v0.22}
end;


procedure TBrowseForm.GridKeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
  {v0.22}
  GridSelToObj;
  {/v0.22}
end;

{v0.22}
function TBrowseForm.GetCurChild: TULObj;
begin
  Result := FCurChild;
  {
  Result := nil;
  if FObj <> nil then
    Result := FObj.ActiveChild;}
end;

procedure TBrowseForm.SetCurChild(AObj:TULObj);
var
  i, c: integer;
begin
{  if FObj <> nil then
    FObj.ActiveChild := AObj;}
  FCurChild := AObj;
  FObj.ActiveChild := AObj;
  { list of childs of Obj selected for showing in browser }
  {v0.24}
  if AObj <> nil then begin
    for i := 0 to AObj.ObjDesc.BrowseFieldCount - 1 do begin
      c := i + Grid.FixedCols;
      if c < Grid.ColCount then
        Grid.Columns[c].Editor := CellEditors.Get(AObj, AObj.ObjDesc.BrowseFields[i].ULIndex);
    end;
  end;
  {/v0.24}
end;
{/v0.22}


{v0.24}
procedure TBrowseForm.FormActivate(Sender: TObject);
begin
  FModified := true;
  UpdateGrid;
end;
{/v0.24}
{v0.24}
procedure TBrowseForm.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);

const cnt:longint = 0;
begin
  { $IFDEF DEBUG}
  if Key = vk_Escape then begin
    ExeLog.Log('BrowseForm.vk_Esc Down ' + IntToStr(cnt));
    inc(cnt);
    if FLastEditRow >= 0{Grid.EditorMode} then begin
      Grid.CellEditor.AbortEdit;
      ActiveControl := Grid;
      {if Grid.DefaultMoveKey <> 0 then
        PostMessage(Handle, WM_KEYDOWN, Grid.DefaultMoveKey, 0);}
      {Grid.EditorMode := false;}
    end;
  end;
  { $ENDIF}

end;
{/v0.24}

end.
