unit ToGRush;

interface

//{$DEFINE TOGRUSH_AUTO_DISIMAGES} // add this symbol to provide 256 gray images
                                   // based on original ones for Disabled state
                                   // of toolbar buttons
//{$DEFINE TOGRUSH_AUTO_DIS_EQ} // RGB channels of the same level while TOGRUSH_AUTO_DISIMAGES
//{$DEFINE TOGRUSH_DROPBTN2} // Drop button will be placed right to the button
                             // having property DropDown, not in the button
//{$DEFINE TOGRUSH_NO_AUTO_SIZE_BTNS} // not use AutoSize for buttons
           // (sensible only in a case, when only images are in the toolbar)

uses Windows, KOL, KOLGRushControls;

function NewButton( AParent: PControl; const Caption: KOLString ): PControl;

function NewCheckbox( AParent: PControl; const Caption: KOLString ): PControl;
function NewRadiobox( AParent: PControl; const Caption: KOLString ): PControl;

function NewPanel( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;

function NewSplitter( AParent: PControl; MinSizePrev, MinSizeNext: Integer ): PControl;
function NewSplitterEx( AParent: PControl; MinSizePrev, MinSizeNext: Integer;
         EdgeStyle: TEdgeStyle ): PControl;


function NewToolbar( AParent: PControl; Align: TControlAlign; Options: TToolbarOptions;
                     Bitmap: HBitmap; const Buttons: array of PChar;
                     const BtnImgIdxArray: array of Integer ) : PControl;

function ToolbarButtonRect( Toolbar: PControl; BtnID: Integer ): TRect;
procedure ToolbarSetTooltips( Toolbar: PControl; BtnID1st: Integer; const Tooltips: array of PKOLChar );
function ToolbarButtonEnabled( Toolbar: PControl; BtnID: Integer ): Boolean;
procedure EnableToolbarButton( Toolbar: PControl; BtnID: Integer; Enable: Boolean );
function ToolbarButtonVisible( Toolbar: PControl; BtnID: Integer ): Boolean;
procedure ShowHideToolbarButton( Toolbar: PControl; BtnID: Integer; Show: Boolean );

function NewProgressbar( AParent: PControl ): PControl;
function NewProgressbarEx( AParent: PControl; Options: TProgressbarOptions ): PControl;

implementation

const
  IS_DRDWN = 16;
type
  PControl_ = ^TControl_;
  TControl_ = object( TControl )
  end;

////////////////////////////////////////////////////////////////////////////////
// BUTTON, CHECK, RADIO CHECK
////////////////////////////////////////////////////////////////////////////////
function NewButton( AParent: PControl; const Caption: KOLString ): PControl;
begin
  Result := Pointer( NewGRushButton( AParent, Caption ).SetSize( 64, 22 ) );
  PControl_( Result ).fIsButton := TRUE;
end;

function NewCheckbox( AParent: PControl; const Caption: KOLString ): PControl;
begin
  Result := Pointer( NewGRushCheckBox( AParent, Caption ).SetSize( 64, 22 ) );
end;

function NewRadiobox( AParent: PControl; const Caption: KOLString ): PControl;
begin
  Result := Pointer( NewGRushRadioBox( AParent, Caption ).SetSize( 64, 22 ) );
end;

////////////////////////////////////////////////////////////////////////////////
// PANEL
////////////////////////////////////////////////////////////////////////////////
function NewPanel( AParent: PControl; EdgeStyle: TEdgeStyle ): PControl;
begin
  if EdgeStyle = esTransparent then
  begin
    Result := KOL.NewPanel( AParent, esNone ).SetSize( 64, 64 );
    Result.Transparent := TRUE;
  end
  else
    Result := Pointer( NewGRushPanel( AParent ) );
end;

////////////////////////////////////////////////////////////////////////////////
// SPLITTER
////////////////////////////////////////////////////////////////////////////////
function NewSplitter( AParent: PControl; MinSizePrev, MinSizeNext: Integer ): PControl;
begin
  Result := Pointer( NewGRushSplitter( AParent, MinSizePrev, MinSizeNext ) );
  Result.Transparent := TRUE;
end;

function NewSplitterEx( AParent: PControl; MinSizePrev, MinSizeNext: Integer;
         EdgeStyle: TEdgeStyle ): PControl;
begin
  Result := Pointer( NewGRushSplitter( AParent, MinSizePrev, MinSizeNext ) );
  Result.Transparent := TRUE;
end;

////////////////////////////////////////////////////////////////////////////////
// TOOL BAR
////////////////////////////////////////////////////////////////////////////////
type
  TTBButtonEvent = packed Record
    BtnID: DWORD;
    Event: TOnToolbarButtonClick;
  end;
  PTBButtonEvent = ^TTBButtonEvent;
procedure ToGR_ClickToolbarBtn( Dummy, Sender: PControl );
var D: DWORD;
    Idx: Integer;
    IsCheck, Checked: Boolean;
    Toolbar: PControl_;
    EventRec: PTBButtonEvent;
begin
  D := GetProp( Sender.Handle, 'GRBTN' );
  Idx := LoWord( D );
  IsCheck := HiWord( D ) and 2 <> 0;
  Checked := HiWord( D ) and 4 <> 0;
  if IsCheck then
  begin
    Checked := not Checked;
    D := D xor $40000;
    SetProp( Sender.Handle, 'GRBTN', D );
    PGrushControl( Sender ).Checked := Checked;
  end;
  Toolbar := Pointer( Sender.Parent );
  if Assigned( Toolbar.fTBEvents ) and
     (Toolbar.fTBevents.Count > Idx) then
  begin
    EventRec := Toolbar.fTBevents.Items[ Idx ];
    if Assigned( EventRec.Event ) then
      EventRec.Event( Toolbar, EventRec.BtnID );
  end
    else
  if Assigned( Toolbar.fOnClick ) then
  begin
    Toolbar.fOnClick( Toolbar );
  end;
end;

procedure ToGR_ClickToolbarBtnDD( Dummy, Sender: PControl );
var D: DWORD;
    Idx: Integer;
    Toolbar: PControl_;
    EventRec: PTBButtonEvent;
begin
  D := GetProp( Sender.Handle, 'GRBTN' );
  Idx := LoWord( D );
  Toolbar := Pointer( Sender.Parent );
  {$IFDEF TOGRUSH_DROPBTN2}
  {$ELSE}
  Toolbar := Pointer( Toolbar.Parent );
  {$ENDIF}
  Toolbar.fCurItem := Idx;
  Toolbar.fCurIndex := Idx;
  Toolbar.fDropped := TRUE;
  if Assigned( Toolbar.fTBevents ) and
     (Toolbar.fTBevents.Count > Idx) then
  begin
    EventRec := Toolbar.fTBevents.Items[ Idx ];
    Toolbar.fCurItem := EventRec.BtnID;
  end;
  if Assigned( Toolbar.OnTBDropDown ) then
  begin
    Toolbar.OnTBDropDown( Toolbar );
  end
    else
  if Assigned( Toolbar.fOnClick ) then
  begin
    Toolbar.fOnClick( Toolbar );
  end;
  Toolbar.fDropped := FALSE;
end;

procedure Provide_DIS_images( var B: PBitmap );
var B2: PBitmap;
    y, y_to, x, c: Integer;
    Src, Dst: PRGBQuad;
    first_pixel: Boolean;
    Transp: DWORD;
begin
  if (B =nil) or B.Empty then Exit;
  B2 := NewDIBBitmap( B.Width, B.Height * 2, pf32bit );
  TRY
    B.Draw( B2.Canvas.Handle, 0, 0 );
    y_to := B.Height;
    first_pixel := TRUE;
    Transp := 0;
    for y := 0 to B.Height-1 do
    begin
      Src := B2.ScanLine[ y ];
      Dst := B2.ScanLine[ y_to ];
      for x := 0 to B2.Width-1 do
      begin
        if first_pixel then
          Transp := PDWORD( Src )^ and $FFFFFF;
        first_pixel := FALSE;
        if PDWORD( Src )^ and $FFFFFF = Transp then
          PDWORD( Dst )^ := Transp
        else
        begin
          {$IFDEF TOGRUSH_AUTO_DIS_BAL}
          c := (Src.rgbRed * 64 + Src.rgbGreen * 128 + Src.rgbBlue * (128 + 64))
               div 256;
          {$ELSE}
          c := (Src.rgbRed * 64 + Src.rgbGreen * 64 + Src.rgbBlue * 64)
               div 100;
          {$ENDIF}
          if c > 255 then c := 255;
          Dst.rgbBlue := c;
          Dst.rgbGreen := c;
          Dst.rgbRed := c;
        end;
        inc( Src );
        inc( Dst );
      end;
      inc( y_to );
    end;
  FINALLY
    B.Assign( B2 );
    B2.Free;
  END;
end;

var DrDownBmp: PBitmap;
function NewToolbar( AParent: PControl; Align: TControlAlign; Options: TToolbarOptions;
                     Bitmap: HBitmap; const Buttons: array of PChar;
                     const BtnImgIdxArray: array of Integer ) : PControl;
var i, BtnID: Integer;
    B, B2: PGRushControl;
    C: String;
    IsSep: Boolean;
    IsDropDown: Boolean;
    IsCheck, Checked, IsRadio: Boolean;
    Idx: Integer;
    D: DWORD;
    imgW, imgH, W, H: Integer;
    Bmp: PBitmap;
    DD_dst: PByte;
    y: Integer;
    ES: TEdgeStyle;
const DD_img: array[ 0..6 ] of Byte = ( $0, $F8, $F8, $70, $70, $20, $20 );
begin
  if Align = caNone then Align := caTop;
  H := 0;
  imgW := 0;
  imgH := 0;
  Bmp := nil;
  if Bitmap <> 0 then
  begin
    Bmp := NewBitmap( 0, 0 );
    Bmp.Handle := Bitmap;
    imgH := Bmp.Height;
    imgW := imgH;
    H := Bmp.Height + 12;
    //Bmp.PixelFormat := pf32bit;
    //Bmp.SaveToFile( GetStartDir + 'test_toolbar1.bmp' );
    {$IFDEF TOGRUSH_AUTO_DISIMAGES}
    Provide_DIS_images( Bmp );
    {$ENDIF}
  end;
  ES := esNone;
  if [tboTransparent, tboFlat] * Options <> [] then
    ES := esTransparent;
  Result := Pointer( NewPanel( AParent, ES ).SetSize( 0, H ).SetAlign(Align) );
  //if Bmp <> nil then Result.Add2AutoFree( Bmp );
  Idx := -1;
  for i := 0 to High( Buttons ) do
  begin
    C := Buttons[ i ];
    IsSep := C = '-';
    IsDropDown := FALSE;
    IsCheck := FALSE;
    Checked := FALSE;
    BtnID := i; //ToolbarsIDcmd; inc( ToolbarsIDcmd );
    if IsSep then C := ''
    else
    begin
      Inc( Idx );
      IsDropDown := (C <> '') and (C[ 1 ] = '^');
      if IsDropDown then Delete( C, 1, 1 );
      IsCheck := (C <> '') and (C[ 1 ] in [ '+', '-' ]);
      if IsCheck then
      begin
        Checked := C[ 1 ] = '+';
        Delete( C, 1, 1 );
        IsRadio := (C <> '') and (C[ 1 ] = '!');
        if IsRadio then Delete( C, 1, 1 );
      end;
    end;
    if Trim( C ) = '' then C := '';
    if IsSep then
      NewPanel( Result, esTransparent ).SetSize( 6, 0 ).SetAlign( caLeft )
    else
    begin
      if C = '' then
      begin
        W := 32;
        if H <> 0 then W := H;
      end
        else
      begin
        W := 64;
      end;
      B := Pointer( NewButton( Result, C ).SetSize( W, 0 ).SetAlign( caLeft ) );
      {$IFDEF USE_NAMES}
      //B.Name := 'TB' + Int2Str( Idx+1 );
      {$ENDIF USE_NAMES}
      B.Tabstop := FALSE;
      B.LikeSpeedButton;
      B.Transparent := TRUE;
      if IsSep then B.Enabled := FALSE;
      if B.GetWindowHandle <> 0 then
      begin
        D := i   or Integer( IsSep ) shl 16
                 or Integer( IsCheck ) shl 17
                 or Integer( Checked ) shl 18
                 or Integer( IsDropDown ) shl 19
                 ;
        SetProp( B.Handle, 'GRBTN', D );
      end;
      SetProp( B.Handle, 'BTNID', BtnID );
      B.OnClick := TOnEvent( MakeMethod( nil, @ ToGR_ClickToolbarBtn ) );
      if Bmp <> nil then
      begin
        B.All_GlyphItemX := idx;
        B.All_GlyphItemY := 0;
        B.All_GlyphBitmap := Bmp;
        B.All_GlyphWidth := ImgW;
        B.All_GlyphHeight := ImgH;
        //B.All_GlyphAttached := TRUE;
        {$IFDEF TOGRUSH_AUTO_DISIMAGES}
        B.Dis_GlyphItemX := idx;
        B.Dis_GlyphItemY := 1;
        B.All_GlyphBitmap := Bmp;
        B.All_GlyphWidth := ImgW;
        B.All_GlyphHeight := ImgH;
        {$ENDIF}
        if not IsDropDown and (C = '') then
          B.All_GlyphHAlign := haCenter;
      end;
      {$IFNDEF TOGRUSH_NO_AUTO_SIZE_BTNS}
        B.fCommandActions.aAutoSzX := 10 + ImgW;
        if ImgW > 0 then inc( B.fCommandActions.aAutoSzX, 5 );
        if IsDropDown then inc( B.fCommandActions.aAutoSzX, 10 );
        B.AutoSize( TRUE );
      {$ENDIF}
      if IsDropDown then
      begin
        {$IFDEF TOGRUSH_DROPBTN2}
        B2 := Pointer( NewButton( Result, C ).SetSize( 5 + 8, 0 ).SetAlign( caLeft ) );
        {$ELSE}
        //B.AutoSize( FALSE );
        //B.Width := W + 13;
        B.All_TextHAlign := haLeft;
        B.Border := 2;
        B2 := Pointer( NewButton( B, C ).SetSize( 5 + 8, 0 ).SetAlign( caRight ) );
        {$ENDIF}
        {$IFDEF USE_NAMES}
        //B2.Name := 'TB_dd' + Int2Str( Idx+1 );
        {$ENDIF USE_NAMES}
        B2.Tabstop := FALSE;
        B2.LikeSpeedButton;
        B2.Transparent := TRUE;
        PGrushControl( B2 ).All_BorderWidth := 0;
        PGrushControl( B2 ).Over_BorderWidth := 1;
        if B2.GetWindowHandle <> 0 then
        begin
          D := i   or Integer( IsSep ) shl 16
                   or Integer( IsCheck ) shl 17
                   or Integer( Checked ) shl 18
                   or Integer( IsDropDown ) shl 19
                   or IS_DRDWN shl 16;
          SetProp( B2.Handle, 'GRBTN', D );
        end;
        B2.OnClick := TOnEvent( MakeMethod( nil, @ ToGR_ClickToolbarBtnDD ) );
        if DrDownBmp = nil then
        begin
          DrDownBmp := NewDIBBitmap( 5, High( DD_img )+1, pf1bit );
          DrDownBmp.DIBPalEntries[ 0 ] := $686868;
          DrDownBmp.DIBPalEntries[ 1 ] := $FFFFFF;
          for y := 0 to High( DD_img ) do
          begin
            DD_dst := DrDownBmp.ScanLine[ y ];
            DD_dst^ := not DD_img[ y ];
          end;
          //B2.All_GlyphItemX := 0;
          //B.All_GlyphItemY := 0;
          B2.All_GlyphBitmap := DrDownBmp;
          DrDownBmp.RefDec;
        end
          else
        begin
          B2.All_GlyphBitmap := DrDownBmp;
        end;
        B2.All_GlyphWidth := 5;
        B2.All_GlyphHeight := High( DD_img )+1;
        B2.All_GlyphHAlign := haCenter;
        B2.All_GlyphVAlign := vaBottom;
      end;
    end;
  end;
  if Bmp <> nil then
  begin
    Bmp.Free;
  end;
end;

function ToolbarButtonRect( Toolbar: PControl; BtnID: Integer ): TRect;
var i: Integer;
    B: PControl;
begin
  for i := 0 to Toolbar.ChildCount-1 do
  begin
    B := Toolbar.Children[ i ];
    if (B.GetWindowHandle <> 0) and
       (Integer( GetProp( B.Handle, 'BTNID' ) ) = BtnID) then
    begin
      Result := B.BoundsRect;
      Exit;
    end;
  end;
  Result := MakeRect( 0, 0, 0, 0 );
end;

procedure ToolbarSetTooltips( Toolbar: PControl; BtnID1st: Integer; const Tooltips: array of PKOLChar );
{$IFDEF USE_MHTOOLTIP}
var i, j: Integer;
    B: PControl;
    found: Boolean;
{$ENDIF}
begin
  Toolbar.TBSetTooltips( BtnID1st, Tooltips );
  {$IFDEF USE_MHTOOLTIP}
  found := FALSE;
  j := 0;
  if BtnID1st < 0 then BtnID1st := 0;
  for i := 0 to Toolbar.ChildCount-1 do
  begin
    if j > High( Tooltips ) then break;
    B := Toolbar.Children[ i ];
    //if not B.IsButton then continue;
    if HiWord( GetProp( B.Handle, 'GRBTN' ) ) and IS_DRDWN <> 0 then
      continue;
    if found or (Integer( GetProp( B.Handle, 'BTNID' ) ) = BtnID1st) then
    begin
      found := TRUE;
      if Trim( Tooltips[ j ] ) <> '' then
        NewHint( B ).Text := Tooltips[ j ];
      inc( BtnID1st );
      inc( j );
    end;
  end;
  {$ENDIF USE_MHTOOLTIP}
end;

function ToolbarButtonEnabled( Toolbar: PControl; BtnID: Integer ): Boolean;
var i: Integer;
    B: PControl;
begin
  for i := 0 to Toolbar.ChildCount-1 do
  begin
    B := Toolbar.Children[ i ];
    if (B.GetWindowHandle <> 0) and
       (Integer( GetProp( B.Handle, 'BTNID' ) ) = BtnID) then
    begin
      Result := B.Enabled;
      Exit;
    end;
  end;
  Result := FALSE;
end;

procedure EnableToolbarButton( Toolbar: PControl; BtnID: Integer; Enable: Boolean );
var i: Integer;
    B: PControl;
begin
  for i := 0 to Toolbar.ChildCount-1 do
  begin
    B := Toolbar.Children[ i ];
    if (B.GetWindowHandle <> 0) and
       (Integer( GetProp( B.Handle, 'BTNID' ) ) = BtnID) then
    begin
      B.Enabled := Enable;
      Exit;
    end;
  end;
end;

function ToolbarButtonVisible( Toolbar: PControl; BtnID: Integer ): Boolean;
var i: Integer;
    B: PControl;
begin
  for i := 0 to Toolbar.ChildCount-1 do
  begin
    B := Toolbar.Children[ i ];
    if (B.GetWindowHandle <> 0) and
       (Integer( GetProp( B.Handle, 'BTNID' ) ) = BtnID) then
    begin
      Result := B.Visible;
      Exit;
    end;
  end;
  Result := FALSE;
end;

procedure ShowHideToolbarButton( Toolbar: PControl; BtnID: Integer; Show: Boolean );
var i: Integer;
    B: PControl;
begin
  for i := 0 to Toolbar.ChildCount-1 do
  begin
    B := Toolbar.Children[ i ];
    if (B.GetWindowHandle <> 0) and
       (Integer( GetProp( B.Handle, 'BTNID' ) ) = BtnID) then
    begin
      B.Visible := Show;
      Exit;
    end;
  end;
end;

////////////////////////////////////////////////////////////////////////////////
// PROGRESS BAR
////////////////////////////////////////////////////////////////////////////////
function NewProgressbar( AParent: PControl ): PControl;
begin
  Result := Pointer( NewGRushProgressBar( AParent ).SetSize( 300, 20 ) );
end;

function NewProgressbarEx( AParent: PControl; Options: TProgressbarOptions ): PControl;
begin
  Result := NewProgressbar( AParent );
end;

end.
