unit VpContactGridPainter; {$I vp.inc} interface uses LCLType, LCLIntf, SysUtils, Types, Classes, Graphics, VpConst, VPBase, VpData, VpBasePainter, VpContactGrid; type TVpContactGridPainter = class(TVpBasePainter) private FContactGrid: TVpContactGrid; FLabelWidth: Integer; FScaledTextMargin: Integer; FAnchorMargin: Integer; FTextColWidth: Integer; // local variables of the original TVpContactGrid method // PhoneLblWidth: Integer; StartContact: Integer; RealColumnWidth: Integer; RealColor: TColor; SizingBarColor: TColor; BevelDarkShadow: TColor; BevelShadow: TColor; BevelHighlight: TColor; BevelFace: TColor; RealBarColor: TColor; RealContactHeadAttrColor: TColor; function CalcHeaderRect(ABitmap: TBitmap): TRect; function CalcInitialAnchor(ABitmap: TBitmap): TPoint; function CalcLabelWidth(ABitmap: TBitmap): Integer; procedure CalcNextColumnAnchor(ABitmap: TBitmap; const AWholeRect: TRect; var Anchor: TPoint); function GetTextColWidth(ABitmap: TBitmap): Integer; function NewColumnNeeded(AWholeRect: TRect; Anchor: TPoint): Boolean; function NewPageNeeded(Anchor: TPoint): Boolean; protected procedure Clear; procedure DrawBorders; procedure DrawContactHeader(ABitmap: TBitmap; AContact: TVpContact; const ARect: TRect); procedure DrawContactRow(ABitmap: TBitmap; AText, ALabel: String; var AWholeRect, ATextRect: TRect); function DrawContactRows(ABitmap: TBitmap; AContact: TVpContact; var Anchor: TPoint; var AWholeRect: TRect; var ACol, ARecsInCol: Integer; var AContactRec: TVpContactRec): Boolean; procedure DrawContacts; procedure DrawVerticalBars; procedure FixFontHeights; procedure InitColors; procedure PaintContactBitmap(ABitmap: TBitmap; AContact: TVpContact; Anchor: TPoint; AWholeRect: TRect); procedure SetMeasurements; override; public constructor Create(AContactGrid: TVpContactGrid; ARenderCanvas: TCanvas); procedure RenderToCanvas(ARenderIn: TRect; AAngle: TVpRotationAngle; AScale: Extended; ARenderDate: TDateTime; AStartLine, AStopLine: Integer; AUseGran: TVpGranularity; ADisplayOnly: Boolean); override; end; implementation uses VpCanvasUtils, VpMisc, VpSR; type TVpContactGridOpener = class(TVpContactGrid); constructor TVpContactGridPainter.Create(AContactGrid: TVpContactGrid; ARenderCanvas: TCanvas); begin inherited Create(ARenderCanvas); FContactGrid := AContactGrid; end; { Calculates the bounding rectangle for the contact header. } function TVpContactGridPainter.CalcHeaderRect(ABitmap: TBitmap): TRect; var textHeight: Integer; margin, half_margin: Integer; begin ABitmap.Canvas.Font.Assign(FContactGrid.ContactHeadAttributes.Font); ABitmap.Canvas.Font.PixelsPerInch := RenderCanvas.Font.PixelsPerInch; {$IF VP_LCL_SCALING = 0} ABitmap.Canvas.Font.Size := ScaleY(ABitmap.Canvas.Font.Size, DesignTimeDPI); {$ENDIF} textHeight := ABitmap.Canvas.TextHeight(VpProductName); margin := FScaledTextMargin; half_margin := FScaledTextMargin div 2; case Angle of ra0: Result := Rect(0, 0, ABitmap.Width, textHeight + half_margin); ra90: Result := Rect(ABitmap.Width - textHeight - half_margin, 0, ABitmap.Width, ABitmap.Height); ra180: Result := Rect(margin, ABitmap.Height - textheight - margin, ABitmap.Width, ABitmap.Height - half_margin); ra270: Result := Rect(0, margin, 3 * half_margin + textHeight, ABitmap.Height); end; end; function TVpContactGridPainter.CalcInitialAnchor(ABitmap: TBitmap): TPoint; begin case Angle of ra0, ra90: Result := Point(FAnchorMargin, FAnchorMargin); ra180: Result := Point(WidthOf(RenderIn) - ABitmap.Width - FAnchorMargin, ABitmap.Height - FAnchorMargin); ra270: Result := Point(FAnchorMargin, HeightOf(RenderIn) - ABitmap.Height - FAnchorMargin); end; end; { Calculates the width of the longest label in each row of the contact display. } function TVpContactGridPainter.CalcLabelWidth(ABitmap: TBitmap): Integer; var s: String; i, w: Integer; begin ABitmap.Canvas.Font.Assign(FContactGrid.Font); ABitmap.Canvas.Font.PixelsPerInch := RenderCanvas.Font.PixelsPerInch; {$IF VP_LCL_SCALING = 0} ABitmap.Canvas.Font.Size := ScaleY(ABitmap.Canvas.Font.Size, DesignTimeDPI); {$ENDIF} Result := ABitmap.Canvas.TextWidth(RSEmail); for i := 0 to 7 do begin s := PhoneLabel(TVpPhoneType(i)) + ': '; w := ABitmap.Canvas.TextWidth(s); if w > Result then Result := w; end; end; { Calculates the anchor for the next column. AWholeRect is the rectangle of the last contact drawn in the previous column. } procedure TVpContactGridPainter.CalcNextColumnAnchor(ABitmap: TBitmap; const AWholeRect: TRect; var Anchor: TPoint); var colDist: Integer; begin colDist := FContactGrid.BarWidth + 1 + FScaledTextMargin * 3; // wp: why? case Angle of ra0: Anchor := Point(Anchor.x + AWholeRect.Right + colDist, FAnchorMargin); ra90: Anchor := Point(FAnchorMargin, Anchor.y + AWholeRect.Bottom + colDist); ra180: Anchor := Point(Anchor.x - (AWholeRect.Right + colDist), ABitmap.Height - FAnchorMargin); ra270: Anchor := Point(FAnchorMargin, Anchor.y - (AWholeRect.Bottom + colDist)); end; end; procedure TVpContactGridPainter.Clear; var I: Integer; begin // Clear client area RenderCanvas.Brush.Color := RealColor; RenderCanvas.FillRect(RenderIn); // Clear the vertical bar array for I := 0 to pred(MaxColumns) do begin with TVpContactGridOpener(FContactGrid) do begin if cgBarArray[I].Index = -1 then Break; cgBarArray[I].Rec := Rect(-1, -1, -1, -1); cgBarArray[I].Index := -1; end; end; // Initialize the contact array at runtime if not (csDesigning in FContactGrid.ComponentState) and (FContactGrid.DataStore <> nil) and (FContactGrid.DataStore.Resource <> nil) then begin with TVpContactGridOpener(FContactGrid) do begin SetLength(cgContactArray, DataStore.Resource.Contacts.Count); for I := 0 to pred(Length(cgContactArray)) do with cgContactArray[I] do begin Index := -1; Contact := nil; WholeRect := Rect(-1, -1, -1, -1); HeaderRect := Rect(-1, -1, -1, -1); AddressRect := Rect(-1, -1, -1, -1); CSZRect := Rect(-1, -1, -1, -1); Phone1Rect := Rect(-1, -1, -1, -1); Phone2Rect := Rect(-1, -1, -1, -1); Phone3Rect := Rect(-1, -1, -1, -1); Phone4Rect := Rect(-1, -1, -1, -1); Phone5Rect := Rect(-1, -1, -1, -1); end; end; end; end; procedure TVpContactGridPainter.DrawBorders; var R: TRect; begin R := TPSRotateRectangle(Angle, RenderIn, Rect(RealLeft, RealTop, RealRight - 1, RealBottom - 1)); case FContactGrid.DrawingStyle of dsNoBorder: ; dsFlat: DrawBevelRect(RenderCanvas, R, BevelShadow, BevelShadow); ds3D: DrawBevelRect(RenderCanvas, R, BevelShadow, BevelHighlight); end; end; procedure TVpContactGridPainter.DrawContactHeader(ABitmap: TBitmap; AContact: TVpContact; const ARect: TRect); var textXOffset, textYOffset: Integer; maxTextWidth: Integer; s: String; begin // Paint the header cell's background ABitmap.Canvas.Brush.Color := RealContactHeadAttrColor; ABitmap.Canvas.Brush.Style := bsSolid; ABitmap.Canvas.FillRect(ARect); // Paint the header cell's border if FContactGrid.ContactHeadAttributes.Bordered and (FContactGrid.DrawingStyle <> dsNoBorder) then begin ABitmap.Canvas.Pen.Color := BevelDarkShadow; ABitmap.Canvas.Pen.Style := psSolid; ABitmap.Canvas.Rectangle(ARect); end; // Paint the header cell's text case Angle of ra0: begin textXOffset := 0; textYOffset := 0; maxTextWidth := WidthOf(ARect); end; ra90: begin textXOffset := WidthOf(ARect) - FScaledTextMargin div 2; textYOffset := FScaledTextMargin div 3; maxTextWidth := HeightOf(ARect); end; ra180: begin textXOffset := WidthOf(ARect) - FScaledTextMargin; textYOffset := HeightOf(ARect) - FScaledTextMargin div 3; maxTextWidth := WidthOf(ARect); end; ra270: begin textXOffset := FScaledTextMargin div 2; textYOffset := HeightOf(ARect) - FScaledTextMargin div 3; maxTextWidth := HeightOf(ARect); end; end; // Set the header font ABitmap.Canvas.Font.Assign(FContactGrid.ContactHeadAttributes.Font); ABitmap.Canvas.Font.PixelsPerInch := RenderCanvas.Font.PixelsPerInch; {$IF VP_LCL_SCALING = 0} ABitmap.Canvas.Font.Size := ScaleY(ABitmap.Canvas.Font.Size, DesignTimeDPI); {$ENDIF} if FContactGrid.Focused and (AContact = FContactGrid.ActiveContact) then ABitmap.Canvas.Font.Style := [fsBold]; // Assemble the header string... s := AssembleName(AContact); s := GetDisplayString(ABitmap.Canvas, s, 2, maxTextWidth - FScaledTextMargin); // ... and draw it using the header font. TPSTextOutAtPoint( ABitmap.Canvas, Angle, Rect(0, 0, ABitmap.Width, ABitmap.Height), ARect.Left + FScaledTextMargin div 2 + TextXOffset, ARect.Top + FScaledTextMargin div 3 + TextYOffset, s ); end; { Draw a row of the contact data: AText and its label ALabel. The row is at the bottom of the previously drawn rows. ATextRect returns the rectangle occupied by this row. AWholeRect is the rectangle occupied by the entire contact data including this row. } procedure TVpContactGridPainter.DrawContactRow(ABitmap: TBitmap; AText, ALabel: String; var AWholeRect, ATextRect: TRect); var txtheight: Integer; txtColWidth: Integer; txtPt: TPoint; begin if AText = '' then begin ATextRect := Rect(0, 0, 0, 0); exit; end; txtHeight := ABitmap.Canvas.TextHeight(VpProductName) + FScaledTextMargin div 2; case Angle of ra0: begin ATextRect := Rect(FScaledTextMargin, AWholeRect.Bottom, AWholeRect.Right, AWholeRect.Bottom + txtHeight); AWholeRect.Bottom := ATextRect.Bottom; txtColWidth := ABitmap.Width; txtPt := ATextRect.TopLeft; end; ra90: begin ATextRect := Rect(AWholeRect.Left - txtHeight - 1, FScaledTextMargin, AWholeRect.Left - 1, AWholeRect.Bottom); AWholeRect.Left := ATextRect.Left; txtColWidth := ABitmap.Height; txtPt := Point(ATextRect.Right, ATextRect.Top); end; ra180: begin ATextRect := Rect(AWholeRect.Left, AWholeRect.Top - txtHeight, AWholeRect.Right - FScaledTextMargin, AWholeRect.Top); AWholeRect.Top := ATextRect.Top; txtColWidth := ABitmap.Width; txtPt := Point(ATextRect.Right, ATextRect.Bottom); end; ra270: begin ATextRect := Rect(AWholeRect.Right + 1, AWholeRect.Top, AWholeRect.Right + txtHeight + 1, AWholeRect.Bottom - FScaledTextMargin); txtPt := Point(ATextRect.Left, ATextRect.Bottom); AWholeRect.Right := ATextRect.Right; txtColWidth := ABitmap.Height; end; end; // case Angle... AText := GetDisplayString(ABitmap.Canvas, AText, 2, txtColWidth - FScaledTextMargin * 2); if ALabel <> '' then begin ABitmap.Canvas.TextOut(txtPt.X, txtPt.Y, ALabel); case Angle of ra0: begin inc(ATextRect.Left, FLabelWidth); txtPt.X := ATextRect.Left; end; ra90: begin inc(ATextRect.Top, FLabelWidth); txtPt.Y := ATextRect.Top; end; ra180: begin dec(ATextRect.Right, FLabelWidth); txtPt.X := ATextRect.Right; end; ra270: begin dec(ATextRect.Bottom, FLabelWidth); txtPt.Y := ATextRect.Bottom; end; end; end; ABitmap.Canvas.TextOut(txtPt.X, txtPt.Y, AText); end; { Draws selected data for the specified contact on the auxiliary bitmap. Anchor ...... position at which the data will appear on the rendering canvas. AWholeRect .. rectangle covered by the data rows (header included). It is relative to the auxiliary bitmap and expanded at exit by the height of the row. ACol ........ Column counter, advances when a new column is started ARecsInCol .. Counter for the records in the first column AContactRec . Record storing mostly the rectangles of the data elements to facilitate handling of clicks in the ContactGrid. Result ...... normally true. But when the size of the rendering canvas is exceeded becomes false to indicate an error condition. } function TVpContactGridPainter.DrawContactRows(ABitmap: TBitmap; AContact: TVpContact; var Anchor: TPoint; var AWholeRect: TRect; var ACol, ARecsInCol: Integer; var AContactRec: TVpContactRec): Boolean; var s: String; spacing: Integer; newCol: Boolean; begin Result := true; // Set font and colors for the contact data ABitmap.Canvas.Font.Assign(FContactGrid.Font); ABitmap.Canvas.Font.PixelsPerInch := RenderCanvas.Font.PixelsPerInch; {$IF VP_LCL_SCALING = 0} ABitmap.Canvas.Font.Size := ScaleY(ABitmap.Canvas.Font.Size, DesignTimeDPI); {$ENDIF} ABitmap.Canvas.Brush.Color := RealColor; case Angle of ra0 : ABitmap.Canvas.Font.Orientation := 0; ra90 : ABitmap.Canvas.Font.Orientation := 2700; ra180 : ABitmap.Canvas.Font.Orientation := 1800; ra270 : ABitmap.Canvas.Font.Orientation := 900; end; // Draw company DrawContactRow(ABitmap, AContact.Company, '', AWholeRect, AContactRec.CompanyRect); // Draw address DrawContactRow(ABitmap, AContact.Address1, '', AWholeRect, AContactRec.AddressRect); // Draw city, state, zip s := AssembleCSZ(AContact, 1, FContactGrid.GetCityStateZipFormat); DrawContactRow(ABitmap, s, '', AWholeRect, AContactRec.CSZRect); // Draw phone1 s := PhoneLabel(TVpPhoneType(AContact.PhoneType1)) + ': '; DrawContactRow(ABitmap, AContact.Phone1, s, AWholeRect, AContactRec.Phone1Rect); // Draw phone2 s := PhoneLabel(TVpPhoneType(AContact.PhoneType2)) + ': '; DrawContactRow(ABitmap, AContact.Phone2, s, AWholeRect, AContactRec.Phone2Rect); // Draw phone3 s := PhoneLabel(TVpPhoneType(AContact.PhoneType3)) + ': '; DrawContactRow(ABitmap, AContact.Phone3, s, AWholeRect, AContactRec.Phone3Rect); // Draw phone4 s := PhoneLabel(TVpPhoneType(AContact.PhoneType4)) + ': '; DrawContactRow(ABitmap, AContact.Phone4, s, AWholeRect, AContactRec.Phone4Rect); // Draw phone5 s := PhoneLabel(TVpPhoneType(AContact.PhoneType5)) + ': '; DrawContactRow(ABitmap, AContact.Phone5, s, AWholeRect, AContactRec.Phone5Rect); // Draw EMail s := FContactGrid.DisplayEMailValue[AContact]; DrawContactRow(ABitmap, s, RSEmail + ': ', AWholeRect, AContactRec.EMailRect); // If this record is too big to fit in the remaining area of this column, // then slide over to the top of the next column } newCol := (ARecsInCol > 0) and NewColumnNeeded(AWholeRect, Anchor); if newCol then begin CalcNextColumnAnchor(ABitmap, AWholeRect, Anchor); if NewPageNeeded(Anchor) then begin // Return value FALSE signals that a new page must be started in the // next rendering iteration. Result := false; exit; end; // New columns Increment the column counter. Store the counter of records // in the 1st column and reset it for the new column. if ACol = 1 then FContactGrid.Col1RecCount := ARecsInCol; Inc(ACol); ARecsInCol := 0; end else // Still the same column: Increment the counter of records per column // (Is evaluated only when we are drawing the first column). inc(ARecsInCol); // Add some spacing between records spacing := FScaledTextMargin * 2; case Angle of ra0 : AWholeRect.Bottom := AWholeRect.Bottom + spacing; ra90 : AWholeRect.Left := AWholeRect.Left - spacing; ra180 : AWholeRect.Top := AWholeRect.Top - spacing; ra270 : AWholeRect.Right := AWholeRect.Right + spacing; end; // Move data rectangles to the position at which they will appear on // the render canvas. OffsetRect(AContactRec.AddressRect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.CSZRect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.CompanyRect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.EMailRect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.Phone1Rect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.Phone2Rect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.Phone3Rect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.Phone4Rect, Anchor.X, Anchor.Y); OffsetRect(AContactRec.Phone5Rect, Anchor.X, Anchor.Y); end; { Draws all contacts. To simplify the layout each contact is drawn first into a temporary bitmap which is later copied to the rendering canvas. } procedure TVpContactGridPainter.DrawContacts; var Anchor: TPoint; I, J: Integer; TmpBmp: TBitmap; contact: TVpContact; Col, RecsInCol: Integer; WholeRect: TRect; oldCol1RecCount: Integer; CR: TVpContactRec; HeadRect: TRect = (Left:0; Top:0; Right:0; Bottom:0); contactCount: Integer; newPage: Boolean; px4: Integer; // Scaled 4 pixels begin // If the component is sufficiently small then no sense in painting it if (FContactGrid.Height < 20) then Exit; // Don't paint contacts at designtime or if the data connection is invalid if (csDesigning in FContactGrid.ComponentState) or (FContactGrid.DataStore = nil) or (FContactGrid.DataStore.Resource = nil) then Exit; // Some initializations contactCount := FContactGrid.DataStore.Resource.Contacts.Count; oldCol1RecCount := FContactGrid.Col1RecCount; FContactGrid.VisibleContacts := 0; FContactGrid.Col1RecCount := 0; px4 := Round(4 * Scale); CR := Default(TVpContactRec); // Create a temporary bitmap for painting the contact items TmpBmp := TBitmap.Create; try if (Angle = ra0) or (Angle = ra180) then begin TmpBmp.Width := RealColumnWidth - FScaledTextMargin * 4 + px4; TmpBmp.Height := RealHeight - FScaledTextMargin * 2; end else begin TmpBmp.Height := RealColumnWidth - FScaledTextMargin * 4 + px4; TmpBmp.Width := RealHeight - FScaledTextMargin * 2; end; // Get the net width of each contact column (without spacers) FTextColWidth := GetTextColWidth(TmpBmp); // Calculate max label width FLabelWidth := CalcLabelWidth(TmpBmp); // Calculate the header rectangle. It is the same for all contacts. HeadRect := CalcHeaderRect(TmpBmp); // Set the anchor starting point for the very first (top/left) contact Anchor := CalcInitialAnchor(TmpBmp); // Sort the records FContactGrid.DataStore.Resource.Contacts.Sort; // Iterate over all contacts Col := 1; RecsInCol := 0; for I := StartContact to pred(contactCount) do begin J := I; // Do not use the loop index outside a for loop! contact := FContactGrid.DataStore.Resource.Contacts.GetContact(I); if contact = nil then Continue; // Clear bmp canvas TmpBmp.Canvas.Brush.Color := RealColor; TmpBmp.Canvas.FillRect(Rect(0, 0, TmpBmp.Width, TmpBmp.Height)); // Draw the contact header DrawContactHeader(TmpBmp, contact, HeadRect); // Draw the contact data WholeRect := HeadRect; if DrawContactRows(TmpBmp, contact, Anchor, WholeRect, Col, RecsInCol, CR) then begin newPage := false; CR.Index := I; CR.Contact := contact; CR.ColIndex := Col - 1; // Move rectangles in ContactGridRec to final position on rendering canvas. // Note: The other rects already have been moved in DrawContactRows(). CR.WholeRect := MoveRect(WholeRect, Anchor); CR.HeaderRect := MoveRect(HeadRect, Anchor); TVpContactGridOpener(FContactGrid).cgContactArray[I] := CR; // Draw the contact bitmap on the rendering canvas PaintContactBitmap(TmpBmp, contact, Anchor, WholeRect); // Slide anchor down for the next record case Angle of ra0 : Anchor.Y := Anchor.Y + WholeRect.Bottom; ra90 : Anchor.X := Anchor.X + WidthOf(WholeRect); ra180 : Anchor.Y := Anchor.Y - HeightOf(WholeRect); ra270 : Anchor.X := Anchor.X + WholeRect.Right; end; end else begin // New page required. newPage := true; break; end; end; // for I := StartCont to ... finally TmpBmp.Free; end; if newPage then begin FContactGrid.ContactsAfter := contactCount - J; TVpContactGridOpener(FContactGrid).FLastPrintLine := J; end else begin // All contacts printed FContactGrid.ContactsAfter := 0; TVpContactGridOpener(FContactGrid).FLastPrintLine := -2; // -2 = no more data available end; FContactGrid.VisibleContacts := contactCount - StartContact - FContactGrid.ContactsAfter; FContactGrid.ColCount := Col; if (oldCol1RecCount > 0) and (FContactGrid.Col1RecCount = 0) then FContactGrid.Col1RecCount := oldCol1RecCount; end; procedure TVpContactGridPainter.DrawVerticalBars; var BarPos, BarCount, I: Integer; scaledExtraBarWidth: Integer; px2: Integer; // scaled 2 pixels begin // If the component is sufficiently small then no sense in painting it. if (FContactGrid.Height < 20) then exit; scaledExtraBarWidth := round(ExtraBarWidth * Scale); //scaledPenWidth := round(1 * Scale); px2 := round(2 * Scale); // Draw vertical bars. RenderCanvas.Pen.Color := RealBarColor; RenderCanvas.Pen.Style := psSolid; RenderCanvas.Pen.Width := 1; //scaledPenWidth; BarPos := RealLeft + px2 + RealColumnWidth + scaledExtraBarWidth; BarCount := 0; while (BarPos < RealRight) and (BarCount < Pred(MaxColumns)) do begin TVpContactGridOpener(FContactGrid).cgBarArray[BarCount].Rec := Rect( BarPos - scaledExtraBarWidth, RealTop, BarPos + scaledExtraBarWidth + FContactGrid.BarWidth, RealBottom ); TVpContactGridOpener(FContactGrid).cgBarArray[BarCount].Index := BarCount; for I := 1 to FContactGrid.BarWidth do begin TPSMoveTo(RenderCanvas, Angle, RenderIn, BarPos, RealTop + px2 + FScaledTextMargin * 2); TPSLineTo(RenderCanvas, Angle, RenderIn, BarPos, RealBottom - FScaledTextMargin * 2); Inc(BarPos); end; Inc(BarPos, RealColumnWidth); Inc(BarCount); end; // If the columns are being resized, then draw the temporary resizing bars. if TVpContactGridOpener(FContactGrid).cgGridState = gsColSizing then begin // Clear sizing bar array for I := 0 to pred(MaxColumns) do with TVpContactGridOpener(FContactGrid) do begin if cgResizeBarArray[I].Index = -1 then Break; cgResizeBarArray[I].Rec := Rect(-1, -1, -1, -1); cgResizeBarArray[I].Index := -1; end; // Draw sizing bars RenderCanvas.Pen.Color := SizingBarColor; RenderCanvas.Pen.Style := psDash; BarPos := RealLeft + px2 + TVpContactGridOpener(FContactGrid).cgNewColWidth + ExtraBarWidth; BarCount := 0; while (BarPos < FContactGrid.Width) and (BarCount < pred(MaxColumns)) do begin TVpContactGridOpener(FContactGrid).cgResizeBarArray[BarCount].Index := BarCount; TVpContactGridOpener(FContactGrid).cgResizeBarArray[BarCount].Rec := Rect( BarPos - ExtraBarWidth, RealTop, BarPos - ExtraBarWidth + FContactGrid.BarWidth, RealBottom ); for I := 1 to FContactGrid.BarWidth do begin TPSMoveTo( RenderCanvas, Angle, RenderIn, RealLeft + BarPos, RealTop + px2 + FScaledTextMargin * 2 ); TPSLineTo( RenderCanvas, Angle, RenderIn, RealLeft + BarPos, RealBottom - FScaledTextMargin * 2 ); Inc(BarPos); end; Inc(BarPos, TVpContactGridOpener(FContactGrid).cgNewColWidth); Inc(BarCount); end; RenderCanvas.Pen.Style := psSolid; end; end; procedure TVpContactGridPainter.FixFontHeights; begin with FContactGrid do begin {$IF VP_LCL_SCALING = 0} ContactHeadAttributes.Font.Height := GetRealFontHeight(ContactHeadAttributes.Font); Font.Height := GetRealFontHeight(Font); {$ELSE} ContactHeadAttributes.Font.Height := FixFontHeight(ContactHeadAttributes.Font); Font.Height := FixFontHeight(Font); {$ENDIF} end; end; function TVpContactGridPainter.GetTextColWidth(ABitmap: TBitmap): Integer; begin case Angle of ra0, ra180: Result := ABitmap.Width; ra90, ra270: Result := ABitmap.Height; end; end; procedure TVpContactGridPainter.InitColors; begin if DisplayOnly then begin RealColor := clWhite; SizingBarColor := clBlack; BevelDarkShadow := clBlack; BevelShadow := clBlack; BevelHighlight := clBlack; BevelFace := clBlack; RealBarColor := clBlack; RealContactHeadAttrColor := clSilver; end else begin RealColor := FContactGrid.Color; SizingBarColor := clBlack; BevelDarkShadow := cl3dDkShadow; BevelShadow := clBtnShadow; BevelHighlight := clBtnHighlight; BevelFace := clBtnFace; RealBarColor := FContactGrid.BarColor; RealContactHeadAttrColor := FContactGrid.ContactHeadAttributes.Color; end; end; { Determines whether the contact rectange AWholeRect execeeds the page height and a new column should be started. } function TVpContactGridPainter.NewColumnNeeded(AWholeRect: TRect; Anchor: TPoint): Boolean; var bottomMargin: Integer; begin bottomMargin := FScaledTextMargin * 2; case Angle of ra0: Result := (RenderIn.Top + Anchor.y + AWholeRect.Bottom >= RenderIn.Bottom - bottomMargin); ra90: Result := (Anchor.x + RenderIn.Left + WidthOf(AWholeRect) > RenderIn.Right - bottomMargin); ra180: Result := (Anchor.y + RenderIn.Top - HeightOf(AWholeRect) <= RenderIn.Top + bottomMargin); ra270: Result := (Anchor.x + RenderIn.Left + WidthOf(AWholeRect) >= RenderIn.Right - bottomMargin); end; end; { Determines whether the new anchor is outside the current page. } function TVpContactGridPainter.NewPageNeeded(Anchor: TPoint): Boolean; begin case Angle of ra0: Result := (Anchor.X + FTextColWidth >= RenderIn.Right); ra90: Result := (Anchor.y + FTextColWidth >= RenderIn.Bottom); ra180: Result := (Anchor.x + FTextColWidth < RenderIn.Left); ra270: Result := (Anchor.y + FTextColWidth <= RenderIn.Top); end; end; { Copy the drawn contact record from the bitmap to the rendering canvas. } procedure TVpContactGridPainter.PaintContactBitmap(ABitmap: TBitmap; AContact: TVpContact; Anchor: TPoint; AWholeRect: TRect); const FOCUS_RECT_DISTANCE = 3; var R: TRect; dist: Integer; begin // Calculate the destination rectangle on the rendering canvas. case Angle of ra0: R := Rect(Anchor.X + AWholeRect.Left + RenderIn.Left, Anchor.Y + AWholeRect.Top + RenderIn.Top, Anchor.X + ABitmap.Width + RenderIn.Left, Anchor.Y + AWholeRect.Bottom + RenderIn.Top ); ra90: R := Rect(AWholeRect.Left + RenderIn.Left - Anchor.X, Anchor.Y + AWholeRect.Top + RenderIn.Top, AWholeRect.Right + RenderIn.Left - Anchor.X, Anchor.Y + AWholeRect.Bottom + RenderIn.Top ); ra180: R := Rect(Anchor.X + AWholeRect.Left + RenderIn.Left, Anchor.Y - HeightOf(AWholeRect) + RenderIn.Top, Anchor.X + ABitmap.Width + RenderIn.Left, Anchor.Y + RenderIn.Top ); ra270: R := Rect(Anchor.X + RenderIn.Left, Anchor.Y + RenderIn.Top, Anchor.X + RenderIn.Left + WidthOf(AWholeRect), Anchor.Y + RenderIn.Top + HeightOf(AWholeRect) ); end; // Copy the auxiliary contact bitmap from AWholeRect to R into the // rendering canvas. RenderCanvas.CopyRect(R, ABitmap.Canvas, AWholeRect); // Draw focus rect around selected record (not in printing mode) if not DisplayOnly and FContactGrid.Focused and (AContact = FContactGrid.ActiveContact) then begin {$IF VP_LCL_SCALING > 0} dist := FContactGrid.Scale96ToFont(FOCUS_RECT_DISTANCE); {$ELSE} dist := ScaleY(FOCUS_RECT_DISTANCE, DesigntimeDPI); {$IFEND} InflateRect(R, dist, 0); OffsetRect(R, 0, -dist); RenderCanvas.DrawFocusRect(R); end; end; procedure TVpContactGridPainter.RenderToCanvas(ARenderIn: TRect; AAngle: TVpRotationAngle; AScale: Extended; ARenderDate: TDateTime; AStartLine, AStopLine: Integer; AUseGran: TVpGranularity; ADisplayOnly: Boolean); begin inherited; InitColors; SavePenBrush; InitPenBrush; if ADisplayOnly then FixFontHeights; Rgn := CreateRectRgn(RenderIn.Left, RenderIn.Top, RenderIn.Right, RenderIn.Bottom); try SelectClipRgn(RenderCanvas.Handle, Rgn); if StartLine = -1 then StartContact := FContactGrid.ContactsBefore else StartContact := StartLine; SetMeasurements; // Clear the control Clear; // Draw the contacts if StartLine <> -2 then // Do not change. Paint calls it with StartLine = -1... DrawContacts; // Draw the vertical bars DrawVerticalBars; // Draw the borders DrawBorders; FContactGrid.UpdateScrollbar; finally SelectClipRgn(RenderCanvas.Handle, 0); DeleteObject(Rgn); end; // Restore canvas settings RestorePenBrush; end; procedure TVpContactGridPainter.SetMeasurements; const MARGIN = 2; var numCols: Integer; begin inherited; FScaledTextMargin := round(FContactGrid.TextMargin * Scale); FAnchorMargin := round(Scale * MARGIN) {%H-}+ FScaledTextMargin * 2; numCols := FContactGrid.PrintNumColumns; if DisplayOnly and (numCols > 0) then RealColumnWidth := (RealWidth - round((2 + ExtraBarWidth) * Scale * (numCols - 1))) div numCols else RealColumnWidth := FContactGrid.ColumnWidth; end; end.