/////////////////////////////////////////////////////////////////////////////
// Name: src/osx/cocoa/toolbar.mm
// Purpose: wxToolBar
// Author: Stefan Csomor
// Created: 04/01/98
// Copyright: (c) Stefan Csomor
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#include "wx/wxprec.h"

#if wxUSE_TOOLBAR

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "wx/toolbar.h"
#include "wx/app.h"
#include "wx/osx/private.h"
#include "wx/osx/private/available.h"
#include "wx/private/bmpbndl.h"
#include "wx/geometry.h"
#include "wx/sysopt.h"

const short kwxMacToolBarToolDefaultWidth = 16;
const short kwxMacToolBarToolDefaultHeight = 16;
const short kwxMacToolBarTopMargin = 4;
const short kwxMacToolBarLeftMargin = 4;
const short kwxMacToolBorder = 0;
const short kwxMacToolSpacing = 6;

wxBEGIN_EVENT_TABLE(wxToolBar, wxToolBarBase)
EVT_PAINT( wxToolBar::OnPaint )
wxEND_EVENT_TABLE()

#pragma mark -
#pragma mark Tool Implementation

// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------

class wxToolBarTool;

@interface wxNSToolBarButton : NSButton
{
wxToolBarTool* impl;
}

- (id)initWithFrame:(NSRect)frame;
- (void) clickedAction: (id) sender;
- (void)setImplementation: (wxToolBarTool *) theImplementation;
- (wxToolBarTool*) implementation;
- (BOOL) isFlipped;

@end

// We have a dual implementation for each tool, WXWidget and NSToolbarItem*

// when embedding native controls in the native toolbar we must make sure the
// control does not get deleted behind our backs, so the retain count gets increased
// (after creation it is 1), first be the creation of the custom NSToolbarItem wrapper
// object, and second by the code 'creating' the custom HIView (which is the same as the
// already existing native control, therefore we just increase the ref count)
// when this view is removed from the native toolbar its count gets decremented again
// and when the HITooolbarItem wrapper object gets destroyed it is decremented as well
// so in the end the control lives with a refcount of one and can be disposed of by the
// wxControl code. For embedded controls on a non-native toolbar this ref count is less
// so we can only test against a range, not a specific value of the refcount.

class wxToolBarTool : public wxToolBarToolBase
{
public:
wxToolBarTool(
wxToolBar *tbar,
int id,
const wxString& label,
const wxBitmapBundle& bmpNormal,
const wxBitmapBundle& bmpDisabled,
wxItemKind kind,
wxObject *clientData,
const wxString& shortHelp,
const wxString& longHelp );

wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label)
: wxToolBarToolBase(tbar, control, label)
{
Init();
if (control != nullptr)
SetControlHandle( (WXWidget) control->GetHandle() );
}

virtual ~wxToolBarTool()
{
ClearControl();
}

WXWidget GetControlHandle()
{
return (WXWidget) m_controlHandle;
}

void SetControlHandle( WXWidget handle )
{
m_controlHandle = handle;
}

void SetPosition( const wxPoint& position );

void ClearControl()
{
if ( m_controlHandle )
{
if ( !IsControl() )
{
[m_controlHandle removeFromSuperview];
[m_controlHandle release];
}
else
{
// the embedded control is not under the responsibility of the tool, it gets disposed of in the
// proper wxControl destructor
}
m_controlHandle = nullptr ;
}

#if wxOSX_USE_NATIVE_TOOLBAR
if ( m_toolbarItem )
{
[m_toolbarItem release];
m_toolbarItem = nullptr;
}
#endif // wxOSX_USE_NATIVE_TOOLBAR
}

wxSize GetSize() const
{
wxSize curSize;

if ( IsControl() )
{
curSize = GetControl()->GetSize();
}
else if ( IsButton() )
{
// curSize = GetToolBar()->GetToolSize();
NSRect best = [(wxNSToolBarButton*)m_controlHandle frame];
curSize = wxSize(best.size.width, best.size.height);
}
else
{
// separator size
curSize = GetToolBar()->GetToolSize();
if ( GetToolBar()->IsVertical() )
curSize.y /= 4;
else
curSize.x /= 4;
}

return curSize;
}

wxPoint GetPosition() const
{
return wxPoint( m_x, m_y );
}

bool Enable( bool enable ) override;

void UpdateImages();

void UpdateToggleImage( bool toggle );

void UpdateLabel()
{
wxToolBar *tbar = (wxToolBar*) GetToolBar();
int style = (tbar ? tbar->GetWindowStyleFlag() : 0);

// strip mnemonics from the label for compatibility with the usual
// labels in wxStaticText sense
wxCFStringRef l(wxStripMenuCodes(m_label));
wxCFStringRef sh( GetShortHelp() );
#if wxOSX_USE_NATIVE_TOOLBAR
if ( m_toolbarItem )
{
[m_toolbarItem setLabel:l.AsNSString()];

[m_toolbarItem setToolTip:sh.AsNSString()];
}
#endif
if ( IsButton() )
{
NSButton* const btn = (NSButton*)m_controlHandle;

[btn setTitle:l.AsNSString()];

if ( style & wxTB_NOICONS )
[btn setImagePosition:NSNoImage];
else if ( style & wxTB_TEXT )
[btn setImagePosition:NSImageAbove];
else
[btn setImagePosition:NSImageOnly];

if ( (style & (wxTB_NOICONS | wxTB_TEXT)) != 0 )
{
[btn sizeToFit];
}
else if (tbar)
{
wxSize toolsize = tbar->GetToolSize();
NSRect frame = [m_controlHandle frame];
frame.size.width = toolsize.x;
frame.size.height = toolsize.y + 2;
[btn setFrame:frame];
}
}

if ( m_controlHandle )
{
[m_controlHandle setToolTip:sh.AsNSString()];
}
}

