Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2003-09

Questions about DB status

15 messages · 6 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Micha Nelissen
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …
Andrew Johnson wrote
I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found a dsnavigator I wrote for some reason based on some freeware one out there. it looks a bit.... wierd and not DBNavigator compatible but it seems to work in delphi, and it can be compiled with pretty much no change in lazarus, so I figured I could start there and work forward by getting a base FieldDataLink and Edit based on MaskEdit and make a basic TextTable based on mylittlebase(which can compile with no changes whatsoever with fpc1.0.10 in delphi mode) however I discovered to my dismay that a good half of the functionality needed to implement a proper TDataset descendant has yet to be implemented. So my question would be this.. should I consider trying to get basic db aware controls "working" even though the backend dataset stuff is sorely lacking in some areas or should I try and get fcl dataset more complete(assuming I am capable). I have a texttable that sort of works in delphi

b!

ut untill the missing functionality is taken care of in fcl it will be rather useless. comments ideas? people want to help?

I am the author of TDbf: tdbf.sf.net which can read/write dbase files and read/write/use/search indexes (ndx,mdx). It now runs 'on' the VCL, it's a TDataSet descendant. Maybe that can be used as a backend. I never tried compiling it because the db-aware controls weren't present anyway...(sigh) and because there is much work to do on the win32 interface too. But I am willing to help where possible.

Regards,

Micha.

Reply by email

Andrew Johnson

I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found a dsnavigator I wrote for some reason based on some freeware one out there. it looks a bit.... wierd and not DBNavigator compatible but it seems to work in delphi, and it can be compiled with pretty much no change in lazarus, so I figured I could start there and work forward by getting a base FieldDataLink and Edit based on MaskEdit and make a basic TextTable based on mylittlebase(which can compile with no changes whatsoever with fpc1.0.10 in delphi mode) however I discovered to my dismay that a good half of the functionality needed to implement a proper TDataset descendant has yet to be implemented. So my question would be this.. should I consider trying to get basic db aware controls "working" even though the backend dataset stuff is sorely lacking in some areas or should I try and get fcl dataset more complete(assuming I am capable). I have a texttable that sort of works in delphi but untill the missing functionality is taken care of in fcl it will be rather useless.

comments ideas? people want to help?

Andrew

Reply by email

Marc Weustink
to Unknown author: Good idea. Please keep in mind 2 things: 1. Non-visual stuff can go in the FCL.…
Andrew Johnson wrote 7 lines
On Sat, 2003-09-13 at 11:19, Andrew Johnson wrote: > Actually I am not so sure on this for one reason. I am still not sure > _why_ it needs this, but I just found and added the following routine to > it(I find its existence atm utterly pointless and silly but .. > compatibility?) The more I think the more I think we should scratch it.

I don't.

I see no use whatsoever even for backward compatibility.

It isn't a matter of backward compatebility, I think it is needed.

When would anyone want to focus a FielddataLink 's control and not know the control already?

IF I look at the implementation of it in D7 the FocusControl makes sense. It is defined in DB and is part of TField and it is used when checking required fields. The DB unit has no knowledge about controls itself, it only signals though the DataLink that an attached control should get focus. For the controls there is the derived TFieldDataLink.

How would you suggest would the TFieldDataLink know which control is attached ?

in which case they could call it before hand.

It is not for ppl. to call the setfocus, it is the underlying field it self.

Marc

Reply by email

Andrew Johnson
to Unknown author: Good idea. Please keep in mind 2 things: 1. Non-visual stuff can go in the FCL.…
Andrew Johnson wrote
Actually I am not so sure on this for one reason. I am still not sure _why_ it needs this, but I just found and added the following routine to it(I find its existence atm utterly pointless and silly but .. compatibility?)

The more I think the more I think we should scratch it. I see no use whatsoever even for backward compatibility. When would anyone want to focus a FielddataLink 's control and not know the control already? in which case they could call it before hand. I am going to not even bother with this routine for now unless someone knows a convincing reason for this kinda thing..

But anyway. I think I have it done. I made an initial TDBText control to test because it only has read and was easier and quicker to implement it. So read apears to work, but I am assuming it works everywhere else( and if not I put a comment including the delphi help above each routine as relevent so people at least can understand why I did what and can fix if they know better). So attached is da beast with a small example, including a basic texttable and the mlb units it uses for those who wish to test it. Actually its quite small(not counting mlb stuff), I expected to have to do a lot more. which is maybe why I am wary about how well it really works inspie of my initial tests :D

Please test!

Andrew

-------------- next part --------------
A non-text attachment was scrubbed...
Name: bin00068.bin
Type: application/octet-stream
Size: 25232 bytes
Desc: "LCLDBTest.tar.gz"
Url : http://localhost/pipermail/lazarus/attachments/20030913/c64a3420/bin00068.bin

Reply by email

Andrew Johnson
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …
Mattias Gaertner wrote 7 lines
On Sat, 13 Sep 2003 09:00:51 -0400 Andrew Johnson wrote: Can you give some advice for distributing the work. Then I will help. Mattias

sure. I would guess if we can get more people working there is no reason to limit the number of people working on it right? so why not create dbctrls (and maybe dbgrids) and add the initial controls which we can finish the implementations of later. Like for now we can do general declarations like

  TFieldDataLink = class(TDataLink)
   {temporarily empty till I commit}
  end;
  TDBEdit = class(TMaskEdit)
  private
    { out link }
    FDataLink: TFieldDataLink;
    { dummy functions for now}
    function GetDataField: string;
    function GetDataSource: TDataSource;
    function GetField: TField;
    Procedure SetDataField(value : string);
    function SetDataSource(value : TDataSource);
  protected
  public
    { will create/destroy datalink later }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    { our generic db published stuff }
    property Field: TField read GetField;
  published
    { our generic db published stuff }
    property DataField: string read GetDataField write SetDataField;
    property DataSource: TDataSource read GetDataSource write
