ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/IBDynamicGrid.pas
Revision: 23
Committed: Fri Mar 13 10:26:52 2015 UTC (9 years, 8 months ago) by tony
Content type: text/x-pascal
File size: 34185 byte(s)
Log Message:
Committing updates for Release R1-2-1

File Contents

# User Rev Content
1 tony 21 (*
2     * IBX For Lazarus (Firebird Express)
3     *
4     * The contents of this file are subject to the Initial Developer's
5     * Public License Version 1.0 (the "License"); you may not use this
6     * file except in compliance with the License. You may obtain a copy
7     * of the License here:
8     *
9     * http://www.firebirdsql.org/index.php?op=doc&id=idpl
10     *
11     * Software distributed under the License is distributed on an "AS
12     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13     * implied. See the License for the specific language governing rights
14     * and limitations under the License.
15     *
16     * The Initial Developer of the Original Code is Tony Whyman.
17     *
18 tony 23 * The Original Code is (C) 2015 Tony Whyman, MWA Software
19 tony 21 * (http://www.mwasoftware.co.uk).
20     *
21     * All Rights Reserved.
22     *
23     * Contributor(s): ______________________________________.
24     *
25     *)
26     unit IBDynamicGrid;
27    
28     {$mode objfpc}{$H+}
29    
30     interface
31    
32     uses
33     Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, DBGrids, DB,
34     IBSqlParser, Grids, IBLookupComboEditBox, LMessages, StdCtrls, ExtCtrls;
35    
36     type
37     {
38     TIBDynamicGrid is a TDBGrid descendent that provides for:
39     - automatic resizing of selected columns to fill the available row length
40     - automatic positioning and sizing of a "totals" control, typically at the
41     column footer, on a per column basis.
42     - DataSet resorting on header row click, sorting the dataset by the selected column.
43     A second click on the same header cell reversed the sort order.
44     - Reselection of the same row following resorting.
45     - A new cell editor that provides the same functionality as TIBLookupComboEditBox.
46     Its properties are specified on a per column basis and allows for one or more
47     columns to have their values selected from a list provided by a dataset.
48     Autocomplete and autoinsert are also available. The existing picklist editor
49     is unaffected by the extension.
50     }
51    
52     TIBDynamicGrid = class;
53    
54     TOnColumnHeaderClick = procedure(Sender: TObject; var ColIndex: integer) of object;
55     TOnUpdateSortOrder = procedure (Sender: TObject; ColIndex: integer; var OrderBy: string) of Object;
56     TKeyDownHandler = procedure (Sender: TObject; var Key: Word; Shift: TShiftState; var Done: boolean) of object;
57    
58     { TDynamicGridDataLink }
59    
60     TDynamicGridDataLink = class(TDataLink)
61     private
62     FOwner: TIBDynamicGrid;
63     protected
64     procedure DataEvent(Event: TDataEvent; Info: Ptrint); override;
65     procedure DataSetScrolled(Distance: Integer); override;
66     public
67     constructor Create(AOwner: TIBDynamicGrid);
68     end;
69    
70     { TDBDynamicGridColumn }
71    
72     TDBDynamicGridColumn = class(TColumn)
73     private
74     FAutoSizeColumn: boolean;
75     FColumnTotalsControl: TControl;
76     FDesignWidth: integer;
77     function GetWidth: integer;
78     procedure SetWidth(AValue: integer);
79     public
80     property DesignWidth: integer read FDesignWidth;
81     published
82     property ColumnTotalsControl: TControl read FColumnTotalsControl write FColumnTotalsControl;
83     property AutoSizeColumn: boolean read FAutoSizeColumn write FAutoSizeColumn;
84     property Width: integer read GetWidth write SetWidth;
85     end;
86    
87     TIBDynamicGridColumn = class;
88    
89     { TDBLookupProperties }
90    
91     TDBLookupProperties = class(TPersistent)
92     private
93     FAutoComplete: boolean;
94     FAutoCompleteText: TComboBoxAutoCompleteText;
95     FAutoInsert: boolean;
96     FDataFieldName: string;
97     FItemHeight: integer;
98     FItemWidth: integer;
99     FKeyField: string;
100     FKeyPressInterval: integer;
101     FListField: string;
102     FListSource: TDataSource;
103     FOnAutoInsert: TAutoInsert;
104     FOnCanAutoInsert: TCanAutoInsert;
105     FOnDrawItem: TDrawItemEvent;
106     FOwner: TIBDynamicGridColumn;
107     FRelationName: string;
108     FStyle: TComboBoxStyle;
109     function GetAutoCompleteText: TComboBoxAutoCompleteText;
110     procedure SetAutoCompleteText(AValue: TComboBoxAutoCompleteText);
111     public
112     constructor Create(aOwner: TIBDynamicGridColumn);
113     property Owner: TIBDynamicGridColumn read FOwner;
114     published
115     property DataFieldName: string read FDataFieldName write FDataFieldName;
116     property KeyField: string read FKeyField write FKeyField;
117     property ItemHeight: integer read FItemHeight write FItemHeight;
118     property ItemWidth: integer read FItemWidth write FItemWidth;
119     property ListSource: TDataSource read FListSource write FListSource;
120     property ListField: string read FListField write FListField;
121     property AutoInsert: boolean read FAutoInsert write FAutoInsert default true;
122     property AutoComplete: boolean read FAutoComplete write FAutoComplete default true;
123     property AutoCompleteText: TComboBoxAutoCompleteText
124     read GetAutoCompleteText write SetAutoCompleteText
125     default DefaultComboBoxAutoCompleteText;
126     property KeyPressInterval: integer read FKeyPressInterval write FKeyPressInterval default 500;
127     property RelationName: string read FRelationName write FRelationName;
128     property Style: TComboBoxStyle read FStyle write FStyle default csDropDown;
129     property OnAutoInsert: TAutoInsert read FOnAutoInsert write FOnAutoInsert;
130     property OnCanAutoInsert: TCanAutoInsert read FOnCanAutoInsert write FOnCanAutoInsert;
131     property OnDrawItem: TDrawItemEvent read FOnDrawItem write FOnDrawItem;
132     end;
133    
134     TDBLookupCellEditor = class;
135    
136     { TIBDynamicGridColumn }
137    
138     TIBDynamicGridColumn = class(TDBDynamicGridColumn)
139     private
140     FDBLookupProperties: TDBLookupProperties;
141     FInitialSortColumn: boolean;
142     procedure DoSetupEditor(Data: PtrInt);
143     procedure DoSetDataSources(Data: PtrInt);
144     procedure SetInitialSortColumn(AValue: boolean);
145     public
146     procedure SetupEditor(Editor: TDBlookupCellEditor);
147     constructor Create(ACollection: TCollection); override;
148     destructor Destroy; override;
149     published
150     property InitialSortColumn: boolean read FInitialSortColumn write SetInitialSortColumn;
151     property DBLookupProperties: TDBLookupProperties read FDBLookupProperties write FDBLookupProperties;
152     end;
153    
154     { TDBLookupCellEditor }
155    
156     TDBLookupCellEditor = class(TIBLookupComboEditBox)
157     private
158     FGrid: TCustomGrid;
159     FCol,FRow: Integer;
160     FEditText: string;
161     protected
162     procedure WndProc(var TheMessage : TLMessage); override;
163     procedure CloseUp; override;
164     procedure KeyDown(var Key : Word; Shift : TShiftState); override;
165     procedure msg_GetValue(var Msg: TGridMessage); message GM_GETVALUE;
166     procedure msg_SetGrid(var Msg: TGridMessage); message GM_SETGRID;
167     procedure msg_SetValue(var Msg: TGridMessage); message GM_SETVALUE;
168     procedure msg_SetPos(var Msg: TGridMessage); message GM_SETPOS;
169     procedure msg_GetGrid(var Msg: TGridMessage); message GM_GETGRID;
170     public
171     procedure EditingDone; override;
172     property BorderStyle;
173     property OnEditingDone;
174     end;
175    
176     TDBDynamicGrid = class(TDBGrid)
177     private
178     { Private declarations }
179     FExpandEditorPanelBelowRow: boolean;
180     FEditorPanel: TWinControl;
181     FExpandedRow: integer;
182     FOnBeforeEditorHide: TNotifyEvent;
183     FOnEditorPanelHide: TNotifyEvent;
184     FOnEditorPanelShow: TNotifyEvent;
185     FOnKeyDownHander: TKeyDownHandler;
186     FResizing: boolean;
187     FWeHaveFocus: boolean;
188     FHidingEditorPanel: boolean;
189     FAllowHide: boolean;
190     procedure DoShowEditorPanel(Data: PtrInt);
191     procedure PositionTotals;
192     procedure KeyDownHandler(Sender: TObject; var Key: Word; Shift: TShiftState);
193     procedure SetEditorPanel(AValue: TWinControl);
194     protected
195     procedure ChangeBounds(ALeft, ATop, AWidth, AHeight: integer; KeepBase: boolean); override;
196     procedure DoEnter; override;
197     procedure DoExit; override;
198     procedure DoGridResize;
199     procedure DoEditorHide; override;
200     procedure DoEditorShow; override;
201     procedure DrawCellText(aCol,aRow: Integer; aRect: TRect; aState: TGridDrawState; aText: String); override;
202     Function EditingAllowed(ACol : Integer = -1) : Boolean; override;
203     procedure EditorHide; override;
204     procedure IndicatorClicked(Button: TMouseButton; Shift:TShiftState); virtual;
205     procedure KeyDown(var Key : Word; Shift : TShiftState); override;
206     procedure Loaded; override;
207     procedure DoOnResize; override;
208     function CreateColumns: TGridColumns; override;
209     procedure HeaderSized(IsColumn: Boolean; Index: Integer); override;
210     procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
211     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
212     procedure TopLeftChanged; override;
213     procedure UpdateActive; override;
214     procedure UpdateEditorPanelBounds;
215     procedure UpdateShowing; override;
216     public
217     procedure HideEditorPanel;
218     procedure ShowEditorPanel;
219     constructor Create(TheComponent: TComponent); override;
220     destructor Destroy ;override;
221     procedure ResizeColumns;
222     published
223     property EditorPanel: TWinControl read FEditorPanel write SetEditorPanel;
224     property ExpandEditorPanelBelowRow: boolean read FExpandEditorPanelBelowRow write FExpandEditorPanelBelowRow;
225     property OnBeforeEditorHide: TNotifyEvent read FOnBeforeEditorHide write FOnBeforeEditorHide;
226     property OnEditorPanelShow: TNotifyEvent read FOnEditorPanelShow write FOnEditorPanelShow;
227     property OnEditorPanelHide: TNotifyEvent read FOnEditorPanelHide write FOnEditorPanelHide;
228     property OnKeyDownHander: TKeyDownHandler read FOnKeyDownHander write FOnKeyDownHander;
229     end;
230    
231     { TIBDynamicGrid }
232    
233     TIBDynamicGrid = class(TDBDynamicGrid)
234     private
235     { Private declarations }
236     FAllowColumnSort: boolean;
237     FDataLink: TDynamicGridDataLink;
238     FOnColumnHeaderClick: TOnColumnHeaderClick;
239     FOnUpdateSortOrder: TOnUpdateSortOrder;
240     FDefaultPositionAtEnd: boolean;
241     FDescending: boolean;
242     FColHeaderClick: boolean;
243     FLastColIndex: integer;
244     FIndexFieldNames: string;
245     FIndexFieldsList: TStringList;
246     FBookmark: array of variant;
247     FDBLookupCellEditor: TDBLookupCellEditor;
248     FActive: boolean;
249     procedure ColumnHeaderClick(Index: integer);
250     function GetDataSource: TDataSource;
251     function GetEditorBorderStyle: TBorderStyle;
252     procedure SetDataSource(AValue: TDataSource);
253     procedure SetEditorBorderStyle(AValue: TBorderStyle);
254     procedure ProcessColumns;
255     procedure SetIndexFieldNames(AValue: string);
256     procedure UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
257     procedure UpdateSortColumn(Sender: TObject);
258     procedure DataSetScrolled(Sender: TObject);
259     procedure RestorePosition(Data: PtrInt);
260     procedure DoReOpen(Data: PtrInt);
261     procedure SetupEditor(aEditor: TDBLookupCellEditor; aCol: integer);
262     protected
263     { Protected declarations }
264     procedure DoEditorHide; override;
265     procedure Loaded; override;
266     function CreateColumns: TGridColumns; override;
267     procedure MouseDown(Button: TMouseButton; Shift:TShiftState; X,Y:Integer); override;
268     procedure LinkActive(Value: Boolean); override;
269     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
270     procedure UpdateActive; override;
271     public
272     { Public declarations }
273     constructor Create(TheComponent: TComponent); override;
274     destructor Destroy; override;
275     function EditorByStyle(Style: TColumnButtonStyle): TWinControl; override;
276     property LastSortColumn: integer read FLastColIndex;
277     published
278     { Published declarations }
279     property AllowColumnSort: boolean read FAllowColumnSort write FAllowColumnSort default true;
280     property DataSource: TDataSource read GetDataSource write SetDataSource;
281     property Descending: boolean read FDescending write FDescending;
282     property EditorBorderStyle: TBorderStyle read GetEditorBorderStyle write SetEditorBorderStyle;
283     property DefaultPositionAtEnd: boolean read FDefaultPositionAtEnd write FDefaultPositionAtEnd;
284     property IndexFieldNames: string read FIndexFieldNames write SetIndexFieldNames;
285     property OnColumnHeaderClick: TOnColumnHeaderClick read FOnColumnHeaderClick write FOnColumnHeaderClick;
286     property OnUpdateSortOrder: TOnUpdateSortOrder read FOnUpdateSortOrder write FOnUpdateSortOrder;
287     end;
288    
289     implementation
290    
291     uses Math, IBQuery, IBCustomDataSet, LCLType;
292    
293     { TDBLookupProperties }
294    
295     function TDBLookupProperties.GetAutoCompleteText: TComboBoxAutoCompleteText;
296     begin
297     Result := FAutoCompleteText;
298     if AutoComplete then
299     Result := Result + [cbactEnabled]
300     end;
301    
302     procedure TDBLookupProperties.SetAutoCompleteText(
303     AValue: TComboBoxAutoCompleteText);
304     begin
305     if AValue <> AutoCompleteText then
306     begin
307     FAutoComplete := cbactEnabled in AValue;
308     FAutoCompleteText := AValue - [cbactEnabled]
309     end;
310     end;
311    
312     constructor TDBLookupProperties.Create(aOwner: TIBDynamicGridColumn);
313     begin
314     inherited Create;
315     FOwner := aOwner;
316     FAutoInsert := true;
317     FAutoComplete := true;
318     FAutoCompleteText := DefaultComboBoxAutoCompleteText;
319     FKeyPressInterval := 500;
320     FListSource := nil;
321     FStyle := csDropDown;
322     end;
323    
324     { TDBDynamicGrid }
325    
326     procedure TDBDynamicGrid.DoGridResize;
327     var ColSum: integer;
328     ResizeColCount: integer;
329     I: integer;
330     adjustment: integer;
331     n: integer;
332     begin
333     if (csDesigning in ComponentState) or (Columns.Count = 0) then Exit;
334    
335     FResizing := true;
336     try
337     ColSum := 0;
338     for I := 0 to ColCount - 1 do
339     ColSum := ColSum + ColWidths[I];
340    
341     if Colsum <> ClientWidth then
342     begin
343     ResizeColCount := 0;
344     for I := 0 to Columns.Count -1 do
345     if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
346     begin
347     Inc(ResizeColCount);
348     Colsum := Colsum + TDBDynamicGridColumn(Columns[I]).DesignWidth - Columns[I].Width;
349     Columns[I].Width := TDBDynamicGridColumn(Columns[I]).DesignWidth;
350     end;
351    
352     if (Colsum < ClientWidth) and (ResizeColCount > 0) then
353     begin
354     adjustment := (ClientWidth - ColSum) div ResizeColCount;
355     n := (ClientWidth - ColSum) mod ResizeColCount;
356    
357     for I := 0 to Columns.Count -1 do
358     if TDBDynamicGridColumn(Columns[I]).AutoSizeColumn then
359     begin
360     if I = 0 then
361     Columns[I].Width := Columns[I].Width + adjustment + n
362     else
363     Columns[I].Width := Columns[I].Width + adjustment;
364     end;
365     end;
366     end;
367     PositionTotals;
368     UpdateEditorPanelBounds;
369     finally
370     FResizing := false
371     end;
372     end;
373    
374     procedure TDBDynamicGrid.DoEditorHide;
375     begin
376     inherited DoEditorHide;
377     if (FExpandedRow >= 0) and (FExpandedRow < RowCount) then
378     RowHeights[FExpandedRow] := DefaultRowHeight;
379     FExpandedRow := -1;
380     if CanFocus then SetFocus;
381     if assigned(FOnEditorPanelHide) then
382     OnEditorPanelHide(self);
383     DoOnResize;
384     end;
385    
386     procedure TDBDynamicGrid.DoEditorShow;
387     begin
388     if Editor = FEditorPanel then
389     begin
390     if ExpandEditorPanelBelowRow then
391     RowHeights[Row] := FEditorPanel.Height + DefaultRowHeight
392     else
393     RowHeights[Row] := FEditorPanel.Height;
394     FExpandedRow := Row;
395     inherited DoEditorShow;
396     UpdateEditorPanelBounds; {Position Editor Panel over expanded Row}
397     FEditorPanel.PerformTab(true); {Select First Control}
398     if assigned(FOnEditorPanelShow) then
399     OnEditorPanelShow(self);
400     end
401     else
402     inherited DoEditorShow;
403     end;
404    
405     procedure TDBDynamicGrid.DrawCellText(aCol, aRow: Integer; aRect: TRect;
406     aState: TGridDrawState; aText: String);
407     var Style: TTextStyle;
408     OldStyle: TTextStyle;
409     begin
410     if ExpandEditorPanelBelowRow and assigned(FEditorPanel) and FEditorPanel.Visible and (aRow = FExpandedRow) then
411     begin
412     {Draw the text at the top of the cell}
413     Style := Canvas.TextStyle;
414     OldStyle := Style;
415     try
416     Style.Layout := tlTop;
417     Canvas.TextStyle := Style;
418     inherited DrawCellText(aCol, aRow, aRect, aState, aText);
419     finally
420     Canvas.TextStyle := OldStyle;
421     end;
422    
423     end
424     else
425     inherited DrawCellText(aCol, aRow, aRect, aState, aText);
426     end;
427    
428     function TDBDynamicGrid.EditingAllowed(ACol: Integer): Boolean;
429     begin
430     Result := ((FEditorPanel <> nil) and (FEditorPanel = Editor))
431     or inherited EditingAllowed(ACol);
432     end;
433    
434     procedure TDBDynamicGrid.EditorHide;
435     begin
436     if assigned(FOnBeforeEditorHide) then
437     OnBeforeEditorHide(self);
438     inherited EditorHide;
439     end;
440    
441     procedure TDBDynamicGrid.IndicatorClicked(Button: TMouseButton;
442     Shift: TShiftState);
443     begin
444     if assigned(FEditorPanel) then
445     begin
446     if FEditorPanel.Visible then
447     HideEditorPanel
448     else
449     ShowEditorPanel;
450     end;
451     end;
452    
453     procedure TDBDynamicGrid.KeyDown(var Key: Word; Shift: TShiftState);
454     begin
455     if (Key = VK_F2) and (Shift = []) and assigned(FEditorPanel) then
456     begin
457     if not FEditorPanel.Visible then
458     ShowEditorPanel
459     end
460     else
461     inherited KeyDown(Key, Shift);
462     end;
463    
464     procedure TDBDynamicGrid.DoShowEditorPanel(Data: PtrInt);
465     begin
466     if AppDestroying in Application.Flags then Exit;
467     ShowEditorPanel;
468     end;
469    
470     procedure TDBDynamicGrid.PositionTotals;
471     var I: integer;
472     acol: TDBDynamicGridColumn;
473     LPos: integer;
474     begin
475     LPos := Left;
476     for I := 0 to FirstGridColumn - 1 do
477     LPos := LPos + ColWidths[I];
478    
479     for I := 0 to Columns.Count - 1 do
480     begin
481     acol := TDBDynamicGridColumn(Columns[I]);
482     if assigned(acol.FColumnTotalsControl) then
483     begin
484     acol.FColumnTotalsControl.AutoSize := false;
485     acol.FColumnTotalsControl.Left := LPos;
486     acol.FColumnTotalsControl.Width := acol.Width
487     end;
488     LPos := LPos + acol.Width;
489     end;
490     end;
491    
492     procedure TDBDynamicGrid.KeyDownHandler(Sender: TObject; var Key: Word;
493     Shift: TShiftState);
494     var Done: boolean;
495     begin
496     if Visible and assigned(FEditorPanel) and FEditorPanel.Visible and FWeHaveFocus then
497     begin
498     Done := false;
499     if assigned(FOnKeyDownHander) then
500     OnKeyDownHander(Sender,Key,Shift,Done);
501     if Done then Exit;
502    
503     {Allow Scrolling}
504     if Key in [VK_UP,VK_DOWN] then
505     KeyDown(Key,Shift)
506     else
507     {Cancel Editor}
508     if Key = VK_ESCAPE then
509     begin
510     if DataLink.DataSet.State in [dsInsert,dsEdit] then
511     DataLink.DataSet.Cancel;
512     KeyDown(Key,Shift);
513     end
514     {save}
515     else
516     if Key = VK_F2 then
517     HideEditorPanel;
518     end
519     end;
520    
521     procedure TDBDynamicGrid.SetEditorPanel(AValue: TWinControl);
522     begin
523     if FEditorPanel = AValue then Exit;
524     if FEditorPanel <> nil then
525     RemoveFreeNotification(FEditorPanel);
526     FEditorPanel := AValue;
527     FreeNotification(FEditorPanel);
528     end;
529    
530     procedure TDBDynamicGrid.ChangeBounds(ALeft, ATop, AWidth, AHeight: integer;
531     KeepBase: boolean);
532     begin
533     if assigned(FEditorPanel) and FEditorPanel.Visible then
534     Application.QueueAsyncCall(@DoShowEditorPanel,0); {Restore afterwards if necessary}
535     inherited ChangeBounds(ALeft, ATop, AWidth, AHeight, KeepBase);
536     end;
537    
538     procedure TDBDynamicGrid.DoEnter;
539     begin
540     inherited DoEnter;
541     FWeHaveFocus := true;
542     end;
543    
544     procedure TDBDynamicGrid.DoExit;
545     begin
546     FWeHaveFocus := false;
547     inherited DoExit;
548     end;
549    
550     procedure TDBDynamicGrid.Loaded;
551     begin
552     inherited Loaded;
553     if assigned(FEditorPanel) and not (csDesigning in ComponentState)then
554     FEditorPanel.Visible := false;
555     DoGridResize
556     end;
557    
558     procedure TDBDynamicGrid.DoOnResize;
559     begin
560     inherited DoOnResize;
561     DoGridResize
562     end;
563    
564     function TDBDynamicGrid.CreateColumns: TGridColumns;
565     begin
566     result := TDBGridColumns.Create(Self, TDBDynamicGridColumn);
567     end;
568    
569     procedure TDBDynamicGrid.HeaderSized(IsColumn: Boolean; Index: Integer);
570     begin
571     inherited HeaderSized(IsColumn, Index);
572     PositionTotals
573     end;
574    
575     procedure TDBDynamicGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
576     Y: Integer);
577     var Coord: TGridCoord;
578     begin
579     inherited MouseDown(Button, Shift, X, Y);
580    
581     Coord := MouseCoord(X,Y);
582     if (Coord.X = 0) and (Coord.Y > 0) then
583     IndicatorClicked(Button,Shift);
584     end;
585    
586     procedure TDBDynamicGrid.Notification(AComponent: TComponent;
587     Operation: TOperation);
588     begin
589     inherited Notification(AComponent, Operation);
590     if (Operation = opRemove) and
591     (AComponent = FEditorPanel) then FEditorPanel := nil;
592     end;
593    
594     procedure TDBDynamicGrid.TopLeftChanged;
595     begin
596     inherited TopLeftChanged;
597     UpdateEditorPanelBounds;
598     end;
599    
600     procedure TDBDynamicGrid.UpdateActive;
601     begin
602     inherited UpdateActive;
603    
604     if not (csLoading in ComponentState) and assigned(DataLink) and
605     assigned(DataLink.DataSet) and (DataLink.DataSet.State = dsInsert) then
606     Application.QueueAsyncCall(@DoShowEditorPanel,0);
607     end;
608    
609     procedure TDBDynamicGrid.UpdateEditorPanelBounds;
610     var R: TRect;
611     Dummy: integer;
612     begin
613     if assigned(FEditorPanel) and FEditorPanel.Visible and
614     (FExpandedRow >= 0) and (FExpandedRow < RowCount) then
615     begin
616     // Upper and Lower bounds for this row
617     ColRowToOffSet(False, True, FExpandedRow, R.Top, R.Bottom);
618     //Left Bound for visible Columns
619     ColRowToOffSet(True,True,1,R.Left,Dummy);
620     //Right Bound for visible columns
621     ColRowToOffSet(True,True,ColCount - 1,Dummy,R.Right);
622     if ExpandEditorPanelBelowRow then
623     R.Top := R.Top + DefaultRowHeight;
624     FEditorPanel.BoundsRect := R;
625     end;
626     end;
627    
628     procedure TDBDynamicGrid.UpdateShowing;
629     begin
630     inherited UpdateShowing;
631     DoGridResize
632     end;
633    
634     procedure TDBDynamicGrid.HideEditorPanel;
635     begin
636     if Editor = FEditorPanel then
637     EditorMode := false;
638     end;
639    
640     procedure TDBDynamicGrid.ShowEditorPanel;
641     begin
642     if csDesigning in ComponentState then Exit;
643     Editor := FEditorPanel;
644     EditorMode := true;
645     end;
646    
647     constructor TDBDynamicGrid.Create(TheComponent: TComponent);
648     begin
649     inherited Create(TheComponent);
650     ScrollBars := ssAutoVertical;
651     if not (csDesigning in ComponentState) then
652     Application.AddOnKeyDownBeforeHandler(@KeyDownHandler,false);
653     end;
654    
655     destructor TDBDynamicGrid.Destroy;
656     begin
657     if not (csDesigning in ComponentState) then
658     Application.RemoveOnKeyDownBeforeHandler(@KeyDownHandler);
659     inherited Destroy;
660     end;
661    
662     procedure TDBDynamicGrid.ResizeColumns;
663     begin
664     DoGridResize;
665     end;
666    
667     { TDBDynamicGridColumn }
668    
669     procedure TDBDynamicGridColumn.SetWidth(AValue: integer);
670     begin
671     if Width = AValue then Exit;
672     inherited Width := AValue;
673     if not TDBDynamicGrid(Grid).FResizing then
674     FDesignWidth := Width
675     end;
676    
677     function TDBDynamicGridColumn.GetWidth: integer;
678     begin
679     Result := inherited Width
680     end;
681    
682     { TDBLookupCellEditor }
683    
684     procedure TDBLookupCellEditor.WndProc(var TheMessage: TLMessage);
685     begin
686     if TheMessage.msg=LM_KILLFOCUS then begin
687     if HWND(TheMessage.WParam) = HWND(Handle) then begin
688     // lost the focus but it returns to ourselves
689     // eat the message.
690     TheMessage.Result := 0;
691     exit;
692     end;
693     end;
694     inherited WndProc(TheMessage);
695     end;
696    
697     procedure TDBLookupCellEditor.CloseUp;
698     begin
699     UpdateData(nil); {Force Record Update}
700     if FGrid<>nil then
701     (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Text);
702     inherited CloseUp;
703     end;
704    
705     procedure TDBLookupCellEditor.KeyDown(var Key: Word; Shift: TShiftState);
706     begin
707     if (Key = VK_TAB) and assigned(FGrid) then
708     TIBDynamicGrid(FGrid).KeyDown(Key,Shift)
709     else
710     inherited KeyDown(Key, Shift);
711     end;
712    
713     procedure TDBLookupCellEditor.msg_GetValue(var Msg: TGridMessage);
714     begin
715     CheckAndInsert;
716     Msg.Col := FCol;
717     Msg.Row := FRow;
718     Msg.Value:= Trim(Text);
719     end;
720    
721     procedure TDBLookupCellEditor.msg_SetGrid(var Msg: TGridMessage);
722     begin
723     FGrid:=Msg.Grid;
724     Msg.Options:=EO_AUTOSIZE or EO_SELECTALL or EO_HOOKKEYPRESS or EO_HOOKKEYUP;
725     end;
726    
727     procedure TDBLookupCellEditor.msg_SetValue(var Msg: TGridMessage);
728     begin
729     FGrid := Msg.Grid;
730     FCol := Msg.Col;
731     FRow := Msg.Row;
732     FEditText := Msg.Value;
733     SelStart := Length(Text);
734     TIBDynamicGrid(FGrid).SetupEditor(self,FCol);
735     end;
736    
737     procedure TDBLookupCellEditor.msg_SetPos(var Msg: TGridMessage);
738     begin
739     FCol := Msg.Col;
740     FRow := Msg.Row;
741     end;
742    
743     procedure TDBLookupCellEditor.msg_GetGrid(var Msg: TGridMessage);
744     begin
745     Msg.Grid := FGrid;
746     Msg.Options:= EO_IMPLEMENTED;
747     end;
748    
749     procedure TDBLookupCellEditor.EditingDone;
750     begin
751     inherited EditingDone;
752     if FGrid<>nil then
753     FGrid.EditingDone;
754     end;
755    
756     { TIBDynamicGridColumn }
757    
758     procedure TIBDynamicGridColumn.DoSetupEditor(Data: PtrInt);
759     var Editor: TDBlookupCellEditor;
760     begin
761     if AppDestroying in Application.Flags then Exit;
762    
763     Editor := TDBlookupCellEditor(Data);
764     Editor.DataSource := nil;
765     Editor.ListSource := nil; {Allows change without causing an error}
766     Editor.KeyValue := NULL;
767    
768     with DBLookupProperties do
769     begin
770     {Setup Properties}
771     Editor.AutoInsert := AutoInsert;
772     Editor.AutoComplete := AutoComplete;
773     Editor.AutoCompleteText := AutoCompleteText;
774     Editor.KeyPressInterval := KeyPressInterval;
775     Editor.Style := Style;
776     Editor.ItemHeight := ItemHeight;
777     Editor.ItemWidth := ItemWidth;
778     Editor.RelationName := RelationName;
779     Editor.OnAutoInsert := OnAutoInsert;
780     Editor.OnCanAutoInsert := OnCanAutoInsert;
781     Editor.OnDrawItem := OnDrawItem;
782    
783     {Setup Data Links}
784     if KeyField <> '' then
785     Editor.KeyField := KeyField
786     else
787     Editor.KeyField := ListField;
788     Editor.ListField := ListField;
789     Editor.DataField := DataFieldName;
790     end;
791     Application.QueueAsyncCall(@DoSetDataSources,PtrInt(Editor));
792     end;
793    
794     procedure TIBDynamicGridColumn.DoSetDataSources(Data: PtrInt);
795     var Editor: TDBlookupCellEditor;
796     begin
797     if AppDestroying in Application.Flags then Exit;
798    
799     Editor := TDBlookupCellEditor(Data);
800     with DBLookupProperties do
801     begin
802     Editor.ListSource := ListSource;
803     if DataFieldName <> '' then
804     Editor.DataSource := TDBGrid(Grid).DataSource;
805     end;
806     Editor.Text := Editor.FEditText;
807     end;
808    
809     procedure TIBDynamicGridColumn.SetInitialSortColumn(AValue: boolean);
810     begin
811     if FInitialSortColumn = AValue then Exit;
812     FInitialSortColumn := AValue;
813     (Grid as TIBDynamicGrid).UpdateSortColumn(self)
814     end;
815    
816     procedure TIBDynamicGridColumn.SetupEditor(Editor: TDBlookupCellEditor);
817     begin
818     Application.QueueAsyncCall(@DoSetupEditor,PtrInt(Editor));
819     end;
820    
821     constructor TIBDynamicGridColumn.Create(ACollection: TCollection);
822     begin
823     inherited Create(ACollection);
824     FDBLookupProperties := TDBLookupProperties.Create(self);
825     end;
826    
827     destructor TIBDynamicGridColumn.Destroy;
828     begin
829     if assigned(FDBLookupProperties) then FDBLookupProperties.Free;
830     inherited Destroy;
831     end;
832    
833     { TDynamicGridDataLink }
834    
835     procedure TDynamicGridDataLink.DataEvent(Event: TDataEvent; Info: Ptrint);
836     begin
837     if (Event = deCheckBrowseMode) and (Info = 1) and not DataSet.Active then
838     begin
839     if (DataSet is TIBDataSet) then
840     FOwner.UpdateSQL(self,TIBDataSet(DataSet).Parser)
841     else
842     if (DataSet is TIBQuery) then
843     FOwner.UpdateSQL(self,TIBQuery(DataSet).Parser)
844     end
845     else
846     inherited DataEvent(Event, Info);
847     end;
848    
849     procedure TDynamicGridDataLink.DataSetScrolled(Distance: Integer);
850     begin
851     inherited DataSetScrolled(Distance);
852     FOwner.DataSetScrolled(self)
853     end;
854    
855     constructor TDynamicGridDataLink.Create(AOwner: TIBDynamicGrid);
856     begin
857     inherited Create;
858     FOwner := AOwner
859     end;
860    
861    
862     { TIBDynamicGrid }
863    
864     procedure TIBDynamicGrid.ColumnHeaderClick(Index: integer);
865     begin
866     FColHeaderClick := true;
867     try
868     if Index = FLastColIndex then
869     FDescending := not FDescending;
870    
871     if assigned(FOnColumnHeaderClick) then
872     OnColumnHeaderClick(self,Index);
873    
874     FLastColIndex := Index;
875     if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
876     begin
877     DataSource.DataSet.Active := false;
878     Application.QueueAsyncCall(@DoReopen,0)
879     end;
880     finally
881     FColHeaderClick := false
882     end;
883     end;
884    
885     function TIBDynamicGrid.GetDataSource: TDataSource;
886     begin
887     if assigned(DataLink) then
888     Result := inherited DataSource
889     else
890     Result := nil;
891     end;
892    
893     function TIBDynamicGrid.GetEditorBorderStyle: TBorderStyle;
894     begin
895     if Editor = FDBLookupCellEditor then
896     Result := FDBLookupCellEditor.BorderStyle
897     else
898     Result := inherited EditorBorderStyle
899     end;
900    
901     procedure TIBDynamicGrid.SetDataSource(AValue: TDataSource);
902     begin
903     inherited DataSource := AValue;
904     FDataLink.DataSource := AValue;
905     end;
906    
907     procedure TIBDynamicGrid.SetEditorBorderStyle(AValue: TBorderStyle);
908     begin
909     inherited EditorBorderStyle := AValue;
910     if FDBLookupCellEditor.BorderStyle <> AValue then
911     begin
912     FDBLookupCellEditor.BorderStyle := AValue;
913     if (Editor = FDBLookupCellEditor) and EditorMode then
914     EditorWidthChanged(Col,FDBLookupCellEditor.Width);
915     end;
916     end;
917    
918     procedure TIBDynamicGrid.ProcessColumns;
919     var i: integer;
920     begin
921     for i := 0 to Columns.Count - 1 do
922     begin
923     if TIBDynamicGridColumn(columns[i]).InitialSortColumn then
924     FLastColIndex := i
925     end
926     end;
927    
928     procedure TIBDynamicGrid.SetIndexFieldNames(AValue: string);
929     var idx: integer;
930     begin
931     if FIndexFieldNames = AValue then Exit;
932     FIndexFieldNames := AValue;
933     idx := 1;
934     FIndexFieldsList.Clear;
935     while idx <= Length(AValue) do
936     FIndexFieldsList.Add(ExtractFieldName(AValue,idx));
937     end;
938    
939     procedure TIBDynamicGrid.UpdateSQL(Sender: TObject; Parser: TSelectSQLParser);
940     var OrderBy: string;
941     FieldPosition: integer;
942     begin
943     if (Sender = TObject(FDataLink)) and assigned(DataSource) and assigned(DataSource.DataSet)
944     and (DataSource.DataSet is TIBCustomDataSet) then
945     begin
946     FieldPosition := Parser.GetFieldPosition(Columns[FLastColIndex].FieldName);
947     if FieldPosition = 0 then Exit;
948    
949     if Descending then
950     Parser.OrderByClause := IntToStr(FieldPosition) + ' desc'
951     else
952     Parser.OrderByClause := IntToStr(FieldPosition) + ' asc';
953    
954     if assigned(FOnUpdateSortOrder) then
955     begin
956     OrderBy := Parser.OrderByClause;
957     OnUpdateSortOrder(self,FLastColIndex,OrderBy);
958     Parser.OrderByClause := OrderBy
959     end
960     end;
961     end;
962    
963     procedure TIBDynamicGrid.UpdateSortColumn(Sender: TObject);
964     var i: integer;
965     begin
966     if Sender is TIBDynamicGridColumn then
967     begin
968     for i := 0 to Columns.Count -1 do
969     if TObject(Columns[i]) <> Sender then
970     TIBDynamicGridColumn(Columns[i]).InitialSortColumn := false
971     end
972    
973     end;
974    
975     procedure TIBDynamicGrid.DataSetScrolled(Sender: TObject);
976     var i: integer;
977     F: TField;
978     begin
979     SetLength(FBookmark,FIndexFieldsList.Count);
980     for i := 0 to FIndexFieldsList.Count - 1 do
981     begin
982     F := DataSource.DataSet.FindField(FIndexFieldsList[i]);
983     if assigned(F) then
984     FBookmark[i] := F.AsVariant;
985     end;
986     end;
987    
988     procedure TIBDynamicGrid.RestorePosition(Data: PtrInt);
989     begin
990     if AppDestroying in Application.Flags then Exit;
991    
992     if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
993     begin
994     if (Length(FBookmark) > 0) and
995     DataSource.DataSet.Locate(FIndexFieldNames,FBookmark,[]) then Exit;
996    
997     if FDefaultPositionAtEnd then
998     DataSource.DataSet.Last
999     end;
1000     end;
1001    
1002     procedure TIBDynamicGrid.DoReOpen(Data: PtrInt);
1003     begin
1004     DataSource.DataSet.Active := true;
1005     end;
1006    
1007     procedure TIBDynamicGrid.SetupEditor(aEditor: TDBLookupCellEditor; aCol: integer
1008     );
1009     var C: TIBDynamicGridColumn;
1010     begin
1011     C := ColumnFromGridColumn(aCol) as TIBDynamicGridColumn;
1012     C.SetupEditor(aEditor);
1013     end;
1014    
1015     procedure TIBDynamicGrid.DoEditorHide;
1016     var i: integer;
1017     begin
1018     inherited DoEditorHide;
1019     if assigned(EditorPanel) then
1020     for i := 0 to EditorPanel.ControlCount -1 do
1021     if EditorPanel.Controls[i] is TIBLookupComboEditBox then
1022     EditorPanel.Controls[i].Perform(CM_VISIBLECHANGED, WParam(ord(false)), 0);
1023     end;
1024    
1025     procedure TIBDynamicGrid.Loaded;
1026     begin
1027     inherited Loaded;
1028     ProcessColumns;
1029     end;
1030    
1031     function TIBDynamicGrid.CreateColumns: TGridColumns;
1032     begin
1033     result := TDBGridColumns.Create(Self, TIBDynamicGridColumn);
1034     end;
1035    
1036     procedure TIBDynamicGrid.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
1037     Y: Integer);
1038     var Coord: TGridCoord;
1039     obe: boolean;
1040     function PtInRect(const Rect : TRect;const p : TPoint) : Boolean;
1041    
1042     begin
1043     PtInRect:=(p.y>=Rect.Top) and
1044     (p.y<Rect.Bottom) and
1045     (p.x>=Rect.Left) and
1046     (p.x<Rect.Right);
1047     end;
1048     begin
1049     if (Editor is TDBLookupCellEditor) and Editor.Visible
1050     and not PtInRect(Editor.BoundsRect,Point(X,Y)) then
1051     Editor.Perform(CM_EXIT,0,0); {Do insert new value if necessary}
1052     inherited MouseDown(Button, Shift, X, Y);
1053     obe := AllowOutboundEvents;
1054     AllowOutboundEvents := false;
1055     try
1056     Coord := MouseCoord(X,Y);
1057     if AllowColumnSort and (Coord.X <> -1) and
1058     (Coord.Y = 0) and (MouseCoord(X+5,Y).X = Coord.X) {not on boundary}
1059     and (MouseCoord(X-5,Y).X = Coord.X) then
1060     ColumnHeaderClick(Coord.X-1);
1061     finally
1062     AllowOutboundEvents := obe
1063     end;
1064     end;
1065    
1066     procedure TIBDynamicGrid.LinkActive(Value: Boolean);
1067     begin
1068     inherited LinkActive(Value);
1069     if (FActive <> Value) and Value then
1070     Application.QueueAsyncCall(@RestorePosition,0);
1071     FActive := Value
1072     end;
1073    
1074     procedure TIBDynamicGrid.Notification(AComponent: TComponent;
1075     Operation: TOperation);
1076     begin
1077     inherited Notification(AComponent, Operation);
1078     if (Operation = opRemove) and
1079     (FDataLink <> nil) and (AComponent = DataSource) then DataSource := nil;
1080     end;
1081    
1082     procedure TIBDynamicGrid.UpdateActive;
1083     begin
1084     inherited UpdateActive;
1085     if assigned(FDataLink) and assigned(FDataLink.DataSet) and
1086     FDataLink.DataSet.Active and (FDataLink.DataSet.State = dsInsert) then
1087     DataSetScrolled(nil);
1088     end;
1089    
1090     constructor TIBDynamicGrid.Create(TheComponent: TComponent);
1091     begin
1092     inherited Create(TheComponent);
1093     FAllowColumnSort := true;
1094     FDataLink := TDynamicGridDataLink.Create(self);
1095     FIndexFieldsList := TStringList.Create;
1096     FIndexFieldsList.Delimiter := ';';
1097     FIndexFieldsList.StrictDelimiter := true;
1098     FDBLookupCellEditor := TDBLookupCellEditor.Create(nil);
1099     FDBLookupCellEditor.Name := 'DBLookupCellEditor';
1100     FDBLookupCellEditor.Visible := False;
1101     FDBLookupCellEditor.AutoSize := false;
1102     end;
1103    
1104     destructor TIBDynamicGrid.Destroy;
1105     begin
1106     if assigned(FDataLink) then FDataLink.Free;
1107     if assigned(FIndexFieldsList) then FIndexFieldsList.Free;
1108     if assigned(FDBLookupCellEditor) then FDBLookupCellEditor.Free;
1109     inherited Destroy;
1110     end;
1111    
1112     function TIBDynamicGrid.EditorByStyle(Style: TColumnButtonStyle): TWinControl;
1113     var C: TIBDynamicGridColumn;
1114     bs: TColumnButtonStyle;
1115     begin
1116     C := ColumnFromGridColumn(Col) as TIBDynamicGridColumn;
1117     if C <> nil then
1118     begin
1119     bs := C.ButtonStyle;
1120     if (bs in [cbsAuto,cbsPickList]) and assigned(C.DBLookupProperties.ListSource) then
1121     begin
1122     Result := FDBLookupCellEditor;
1123     Exit;
1124     end;
1125     end;
1126     Result := inherited EditorByStyle(Style);
1127     end;
1128    
1129     end.