void Action()
{
wxToolBar *tbar = (wxToolBar*) GetToolBar();
if (CanBeToggled())
{
bool shouldToggle;

shouldToggle = !IsToggled();
tbar->ToggleTool( GetId(), shouldToggle );
}

tbar->OnLeftClick( GetId(), IsToggled() );
}

#if wxOSX_USE_NATIVE_TOOLBAR
void SetToolbarItemRef( NSToolbarItem* ref )
{
if ( m_toolbarItem )
[m_toolbarItem release];

m_toolbarItem = ref;
}

NSToolbarItem* GetToolbarItemRef() const
{
return m_toolbarItem;
}

void SetIndex( CFIndex idx )
{
m_index = idx;
}

CFIndex GetIndex() const
{
return m_index;
}

virtual void SetLabel(const wxString& label) override
{
wxToolBarToolBase::SetLabel(label);
UpdateLabel();
}

virtual bool SetShortHelp(const wxString& help) override
{
if ( !wxToolBarToolBase::SetShortHelp(help) )
return false;

UpdateLabel();

return true;
}
#endif // wxOSX_USE_NATIVE_TOOLBAR

private:
void Init()
{
m_controlHandle = nullptr;

#if wxOSX_USE_NATIVE_TOOLBAR
m_toolbarItem = nullptr;
m_index = -1;
#endif
}

WXWidget m_controlHandle;
wxCoord m_x;
wxCoord m_y;
wxBitmap m_alternateBitmap;

#if wxOSX_USE_NATIVE_TOOLBAR
NSToolbarItem* m_toolbarItem;
// position in its toolbar, -1 means not inserted
CFIndex m_index;
#endif
};

#if wxOSX_USE_NATIVE_TOOLBAR

@interface wxNSToolbarItem : NSToolbarItem
{
wxToolBarTool* impl;
}

- (id) initWithItemIdentifier: (NSString*) identifier;
- (void)setImplementation: (wxToolBarTool *) theImplementation;
- (wxToolBarTool*) implementation;
- (void) clickedAction: (id) sender;
- (BOOL) validateToolbarItem:(NSToolbarItem *)theItem;

@end

@interface wxNSToolbarDelegate : NSObject <NSToolbarDelegate>
{
bool m_isSelectable;
}

- (void)setSelectable:(bool) value;

- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag;

- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar;

- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar;

- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar;

@end

@interface wxNSToolbar : NSToolbar
{
wxNSToolbarDelegate* toolbarDelegate;
}

- (id)initWithIdentifier:(NSString *)identifier;
- (void) dealloc;

@end

#endif

#if wxOSX_USE_NATIVE_TOOLBAR

@implementation wxNSToolbarItem

- (id)initWithItemIdentifier: (NSString*) identifier
{
if ( self = [super initWithItemIdentifier:identifier] )
{
impl = nullptr;
[self setTarget: self];
[self setAction: @selector(clickedAction:)];
}
return self;
}

- (void) clickedAction: (id) sender
{
wxUnusedVar(sender);
if ( impl )
{
impl->Action();
}
}

- (void)setImplementation: (wxToolBarTool *) theImplementation
{
impl = theImplementation;
}

- (wxToolBarTool*) implementation
{
return impl;
}

- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem
{
wxUnusedVar(theItem);
return impl->IsEnabled() ? YES:NO;
}

@end

@implementation wxNSToolbarDelegate

- (id)init
{
if ( self = [super init] )
{
m_isSelectable = false;
}
return self;
}

- (void)setSelectable:(bool) value
{
wxUnusedVar(value);
m_isSelectable = true;
}

- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
{
wxUnusedVar(toolbar);
return nil;
}

- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
{
wxUnusedVar(toolbar);
return nil;
}

- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
{
if ( m_isSelectable )
return [[toolbar items] valueForKey:@"itemIdentifier"];
else
return nil;
}

- (NSToolbarItem*) toolbar:(NSToolbar*) toolbar itemForItemIdentifier:(NSString*) itemIdentifier willBeInsertedIntoToolbar:(BOOL) flag
{
wxUnusedVar(toolbar);
wxToolBarTool* tool = (wxToolBarTool*) [itemIdentifier longLongValue];
if ( tool )
{
wxNSToolbarItem* item = (wxNSToolbarItem*) tool->GetToolbarItemRef();
if ( flag && tool->IsControl() )
{
NSView* view = tool->GetControl()->GetHandle();
[view removeFromSuperview];
[item setView:view];
wxSize sz = tool->GetControl()->GetSize();
NSSize size = NSMakeSize((float)sz.x, (float)sz.y);
[item setMaxSize:size];
[item setMinSize:size];
[view setHidden:NO];
}
return item;
}
return nil;
}

@end

@implementation wxNSToolbar

- (id)initWithIdentifier:(NSString *)identifier
{
if (self = [super initWithIdentifier:identifier])
{
toolbarDelegate = [[wxNSToolbarDelegate alloc] init];
[self setDelegate:toolbarDelegate];
}
return self;
}

- (void)dealloc
{
[toolbarDelegate release];
[super dealloc];
}

@end

#endif

@implementation wxNSToolBarButton

- (id)initWithFrame:(NSRect)frame
{
if ( self = [super initWithFrame:frame] )
{
impl = nullptr;
[self setTarget: self];
[self setAction: @selector(clickedAction:)];
}
return self;
}

