ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/ibx/trunk/ibcontrols/IBDynamicGrid.pas
Revision: 29
Committed: Sat May 9 11:37:49 2015 UTC (9 years, 6 months ago) by tony
Content type: text/x-pascal
File size: 34991 byte(s)
Log Message:
Committing updates for Release R1-2-4

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