ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/IBDynamicGrid.pas
Revision: 31
Committed: Tue Jul 14 15:31:25 2015 UTC (8 years, 9 months ago) by tony
Content type: text/x-pascal
File size: 36064 byte(s)
Log Message:
Committing updates for Release R1-3-0

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