www.pudn.com > xpbarmenu.rar > BarPopupMenu.pas


{ Component: TBarPopupMenu 
  =========================================================================== 
  Bluecave Software 
                  (C) Copyright 2000, Jouni Airaksinen (Mintus@Codefield.com) 
  =========================================================================== 
  Last Updated date: 2000-06-24 (yyyy-mm-dd) 
  --------------------------------------------------------------------------- 
 
 
              << LOOK COPYRIGHT, FEES & DISCLAIMER SECTION >> 
 
 
  -- Version 1.0.3 -- (2000-06-24) 
     - Support for my Expanding menu component system ( ignores items with 
       caption as '#' ) 
 
  -- Version 1.0.2 -- (2000-06-24) 
     - property BarVisible: Boolean 
 
  -- Version 1.0.1 -- (2000-06-23) 
     - Fixed font assignment AV using elipses (...) button 
 
  -- Version 1.0.0 -- (2000-06-22) 
 
    This is continued part of my article at www.delphi3000.com. Article 
    ID 1133, http://www.delphi3000.com/article.asp?id=1133 
 
    TBarPopupMenu component for Delphi 5. Be my guest and use it where-ever 
    you like, just mention my name and e-mail somewhere in your software. 
 
    Copy/Paste here, 
     "TBarPopupMenu, © Copyright 2000: Jouni Airaksinen, Mintus@Codefield.com" 
 
    There is demo program at Demo\ directory. 
 
    New properties in TBarPopupMenu: 
 
    property Bitmap: TBitmap 
      Bitmap to be positioned somewhere in the popupmenu (e.g. application 
      logo) 
 
    property BarWidth: Integer 
      Width of bar on the left side of popupmenu 
 
    property BarVisible: Boolean 
      Toggles bar visibility. If it's false, menu looks like normal menu. 
 
    property BitmapOffsetX: Integer 
    property BitmapOffsetY: Integer 
      Offset values to position bitmap 
 
    property BitmapVertAlignment: TBitmapVertAlignment 
    property BitmapHorzAlignment: TBitmapHorzAlignment 
      Alignment values to position bitmap, fine tune position with Offsets 
 
    property GradientEnd: TColor 
    property GradientStart: TColor 
      Gradient colors, if set to same color is drawn with one FillRect call 
 
    property Transparent: Boolean 
      Transparency of Bitmap 
 
    property VerticalFont: TFont 
    property VerticalText: string 
      Vertical text and it's font on the bar 
 
    property VerticalTextOffsetY: Integer 
      Offset for vertical text, normally you want it to be negative e.g. 
      defaults to -6. 
 
 
    Notes about root Items: 
 
      You can't assign (well you can, but events don't get executed) 
      OnMeasureItem nor OnAdvancedDrawItem events to root items. 
      Maybe I fix this later, if someone really needs those.. 
 
      Otherwise, you can use Actions and other stuff normally. 
 
  --------------------------------------------------------------------------- 
  Questions to, 
 
    Jouni Airaksinen, (programming: Mintus@Codefield.com; personal: virgin@sci.fi) 
    Bluecave Software, (http://www.bluecave.net/) 
    Codefield.com, (http://Codefield.com/) 
 
  == COPYRIGHT, FEES & DISCLAIMER ============================================ 
 
   YOU MUST include my name and e-mail address somewhere in your application; 
   Splash screen, About screen, readme file or something else. Place where 
   it's readable by normal user. 
 
  "TBarPopupMenu, © Copyright 2000: Jouni Airaksinen, Mintus@Codefield.com" 
 
 
   THIS CODE is free. Yes, free. I'm not going to ask any money from you. 
   Remember to mention my name and you are ready to go. You may use this 
   in your products which are: freeware, shareware, commericalware, 
   whateverware. 
 
   ------------------------------------------------------------------------- 
 
   COPYRIGHT remains with me. If you are going to use my code as base for 
   your own component(s), please ask permission first. I want to know how my 
   code is used. 
 
   IF YOU USE you agree to accept my changes into the component(s). Even if 
   it would cause work for you (please contact me if amount of work goes 
   into another millennium, maybe we can have peaceful some solution). 
 
   IF I DECIDE to make my component(s)/code commercial, it's my decision. 
   Also if I want to stop developing my component any further, I'm allowed 
   to do so (Most likely, if component(s)/code is commercial, I will develope 
   it as long as there is need for it). 
 
   FINANCIAL LOST caused by my component(s)/code (not including possible 
   registration fee of my component(s)/code) is not my headache. You cover 
   your loss. 
 
                                      Jouni Airaksinen (Mintus@Codefield.com) 
                                      Web: http://Codefield.com/home/mintus 
  =========================================================================== } 
 
Unit BarPopupMenu; 
 
interface 
 
uses 
  Windows, SysUtils, Classes, Graphics, Menus, Forms; 
 
const 
  BarSpace = 2; 
  ExpandChar = '#'; 
type 
  TBitmapVertAlignment = (bvaTop, bvaBottom, bvaMiddle); 
  TBitmapHorzAlignment = (bhaLeft, bhaRight, bhaCenter); 
 
  TBarPopupMenu = class(TPopupMenu) 
  private 
    { Private declarations } 
    FBitmap: TBitmap; 
    FBitmapOffsetX, 
    FBitmapOffsetY: Integer; 
    FBitmapVertAlignment: TBitmapVertAlignment; 
    FBitmapHorzAlignment: TBitmapHorzAlignment; 
 
    FclStart, 
    FclEnd: TColor; 
 
    FVerticalText: string; 
    FVerticalFont: TFont; 
{    FVerticalTextOffsetX,} 
    FVerticalTextOffsetY: Integer; 
    FBarWidth: Integer; 
    FBarVisible: Boolean; 
 
    PopupHeight: Integer; 
    Drawn: Boolean; 
  protected 
    { Protected declarations } 
    procedure ExpandItemWidth(Sender: TObject; ACanvas: TCanvas; var Width, 
      Height: Integer); 
    procedure AdvancedDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; 
      State: TOwnerDrawState); 
 
    procedure SetTransparent(Value: Boolean); 
    function GetTransparent: Boolean; 
 
    function GetBitmap: TBitmap; 
    procedure SetBitmap(Value: TBitmap); 
 
    procedure SetVerticalFont(Value: TFont); 
  public 
    { Public declarations } 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
 
    procedure Popup(X, Y: Integer); override; 
