unit KsSkinItems;

{$I KsSkinVer.Inc}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, CommCtrl, Menus, ActnList, ImgList, KsBitmap, KsUtils, KsItems,
  KsSkinObjects, KsSkinSource, KsSkinEngine;

type

{ TSeSkinItem }

  TSeSkinItem = class(TKsCustomItem)
  private
    FSkinEngine: TSeSkinEngine;
    FSkinMenuBar: TseSkinObject;
    FSkinPopupMenu: TseSkinObject;
    FSkinMenuBarItem: TseSkinObject;
    FSkinPopupMenuItem: TseSkinObject;
    FSkinPopupMenuSep: TseSkinObject;

    procedure SetSkinEngine(const Value: TSeSkinEngine);
  protected
    function UseSkin: boolean;
    { Overrides }
    class function GetPopupWindowClass: TClass; override;
    { VCL protected  }
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure CalcSize(Buffer: TKsBitmap; View: TKsItemView; var AWidth,
      AHeight: integer); override;
    procedure DrawItem(Buffer: TKsBitmap; View: TKsItemView; Rect: TRect;
      Active, Hover: boolean); override;

    property SkinEngine: TSeSkinEngine read FSkinEngine write SetSkinEngine;
  published
    property Checked;
    property Enabled;
    property Caption;
    property ImageIndex;
    property Images;
    property ShortCut;
    property Visible;
    property OnClick;
  end;

implementation {===============================================================}

uses KsSkinMenuForm, KsForms;

{ TSeSkinItem }

constructor TSeSkinItem.Create(AOwner: TComponent);
begin
  inherited;
end;

destructor TSeSkinItem.Destroy;
begin
  inherited;
end;

{ Skin support }

function TSeSkinItem.UseSkin: boolean;
begin
  if (csDestroying in ComponentState) or (csLoading in ComponentState) then
    Result := false
  else
    if (FSkinEngine <> nil) and (FSkinEngine.SkinSource <> nil) and
       (not FSkinEngine.SkinSource.IsChanging) and
       (FSkinEngine.SkinSource.Count > 0) and
       (FSkinMenuBar <> nil) and
       (FSkinPopupMenu <> nil)
    then
      Result := true
    else
      Result := false;
end;

procedure TSeSkinItem.CalcSize(Buffer: TKsBitmap; View: TKsItemView;
  var AWidth, AHeight: integer);
begin
  if UseSkin then
  begin
    if (View.IsMenuBar) then
    begin
      if FSkinMenuBarItem = nil then
      begin
        inherited ;
        Exit;
      end;

      Buffer.Font.Assign(FSkinMenuBarItem.Font);

      with FSkinMenuBarItem do
        AWidth := Buffer.TextWidth(Caption) + TextMarginLeft + TextMarginRight;

      AHeight := FSkinMenuBarItem.Height;

      if MDIItemKind <> mikNone then
        AWidth := AHeight;
    end
    else
    begin
      if FSkinPopupMenuItem = nil then
      begin
        inherited ;
        Exit;
      end;

      Buffer.Font.Assign(FSkinPopupMenuItem.Font);

      AWidth := GlyphWidth + Buffer.TextWidth(Caption) + ItemStep + Buffer.TextWidth(ShortCutToText(ShortCut)) + SubMenuWidth;
      AHeight := FSkinPopupMenuItem.Height;

      Exit;
    end;
  end
  else
    inherited ;
end;

procedure TSeSkinItem.DrawItem(Buffer: TKsBitmap; View: TKsItemView;
  Rect: TRect; Active, Hover: boolean);
const
  CheckMarkPoints: array[0..11] of TPoint = (
    { Black }
    (X: -2; Y: -2), (X: 0; Y:  0), (X:  4; Y: -4),
    (X:  4; Y: -3), (X: 0; Y:  1), (X: -2; Y: -1),
    (X: -2; Y: -2),
    { White }
    (X: -3; Y: -2), (X: -3; Y: -1), (X: 0; Y: 2),
    (X:  5; Y: -3), (X:  5; Y: -5));
var
  R: TRect;
  Points: array[0..11] of TPoint;
  Canvas: TCanvas;
  i, X, Y: integer;
  B: TBitmap;
  Flags: integer;
  IHandle, IHandle2 : HICON;
  IconX, IconY : integer;
  ID: boolean;
  SkinObject: TSeSkinObject;
