ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/IBDynamicGrid.pas
Revision: 27
Committed: Tue Apr 14 13:10:23 2015 UTC (9 years ago) by tony
Content type: text/x-pascal
File size: 34961 byte(s)
Log Message:
Committing updates for Release R1-2-3

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