SetDataSource;
    { all the parent stuff we want published.. probably more then just
this }
    property Anchors;
    property Enabled;
    property Font;
    property PopupMenu;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property OnChange;
  end;

we need -

TDBEdit,
TDBText,
TDBCheckbox,
TDBComboBox,
TDBListBox,
TDBRadioGroup,
TDBMemo,
TDBImage,
TDBNavigator,
and maybe a TDBCalender.. thats a good start.

Once I get this datalink semi-done in a little bit here I can add it and we can have different people try and implement each control more fully. They still won't be fully testable in lcl, but I assume we might be able to test to some extent like Jesus does with his grid..

Andrew

Reply by email

Mattias Gaertner
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …
Show quoted text 14 lines
On Sat, 2003-09-13 at 14:12, Mattias Gaertner wrote: > > Once I get this datalink semi-done in a little bit here I can add it > > and we can have different people try and implement each control more > > fully. They still won't be fully testable in lcl, but I assume we > > might be able to test to some extent like Jesus does with his grid.. > > I think, we should add/implement/test it first in the LCL and then move > parts to the FCL. Makes debugging and compiling easier. Please tell, > when the base works basically and how and when others can help. > agreed. so now that I have the basics I am testing writing/updating by implementing in its basics TDBEdit. once I have it done is it ok if I add lcl/dbctrls and a lcl/include/dbtext.inc and lcl/include/dbedit.inc?

Of course.
cvs is our friend.

I won't build it or add it too the component pallette at least till we get a DBNavigator working. But I would just make like to make sure any further changes and readily components are available to those who want to test/debug/patch etc.

Mattias

Reply by email

Mattias Gaertner
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …
Show quoted text 77 lines
On Sat, 2003-09-13 at 09:13, Mattias Gaertner wrote: > On Sat, 13 Sep 2003 09:00:51 -0400 > Andrew Johnson wrote: > > Can you give some advice for distributing the work. Then I will help. > > > Mattias sure. I would guess if we can get more people working there is no reason to limit the number of people working on it right? so why not create dbctrls (and maybe dbgrids) and add the initial controls which we can finish the implementations of later. Like for now we can do general declarations like TFieldDataLink = class(TDataLink) {temporarily empty till I commit} end; TDBEdit = class(TMaskEdit) private { out link } FDataLink: TFieldDataLink; { dummy functions for now} function GetDataField: string; function GetDataSource: TDataSource; function GetField: TField; Procedure SetDataField(value : string); function SetDataSource(value : TDataSource); protected public { will create/destroy datalink later } constructor Create(AOwner: TComponent); override; destructor Destroy; override; { our generic db published stuff } property Field: TField read GetField; published { our generic db published stuff } property DataField: string read GetDataField write SetDataField; property DataSource: TDataSource read GetDataSource write SetDataSource; { all the parent stuff we want published.. probably more then just this } property Anchors; property Enabled; property Font; property PopupMenu; property ShowHint; property TabOrder; property TabStop; property Visible; property OnChange; end; we need - TDBEdit, TDBText, TDBCheckbox, TDBComboBox, TDBListBox, TDBRadioGroup, TDBMemo, TDBImage, TDBNavigator, and maybe a TDBCalender.. thats a good start. Once I get this datalink semi-done in a little bit here I can add it and we can have different people try and implement each control more fully. They still won't be fully testable in lcl, but I assume we might be able to test to some extent like Jesus does with his grid..

I think, we should add/implement/test it first in the LCL and then move parts to the FCL. Makes debugging and compiling easier. Please tell, when the base works basically and how and when others can help.

Mattias

Reply by email

Mattias Gaertner
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …

On Sat, 13 Sep 2003 09:00:51 -0400
Andrew Johnson wrote:

Can you give some advice for distributing the work. Then I will help.

Mattias

Show quoted text 74 lines
ah ok cool. I will look more shortly, but I have started a DataLink for data-aware controls, I am looking at Jesus' dbgrid for examples on use and using my old Delphi4 help files to try and make it as compatible as possible. Once I get that "working" I will try and make a DBEdit based around MaskEdit so we have something to use as a db navigator and an edit. thats good enough for testing and we can start making sure the fcl db backend is good enough fo all these backends like yours. Since this is so low level we should make actual changes needed from delphi dataset controls as minimal as possible, no more then from one version of delphi to the next, so we will need to fill in the appendrecord/insertrecord/insert, findnext/find etc routines, as well as quite a few others( I think we may be missng a few as well as the unimplimented ones). Once enough are working my dummy unfinished texttable or your dxf table(if it can be compilied) can be used to refine it further. would you like to take my attatched dsnavigator and try and make it more compatible with delphi's so it can be the lcl dbnavigator? it can compile under both delphi and lazarus so you can test it and such in delphi, with your existing stuff to compare and change. I removed the resources so we will need images once it works from the glyphs, but I hacked support for text in so it can have text and/or glyphs, and has all text on my default(yes we will want localized resource string later if we keep this as a feature). Andrew On Sat, 2003-09-13 at 08:27, Micha Nelissen wrote: > Andrew Johnson wrote: > > > I want to get DB working in Lazarus, so I looked around in my old > > Delphi cruft and found a dsnavigator I wrote for some reason based on > > some freeware one out there. it looks a bit.... wierd and not > > DBNavigator compatible but it seems to work in delphi, and it can be > > compiled with pretty much no change in lazarus, so I figured I could > > start there and work forward by getting a base FieldDataLink and Edit > > based on MaskEdit and make a basic TextTable based on > > mylittlebase(which can compile with no changes whatsoever with > > fpc1.0.10 in delphi mode) however I discovered to my dismay that a > > good half of the functionality needed to implement a proper TDataset > > descendant has yet to be implemented. So my question would be this.. > > should I consider trying to get basic db aware controls "working" even > > though the backend dataset stuff is sorely lacking in some areas or > > should I try and get fcl dataset more complete(assuming I am capable). > > I have a texttable that sort of works in delp! hi > > b! > > ut untill the missing functionality is taken care of in fcl it will be > > rather useless. > > > > comments ideas? people want to help? > > I am the author of TDbf: tdbf.sf.net which can read/write dbase files > and read/write/use/search indexes (ndx,mdx). It now runs 'on' the VCL, > it's a TDataSet descendant. Maybe that can be used as a backend. I never > tried compiling it because the db-aware controls weren't present > anyway...(sigh) and because there is much work to do on the win32 > interface too. But I am willing to help where possible. > > Regards, > > Micha. > > > > > > > _________________________________________________________________ > To unsubscribe: mail with > "unsubscribe" as the Subject > archives at http://www.lazarus.freepascal.org/mailarchives