begin
  if UseSkin then
  begin
    if (View.IsMenuBar) and (FSkinMenuBar <> nil) and (FSkinMenuBarItem <> nil) then
    begin

      { MDI Items }
      if (MDIItemKind <> mikNone) and (MDIItemKind <> mikSysMenu) then
      begin
        { Draw SysIcon }
        case MDIItemKind of
          mikMinimize: begin
            SkinObject := FSkinMenuBar.FindObjectByName('Min');
            if SkinObject <> nil then
            begin
              { Set State }
              if Active then
                if Hover then SkinObject.State := ssHot
                else SkinObject.State := ssFocused
              else
                SkinObject.State := ssNormal;
              { Draw }
              R := SkinObject.BoundsRect;
              RectCenter(R, Rect);
              SkinObject.BoundsRect := R;
              SkinObject.Draw(Buffer);
            end;
          end;
          mikRestore: begin
            SkinObject := FSkinMenuBar.FindObjectByName('Restore');
            if SkinObject <> nil then
            begin
              { Set State }
              if Active then
                if Hover then SkinObject.State := ssHot
                else SkinObject.State := ssFocused
              else
                SkinObject.State := ssNormal;
              { Draw }
              R := SkinObject.BoundsRect;
              RectCenter(R, Rect);
              SkinObject.BoundsRect := R;
              SkinObject.Draw(Buffer);
            end;
          end;
          mikClose: begin
            SkinObject := FSkinMenuBar.FindObjectByName('Close');
            if SkinObject <> nil then
            begin
              { Set State }
              if Active then
                if Hover then SkinObject.State := ssHot
                else SkinObject.State := ssFocused
              else
                SkinObject.State := ssNormal;
              { Draw }
              R := SkinObject.BoundsRect;
              RectCenter(R, Rect);
              SkinObject.BoundsRect := R;
              SkinObject.Draw(Buffer);
            end;
          end;
        end;

        Exit;
      end;

      Buffer.Font.Assign(FSkinMenuBarItem.Font);
      FSkinMenuBarItem.BoundsRect := Rect;
      FSkinMenuBarItem.Text := Caption;

      { Set state }
      if Active then
        if Hover then FSkinMenuBarItem.State := ssHot
        else FSkinMenuBarItem.State := ssFocused
      else
        FSkinMenuBarItem.State := ssNormal;

      if not Enabled then FSkinMenuBarItem.Enabled := false
      else FSkinMenuBarItem.Enabled := true;

      { Draw }
      FSkinMenuBarItem.Draw(Buffer);

      { Draw MDI system menu icon }
      if MDIItemKind = mikSysMenu then
      begin
        ID := false;
        if (ActiveMDIForm as TKsCustomForm).Form.Icon.Handle <> 0 then
          IHandle := (ActiveMDIForm as TKsCustomForm).Form.Icon.Handle
        else
          if Application.Icon.Handle <> 0 then
            IHandle := Application.Icon.Handle
          else
          begin
            IHandle := LoadIcon(0, IDI_APPLICATION);
            ID := true;
          end;
        IconX := GetSystemMetrics(SM_CXSMICON);
        if IconX = 0 then IconX := GetSystemMetrics(SM_CXSIZE);
        IconY := GetSystemMetrics(SM_CYSMICON);
        if IconY = 0 then IconY := GetSystemMetrics(SM_CYSIZE);
        IHandle2 := CopyImage(IHandle, IMAGE_ICON, IconX, IconY, LR_COPYFROMRESOURCE);

        R := Classes.Rect(0, 0, 16, 16);
        RectCenter(R, Rect);
        DrawIconEx(Buffer.DC, R.Left, R.Top, IHandle2, 0, 0, 0, 0, DI_NORMAL);

        DestroyIcon(IHandle2);
        if ID then DestroyIcon(IHandle);
      end;

      Exit;
    end;

    if not View.IsMenuBar and (FSkinPopupMenu <> nil) and (FSkinPopupMenuItem <> nil) then
    begin
      Buffer.Font.Assign(FSkinPopupMenuItem.Font);
      FSkinPopupMenuItem.BoundsRect := Rect;
      FSkinPopupMenuItem.Text := Caption;

      { Set state }
      if Active then
        if Hover then FSkinPopupMenuItem.State := ssHot
        else FSkinPopupMenuItem.State := ssFocused
      else
        FSkinPopupMenuItem.State := ssNormal;

      if Caption = '-' then
      begin
        FSkinPopupMenuItem.State := ssNormal;
        if FSkinPopupMenuSep <> nil then
        begin
          { Draw Separator Object }
          FSkinPopupMenuSep.BoundsRect := Rect;
          FSkinPopupMenuSep.State := ssNormal;
          FSkinPopupMenuSep.Draw(Buffer);
          Exit;
        end;
        FSkinPopupMenuItem.Draw(Buffer);
        Exit;
      end;

      if not Enabled then FSkinPopupMenuItem.Enabled := false
      else FSkinPopupMenuItem.Enabled := true;

      { Set Shortcut Text }
      if ShortCut <> 0 then
        FSkinPopupMenuItem.RightText := ShortCutToText(ShortCut)
      else
        FSkinPopupMenuItem.RightText := '';

      { Draw }
      FSkinPopupMenuItem.Draw(Buffer);

      { Draw Image and Checked }
      if (Checked) or ((ImgList <> nil) and (ImageIndex >= 0)) then
      begin
        { Draw Glyph }
        Canvas := TCanvas.Create;
        try
          Canvas.Handle := Buffer.DC;
          R := Rect;
          R.Right := R.Left + GlyphWidth;
          if (ImgList <> nil) and (ImageIndex >= 0) then
          begin
            { Draw Image }
            ImgList.Draw(Canvas,
              R.Left + ((R.Right - R.Left) - ImgList.Width) div 2,
              R.Top + ((R.Bottom - R.Top) - ImgList.Height) div 2,
              ImageIndex, Enabled);
          end
          else
          begin
            { Draw CheckBox }
            X := (R.Left + R.Right) div 2 - 2;
            Y := (R.Top + R.Bottom) div 2 + 1;
            System.Move(CheckMarkPoints, Points, 12 * SizeOf(TPoint));
            for i := Low(Points) to High(Points) do begin
              Inc (Points[I].X, X);
              Inc (Points[I].Y, Y);
            end;
            Canvas.Pen.Color := Buffer.Font.Color;
            Polyline(Canvas.Handle, Points[0], 7);
            Canvas.Pen.Color := Buffer.Font.Color;
            Polyline(Canvas.Handle, Points[7], 5);
          end;
        finally
          Canvas.Handle := 0;
          Canvas.Free;
        end;
      end;

      { Draw Submenu Arrow }
      if Count > 0 then
      begin
        R := Rect;
        R.Left := R.Right - SubMenuWidth;
        
        B := TBitmap.Create;
        try
          B.Width := RectWidth(R);
          B.Height := RectHeight(R);
          { Draw to B }
          DrawFrameControl(B.Canvas.Handle, Classes.Rect(0, 0, RectWidth(R),
            RectHeight(R)), DFC_MENU, DFCS_MENUARROW);
          { Copy to Buffer }
          for X := 0 to B.Width-1 do
            for Y := 0 to B.Height-1 do
              if B.Canvas.Pixels[X, Y] = clBlack then
                Buffer.Pixels[R.Left + X, R.Top + Y] := Buffer.Font.Color;
        finally
          B.Free;
        end;
      end;

      Exit;
    end;

    inherited ;
  end
  else
    inherited;
