1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/cocoa/toolbar.mm
3// Purpose:     wxToolBar
4// Author:      David Elliott
5// Modified by:
6// Created:     2003/08/17
7// Copyright:   (c) 2003 David Elliott
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#if wxUSE_TOOLBAR_NATIVE
23
24#include "wx/toolbar.h"
25
26#ifndef WX_PRECOMP
27    #include "wx/frame.h"
28    #include "wx/log.h"
29#endif // WX_PRECOMP
30
31#include "wx/cocoa/string.h"
32#include "wx/cocoa/autorelease.h"
33
34#import <AppKit/NSView.h>
35#import <AppKit/NSButtonCell.h>
36#import <AppKit/NSMatrix.h>
37#import <AppKit/NSImage.h>
38#import <AppKit/NSEvent.h>
39#import <AppKit/NSColor.h>
40#import <AppKit/NSAttributedString.h>
41#import <AppKit/NSFont.h>
42
43#include <math.h>
44
45// ========================================================================
46// wxToolBarTool
47// ========================================================================
48class wxToolBarTool : public wxToolBarToolBase
49{
50public:
51    wxToolBarTool(wxToolBar *tbar, int toolid, const wxString& label,
52            const wxBitmap& bitmap1, const wxBitmap& bitmap2,
53            wxItemKind kind, wxObject *clientData,
54            const wxString& shortHelpString, const wxString& longHelpString)
55    :   wxToolBarToolBase(tbar, toolid, label, bitmap1, bitmap2, kind,
56            clientData, shortHelpString, longHelpString)
57    {
58        Init();
59        CreateButtonCell();
60    }
61
62    wxToolBarTool(wxToolBar *tbar, wxControl *control, const wxString& label)
63        : wxToolBarToolBase(tbar, control, label)
64    {
65        Init();
66    }
67    ~wxToolBarTool();
68
69    bool CreateButtonCell();
70
71    // is this a radio button?
72    //
73    // unlike GetKind(), can be called for any kind of tools, not just buttons
74    bool IsRadio() const { return IsButton() && GetKind() == wxITEM_RADIO; }
75
76    NSRect GetFrameRect()
77    {   return m_frameRect; }
78    void SetFrameRect(NSRect frameRect)
79    {   m_frameRect = frameRect; }
80    void DrawTool(NSView *nsview);
81
82    NSButtonCell *GetNSButtonCell()
83    {   return m_cocoaNSButtonCell; }
84protected:
85    void Init();
86    NSButtonCell *m_cocoaNSButtonCell;
87    NSRect m_frameRect;
88};
89
90// ========================================================================
91// wxToolBarTool
92// ========================================================================
93void wxToolBarTool::Init()
94{
95    m_cocoaNSButtonCell = NULL;
96    m_frameRect = NSZeroRect;
97}
98
99void wxToolBar::CocoaToolClickEnded()
100{
101    wxASSERT(m_mouseDownTool);
102    wxCommandEvent event(wxEVT_MENU, m_mouseDownTool->GetId());
103    InitCommandEvent(event);
104    Command(event);
105}
106
107wxToolBarTool::~wxToolBarTool()
108{
109    [m_cocoaNSButtonCell release];
110}
111
112bool wxToolBarTool::CreateButtonCell()
113{
114    wxAutoNSAutoreleasePool pool;
115
116    NSImage *nsimage = [m_bmpNormal.GetNSImage(true) retain];
117    m_cocoaNSButtonCell = [[NSButtonCell alloc] initTextCell:nil];
118    [m_cocoaNSButtonCell setImage:nsimage];
119    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:wxNSStringWithWxString(m_label) attributes:[NSDictionary dictionaryWithObject:[NSFont labelFontOfSize:0.0] forKey:NSFontAttributeName]];
120//    [m_cocoaNSButtonCell setTitle:wxNSStringWithWxString(m_label)];
121    [m_cocoaNSButtonCell setAttributedTitle:[attributedTitle autorelease]];
122
123    // Create an alternate image in the style of NSToolBar
124    if(nsimage)
125    {
126        NSImage *alternateImage = [[NSImage alloc] initWithSize:[nsimage size]];
127        [alternateImage lockFocus];
128        // Paint the entire image with solid black at 50% transparency
129        NSRect imageRect = NSZeroRect;
130        imageRect.size = [alternateImage size];
131        [[NSColor colorWithCalibratedWhite:0.0 alpha:0.5] set];
132        NSRectFill(imageRect);
133        // Composite the original image with the alternate image
134        [nsimage compositeToPoint:NSZeroPoint operation:NSCompositeDestinationAtop];
135        [alternateImage unlockFocus];
136        [m_cocoaNSButtonCell setAlternateImage:alternateImage];
137        [alternateImage release];
138    }
139    [nsimage release];
140
141    NSMutableAttributedString *alternateTitle = [[NSMutableAttributedString alloc] initWithAttributedString:[m_cocoaNSButtonCell attributedTitle]];
142    [alternateTitle applyFontTraits:NSBoldFontMask range:NSMakeRange(0,[alternateTitle length])];
143    [m_cocoaNSButtonCell setAttributedAlternateTitle:alternateTitle];
144    [alternateTitle release];
145
146    // ----
147    [m_cocoaNSButtonCell setImagePosition:NSImageBelow];
148//    [m_cocoaNSButtonCell setBezeled:NO];
149    [m_cocoaNSButtonCell setButtonType:NSMomentaryChangeButton];
150    [m_cocoaNSButtonCell setBordered:NO];
151//    [m_cocoaNSButtonCell setHighlightsBy:NSContentsCellMask|NSPushInCellMask];
152//    [m_cocoaNSButtonCell setShowsStateBy:NSContentsCellMask|NSPushInCellMask];
153    return true;
154}
155
156void wxToolBarTool::DrawTool(NSView *nsview)
157{
158    [m_cocoaNSButtonCell drawWithFrame:m_frameRect inView:nsview];
159}
160
161// ========================================================================
162// wxToolBar
163// ========================================================================
164IMPLEMENT_DYNAMIC_CLASS(wxToolBar, wxControl)
165
166//-----------------------------------------------------------------------------
167// wxToolBar construction
168//-----------------------------------------------------------------------------
169
170void wxToolBar::Init()
171{
172    m_owningFrame = NULL;
173    m_mouseDownTool = NULL;
174}
175
176wxToolBar::~wxToolBar()
177{
178}
179
180bool wxToolBar::Create( wxWindow *parent,
181                        wxWindowID winid,
182                        const wxPoint& pos,
183                        const wxSize& size,
184                        long style,
185                        const wxString& name )
186{
187    // Call wxControl::Create so we get a wxNonControlNSControl
188    if ( !wxToolBarBase::Create(parent, winid, pos, size, style,
189                                wxDefaultValidator, name) )
190        return false;
191
192    FixupStyle();
193
194    return true;
195}
196
197wxToolBarToolBase *wxToolBar::CreateTool(int toolid,
198                                         const wxString& text,
199                                         const wxBitmap& bitmap1,
200                                         const wxBitmap& bitmap2,
201                                         wxItemKind kind,
202                                         wxObject *clientData,
203                                         const wxString& shortHelpString,
204                                         const wxString& longHelpString)
205{
206    return new wxToolBarTool(this, toolid, text, bitmap1, bitmap2, kind,
207                             clientData, shortHelpString, longHelpString);
208}
209
210wxToolBarToolBase *
211wxToolBar::CreateTool(wxControl *control, const wxString& label)
212{
213    return new wxToolBarTool(this, control, label);
214}
215
216void wxToolBar::SetWindowStyleFlag( long style )
217{
218    wxToolBarBase::SetWindowStyleFlag(style);
219}
220
221bool wxToolBar::DoInsertTool(size_t pos, wxToolBarToolBase *toolBase)
222{
223    return true;
224}
225
226bool wxToolBar::DoDeleteTool(size_t WXUNUSED(pos), wxToolBarToolBase *toolBase)
227{
228    Realize();
229    return true;
230}
231
232bool wxToolBar::Cocoa_acceptsFirstMouse(bool &acceptsFirstMouse, WX_NSEvent theEvent)
233{
234    acceptsFirstMouse = true; return true;
235}
236
237bool wxToolBar::Cocoa_drawRect(const NSRect &rect)
238{
239    wxToolBarToolsList::compatibility_iterator node;
240    for(node = m_tools.GetFirst(); node; node = node->GetNext())
241    {
242        wxToolBarTool *tool = static_cast<wxToolBarTool*>(node->GetData());
243        tool->DrawTool(m_cocoaNSView);
244    }
245    return wxToolBarBase::Cocoa_drawRect(rect);
246}
247
248static const NSSize toolPadding = { 4.0, 4.0 };
249
250static NSRect AddToolPadding(NSRect toolRect)
251{
252        toolRect.origin.x -= toolPadding.width;
253        toolRect.size.width += 2.0*toolPadding.width;
254        toolRect.origin.y -= toolPadding.height;
255        toolRect.size.height += 2.0*toolPadding.height;
256        return toolRect;
257}
258
259bool wxToolBar::Cocoa_mouseDragged(WX_NSEvent theEvent)
260{
261    if(m_mouseDownTool && [m_cocoaNSView
262            mouse:[m_cocoaNSView convertPoint:[theEvent locationInWindow]
263                fromView:nil]
264            inRect:AddToolPadding(m_mouseDownTool->GetFrameRect())])
265    {
266        NSButtonCell *buttonCell = m_mouseDownTool->GetNSButtonCell();
267        if(buttonCell)
268        {
269            [buttonCell retain];
270            [buttonCell setHighlighted: YES];
271            if([buttonCell trackMouse: theEvent
272                inRect:AddToolPadding(m_mouseDownTool->GetFrameRect()) ofView:m_cocoaNSView
273                untilMouseUp:NO])
274            {
275                CocoaToolClickEnded();
276                m_mouseDownTool = NULL;
277                wxLogTrace(wxTRACE_COCOA,wxT("Button was clicked after drag!"));
278            }
279            [buttonCell setHighlighted: NO];
280            [buttonCell release];
281        }
282    }
283    return wxToolBarBase::Cocoa_mouseDragged(theEvent);
284}
285
286bool wxToolBar::Cocoa_mouseDown(WX_NSEvent theEvent)
287{
288    wxToolBarTool *tool = CocoaFindToolForPosition([m_cocoaNSView convertPoint:[theEvent locationInWindow] fromView:nil]);
289    if(tool)
290    {
291        NSButtonCell *buttonCell = tool->GetNSButtonCell();
292        if(buttonCell)
293        {
294            [buttonCell retain];
295            m_mouseDownTool = tool;
296            [buttonCell setHighlighted: YES];
297            if([buttonCell trackMouse: theEvent
298                inRect:AddToolPadding(tool->GetFrameRect()) ofView:m_cocoaNSView
299                untilMouseUp:NO])
300            {
301                CocoaToolClickEnded();
302                m_mouseDownTool = NULL;
303                wxLogTrace(wxTRACE_COCOA,wxT("Button was clicked!"));
304            }
305            [buttonCell setHighlighted: NO];
306            [buttonCell release];
307        }
308    }
309    return wxToolBarBase::Cocoa_mouseDown(theEvent);
310}
311
312bool wxToolBar::Realize()
313{
314    wxAutoNSAutoreleasePool pool;
315
316    wxToolBarToolsList::compatibility_iterator node;
317    NSSize totalSize = NSZeroSize;
318    // This is for horizontal, TODO: vertical
319    for(node = m_tools.GetFirst(); node; node = node->GetNext())
320    {
321        wxToolBarTool *tool = static_cast<wxToolBarTool*>(node->GetData());
322        if(tool->IsControl())
323        {
324            totalSize.width = ceil(totalSize.width);
325            wxControl *control = tool->GetControl();
326            wxSize controlSize = control->GetSize();
327            control->SetPosition(wxPoint((wxCoord)totalSize.width,0));
328            totalSize.width += controlSize.x;
329            if(controlSize.y > totalSize.height)
330                totalSize.height = controlSize.y;
331        }
332        else if(tool->IsSeparator())
333        {
334            totalSize.width += 2.0;
335        }
336        else
337        {
338            NSButtonCell *buttonCell = tool->GetNSButtonCell();
339            NSSize toolSize = [buttonCell cellSize];
340            tool->SetFrameRect(NSMakeRect(totalSize.width+toolPadding.width,toolPadding.height,toolSize.width,toolSize.height));
341            toolSize.width += 2.0*toolPadding.width;
342            toolSize.height += 2.0*toolPadding.height;
343            totalSize.width += toolSize.width;
344            if(toolSize.height > totalSize.height)
345                totalSize.height = toolSize.height;
346        }
347    }
348    m_bestSize = wxSize((wxCoord)ceil(totalSize.width),(wxCoord)ceil(totalSize.height));
349    if(m_owningFrame)
350        m_owningFrame->UpdateFrameNSView();
351    return true;
352}
353
354wxSize wxToolBar::DoGetBestSize() const
355{
356    return m_bestSize;
357}
358
359// ----------------------------------------------------------------------------
360// wxToolBar tools state
361// ----------------------------------------------------------------------------
362
363void wxToolBar::DoEnableTool(wxToolBarToolBase *toolBase, bool enable)
364{
365}
366
367void wxToolBar::DoToggleTool( wxToolBarToolBase *toolBase, bool toggle )
368{
369}
370
371void wxToolBar::DoSetToggle(wxToolBarToolBase * WXUNUSED(tool),
372                            bool WXUNUSED(toggle))
373{
374}
375
376// ----------------------------------------------------------------------------
377// wxToolBar geometry
378// ----------------------------------------------------------------------------
379
380wxToolBarToolBase *wxToolBar::FindToolForPosition(wxCoord x, wxCoord y) const
381{
382    return NULL;
383}
384
385wxToolBarTool *wxToolBar::CocoaFindToolForPosition(const NSPoint& pos) const
386{
387    wxToolBarToolsList::compatibility_iterator node;
388    for(node = m_tools.GetFirst(); node; node = node->GetNext())
389    {
390        wxToolBarTool *tool = static_cast<wxToolBarTool*>(node->GetData());
391        if(tool->IsControl())
392        {
393            // TODO
394        }
395        else if(tool->IsSeparator())
396        {   // Do nothing
397        }
398        else
399        {
400            if([m_cocoaNSView mouse:pos inRect:AddToolPadding(tool->GetFrameRect())])
401                return tool;
402        }
403    }
404    return NULL;
405}
406
407void wxToolBar::SetMargins( int x, int y )
408{
409}
410
411void wxToolBar::SetToolSeparation( int separation )
412{
413    m_toolSeparation = separation;
414}
415
416void wxToolBar::SetToolShortHelp( int id, const wxString& helpString )
417{
418}
419
420// ----------------------------------------------------------------------------
421// wxToolBar idle handling
422// ----------------------------------------------------------------------------
423
424void wxToolBar::OnInternalIdle()
425{
426}
427
428#endif // wxUSE_TOOLBAR_NATIVE
429