- (void) clickedAction: (id) sender
{
wxUnusedVar(sender);
if ( impl )
{
impl->Action();
}
}

- (void)setImplementation: (wxToolBarTool *) theImplementation
{
impl = theImplementation;
}

- (wxToolBarTool*) implementation
{
return impl;
}

- (BOOL) isFlipped
{
return YES;
}

@end

bool wxToolBarTool::Enable( bool enable )
{
if ( wxToolBarToolBase::Enable( enable ) == false )
return false;

if ( IsControl() )
{
GetControl()->Enable( enable );
}
else if ( IsButton() )
{
#if wxOSX_USE_NATIVE_TOOLBAR
if ( m_toolbarItem != nullptr )
[m_toolbarItem setEnabled:enable];
#endif

if ( m_controlHandle != nullptr )
[(NSControl*)m_controlHandle setEnabled:enable];
}

return true;
}

void wxToolBarTool::SetPosition( const wxPoint& position )
{
m_x = position.x;
m_y = position.y;

int mac_x = position.x;
int mac_y = position.y;

if ( IsButton() )
{
NSRect frame = [m_controlHandle frame];
if ( frame.origin.x != mac_x || frame.origin.y != mac_y )
{
frame.origin.x = mac_x;
frame.origin.y = mac_y;
[m_controlHandle setFrame:frame];
}
}
else if ( IsControl() )
{
// embedded native controls are moved by the OS
#if wxOSX_USE_NATIVE_TOOLBAR
if ( ((wxToolBar*)GetToolBar())->MacWantsNativeToolbar() == false )
#endif
{
GetControl()->Move( position );
}
}
else
{
NSRect frame = [m_controlHandle frame];
if ( frame.origin.x != mac_x || frame.origin.y != mac_y )
{
frame.origin.x = mac_x;
frame.origin.y = mac_y;
[m_controlHandle setFrame:frame];
}
}
}

void wxToolBarTool::UpdateImages()
{
[(NSButton*) m_controlHandle setImage:wxOSXGetImageFromBundle(m_bmpNormal)];

if ( CanBeToggled() )
{
// TODO CS this should use the best current representation, or optionally iterate through all
wxSize sz = m_bmpNormal.GetDefaultSize();
m_alternateBitmap = wxBitmap();
m_alternateBitmap.Create(sz.x, sz.y, -1); // TODO CS m_alternateBitmap.CreateWithDIPSize(sz, m_bmpNormal.GetScaleFactor());
m_alternateBitmap.UseAlpha();
wxMemoryDC dc;

dc.SelectObject(m_alternateBitmap);
// This color corresponds to OS X Yosemite's rendering of selected toolbar items
// See also https://github.com/wxWidgets/wxWidgets/issues/16645
wxColour grey(0xB9, 0xB9, 0xB9);
dc.SetBackground(*wxTRANSPARENT_BRUSH);
dc.Clear();
dc.SetPen(grey);
dc.SetBrush(grey);
dc.DrawRoundedRectangle( 0, 0, sz.x, sz.y, 3 );
dc.DrawBitmap( m_bmpNormal.GetBitmap(sz), 0, 0, true );
dc.SelectObject( wxNullBitmap );

[(NSButton*) m_controlHandle setAlternateImage:m_alternateBitmap.GetNSImage()];
}
UpdateToggleImage( CanBeToggled() && IsToggled() );
}

void wxToolBarTool::UpdateToggleImage( bool toggle )
{
#if wxOSX_USE_NATIVE_TOOLBAR
// Avoid setting the image if we're not showing icons because the image may
// be invalid.
wxToolBar *tbar = (wxToolBar*) GetToolBar();
int style = tbar ? tbar->GetWindowStyleFlag() : 0;
if ( m_toolbarItem != nullptr && !(style & wxTB_NOICONS) )
{
// the native toolbar item only has a 'selected' state (one for one toolbar)
// so we emulate the toggle here
if ( CanBeToggled() && toggle )
[m_toolbarItem setImage:m_alternateBitmap.GetNSImage()];
else
[m_toolbarItem setImage:wxOSXGetImageFromBundle(m_bmpNormal)];
}
#endif

if ( IsButton() )
[(NSButton*)m_controlHandle setState:(toggle ? NSOnState : NSOffState)];
}

wxToolBarTool::wxToolBarTool(
wxToolBar *tbar,
int id,
const wxString& label,
const wxBitmapBundle& bmpNormal,
const wxBitmapBundle& bmpDisabled,
wxItemKind kind,
wxObject *clientData,
const wxString& shortHelp,
const wxString& longHelp )
:
wxToolBarToolBase(
tbar, id, label, bmpNormal, bmpDisabled, kind,
clientData, shortHelp, longHelp )
{
Init();
}

#pragma mark -
#pragma mark Toolbar Implementation

wxToolBarToolBase *wxToolBar::CreateTool(
int id,
const wxString& label,
const wxBitmapBundle& bmpNormal,
const wxBitmapBundle& bmpDisabled,
wxItemKind kind,
wxObject *clientData,
const wxString& shortHelp,
const wxString& longHelp )
{
return new wxToolBarTool(
this, id, label, bmpNormal, bmpDisabled, kind,
clientData, shortHelp, longHelp );
}

wxToolBarToolBase *
wxToolBar::CreateTool(wxControl *control, const wxString& label)
{
return new wxToolBarTool(this, control, label);
}

void wxToolBar::Init()
{
m_maxWidth = -1;
m_maxHeight = -1;
m_defaultWidth = kwxMacToolBarToolDefaultWidth;
m_defaultHeight = kwxMacToolBarToolDefaultHeight;

#if wxOSX_USE_NATIVE_TOOLBAR
m_macToolbar = nullptr;
m_macUsesNativeToolbar = false;
#endif
}

