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