unit ce_dsgncontrols;
{$I ce_defines.inc}

interface

uses
  Classes, SysUtils, Forms, Controls, ComCtrls, ExtCtrls, buttons, graphics,
  Menus, LMessages, LCLType, Toolwin;

type

  (**
   * Toolbutton with methods to load the glyph from the shared resources
   *)
  TCEToolButton = class(TToolButton)
  private
    fResourceName: string;
    fScaledSeparator: boolean;
    fPng: TPortableNetworkGraphic;
    fDPng: TPortableNetworkGraphic;
    function  findResourceWithSize(value: integer): boolean;
    procedure setResourceName(const value: string);
    procedure setScaledSeparator(value: boolean);
    procedure setToolBar(value: TToolbar);
  protected
    procedure reloadPng;
    procedure Paint; override;
    procedure SetEnabled(Value: Boolean); override;
  published
    property resourceName: string read fResourceName write setResourceName;
    property scaledSeparator: boolean read fScaledSeparator write setScaledSeparator;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    procedure toBitmap(value: TBitmap);
  end;

  TToolBarScaling = (auto, force16, force24, force32);

  (**
   * Toolbar with design-time support for TCEToolbutton
   *)
  TCEToolBar = class(TToolBar)
  protected
    fDesignMenu: TPopupMenu;
    fToolBarScaling: TToolBarScaling;
    procedure dsgnAdd(style: TToolButtonStyle);
    procedure dsgnAddButton(sender: TObject);
    procedure dsgnAddDivider(sender: TObject);
    procedure dsgnAddSeparator(sender: TObject);
    procedure dsgnAddDropdown(sender: TObject);
    procedure dsgnAddCheckbutton(sender: TObject);
    procedure setScaling(value: TToolBarScaling);
    procedure Loaded; override;
  public
    constructor Create(TheOwner: TComponent); override;
    destructor Destroy; override;
    procedure CMDesignHitTest(var Message: TCMDesignHitTest); message CM_DESIGNHITTEST;
  published
    property ButtonHeight stored false;
    property ButtonWidth stored false;
    property Scaling: TToolBarScaling read fToolBarScaling write setScaling stored false;
  end;

  procedure register;

implementation

procedure register;
begin
  RegisterComponents('Coedit', [TCEToolBar, TCEToolButton]);
end;

constructor TCEToolButton.Create(TheOwner: TComponent);
begin
  inherited;
  fPng := TPortableNetworkGraphic.Create;
  fDPng := TPortableNetworkGraphic.Create;
  AutoSize := true;
end;

destructor TCEToolButton.Destroy;
begin
  fPng.FreeImage;
  fPng.Free;
  fDPng.FreeImage;
  fDPng.Free;
  inherited;
end;

procedure TCEToolButton.setToolBar(value: TToolbar);
begin
  FToolBar := value;
end;

function TCEToolButton.findResourceWithSize(value: integer): boolean;
var
  n: string;
begin
  n := resourceName + IntToStr(value);
  result := FindResource(HINSTANCE, PChar(n), PChar(RT_RCDATA)) <> 0;
end;

procedure TCEToolButton.reloadPng;
var
  i: integer;
  j: integer;
  p: PRGBAQuad;
  g: byte;
  n: string;
  h: integer;
begin
  if csDesigning in ComponentState then
    exit;

  fPng.FreeImage;
  fDPng.FreeImage;

  n := resourceName;
  h := FToolBar.ButtonHeight;

  if n.IsEmpty then
    exit;

  if (h > 50) and findResourceWithSize(32) then
    n += '32'
  else if (h > 30) and findResourceWithSize(24) then
    n += '24';

  fPng.LoadFromResourceName(HINSTANCE, n);
  fDPng.LoadFromResourceName(HINSTANCE, n);

  if fDpng.PixelFormat = pf32bit then
    for i:= 0 to fDPng.Height-1 do
  begin
    {$PUSH}{$HINTS OFF}{$WARNINGS OFF}{$R-}
    p := PRGBAQuad(fDPng.ScanLine[i]);
    {$POP}
    for j:= 0 to fDPng.Width-1 do
    begin
      g := (p^.Red div 5) + (p^.Green div 100) * 70 + (p^.Blue div 11);
      p^.Green:=g;
      p^.Blue:=g;
      p^.Red:=g;
      p += 1;
    end;
  end;
end;

procedure TCEToolButton.setResourceName(const value: string);
begin
  if fResourceName = value then
    exit;
  fResourceName := value;
  if csDesigning in ComponentState then
    exit;
  if Style in [tbsButton, tbsDropDown] then
    reloadPng;
  if assigned(fToolBar) then
    FToolBar.Repaint;
end;

procedure TCEToolButton.setScaledSeparator(value: boolean);
begin
  if fScaledSeparator = value then
    exit;
  fScaledSeparator:=value;
  // store ratio if true
end;

procedure TCEToolButton.SetEnabled(Value: Boolean);
var
  old: boolean;
begin
  old := Enabled;
  inherited;
  if (old <> Enabled) and assigned(fToolBar) then
    FToolBar.Repaint;
end;

procedure TCEToolButton.toBitmap(value: TBitmap);
begin
  value.Assign(fPng);