// also for the toolbar we have the dual implementation:
// only when MacInstallNativeToolbar is called is the native toolbar set as the window toolbar

bool wxToolBar::Create(
wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name )
{
if ( !wxToolBarBase::Create( parent, id, pos, size, style, wxDefaultValidator, name ) )
return false;

FixupStyle();

OSStatus err = noErr;

#if wxOSX_USE_NATIVE_TOOLBAR

if (parent->IsKindOf(CLASSINFO(wxFrame)) && wxSystemOptions::GetOptionInt(wxT("mac.toolbar.no-native")) != 1)
{
wxString identifier = wxString::Format( wxT("%p"), this );
wxCFStringRef cfidentifier(identifier);
NSToolbar* tb = [[wxNSToolbar alloc] initWithIdentifier:cfidentifier.AsNSString()];

m_macToolbar = tb ;

if (m_macToolbar != nullptr)
{
NSToolbarDisplayMode mode = NSToolbarDisplayModeDefault;
NSToolbarSizeMode displaySize = NSToolbarSizeModeSmall;

if ( style & wxTB_NOICONS )
mode = NSToolbarDisplayModeLabelOnly;
else if ( style & wxTB_TEXT )
mode = NSToolbarDisplayModeIconAndLabel;
else
mode = NSToolbarDisplayModeIconOnly;

[tb setDisplayMode:mode];
[tb setSizeMode:displaySize];
}
}
#endif // wxOSX_USE_NATIVE_TOOLBAR

return (err == noErr);
}

wxToolBar::~wxToolBar()
{
// removal only works while the toolbar is there
wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
if ( frame && frame->GetToolBar() == this )
{
frame->SetToolBar(nullptr);
}

#if wxOSX_USE_NATIVE_TOOLBAR
[(NSToolbar*)m_macToolbar setDelegate:nil];
[(NSToolbar*)m_macToolbar release];
m_macToolbar = nullptr;
#endif // wxOSX_USE_NATIVE_TOOLBAR
}

bool wxToolBar::Show( bool show )
{
WXWindow tlw = MacGetTopLevelWindowRef();
bool bResult = (tlw != nullptr);

if (bResult)
{
#if wxOSX_USE_NATIVE_TOOLBAR
bool ownToolbarInstalled = false;
MacTopLevelHasNativeToolbar( &ownToolbarInstalled );
if (ownToolbarInstalled)
{
bResult = ([(NSToolbar*)m_macToolbar isVisible] != show);
if ( bResult )
[(NSToolbar*)m_macToolbar setVisible:show];
}
else
bResult = wxToolBarBase::Show( show );
#else

bResult = wxToolBarBase::Show( show );
#endif
}

return bResult;
}

bool wxToolBar::IsShown() const
{
bool bResult;

#if wxOSX_USE_NATIVE_TOOLBAR
bool ownToolbarInstalled;

MacTopLevelHasNativeToolbar( &ownToolbarInstalled );
if (ownToolbarInstalled)
{
bResult = [(NSToolbar*)m_macToolbar isVisible];
}
else
bResult = wxToolBarBase::IsShown();
#else

bResult = wxToolBarBase::IsShown();
#endif

return bResult;
}

void wxToolBar::DoGetSize( int *width, int *height ) const
{
#if wxOSX_USE_NATIVE_TOOLBAR
bool ownToolbarInstalled;

MacTopLevelHasNativeToolbar( &ownToolbarInstalled );
if ( ownToolbarInstalled )
{
WXWindow tlw = MacGetTopLevelWindowRef();
float toolbarHeight = 0.0;
NSRect windowFrame = NSMakeRect(0, 0, 0, 0);

if(m_macToolbar && [(NSToolbar*)m_macToolbar isVisible])
{
windowFrame = [NSWindow contentRectForFrameRect:[tlw frame]
styleMask:[tlw styleMask]];
toolbarHeight = NSHeight(windowFrame)
- NSHeight([[tlw contentView] frame]);
}

if ( width != nullptr )
*width = (int)windowFrame.size.width;
if ( height != nullptr )
*height = (int)toolbarHeight;
}
else
wxToolBarBase::DoGetSize( width, height );

#else
wxToolBarBase::DoGetSize( width, height );
#endif
}

void wxToolBar::DoGetPosition(int*x, int *y) const
{
#if wxOSX_USE_NATIVE_TOOLBAR
bool ownToolbarInstalled;

MacTopLevelHasNativeToolbar( &ownToolbarInstalled );
if ( ownToolbarInstalled )
{
WXWindow tlw = MacGetTopLevelWindowRef();
float toolbarHeight = 0.0;
NSRect windowFrame = NSMakeRect(0, 0, 0, 0);

if(m_macToolbar && [(NSToolbar*)m_macToolbar isVisible])
{
windowFrame = [NSWindow contentRectForFrameRect:[tlw frame]
styleMask:[tlw styleMask]];
toolbarHeight = NSHeight(windowFrame)
- NSHeight([[tlw contentView] frame]);
}

// it is extending to the north of the content area

if ( x != nullptr )
*x = 0;
if ( y != nullptr )
*y = -toolbarHeight;
}
else
wxToolBarBase::DoGetPosition( x, y );

#else
wxToolBarBase::DoGetPosition( x, y );
#endif
}

wxSize wxToolBar::DoGetBestSize() const
{
// was updated in Realize()

wxSize size = GetMinSize();

return size;
}