Reply by email

Unknown author
to Andrew Johnson: sure. I would guess if we can get more people working there is no reason to limit the…
Andrew Johnson wrote 77 lines
On Sat, 2003-09-13 at 09:13, Mattias Gaertner wrote: > On Sat, 13 Sep 2003 09:00:51 -0400 > Andrew Johnson wrote: > > Can you give some advice for distributing the work. Then I will help. > > > Mattias sure. I would guess if we can get more people working there is no reason to limit the number of people working on it right? so why not create dbctrls (and maybe dbgrids) and add the initial controls which we can finish the implementations of later. Like for now we can do general declarations like TFieldDataLink = class(TDataLink) {temporarily empty till I commit} end; TDBEdit = class(TMaskEdit) private { out link } FDataLink: TFieldDataLink; { dummy functions for now} function GetDataField: string; function GetDataSource: TDataSource; function GetField: TField; Procedure SetDataField(value : string); function SetDataSource(value : TDataSource); protected public { will create/destroy datalink later } constructor Create(AOwner: TComponent); override; destructor Destroy; override; { our generic db published stuff } property Field: TField read GetField; published { our generic db published stuff } property DataField: string read GetDataField write SetDataField; property DataSource: TDataSource read GetDataSource write SetDataSource; { all the parent stuff we want published.. probably more then just this } property Anchors; property Enabled; property Font; property PopupMenu; property ShowHint; property TabOrder; property TabStop; property Visible; property OnChange; end; we need - TDBEdit, TDBText, TDBCheckbox, TDBComboBox, TDBListBox, TDBRadioGroup, TDBMemo, TDBImage, TDBNavigator, and maybe a TDBCalender.. thats a good start. Once I get this datalink semi-done in a little bit here I can add it and we can have different people try and implement each control more fully. They still won't be fully testable in lcl, but I assume we might be able to test to some extent like Jesus does with his grid..

Good idea.

Please keep in mind 2 things:

1. Non-visual stuff can go in the FCL.
TFieldDatalink (and some others as well maybe) belongs there, I think.

2. The FCL implementation of TDataset has no support for persistent fields yet. For this, the TField/TFields should be rewritten as a TCollectionItem/TNamedCollection pair, not TComponent/TList as it is now.

Michael.

Reply by email

Andrew Johnson
to Unknown author
Marc Weustink wrote 32 lines
At 21:21 13-9-2003, Andrew Johnson wrote: >On Sat, 2003-09-13 at 11:19, Andrew Johnson wrote: > > Actually I am not so sure on this for one reason. I am still not sure > > _why_ it needs this, but I just found and added the following routine to > > it(I find its existence atm utterly pointless and silly but .. > > compatibility?) > >The more I think the more I think we should scratch it. I don't. >I see no use whatsoever even for backward compatibility. It isn't a matter of backward compatebility, I think it is needed. >When would anyone want to >focus a FielddataLink 's control and not know the control already? IF I look at the implementation of it in D7 the FocusControl makes sense. It is defined in DB and is part of TField and it is used when checking required fields. The DB unit has no knowledge about controls itself, it only signals though the DataLink that an attached control should get focus. For the controls there is the derived TFieldDataLink. How would you suggest would the TFieldDataLink know which control is attached ? >in which case they could call it before hand. It is not for ppl. to call the setfocus, it is the underlying field it self. Marc

hrmmmmmm.

OK I think I can see when it might be used then.

But I don't understand why this needs to be a function accessing tcontrol/twincontrol in the DataLink itself, every other update function is done by attatching a callback to the datalink from the dataaware control. If a control _can_ recieve focus it should assign its own routine to handle that, or have a special DataLink like dbgrid needs(and even then I don't see what it would need that couldn't be done better with callbacks/messages).

So maybe implementing this routine is ok, but having TFieldDataLink do the setting of focus itself still seems silly. It should inform the control that focus is requested and the control can set itself. A simple callback should suffice.

A FieldDatalink is going to be attatched to a single control with a single field. Thats its purpose. It shouldn't need to know what the control is or if its even a tcontrol, thats why we have messages and callbacks. So if I add this I would far prefer an OnRequestFocus callback. It would allow this to be in db later on, without losing this functionality. It wouldn't be entirely backward compatible but I don't see it as a large problem if someone ports an existing db control to add a function to setfocus if needed, plus if implemented this way in the parent DataLink class it would allow a dbrid and its datalink to handle which control to set focus too in a row for instance in the event it is using a custom control for editing a column, but without requiring to much additional functionality in its own DataLink.

So would that suffice? or do you stil think we should do it the Delphi way? It's easy to implement either way, so long as the functionality is properly added to the pertinent portions of db. (db currently knows nothing of this or maskedit, and text vs displaytext doesn't work, so anything visual backend in db will need work/implementing anyway. so it doesn't matter which way we do it from that perspective)