//    property OwnerDraw; 
  published 
    { Published declarations } 
    property Bitmap: TBitmap read GetBitmap write SetBitmap; 
    property BarWidth: Integer read FBarWidth write FBarWidth default 31; 
    property BitmapOffsetX: Integer read FBitmapOffsetX write FBitmapOffsetX default 0; 
    property BitmapOffsetY: Integer read FBitmapOffsetY write FBitmapOffsetY default 0; 
    property BitmapVertAlignment: TBitmapVertAlignment read FBitmapVertAlignment 
      write FBitmapVertAlignment default bvaBottom; 
    property BitmapHorzAlignment: TBitmapHorzAlignment read FBitmapHorzAlignment 
      write FBitmapHorzAlignment default bhaLeft; 
    property BarVisible: Boolean read FBarVisible write FBarVisible default True; 
    property GradientEnd: TColor read FclEnd write FclEnd default clBlack; 
    property GradientStart: TColor read FclStart write FclStart default clBlue; 
    property Transparent: Boolean read GetTransparent write SetTransparent default True; 
    property VerticalFont: TFont read FVerticalFont write SetVerticalFont; 
    property VerticalText: string read FVerticalText write FVerticalText; 
    property VerticalTextOffsetY: Integer read FVerticalTextOffsetY 
      write FVerticalTextOffsetY default -6; 
  end; 
 
procedure Register; 
 
implementation 
 
procedure Register; 
begin 
  RegisterComponents('XP', [TBarPopupMenu]); 
end; 
 
constructor TBarPopupMenu.Create(AOwner: TComponent); 
begin 
  inherited Create(AOwner); 
 
  OwnerDraw := True; 
 
  FBitmapOffsetX := 0; 
  FBitmapOffsetY := 0; 
  FBitmapVertAlignment := bvaBottom; 
  FBitmapHorzAlignment := bhaLeft; 
 
  FVerticalFont := TFont.Create; 
  with FVerticalFont do 
  begin 
    Name := 'Tahoma'; 
    Size := 14; 
    Color := clWhite; 
    Style := [fsBold, fsItalic]; 
  end; 
 
  FVerticalTextOffsetY := -6; 
 
  FclStart := clBlue; 
  FclEnd := clBlack; 
 
  FBarWidth := 31; 
 
  FBarVisible := True; 
 
  if (Application.Handle <> 0) then 
    FVerticalText := Application.Title; { some defaults } 
end; 
 
destructor TBarPopupMenu.Destroy; 
begin 
  FVerticalFont.Free; 
  if Assigned(FBitmap) then FBitmap.Free; 
  inherited Destroy; 
end; 
 