end;

{ Override }

class function TSeSkinItem.GetPopupWindowClass: TClass;
begin
  Result := TSeSkinPopupWindow;
end;

procedure TSeSkinItem.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;
  if (Operation = opRemove) and (AComponent = FSkinEngine) then FSkinEngine := nil;
end;

procedure TSeSkinItem.SetSkinEngine(const Value: TSeSkinEngine);
var
  i: integer;
begin
  FSkinEngine := Value;

  if (FSkinEngine <> nil) and (FSkinEngine.SkinSource <> nil) and
     (not FSkinEngine.SkinSource.IsChanging) and
     (FSkinEngine.SkinSource.Count > 0) then
  begin
    FSkinMenuBar := FSkinEngine.SkinSource.GetObjectByName('MenuBar');
    FSkinPopupMenu := FSkinEngine.SkinSource.GetObjectByName('PopupMenu');
    if FSkinMenuBar <> nil then
      if IsToolbar then
        FSkinMenuBarItem := FSkinMenuBar.FindObjectByName('ToolbarItem')
      else
        FSkinMenuBarItem := FSkinMenuBar.FindObjectByName('Item');
    if FSkinPopupMenu <> nil then
    begin
      FSkinPopupMenuItem := FSkinPopupMenu.FindObjectByName('Item');
      FSkinPopupMenuSep := FSkinPopupMenu.FindObjectByName('Separator');
    end;
  end
  else
  begin
    FSkinPopupMenu := nil;
    FSkinMenuBar := nil;
    FSkinMenuBarItem := nil;
    FSkinPopupMenuItem := nil;
  end;

  for i := 0 to Count-1 do
    if Items[i] is TSeSkinItem then
      TSeSkinItem(Items[i]).SkinEngine := Value;
end;

initialization
  RegisterClasses([TSeSkinItem]);
finalization
end.