void wxToolBar::SetWindowStyleFlag( long style )
{
wxToolBarBase::SetWindowStyleFlag( style );

#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
NSToolbarDisplayMode mode = NSToolbarDisplayModeDefault;

if ( style & wxTB_NOICONS )
mode = NSToolbarDisplayModeLabelOnly;
else if ( style & wxTB_TEXT )
mode = NSToolbarDisplayModeIconAndLabel;
else
mode = NSToolbarDisplayModeIconOnly;

[(NSToolbar*) m_macToolbar setDisplayMode:mode];
}
#endif

wxToolBarTool *tool;
wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
while ( node )
{
tool = (wxToolBarTool *) node->GetData();
if ( tool != nullptr )
{
tool->UpdateLabel();
}

node = node->GetNext();
}

InvalidateBestSize();
}

#if wxOSX_USE_NATIVE_TOOLBAR
bool wxToolBar::MacWantsNativeToolbar()
{
return m_macUsesNativeToolbar;
}

bool wxToolBar::MacTopLevelHasNativeToolbar(bool *ownToolbarInstalled) const
{
bool bResultV = false;

if (ownToolbarInstalled != nullptr)
*ownToolbarInstalled = false;

WXWindow tlw = MacGetTopLevelWindowRef();
if (tlw != nullptr)
{
NSToolbar* curToolbarRef = [tlw toolbar];
bResultV = (curToolbarRef != nullptr);
if (bResultV && (ownToolbarInstalled != nullptr))
*ownToolbarInstalled = (curToolbarRef == m_macToolbar);
}

return bResultV;
}

bool wxToolBar::MacInstallNativeToolbar(bool usesNative)
{
bool bResult = false;

if (usesNative && (m_macToolbar == nullptr))
return bResult;

if (usesNative && HasFlag(wxTB_LEFT|wxTB_RIGHT|wxTB_BOTTOM) )
return bResult;

WXWindow tlw = MacGetTopLevelWindowRef();
if (tlw == nullptr)
return bResult;

// check the existing toolbar
NSToolbar* curToolbarRef = [tlw toolbar];

m_macUsesNativeToolbar = usesNative;

if (m_macUsesNativeToolbar)
{
// only install toolbar if there isn't one installed already
if (curToolbarRef == nullptr)
{
bResult = true;
[tlw setToolbar:(NSToolbar*) m_macToolbar];
[(NSToolbar*) m_macToolbar setVisible:YES];

GetPeer()->Move(0,0,0,0 );
SetSize( wxSIZE_AUTO_WIDTH, 0 );
GetPeer()->SetVisibility( false );
wxToolBarBase::Show( false );
}
}
else
{
// only deinstall toolbar if this is the installed one
if (m_macToolbar == curToolbarRef)
{
bResult = true;
[(NSToolbar*) m_macToolbar setVisible:NO];
MacUninstallNativeToolbar();
GetPeer()->SetVisibility( true );
}
}

if (bResult)
InvalidateBestSize();

// wxLogDebug( wxT(" --> [%lx] - result [%s]"), (long)this, bResult ? wxT("T") : wxT("F") );
return bResult;
}

void wxToolBar::MacUninstallNativeToolbar()
{
if (!m_macToolbar)
return;

WXWindow tlw = MacGetTopLevelWindowRef();
if (tlw)
[tlw setToolbar:nil];
}
#endif

void wxToolBar::DoLayout()
{
int maxToolWidth = 0;
int maxToolHeight = 0;

int tw, th;
GetSize( &tw, &th );

// find the maximum tool width and height
// and the number of stretchable items
int numStretchableSpaces = 0;
wxToolBarTool *tool;
wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
while ( node )
{
tool = (wxToolBarTool *) node->GetData();
if ( tool != nullptr )
{
wxSize sz = tool->GetSize();

if ( sz.x > maxToolWidth )
maxToolWidth = sz.x;
if ( sz.y > maxToolHeight )
maxToolHeight = sz.y;
if ( tool->IsStretchableSpace() )
numStretchableSpaces++;
}

node = node->GetNext();
}

// layout non-native toolbar

bool isHorizontal = !IsVertical();

int maxWidth = 0;
int maxHeight = 0;

int x = m_xMargin + kwxMacToolBarLeftMargin;
int y = m_yMargin + kwxMacToolBarTopMargin;

node = m_tools.GetFirst();
while ( node )
{
tool = (wxToolBarTool*) node->GetData();
if ( tool == nullptr )
{
node = node->GetNext();
continue;
}

// set tool position:
// for the moment just perform a single row/column alignment
wxSize cursize = tool->GetSize();
if ( x + cursize.x > maxWidth )
maxWidth = x + cursize.x;
if ( y + cursize.y > maxHeight )
maxHeight = y + cursize.y;

// update the item positioning state
if ( !isHorizontal )
y += cursize.y + kwxMacToolSpacing;
else
x += cursize.x + kwxMacToolSpacing;

node = node->GetNext();
}

if ( isHorizontal )
{
// if not set yet, only one row
if ( m_maxRows <= 0 )
SetRows( 1 );

maxWidth += m_xMargin + kwxMacToolBarLeftMargin;
m_minWidth = maxWidth;
m_minHeight = m_maxHeight = maxToolHeight + 2 * (m_yMargin + kwxMacToolBarTopMargin);
}
else
{
// if not set yet, have one column
if ( (GetToolsCount() > 0) && (m_maxRows <= 0) )
SetRows( GetToolsCount() );

maxHeight += m_yMargin + kwxMacToolBarTopMargin;
m_minHeight = maxHeight;
m_minWidth = m_maxWidth = maxToolWidth + 2 * (m_yMargin + kwxMacToolBarTopMargin);
}

int totalStretchableSpace = 0;
int spacePerStretchable = 0;
if ( numStretchableSpaces > 0 )
{
if ( isHorizontal )
totalStretchableSpace = tw - maxWidth;
else
totalStretchableSpace = th - maxHeight;

if ( totalStretchableSpace > 0 )
spacePerStretchable = totalStretchableSpace / numStretchableSpaces;
}

// perform real positioning

x = m_xMargin + kwxMacToolBarLeftMargin;
y = m_yMargin + kwxMacToolBarTopMargin;

node = m_tools.GetFirst();
int currentStretchable = 0;
while ( node )
{
tool = (wxToolBarTool*) node->GetData();
if ( tool == nullptr )
{
node = node->GetNext();
continue;
}

wxSize cursize = tool->GetSize();
if ( tool->IsStretchableSpace() )
{
++currentStretchable;
int thisSpace = currentStretchable == numStretchableSpaces ?
totalStretchableSpace - (currentStretchable-1)*spacePerStretchable :
spacePerStretchable;
if ( isHorizontal )
cursize.x += thisSpace;
else
cursize.y += thisSpace;
}

if ( !isHorizontal )
{
int x1 = x + ( maxToolWidth - cursize.x ) / 2;
tool->SetPosition( wxPoint(x1, y) );
}
else
{
int y1 = y + ( maxToolHeight - cursize.y ) / 2;
tool->SetPosition( wxPoint(x, y1) );
}

// update the item positioning state
if ( !isHorizontal )
y += cursize.y + kwxMacToolSpacing;
else
x += cursize.x + kwxMacToolSpacing;

node = node->GetNext();
}

}