Andrew

Reply by email

Andrew Johnson
to Unknown author: Good idea. Please keep in mind 2 things: 1. Non-visual stuff can go in the FCL.…
Show quoted text 7 lines
Good idea. Please keep in mind 2 things: 1. Non-visual stuff can go in the FCL. TFieldDatalink (and some others as well maybe) belongs there, I think.

Actually I am not so sure on this for one reason. I am still not sure _why_ it needs this, but I just found and added the following routine to it(I find its existence atm utterly pointless and silly but .. compatibility?)

Delphi Help ->
Call FocusControl to give the Control associated with this TFieldDataLink
object the input focus. FocusControl checks whether the Control can receive
input focus, and if so, calls its SetFocus method to move focus to the
Control.
<-- Delphi Help

procedure TFieldDataLink.FocusControl(Field: TFieldRef);
begin
  if Assigned(Field^) and (Field^ = FField) then
    If Assigned(Control) and (Control is TWinControl) then
      If TWinControl(Control).CanFocus then begin
        Field^ := nil;
        TWinControl(Control).SetFocus;
      end;
end;

so.. in this _one_ routine, we have a visual control dependency. if we don't need this and can remove it then I agree it should go in FCL db, but otherwise its stuck in LCL dbctrls. :)

2. The FCL implementation of TDataset has no support for persistent fields yet. For this, the TField/TFields should be rewritten as a TCollectionItem/TNamedCollection pair, not TComponent/TList as it is now.

ok. I will look into this later on if someone else doesn't get to it first.

Andrew

Reply by email

Unknown author
to Marc Weustink: I don't. It isn't a matter of backward compatebility, I think it is needed. IF I look at…
Marc Weustink wrote 23 lines
At 21:21 13-9-2003, Andrew Johnson wrote: >On Sat, 2003-09-13 at 11:19, Andrew Johnson wrote: > > Actually I am not so sure on this for one reason. I am still not sure > > _why_ it needs this, but I just found and added the following routine to > > it(I find its existence atm utterly pointless and silly but .. > > compatibility?) > >The more I think the more I think we should scratch it. I don't. >I see no use whatsoever even for backward compatibility. It isn't a matter of backward compatebility, I think it is needed. >When would anyone want to >focus a FielddataLink 's control and not know the control already? IF I look at the implementation of it in D7 the FocusControl makes sense. It is defined in DB and is part of TField and it is used when checking required fields. The DB unit has no knowledge about controls itself, it only signals though the DataLink that an attached control should get focus. For the controls there is the derived TFieldDataLink.

It can also be used when checking the contents of a field: e.g. for a numeric field, when the user enters a 123.34 for an integer field, then an 'Invalid integer' message should be given, and the control should get focus.

How would you suggest would the TFieldDataLink know which control is attached ?

Normally, the control creates a descendent of TFieldDataLink and passes itself as the control to the constructor.

Michael.

Reply by email

Andrew Johnson
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …
Mattias Gaertner wrote 9 lines
> Once I get this datalink semi-done in a little bit here I can add it and > we can have different people try and implement each control more fully. > They still won't be fully testable in lcl, but I assume we might be able > to test to some extent like Jesus does with his grid.. I think, we should add/implement/test it first in the LCL and then move parts to the FCL. Makes debugging and compiling easier. Please tell, when the base works basically and how and when others can help.

agreed. so now that I have the basics I am testing writing/updating by implementing in its basics TDBEdit. once I have it done is it ok if I add lcl/dbctrls and a lcl/include/dbtext.inc and lcl/include/dbedit.inc?

I won't build it or add it too the component pallette at least till we get a DBNavigator working. But I would just make like to make sure any further changes and readily components are available to those who want to test/debug/patch etc.

Andrew

Reply by email

Andrew Johnson
to Andrew Johnson: I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found …

ah ok cool. I will look more shortly, but I have started a DataLink for data-aware controls, I am looking at Jesus' dbgrid for examples on use and using my old Delphi4 help files to try and make it as compatible as possible. Once I get that "working" I will try and make a DBEdit based around MaskEdit so we have something to use as a db navigator and an edit. thats good enough for testing and we can start making sure the fcl db backend is good enough fo all these backends like yours. Since this is so low level we should make actual changes needed from delphi dataset controls as minimal as possible, no more then from one version of delphi to the next, so we will need to fill in the appendrecord/insertrecord/insert, findnext/find etc routines, as well as quite a few others( I think we may be missng a few as well as the unimplimented ones). Once enough are working my dummy unfinished texttable or your dxf table(if it can be compilied) can be used to refine it further.

would you like to take my attatched dsnavigator and try and make it more compatible with delphi's so it can be the lcl dbnavigator? it can compile under both delphi and lazarus so you can test it and such in delphi, with your existing stuff to compare and change. I removed the resources so we will need images once it works from the glyphs, but I hacked support for text in so it can have text and/or glyphs, and has all text on my default(yes we will want localized resource string later if we keep this as a feature).

Andrew

