{ TToolButton } constructor TToolButton.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle := [csCaptureMouse, csSetCaption, csClickEvents]; Width := 23; Height := 22; FImageIndex := -1; FStyle := tbsButton; end; procedure TToolButton.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin if (Style = tbsDropDown) and (Button = mbLeft) and Enabled then Down := not Down; inherited MouseDown(Button, Shift, X, Y); end; procedure TToolButton.MouseMove(Shift: TShiftState; X, Y: Integer); begin inherited MouseMove(Shift, X, Y); if (Style = tbsDropDown) and MouseCapture then Down := (X >= 0) and (X < ClientWidth) and (Y >= 0) and (Y <= ClientHeight); end; procedure TToolButton.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin inherited MouseUp(Button, Shift, X, Y); if (Button = mbLeft) and (X >= 0) and (X < ClientWidth) and (Y >= 0) and (Y <= ClientHeight) then if Style = tbsDropDown then Down := False; end; procedure TToolButton.Click; begin inherited Click; end; procedure TToolButton.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if Operation = opRemove then begin if AComponent = DropdownMenu then DropdownMenu := nil else if AComponent = MenuItem then MenuItem := nil; end; end; procedure TToolButton.CMTextChanged(var Message: TMessage); begin inherited; UpdateControl; if not (csLoading in ComponentState) and (FToolBar <> nil) and FToolBar.ShowCaptions then begin FToolBar.FButtonWidth := 0; FToolBar.FButtonHeight := 0; FToolBar.RecreateButtons; end; end; procedure TToolButton.SetBounds(ALeft, ATop, AWidth, AHeight: Integer); var Pos: Integer; Reordered, NeedsUpdate: Boolean; ResizeWidth, ResizeHeight: Boolean; begin if ((ALeft <> Left) or (ATop <> Top) or (AWidth <> Width) or (AHeight <> Height)) and (FUpdateCount = 0) and not (csLoading in ComponentState) and (FToolBar <> nil) then begin Pos := Index; Reordered := FToolBar.ReorderButton(Pos, ALeft, ATop) <> Pos; if Reordered then begin NeedsUpdate := False; if Index < Pos then Pos := Index end else begin NeedsUpdate := (Style in [tbsSeparator, tbsDivider]) and (AWidth <> Width); Reordered := NeedsUpdate; end; if (Style = tbsDropDown) and ((GetComCtlVersion >= ComCtlVersionIE4) or { IE3 doesn't display drop-down arrows } not FToolBar.Flat) then AWidth := FToolBar.ButtonWidth + AWidth - Width; ResizeWidth := not (Style in [tbsSeparator, tbsDivider]) and (AWidth <> FToolBar.ButtonWidth); ResizeHeight := AHeight <> FToolBar.ButtonHeight; if NeedsUpdate then inherited SetBounds(ALeft, ATop, AWidth, AHeight); if csDesigning in ComponentState then begin if ResizeWidth then FToolBar.ButtonWidth := AWidth; if ResizeHeight then FToolBar.ButtonHeight := AHeight; end; if Reordered and not ResizeWidth and not ResizeHeight then begin if NeedsUpdate then if Style in [tbsSeparator, tbsDivider] then FToolBar.RefreshButton(Pos) else FToolBar.UpdateButton(Pos); FToolBar.ResizeButtons; FToolBar.RepositionButtons(0); end else FToolBar.RepositionButton(Pos); end else inherited SetBounds(ALeft, ATop, AWidth, AHeight); end; procedure TToolButton.Paint; const XorColor = $00FFD8CE; DropDownWidth = 14; var R: TRect; begin if FToolBar = nil then Exit; if (Style = tbsDropDown) and not FToolbar.Flat and not FToolBar.FMenuDropped and (GetComCtlVersion = ComCtlVersionIE5) then with Canvas do begin if not Down then begin R := Rect(Width - DropDownWidth, 1, Width, Height); DrawEdge(Handle, R, BDR_RAISEDOUTER, BF_TOP or BF_RIGHT or BF_BOTTOM); R.Top := 0; DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT); end else begin R := Rect(Width - DropDownWidth + 1, -1, Width, Height); DrawEdge(Handle, R, BDR_SUNKEN, BF_TOP or BF_RIGHT or BF_BOTTOM); DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT); end; end; if Style = tbsDivider then with Canvas do begin R := Rect(Width div 2 - 1, 0, Width, Height); DrawEdge(Handle, R, EDGE_ETCHED, BF_LEFT) end; if csDesigning in ComponentState then { Draw separator outline } if Style in [tbsSeparator, tbsDivider] then with Canvas do begin Pen.Style := psDot; Pen.Mode := pmXor; Pen.Color := XorColor; Brush.Style := bsClear; Rectangle(0, 0, ClientWidth, ClientHeight); end { Draw Flat button face } else if FToolBar.Flat and not Down then with Canvas do begin R := Rect(0, 0, Width, Height); DrawEdge(Handle, R, BDR_RAISEDINNER, BF_RECT); end; end; function TToolButton.GetButtonState: Byte; begin Result := 0; if FDown then if Style = tbsCheck then Result := Result or ButtonStates[tbsChecked] else Result := Result or ButtonStates[tbsPressed]; if Enabled and ((FToolBar = nil) or FToolBar.Enabled) then Result := Result or ButtonStates[tbsEnabled]; if not Visible and not (csDesigning in ComponentState) then Result := Result or ButtonStates[tbsHidden]; if FIndeterminate then Result := Result or ButtonStates[tbsIndeterminate]; if FWrap then Result := Result or ButtonStates[tbsWrap]; if FMarked then Result := Result or ButtonStates[tbsMarked]; end; procedure TToolButton.SetAutoSize(Value: Boolean); begin if Value <> AutoSize then begin FAutoSize := Value; UpdateControl; if not (csLoading in ComponentState) and (FToolBar <> nil) and FToolBar.ShowCaptions then begin FToolBar.FButtonWidth := 0; FToolBar.FButtonHeight := 0; FToolBar.RecreateButtons; end; end; end; procedure TToolButton.SetButtonState(State: Byte); begin FDown := State and (TBSTATE_CHECKED or TBSTATE_PRESSED) <> 0; Enabled := State and TBSTATE_ENABLED <> 0; if not (csDesigning in ComponentState) then Visible := State and TBSTATE_HIDDEN = 0; FIndeterminate := not FDown and (State and TBSTATE_INDETERMINATE <> 0); FWrap := State and TBSTATE_WRAP <> 0; FMarked := State and TBSTATE_MARKED <> 0; end; procedure TToolButton.SetToolBar(AToolBar: TToolBar); begin if FToolBar <> AToolBar then begin if FToolBar <> nil then FToolBar.RemoveButton(Self); Parent := AToolBar; if AToolBar <> nil then AToolBar.InsertButton(Self); end; end; procedure TToolButton.CMVisibleChanged(var Message: TMessage); var Button: TTBButton; begin if not (csDesigning in ComponentState) and (FToolBar <> nil) then begin if FToolBar <> nil then with FToolBar do begin if Perform(TB_GETBUTTON, Index, Longint(@Button)) <> 0 then Perform(TB_HIDEBUTTON, Button.idCommand, MakeLong(Ord(not Self.Visible), 0)); { Force a resize to occur } if AutoSize then AdjustSize; end; UpdateControl; FToolBar.RepositionButtons(Index); end; end; procedure TToolButton.CMEnabledChanged(var Message: TMessage); begin if FToolBar <> nil then FToolBar.Perform(TB_ENABLEBUTTON, Index, Ord(Enabled)); end; procedure TToolButton.CMHitTest(var Message: TCMHitTest); begin Message.Result := Ord(not (Style in [tbsDivider, tbsSeparator]) or (DragKind = dkDock)); end; procedure TToolButton.SetDown(Value: Boolean); const DownMessage: array[Boolean] of Integer = (TB_PRESSBUTTON, TB_CHECKBUTTON); begin if Value <> FDown then begin FDown := Value; if FToolBar <> nil then begin FToolBar.Perform(DownMessage[Style = tbsCheck], Index, MakeLong(Ord(Value), 0)); FToolBar.UpdateButtonStates; end; end; end; procedure TToolButton.SetDropdownMenu(Value: TPopupMenu); begin if Value <> FDropdownMenu then begin FDropdownMenu := Value; if Value <> nil then Value.FreeNotification(Self); end; end; procedure TToolButton.SetGrouped(Value: Boolean); begin if FGrouped <> Value then begin FGrouped := Value; UpdateControl; end; end; procedure TToolButton.SetImageIndex(Value: TImageIndex); begin if FImageIndex <> Value then begin FImageIndex := Value; if FToolBar <> nil then begin RefreshControl; FToolBar.Perform(TB_CHANGEBITMAP, Index, Value); if FToolBar.Transparent or FToolBar.Flat then Invalidate; end; end; end; procedure TToolButton.SetMarked(Value: Boolean); begin if FMarked <> Value then begin FMarked := Value; if FToolBar <> nil then FToolBar.Perform(TB_MARKBUTTON, Index, Longint(Ord(Value))); end; end; procedure TToolButton.SetIndeterminate(Value: Boolean); begin if FIndeterminate <> Value then begin if Value then SetDown(False); FIndeterminate := Value; if FToolBar <> nil then FToolBar.Perform(TB_INDETERMINATE, Index, Longint(Ord(Value))); end; end; procedure TToolButton.SetMenuItem(Value: TMenuItem); begin { Copy all appropriate values from menu item } if Value <> nil then begin if FMenuItem <> Value then Value.FreeNotification(Self); Action := Value.Action; Caption := Value.Caption; Down := Value.Checked; Enabled := Value.Enabled; Hint := Value.Hint; ImageIndex := Value.ImageIndex; Visible := Value.Visible; end; FMenuItem := Value; end; procedure TToolButton.SetStyle(Value: TToolButtonStyle); begin if FStyle <> Value then begin FStyle := Value; Invalidate; if not (csLoading in ComponentState) and (FToolBar <> nil) then begin if FToolBar.ShowCaptions then begin FToolBar.FButtonWidth := 0; FToolBar.FButtonHeight := 0; FToolBar.RecreateButtons end else begin if Style in [tbsDivider, tbsSeparator] then RefreshControl else if Style = tbsDropDown then FToolbar.RecreateButtons else UpdateControl; FToolBar.ResizeButtons; FToolbar.RepositionButtons(Index); end; FToolBar.AdjustSize; end; end; end; procedure TToolButton.SetWrap(Value: Boolean); begin if FWrap <> Value then begin FWrap := Value; if FToolBar <> nil then RefreshControl; end; end; procedure TToolButton.BeginUpdate; begin Inc(FUpdateCount); end; procedure TToolButton.EndUpdate; begin Dec(FUpdateCount); end; function TToolButton.GetIndex: Integer; begin if FToolBar <> nil then Result := FToolBar.FButtons.IndexOf(Self) else Result := -1; end; function TToolButton.IsWidthStored: Boolean; begin Result := Style in [tbsSeparator, tbsDivider]; end; procedure TToolButton.RefreshControl; begin if (FToolBar <> nil) and FToolBar.RefreshButton(Index) then begin { R := BoundsRect; R.Left := 0; ValidateRect(FToolBar.Handle, @R); R.Bottom := R.Top; R.Top := 0; R.Right := FToolBar.ClientWidth; ValidateRect(FToolBar.Handle, @R);} end; end; procedure TToolButton.UpdateControl; begin if FToolBar <> nil then FToolBar.UpdateButton(Index); end; function TToolButton.CheckMenuDropdown: Boolean; begin Result := not (csDesigning in ComponentState) and ((DropdownMenu <> nil) and DropdownMenu.AutoPopup or (MenuItem <> nil)) and (FToolBar <> nil) and FToolBar.CheckMenuDropdown(Self); end; function TToolButton.IsCheckedStored: Boolean; begin Result := (ActionLink = nil) or not TToolButtonActionLink(ActionLink).IsCheckedLinked; end; function TToolButton.IsImageIndexStored: Boolean; begin Result := (ActionLink = nil) or not TToolButtonActionLink(ActionLink).IsImageIndexLinked; end; procedure TToolButton.ActionChange(Sender: TObject; CheckDefaults: Boolean); begin inherited ActionChange(Sender, CheckDefaults); if Sender is TCustomAction then with TCustomAction(Sender) do begin if not CheckDefaults or (Self.Down = False) then Self.Down := Checked; if not CheckDefaults or (Self.ImageIndex = -1) then Self.ImageIndex := ImageIndex; end; end; function TToolButton.GetActionLinkClass: TControlActionLinkClass; begin Result := TToolButtonActionLink; end; procedure TToolButton.AssignTo(Dest: TPersistent); begin inherited AssignTo(Dest); if Dest is TCustomAction then with TCustomAction(Dest) do begin Checked := Self.Down; ImageIndex := Self.ImageIndex; end; end; procedure TToolButton.ValidateContainer(AComponent: TComponent); var W: Integer; begin inherited ValidateContainer(AComponent); { Update non-stored Width and Height if inserting into TToolBar } if (csLoading in ComponentState) and (AComponent is TToolBar) then begin if Style in [tbsDivider, tbsSeparator] then W := Width else W := TToolBar(AComponent).ButtonWidth; SetBounds(Left, Top, W, TToolBar(AComponent).ButtonHeight); end; end; { TToolBar } constructor TToolBar.Create(AOwner: TComponent); begin inherited Create(AOwner); ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents, csDoubleClicks, csMenuEvents, csSetCaption]; Width := 150; Height := 29; Align := alTop; EdgeBorders := [ebTop]; FButtonWidth := 23; FButtonHeight := 22; FCustomizable := False; FCustomizing := False; FNewStyle := True; FWrapable := True; FButtons := TList.Create; FCanvas := TControlCanvas.Create; TControlCanvas(FCanvas).Control := Self; FImageChangeLink := TChangeLink.Create; FImageChangeLink.OnChange := ImageListChange; FDisabledImageChangeLink := TChangeLink.Create; FDisabledImageChangeLink.OnChange := DisabledImageListChange; FHotImageChangeLink := TChangeLink.Create; FHotImageChangeLink.OnChange := HotImageListChange; FNullBitmap := TBitmap.Create; with FNullBitmap do begin Width := 1; Height := 1; Canvas.Brush.Color := clBtnFace; Canvas.FillRect(Rect(0,0,1,1)); end; FloatingDockSiteClass := TToolDockForm; end; destructor TToolBar.Destroy; var I: Integer; begin FNullBitmap.Free; FHotImageChangeLink.Free; FDisabledImageChangeLink.Free; FImageChangeLink.Free; for I := 0 to FButtons.Count - 1 do if TControl(FButtons[I]) is TToolButton then TToolButton(FButtons[I]).FToolBar := nil; FButtons.Free; FCanvas.Free; inherited Destroy; end; procedure TToolBar.CreateParams(var Params: TCreateParams); const TBSTYLE_TRANSPARENT = $8000; // IE4 style DefaultStyles = CCS_NOPARENTALIGN or CCS_NOMOVEY or CCS_NORESIZE or CCS_NODIVIDER; ListStyles: array[Boolean] of DWORD = (0, TBSTYLE_LIST); FlatStyles: array[Boolean] of DWORD = (0, TBSTYLE_FLAT); TransparentStyles: array[Boolean] of DWORD = (0, TBSTYLE_TRANSPARENT); CustomizeStyles: array[Boolean] of DWORD = (0, CCS_ADJUSTABLE); begin FNewStyle := InitCommonControl(ICC_BAR_CLASSES); inherited CreateParams(Params); CreateSubClass(Params, TOOLBARCLASSNAME); with Params do begin Style := Style or DefaultStyles or FlatStyles[FFlat] or ListStyles[FList] or TransparentStyles[FTransparent] or CustomizeStyles[FCustomizable]; //! WINBUG: Without this style the toolbar has a two pixel margin above //! the buttons when ShowCaptions = True. if ShowCaptions then Style := Style xor TBSTYLE_TRANSPARENT;//! WindowClass.style := WindowClass.style and not (CS_HREDRAW or CS_VREDRAW); end; end; procedure TToolBar.CreateWnd; const { IE4 support } TB_SETEXTENDEDSTYLE = (WM_USER + 84); // For TBSTYLE_EX_* TB_GETEXTENDEDSTYLE = (WM_USER + 85); // For TBSTYLE_EX_* TBSTYLE_EX_DRAWDDARROWS = $0001; // IE4 toolbar style var DisplayDC: HDC; SaveFont, StockFont: HFONT; TxtMetric: TTextMetric; begin inherited CreateWnd; { Maintain backward compatibility with IE3 which always draws drop-down arrows for buttons in which Style = tbsDropDown. } if GetComCtlVersion >= ComCtlVersionIE4 then Perform(TB_SETEXTENDEDSTYLE, 0, Perform(TB_GETEXTENDEDSTYLE, 0, 0) or TBSTYLE_EX_DRAWDDARROWS); FOldHandle := 0; StockFont := GetStockObject(SYSTEM_FONT); if StockFont <> 0 then begin DisplayDC := GetDC(0); if (DisplayDC <> 0) then begin SaveFont := SelectObject(DisplayDC, StockFont); if (GetTextMetrics(DisplayDC, TxtMetric)) then with TxtMetric do FHeightMargin := tmHeight - tmInternalLeading - tmExternalLeading + 1; SelectObject(DisplayDC, SaveFont); ReleaseDC(0, DisplayDC); end; end; RecreateButtons; Invalidate; end; procedure TToolBar.CreateButtons(NewWidth, NewHeight: Integer); function ToolButtonVisible: Boolean; var I: Integer; Control: TControl; begin for I := 0 to FButtons.Count - 1 do begin Control := TControl(FButtons[I]); if (Control is TToolButton) and ((csDesigning in ComponentState) or Control.Visible) and not (TToolButton(Control).Style in [tbsSeparator, tbsDivider]) then begin Result := True; Exit; end; end; Result := False; end; var ImageWidth, ImageHeight: Integer; I: Integer; begin BeginUpdate; try HandleNeeded; Perform(TB_BUTTONSTRUCTSIZE, SizeOf(TTBButton), 0); Perform(TB_SETINDENT, FIndent, 0); if FImages <> nil then begin ImageWidth := 14; //искать //! WINBUG: Without this style the toolbar has a two pixel margin above //Common control requries at least a space is used when showing button ImageHeight := FImages.Height; end else if FDisabledImages <> nil then begin ImageWidth := FDisabledImages.Width; ImageHeight := FDisabledImages.Height; end else if FHotImages <> nil then begin ImageWidth := FHotImages.Width; ImageHeight := FHotImages.Height; end else begin ImageWidth := 0; ImageHeight := 0; end; Perform(TB_SETBITMAPSIZE, 0, MakeLParam(ImageWidth, ImageHeight)); { Adjust the working height if there is a visible TToolButton whose caption height is automatically added by the common control. } // if ShowCaptions and ToolButtonVisible then Dec(NewHeight, FHeightMargin); { Prevent toolbar from setting default button size } if NewWidth <= 0 then NewWidth := 1; if NewHeight <= 0 then NewHeight := 1; Perform(TB_SETBUTTONSIZE, 0, MakeLParam(NewWidth, NewHeight)); FButtonWidth := NewWidth; FButtonHeight := NewHeight; finally EndUpdate; end; { Retrieve current button sizes } for I := 0 to InternalButtonCount - 1 do Perform(TB_DELETEBUTTON, 0, 0); UpdateButtons; UpdateImages; GetButtonSize(FButtonWidth, FButtonHeight); end; procedure TToolBar.RepositionButton(Index: Integer); var TBButton: TTBButton; Button: TControl; R: TRect; AdjustY: Integer; begin if (csLoading in ComponentState) or (Perform(TB_GETBUTTON, Index, Longint(@TBButton)) = 0) then Exit; if Perform(TB_GETITEMRECT, Index, Longint(@R)) <> 0 then begin Button := TControl(TBButton.dwData); if Button is TToolButton then TToolButton(Button).BeginUpdate; try if not (Button is TToolButton) then with Button do begin if Button is TWinControl then HandleNeeded; { Check for a control that doesn't size and center it } BoundsRect := R; if Height < R.Bottom - R.Top then begin AdjustY := (R.Bottom - R.Top - Height) div 2; SetBounds(R.Left, R.Top + AdjustY, R.Right - R.Left, Height); end; end else Button.BoundsRect := R; finally if Button is TToolButton then TToolButton(Button).EndUpdate; end; end; end; procedure TToolBar.RepositionButtons(Index: Integer); var I: Integer; begin if (csLoading in ComponentState) or (FUpdateCount > 0) then Exit; BeginUpdate; try for I := InternalButtonCount - 1 downto Index do RepositionButton(I); finally EndUpdate; end; end; procedure TToolBar.GetButtonSize(var AWidth, AHeight: Integer); var LastIndex: Integer; R: TRect; TBButton: TTBButton; begin if HandleAllocated then begin if GetComCtlVersion >= ComCtlVersionIE3 then begin LastIndex := Perform(TB_GETBUTTONSIZE, 0, 0); AHeight := LastIndex shr 16; AWidth := LastIndex and $FFFF; end else begin LastIndex := InternalButtonCount - 1; if LastIndex < 0 then Exit; while (LastIndex >= 0) and (Perform(TB_GETBUTTON, LastIndex, Integer(@TBButton)) <> 0) and (TBButton.fsStyle and TBSTYLE_SEP <> 0) do Dec(LastIndex); if LastIndex < 0 then begin if Perform(TB_GETITEMRECT, 0, Longint(@R)) <> 0 then AHeight := R.Bottom - R.Top; Exit; end; if Perform(TB_GETITEMRECT, LastIndex, Longint(@R)) <> 0 then begin AHeight := R.Bottom - R.Top; AWidth := R.Right - R.Left; end; end; end; end; procedure TToolBar.SetButtonHeight(Value: Integer); begin if Value <> FButtonHeight then begin FButtonHeight := Value; RecreateButtons; end; end; procedure TToolBar.SetButtonWidth(Value: Integer); begin if Value <> FButtonWidth then begin FButtonWidth := Value; RecreateButtons; end; end; procedure TToolBar.InsertButton(Control: TControl); var FromIndex, ToIndex: Integer; begin if Control is TToolButton then TToolButton(Control).FToolBar := Self; if not (csLoading in Control.ComponentState) then begin FromIndex := FButtons.IndexOf(Control); if FromIndex >= 0 then ToIndex := ReorderButton(Fromindex, Control.Left, Control.Top) else begin ToIndex := ButtonIndex(FromIndex, Control.Left, Control.Top); FButtons.Insert(ToIndex, Control); UpdateItem(TB_INSERTBUTTON, ToIndex, ToIndex); end; end else begin ToIndex := FButtons.Add(Control); UpdateButton(ToIndex); end; if Wrapable then RepositionButtons(0) else RepositionButtons(ToIndex); RecreateButtons; end; procedure TToolBar.RemoveButton(Control: TControl); var I, Pos: Integer; begin I := FButtons.IndexOf(Control); if I >= 0 then begin if Control is TToolButton then TToolButton(Control).FToolBar := nil; Pos := FButtons.Remove(Control); if FCustomizing and not FRestoring then Exit; Perform(TB_DELETEBUTTON, Pos, 0); ResizeButtons; if Wrapable then RepositionButtons(0) else RepositionButtons(Pos); RecreateButtons; end; end; function TToolBar.UpdateItem(Message, FromIndex, ToIndex: Integer): Boolean; var Control: TControl; Button: TTBButton; CaptionText: string; Buffer: array[0..4095] of Char; begin Control := TControl(FButtons[FromIndex]); if Control is TToolButton then with TToolButton(Control) do begin FillChar(Button, SizeOf(Button), 0); if Style in [tbsSeparator, tbsDivider] then begin Button.iBitmap := Width; Button.idCommand := FromIndex; end else begin if ImageIndex < 0 then Button.iBitmap := -2 else Button.iBitmap := ImageIndex; Button.idCommand := FromIndex; end; with Button do begin fsStyle := ButtonStyles[Style]; if AutoSize and (GetComCtlVersion >= ComCtlVersionIE4) then fsStyle := fsStyle or TBSTYLE_AUTOSIZE; end; Button.fsState := GetButtonState; if FGrouped then Button.fsStyle := Button.fsStyle or TBSTYLE_GROUP; Button.dwData := Longint(Control); if ShowCaptions then begin if Caption <> '' then CaptionText := Caption else { Common control requries at least a space is used when showing button captions. If any one button's caption is empty (-1) then none of the buttons' captions will not be displayed. } //CaptionText := ' '; CaptionText := ''; StrPCopy(Buffer, CaptionText); { TB_ADDSTRING requires two null terminators } Buffer[Length(CaptionText) + 1] := #0; Button.iString := Self.Perform(TB_ADDSTRING, 0, Longint(@Buffer)); end else Button.iString := -1; end else begin FillChar(Button, SizeOf(Button), 0); Button.fsStyle := ButtonStyles[tbsSeparator]; Button.iBitmap := Control.Width; Button.idCommand := -1; if not Control.Visible and not (csDesigning in Control.ComponentState) then Button.fsState := Button.fsState or ButtonStates[tbsHidden]; Button.dwData := Longint(Control); Button.iString := -1; end; Result := Self.Perform(Message, ToIndex, Integer(@Button)) <> 0; end; function TToolBar.UpdateItem2(Message, FromIndex, ToIndex: Integer): Boolean; var Control: TControl; Button: TTBButtonInfo; CaptionText: string; Buffer: array[0..4095] of Char; begin Control := TControl(FButtons[FromIndex]); FillChar(Button, SizeOf(Button), 0); Button.cbSize := SizeOf(Button); if Control is TToolButton then with TToolButton(Control) do begin Button.dwMask := TBIF_STATE or TBIF_STYLE or TBIF_LPARAM or TBIF_COMMAND or TBIF_SIZE; if Style in [tbsSeparator, tbsDivider] then begin Button.idCommand := FromIndex; end else begin Button.dwMask := Button.dwMask or TBIF_IMAGE; if ImageIndex < 0 then Button.iImage := -2 else Button.iImage := ImageIndex; Button.idCommand := FromIndex; end; with Button do begin cx := Width; fsStyle := ButtonStyles[Style]; if AutoSize then fsStyle := fsStyle or TBSTYLE_AUTOSIZE; if Grouped then Button.fsStyle := Button.fsStyle or TBSTYLE_GROUP; end; Button.fsState := GetButtonState; Button.lParam := Longint(Control); if ShowCaptions then begin if Caption <> '' then CaptionText := Caption else { Common control requries at least a space is used when showing button captions. If any one button's caption is empty (-1) then none of the buttons' captions will not be displayed. } CaptionText := ' '; StrPCopy(Buffer, CaptionText); { TB_ADDSTRING requires two null terminators } Buffer[Length(CaptionText) + 1] := #0; //Button.iString := Self.Perform(TB_ADDSTRING, 0, Longint(@Buffer)); Button.pszText := Buffer; Button.cchText := Length(CaptionText); Button.dwMask := Button.dwMask or TBIF_TEXT; end else begin Button.pszText := nil; Button.cchText := 0; end; { if Style in [tbsSeparator, tbsDivider] then begin with Button do begin if Visible then begin dwMask := TBIF_STYLE or TBIF_STATE or TBIF_LPARAM; fsState := TBSTATE_ENABLED or TBSTATE_WRAP; fsStyle := TBSTYLE_BUTTON; end; end; end;} end else begin Button.dwMask := TBIF_TEXT or TBIF_STATE or TBIF_STYLE or TBIF_LPARAM or TBIF_COMMAND or TBIF_SIZE; Button.fsStyle := ButtonStyles[tbsSeparator]; Button.cx := Control.Width; Button.idCommand := -1; Button.lParam := Longint(Control); Button.pszText := nil; Button.cchText := 0; end; Result := Self.Perform(Message, ToIndex, Integer(@Button)) <> 0; end; function TToolBar.RefreshButton(Index: Integer): Boolean; var Style: Longint; begin if not (csLoading in ComponentState) and (FUpdateCount = 0) then begin BeginUpdate; try Style := GetWindowLong(Handle, GWL_STYLE); SetWindowLong(Handle, GWL_STYLE, Style and not WS_VISIBLE); try Result := (Index < InternalButtonCount) and UpdateItem(TB_DELETEBUTTON, Index, Index) and UpdateItem(TB_INSERTBUTTON, Index, Index); finally SetWindowLong(Handle, GWL_STYLE, Style); end; finally EndUpdate; end; end else Result := False; end; procedure TToolBar.UpdateButton(Index: Integer); var Style: Longint; begin if (csLoading in ComponentState) or (FUpdateCount > 0) then Exit; BeginUpdate; try HandleNeeded; Style := GetWindowLong(Handle, GWL_STYLE); SetWindowLong(Handle, GWL_STYLE, Style and not WS_VISIBLE); try if Index < InternalButtonCount then UpdateItem2(TB_SETBUTTONINFO, Index, Index) else UpdateItem(TB_INSERTBUTTON, Index, Index); finally SetWindowLong(Handle, GWL_STYLE, Style); end; finally EndUpdate; end; end; procedure TToolBar.UpdateButtons; const BlankButton: TTBButton = (iBitmap: 0; idCommand: 0; fsState: 0; fsStyle: TBSTYLE_BUTTON; dwData: 0; iString: 0); var I: Integer; Count: Integer; Style: Longint; begin BeginUpdate; try HandleNeeded; Style := GetWindowLong(Handle, GWL_STYLE); SetWindowLong(Handle, GWL_STYLE, Style and not WS_VISIBLE); try Count := InternalButtonCount; for I := 0 to FButtons.Count - 1 do begin if I < Count then UpdateItem2(TB_SETBUTTONINFO, I, I) else UpdateItem(TB_INSERTBUTTON, I, I); end; finally SetWindowLong(Handle, GWL_STYLE, Style); end; finally EndUpdate; end; RepositionButtons(0); end; procedure TToolBar.UpdateButtonState(Index: Integer); var TBButton: TTBButton; begin if (Perform(TB_GETBUTTON, Index, Integer(@TBButton)) <> 0) then with TToolButton(TBButton.dwData) do begin SetButtonState(TBButton.fsState); Self.Perform(TB_SETSTATE, Index, MakeLong(GetButtonState, 0)); end; end; procedure TToolBar.UpdateButtonStates; var I: Integer; begin for I := 0 to FButtons.Count - 1 do if TControl(FButtons[I]) is TToolButton then UpdateButtonState(I); end; procedure TToolBar.SetShowCaptions(Value: Boolean); begin if FShowCaptions <> Value then begin FShowCaptions := Value; if not (csLoading in ComponentState) then RecreateWnd; AdjustSize; end; end; function TToolBar.GetButton(Index: Integer): TToolButton; begin Result := FButtons[Index]; end; function TToolBar.GetButtonCount: Integer; begin Result := FButtons.Count; end; function TToolBar.GetRowCount: Integer; begin Result := Perform(TB_GETROWS, 0, 0); end; procedure TToolBar.SetList(Value: Boolean); begin if FList <> Value then begin FList := Value; RecreateWnd; end; end; procedure TToolBar.SetFlat(Value: Boolean); begin if FFlat <> Value then begin FFlat := Value; RecreateWnd; end; end; procedure TToolBar.SetTransparent(Value: Boolean); begin if FTransparent <> Value then begin FTransparent := Value; RecreateWnd; end; end; procedure TToolBar.SetWrapable(Value: Boolean); begin if FWrapable <> Value then begin FWrapable := Value; if AutoSize then AdjustSize; end; end; procedure TToolBar.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if Operation = opRemove then begin if AComponent = FImages then Images := nil; if AComponent = FHotImages then HotImages := nil; if AComponent = FDisabledImages then DisabledImages := nil; if AComponent = FMenu then Menu := nil; end; end; procedure TToolBar.LoadImages(AImages: TCustomImageList); var AddBitmap: TTBAddBitmap; ReplaceBitmap: TTBReplaceBitmap; NewHandle: HBITMAP; function GetImageBitmap(ImageList: TCustomImageList): HBITMAP; var I: Integer; Bitmap: TBitmap; R: TRect; begin Bitmap := TBitmap.Create; try Bitmap.Width := ImageList.Width * ImageList.Count; Bitmap.Height := ImageList.Height; R := Rect(0,0,Width,Height); with Bitmap.Canvas do begin Brush.Color := clBtnFace; FillRect(R); end; for I := 0 to ImageList.Count - 1 do ImageList_Draw(ImageList.Handle, I, Bitmap.Canvas.Handle, I * ImageList.Width, 0, ILD_TRANSPARENT); Result := Bitmap.ReleaseHandle; finally Bitmap.Free; end; end; begin if AImages <> nil then NewHandle := GetImageBitmap(AImages) else with TBitmap.Create do try Assign(FNullBitmap); NewHandle := ReleaseHandle; finally Free; end; if FOldHandle = 0 then begin AddBitmap.hInst := 0; AddBitmap.nID := NewHandle; Perform(TB_ADDBITMAP, ButtonCount, Longint(@AddBitmap)); end else begin with ReplaceBitmap do begin hInstOld := 0; nIDOld := FOldHandle; hInstNew := 0; nIDNew := NewHandle; nButtons := ButtonCount; end; Perform(TB_REPLACEBITMAP, 0, Longint(@ReplaceBitmap)); if FOldHandle <> 0 then DeleteObject(FOldHandle); end; FOldHandle := NewHandle; end; procedure TToolBar.UpdateImages; begin if FNewStyle then begin if FImages <> nil then SetImageList(FImages.Handle); if FDisabledImages <> nil then SetDisabledImageList(FDisabledImages.Handle); if FHotImages <> nil then SetHotImageList(FHotImages.Handle); end else if HandleAllocated then LoadImages(FImages); end; procedure TToolBar.ImageListChange(Sender: TObject); begin if HandleAllocated and (Sender = Images) then RecreateButtons; end; procedure TToolBar.SetImageList(Value: HImageList); begin if HandleAllocated then Perform(TB_SETIMAGELIST, 0, Value); Invalidate; end; procedure TToolBar.SetImages(Value: TCustomImageList); begin if FImages <> nil then FImages.UnRegisterChanges(FImageChangeLink); FImages := Value; if FImages <> nil then begin FImages.RegisterChanges(FImageChangeLink); FImages.FreeNotification(Self); end else SetImageList(0); RecreateButtons; end; procedure TToolBar.DisabledImageListChange(Sender: TObject); begin if HandleAllocated and (Sender = DisabledImages) then RecreateButtons; end; procedure TToolBar.SetDisabledImageList(Value: HImageList); begin if HandleAllocated then Perform(TB_SETDISABLEDIMAGELIST, 0, Value); Invalidate; end; procedure TToolBar.SetDisabledImages(Value: TCustomImageList); begin if FDisabledImages <> nil then FDisabledImages.UnRegisterChanges(FDisabledImageChangeLink); FDisabledImages := Value; if FDisabledImages <> nil then begin FDisabledImages.RegisterChanges(FDisabledImageChangeLink); FDisabledImages.FreeNotification(Self); end else SetDisabledImageList(0); RecreateButtons; end; procedure TToolBar.HotImageListChange(Sender: TObject); begin if HandleAllocated and (Sender = HotImages) then RecreateButtons; end; procedure TToolBar.SetHotImageList(Value: HImageList); begin if HandleAllocated then Perform(TB_SETHOTIMAGELIST, 0, Value); Invalidate; end; procedure TToolBar.SetHotImages(Value: TCustomImageList); begin if FHotImages <> nil then FHotImages.UnRegisterChanges(FHotImageChangeLink); FHotImages := Value; if FHotImages <> nil then begin FHotImages.RegisterChanges(FHotImageChangeLink); FHotImages.FreeNotification(Self); end else SetHotImageList(0); RecreateButtons; end; procedure TToolBar.SetIndent(Value: Integer); begin if FIndent <> Value then begin FIndent := Value; RecreateWnd; end; end; procedure TToolBar.SetMenu(const Value: TMainMenu); var I: Integer; begin if FMenu = Value then exit; if csAcceptsControls in ControlStyle then begin ControlStyle := [csCaptureMouse, csClickEvents, csDoubleClicks, csMenuEvents, csSetCaption]; RecreateWnd; end; ShowCaptions := True; if Assigned(FMenu) then for I := ButtonCount - 1 downto 0 do Buttons[I].Free; if Assigned(FMenu) then FMenu.RemoveFreeNotification(Self); FMenu := Value; if not Assigned(FMenu) then exit; FMenu.FreeNotification(Self); for I := ButtonCount to FMenu.Items.Count - 1 do begin with TToolButton.Create(Self) do try AutoSize := True; Grouped := True; Parent := Self; Buttons[I].MenuItem := FMenu.Items[I]; except Free; raise; end; end; { Copy attributes from each menu item } for I := 0 to FMenu.Items.Count - 1 do Buttons[i].MenuItem := FMenu.Items[I]; end; procedure TToolBar.RecreateButtons; begin if ([csLoading, csDestroying] * ComponentState = []) or HandleAllocated then begin CreateButtons(FButtonWidth, FButtonHeight); ResizeButtons; end; end; procedure TToolBar.WMCaptureChanged(var Message: TMessage); begin inherited; if FInMenuLoop and FCaptureChangeCancels then CancelMenu; end; procedure TToolBar.WMKeyDown(var Message: TWMKeyDown); var Item: Integer; Button: TToolButton; P: TPoint; begin if FInMenuLoop then begin Item := Perform(TB_GETHOTITEM, 0, 0); case Message.CharCode of VK_RETURN, VK_DOWN: begin if (Item > -1) and (Item < FButtons.Count) then begin Button := TToolButton(FButtons[Item]); P := Button.ClientToScreen(Point(1, 1)); ClickButton(Button); end; { Prevent default processing } if Message.CharCode = VK_DOWN then Exit; end; VK_ESCAPE: CancelMenu; end; end; inherited; end; procedure TToolBar.GetChildren(Proc: TGetChildProc; Root: TComponent); var I: Integer; Control: TControl; begin if Assigned(Menu) then exit; for I := 0 to FButtons.Count - 1 do Proc(TComponent(FButtons[I])); for I := 0 to ControlCount - 1 do begin Control := Controls[I]; if (Control.Owner = Root) and (FButtons.IndexOf(Control) = -1) then Proc(Control); end; end; procedure TToolBar.Loaded; var I: Integer; begin RecreateButtons; { Make sure we dock controls after streaming } for I := 0 to ControlCount - 1 do Controls[I].HostDockSite := Self; inherited Loaded; ResizeButtons; RepositionButtons(0); end; procedure TToolBar.BeginUpdate; begin Inc(FUpdateCount); end; procedure TToolBar.EndUpdate; begin Dec(FUpdateCount); end; procedure TToolBar.ResizeButtons; begin if not (csLoading in ComponentState) and HandleAllocated then begin Perform(TB_AUTOSIZE, 0, 0); if AutoSize then AdjustSize; end; end; function TToolBar.InternalButtonCount: Integer; begin Result := Perform(TB_BUTTONCOUNT, 0, 0); end; function TToolBar.ButtonIndex(OldIndex, ALeft, ATop: Integer): Integer; var Dist, Tmp, Head, Tail: Integer; Control: TControl; begin if (OldIndex >= 0) and (FButtons.Count <= 1) then begin Result := OldIndex; Exit; end; { Find row closest to ATop } Result := 0; if FButtons.Count = 0 then Exit; Tmp := 0; Head := 0; Tail := 0; Dist := MaxInt; while (Dist > 0) and (Result < FButtons.Count) do begin if Result <> OldIndex then begin Control := TControl(FButtons[Result]); if (Control is TToolButton) and TToolButton(Control).Wrap or (Result = FButtons.Count - 1) then begin if Abs(ATop - Control.Top) < Dist then begin Dist := Abs(ATop - Control.Top); Head := Tmp; Tail := Result; end; Tmp := Result + 1; end; end else Tail := Result; Inc(Result); end; { Find button on Row closest to ALeft } for Result := Head to Tail do if (Result <> OldIndex) and (ALeft <= TControl(FButtons[Result]).Left) then Break; { Return old position if new position is last on the row and old position was already the last on the row. } if (Result = OldIndex + 1) and (OldIndex in [Head..Tail]) then Result := OldIndex; end; function TToolBar.ReorderButton(OldIndex, ALeft, ATop: Integer): Integer; var Control: TControl; begin Result := ButtonIndex(OldIndex, ALeft, ATop); if Result <> OldIndex then begin { If we are inserting to the right of our deletion then account for shift } if OldIndex < Result then Dec(Result); Control := FButtons[OldIndex]; FButtons.Delete(OldIndex); FButtons.Insert(Result, Control); BeginUpdate; try Perform(TB_DELETEBUTTON, OldIndex, 0); UpdateItem(TB_INSERTBUTTON, Result, Result); finally EndUpdate; end; end; end; procedure TToolBar.AdjustControl(Control: TControl); var I, Pos: Integer; R: TRect; Reordered, NeedsUpdate: Boolean; begin Pos := FButtons.IndexOf(Control); if Pos = -1 then Exit; Reordered := ReorderButton(Pos, Control.Left, Control.Top) <> Pos; NeedsUpdate := False; if Reordered then begin I := FButtons.IndexOf(Control); if I < Pos then Pos := I; end else if Perform(TB_GETITEMRECT, Pos, Longint(@R)) <> 0 then begin NeedsUpdate := Control.Width <> R.Right - R.Left; Reordered := NeedsUpdate; end; if (csDesigning in ComponentState) and (Control.Height <> ButtonHeight) then ButtonHeight := Control.Height else if Reordered then begin if NeedsUpdate then RefreshButton(Pos); ResizeButtons; RepositionButtons(0); end else RepositionButton(Pos); end; procedure TToolBar.AlignControls(AControl: TControl; var Rect: TRect); begin if FUpdateCount > 0 then Exit; if AControl = nil then RepositionButtons(0) else if not (AControl is TToolButton) then AdjustControl(AControl); end; procedure TToolBar.ChangeScale(M, D: Integer); begin { Scaling isn't a standard behavior for toolbars. We prevent scaling from occurring here. } end; procedure TToolBar.WMEraseBkgnd(var Message: TWMEraseBkgnd); begin if not Transparent then inherited else DefaultHandler(Message); end; procedure TToolBar.WMGetDlgCode(var Message: TMessage); begin if FInMenuLoop then Message.Result := DLGC_WANTARROWS; end; { Need to read/write caption ourselves - default wndproc seems to discard it. } procedure TToolBar.WMGetText(var Message: TWMGetText); begin with Message do Result := StrLen(StrLCopy(PChar(Text), PChar(FCaption), TextMax - 1)); end; procedure TToolBar.WMGetTextLength(var Message: TWMGetTextLength); begin Message.Result := Length(FCaption); end; procedure TToolBar.WMSetText(var Message: TWMSetText); begin with Message do SetString(FCaption, Text, StrLen(Text)); end; procedure TToolBar.WMNotifyFormat(var Message: TMessage); begin with Message do Result := DefWindowProc(Handle, Msg, WParam, LParam); end; procedure TToolBar.WMSize(var Message: TWMSize); var W, H: Integer; begin inherited; if not AutoSize then begin W := Width; H := Height; WrapButtons(W, H); end; end; procedure TToolBar.WMSysChar(var Message: TWMSysChar); var Form: TCustomForm; begin { Default wndproc doesn't re-route WM_SYSCHAR messages to parent. } Form := GetParentForm(Self); if Form <> nil then begin Form.Dispatch(Message); Exit; end else inherited; end; procedure TToolBar.WMWindowPosChanged(var Message: TWMWindowPosChanged); var R: TRect; NcX, NcY: Integer; Rgn1, Rgn2: HRgn; begin { Erase only what's been uncovered when toolbar is flat - avoid flicker } if Flat and HandleAllocated and (Parent <> nil) then begin GetWindowRect(Handle, R); NcX := R.Right - R.Left - ClientWidth; NcY := R.Bottom - R.Top - ClientHeight; Rgn1 := CreateRectRgn(0, 0, Width - NcX, Height - NcY); with Message.WindowPos^ do Rgn2 := CreateRectRgn(0, 0, cx - NcY, cy - NcY); CombineRgn(Rgn1, Rgn2, Rgn1, RGN_XOR); GetRgnBox(Rgn1, R); { Allow a 2 pixel buffer } Dec(R.Left, 2); DeleteObject(Rgn1); DeleteObject(Rgn2); inherited; RedrawWindow(Handle, @R, 0, RDW_INVALIDATE or RDW_ERASE); end else inherited; end; procedure TToolBar.WMWindowPosChanging(var Message: TWMWindowPosChanging); const BackgroundValid = SWP_NOSIZE or SWP_NOMOVE; var R: TRect; begin { Invalidate old background when toolbar is flat and is about to be moved } if Transparent and (Message.WindowPos^.flags and BackgroundValid <> BackgroundValid) and (Parent <> nil) and Parent.HandleAllocated then begin R := BoundsRect; InvalidateRect(Parent.Handle, @R, True); end; inherited; end; function TToolBar.WrapButtons(var NewWidth, NewHeight: Integer): Boolean; var Index, NcX, NcY: Integer; Vertical: Boolean; PrevSize, CurrSize: TPoint; R: TRect; WrapStates: TBits; procedure CalcSize(var CX, CY: Integer); var IsWrapped: Boolean; I, Tmp, X, Y, HeightChange: Integer; Control: TControl; begin CX := 0; CY := 0; X := Indent; Y := 0; for I := 0 to FButtons.Count - 1 do begin Control := TControl(FButtons[I]); if (csDesigning in ComponentState) or Control.Visible then begin if (Control is TToolButton) and (I < FButtons.Count - 1) then if WrapStates <> nil then IsWrapped := WrapStates[I] else IsWrapped := TToolButton(Control).Wrap else IsWrapped := False; if Control is TToolButton and (TToolButton(Control).Style in [tbsSeparator, tbsDivider]) then begin { Store the change in height, from the current row to the next row after wrapping, in HeightChange. THe IE4 version of comctl32 considers this height to be the width the last separator on the current row - prior versions of comctl32 consider this height to be 2/3 the width the last separator. } HeightChange := Control.Width; if (GetComCtlVersion < ComCtlVersionIE4) or not Flat and (GetComCtlVersion >= ComCtlVersionIE401) then HeightChange := HeightChange * 2 div 3; if IsWrapped and (I < FButtons.Count - 1) then begin Tmp := Y + ButtonHeight + HeightChange; if Tmp > CY then CY := Tmp; end else begin Tmp := X + Control.Width; if Tmp > CX then CX := Tmp; end; if IsWrapped then Inc(Y, HeightChange); end else begin Tmp := X + Control.Width; if Tmp > CX then CX := Tmp; Tmp := Y + ButtonHeight; if Tmp > CY then CY := Tmp; end; if IsWrapped then begin X := Indent; Inc(Y, ButtonHeight); end else Inc(X, Control.Width); end; end; { Adjust for 2 pixel top margin when not flat style buttons } if (CY > 0) and not Flat then Inc(CY, 2); end; function WrapHorz(CX: Integer): Integer; var I, J, X: Integer; Control: TControl; Found: Boolean; begin Result := 1; X := Indent; I := 0; while I < FButtons.Count do begin Control := TControl(FButtons[I]); if Control is TToolButton then WrapStates[I] := False; if (csDesigning in ComponentState) or Control.Visible then begin if (X + Control.Width > CX) and (not (Control is TToolButton) or not (TToolButton(Control).Style in [tbsDivider, tbsSeparator])) then begin Found := False; for J := I downto 0 do if TControl(FButtons[J]) is TToolButton then with TToolButton(FButtons[J]) do if ((csDesigning in ComponentState) or Visible) and (Style in [tbsSeparator, tbsDivider]) then begin if not WrapStates[J] then begin Found := True; I := J; X := Indent; WrapStates[J] := True; Inc(Result); end; Break; end; if not Found then begin for J := I - 1 downto 0 do if TControl(FButtons[J]) is TToolButton then with TToolButton(FButtons[J]) do if (csDesigning in ComponentState) or Visible then begin if not WrapStates[J] then begin Found := True; I := J; X := Indent; WrapStates[J] := True; Inc(Result); end; Break; end; if not Found then Inc(X, Control.Width); end; end else Inc(X, Control.Width); end; Inc(I); end; end; function WrapSizeVert(var CX, CY: Integer): Integer; var HorzSize, VertSize, Size, PrevSize: TPoint; begin PrevSize := Point(-1,-1); Size := Point(0,0); Result := 0; WrapHorz(0); CalcSize(VertSize.X, VertSize.Y); WrapHorz(MaxInt); CalcSize(HorzSize.X, HorzSize.Y); while VertSize.X < HorzSize.X do begin PrevSize := Size; Size.X := (VertSize.X + HorzSize.X) div 2; Result := WrapHorz(Size.X); CalcSize(Size.X, Size.Y); if CY < Size.Y then begin if (VertSize.X = Size.X) and (VertSize.Y = Size.Y) then begin Result := WrapHorz(HorzSize.X); Break; end; VertSize := Size; end else if CY > Size.Y then begin HorzSize := Size; if (PrevSize.X = Size.X) and (PrevSize.Y = Size.Y) then Break; end else Break; end; end; function WrapSizeHorz(var CX, CY: Integer): Integer; var HorzRows, VertRows, Min, Mid, Max: Integer; HorzSize: TPoint; begin Result := 0; Min := 0; Max := CX; HorzRows := WrapHorz(Max); VertRows := WrapHorz(0); if HorzRows <> VertRows then while Min < Max do begin Mid := (Min + Max) div 2; VertRows := WrapHorz(Mid); if VertRows = HorzRows then Max := Mid else begin if Min = Mid then begin WrapHorz(Max); Break; end; Min := Mid; end; end; CalcSize(HorzSize.X, HorzSize.Y); WrapHorz(HorzSize.X); end; begin Result := True; if HandleAllocated then begin Index := InternalButtonCount - 1; if (Index >= 0) or not (csDesigning in ComponentState) then begin WrapStates := nil; PrevSize.X := ClientWidth; PrevSize.Y := ClientHeight; { Calculate non-client border size } NcX := Width - PrevSize.X; NcY := Height - PrevSize.Y; { Remember previous size for comparison } R.BottomRight := PrevSize; CalcSize(PrevSize.X, PrevSize.Y); { Get current window size minus the non-client borders } CurrSize := Point(NewWidth - NcX, NewHeight - NcY); { Decide best way to calculate layout } if Align <> alNone then Vertical := Align in [alLeft, alRight] else Vertical := Abs(CurrSize.X - R.Right) < Abs(CurrSize.Y - R.Bottom); if Wrapable then begin WrapStates := TBits.Create; try WrapStates.Size := FButtons.Count; if Vertical then WrapSizeVert(CurrSize.X, CurrSize.Y) else WrapSizeHorz(CurrSize.X, CurrSize.Y); { CurrSize now has optimium dimensions } CalcSize(CurrSize.X, CurrSize.Y); if (Vertical or (Align = alNone)) and (CurrSize.X <> PrevSize.X) or (CurrSize.Y <> PrevSize.Y) then begin { Enforce changes to Wrap property } for Index := 0 to WrapStates.Size - 1 do if TControl(FButtons[Index]) is TToolButton then TToolButton(FButtons[Index]).Wrap := WrapStates[Index]; RepositionButtons(0); end else { Overwrite any changes to buttons' Wrap property } UpdateButtonStates; finally WrapStates.Free; end; end else { CurrSize now has optimium dimensions } CalcSize(CurrSize.X, CurrSize.Y); if AutoSize and (Align <> alClient) then begin if Vertical or (Align = alNone) then NewWidth := CurrSize.X + NcX; if not Vertical or (Align = alNone) then NewHeight := CurrSize.Y + NcY; end; end; end; end; function TToolBar.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; begin Result := WrapButtons(NewWidth, NewHeight); end; procedure TToolBar.CMControlChange(var Message: TCMControlChange); begin inherited; with Message do if Inserting then InsertButton(Control) else RemoveButton(Control); end; procedure TToolBar.CNChar(var Message: TWMChar); begin { We got here through the installed ToolMenuKeyHook } if FInMenuLoop and not (csDesigning in ComponentState) then with Message do if Perform(CM_DIALOGCHAR, CharCode, KeyData) <> 0 then Result := 1; end; procedure TToolBar.CMDialogChar(var Message: TCMDialogChar); var Button: TToolButton; begin if Enabled and Showing and ShowCaptions then begin Button := FindButtonFromAccel(Message.CharCode); if Button <> nil then begin { Display a drop-down menu after hitting the accelerator key if IE3 is installed. Otherwise, fire the OnClick event for IE4. We do this because the IE4 version of the drop-down metaphor is more complete, allowing the user to click a button OR drop-down its menu. } if ((Button.Style <> tbsDropDown) or (GetComCtlVersion < ComCtlVersionIE4)) and ((Button.DropdownMenu <> nil) or (Button.MenuItem <> nil)) then TrackMenu(Button) else Button.Click; Message.Result := 1; Exit; end; end; inherited; end; procedure TToolBar.CMEnabledChanged(var Message: TMessage); begin inherited; Broadcast(Message); end; procedure TToolBar.CMColorChanged(var Message: TMessage); begin inherited; RecreateWnd; end; procedure TToolBar.CMParentColorChanged(var Message: TMessage); begin inherited; { If toolbar is transparent then repaint when parent changes color } if Transparent then Invalidate; end; procedure TToolBar.CNSysKeyDown(var Message: TWMSysKeyDown); begin inherited; if (Message.CharCode = VK_MENU) then CancelMenu; end; procedure TToolBar.CMSysFontChanged(var Message: TMessage); begin inherited; RecreateWnd; end; procedure TToolBar.CNDropDownClosed(var Message: TMessage); begin ClearTempMenu; FMenuDropped := False; if (GetComCtlVersion = ComCtlVersionIE5) and (FMenuButton <> nil) then FMenuButton.Invalidate; FCaptureChangeCancels := True; end; procedure TToolBar.CNNotify(var Message: TWMNotify); var Button: TToolButton; DefaultDraw: Boolean; R: TRect; Flags: TTBCustomDrawFlags; LogFont: TLogFont; begin with Message do case NMHdr^.code of TBN_DROPDOWN: with PNMToolBar(NMHdr)^ do { We can safely assume that a TBN_DROPDOWN message was generated by a TToolButton and not any TControl. } if Perform(TB_GETBUTTON, iItem, Longint(@tbButton)) <> 0 then begin Button := TToolButton(tbButton.dwData); if Button <> nil then Button.CheckMenuDropDown; end; NM_CUSTOMDRAW: with PNMTBCustomDraw(NMHdr)^ do try FCanvas.Lock; Result := CDRF_DODEFAULT; if (nmcd.dwDrawStage and CDDS_ITEM) = 0 then begin R := ClientRect; case nmcd.dwDrawStage of CDDS_PREPAINT: begin if IsCustomDrawn(dtControl, cdPrePaint) then begin try FCanvas.Handle := nmcd.hdc; FCanvas.Font := Font; FCanvas.Brush := Brush; DefaultDraw := CustomDraw(R, cdPrePaint); if not DefaultDraw then begin Result := CDRF_SKIPDEFAULT; Exit; end; clrText := ColorToRGB(FCanvas.Font.Color); clrBtnFace := ColorToRGB(FCanvas.Brush.Color); finally FCanvas.Handle := 0; end; end; if IsCustomDrawn(dtItem, cdPrePaint) or IsCustomDrawn(dtItem, cdPreErase) then Result := Result or CDRF_NOTIFYITEMDRAW; if IsCustomDrawn(dtItem, cdPostPaint) then Result := Result or CDRF_NOTIFYPOSTPAINT; if IsCustomDrawn(dtItem, cdPostErase) then Result := Result or CDRF_NOTIFYPOSTERASE; end; CDDS_POSTPAINT: if IsCustomDrawn(dtControl, cdPostPaint) then CustomDraw(R, cdPostPaint); CDDS_PREERASE: if IsCustomDrawn(dtControl, cdPreErase) then CustomDraw(R, cdPreErase); CDDS_POSTERASE: if IsCustomDrawn(dtControl, cdPostErase) then CustomDraw(R, cdPostErase); end; end else begin Button := Buttons[nmcd.dwItemSpec]; if Button = nil then Exit; case nmcd.dwDrawStage of CDDS_ITEMPREPAINT: begin //release the font we may have loaned during item drawing. if (nmcd.dwDrawStage and CDDS_ITEMPOSTPAINT <> 0) and (FOurFont + FStockFont <> 0) then begin SelectObject(nmcd.hdc, FStockFont); DeleteObject(FOurFont); FOurFont := 0; FStockFont := 0; end; try FCanvas.Handle := nmcd.hdc; FCanvas.Font := Self.Font; FCanvas.Brush := Self.Brush; FCanvas.Font.OnChange := CanvasChanged; FCanvas.Brush.OnChange := CanvasChanged; FCanvasChanged := False; Flags := []; DefaultDraw := CustomDrawButton(Button, TCustomDrawState(Word(nmcd.uItemState)), cdPrePaint, Flags); if tbNoEdges in Flags then Result := Result or TBCDRF_NOEDGES; if tbHiliteHotTrack in Flags then Result := Result or TBCDRF_HILITEHOTTRACK; if tbNoOffset in Flags then Result := Result or TBCDRF_NOOFFSET; if tbNoMark in Flags then Result := Result or TBCDRF_NOMARK; if tbNoEtchedEffect in Flags then Result := Result or TBCDRF_NOETCHEDEFFECT; clrText := ColorToRGB(FCanvas.Font.Color); clrBtnFace := ColorToRGB(FCanvas.Brush.Color); if not DefaultDraw then begin Result := Result or CDRF_SKIPDEFAULT; Exit; end else if FCanvasChanged then begin FCanvasChanged := False; FCanvas.Font.OnChange := nil; FCanvas.Brush.OnChange := nil; if GetObject(FCanvas.Font.Handle, SizeOf(LogFont), @LogFont) <> 0 then begin FCanvas.Handle := 0; // disconnect from hdc // don't delete the stock font FOurFont := CreateFontIndirect(LogFont); FStockFont := SelectObject(nmcd.hdc, FOurFont); Result := Result or CDRF_NEWFONT; end; if IsCustomDrawn(dtItem, cdPostPaint) then Result := Result or CDRF_NOTIFYPOSTPAINT; end; finally FCanvas.Handle := 0; end; end; CDDS_ITEMPOSTPAINT: if Button <> nil then CustomDrawButton(Button, TCustomDrawState(Word(nmcd.uItemState)), cdPostPaint, Flags); CDDS_ITEMPREERASE: if Button <> nil then CustomDrawButton(Button, TCustomDrawState(Word(nmcd.uItemState)), cdPreErase, Flags); CDDS_ITEMPOSTERASE: if Button <> nil then CustomDrawButton(Button, TCustomDrawState(Word(nmcd.uItemState)), cdPostErase, Flags); end; end; finally FCanvas.Unlock; end; TBN_QUERYINSERT: with PNMToolbar(NMHdr)^ do begin Result := Integer(DoQueryInsert(iItem)); if (FButtons.IndexOf(Pointer(tbButton.dwData)) < 0) and (TControl(tbButton.dwData) is TToolButton) then begin { FButtons.Insert(iItem, Pointer(tbButton.dwData)); TToolButton(tbButton.dwData).FToolbar := Self; if Assigned(FOnCustomizeAdded) then FOnCustomizeAdded(Self, TToolButton(tbButton.dwData));} end else if (tbButton.dwData = 0) and (tbButton.fsStyle = ButtonStyles[tbsSeparator]) then begin Button := TToolButton.Create(Owner); Button.Style := tbsSeparator; FButtons.Insert(iItem, Button); Inc(FSeparators); end; end; TBN_QUERYDELETE: Result := Integer(DoQueryDelete(PNMToolbar(NMHdr)^.iItem)); TBN_GETBUTTONINFO: with PNMToolBar(NMHdr)^ do begin if FCustomizing then Result := Integer(DoGetButton(PNMToolbar(NMHdr))); end; TBN_DELETINGBUTTON: if FCustomizing and not FRestoring and Assigned(FOnCustomizeDelete) then begin Button := TToolButton(FButtons[PNMToolbar(NMHdr)^.iItem]); FOnCustomizeDelete(Self, Button); end; TBN_BEGINADJUST: begin FCustomizing := True; FSeparators := 0; if Assigned(FOnCustomizing) then FOnCustomizing(Self); if not FRestoring then SaveButtons(True); end; TBN_ENDADJUST: begin if not FRestoring then begin RecreateButtonsFromToolbar; FCustomizing := False; end else if Assigned(FOnCustomizeReset) then FOnCustomizeReset(Self); FRestoring := False; end; TBN_TOOLBARCHANGE: begin if not FCustomizing then //Buttons were dragged holding SHIFT key down. RecreateButtonsFromToolbar; if Assigned(FOnCustomized) then FOnCustomized(Self); end; TBN_RESET: begin FRestoring := True; SaveButtons(False); RecreateButtonsFromToolbar; FRestoring := False; end; end; end; procedure TToolBar.RecreateButtonsFromToolbar; var I: Integer; ButtonInfo: TTBBUTTON; Button: TToolButton; TBButtonCount: Integer; begin TBButtonCount := SendMessage(Handle, TB_BUTTONCOUNT, 0, 0); FButtons.Clear; for I := 0 to TBButtonCount - 1 do begin SendMessage(Handle, TB_GETBUTTON, I, Longint(@ButtonInfo)); if ButtonInfo.dwData = 0 then begin Button := TToolButton.Create(Owner); Button.Style := tbsSeparator; ButtonInfo.dwData := Integer(Button); Button.FToolbar := Self; SendMessage(Handle, TB_DELETEBUTTON, I, 0); SendMessage(Handle, TB_INSERTBUTTON, I, Longint(@ButtonInfo)); end; FButtons.Add(Pointer(ButtonInfo.dwData)); end; RecreateButtons; end; type TControlAccess = class(TControl); procedure TToolBar.WndProc(var Message: TMessage); var Control: TControl; CapControl: TControl; Msg: TMsg; function IsToolButtonMouseMsg(var Message: TWMMouse): Boolean; begin if GetCapture = Handle then begin CapControl := GetCaptureControl; if (CapControl <> nil) and (CapControl.Parent <> Self) then CapControl := nil; end else CapControl := nil; Control := ControlAtPos(SmallPointToPoint(Message.Pos), False); Result := (Control <> nil) and (Control is TToolButton) and not Control.Dragging; end; procedure SendDropdownMsg(Button: TToolButton); var Msg: TNMToolBar; begin FillChar(Msg, SizeOf(Msg), 0); with Msg, hdr do begin hwndFrom := Handle; idFrom := Handle; code := TBN_DROPDOWN; iItem := Button.Index; end; SendMessage(Handle, WM_NOTIFY, Handle, Longint(@Msg)); end; begin if not (csDesigning in ComponentState) then begin case Message.Msg of WM_MOUSEMOVE: begin { Call default wndproc to get buttons to repaint when Flat = True. } if IsToolButtonMouseMsg(TWMMouse(Message)) then begin { Prevent painting of flat buttons when they are dock clients } if TControlAccess(Control).DragMode <> dmAutomatic then DefaultHandler(Message); end else DefaultHandler(Message); end; WM_LBUTTONUP: { Update button states after a click. } if IsToolButtonMouseMsg(TWMMouse(Message)) then begin DefaultHandler(Message); if CapControl = Control then begin with TToolButton(Control) do if Down and Grouped and AllowAllUp and (Style = tbsCheck) then Down := False; UpdateButtonStates; end else if (CapControl is TToolButton) or (TToolButton(Control).Style = tbsDropDown) then Exit; end; WM_LBUTTONDOWN, WM_LBUTTONDBLCLK: if IsToolButtonMouseMsg(TWMMouse(Message)) then begin { Check if mouse is clicked on a drop-down button's arrow (for IE4 the arrow is within 13 pixels from the right, for IE3 there is no distinction - the entire button is used). If an arrow click is detected then don't process this mouse event - a TBN_DROPDOWN notification will be created for us by the default wndproc. } with TToolButton(Control) do begin { Allow IsControlMouseMsg to deliver message to button } if FInMenuLoop and Self.MouseCapture then MouseCapture := True; if (Style <> tbsDropDown) or (GetComCtlVersion >= ComCtlVersionIE4) and (TWMMouse(Message).XPos < Left + ButtonWidth) then inherited WndProc(Message); end; if not Control.Dragging then DefaultHandler(Message); if (TToolButton(Control).Style <> tbsDropDown) and ((TToolButton(Control).DropdownMenu <> nil) or (TToolButton(Control).MenuItem <> nil)) then begin try SendDropDownMsg(TToolButton(Control)); finally { Here we remove WM_LBUTTONDOWN message sent and instead dispatch it as a WM_LBUTTONUP to get a Click fired. } Msg.Message := 0; if PeekMessage(Msg, Handle, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE) and (Msg.Message = WM_QUIT) then PostQuitMessage(Msg.WParam) else begin Message.Msg := WM_LBUTTONUP; Dispatch(Message); end; end; end; Exit; end; end end; inherited WndProc(Message); end; procedure TToolBar.FlipChildren(AllLevels: Boolean); begin { do not flip controls } end; function TToolBar.FindButtonFromAccel(Accel: Word): TToolButton; var I: Integer; begin for I := 0 to FButtons.Count - 1 do if TControl(FButtons[I]) is TToolButton then begin Result := TToolButton(FButtons[I]); if Result.Visible and Result.Enabled and IsAccel(Accel, Result.Caption) then Exit; end; Result := nil; end; { CustomDraw support } function TToolBar.IsCustomDrawn(Target: TCustomDrawTarget; Stage: TCustomDrawStage): Boolean; begin if Stage = cdPrePaint then begin if Target = dtItem then Result := Assigned(FOnCustomDrawButton) or Assigned(FOnAdvancedCustomDrawButton) else if Target = dtControl then Result := Assigned(FOnCustomDraw) or Assigned(FOnAdvancedCustomDraw) or Assigned(FOnCustomDrawButton) or Assigned(FOnAdvancedCustomDrawButton) else Result := False; end else begin if Target = dtItem then Result := Assigned(FOnAdvancedCustomDrawButton) else if Target = dtControl then Result := Assigned(FOnAdvancedCustomDraw) or Assigned(FOnAdvancedCustomDrawButton) else Result := False; end; end; function TToolBar.CustomDraw(const ARect: TRect; Stage: TCustomDrawStage): Boolean; begin Result := True; if (Stage = cdPrePaint) and Assigned(FOnCustomDraw) then FOnCustomDraw(Self, ARect, Result); if Assigned(FOnAdvancedCustomDraw) then FOnAdvancedCustomDraw(Self, ARect, Stage, Result); end; function TToolBar.CustomDrawButton(Button: TToolButton; State: TCustomDrawState; Stage: TCustomDrawStage; var Flags: TTBCustomDrawFlags): Boolean; begin Result := True; if (Stage = cdPrePaint) and Assigned(FOnCustomDrawButton) then FOnCustomDrawButton(Self, Button, State, Result); if Assigned(FOnAdvancedCustomDrawButton) then FOnAdvancedCustomDrawButton(Self, button, State, Stage, Flags, Result); end; procedure TToolBar.CanvasChanged(Sender: TObject); begin FCanvasChanged := True; end; { Toolbar menu support } var ToolMenuHook: HHOOK; InitDone: Boolean = False; MenuToolBar, MenuToolBar2: TToolBar; MenuButtonIndex: Integer; LastMenuItem: TMenuItem; LastMousePos: TPoint; StillModal: Boolean; function ToolMenuGetMsgHook(Code: Integer; WParam: Longint; var Msg: TMsg): Longint; stdcall; const RightArrowKey: array[Boolean] of Word = (VK_LEFT, VK_RIGHT); LeftArrowKey: array[Boolean] of Word = (VK_RIGHT, VK_LEFT); var P: TPoint; Target: TControl; Item: Integer; FindKind: TFindItemKind; ParentMenu: TMenu; function FindButton(Forward: Boolean): TToolButton; var ToolBar: TToolBar; I, J, Count: Integer; begin ToolBar := MenuToolBar; if ToolBar <> nil then begin J := MenuButtonIndex; I := J; Count := ToolBar.ButtonCount; if Forward then repeat if I = Count - 1 then I := 0 else Inc(I); Result := ToolBar.Buttons[I]; if Result.Visible and Result.Enabled and Result.Grouped then Exit; until I = J else repeat if I = 0 then I := Count - 1 else Dec(I); Result := ToolBar.Buttons[I]; if Result.Visible and Result.Enabled and Result.Grouped then Exit; until I = J; end; Result := nil; end; begin if LastMenuItem <> nil then begin ParentMenu := LastMenuItem.GetParentMenu; if ParentMenu <> nil then begin if ParentMenu.IsRightToLeft then if Msg.WParam = VK_LEFT then Msg.WParam := VK_RIGHT else if Msg.WParam = VK_RIGHT then Msg.WParam := VK_LEFT; end; end; Result := CallNextHookEx(ToolMenuHook, Code, WParam, Longint(@Msg)); if Result <> 0 then Exit; if (Code = MSGF_MENU) then begin Target := nil; if not InitDone then begin InitDone := True; PostMessage(Msg.Hwnd, WM_KEYDOWN, VK_DOWN, 0); end; case Msg.Message of WM_MENUSELECT: begin if (HiWord(Msg.WParam) = $FFFF) and (Msg.LParam = 0) then begin if not StillModal then MenuToolBar.CancelMenu; Exit; end else StillModal := False; FindKind := fkCommand; if HiWord(Msg.WParam) and MF_POPUP <> 0 then FindKind := fkHandle; if FindKind = fkHandle then Item := GetSubMenu(Msg.LParam, LoWord(Msg.WParam)) else Item := LoWord(Msg.WParam); LastMenuItem := MenuToolBar.FTempMenu.FindItem(Item, FindKind); end; WM_SYSKEYDOWN: if Msg.WParam = VK_MENU then begin MenuToolBar.CancelMenu; Exit; end; WM_KEYDOWN: if Msg.WParam = VK_RETURN then MenuToolBar.FMenuResult := True else if Msg.WParam = VK_ESCAPE then StillModal := True else if LastMenuItem <> nil then begin if (Msg.WParam = VK_RIGHT) and (LastMenuItem.Count = 0) then Target := FindButton(True) else if (Msg.WParam = VK_LEFT) and (LastMenuItem.GetParentComponent is TPopupMenu) then Target := FindButton(False) else Target := nil; if Target <> nil then P := Target.ClientToScreen(Point(0,0)); end; WM_MOUSEMOVE: begin P := Msg.pt; if (P.X <> LastMousePos.X) or (P.Y <> LastMousePos.Y) then begin Target := FindDragTarget(P, False); LastMousePos := P; end; end; end; if (Target <> nil) and (Target is TToolButton) then begin with TToolButton(Target) do if (Index <> MenuButtonIndex) and Grouped and (Parent <> nil) and Parent.HandleAllocated then begin StillModal := True; MenuToolBar.FCaptureChangeCancels := False; MenuToolBar.ClickButton(TToolButton(Target)); MenuToolBar.ClickButton(TToolButton(Target)); end; end; end; end; procedure InitToolMenuHooks; begin StillModal := False; GetCursorPos(LastMousePos); if ToolMenuHook = 0 then ToolMenuHook := SetWindowsHookEx(WH_MSGFILTER, @ToolMenuGetMsgHook, 0, GetCurrentThreadID); end; procedure ReleaseToolMenuHooks; begin if ToolMenuHook <> 0 then UnhookWindowsHookEx(ToolMenuHook); ToolMenuHook := 0; LastMenuItem := nil; MenuToolBar := nil; MenuButtonIndex := -1; InitDone := False; end; var ToolMenuKeyHook: HHOOK; procedure ReleaseToolMenuKeyHooks; forward; function ToolMenuKeyMsgHook(Code: Integer; WParam: Longint; var Msg: TMsg): Longint; stdcall; begin if (Code = HC_ACTION) then begin if Msg.Message = CM_DEACTIVATE then MenuToolBar2.CancelMenu else if Msg.message = WM_COMMAND then ReleaseToolMenuKeyHooks else if (ToolMenuHook = 0) and ((Msg.Message = WM_CHAR) or (Msg.Message = WM_KEYDOWN) or (Msg.Message = WM_KEYUP) or (Msg.Message = WM_SYSKEYDOWN) or (Msg.Message = WM_SYSKEYUP)) then Msg.hwnd := MenuToolBar2.Handle; end; Result := CallNextHookEx(ToolMenuKeyHook, Code, WParam, Longint(@Msg)) end; procedure InitToolMenuKeyHooks; begin if ToolMenuKeyHook = 0 then ToolMenuKeyHook := SetWindowsHookEx(WH_GETMESSAGE, @ToolMenuKeyMsgHook, 0, GetCurrentThreadID); end; procedure ReleaseToolMenuKeyHooks; begin if ToolMenuKeyHook <> 0 then UnhookWindowsHookEx(ToolMenuKeyHook); ToolMenuKeyHook := 0; MenuToolBar2 := nil; end; procedure TToolBar.ClearTempMenu; var I: Integer; Item: TMenuItem; begin if (FButtonMenu <> nil) and (FMenuButton <> nil) and (FMenuButton.MenuItem <> nil) and (FTempMenu <> nil) then begin for I := FTempMenu.Items.Count - 1 downto 0 do begin Item := FTempMenu.Items[I]; FTempMenu.Items.Delete(I); FButtonMenu.Insert(0, Item); end; FTempMenu.Free; FTempMenu := nil; FMenuButton := nil; FButtonMenu := nil; end; end; function TToolBar.CheckMenuDropdown(Button: TToolButton): Boolean; var Hook: Boolean; Menu: TMenu; Item: TMenuItem; I: Integer; ParentMenu: TMenu; APoint: TPoint; begin Result := False; if Button = nil then Exit; FCaptureChangeCancels := False; try if Button.DropdownMenu <> nil then FTempMenu := Button.DropdownMenu else if Button.MenuItem <> nil then begin Button.MenuItem.Click; ClearTempMenu; FTempMenu := TPopupMenu.Create(Self); ParentMenu := Button.MenuItem.GetParentMenu; if ParentMenu <> nil then FTempMenu.BiDiMode := ParentMenu.BiDiMode; FTempMenu.HelpContext := Button.MenuItem.HelpContext; FTempMenu.TrackButton := tbLeftButton; Menu := Button.MenuItem.GetParentMenu; if Menu <> nil then FTempMenu.Images := Menu.Images; FButtonMenu := Button.MenuItem; for I := FButtonMenu.Count - 1 downto 0 do begin Item := FButtonMenu.Items[I]; FButtonMenu.Delete(I); FTempMenu.Items.Insert(0, Item); end; end else Exit; SendCancelMode(nil); FTempMenu.PopupComponent := Self; Hook := Button.Grouped or (Button.MenuItem <> nil); if Hook then begin MenuButtonIndex := Button.Index; MenuToolBar := Self; InitToolMenuHooks; end; Perform(TB_SETHOTITEM, -1, 0); try APoint := Button.ClientToScreen(Point(0, Button.ClientHeight)); if FTempMenu.IsRightToLeft then Inc(APoint.X, Button.Width); FMenuDropped := True; if (GetSystemMetrics(SM_CYMENU) * FTempMenu.Items.Count) + APoint.Y > Screen.MonitorFromPoint(APoint).Height then Dec(APoint.Y, Button.Height); if GetComCtlVersion = ComCtlVersionIE5 then Button.Invalidate; FTempMenu.Popup(APoint.X, APoint.Y); finally if Hook then ReleaseToolMenuHooks; end; FMenuButton := Button; if StillModal then Perform(TB_SETHOTITEM, Button.Index, 0); Result := True; finally PostMessage(Handle, CN_DROPDOWNCLOSED, 0, 0); end; end; procedure TToolBar.WMSysCommand(var Message: TWMSysCommand); function IsMenuBar: Boolean; var I: Integer; begin Result := False; for I := 0 to FButtons.Count - 1 do if (TControl(FButtons[I]) is TToolButton) and Assigned(TToolButton(FButtons[I]).MenuItem) then begin Result := True; Break; end; end; var Button: TToolButton; begin { Enter menu loop if only the Alt key is pressed -- ignore Alt-Space and let the default processing show the system menu. } if not FInMenuLoop and Enabled and Showing and ShowCaptions and IsMenuBar then with Message do if (CmdType and $FFF0 = SC_KEYMENU) and (Key <> VK_SPACE) and (Key <> Word('-')) and (GetCapture = 0) then begin if Key = 0 then Button := nil else Button := FindButtonFromAccel(Key); if (Key = 0) or (Button <> nil) then begin TrackMenu(Button); Result := 1; Exit; end; end; end; procedure TToolBar.ClickButton(Button: TToolButton); var P: TPoint; begin FCaptureChangeCancels := False; P := Button.ClientToScreen(Point(0, 0)); PostMessage(Handle, WM_LBUTTONDOWN, MK_LBUTTON, Longint(PointToSmallPoint(ScreenToClient(P)))); end; procedure TToolBar.InitMenu(Button: TToolButton); begin Perform(TB_SETANCHORHIGHLIGHT, 1, 0); MenuToolBar2 := Self; MouseCapture := True; InitToolMenuKeyHooks; if Button <> nil then begin Perform(TB_SETHOTITEM, Button.Index, 0); ClickButton(Button); end else Perform(TB_SETHOTITEM, 0, 0); if Button = nil then FCaptureChangeCancels := True; end; procedure TToolBar.CancelMenu; begin if FInMenuLoop then begin ReleaseToolMenuKeyHooks; MouseCapture := False; Perform(TB_SETANCHORHIGHLIGHT, 0, 0); end; FInMenuLoop := False; FCaptureChangeCancels := False; Perform(TB_SETHOTITEM, -1, 0); end; function TToolBar.TrackMenu(Button: TToolButton): Boolean; begin { Alread in menu loop - click button to drop-down menu } if FInMenuLoop then begin if Button <> nil then begin ClickButton(Button); Result := True; end else Result := False; Exit; end; InitMenu(Button); try FInMenuLoop := True; repeat Application.HandleMessage; if Application.Terminated then FInMenuLoop := False; until not FInMenuLoop; finally CancelMenu; end; Result := FMenuResult; end; procedure TToolBar.CMFontChanged(var Message); begin if HandleAllocated and FShowCaptions then begin Perform(WM_SETFONT, Font.Handle, 0); if not (csLoading in ComponentState) then RecreateWnd; end; NotifyControls(CM_PARENTFONTCHANGED); end; procedure TToolBar.SetCustomizable(const Value: Boolean); begin if Value <> FCustomizable then begin FCustomizable := Value; RecreateWnd; end; end; function TToolBar.DoGetButton(NMToolbar: PNMToolbar): Boolean; const MaxLen = 128; var NewButton: TControl; Title: string; Buffer: array[0..MaxLen] of Char; begin if (NMToolbar^.iItem >= FButtons.Count) then begin Result := Assigned(FOnCustomizeNewButton); if Result then begin NewButton := nil; FOnCustomizeNewButton(Self, NMToolbar^.iItem - FButtons.Count + FSeparators, TToolButton(NewButton)); Result := NewButton <> nil; if Result then TToolButton(NewButton).FToolbar := Self; end; end else begin NewButton := FButtons[NMToolbar^.iItem]; Result := NewButton is TToolButton; end; if Result then with NMToolbar^, NewButton as TToolButton do begin if Style in [tbsSeparator, tbsDivider] then Title := SSeparator else Title := Caption; StrLCopy(pszText, PChar(Title), MaxLen); cchText := StrLen(pszText); StrLCopy(Buffer, PChar(Title), MaxLen); Buffer[Length(Title) + 1] := #0; if ShowCaptions then tbButton.iString := Self.Perform(TB_ADDSTRING, 0, Longint(@Buffer)) else tbButton.iString := -1; tbButton.idCommand := Index; tbButton.iBitmap := ImageIndex; tbButton.fsStyle := ButtonStyles[Style]; tbButton.fsState := GetButtonState; tbButton.dwData := Integer(NewButton); end; end; function TToolBar.DoQueryDelete(Index: Integer): Boolean; begin Result := True; if Assigned(FOnCustomizeCanDelete) then FOnCustomizeCanDelete(Self, Index, Result); end; function TToolBar.DoQueryInsert(Index: Integer): Boolean; begin Result := True; if Assigned(FOnCustomizeCanInsert) then FOnCustomizeCanInsert(Self, Index, Result); end; procedure TToolBar.SaveButtons(Save: Boolean); var SP: TTBSaveParams; begin SP.hkr := HKEY_CURRENT_USER; SP.pszSubKey := PChar(FCustomizeKeyName); SP.pszValueName := PChar(FCustomizeValueName); SendMessage(Handle, TB_SAVERESTORE, Integer(Save), Longint(@SP)); end; { TXPMenue } constructor TXPMenu.Create(AOwner: TComponent); begin inherited Create(AOwner); FFont := TFont.Create; GetSystemMenuFont(FFont); FForm := TForm(Owner); FUseSystemColors := true; FColor := clBtnFace; FIconBackColor := clBtnFace; FSelectColor := clHighlight; FSelectBorderColor := clHighlight; FMenuBarColor := clBtnFace; FDisabledColor := clInactiveCaption; FSeparatorColor := clBtnFace; FCheckedColor := clHighlight; FSelectFontColor := FFont.Color; FIconWidth := 24; FDrawSelect := true; if FActive then begin InitMenueItems(true); end; end; destructor TXPMenu.Destroy; begin InitMenueItems(false); FFont.Free; inherited; end; procedure TXPMenu.ActivateMenuItem(MenuItem: TMenuItem); procedure Activate(MenuItem: TMenuItem); begin if addr(MenuItem.OnDrawItem) <> addr(TXPMenu.DrawItem) then begin if (not assigned(MenuItem.OnDrawItem)) or (FOverrideOwnerDraw) then MenuItem.OnDrawItem := DrawItem; if (not assigned(MenuItem.OnMeasureItem)) or (FOverrideOwnerDraw) then MenuItem.OnMeasureItem := MeasureItem; end end; var i, j: integer; begin Activate(MenuItem); for i := 0 to MenuItem.Parent.Count -1 do begin Activate(MenuItem.Parent.Items[i]); for j := 0 to MenuItem.Parent.Items[i].Count - 1 do ActivateMenuItem(MenuItem.Parent.Items[i].Items[j]); end; end; procedure TXPMenu.InitMenueItems(Enable: boolean); procedure Activate(MenuItem: TMenuItem); begin if Enable then begin if (not assigned(MenuItem.OnDrawItem)) or (FOverrideOwnerDraw) then MenuItem.OnDrawItem := DrawItem; if (not assigned(MenuItem.OnMeasureItem)) or (FOverrideOwnerDraw) then MenuItem.OnMeasureItem := MeasureItem; end else begin if addr(MenuItem.OnDrawItem) = addr(TXPMenu.DrawItem) then MenuItem.OnDrawItem := nil; if addr(MenuItem.OnMeasureItem) = addr(TXPMenu.MeasureItem) then MenuItem.OnMeasureItem := nil; end; end; procedure ItrateMenu(MenuItem: TMenuItem); var i: integer; begin Activate(MenuItem); for i := 0 to MenuItem.Count - 1 do ItrateMenu(MenuItem.Items[i]); end; var i, x: integer; begin for i := 0 to FForm.ComponentCount - 1 do begin if FForm.Components[i] is TMainMenu then begin for x := 0 to TMainMenu(FForm.Components[i]).Items.Count - 1 do begin TMainMenu(FForm.Components[i]).OwnerDraw := Enable;//Thanks Yann. Activate(TMainMenu(FForm.Components[i]).Items[x]); ItrateMenu(TMainMenu(FForm.Components[i]).Items[x]); end; { if Enable then begin if (not assigned(TMainMenu(FForm.Components[i]).OnChange)) and (FRefreshOnChange) then TMainMenu(FForm.Components[i]).OnChange := MenuChange; end else if addr(TMainMenu(FForm.Components[i]).OnChange) = addr(TXPMenu.MenuChange) then TMainMenu(FForm.Components[i]).OnChange := nil; } end; if FForm.Components[i] is TPopupMenu then begin for x := 0 to TPopupMenu(FForm.Components[i]).Items.Count - 1 do begin TPopupMenu(FForm.Components[i]).OwnerDraw := Enable; Activate(TMainMenu(FForm.Components[i]).Items[x]); ItrateMenu(TMainMenu(FForm.Components[i]).Items[x]); end; { if Enable then begin if (not assigned(TPopupMenu(FForm.Components[i]).OnChange)) and (FRefreshOnChange) then TPopupMenu(FForm.Components[i]).OnChange := MenuChange; end else if addr(TPopupMenu(FForm.Components[i]).OnChange) = addr(TXPMenu.MenuChange) then TPopupMenu(FForm.Components[i]).OnChange := nil; } end; if FForm.Components[i] is TToolBar then if not (csDesigning in ComponentState) then begin if not TToolBar(FForm.Components[i]).Flat then TToolBar(FForm.Components[i]).Flat := true; if Enable then begin for x := 0 to TToolBar(FForm.Components[i]).ButtonCount - 1 do if (not assigned(TToolBar(FForm.Components[i]).OnCustomDrawButton)) or (FOverrideOwnerDraw) then begin TToolBar(FForm.Components[i]).OnCustomDrawButton := ToolBarDrawButton; end; end else begin if addr(TToolBar(FForm.Components[i]).OnCustomDrawButton) = addr(TXPMenu.ToolBarDrawButton) then TToolBar(FForm.Components[i]).OnCustomDrawButton := nil; end; end; end; end; procedure TXPMenu.DrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); begin if FActive then MenueDrawItem(Sender, ACanvas, ARect, Selected); end; function TXPMenu.GetImageExtent(MenuItem: TMenuItem): TPoint; var HasImgLstBitmap: boolean; B: TBitmap; FTopMenu: boolean; begin FTopMenu := false; B := TBitmap.Create; B.Width := 0; B.Height := 0; Result.x := 0; Result.Y := 0; HasImgLstBitmap := false; if FForm.Menu <> nil then if MenuItem.GetParentComponent.Name = FForm.Menu.Name then begin FTopMenu := true; if FForm.Menu.Images <> nil then if MenuItem.ImageIndex <> -1 then HasImgLstBitmap := true; end; if (MenuItem.Parent.GetParentMenu.Images <> nil) {$IFDEF VER5U} or (MenuItem.Parent.SubMenuImages <> nil) {$ENDIF} then begin if MenuItem.ImageIndex <> -1 then HasImgLstBitmap := true else HasImgLstBitmap := false; end; if HasImgLstBitmap then begin {$IFDEF VER5U} if MenuItem.Parent.SubMenuImages <> nil then MenuItem.Parent.SubMenuImages.GetBitmap(MenuItem.ImageIndex, B) else {$ENDIF} MenuItem.Parent.GetParentMenu.Images.GetBitmap(MenuItem.ImageIndex, B) end else if MenuItem.Bitmap.Width > 0 then B.Assign(TBitmap(MenuItem.Bitmap)); Result.x := B.Width; Result.Y := B.Height; if not FTopMenu then if Result.x < FIconWidth then Result.x := FIconWidth; B.Free; end; procedure TXPMenu.MeasureItem(Sender: TObject; ACanvas: TCanvas; var Width, Height: Integer); var s: string; W, H: integer; P: TPoint; IsLine: boolean; begin if FActive then begin S := TMenuItem(Sender).Caption; //------ if S = '-' then IsLine := true else IsLine := false; if IsLine then //------ if IsLine then S := ''; if Trim(ShortCutToText(TMenuItem(Sender).ShortCut)) <> '' then S := S + ShortCutToText(TMenuItem(Sender).ShortCut) + 'WWW'; ACanvas.Font.Assign(FFont); W := ACanvas.TextWidth(s); if pos('&', s) > 0 then W := W - ACanvas.TextWidth('&'); P := GetImageExtent(TMenuItem(Sender)); W := W + P.x + 10; if Width < W then Width := W; if IsLine then Height := 4 else begin H := ACanvas.TextHeight(s) + Round(ACanvas.TextHeight(s) * 0.75); if P.y + 4 > H then H := P.y + 4; if Height < H then Height := H; end; end; end; procedure TXPMenu.MenueDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean); var txt: string; B: TBitmap; IconRect, TextRect, CheckedRect: TRect; i, X1, X2: integer; TextFormat: integer; HasImgLstBitmap: boolean; FMenuItem: TMenuItem; FMenu: TMenu; FTopMenu: boolean; ISLine: boolean; ImgListHandle: HImageList; {Commctrl.pas} ImgIndex: integer; hWndM: HWND; hDcM: HDC; begin FTopMenu := false; FMenuItem := TMenuItem(Sender); SetGlobalColor(ACanvas); if FMenuItem.Caption = '-' then IsLine := true else IsLine := false; FMenu := FMenuItem.Parent.GetParentMenu; if FMenu is TMainMenu then for i := 0 to FMenuItem.GetParentMenu.Items.Count - 1 do if FMenuItem.GetParentMenu.Items[i] = FMenuItem then begin FTopMenu := True; break; end; ACanvas.Font.Assign(FFont); if FMenu.IsRightToLeft then ACanvas.Font.Charset := ARABIC_CHARSET; Inc(ARect.Bottom, 1); TextRect := ARect; txt := ' ' + FMenuItem.Caption; B := TBitmap.Create; HasImgLstBitmap := false; if FMenuItem.Bitmap.Width > 0 then B.Assign(TBitmap(FMenuItem.Bitmap)); if (FMenuItem.Parent.GetParentMenu.Images <> nil) {$IFDEF VER5U} or (FMenuItem.Parent.SubMenuImages <> nil) {$ENDIF} then begin if FMenuItem.ImageIndex <> -1 then HasImgLstBitmap := true else HasImgLstBitmap := false; end; if FMenu.IsRightToLeft then begin X1 := ARect.Right - FIconWidth; X2 := ARect.Right; end else begin X1 := ARect.Left; X2 := ARect.Left + FIconWidth; end; IconRect := Rect(X1, ARect.Top, X2, ARect.Bottom); if HasImgLstBitmap then begin CheckedRect := IconRect; Inc(CheckedRect.Left, 1); Inc(CheckedRect.Top, 2); Dec(CheckedRect.Right, 3); Dec(CheckedRect.Bottom, 2); end else begin CheckedRect.Left := IconRect.Left + (IConRect.Right - IconRect.Left - 10) div 2; CheckedRect.Top := IconRect.Top + (IConRect.Bottom - IconRect.Top - 10) div 2; CheckedRect.Right := CheckedRect.Left + 10; CheckedRect.Bottom := CheckedRect.Top + 10; end; if FMenu.IsRightToLeft then begin X1 := ARect.Left; X2 := ARect.Right - FIconWidth; if B.Width > FIconWidth then X2 := ARect.Right - B.Width - 4; end else begin X1 := ARect.Left + FIconWidth; if B.Width > X1 then X1 := B.Width + 4; X2 := ARect.Right; end; TextRect := Rect(X1, ARect.Top, X2, ARect.Bottom); if FTopMenu then begin if not HasImgLstBitmap then begin TextRect := ARect; end else begin if FMenu.IsRightToLeft then TextRect.Right := TextRect.Right + 5 else TextRect.Left := TextRect.Left - 5; end end; if FTopMenu then begin ACanvas.brush.color := FFMenuBarColor; ACanvas.Pen.Color := FFMenuBarColor; ACanvas.FillRect(ARect); end else begin if (Is16Bit and FGradient) then begin inc(ARect.Right,2); //needed for RightToLeft DrawGradient(ACanvas, ARect, FMenu.IsRightToLeft); Dec(ARect.Right,2); end else begin ACanvas.brush.color := FFColor; ACanvas.FillRect(ARect); ACanvas.brush.color := FFIconBackColor; ACanvas.FillRect(IconRect); end; //------------ end; if FMenuItem.Enabled then ACanvas.Font.Color := FFont.Color else ACanvas.Font.Color := FDisabledColor; if Selected and FDrawSelect then begin ACanvas.brush.Style := bsSolid; if FTopMenu then begin DrawTopMenuItem(FMenuItem, ACanvas, ARect, FMenu.IsRightToLeft); end else //------ if FMenuItem.Enabled then begin Inc(ARect.Top, 1); Dec(ARect.Bottom, 1); if FFlatMenu then Dec(ARect.Right, 1); ACanvas.brush.color := FFSelectColor; ACanvas.FillRect(ARect); ACanvas.Pen.color := FFSelectBorderColor; ACanvas.Brush.Style := bsClear; ACanvas.RoundRect(Arect.Left, Arect.top, Arect.Right, Arect.Bottom, 0, 0); Dec(ARect.Top, 1); Inc(ARect.Bottom, 1); if FFlatMenu then Inc(ARect.Right, 1); end; //----- end; DrawCheckedItem(FMenuItem, Selected, HasImgLstBitmap, ACanvas, CheckedRect); //----- if HasImgLstBitmap then begin {$IFDEF VER5U} if FMenuItem.Parent.SubMenuImages <> nil then begin //FMenuItem.Parent.SubMenuImages.GetBitmap(FMenuItem.ImageIndex, B); ImgListHandle := FMenuItem.Parent.SubMenuImages.Handle; ImgIndex := FMenuItem.ImageIndex; B.Width := FMenuItem.Parent.SubMenuImages.Width; B.Height := FMenuItem.Parent.SubMenuImages.Height; B.Canvas.Brush.Color := FFIconBackColor; B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height)); ImageList_DrawEx(ImgListHandle, ImgIndex, B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent); end else {$ENDIF} begin //FMenuItem.Parent.GetParentMenu.Images.GetBitmap(FMenuItem.ImageIndex, B); ImgListHandle := FMenuItem.Parent.GetParentMenu.Images.Handle; ImgIndex := FMenuItem.ImageIndex; B.Width := FMenuItem.Parent.GetParentMenu.Images.Width; B.Height := FMenuItem.Parent.GetParentMenu.Images.Height; B.Canvas.Brush.Color := FFIconBackColor; B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height)); ImageList_DrawEx(ImgListHandle, ImgIndex, B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent); end; end else if FMenuItem.Bitmap.Width > 0 then B.Assign(TBitmap(FMenuItem.Bitmap)); DrawIcon(FMenuItem, ACanvas, B, IconRect, Selected, False, FMenuItem.Enabled, FMenuItem.Checked, FTopMenu, FMenu.IsRightToLeft); //-------- if not IsLine then begin if FMenu.IsRightToLeft then begin TextFormat := DT_RIGHT + DT_RTLREADING; Dec(TextRect.Right, 5); end else begin TextFormat := 0; Inc(TextRect.Left, 5); end; DrawTheText(txt, ShortCutToText(FMenuItem.ShortCut), ACanvas, TextRect, Selected, FMenuItem.Enabled, FMenuItem.Default, FTopMenu, FMenu.IsRightToLeft, TextFormat); //----------- end else begin if FMenu.IsRightToLeft then begin X1 := TextRect.Left; X2 := TextRect.Right - 7; end else begin X1 := TextRect.Left + 7; X2 := TextRect.Right; end; ACanvas.Pen.Color := FFSeparatorColor; ACanvas.MoveTo(X1, TextRect.Top + Round((TextRect.Bottom - TextRect.Top) / 2)); ACanvas.LineTo(X2, TextRect.Top + Round((TextRect.Bottom - TextRect.Top) / 2)) end; B.free; //------ if not (csDesigning in ComponentState) then begin if (FFlatMenu) and (not FTopMenu) then begin hDcM := ACanvas.Handle; hWndM := WindowFromDC(hDcM); if hWndM <> FForm.Handle then begin DrawWindowBorder(hWndM, FMenu.IsRightToLeft); end; end; end; //----- ActivateMenuItem(FMenuItem); // to check for new sub items end; procedure TXPMenu.ToolBarDrawButton(Sender: TToolBar; Button: TToolButton; State: TCustomDrawState; var DefaultDraw: Boolean); var ACanvas: TCanvas; ARect, HoldRect: TRect; B: TBitmap; HasBitmap: boolean; BitmapWidth: integer; TextFormat: integer; XButton: TToolButton; HasBorder: boolean; HasBkg: boolean; IsTransparent: boolean; FBSelectColor: TColor; procedure DrawBorder; var BRect, WRect: TRect; procedure DrawRect; begin ACanvas.Pen.color := FFSelectBorderColor; ACanvas.MoveTo(WRect.Left, WRect.Top); ACanvas.LineTo(WRect.Right, WRect.Top); ACanvas.LineTo(WRect.Right, WRect.Bottom); ACanvas.LineTo(WRect.Left, WRect.Bottom); ACanvas.LineTo(WRect.Left, WRect.Top); end; begin BRect := HoldRect; Dec(BRect.Bottom, 1); Inc(BRect.Top, 1); Dec(BRect.Right, 1); WRect := BRect; if Button.Style = tbsDropDown then begin Dec(WRect.Right, 13); DrawRect; WRect := BRect; Inc(WRect.Left, WRect.Right - WRect.Left - 13); DrawRect; end else begin DrawRect; end; end; begin B := nil; HasBitmap := (TToolBar(Button.Parent).Images <> nil) and (Button.ImageIndex <> -1) and (Button.ImageIndex <= TToolBar(Button.Parent).Images.Count - 1); IsTransparent := TToolBar(Button.Parent).Transparent; ACanvas := Sender.Canvas; SetGlobalColor(ACanvas); if (Is16Bit) and (not UseSystemColors) then FBSelectColor := NewColor(ACanvas, FSelectColor, 68) else FBSelectColor := FFSelectColor; HoldRect := Button.BoundsRect; ARect := HoldRect; //if FUseSystemColors then begin if (Button.MenuItem <> nil) then begin if (TToolBar(Button.Parent).Font.Name <> FFont.Name) or (TToolBar(Button.Parent).Font.Size <> FFont.Size) then begin TToolBar(Button.Parent).Font.Assign(FFont); Button.AutoSize := false; Button.AutoSize := true; end; end end; if Is16Bit then ACanvas.brush.color := NewColor(ACanvas, clBtnFace, 16) else ACanvas.brush.color := clBtnFace; if not IsTransparent then ACanvas.FillRect(ARect); HasBorder := false; HasBkg := false; if (cdsHot in State) then begin if (cdsChecked in State) or (Button.Down) or (cdsSelected in State) then ACanvas.Brush.Color := FCheckedAreaSelectColor else ACanvas.brush.color := FBSelectColor; HasBorder := true; HasBkg := true; end; if (cdsChecked in State) and not (cdsHot in State) then begin ACanvas.Brush.Color := FCheckedAreaColor; HasBorder := true; HasBkg := true; end; if (cdsIndeterminate in State) and not (cdsHot in State) then begin ACanvas.Brush.Color := FBSelectColor; HasBkg := true; end; if (Button.MenuItem <> nil) and (State = []) then begin ACanvas.brush.color := FFMenuBarColor; if not IsTransparent then HasBkg := true; end; Inc(ARect.Top, 1); if HasBkg then ACanvas.FillRect(ARect); if HasBorder then DrawBorder; if (Button.MenuItem <> nil) and (cdsSelected in State) then begin DrawTopMenuItem(Button, ACanvas, ARect, false); DefaultDraw := false; end; ARect := HoldRect; DefaultDraw := false; if Button.Style = tbsDropDown then begin ACanvas.Pen.Color := clBlack; DrawArrow(ACanvas, (ARect.Right - 14) + ((14 - 5) div 2), ARect.Top + ((ARect.Bottom - ARect.Top - 3) div 2) + 1); end; BitmapWidth := 0; if HasBitmap then begin try B := TBitmap.Create; //TToolBar(Button.Parent).Images.GetBitmap(Button.ImageIndex, B); B.Width := TToolBar(Button.Parent).Images.Width; B.Height := TToolBar(Button.Parent).Images.Height; B.Canvas.Brush.Color := ACanvas.Brush.Color; B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height)); ImageList_DrawEx(TToolBar(Button.Parent).Images.Handle, Button.ImageIndex, B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent); ImgLstHandle:= TToolBar(Button.Parent).Images.Handle; ImgLstIndex:= Button.ImageIndex; BitmapWidth := b.Width; if Button.Style = tbsDropDown then Dec(ARect.Right, 12); if TToolBar(Button.Parent).List then begin if Button.BiDiMode = bdRightToLeft then begin Dec(ARect.Right, 3); ARect.Left := ARect.Right - BitmapWidth; end else begin Inc(ARect.Left, 3); ARect.Right := ARect.Left + BitmapWidth end end else ARect.Left := Round(ARect.Left + (ARect.Right - ARect.Left - B.Width)/2); inc(ARect.Top, 2); ARect.Bottom := ARect.Top + B.Height + 6; DrawIcon(Button, ACanvas, B, ARect, (cdsHot in State), (cdsSelected in State), Button.Enabled, (cdsChecked in State), false, false); finally B.Free; end; ARect := HoldRect; DefaultDraw := false; end; //----------- if TToolBar(Button.Parent).ShowCaptions then begin if Button.Style = tbsDropDown then Dec(ARect.Right, 12); if not TToolBar(Button.Parent).List then begin TextFormat := DT_Center; ARect.Top := ARect.Bottom - ACanvas.TextHeight(Button.Caption) - 3; end else begin TextFormat := DT_VCENTER; if Button.BiDiMode = bdRightToLeft then begin TextFormat := TextFormat + DT_Right; Dec(ARect.Right, BitmapWidth + 7); end else begin Inc(ARect.Left, BitmapWidth + 6); end end; if (Button.MenuItem <> nil) then begin TextFormat := DT_Center; end; if Button.BiDiMode = bdRightToLeft then TextFormat := TextFormat + DT_RTLREADING; DrawTheText(Button.Caption, '', ACanvas, ARect, (cdsSelected in State), Button.Enabled, false, (Button.MenuItem <> nil), (Button.BidiMode = bdRightToLeft), TextFormat); ARect := HoldRect; DefaultDraw := false; end; if Button.Index > 0 then begin XButton := TToolBar(Button.Parent).Buttons[Button.Index - 1]; if (XButton.Style = tbsDivider) or (XButton.Style = tbsSeparator) then begin ARect := XButton.BoundsRect; if Is16Bit then ACanvas.brush.color := NewColor(ACanvas, clBtnFace, 16) else ACanvas.brush.color := clBtnFace; if not IsTransparent then ACanvas.FillRect(ARect); // if (XButton.Style = tbsDivider) then // Can't get it. if XButton.Tag > 0 then begin Inc(ARect.Top, 2); Dec(ARect.Bottom, 1); ACanvas.Pen.color := FFDisabledColor; ARect.Left := ARect.Left + (ARect.Right - ARect.Left) div 2; ACanvas.MoveTo(ARect.Left, ARect.Top); ACanvas.LineTo(ARect.Left, ARect.Bottom); end; ARect := Button.BoundsRect; DefaultDraw := false; end; end; if Button.MenuItem <> nil then ActivateMenuItem(Button.MenuItem); end; procedure TXPMenu.SetGlobalColor(ACanvas: TCanvas); begin //----- if GetDeviceCaps(ACanvas.Handle, BITSPIXEL) < 16 then Is16Bit := false else Is16Bit := true; FFColor := FColor; FFIconBackColor := FIconBackColor; FFSelectColor := FSelectColor; if Is16Bit then begin FCheckedAreaColor := NewColor(ACanvas, FSelectColor, 75); FCheckedAreaSelectColor := NewColor(ACanvas, FSelectColor, 50); FMenuBorderColor := GetShadeColor(ACanvas, clBtnFace, 90); FMenuShadowColor := GetShadeColor(ACanvas, clBtnFace, 76); end else begin FFSelectColor := FSelectColor; FCheckedAreaColor := clWhite; FCheckedAreaSelectColor := clSilver; FMenuBorderColor := clBtnShadow; FMenuShadowColor := clBtnShadow; end; FFSelectBorderColor := FSelectBorderColor; FFSelectFontColor := FSelectFontColor; FFMenuBarColor := FMenuBarColor; FFDisabledColor := FDisabledColor; FFCheckedColor := FCheckedColor; FFSeparatorColor := FSeparatorColor; if FUseSystemColors then begin GetSystemMenuFont(FFont); FFSelectFontColor := FFont.Color; if not Is16Bit then begin FFColor := clWhite; FFIconBackColor := clBtnFace; FFSelectColor := clWhite; FFSelectBorderColor := clHighlight; FFMenuBarColor := FFIconBackColor; FFDisabledColor := clBtnShadow; FFCheckedColor := clHighlight; FFSeparatorColor := clBtnShadow; FCheckedAreaColor := clWhite; FCheckedAreaSelectColor := clWhite; end else begin FFColor := NewColor(ACanvas, clBtnFace, 86); FFIconBackColor := NewColor(ACanvas, clBtnFace, 16); FFSelectColor := NewColor(ACanvas, clHighlight, 68); FFSelectBorderColor := clHighlight; FFMenuBarColor := clMenu; FFDisabledColor := NewColor(ACanvas, clBtnShadow, 40); FFSeparatorColor := NewColor(ACanvas, clBtnShadow, 25); FFCheckedColor := clHighlight; FCheckedAreaColor := NewColor(ACanvas, clHighlight, 75); FCheckedAreaSelectColor := NewColor(ACanvas, clHighlight, 50); end; end; end; procedure TXPMenu.DrawTopMenuItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect; IsRightToLeft: boolean); var X1, X2: integer; DefColor, HoldColor: TColor; begin X1 := ARect.Left; X2 := ARect.Right; ACanvas.brush.Style := bsSolid; ACanvas.brush.color := FFIconBackColor; ACanvas.FillRect(ARect); ACanvas.Pen.Color := FMenuBorderColor; if (not IsRightToLeft) and (Is16Bit) and (Sender is TMenuItem) then begin ACanvas.MoveTo(X1, ARect.Bottom - 1); ACanvas.LineTo(X1, ARect.Top); ACanvas.LineTo(X2 - 8, ARect.Top); ACanvas.LineTo(X2 - 8, ARect.Bottom); DefColor := FFMenuBarColor; HoldColor := GetShadeColor(ACanvas, DefColor, 10); ACanvas.Brush.Style := bsSolid; ACanvas.Brush.Color := HoldColor; ACanvas.Pen.Color := HoldColor; ACanvas.FillRect(Rect(X2 - 7, ARect.Top, X2, ARect.Bottom)); HoldColor := GetShadeColor(ACanvas, DefColor, 30); ACanvas.Brush.Color := HoldColor; ACanvas.Pen.Color := HoldColor; ACanvas.FillRect(Rect(X2 - 7, ARect.Top + 3, X2 - 2, ARect.Bottom)); HoldColor := GetShadeColor(ACanvas, DefColor, 40 + 20); ACanvas.Brush.Color := HoldColor; ACanvas.Pen.Color := HoldColor; ACanvas.FillRect(Rect(X2 - 7, ARect.Top + 5, X2 - 3, ARect.Bottom)); HoldColor := GetShadeColor(ACanvas, DefColor, 60 + 40); ACanvas.Brush.Color := HoldColor; ACanvas.Pen.Color := HoldColor; ACanvas.FillRect(Rect(X2 - 7, ARect.Top + 6, X2 - 5, ARect.Bottom)); //--- ACanvas.Pen.Color := DefColor; ACanvas.MoveTo(X2 - 5, ARect.Top + 1); ACanvas.LineTo(X2 - 1, ARect.Top + 1); ACanvas.LineTo(X2 - 1, ARect.Top + 6); ACanvas.MoveTo(X2 - 3, ARect.Top + 2); ACanvas.LineTo(X2 - 2, ARect.Top + 2); ACanvas.LineTo(X2 - 2, ARect.Top + 3); ACanvas.LineTo(X2 - 3, ARect.Top + 3); ACanvas.Pen.Color := GetShadeColor(ACanvas, DefColor, 10); ACanvas.MoveTo(X2 - 6, ARect.Top + 3); ACanvas.LineTo(X2 - 3, ARect.Top + 3); ACanvas.LineTo(X2 - 3, ARect.Top + 6); ACanvas.LineTo(X2 - 4, ARect.Top + 6); ACanvas.LineTo(X2 - 4, ARect.Top + 3); ACanvas.Pen.Color := GetShadeColor(ACanvas, DefColor, 30); ACanvas.MoveTo(X2 - 5, ARect.Top + 5); ACanvas.LineTo(X2 - 4, ARect.Top + 5); ACanvas.LineTo(X2 - 4, ARect.Top + 9); ACanvas.Pen.Color := GetShadeColor(ACanvas, DefColor, 40); ACanvas.MoveTo(X2 - 6, ARect.Top + 5); ACanvas.LineTo(X2 - 6, ARect.Top + 7); end else begin ACanvas.Pen.Color := FMenuBorderColor; ACanvas.Brush.Color := FMenuShadowColor; ACanvas.MoveTo(X1, ARect.Bottom - 1); ACanvas.LineTo(X1, ARect.Top); ACanvas.LineTo(X2 - 3, ARect.Top); ACanvas.LineTo(X2 - 3, ARect.Bottom); ACanvas.Pen.Color := ACanvas.Brush.Color; ACanvas.FillRect(Rect(X2 - 2, ARect.Top + 2, X2, ARect.Bottom)); end; end; procedure TXPMenu.DrawCheckedItem(FMenuItem: TMenuItem; Selected, HasImgLstBitmap: boolean; ACanvas: TCanvas; CheckedRect: TRect); var X1, X2: integer; begin if FMenuItem.RadioItem then begin if FMenuItem.Checked then begin ACanvas.Pen.color := FFSelectBorderColor; if selected then ACanvas.Brush.Color := FCheckedAreaSelectColor else ACanvas.Brush.Color := FCheckedAreaColor; ACanvas.Brush.Style := bsSolid; if HasImgLstBitmap then begin ACanvas.RoundRect(CheckedRect.Left, CheckedRect.Top, CheckedRect.Right, CheckedRect.Bottom, 6, 6); end else begin ACanvas.Ellipse(CheckedRect.Left, CheckedRect.Top, CheckedRect.Right, CheckedRect.Bottom); end; end; end else begin if (FMenuItem.Checked) then if (not HasImgLstBitmap) then begin ACanvas.Pen.color := FFCheckedColor; if selected then ACanvas.Brush.Color := FCheckedAreaSelectColor else ACanvas.Brush.Color := FCheckedAreaColor; ; ACanvas.Brush.Style := bsSolid; ACanvas.Rectangle(CheckedRect.Left, CheckedRect.Top, CheckedRect.Right, CheckedRect.Bottom); ACanvas.Pen.color := clBlack; x1 := CheckedRect.Left + 1; x2 := CheckedRect.Top + 5; ACanvas.MoveTo(x1, x2); x1 := CheckedRect.Left + 4; x2 := CheckedRect.Bottom - 2; ACanvas.LineTo(x1, x2); //-- x1 := CheckedRect.Left + 2; x2 := CheckedRect.Top + 5; ACanvas.MoveTo(x1, x2); x1 := CheckedRect.Left + 4; x2 := CheckedRect.Bottom - 3; ACanvas.LineTo(x1, x2); //-- x1 := CheckedRect.Left + 2; x2 := CheckedRect.Top + 4; ACanvas.MoveTo(x1, x2); x1 := CheckedRect.Left + 5; x2 := CheckedRect.Bottom - 3; ACanvas.LineTo(x1, x2); //----------------- x1 := CheckedRect.Left + 4; x2 := CheckedRect.Bottom - 3; ACanvas.MoveTo(x1, x2); x1 := CheckedRect.Right + 2; x2 := CheckedRect.Top - 1; ACanvas.LineTo(x1, x2); //-- x1 := CheckedRect.Left + 4; x2 := CheckedRect.Bottom - 2; ACanvas.MoveTo(x1, x2); x1 := CheckedRect.Right - 2; x2 := CheckedRect.Top + 3; ACanvas.LineTo(x1, x2); end else begin ACanvas.Pen.color := FFSelectBorderColor; if selected then ACanvas.Brush.Color := FCheckedAreaSelectColor else ACanvas.Brush.Color := FCheckedAreaColor; ACanvas.Brush.Style := bsSolid; ACanvas.Rectangle(CheckedRect.Left, CheckedRect.Top, CheckedRect.Right, CheckedRect.Bottom); end; end; end; procedure TXPMenu.DrawTheText(txt, ShortCuttext: string; ACanvas: TCanvas; TextRect: TRect; Selected, Enabled, Default, TopMenu, IsRightToLeft: boolean; TextFormat: integer); var DefColor: TColor; begin DefColor := FFont.Color; ACanvas.Font := FFont; if Enabled then DefColor := FFont.Color; if Selected then DefColor := FFSelectFontColor; if not Enabled then begin DefColor := FFDisabledColor; if Selected then if Is16Bit then DefColor := NewColor(ACanvas, FFDisabledColor, 30); end; if (TopMenu and Selected) then DefColor := TopMenuFontColor(ACanvas, FFIconBackColor); ACanvas.Font.color := DefColor; // will not affect Buttons TextRect.Top := TextRect.Top + ((TextRect.Bottom - TextRect.Top) - ACanvas.TextHeight('W')) div 2; SetBkMode(ACanvas.Handle, TRANSPARENT); if Default and Enabled then begin Inc(TextRect.Left, 1); ACanvas.Font.color := GetShadeColor(ACanvas, ACanvas.Pixels[TextRect.Left, TextRect.Top], 30); DrawtextEx(ACanvas.Handle, PChar(txt), Length(txt), TextRect, TextFormat, nil); Dec(TextRect.Left, 1); Inc(TextRect.Top, 2); Inc(TextRect.Left, 1); Inc(TextRect.Right, 1); ACanvas.Font.color := GetShadeColor(ACanvas, ACanvas.Pixels[TextRect.Left, TextRect.Top], 30); DrawtextEx(ACanvas.Handle, PChar(txt), Length(txt), TextRect, TextFormat, nil); Dec(TextRect.Top, 1); Dec(TextRect.Left, 1); Dec(TextRect.Right, 1); ACanvas.Font.color := GetShadeColor(ACanvas, ACanvas.Pixels[TextRect.Left, TextRect.Top], 40); DrawtextEx(ACanvas.Handle, PChar(txt), Length(txt), TextRect, TextFormat, nil); Inc(TextRect.Left, 1); Inc(TextRect.Right, 1); ACanvas.Font.color := GetShadeColor(ACanvas, ACanvas.Pixels[TextRect.Left, TextRect.Top], 60); DrawtextEx(ACanvas.Handle, PChar(txt), Length(txt), TextRect, TextFormat, nil); Dec(TextRect.Left, 1); Dec(TextRect.Right, 1); Dec(TextRect.Top, 1); ACanvas.Font.color := DefColor; end; DrawtextEx(ACanvas.Handle, PChar(txt), Length(txt), TextRect, TextFormat, nil); txt := ShortCutText + ' '; if not Is16Bit then ACanvas.Font.color := DefColor else ACanvas.Font.color := GetShadeColor(ACanvas, DefColor, -40); if IsRightToLeft then begin Inc(TextRect.Left, 10); TextFormat := DT_LEFT end else begin Dec(TextRect.Right, 10); TextFormat := DT_RIGHT; end; DrawtextEx(ACanvas.Handle, PChar(txt), Length(txt), TextRect, TextFormat, nil); end; procedure TXPMenu.DrawIcon(Sender: TObject; ACanvas: TCanvas; B: TBitmap; IconRect: Trect; Hot, Selected, Enabled, Checked, FTopMenu, IsRightToLeft: boolean); var DefColor: TColor; X1, X2: integer; begin if B <> nil then begin X1 := IconRect.Left; X2 := IconRect.Top + 2; if Sender is TMenuItem then begin inc(X2, 2); if FIconWidth >= B.Width then X1 := X1 + ((FIconWidth - B.Width) div 2) - 1 else begin if IsRightToLeft then X1 := IconRect.Right - b.Width - 2 else X1 := IconRect.Left + 2; end; end; if (Hot) and (not FTopMenu) and (Enabled) and (not Checked) then if not Selected then begin dec(X1, 1); dec(X2, 1); end; if (not Hot) and (Enabled) and (not Checked) then if Is16Bit then DimBitmap(B, 30); if (not Hot) and (not Enabled) then GrayBitmap(B, 60); if (Hot) and (not Enabled) then GrayBitmap(B, 80); if (Hot) and (Enabled) and (not Checked) then begin if (Is16Bit) and (not UseSystemColors) and (Sender is TToolButton) then DefColor := NewColor(ACanvas, FSelectColor, 68) else DefColor := FFSelectColor; DefColor := GetShadeColor(ACanvas, DefColor, 50); DrawBitmapShadow(B, ACanvas, X1 + 2, X2 + 2, DefColor); end; B.Transparent := true; ACanvas.Draw(X1, X2, B); end; end; procedure TXPMenu.DrawArrow(ACanvas: TCanvas; X, Y: integer); begin ACanvas.MoveTo(X, Y); ACanvas.LineTo(X + 4, Y); ACanvas.MoveTo(X + 1, Y + 1); ACanvas.LineTo(X + 4, Y); ACanvas.MoveTo(X + 2, Y + 2); ACanvas.LineTo(X + 3, Y); end; function TXPMenu.TopMenuFontColor(ACanvas: TCanvas; Color: TColor): TColor; var r, g, b, avg: integer; begin Color := ColorToRGB(Color); r := Color and $000000FF; g := (Color and $0000FF00) shr 8; b := (Color and $00FF0000) shr 16; Avg := (r + b) div 2; if (Avg > 150) or (g > 200) then Result := FFont.Color else Result := NewColor(ACanvas, Color, 90); // Result := FColor; end; procedure TXPMenu.SetActive(const Value: boolean); begin FActive := Value; if FActive then begin InitMenueItems(false); InitMenueItems(true); end else InitMenueItems(false); // Tolik 03/05/2019 // Windows.DrawMenuBar(FForm.Handle); // end; procedure TXPMenu.SetForm(const Value: TForm); var Hold: boolean; begin if Value <> FForm then begin Hold := Active; Active := false; FForm := Value; if Hold then Active := True; end; end; procedure TXPMenu.SetFont(const Value: TFont); begin FFont.Assign(Value); Windows.DrawMenuBar(FForm.Handle); end; procedure TXPMenu.SetColor(const Value: TColor); begin FColor := Value; end; procedure TXPMenu.SetIconBackColor(const Value: TColor); begin FIconBackColor := Value; end; procedure TXPMenu.SetMenuBarColor(const Value: TColor); begin FMenuBarColor := Value; Windows.DrawMenuBar(FForm.Handle); end; procedure TXPMenu.SetCheckedColor(const Value: TColor); begin FCheckedColor := Value; end; procedure TXPMenu.SetSeparatorColor(const Value: TColor); begin FSeparatorColor := Value; end; procedure TXPMenu.SetSelectBorderColor(const Value: TColor); begin FSelectBorderColor := Value; end; procedure TXPMenu.SetSelectColor(const Value: TColor); begin FSelectColor := Value; end; procedure TXPMenu.SetDisabledColor(const Value: TColor); begin FDisabledColor := Value; end; procedure TXPMenu.SetSelectFontColor(const Value: TColor); begin FSelectFontColor := Value; end; procedure TXPMenu.SetIconWidth(const Value: integer); begin FIconWidth := Value; end; procedure TXPMenu.SetDrawSelect(const Value: boolean); begin FDrawSelect := Value; end; procedure TXPMenu.SetOverrideOwnerDraw(const Value: boolean); begin FOverrideOwnerDraw := Value; if FActive then Active := True; end; procedure TXPMenu.SetUseSystemColors(const Value: boolean); begin FUseSystemColors := Value; Windows.DrawMenuBar(FForm.Handle); end; { procedure TXPMenu.SetRefreshOnChange(const Value: boolean); begin FRefreshOnChange := Value; if not (csDesigning in ComponentState) and (FActive) then Active := True; end; procedure TXPMenu.MenuChange(Sender: TObject; Source: TMenuItem; Rebuild: Boolean); begin if not (csDesigning in ComponentState) then if (Source <> nil) then if ComponentState = [] then Active := true ; end; } procedure TXPMenu.SetGradient(const Value: boolean); begin FGradient := Value; end; procedure TXPMenu.SetFlatMenu(const Value: boolean); begin FFlatMenu := Value; end; procedure GetSystemMenuFont(Font: TFont); var FNonCLientMetrics: TNonCLientMetrics; begin FNonCLientMetrics.cbSize := Sizeof(TNonCLientMetrics); if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @FNonCLientMetrics,0) then begin Font.Handle := CreateFontIndirect(FNonCLientMetrics.lfMenuFont); Font.Color := clMenuText; if Font.Name = 'MS Sans Serif' then Font.Name := 'Tahoma'; end; end; procedure TXPMenu.DrawGradient(ACanvas: TCanvas; ARect: TRect; IsRightToLeft: boolean); var i: integer; v: integer; FRect: TRect; begin fRect := ARect; V := 0; if IsRightToLeft then begin fRect.Left := fRect.Right - 1; for i := ARect.Right Downto ARect.Left do begin if (fRect.Left < ARect.Right) and (fRect.Left > ARect.Right - FIconWidth + 5) then inc(v, 3) else inc(v, 1); if v > 96 then v := 96; ACanvas.Brush.Color := NewColor(ACanvas, FFIconBackColor, v); ACanvas.FillRect(fRect); fRect.Left := fRect.Left - 1; fRect.Right := fRect.Left - 1; end; end else begin fRect.Right := fRect.Left + 1; for i := ARect.Left to ARect.Right do begin if (fRect.Left > ARect.Left) and (fRect.Left < ARect.Left + FIconWidth + 5) then inc(v, 3) else inc(v, 1); if v > 96 then v := 96; ACanvas.Brush.Color := NewColor(ACanvas, FFIconBackColor, v); ACanvas.FillRect(fRect); fRect.Left := fRect.Left + 1; fRect.Right := fRect.Left + 1; end; end; end; procedure TXPMenu.DrawWindowBorder(hWnd: HWND; IsRightToLeft: boolean); var WRect, CRect: TRect; dCanvas: TCanvas; begin if hWnd <= 0 then begin exit; end; dCanvas := nil; try dCanvas := TCanvas.Create; dCanvas.Handle := GetDc(0); GetClientRect(hWnd, CRect); GetWindowRect(hWnd, WRect); ExcludeClipRect(dCanvas.Handle, CRect.Left, CRect.Top, CRect.Right, CRect.Bottom); dCanvas.Brush.Style := bsClear; Dec(WRect.Right, 2); Dec(WRect.Bottom, 2); dCanvas.Pen.Color := FMenuBorderColor; dCanvas.Rectangle(WRect.Left, WRect.Top, WRect.Right, WRect.Bottom); if IsRightToLeft then begin dCanvas.Pen.Color := FFColor; dCanvas.Rectangle(WRect.Left + 1, WRect.Top + 1, WRect.Right - 2, WRect.Top + 3); dCanvas.MoveTo(WRect.Left + 2, WRect.Top + 2); dCanvas.LineTo(WRect.Left + 2, WRect.Bottom - 2); dCanvas.Pen.Color := FFIconBackColor; dCanvas.MoveTo(WRect.Right - 2, WRect.Top + 2); dCanvas.LineTo(WRect.Right - 2, WRect.Bottom - 2); dCanvas.MoveTo(WRect.Right - 2, WRect.Top + 2); dCanvas.LineTo(WRect.Right - 1 - FIconWidth, WRect.Top + 2); end else begin if not FGradient then begin dCanvas.Pen.Color := FFColor; dCanvas.Rectangle(WRect.Left + 1, WRect.Top + 1, WRect.Right - 2, WRect.Top + 3); dCanvas.Pen.Color := FFIconBackColor; dCanvas.MoveTo(WRect.Left + 1, WRect.Top + 2); dCanvas.LineTo(WRect.Left + 2 + FIconWidth, WRect.Top + 2); end; dCanvas.Pen.Color := FFIconBackColor; dCanvas.MoveTo(WRect.Left + 1, WRect.Top + 1); dCanvas.LineTo(WRect.Left + 1, WRect.Bottom - 2); end; Inc(WRect.Right, 2); Inc(WRect.Bottom, 2); dCanvas.Pen.Color := FMenuShadowColor; dCanvas.Rectangle(WRect.Left +2, WRect.Bottom, WRect.Right, WRect.Bottom - 2); dCanvas.Rectangle(WRect.Right - 2, WRect.Bottom, WRect.Right, WRect.Top + 2); dCanvas.Pen.Color := FFIconBackColor; dCanvas.Rectangle(WRect.Left, WRect.Bottom - 2, WRect.Left + 2, WRect.Bottom); dCanvas.Rectangle(WRect.Right - 2, WRect.Top, WRect.Right, WRect.Top + 2); finally IntersectClipRect(dCanvas.Handle, WRect.Left, WRect.Top, WRect.Right, WRect.Bottom); dCanvas.Free; end; end; procedure TXPMenu.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if (Operation = opInsert) and ((AComponent is TMenuItem) or (AComponent is TToolButton)) then begin if (csDesigning in ComponentState) then Active := true else //if ComponentState = [] then Active := true ; end; end; function GetShadeColor(ACanvas: TCanvas; clr: TColor; Value: integer): TColor; var r, g, b: integer; begin clr := ColorToRGB(clr); r := Clr and $000000FF; g := (Clr and $0000FF00) shr 8; b := (Clr and $00FF0000) shr 16; r := (r - value); if r < 0 then r := 0; if r > 255 then r := 255; g := (g - value) + 2; if g < 0 then g := 0; if g > 255 then g := 255; b := (b - value); if b < 0 then b := 0; if b > 255 then b := 255; Result := Windows.GetNearestColor(ACanvas.Handle, RGB(r, g, b)); end; function NewColor(ACanvas: TCanvas; clr: TColor; Value: integer): TColor; var r, g, b: integer; begin if Value > 100 then Value := 100; clr := ColorToRGB(clr); r := Clr and $000000FF; g := (Clr and $0000FF00) shr 8; b := (Clr and $00FF0000) shr 16; r := r + Round((255 - r) * (value / 100)); g := g + Round((255 - g) * (value / 100)); b := b + Round((255 - b) * (value / 100)); Result := Windows.GetNearestColor(ACanvas.Handle, RGB(r, g, b)); end; function GrayColor(ACanvas: TCanvas; clr: TColor; Value: integer): TColor; var r, g, b, avg: integer; begin if Value > 100 then Value := 100; clr := ColorToRGB(clr); r := Clr and $000000FF; g := (Clr and $0000FF00) shr 8; b := (Clr and $00FF0000) shr 16; Avg := (r + g + b) div 3; Avg := Avg + Value; if Avg > 240 then Avg := 240; Result := Windows.GetNearestColor (ACanvas.Handle,RGB(Avg, avg, avg)); end; procedure GrayBitmap(ABitmap: TBitmap; Value: integer); var x, y: integer; LastColor1, LastColor2, Color: TColor; begin LastColor1 := 0; LastColor2 := 0; for y := 0 to ABitmap.Height do for x := 0 to ABitmap.Width do begin Color := ABitmap.Canvas.Pixels[x, y]; if Color = LastColor1 then ABitmap.Canvas.Pixels[x, y] := LastColor2 else begin LastColor2 := GrayColor(ABitmap.Canvas , Color, Value); ABitmap.Canvas.Pixels[x, y] := LastColor2; LastColor1 := Color; end; end; end; procedure DimBitmap(ABitmap: TBitmap; Value: integer); var x, y: integer; LastColor1, LastColor2, Color: TColor; begin if Value > 100 then Value := 100; LastColor1 := -1; LastColor2 := -1; for y := 0 to ABitmap.Height - 1 do for x := 0 to ABitmap.Width - 1 do begin Color := ABitmap.Canvas.Pixels[x, y]; if Color = LastColor1 then ABitmap.Canvas.Pixels[x, y] := LastColor2 else begin LastColor2 := NewColor(ABitmap.Canvas, Color, Value); ABitmap.Canvas.Pixels[x, y] := LastColor2; LastColor1 := Color; end; end; end; procedure DrawBitmapShadow(B: TBitmap; ACanvas: TCanvas; X, Y: integer; ShadowColor: TColor); var BX, BY: integer; TransparentColor: TColor; begin TransparentColor := B.Canvas.Pixels[0, B.Height - 1]; for BY := 0 to B.Height - 1 do for BX := 0 to B.Width - 1 do begin if B.Canvas.Pixels[BX, BY] <> TransparentColor then ACanvas.Pixels[X + BX, Y + BY] := ShadowColor; end; end;