end;

procedure TCEToolButton.Paint;
var
  rc: TRect;
  x, y: integer;
begin
  inherited;
  if (fResourceName <> '') and (style in [tbsButton, tbsDropDown, tbsCheck]) then
  begin
    rc := ClientRect;
    if Style = tbsDropDown then
      rc.Right := rc.left + FToolBar.ButtonWidth;
    x := ((rc.Right - rc.Left) - fPng.width) div 2;
    y := ((rc.Bottom - rc.Top) - fPng.Height) div 2;
    if Enabled then
      Canvas.Draw(x, y, fPng)
    else
      Canvas.Draw(x, y, fDPng);
  end;
end;

constructor TCEToolBar.Create(TheOwner: TComponent);
var
  item: TMenuItem;
begin
  inherited;
  if csDesigning in ComponentState then
  begin
    fDesignMenu := TPopupMenu.Create(nil);
    fDesignMenu.Name:= 'CEToolbarDsgnMenu';
    item := TMenuItem.Create(fDesignMenu);
    item.Caption:= 'add button';
    item.OnClick:= @dsgnAddButton;
    fDesignMenu.Items.Add(item);
    item := TMenuItem.Create(fDesignMenu);
    item.Caption:= 'add separator';
    item.OnClick:= @dsgnAddSeparator;
    fDesignMenu.Items.Add(item);
    item := TMenuItem.Create(fDesignMenu);
    item.Caption:= 'add divider';
    item.OnClick:= @dsgnAddDivider;
    fDesignMenu.Items.Add(item);
    item := TMenuItem.Create(fDesignMenu);
    item.Caption:= 'add check';
    item.OnClick:= @dsgnAddCheckbutton;
    fDesignMenu.Items.Add(item);
    item := TMenuItem.Create(fDesignMenu);
    item.Caption:= 'add dropdown';
    item.OnClick:= @dsgnAddDropdown;
    fDesignMenu.Items.Add(item);
  end;
  borderSpacing.Left := 2;
  borderSpacing.Top := 2;
  borderSpacing.Right := 2;
  borderSpacing.Bottom := 0;
  EdgeInner:= esNone;
  EdgeOuter:= esNone;
  Flat := false;
  Transparent := true;
end;

destructor TCEToolBar.Destroy;
begin
  if csDesigning in ComponentState then
    fDesignMenu.Free;
  inherited;
end;

procedure TCEToolBar.Loaded;
begin
  inherited;
  setScaling(auto);
end;

procedure TCEToolBar.setScaling(value: TToolBarScaling);
var
  i: integer;
begin
  fToolBarScaling := value;
  if ((fToolBarScaling = TToolBarScaling.auto) and (ScaleY(16, 96) >= 32)) or
     (fToolBarScaling = TToolBarScaling.force32) then
  begin
    height := 60;
    ButtonHeight := 56;
    ButtonWidth := 56;
  end else
  if ((fToolBarScaling = TToolBarScaling.auto) and (ScaleY(16, 96) >= 24)) or
     (fToolBarScaling = TToolBarScaling.force24) then
  begin
    height := 44;
    ButtonHeight := 42;
    ButtonWidth := 42;
  end
  else
  begin
    height :=  30;
    ButtonHeight := 28;
    ButtonWidth := 28;
  end;

  for i := 0 to ControlCount-1 do
    if Controls[i] is TCEToolButton then
      TCEToolButton(Controls[i]).reloadPng;

  Repaint;
end;

procedure TCEToolBar.CMDesignHitTest(var Message: TCMDesignHitTest);
begin
  inherited;
  if not (csDesigning in ComponentState) then
    exit;
  if Message.Keys <> MK_RBUTTON then
    exit;
  Message.Result := 0;
  fDesignMenu.PopUp(Mouse.CursorPos.x,Mouse.CursorPos.y);
end;

procedure TCEToolBar.dsgnAdd(style: TToolButtonStyle);
var
  button: TCEToolButton;
  str: string = '';
  i: integer = 0;
begin
  button := TCEToolButton.Create(owner);
  while true do
  begin
    str := format('button%d',[i]);
    if owner.FindComponent(str) = nil then
      break;
    i += 1;
  end;
  button.Name:= str;
  button.Style := style;
  InsertControl(button);
  ButtonList.add(button);
  button.setToolBar(self);
  if style = tbsDivider then
    width := 16;
end;

procedure TCEToolBar.dsgnAddButton(sender: TObject);
begin
  dsgnAdd(tbsButton);
end;

procedure TCEToolBar.dsgnAddDivider(sender: TObject);
begin
  dsgnAdd(tbsDivider);
end;

procedure TCEToolBar.dsgnAddSeparator(sender: TObject);
begin
  dsgnAdd(tbsSeparator);
end;

procedure TCEToolBar.dsgnAddCheckbutton(sender: TObject);
begin
  dsgnAdd(tbsCheck);
end;

procedure TCEToolBar.dsgnAddDropdown(sender: TObject);
begin
  dsgnAdd(tbsDropDown);
end;

initialization
  RegisterClasses([TCEToolBar, TCEToolButton]);
end.