Micha Nelissen wrote 29 lines
Andrew Johnson wrote: > I want to get DB working in Lazarus, so I looked around in my old Delphi cruft and found a dsnavigator I wrote for some reason based on some freeware one out there. it looks a bit.... wierd and not DBNavigator compatible but it seems to work in delphi, and it can be compiled with pretty much no change in lazarus, so I figured I could start there and work forward by getting a base FieldDataLink and Edit based on MaskEdit and make a basic TextTable based on mylittlebase(which can compile with no changes whatsoever with fpc1.0.10 in delphi mode) however I discovered to my dismay that a good half of the functionality needed to implement a proper TDataset descendant has yet to be implemented. So my question would be this.. should I consider trying to get basic db aware controls "working" even though the backend dataset stuff is sorely lacking in some areas or should I try and get fcl dataset more complete(assuming I am capable). I have a texttable that sort of works in delphi b! > ut untill the missing functionality is taken care of in fcl it will be rather useless. > > comments ideas? people want to help? I am the author of TDbf: tdbf.sf.net which can read/write dbase files and read/write/use/search indexes (ndx,mdx). It now runs 'on' the VCL, it's a TDataSet descendant. Maybe that can be used as a backend. I never tried compiling it because the db-aware controls weren't present anyway...(sigh) and because there is much work to do on the win32 interface too. But I am willing to help where possible. Regards, Micha. _________________________________________________________________ To unsubscribe: mail with "unsubscribe" as the Subject archives at http://www.lazarus.freepascal.org/mailarchives
unit dsnavigator;
{$IfDef FPC}
{$mode Delphi}{$H+}
{$EndIf}
interface

uses
SysUtils, Classes, Graphics, GraphType, Controls,Dialogs,

  ExtCtrls, db, buttons;
Type BInt = 1..12;
const
  DSBFirst        : BInt = 1;
  DSBPrior        : BInt = 2;
  DSBNext         : BInt = 3;
  DSBLast         : BInt = 4;
  DSBInsert       : BInt = 5;
  DSBDelete       : BInt = 6;
  DSBEdit         : BInt = 7;
  DSBPost         : BInt = 8;
  DSBCancel       : BInt = 9;
  DSBRefresh      : BInt = 10;
  DSBBookmark     : BInt = 11;
  DSBGotoBookmark : BInt = 12;
type
  TDSButton = (ubFirst, ubPrior, ubNext, ubLast, ubInsert, ubDelete, ubEdit, ubPost,
                    ubCancel, ubRefresh, ubBookmark, ubGotoBookmark);
  TDSButtons = Set of TDSButton;
  TDSTypeInsert = (tiAppend, tiInsert);
  TDSRefreshAction = (raRefresh, raCloseOpen);
  TDSNavigator = class;
  TDSDataLink = Class(TDataLink)
  private
    FNavigator: TDSNavigator;
  protected
    procedure EditingChanged; override;
    procedure DataSetChanged; override;
    procedure ActiveChanged; override;
  public
    constructor Create(ANav: TDSNavigator);
    destructor Destroy; override;
  end;
  TDSNavButton = class(TSpeedButton)
  private
    FRepeatTimer: TTimer;
    procedure TimerExpired(Sender: TObject);
  protected
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
      X, Y: Integer); override;
  public
    destructor Destroy; override;
  end;
  TButtonStrings = Array[1..12] of string;
  TDSNavigator = class(TCustomPanel)
  private
    FLayout : TButtonLayout;
    BM: TBookmark;
    NavOwner: TComponent;
    FDataSource: TDataSource;
    VVisibleButtonCount: Integer;
    VButtons: Array[1..12] of TDSNavButton;
    VDataLink: TDSDataLink;
    FFlat: Boolean;
    resizing : boolean;
    FCaptions : TButtonStrings;
    FAfterClick : TNotifyEvent;
    FConfirmDeleteString: String;
    FOverrideFirstButton: TNotifyEvent;
    FOverrideInsertButton: TNotifyEvent;
    FOverrideCancelButton: TNotifyEvent;
    FOverrideGotoBookmarkButton: TNotifyEvent;
    FOverridePostButton: TNotifyEvent;
    FOverrideBookmarkButton: TNotifyEvent;
    FOverrideLastButton: TNotifyEvent;
    FOverrideNextButton: TNotifyEvent;
    FOverridePriorButton: TNotifyEvent;
    FOverrideEditButton: TNotifyEvent;
    FOverrideDeleteButton: TNotifyEvent;
    FOverrideRefreshButton: TNotifyEvent;
    FInsertType: TDSTypeInsert;
    FRefreshAction: TDSRefreshAction;
    FDeleteConfirmation: Boolean;
    FVisibleButtons,
    FButtonCaptions,
    FButtonGlyphs: TDSButtons;
    Procedure CreateButton(Num : integer);
    Function GetCaptions(index: Bint) : string;
    procedure SetDataSource(const Value: TDataSource);
    procedure SetFlat(const Value: Boolean);
    procedure SetDeleteConfirmation(const Value: Boolean);
    procedure SetVisibleButtons(const Value: TDSButtons);
    procedure SetButtonCaptions(const Value: TDSButtons);
    procedure SetButtonGlyphs(const Value: TDSButtons);
    procedure SetLayout(Value : TButtonLayout);
    procedure SetCaptions(index: Bint; Value : string);
    Procedure AftClick;
  protected
    procedure ButtonState;
  public
    procedure FirstClick(Sender: TObject);
    procedure LastClick(Sender: TObject);
    procedure PriorClick(Sender: TObject);
    procedure NextClick(Sender: TObject);
    procedure InsertClick(Sender: TObject);
    procedure EditClick(Sender: TObject);
    procedure DeleteClick(Sender: TObject);
    procedure RefreshClick(Sender: TObject);
    procedure PostClick(Sender: TObject);
    procedure CancelClick(Sender: TObject);
    procedure BookmarkClick(Sender: TObject);
    procedure GotoBookmarkClick(Sender: TObject);
    procedure Resize; override;
    procedure Paint; override;
    constructor Create(Owner: Tcomponent); Override;
    destructor destroy; override;
    property Captions[index : Bint] : string read GetCaptions write SetCaptions;
  published
    // Properties
    property DeleteConfirmation: Boolean read FDeleteConfirmation write SetDeleteConfirmation;
    property Align;
    property Anchors;
    property ConfirmDeleteString: String read FConfirmDeleteString write FConfirmDeleteString;
    property Constraints;
    property DataSource: TDataSource read FDataSource write SetDataSource;
    property Enabled;
    property Flat: Boolean read FFlat write SetFlat;
    property InsertType: TDSTypeInsert read FInsertType write FInsertType
      default tiInsert;
    property ParentShowHint;
    property PopupMenu;
    property RefreshAction: TDSRefreshAction read FRefreshAction write FRefreshAction default raRefresh;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property ButtonsLayout : TButtonLayout read FLayout write SetLayout;
    property VisibleButtons: TDSButtons read FVisibleButtons write SetVisibleButtons default
      [ubFirst, ubPrior, ubNext, ubLast, ubInsert, ubDelete,
        ubEdit, ubPost, ubCancel, ubRefresh];
    property ButtonCaptions: TDSButtons read FButtonCaptions
      write SetButtonCaptions default [ubInsert, ubDelete,
      ubEdit, ubPost, ubCancel, ubRefresh];
    property ButtonGlyphs: TDSButtons read FButtonGlyphs
      write SetButtonGlyphs default [ubFirst, ubPrior, ubNext,
      ubLast, ubInsert, ubDelete, ubEdit, ubPost, ubCancel, ubRefresh];
    // Standard Events
    property AfterClick : TNotifyEvent read FAfterClick write FAfterClick;
    property OnClick;
    property OnDblClick;
    property OnEnter;
    property OnExit;
    property OnResize;
    // Override Events
    property OverrideFirstButton: TNotifyEvent read FOverrideFirstButton write FOverrideFirstButton;
    property OverridePriorButton: TNotifyEvent read FOverridePriorButton write FOverridePriorButton;
    property OverrideNextButton: TNotifyEvent read FOverrideNextButton write FOverrideNextButton;
    property OverrideLastButton: TNotifyEvent read FOverrideLastButton write FOverrideLastButton;
    property OverrideInsertButton: TNotifyEvent read FOverrideInsertButton write FOverrideInsertButton;
    property OverrideEditButton: TNotifyEvent read FOverrideEditButton write FOverrideEditButton;
    property OverrideDeleteButton: TNotifyEvent read FOverrideDeleteButton write FOverrideDeleteButton;
    property OverridePostButton: TNotifyEvent read FOverridePostButton write FOverridePostButton;
    property OverrideCancelButton: TNotifyEvent read FOverrideCancelButton write FOverrideCancelButton;
    property OverrideBookmarkButton: TNotifyEvent read FOverrideBookmarkButton write FOverrideBookmarkButton;
    property OverrideGotoBookmarkButton: TNotifyEvent read FOverrideGotoBookmarkButton write FOverrideGotoBookmarkButton;
    property OverrideRefreshButton: TNotifyEvent read FOverrideRefreshButton write FOverrideRefreshButton;
  end;