bool wxToolBar::Realize()
{
if ( !wxToolBarBase::Realize() )
return false;

wxToolBarTool *tool;
wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();

#if wxOSX_USE_NATIVE_TOOLBAR
CFIndex currentPosition = 0;
bool insertAll = false;

NSToolbar* refTB = (NSToolbar*)m_macToolbar;

node = m_tools.GetFirst();
while ( node )
{
tool = (wxToolBarTool*) node->GetData();
if ( tool == nullptr )
{
node = node->GetNext();
continue;
}

// install in native NSToolbar
if ( refTB )
{
NSToolbarItem* hiItemRef = tool->GetToolbarItemRef();
if ( hiItemRef != nullptr )
{
// since setting the help texts is non-virtual we have to update
// the strings now
wxCFStringRef sh( tool->GetShortHelp() );
[hiItemRef setToolTip:sh.AsNSString()];

if ( insertAll || (tool->GetIndex() != currentPosition) )
{
if ( !insertAll )
{
insertAll = true;

// if this is the first tool that gets newly inserted or repositioned
// first remove all 'old' tools from here to the right, because of this
// all following tools will have to be reinserted (insertAll).
for ( wxToolBarToolsList::compatibility_iterator node2 = m_tools.GetLast();
node2 != node;
node2 = node2->GetPrevious() )
{
wxToolBarTool *tool2 = (wxToolBarTool*) node2->GetData();

const long idx = tool2->GetIndex();
if ( idx != -1 )
{
[refTB removeItemAtIndex:idx];
tool2->SetIndex(-1);
}
}
}

wxCFStringRef cfidentifier;
NSString *nsItemId;
if (tool->GetStyle() == wxTOOL_STYLE_SEPARATOR)
{
if ( tool->IsStretchable() )
nsItemId = NSToolbarFlexibleSpaceItemIdentifier;
else
nsItemId = NSToolbarSpaceItemIdentifier;
}
else
{
cfidentifier = wxCFStringRef(wxString::Format("%ld", (long)tool));
nsItemId = cfidentifier.AsNSString();
}

[refTB insertItemWithItemIdentifier:nsItemId atIndex:currentPosition];
tool->SetIndex( currentPosition );
}

currentPosition++;
}
}
node = node->GetNext();
}

#endif

DoLayout();

// adjust radio items

bool lastIsRadio = false;
bool curIsRadio = false;

node = m_tools.GetFirst();
while ( node )
{
tool = (wxToolBarTool*) node->GetData();
if ( tool == nullptr )
{
node = node->GetNext();
continue;
}

// update radio button (and group) state
lastIsRadio = curIsRadio;
curIsRadio = ( tool->IsButton() && (tool->GetKind() == wxITEM_RADIO) );

if ( !curIsRadio )
{
if ( tool->IsToggled() )
DoToggleTool( tool, true );
}
else
{
if ( !lastIsRadio )
{
if ( tool->Toggle( true ) )
{
DoToggleTool( tool, true );
}
}
else if ( tool->IsToggled() )
{
if ( tool->IsToggled() )
DoToggleTool( tool, true );

wxToolBarToolsList::compatibility_iterator nodePrev = node->GetPrevious();
while ( nodePrev )
{
wxToolBarToolBase *toggleTool = nodePrev->GetData();
if ( (toggleTool == nullptr) || !toggleTool->IsButton() || (toggleTool->GetKind() != wxITEM_RADIO) )
break;

if ( toggleTool->Toggle( false ) )
DoToggleTool( toggleTool, false );

nodePrev = nodePrev->GetPrevious();
}
}
}

node = node->GetNext();
}

InvalidateBestSize();
SetInitialSize( wxSize(m_minWidth, m_minHeight));

SendSizeEventToParent();

return true;
}

void wxToolBar::DoSetSize(int x, int y, int width, int height, int sizeFlags)
{
wxToolBarBase::DoSetSize(x, y, width, height, sizeFlags);

DoLayout();
}