procedure TBarPopupMenu.SetTransparent(Value: Boolean); 
begin 
  if FBitmap = nil then Exit; 
  if (Value <> FBitmap.Transparent) then 
    FBitmap.Transparent := Value; 
end; 
 
function TBarPopupMenu.GetTransparent: Boolean; 
begin 
  if FBitmap = nil then 
    Result := False 
  else 
    Result := FBitmap.Transparent; 
end; 
 
procedure TBarPopupMenu.SetBitmap(Value: TBitmap); 
begin 
  if FBitmap = nil then 
  begin 
    FBitmap := TBitmap.Create; 
    FBitmap.Transparent := True; 
  end; 
{  if Value = nil then 
    FBitmap.Free 
  else} 
    FBitmap.Assign(Value); 
end; 
 
function TBarPopupMenu.GetBitmap: TBitmap; 
begin 
  if FBitmap = nil then 
  begin 
    FBitmap := TBitmap.Create; 
    FBitmap.Transparent := True; 
  end; 
  Result := FBitmap; 
end; 
 
procedure TBarPopupMenu.SetVerticalFont(Value: TFont); 
begin 
  FVerticalFont.Assign(Value); 
end; 
 
{ ============================================================================ 
  CreateRotatedFont 
  Date: 2000-06-22 
  Description: Creates rotated font, returns handle to it 
  Parameters: 
    F: TFont, where to copy styles 
    Angle: Integer, font angle 
  ---------------------------------------------------------------------------- } 
function CreateRotatedFont(F: TFont; Angle: Integer): hFont; 
var 
  LF : TLogFont; 
