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

File Contents

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