procedure Register;
implementation

var BtnTypeName: array[1..12] of String = ('FIRST', 'PRIOR', 'NEXT',

    'LAST', 'INSERT', 'DELETE', 'EDIT', 'POST', 'CANCEL', 'REFRESH', 'BOOKMARK', 'GOTOBOOKMARK');
    BtnTypeCaption: array[1..12] of String = ('First', 'Prior', 'Next',
    'Last', 'Insert', 'Delete', 'Edit', 'Post', 'Cancel', 'Refresh',
    'Bookmark', 'Goto Bookmark');

const ButtonArray: Array[1..12] of TDSButton = (ubFirst, ubPrior, ubNext, ubLast, ubInsert, ubDelete, ubEdit, ubPost,

  ubCancel, ubRefresh, ubBookmark, ubGotoBookmark);
procedure Register;
begin
  RegisterComponents('Data Controls', [TDSNavigator]);
end;

{ TDSDataLink }

procedure TDSDataLink.ActiveChanged;
begin
  if FNavigator  nil then
    FNavigator.ButtonState;
end;
constructor TDSDataLink.Create(ANav: TDSNavigator);
begin
  inherited Create;
  FNavigator := ANav;
  VisualControl := True;
end;
procedure TDSDataLink.DataSetChanged;
begin
  if FNavigator  nil then
    FNavigator.ButtonState;
end;
destructor TDSDataLink.Destroy;
begin
  FNavigator := nil;
  inherited Destroy;
end;
procedure TDSDataLink.EditingChanged;
begin
  if FNavigator  nil then
    FNavigator.ButtonState;
end;

{TDSNavButton}

destructor TDSNavButton.Destroy;
begin
  if FRepeatTimer  nil then
    FRepeatTimer.Free;
  inherited Destroy;
end;
procedure TDSNavButton.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited MouseDown (Button, Shift, X, Y);
  if FRepeatTimer = nil then
    FRepeatTimer := TTimer.Create(nil);
  FRepeatTimer.OnTimer := TimerExpired;
  FRepeatTimer.Interval := 400;
  FRepeatTimer.Enabled  := True;