begin 
  FillChar(LF, SizeOf(LF), #0); 
  with LF do 
  begin 
    lfHeight := F.Height; 
    lfWidth := 0; 
    lfEscapement := Angle*10; 
    lfOrientation := 0; 
    if fsBold in F.Style then 
      lfWeight := FW_BOLD 
    else 
      lfWeight := FW_NORMAL; 
    lfItalic := Byte(fsItalic in F.Style); 
    lfUnderline := Byte(fsUnderline in F.Style); 
    lfStrikeOut := Byte(fsStrikeOut in F.Style); 
    lfCharSet := DEFAULT_CHARSET; 
    StrPCopy(lfFaceName, F.Name); 
    lfQuality := DEFAULT_QUALITY; 
 
    lfOutPrecision := OUT_DEFAULT_PRECIS; 
    lfClipPrecision := CLIP_DEFAULT_PRECIS; 
    case F.Pitch of 
      fpVariable: lfPitchAndFamily := VARIABLE_PITCH; 
      fpFixed: lfPitchAndFamily := FIXED_PITCH; 
    else 
      lfPitchAndFamily := DEFAULT_PITCH; 
    end; 
  end; 
  Result := CreateFontIndirect(LF); 
end; 
{ ============================================================================ 
  TBarPopupMenu.Popup 
  Date: 2000-06-22 
  Description: 
    Set initial events for popup items. Currently just overwrites old events.. 
  ---------------------------------------------------------------------------- } 
procedure TBarPopupMenu.Popup(X, Y: Integer); 
var i: Integer; 
begin 
  PopupHeight := 0; 
  Drawn := False; 
 
  if (Items.Count > 0) then 
    for i := 0 to Items.Count-1 do 
    begin 
      Items[i].OnMeasureItem := ExpandItemWidth; 
      Items[i].OnAdvancedDrawItem := AdvancedDrawItem; 
    end; 
 
  inherited Popup(X, Y); 
end; 
 
procedure TBarPopupMenu.ExpandItemWidth(Sender: TObject; 
  ACanvas: TCanvas; var Width, Height: Integer); 
var 
  MenuItem: TMenuItem; 
begin 
  if not FBarVisible then Exit; 
 
  Width := Width + FBarWidth+10; { make space for graphical bar } 
 
  MenuItem := TMenuItem(Sender); 
 
  if MenuItem.Visible and (MenuItem.Caption <> ExpandChar) then 
    PopupHeight := PopupHeight + Height; 
end; 
 
procedure TBarPopupMenu.AdvancedDrawItem(Sender: TObject; 
  ACanvas: TCanvas; ARect: TRect; State: TOwnerDrawState); 
var 
  i, itmp, x, y: Integer; 
  r: TRect; 
  rc1, rc2, gc1, gc2, bc1, bc2: Byte; 
  ColorStart, ColorEnd: Longint; 
  OnAdvancedDrawItem: TAdvancedMenuDrawItemEvent; 
  MenuItem: TMenuItem; 
begin 
  MenuItem := TMenuItem(Sender); 
 
  if MenuItem.Caption = ExpandChar then Exit; { it has been already drawn } 
 
  { we need to remove draw event so DrawMenuItem won't generate infinite loop! 
    (Recursive) } 
  OnAdvancedDrawItem := MenuItem.OnAdvancedDrawItem; 
  MenuItem.OnAdvancedDrawItem := nil; 
 
  { align rect where item is draw so that vcl will leave bar for us } 
  r := ARect; 
  if FBarVisible then 
  begin 
    r.Right := r.Right - FBarWidth; { remove bar width } 
    OffsetRect(r, FBarWidth, 0); 
  end; 
 
  { draw item and restore event back } 
  DrawMenuItem(MenuItem, ACanvas, r, State); 
  MenuItem.OnAdvancedDrawItem := OnAdvancedDrawItem; 
 
  if not FBarVisible then Exit; 
 
  if not Drawn then 
  begin 
    ACanvas.Brush.Style := bsSolid; 
    if (FclStart = FclEnd) then { same color, just one fillrect required } 
      begin 
        ACanvas.Brush.Color := FclStart; 
        ACanvas.FillRect(Rect(0, ARect.Top, FBarWidth - BarSpace, ARect.Bottom{ + 1})); 
      end 
    else { draw smooth gradient bar part for this item } 
    begin 
      ColorStart := ColorToRGB(FclStart); 
      ColorEnd := ColorToRGB(FclEnd); 
 
      rc1 := GetRValue(ColorStart); 
      gc1 := GetGValue(ColorStart); 
      bc1 := GetBValue(ColorStart); 
      rc2 := GetRValue(ColorEnd); 
      gc2 := GetGValue(ColorEnd); 
      bc2 := GetBValue(ColorEnd); 
 
      for i := 0 to (ARect.Bottom - ARect.Top) do 
      begin 
        ACanvas.Brush.Color := RGB( 
          (rc1 + (((rc2 - rc1) * (ARect.Top + i)) div PopupHeight)), 
          (gc1 + (((gc2 - gc1) * (ARect.Top + i)) div PopupHeight)), 
          (bc1 + (((bc2 - bc1) * (ARect.Top + i)) div PopupHeight))); 
        ACanvas.FillRect(Rect(0, ARect.Top + i, FBarWidth - BarSpace, ARect.Top + i + 1)); 
      end; 
    end; 
 
    { vertical text to gradient bar } 
    with ACanvas.Font do 
    begin 
      Assign(FVerticalFont); 
 
      itmp := Handle; { store old } 
      Handle := CreateRotatedFont(ACanvas.Font, 90); 
 
      x := Round((FBarWidth - ACanvas.TextHeight('X')) / 2 - 0.5); { gives much better centering } 
//      x := (FBarWidth - ACanvas.TextHeight('X')) div 2; 
    end; 
 
    ACanvas.Brush.Style := bsClear; 
 
    r := Rect(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom + 1); 
 
    y := PopupHeight + FVerticalTextOffsetY; 
    if Assigned(FBitmap) and (FBitmapVertAlignment = bvaBottom) then 
      y := y - FBitmap.Height; 
 
    ExtTextOut(ACanvas.Handle, x - 1, y, ETO_CLIPPED, 
      @r, PChar(VerticalText), Length(VerticalText), nil); 
 
    { delete created font and restore old handle } 
    DeleteObject(ACanvas.Font.Handle); 
    ACanvas.Font.Handle := itmp; 
 
    if PopupHeight = ARect.Bottom {MenuItem = MenuItem.Parent.Items[MenuItem.Parent.Count-1]} then 
      begin 
        Drawn := True; 
 
        { draw bitmap } 
        if Assigned(FBitmap) then 
        begin 
          y := 0; x := 0; 
          case FBitmapVertAlignment of 
            bvaTop:    y := FBitmapOffsetY; 
            bvaBottom: y := PopupHeight + FBitmapOffsetY - FBitmap.Height; 
            bvaMiddle: y := ((PopupHeight - Fbitmap.Height) div 2) + FBitmapOffsetY; 
          end; 
 
          case FBitmapHorzAlignment of 
            bhaLeft:   x := FBitmapOffsetX; 
            bhaRight:  x := (FBarWidth - BarSpace) + FBitmapOffsetX - FBitmap.Width; 
            bhaCenter: x := ((FBarWidth - BarSpace - FBitmap.Width) div 2) + FBitmapOffsetX; 
          end; 
 
          ACanvas.Draw(x, y, FBitmap); 
        end; 
      end; 
  end; 
end; 
 
end.