void wxToolBar::DoSetToolBitmapSize(const wxSize& size)
{
m_defaultWidth = size.x + kwxMacToolBorder;
m_defaultHeight = size.y + kwxMacToolBorder;

#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
int maxs = wxMax( size.x, size.y );
NSToolbarSizeMode sizeSpec;

if ( maxs > 24 )
sizeSpec = NSToolbarSizeModeRegular;
else
sizeSpec = NSToolbarSizeModeSmall;

[(NSToolbar*) m_macToolbar setSizeMode:sizeSpec ];
}
#endif
}

// The button size is bigger than the bitmap size
wxSize wxToolBar::GetToolSize() const
{
return wxSize(m_defaultWidth + kwxMacToolBorder, m_defaultHeight + kwxMacToolBorder);
}

void wxToolBar::SetRows(int nRows)
{
// avoid resizing the frame uselessly
if ( nRows != m_maxRows )
m_maxRows = nRows;
}

void wxToolBar::MacSuperChangedPosition()
{
wxWindow::MacSuperChangedPosition();

/*
#if wxOSX_USE_NATIVE_TOOLBAR
if (! m_macUsesNativeToolbar )
Realize();
#else

Realize();
#endif
*/
}

void wxToolBar::SetToolNormalBitmap( int id, const wxBitmapBundle& bitmap )
{
wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
if ( tool )
{
wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools."));

tool->SetNormalBitmap(bitmap);

// a side-effect of the UpdateToggleImage function is that it always changes the bitmap used on the button.
tool->UpdateImages();
}
}

void wxToolBar::SetToolDisabledBitmap( int id, const wxBitmapBundle& bitmap )
{
wxToolBarTool* tool = static_cast<wxToolBarTool*>(FindById(id));
if ( tool )
{
wxCHECK_RET( tool->IsButton(), wxT("Can only set bitmap on button tools."));

tool->SetDisabledBitmap(bitmap);

// TODO: what to do for this one?
}
}

wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
{
wxToolBarTool *tool;
wxToolBarToolsList::compatibility_iterator node = m_tools.GetFirst();
while ( node )
{
tool = (wxToolBarTool *)node->GetData();
if (tool != nullptr)
{
wxRect2DInt r( tool->GetPosition(), tool->GetSize() );
if ( r.Contains( wxPoint( x, y ) ) )
return tool;
}

node = node->GetNext();
}

return nullptr;
}

wxString wxToolBar::MacGetToolTipString( wxPoint &pt )
{
wxToolBarToolBase *tool = FindToolForPosition( pt.x, pt.y );
if ( tool != nullptr )
return tool->GetShortHelp();

return wxEmptyString;
}

void wxToolBar::DoEnableTool(wxToolBarToolBase * WXUNUSED(t), bool WXUNUSED(enable))
{
// everything already done in the tool's Enable implementation
}

void wxToolBar::DoToggleTool(wxToolBarToolBase *t, bool toggle)
{
wxToolBarTool *tool = (wxToolBarTool *)t;
if ( ( tool != nullptr ) && tool->IsButton() )
tool->UpdateToggleImage( toggle );
}

bool wxToolBar::DoInsertTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolBase)
{
wxToolBarTool *tool = static_cast< wxToolBarTool*>(toolBase );
if (tool == nullptr)
return false;

long style = GetWindowStyleFlag();

wxSize toolSize = GetToolSize();
WXWidget controlHandle = nullptr;
NSRect toolrect = NSMakeRect(0, 0, toolSize.x, toolSize.y + 2 );

#if wxOSX_USE_NATIVE_TOOLBAR
wxString label = tool->GetLabel();
if (m_macToolbar && !label.empty() )
{
// strip mnemonics from the label for compatibility
// with the usual labels in wxStaticText sense
label = wxStripMenuCodes(label);
}
#endif // wxOSX_USE_NATIVE_TOOLBAR

switch (tool->GetStyle())
{
case wxTOOL_STYLE_SEPARATOR:
{
wxASSERT( tool->GetControlHandle() == nullptr );
toolSize.x /= 4;
toolSize.y /= 4;
if ( IsVertical() )
toolrect.size.height = toolSize.y;
else
toolrect.size.width = toolSize.x;

// in flat style we need a visual separator
#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
NSString * nsItemId = nil;

if ( tool->IsStretchable() )
nsItemId = NSToolbarFlexibleSpaceItemIdentifier;
else
nsItemId = NSToolbarSpaceItemIdentifier;

NSToolbarItem* item = [[NSToolbarItem alloc] initWithItemIdentifier:nsItemId];
tool->SetToolbarItemRef( item );
}
#endif // wxOSX_USE_NATIVE_TOOLBAR

NSBox* box = [[NSBox alloc] initWithFrame:toolrect];
[box setBoxType:NSBoxSeparator];
controlHandle = box;
tool->SetControlHandle( controlHandle );
}
break;

case wxTOOL_STYLE_BUTTON:
{
wxASSERT( tool->GetControlHandle() == nullptr );

wxNSToolBarButton* v = [[wxNSToolBarButton alloc] initWithFrame:toolrect];

[v setBezelStyle:NSSmallSquareBezelStyle];
[[v cell] setControlSize:NSSmallControlSize];
[v setFont:[NSFont fontWithName:[[v font] fontName] size:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
[v setBordered:NO];
[v setButtonType: ( tool->CanBeToggled() ? NSToggleButton : NSMomentaryPushInButton )];
[v setImplementation:tool];

controlHandle = v;

#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
wxString identifier = wxString::Format(wxT("%ld"), (long) tool);
wxCFStringRef cfidentifier( identifier );
wxNSToolbarItem* item = [[wxNSToolbarItem alloc] initWithItemIdentifier:cfidentifier.AsNSString() ];
[item setImplementation:tool];
tool->SetToolbarItemRef( item );
}

#endif // wxOSX_USE_NATIVE_TOOLBAR
tool->SetControlHandle( controlHandle );
if ( !(style & wxTB_NOICONS) )
tool->UpdateImages();
tool->UpdateLabel();

#if 0
InstallControlEventHandler(
(WXWidget) controlHandle, GetwxMacToolBarToolEventHandlerUPP(),
GetEventTypeCount(eventList), eventList, tool, nullptr );
#endif
}
break;

case wxTOOL_STYLE_CONTROL:

#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
WXWidget view = (WXWidget) tool->GetControl()->GetHandle() ;
wxCHECK_MSG( view, false, wxT("control must be non-null") );

wxString identifier = wxString::Format(wxT("%ld"), (long) tool);
wxCFStringRef cfidentifier( identifier );
wxNSToolbarItem* item = [[wxNSToolbarItem alloc] initWithItemIdentifier:cfidentifier.AsNSString() ];
[item setImplementation:tool];
tool->SetToolbarItemRef( item );
}
#else
// right now there's nothing to do here
#endif
tool->UpdateLabel();
break;

default:
break;
}