end;
procedure TDSNavButton.MouseUp(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  inherited MouseUp (Button, Shift, X, Y);
  if FRepeatTimer  nil then begin
    FRepeatTimer.Enabled  := False;
    FRepeatTimer.Free;
    FRepeatTimer := nil;
  end;
end;
procedure TDSNavButton.TimerExpired(Sender: TObject);
begin
  FRepeatTimer.Interval := 100;
  if (FState = bsDown) and MouseCapture then
  begin
    try
      Click;
    except
      if FRepeatTimer  nil then begin
        FRepeatTimer.Enabled  := False;
        FRepeatTimer.Free;
        FRepeatTimer := nil;
      end;
      raise;
    end;
  end;
end;

{ TDSNavigator }

Procedure TDSNavigator.AftClick;
begin
  if Assigned(FAfterClick) then
    FAfterClick(Self);
end;
procedure TDSNavigator.SetCaptions(index: BInt; Value : string);
begin
  FCaptions[Index] := Value;
  SetButtonCaptions(FButtonCaptions);
end;
Function TDSNavigator.GetCaptions(index: Bint) : string;
begin
  Result := FCaptions[Index];
end;
procedure TDSNavigator.SetLayout(Value : TButtonLayout);
var
  I : integer;
begin
  FLayout := Value;
  For I := 1 to 12 do
    If VButtons[i]  nil then
      VButtons[i].Layout := Value;
  repaint;
end;
procedure TDSNavigator.SetButtonCaptions(const Value: TDSButtons);
var
  I : integer;
begin
  FButtonCaptions := Value;
  For I := 1 to 12 do
    If VButtons[I]  nil then
      If (ButtonArray[I] in Value) then begin
        VButtons[I].Caption := Captions[I];
        VButtons[i].Hint := Captions[I];
      end
      else
        VButtons[I].Caption := '';
  repaint;
end;
procedure TDSNavigator.SetButtonGlyphs(const Value: TDSButtons);
var
  I : integer;
begin
{  FButtonGlyphs := Value;
  For I := 1 to 12 do
    If (ButtonArray[I] in Value) then
      If VButtons[I]  nil then
        VButtons[I].Glyph.LoadFromResourceName(HInstance,
          'unb_' + BtnTypeName[I]);
  repaint;}
end;
constructor TDSNavigator.Create(Owner: TComponent);
var
  i: Integer;
begin
  inherited Create(Owner);
  NavOwner := Owner;
  FConfirmDeleteString := 'Delete this record?';
  VDataLink := TDSDataLink.Create(Self);
  FDeleteConfirmation := True;
  Caption := ' ';
  FInsertType := tiInsert;
  FVisibleButtons := [ubFirst, ubPrior, ubNext, ubLast, ubInsert, ubDelete,
    ubEdit, ubPost, ubCancel, ubRefresh];
  FButtonGlyphs := [];//no lcl glyphs yet so turn this off for now
  FButtonCaptions := FVisibleButtons;// and turn this on
  for I := 1 to 12 do
    FCaptions[I] := BtnTypeCaption[I];
  for i := 1 to 12 do
    CreateButton(I);
  VVisibleButtonCount := 12;
  SetVisibleButtons(FVisibleButtons);
  ButtonState;
end;
destructor TDSNavigator.destroy;
var
  i: Integer;
begin
  VDataLink.Free;
  for i := 1 to 12 do
    if Assigned(VButtons[i]) then
      VButtons[i].Free;
  inherited;
end;
procedure TDSNavigator.DeleteClick(Sender: TObject);
var
  S: String;
begin
  Click;
  if Assigned(FOverrideDeleteButton) then
    OverrideDeleteButton(Self)
  else begin
    if FDeleteConfirmation then begin
      if FConfirmDeleteString = '' then
        S := 'Delete this record?'
      else
        S := FConfirmDeleteString;
      if MessageDlg(S, mtConfirmation, [mbYes, mbNo], 0) = mrYes
      then
        DataSource.DataSet.Delete;
    end
    else
      DataSource.DataSet.Delete;
  end;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.InsertClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideInsertButton) then
    OverrideInsertButton(Self)
  else
    if FInsertType = tiAppend then
      DataSource.DataSet.Append
    else
      DataSource.DataSet.Insert;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.BookmarkClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideBookmarkButton) then
    OverrideBookmarkButton(Self)
  else
    Bm := DataSource.DataSet.GetBookmark;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.CancelClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideCancelButton) then
    OverrideCancelButton(Self)
  else
    DataSource.DataSet.Cancel;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.EditClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideEditButton) then
    OverrideEditButton(Self)
  else
    DataSource.DataSet.Edit;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.FirstClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideFirstButton) then
    OverrideFirstButton(Self)
  else
    DataSource.DataSet.First;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.GotoBookmarkClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideGotoBookmarkButton) then
    OverrideGotoBookmarkButton(Self)
  else begin
    DataSource.DataSet.GotoBookmark(BM);
    DataSource.DataSet.FreeBookmark(BM);
    bm := nil;
  end;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.LastClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideLastButton) then
    OverrideLastButton(Self)
  else
    DataSource.DataSet.Last;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.NextClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverrideNextButton) then
    OverrideNextButton(Self)
  else
    DataSource.DataSet.Next;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.PriorClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverridePriorButton) then
    OverridePriorButton(Self)
  else
    DataSource.DataSet.Prior;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.RefreshClick(Sender: TObject);
var
  rec : integer;
begin
  Click;
  if Assigned(FOverrideRefreshButton) then
          OverrideRefreshButton(Self)
  else
    if FRefreshAction = raRefresh then
      DataSource.DataSet.Refresh
    else begin
      rec := DataSource.DataSet.RecNo;
      DataSource.DataSet.Close;
      DataSource.DataSet.Open;
      DataSource.DataSet.RecNo := rec;
    end;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.PostClick(Sender: TObject);
begin
  Click;
  if Assigned(FOverridePostButton) then
    OverridePostButton(Self)
  else
    DataSource.DataSet.Post;
  AftClick;
  ButtonState;
end;
procedure TDSNavigator.Paint;
begin
  inherited;
  if not resizing then begin
    resizing := true;
    Resize;
  end;
end;
procedure TDSNavigator.Resize;
var
  H, W, ButWidth, i, btnLeft: Integer;
