unit QuickStrikeGrid; interface Uses Controls, Grids, Classes, Types, Messages, Graphics; Type // If a string is missing or invalid, it should be set to ''. If a double // is missing or invalid it should be set to NaN. TQuickStrikeEntry = Record Symbol : String; Last : Double; PrevClose : Double; SmaDivergence : Double; Time : String; End; TQuickStrikeEntries = Array Of TQuickStrikeEntry; TQuickStrikeGrid = Class(TDrawGrid) Private FEntries : TQuickStrikeEntries; FTextColor : TColor; Procedure SetColWidth(ACol : LongInt; Const SizeHint : String); Procedure WMNCCalcSize(var msg: TMessage); message WM_NCCALCSIZE; Protected Procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); Override; Procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Override; Public // I make a copy of this so you can change the original. // The stuff you want to show up on top is entry 0 of the array. Procedure SetEntries(Entries : TQuickStrikeEntries); // Presumably you just set visible = true, and align = alClient. // We set the Parent to be the Owner. That is required to make the // initialization go smoothly. Constructor Create(AOwner: TWinControl); Reintroduce; // Use Color to change the background color, and TextColor to change the // foreground color. We do not automatically redraw when these are // changed. We could, but for simplicity I'm assuming that these will // only be set on creation. Property TextColor : TColor Read FTextColor Write FTextColor; End; implementation Uses ExternalLinkingUnit, Math, SysUtils, Windows; Const ColSymbol = 0; ColLast = 1; ColUpDay = 2; ColUpDayPercent = 3; ColDiverg = 4; ColTime = 5; Procedure TQuickStrikeGrid.WMNCCalcSize(var msg: TMessage); Var Style : Integer; Begin // Disable the horizontal scroll bar to make it look more like what they // already had. // Code inspired by: http://www.delphi3000.com/articles/article_3254.asp style := getWindowLong( handle, GWL_STYLE ); If (Style And WS_HSCROLL) <> 0 Then SetWindowLong(Handle, GWL_STYLE, Style And Not WS_HSCROLL); Inherited End; { This was copied in large part from AlertGrids.Pas in the ActiveX control. There are several variations of this out there, but this one seemed like the simplest. } Procedure TQuickStrikeGrid.DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); Var ClientRect : TRect; Function ClientWidth : Integer; Begin Result := ClientRect.Right - ClientRect.Left End; Function ClientHeight : Integer; Begin Result := ClientRect.Bottom - ClientRect.Top End; Procedure TextRightJustify(TextString : String); Var TextHeight, TextWidth : Integer; Begin TextHeight := Canvas.TextHeight(TextString); TextWidth := Canvas.TextWidth(TextString); Canvas.Brush.Style := bsClear; Canvas.TextRect(ClientRect, ClientRect.Left + Max(ClientWidth - TextWidth, 0), ClientRect.Top + Max(ClientHeight - TextHeight, 0) Div 2, TextString) End; Procedure TextLeftJustify(TextString : String); Var TextHeight : Integer; Begin TextHeight := Canvas.TextHeight(TextString); Canvas.Brush.Style := bsClear; Canvas.TextRect(ClientRect, ClientRect.Left, ClientRect.Top + Max(ClientHeight - TextHeight, 0) Div 2, TextString) End; Procedure TextCenter(TextString : String); Var TextHeight, TextWidth : Integer; Begin TextHeight := Canvas.TextHeight(TextString); TextWidth := Canvas.TextWidth(TextString); Canvas.TextRect(ClientRect, ClientRect.Left + Max(ClientWidth - TextWidth, 0) Div 2, ClientRect.Top + Max(ClientHeight - TextHeight, 0) Div 2, TextString); End; Function TooWide(TextString : String) : Boolean; Begin Result := Canvas.TextWidth(TextString) > ClientWidth End; Function MakeIntegerFit(I : Integer) : String; Procedure Adjust(Adjusted : Double; Symbol : Char); Begin Result := Format('%.2f%s', [Adjusted, Symbol]); If TooWide(Result) Then Begin Result := Format('%.1f%s', [Adjusted, Symbol]); If TooWide(Result) Then Begin Result := Format('%.0f%s', [Adjusted, Symbol]); If TooWide(Result) Then Result := '...' End End End; Begin Result := Format('%d', [I]); If TooWide(Result) Then If (I < 1000) Then Result := '...' Else If (I < 1000000) Then Adjust(I / 1.0e+3, 'K') Else Adjust(I / 1.0e+6, 'M') End; Function MakeStringFit(S : String) : String; Var Count, SpaceRemaining : Integer; Begin If TooWide(S) Then Begin Count := 1; SpaceRemaining := ClientWidth - Canvas.TextWidth('...'); While Count <= Length(S) Do Begin SpaceRemaining := SpaceRemaining - Canvas.TextWidth(S[Count]); If SpaceRemaining < 0 Then Break; Inc(Count) End; Result := Copy(S, 0, Pred(Count)) + '...'; End Else Result := S; End; Function MakePriceFit(P : Double) : String; Begin Result := Format('%f', [P]); If TooWide(Result) Then Result := '...' End; Function MakePercentFit(P : Double) : String; Begin Result := Format('%f%%', [P]); If TooWide(Result) Then Result := '...' End; Var DataIndex, DataTypeIndex : Integer; EntryValid : Boolean; Name : String; FgColor, BgColor, SwapColor : TColor; Begin { TQuickStrikeGrid.DrawCell } DataIndex := Pred(ARow); EntryValid := (DataIndex >= 0) And (DataIndex < Length(FEntries)); DataTypeIndex := ACol; Canvas.Font.Style := []; If gdFixed In AState Then Begin BgColor := FixedColor; FgColor := clBlack End Else Begin BgColor := Color; FgColor := TextColor End; If EntryValid And (DataIndex = 0) Then Begin // Swap the colors for the most recent entry. SwapColor := FgColor; FgColor := BgColor; BgColor := SwapColor End; Canvas.Brush.Color := BgColor; Canvas.Font.Color := FgColor; Canvas.FillRect(ARect); ClientRect := ARect; If (gdFixed In AState) And Ctl3D Then Begin DrawEdge(Canvas.Handle, ARect, BDR_RAISEDINNER, BF_BOTTOM Or BF_RIGHT Or BF_TOP Or BF_LEFT); Inc(ClientRect.Left); Inc(ClientRect.Top); Dec(ClientRect.Right); Dec(ClientRect.Bottom) End; If ClientWidth > 14 Then Begin Inc(ClientRect.Left, 3); Dec(ClientRect.Right, 3) End; If gdFixed In AState Then Begin Case DataTypeIndex Of ColSymbol : Name := 'Symbol'; ColLast : Name := 'Last'; ColUpDay : Name := 'Chg'; ColUpDayPercent : Name := '%Chg'; ColDiverg : Name := 'Divrg'; ColTime : Name := 'Time'; Else Name := ''; End; TextCenter(MakeStringFit(Name)) { This is a column or row header. When we start sorting the items, we should consider using the triangles from TSortableHeaderCellRenderer.DrawTriangle in MarketViewBase.Pas to show which row or column is the sort key, and the direction of the sort operation. } End Else If EntryValid Then With FEntries[DataIndex] Do Case DataTypeIndex Of ColSymbol : TextLeftJustify(MakeStringFit(Symbol)); ColLast : If Not IsNaN(Last) Then TextRightJustify(MakePriceFit(Last)); ColUpDay : If Not (IsNaN(Last) Or IsNaN(PrevClose)) Then TextRightJustify(MakePriceFit(Last - PrevClose)); ColUpDayPercent : If Not (IsNaN(Last) Or IsNaN(PrevClose) Or (PrevClose = 0)) Then TextRightJustify(MakePercentFit((Last - PrevClose) / PrevClose * 100.0)); ColDiverg : If Not (IsNaN(SmaDivergence)) Then TextRightJustify(MakePriceFit(SmaDivergence)); ColTime : TextLeftJustify(Time); End End; { TQuickStrikeGrid.DrawCell } Procedure TQuickStrikeGrid.SetEntries(Entries : TQuickStrikeEntries); Begin FEntries := Entries; SetLength(FEntries, Length(FEntries)); RowCount := Succ(Max(1, Length(FEntries))); Invalidate End; Procedure TQuickStrikeGrid.SetColWidth(ACol : LongInt; Const SizeHint : String); Begin If Ctl3D Then ColWidths[ACol] := 16 + Canvas.TextWidth(SizeHint) Else ColWidths[ACol] := 12 + Canvas.TextWidth(SizeHint) End; Procedure TQuickStrikeGrid.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); Var DataIndex, Col, Row : Integer; Begin Inherited; If Button = mbLeft Then Begin MouseToCell(X, Y, Col, Row); DataIndex := Pred(Row); If (DataIndex >= 0) And (DataIndex < Length(FEntries)) Then ExternalLinkingWindow.SendSymbol(FEntries[DataIndex].Symbol) End End; Constructor TQuickStrikeGrid.Create(AOwner: TWinControl); Begin Inherited Create(AOwner); DoubleBuffered := True; Color := clBlack; TextColor := clWhite; ColCount := 6; RowCount := 2; FixedRows := 1; FixedCols := 0; Options := [goColSizing, goThumbTracking]; DefaultDrawing := False; If Assigned(AOwner) Then Begin Parent := AOwner; Canvas.Font := Font; DefaultRowHeight := Canvas.TextHeight('Xy()') + 4; SetColWidth(ColSymbol, 'Symbol'); SetColWidth(ColLast, '100.00'); SetColWidth(ColUpDay, '-1.93'); SetColWidth(ColUpDayPercent, '-13.3%'); SetColWidth(ColDiverg, '-0.12'); SetColWidth(ColTime, '00:00:00'); End; End; End.