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