begin
  inherited;
  H := Self.Height;
  W := Self.Width;
  ButWidth := 25;
  if (W <= 0) then
    w := 100;
  if VVisibleButtonCount >0 then
   ButWidth :=  (W div VVisibleButtonCount);
  btnLeft := 0;
  for i := 1 to 12 do
  begin
    If VButtons[I]  nil then begin
      if VButtons[i].Visible then begin
        VButtons[i].Parent := self;
        VButtons[i].SetBounds(btnLeft, 0, ButWidth, H);
        btnLeft := btnLeft + ButWidth;
      end
      else
        VButtons[i].Parent := nil;
      VButtons[i].Tag := i;
    end;
  end;
  for i := 12 downto 1 do
  begin
    If VButtons[I]  nil then begin
      if VButtons[i].Visible then
      begin
        VButtons[i].Width := VButtons[i].Width + (W - (ButWidth * VVisibleButtonCount));
        break;
      end;
    end;
  end;
  Repaint
end;
procedure TDSNavigator.ButtonState;
var
  I : integer;
  EOF,BOF : Boolean;
  State,
  AllInActive: Boolean;
begin
  AllInActive := False;
  if not Assigned(VDataLink.DataSource) then
  begin
     State := False;
     AllInactive := True;
  end
  else begin
    if not Assigned(VDataLink.DataSource.Dataset) then
    begin
      State := False;
      AllInactive := True;
    end
    else begin
      if not VDataLink.DataSource.Dataset.Active then
      begin
        State := False;
        AllInactive := True;
      end
      else
        State := (VDataLink.DataSource.DataSet.State = dsBrowse);
    end;
  end;
  if AllInActive then begin
    For I := 1 to 12 do
      if VButtons[I]  nil then
        VButtons[I].Enabled := False;
  end
  else begin
    EOF := (DataSource.DataSet.EOF) or
      ((DataSource.DataSet.RecNo >=
        DataSource.DataSet.RecordCount) and not
        DataSource.DataSet.Filtered);
    BOF := (((DataSource.DataSet.BOF) or{
      (DataSource.DataSet.RecNO <= 1)) or}
      (DataSource.DataSet.RecordCount <= 1)));
    For I := 1 to 2 do
      If VButtons[I]  nil then
        VButtons[I].Enabled := (not Bof) and (State or
          VDataLink.DataSource.AutoEdit);
    For I := 3 to 4 do
      If VButtons[I]  nil then
        VButtons[I].Enabled := (not EOF)  and (State or
          VDataLink.DataSource.AutoEdit);
    For I := 5 to 7 do
      If VButtons[I]  nil then
        VButtons[I].Enabled := State and (not VDataLink.DataSource.AutoEdit);
    For I := 8 to 9 do
      If VButtons[I]  nil then
        VButtons[I].Enabled := (not State) or (VDataLink.DataSource.AutoEdit);
    If VButtons[10]  nil then
      VButtons[10].Enabled := State and (not VDataLink.DataSource.AutoEdit);
    If VButtons[11]  nil then
      VButtons[11].Enabled := State;
    If VButtons[12]  nil then
      VButtons[12].Enabled := State and Assigned(BM);
  end;
  SetButtonCaptions(FButtonCaptions);
end;
procedure TDSNavigator.SetDataSource(const Value: TDataSource);
begin
  FDataSource := Value;
  if not Assigned(FDataSource) then
    Exit;
  VDataLink.DataSource := FDataSource;
  ButtonState;
end;
procedure TDSNavigator.SetFlat(const Value: Boolean);
var
  i: Integer;
begin
  FFlat := Value;
  for i := 1 to 12 do
    If VButtons[i]  nil then
      VButtons[i].Flat := FFlat;
  if value then
    BevelOuter := bvNone
  else
    BevelOuter := bvRaised;
end;
procedure TDSNavigator.SetDeleteConfirmation(const Value: Boolean);
begin
  FDeleteConfirmation := Value;
end;
procedure TDSNavigator.SetVisibleButtons(const Value: TDSButtons);
var
  I: Integer;
begin
  FVisibleButtons := Value;
  VVisibleButtonCount := 0;
  for i := 1 to 12 do begin
    If VButtons[i]  nil then begin
      VButtons[i].Visible := (ButtonArray[i] in FVisibleButtons);
      if VButtons[i].Visible then
        VVisibleButtonCount := VVisibleButtonCount + 1
      else begin
        VButtons[i].Free;
        VButtons[i] := nil;
      end;
    end
    else begin
      If (ButtonArray[i] in FVisibleButtons) then
        CreateButton(I);
    end;
  end;
  Resize;
  SetButtonCaptions(FButtonCaptions);
  SetButtonGlyphs(FButtonGlyphs);
  SetLayout(FLayout);
end;
Procedure TDSNavigator.CreateButton(Num : integer);
begin
  VButtons[Num] := TDSNavButton.Create(Self);
  with VButtons[Num] do begin
    Hint := Captions[Num];
    Caption := '';
    Flat := FFlat;
    Parent := Self;
    NumGlyphs := 2;
    Transparent := True;
    layout := FLayout;
    Tag := Num;
    Case Num of
       1:onClick := FirstClick;
       2:onClick := PriorClick;
       3:onClick := NextClick;
       4:onClick := LastClick;
       5:onClick := InsertClick;
       6:onClick := DeleteClick;
       7:onClick := EditClick;
       8:onClick := PostClick;
       9:onClick := CancelClick;
      10:onClick := RefreshClick;
      11:onClick := BookmarkClick;
      12:onClick := GotoBookmarkClick;
    end;
  end;
end;
end.

Reply by email

Marco van de Voort
to Andrew Johnson
ah ok cool. I will look more shortly, but I have started a DataLink for data-aware controls, I am looking at Jesus' dbgrid for examples on use and using my old Delphi4 help files to try and make it as compatible as possible.

If you want to mess with TDatasets, also contact FPC's Michael van Canneyt, which maintains the FCL parts.

Reply by email