if ( controlHandle )
{
WXWidget container = (WXWidget) GetHandle();
wxASSERT_MSG( container != nullptr, wxT("No valid Mac container control") );

// SetControlVisibility( controlHandle, true, true );
[container addSubview:controlHandle];
}

// nothing special to do here - we relayout in Realize() later
InvalidateBestSize();

return true;

}

void wxToolBar::DoSetToggle(wxToolBarToolBase *WXUNUSED(tool), bool WXUNUSED(toggle))
{
wxFAIL_MSG( wxT("not implemented") );
}

bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolbase)
{
wxToolBarTool* tool = static_cast< wxToolBarTool*>(toolbase );
wxToolBarToolsList::compatibility_iterator node;
for ( node = m_tools.GetFirst(); node; node = node->GetNext() )
{
wxToolBarToolBase *tool2 = node->GetData();
if ( tool2 == tool )
{
// let node point to the next node in the list
node = node->GetNext();

break;
}
}

wxSize sz = ((wxToolBarTool*)tool)->GetSize();

#if wxOSX_USE_NATIVE_TOOLBAR
CFIndex removeIndex = tool->GetIndex();
#endif

#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
if ( removeIndex != -1 && m_macToolbar )
{
[(NSToolbar*) m_macToolbar removeItemAtIndex:removeIndex];
tool->SetIndex( -1 );
}
}
#endif

tool->ClearControl();

// and finally reposition all the controls after this one

for ( /* node -> first after deleted */; node; node = node->GetNext() )
{
wxToolBarTool *tool2 = (wxToolBarTool*) node->GetData();
wxPoint pt = tool2->GetPosition();

if ( IsVertical() )
pt.y -= sz.y;
else
pt.x -= sz.x;

tool2->SetPosition( pt );

#if wxOSX_USE_NATIVE_TOOLBAR
if (m_macToolbar != nullptr)
{
if ( removeIndex != -1 && tool2->GetIndex() > removeIndex )
tool2->SetIndex( tool2->GetIndex() - 1 );
}
#endif
}

InvalidateBestSize();

return true;
}

#include <Carbon/Carbon.h>

void wxToolBar::OnPaint(wxPaintEvent& event)
{
#if wxOSX_USE_NATIVE_TOOLBAR
if ( m_macUsesNativeToolbar )
{
// nothing to do here
}
else
#endif
{
const int direction = GetDirection();
int w, h;
GetSize( &w, &h );

wxPaintDC dc(this);

wxRect rect(0,0,w,h);

// TODO determine whether to use flat appearance in earlier system
if ( WX_IS_MACOS_AVAILABLE(10, 14 ) )
{
// No gradient.
}
else
{
dc.GradientFillLinear( rect , wxColour( 0xCC,0xCC,0xCC ), wxColour( 0xA8,0xA8,0xA8 ) , wxSOUTH );
}

dc.SetPen( wxPen( wxColour( 0x51,0x51,0x51 ) ) );

if ( direction == wxTB_RIGHT )
dc.DrawLine(0, 0, 0, h);
else if ( direction == wxTB_LEFT )
dc.DrawLine(w-1, 0, w-1, h);
else if ( direction == wxTB_BOTTOM )
dc.DrawLine(0, 0, w, 0);
else if ( direction == wxTB_TOP )
dc.DrawLine(0, h-1, w, h-1);
}
event.Skip();
}

#if wxOSX_USE_NATIVE_TOOLBAR
void wxToolBar::OSXSetSelectableTools(bool set)
{
wxCHECK_RET( m_macToolbar, "toolbar must be non-null" );
[(wxNSToolbarDelegate*)[(NSToolbar*)m_macToolbar delegate] setSelectable:set];
}

void wxToolBar::OSXSelectTool(int toolId)
{
wxToolBarToolBase *tool = FindById(toolId);
wxCHECK_RET( tool, "invalid tool ID" );
wxCHECK_RET( m_macToolbar, "toolbar must be non-null" );

wxString identifier = wxString::Format(wxT("%ld"), (long)tool);
wxCFStringRef cfidentifier(identifier);
[(NSToolbar*)m_macToolbar setSelectedItemIdentifier:cfidentifier.AsNSString()];
}
#endif // wxOSX_USE_NATIVE_TOOLBAR

#endif // wxUSE_TOOLBAR