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

# Content
1 (*
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 * The Original Code is (C) 2015 Tony Whyman, MWA Software
19 * (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 IBCustomDataSet;
36
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 FOnCloseUp: TNotifyEvent;
95 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 property OnCloseUp: TNotifyEvent read FOnCloseUp write FOnCloseUp;
122 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 procedure Loaded; override;
157 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 function ActiveControl: TControl;
183 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 property VisibleRowCount;
216 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 {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 { TIBDynamicGrid }
241
242 TIBDynamicGrid = class(TDBDynamicGrid)
243 private
244 { Private declarations }
245 FAllowColumnSort: boolean;
246 FIBControlLink: TIBGridControlLink;
247 FOnColumnHeaderClick: TOnColumnHeaderClick;
248 FOnRestorePosition: TOnRestorePosition;
249 FOnUpdateSortOrder: TOnUpdateSortOrder;
250 FDefaultPositionAtEnd: boolean;
251 FDescending: boolean;
252 FColHeaderClick: boolean;
253 FLastColIndex: integer;
254 FIndexFieldNames: string;
255 FIndexFieldsList: TStringList;
256 FBookmark: TLocationArray;
257 FDBLookupCellEditor: TDBLookupCellEditor;
258 FActive: boolean;
259 procedure ColumnHeaderClick(Index: integer);
260 function GetDataSource: TDataSource;
261 function GetEditorBorderStyle: TBorderStyle;
262 procedure IBControlLinkChanged;
263 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 procedure RestorePosition;
270 procedure SavePosition;
271 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 procedure MoveSelection; override;
281 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 property OnRestorePosition: TOnRestorePosition read FOnRestorePosition write FOnRestorePosition;
299 property OnUpdateSortOrder: TOnUpdateSortOrder read FOnUpdateSortOrder write FOnUpdateSortOrder;
300 end;
301
302 implementation
303
304 uses Math, IBQuery, LCLType;
305
306 { 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 { 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 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 DoOnChangeBounds;
412 if assigned(FOnEditorPanelHide) then
413 OnEditorPanelHide(self);
414 end;
415 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 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 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 AControl: TControl;
538 begin
539 if Visible and assigned(FEditorPanel) and FEditorPanel.Visible and FWeHaveFocus then
540 begin
541 Done := false;
542 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 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 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 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 Begin
757 (FGrid as TIBDynamicGrid).EditorTextChanged(FCol, FRow, Trim(Text));
758 (FGrid as TIBDynamicGrid).UpdateData;
759 end;
760 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 procedure TDBLookupCellEditor.Loaded;
772 begin
773 inherited Loaded;
774 Text := '';
775 end;
776
777 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 Editor.OnCloseUp := OnCloseUp;
846
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 Editor.SelStart := Length(Editor.Text);
872 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 IBControlLinkChanged;
942 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 if assigned(DataSource) and assigned(DataSource.DataSet)
981 and (DataSource.DataSet is TIBCustomDataSet) then
982 begin
983 if (FLastColIndex < 0) or (FLastColIndex >= Columns.Count) then Exit;
984 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 procedure TIBDynamicGrid.RestorePosition;
1014 begin
1015 if assigned(DataSource) and assigned(DataSource.DataSet) and DataSource.DataSet.Active then
1016 begin
1017 if assigned(FOnRestorePosition) then
1018 OnRestorePosition(self,@FBookmark);
1019 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 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 procedure TIBDynamicGrid.DoReOpen(Data: PtrInt);
1043 begin
1044 DataSource.DataSet.Active := true;
1045 end;
1046
1047 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 procedure TIBDynamicGrid.SetupEditor(aEditor: TDBLookupCellEditor; aCol: integer
1056 );
1057 var C: TIBDynamicGridColumn;
1058 begin
1059 C := ColumnFromGridColumn(aCol) as TIBDynamicGridColumn;
1060 if (c <> nil) then
1061 C.SetupEditor(aEditor);
1062 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 IBControlLinkChanged;
1078 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 if AllowColumnSort and (Coord.X <> -1) and (FixedRows > 0) and
1108 (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 procedure TIBDynamicGrid.MoveSelection;
1117 begin
1118 inherited MoveSelection;
1119 SavePosition;
1120 end;
1121
1122 procedure TIBDynamicGrid.LinkActive(Value: Boolean);
1123 begin
1124 IBControlLinkChanged;
1125 inherited LinkActive(Value);
1126 if (FActive <> Value) and Value then
1127 RestorePosition;
1128 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 (FIBControlLink <> nil) and (AComponent = DataSource) then FIBControlLink.IBDataSet := nil;
1137 end;
1138
1139 procedure TIBDynamicGrid.UpdateActive;
1140 begin
1141 inherited UpdateActive;
1142 if assigned(DataLink) and assigned(DataLink.DataSet) and
1143 DataLink.DataSet.Active and (DataLink.DataSet.State = dsInsert) then
1144 SavePosition;
1145 end;
1146
1147 constructor TIBDynamicGrid.Create(TheComponent: TComponent);
1148 begin
1149 inherited Create(TheComponent);
1150 FAllowColumnSort := true;
1151 FIBControlLink := TIBGridControlLink.Create(self);
1152 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 if assigned(FIBControlLink) then FIBControlLink.Free;
1164 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.