{----------------------------------------------------------------------------- The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/MPL-1.1.html Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is: JvBevel.PAS, released on 2001-02-28. The Initial Developer of the Original Code is Sébastien Buysse [sbuysse att buypin dott com] Portions created by Sébastien Buysse are Copyright (C) 2001 Sébastien Buysse. All Rights Reserved. Contributor(s): Michael Beck [mbeck att bigfoot dott com]. You may retrieve the latest version of this file at the Project JEDI's JVCL home page, located at http://jvcl.sourceforge.net Known Issues: -----------------------------------------------------------------------------} // $Id: JvBevel.pas 11059 2006-11-29 17:12:58Z marquardt $ unit JvBevel; {$I jvcl.inc} interface uses {$IFDEF UNITVERSIONING} JclUnitVersioning, {$ENDIF UNITVERSIONING} Windows, Messages, Classes, Controls, ExtCtrls, Graphics, JvThemes, JvExExtCtrls; type TJvBevelLines = class; TJvBevelStyle = (bsLowered, bsRaised, bsCustomStyle); TJvBevelShape = (bsBox, bsFrame, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine, bsSpacer, bsCustomShape); TJvBevel = class(TJvExBevel) private FStyle: TJvBevelStyle; FShape: TJvBevelShape; FOuter: TBevelCut; FInner: TBevelCut; FPenWidth: Integer; FPenStyle: TPenStyle; FBold: Boolean; FEdges: TBevelEdges; FVerticalLines: TJvBevelLines; FHorizontalLines: TJvBevelLines; FHorLines: TJvBevelLines; procedure ReadBevelInner(Reader: TReader); procedure ReadBevelOuter(Reader: TReader); procedure ReadBevelSides(Reader: TReader); procedure ReadBevelBold(Reader: TReader); procedure ReadBevelPenStyle(Reader: TReader); procedure ReadBevelPenWidth(Reader: TReader); procedure IgnoreValue(Reader: TReader); procedure LinesChange(Sender: TObject); procedure SetStyle(const Value: TJvBevelStyle); procedure SetShape(const Value: TJvBevelShape); procedure SetInner(const Value: TBevelCut); procedure SetOuter(const Value: TBevelCut); procedure SetPenWidth(const Value: Integer); procedure SetPenStyle(const Value: TPenStyle); procedure SetBold(const Value: Boolean); procedure SetEdges(const Value: TBevelEdges); procedure SetHorizontalLines(const Value: TJvBevelLines); procedure SetVerticalLines(const Value: TJvBevelLines); protected procedure DrawBevel(R: TRect; Cut: TBevelCut; EffectiveEdges: TBevelEdges); procedure DrawBold(R: TRect; Cut: TBevelCut; EffectiveEdges: TBevelEdges); procedure DrawLines(InnerRect: TRect; Lines: TJvBevelLines; Vertical: Boolean); procedure Paint; override; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure DefineProperties(Filer: TFiler); override; published property Bold: Boolean read FBold write SetBold default False; property Edges: TBevelEdges read FEdges write SetEdges default [beLeft, beTop, beRight, beBottom]; property Inner: TBevelCut read FInner write SetInner default bvNone; property Outer: TBevelCut read FOuter write SetOuter default bvLowered; property PenStyle: TPenStyle read FPenStyle write SetPenStyle default psSolid; property PenWidth: Integer read FPenWidth write SetPenWidth default 1; property HintColor; property HorizontalLines: TJvBevelLines read FHorizontalLines write SetHorizontalLines; property Shape: TJvBevelShape read FShape write SetShape default bsBox; property Style: TJvBevelStyle read FStyle write SetStyle default bsLowered; property VerticalLines: TJvBevelLines read FVerticalLines write SetVerticalLines; property OnMouseEnter; property OnMouseLeave; property OnParentColorChange; property OnClick; property OnDblClick; property OnDragDrop; property OnDragOver; {$IFDEF VCL} property OnEndDock; property OnStartDock; {$ENDIF VCL} property OnEndDrag; property OnMouseDown; property OnMouseMove; property OnMouseUp; property OnResize; property OnStartDrag; end; TJvBevelLines = class(TPersistent) private FCount: Cardinal; FStyle: TBevelCut; FBold: Boolean; FThickness: Byte; FIgnoreBorder: Boolean; FOnChange: TNotifyEvent; procedure IgnoreValue(Reader: TReader); procedure SetBold(const Value: Boolean); procedure SetCount(const Value: Cardinal); procedure SetIgnoreBorder(const Value: Boolean); procedure SetStyle(const Value: TBevelCut); procedure SetThickness(const Value: Byte); protected procedure DoChange; virtual; public constructor Create; procedure DefineProperties(Filer: TFiler); override; procedure Assign(Source: TPersistent); override; property OnChange: TNotifyEvent read FOnChange write FOnChange; published property Count: Cardinal read FCount write SetCount default 0; property Style: TBevelCut read FStyle write SetStyle default bvLowered; property Bold: Boolean read FBold write SetBold default False; property Thickness: Byte read FThickness write SetThickness default 1; property IgnoreBorder: Boolean read FIgnoreBorder write SetIgnoreBorder default False; end; {$IFDEF UNITVERSIONING} const UnitVersioning: TUnitVersionInfo = ( RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvBevel.pas $'; Revision: '$Revision: 11059 $'; Date: '$Date: 2006-11-29 18:12:58 +0100 (mer., 29 nov. 2006) $'; LogPath: 'JVCL\run' ); {$ENDIF UNITVERSIONING} implementation uses SysUtils, TypInfo, {$IFDEF HAS_UNIT_RTLCONSTS} RTLConsts, {$ELSE} Consts, {$ENDIF HAS_UNIT_RTLCONSTS} JvResources; type TReaderAccess = class(TReader); //=== { TJvBevel } =========================================================== constructor TJvBevel.Create(AOwner: TComponent); begin inherited Create(AOwner); {$IFDEF VCL} IncludeThemeStyle(Self, [csParentBackground]); {$ENDIF VCL} FHorizontalLines := TJvBevelLines.Create; FVerticalLines := TJvBevelLines.Create; FHorLines := TJvBevelLines.Create; FHorizontalLines.OnChange := LinesChange; FVerticalLines.OnChange := LinesChange; FEdges := [beLeft, beTop, beRight, beBottom]; FInner := bvNone; FOuter := bvLowered; FPenWidth := 1; FShape := bsBox; FStyle := bsLowered; end; procedure TJvBevel.DefineProperties(Filer: TFiler); begin // Required for silent migration from Globus' TJvgBevel Filer.DefineProperty('BevelInner', ReadBevelInner, nil, False); Filer.DefineProperty('BevelOuter', ReadBevelOuter, nil, False); Filer.DefineProperty('BevelSides', ReadBevelSides, nil, False); Filer.DefineProperty('BevelBold', ReadBevelBold, nil, False); Filer.DefineProperty('BevelPenStyle', ReadBevelPenStyle, nil, False); Filer.DefineProperty('BevelPenWidth', ReadBevelPenWidth, nil, False); Filer.DefineProperty('InteriorOffset', IgnoreValue, nil, False); inherited DefineProperties(Filer); end; destructor TJvBevel.Destroy; begin FHorizontalLines.Free; FVerticalLines.Free; FHorLines.Free; inherited Destroy; end; procedure TJvBevel.DrawBevel(R: TRect; Cut: TBevelCut; EffectiveEdges: TBevelEdges); var ColorTopLeft: TColor; ColorBottomRight: TColor; begin ColorTopLeft := clNone; ColorBottomRight := clNone; case Cut of bvLowered: begin ColorTopLeft := clBtnShadow; ColorBottomRight := clBtnHighlight; end; bvRaised: begin ColorTopLeft := clBtnHighlight; ColorBottomRight := clBtnShadow; end; end; if ColorTopLeft <> clNone then begin Canvas.Pen.Color := ColorTopLeft; if beLeft in EffectiveEdges then begin Canvas.MoveTo(R.Left, R.Bottom - 1); Canvas.LineTo(R.Left, R.Top - 1); end; if beTop in EffectiveEdges then begin Canvas.MoveTo(R.Left, R.Top); Canvas.LineTo(R.Right, R.Top); end; end; if ColorBottomRight <> clNone then begin Canvas.Pen.Color := ColorBottomRight; if beRight in EffectiveEdges then begin Canvas.MoveTo(R.Right, R.Top); Canvas.LineTo(R.Right, R.Bottom); end; if beBottom in EffectiveEdges then begin Canvas.MoveTo(R.Right, R.Bottom); Canvas.LineTo(R.Left - 1, R.Bottom); end; end; end; procedure TJvBevel.DrawBold(R: TRect; Cut: TBevelCut; EffectiveEdges: TBevelEdges); begin Canvas.Pen.Color := cl3DDkShadow; if Cut = bvLowered then begin if beLeft in EffectiveEdges then begin Canvas.MoveTo(R.Left - 1, R.Bottom - 1); Canvas.LineTo(R.Left - 1, R.Top - 1); end; if beTop in EffectiveEdges then begin Canvas.MoveTo(R.Left - 1, R.Top - 1); Canvas.LineTo(R.Right, R.Top - 1); end; end; if Cut = bvRaised then begin if beBottom in EffectiveEdges then begin Canvas.MoveTo(R.Left, R.Bottom + 1); Canvas.LineTo(R.Right + 1, R.Bottom + 1); end; if beRight in EffectiveEdges then begin Canvas.MoveTo(R.Right + 1, R.Bottom + 1); Canvas.LineTo(R.Right + 1, R.Top - 1); end; end; end; procedure TJvBevel.DrawLines(InnerRect: TRect; Lines: TJvBevelLines; Vertical: Boolean); var EffectiveRect: TRect; LineRect: TRect; I: Integer; LineEdges: TBevelEdges; begin if Lines.IgnoreBorder then EffectiveRect := Rect(0, 0, Width-1, Height-1) else EffectiveRect := Rect(InnerRect.Left+1, InnerRect.Top+1, InnerRect.Right-1, InnerRect.Bottom-1); if Lines.Style = bvSpace then LineEdges := [beLeft, beTop] else if Vertical then LineEdges := [beLeft, beRight] else LineEdges := [beTop, beBottom]; for I := 1 to Lines.Count do begin LineRect := EffectiveRect; if Vertical then begin LineRect.Left := Muldiv(I, Width, Lines.Count + 1); LineRect.Right := LineRect.Left + Lines.Thickness + Ord(Lines.Bold); end else begin LineRect.Top := Muldiv(I, Height, Lines.Count + 1); LineRect.Bottom := LineRect.Top + Lines.Thickness + Ord(Lines.Bold); end; DrawBevel(LineRect, Lines.Style, LineEdges); end; end; procedure TJvBevel.IgnoreValue(Reader: TReader); begin TReaderAccess(Reader).SkipValue; end; procedure TJvBevel.LinesChange(Sender: TObject); begin Invalidate; Style := bsCustomStyle; end; procedure TJvBevel.Paint; var EffectiveOuterRect: TRect; EffectiveInnerRect: TRect; begin if (Style = bsCustomStyle) or (Shape = bsCustomShape) then begin Canvas.Pen.Style := PenStyle; Canvas.Pen.Width := PenWidth; EffectiveOuterRect := Rect(0, 0, Width - 1, Height - 1); EffectiveInnerRect := Rect(1, 1, Width - 2, Height - 2); // Boldness adds a dark shadow line outside any border line that is // drawn in clBtnShadow. This effectively pushes the line inwards. // clBtnShadow is used at top left for bvLowered and at bottom right // for bvRaised. In these cases the place where the clBtnShadow line // is to be drawn has to be moved inward and a cl3DDkShadow drawn in // its place. if Bold then begin case Outer of bvLowered: begin Inc(EffectiveOuterRect.Left); Inc(EffectiveOuterRect.Top); Inc(EffectiveInnerRect.Left); Inc(EffectiveInnerRect.Top); end; bvRaised: begin Dec(EffectiveOuterRect.Right); Dec(EffectiveOuterRect.Bottom); Dec(EffectiveInnerRect.Right); Dec(EffectiveInnerRect.Bottom); end; end; case Inner of bvLowered: begin Inc(EffectiveInnerRect.Left); Inc(EffectiveInnerRect.Top); end; bvRaised: begin Dec(EffectiveInnerRect.Right); Dec(EffectiveInnerRect.Bottom); end; end; DrawBold(EffectiveOuterRect, Outer, Edges); DrawBold(EffectiveInnerRect, Inner, Edges); end; DrawBevel(EffectiveOuterRect, Outer, Edges); DrawBevel(EffectiveInnerRect, Inner, Edges); if Inner in [bvLowered, bvRaised] then begin DrawLines(EffectiveInnerRect, HorizontalLines, False); DrawLines(EffectiveInnerRect, VerticalLines, True); end else begin DrawLines(EffectiveOuterRect, HorizontalLines, False); DrawLines(EffectiveOuterRect, VerticalLines, True); end; end else begin inherited Paint; end; end; procedure TJvBevel.ReadBevelBold(Reader: TReader); begin Bold := Reader.ReadBoolean; end; procedure TJvBevel.ReadBevelInner(Reader: TReader); begin Inner := TBevelCut(GetEnumValue(TypeInfo(TBevelCut), Reader.ReadIdent)); end; procedure TJvBevel.ReadBevelOuter(Reader: TReader); begin Outer := TBevelCut(GetEnumValue(TypeInfo(TBevelCut), Reader.ReadIdent)); end; procedure TJvBevel.ReadBevelPenStyle(Reader: TReader); begin PenStyle := TPenStyle(GetEnumValue(TypeInfo(TPenStyle), Reader.ReadIdent)); end; procedure TJvBevel.ReadBevelPenWidth(Reader: TReader); begin PenWidth := Reader.ReadInteger; end; procedure TJvBevel.ReadBevelSides(Reader: TReader); var EnumType: PTypeInfo; EnumName: string; Value: Integer; begin // To allow for the Globus TglSide property to be read, we must read the // set ourselves, replacing the fsd prefix by be before reading the value try if Reader.ReadValue <> vaSet then raise EReadError.CreateRes(@SInvalidPropertyValue); EnumType := TypeInfo(TBevelEdge); Edges := []; while True do begin EnumName := Reader.ReadStr; if EnumName = '' then Break; EnumName := StringReplace(EnumName, 'fsd', 'be', []); Value := GetEnumValue(EnumType, EnumName); if Value = -1 then raise EReadError.CreateRes(@SInvalidPropertyValue); Include(FEdges, TBevelEdge(Value)); end; except // Reader.SkipSetBody while Reader.ReadStr <> '' do ; raise; end; end; procedure TJvBevel.SetBold(const Value: Boolean); begin if FBold <> Value then begin FBold := Value; Invalidate; end; end; procedure TJvBevel.SetEdges(const Value: TBevelEdges); begin if FEdges <> Value then begin FEdges := Value; Shape := bsCustomShape; Invalidate; end; end; procedure TJvBevel.SetHorizontalLines(const Value: TJvBevelLines); begin FHorizontalLines.Assign(Value); end; procedure TJvBevel.SetInner(const Value: TBevelCut); begin if FInner <> Value then begin FInner := Value; Style := bsCustomStyle; Invalidate; end; end; procedure TJvBevel.SetOuter(const Value: TBevelCut); begin if FOuter <> Value then begin FOuter := Value; Style := bsCustomStyle; Invalidate; end; end; procedure TJvBevel.SetPenStyle(const Value: TPenStyle); begin if FPenStyle <> Value then begin FPenStyle := Value; Invalidate; end; end; procedure TJvBevel.SetPenWidth(const Value: Integer); begin if FPenWidth <> Value then begin FPenWidth := Value; Invalidate; end; end; procedure TJvBevel.SetShape(const Value: TJvBevelShape); begin if FShape <> Value then begin FShape := Value; if FShape <> bsCustomShape then begin inherited Shape := TBevelShape(FShape); // Set the other properties so that should the style become // bsCustomStyle, the rendering is the closest it can be to // the one done in the ancestor. // Those next few lines define the most common properties. FEdges := [beTop, beLeft, beBottom, beRight]; case Style of bsLowered: begin FInner := bvRaised; FOuter := bvLowered; end; bsRaised: begin FInner := bvLowered; FOuter := bvRaised; end; end; // And now we adjust. case FShape of bsBox: begin FInner := bvNone; case Style of bsLowered: FOuter := bvLowered; bsRaised: FOuter := bvRaised; end; end; bsTopLine: begin FEdges := [beTop]; end; bsBottomLine: begin FEdges := [beBottom]; end; bsLeftLine: begin FEdges := [beLeft]; end; bsRightLine: begin FEdges := [beRight]; end; bsSpacer: begin FInner := bvSpace; FOuter := bvSpace; FEdges := []; end; end; end; Invalidate; end; end; procedure TJvBevel.SetStyle(const Value: TJvBevelStyle); begin if Value <> FStyle then begin FStyle := Value; if FStyle <> bsCustomStyle then begin inherited Style := TBevelStyle(FStyle); // Set the other properties so that should the shape become // bsCustomShape, the rendering is the closest it can be to // the one done in the ancestor. // Those next few lines define the most common properties. case FStyle of bsLowered: begin FInner := bvRaised; FOuter := bvLowered; end; bsRaised: begin FInner := bvLowered; FOuter := bvRaised; end; end; // And now we adjust case Shape of bsBox: begin FInner := bvNone; case FStyle of bsLowered: FOuter := bvLowered; bsRaised: FOuter := bvRaised; end; end; bsSpacer: begin FInner := bvSpace; FOuter := bvSpace; end; end; end; Invalidate; end; end; procedure TJvBevel.SetVerticalLines(const Value: TJvBevelLines); begin FVerticalLines.Assign(Value); end; //=== { TJvBevelLines } ====================================================== constructor TJvBevelLines.Create; begin inherited Create; FStyle := bvLowered; FThickness := 1; end; procedure TJvBevelLines.Assign(Source: TPersistent); begin if Source is TJvBevelLines then begin FCount := (Source as TJvBevelLines).Count; FStyle := (Source as TJvBevelLines).Style; FBold := (Source as TJvBevelLines).Bold; FThickness := (Source as TJvBevelLines).Thickness; FIgnoreBorder := (Source as TJvBevelLines).IgnoreBorder; DoChange; end else begin inherited Assign(Source); end; end; procedure TJvBevelLines.DefineProperties(Filer: TFiler); begin Filer.DefineProperty('Step', IgnoreValue, nil, False); Filer.DefineProperty('Origin', IgnoreValue, nil, False); inherited DefineProperties(Filer); end; procedure TJvBevelLines.DoChange; begin if Assigned(FOnChange) then FOnChange(Self); end; procedure TJvBevelLines.IgnoreValue(Reader: TReader); begin TReaderAccess(Reader).SkipValue; end; procedure TJvBevelLines.SetBold(const Value: Boolean); begin if FBold <> Value then begin FBold := Value; DoChange; end; end; procedure TJvBevelLines.SetCount(const Value: Cardinal); begin if FCount <> Value then begin FCount := Value; DoChange; end; end; procedure TJvBevelLines.SetIgnoreBorder(const Value: Boolean); begin if FIgnoreBorder <> Value then begin FIgnoreBorder := Value; DoChange; end; end; procedure TJvBevelLines.SetStyle(const Value: TBevelCut); begin if FStyle <> Value then begin FStyle := Value; DoChange; end; end; procedure TJvBevelLines.SetThickness(const Value: Byte); begin if FThickness <> Value then begin FThickness := Value; DoChange; end; end; {$IFDEF UNITVERSIONING} initialization RegisterUnitVersion(HInstance, UnitVersioning); finalization UnregisterUnitVersion(HInstance); {$ENDIF UNITVERSIONING} end.