]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/taskbar.mm
use reserve() instead of Alloc() in WX_APPEND_ARRAY so that it works with std classes too
[wxWidgets.git] / src / cocoa / taskbar.mm
CommitLineData
f1d9e1ec
DE
1/////////////////////////////////////////////////////////////////////////
2// File: src/cocoa/taskbar.mm
3// Purpose: Implements wxTaskBarIcon class
4// Author: David Elliott
5// Modified by:
6// Created: 2004/01/24
7// RCS-ID: $Id$
8// Copyright: (c) 2004 David Elliott
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////
11
f1d9e1ec
DE
12#include "wx/wxprec.h"
13#ifdef wxHAS_TASK_BAR_ICON
14
15#ifndef WX_PRECOMP
16 #include "wx/menu.h"
17 #include "wx/icon.h"
18 #include "wx/log.h"
19 #include "wx/dcclient.h"
20#endif
21
22#include "wx/taskbar.h"
23
24#import <AppKit/NSApplication.h>
25#import <AppKit/NSImage.h>
26#import <AppKit/NSMenu.h>
27#import <AppKit/NSMenuItem.h>
28#import <AppKit/NSStatusBar.h>
29#import <AppKit/NSStatusItem.h>
30#import <AppKit/NSView.h>
31#import <Foundation/NSArray.h>
32#import <Foundation/NSEnumerator.h>
33
34#import <AppKit/NSEvent.h>
35#import <AppKit/NSWindow.h>
36#import <AppKit/NSGraphicsContext.h>
37
38#include "wx/cocoa/NSApplication.h"
39#include "wx/cocoa/autorelease.h"
40
04fa0af1
RN
41// A category for methods that are only present in Panther's SDK
42@interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
43- (void)popUpStatusItemMenu:(NSMenu *)menu;
44@end
45
f1d9e1ec
DE
46class wxTaskBarIconWindow;
47
48// ============================================================================
49// wxTaskBarIconCocoaImpl
50// Base class for the various Cocoa implementations.
51// ============================================================================
52class wxTaskBarIconCocoaImpl
53{
54public:
55 wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
56 : m_taskBarIcon(taskBarIcon)
57 , m_iconWindow(NULL)
58 {}
59 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0;
60 virtual bool RemoveIcon() = 0;
61 virtual bool PopupMenu(wxMenu *menu) = 0;
62 virtual ~wxTaskBarIconCocoaImpl();
63 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; }
64protected:
4092a275
DE
65 inline wxMenu* CreatePopupMenu()
66 { wxASSERT(m_taskBarIcon);
9d1e4703 67 return m_taskBarIcon->CreatePopupMenu();
4092a275 68 }
f1d9e1ec
DE
69 wxTaskBarIcon *m_taskBarIcon;
70 wxTaskBarIconWindow *m_iconWindow;
71private:
72 wxTaskBarIconCocoaImpl();
73};
74
75// ============================================================================
76// wxTaskBarIconDockImpl
77// An implementation using the Dock icon.
78// ============================================================================
79class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
80{
81public:
82 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
83 virtual ~wxTaskBarIconDockImpl();
84 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
85 virtual bool RemoveIcon();
86 virtual bool PopupMenu(wxMenu *menu);
87
88 static WX_NSMenu CocoaGetDockNSMenu();
89protected:
90 WX_NSMenu CocoaDoGetDockNSMenu();
91 WX_NSImage m_originalDockIcon;
92 // There can be only one Dock icon, so make sure we keep it that way
93 static wxTaskBarIconDockImpl *sm_dockIcon;
94private:
95 wxTaskBarIconDockImpl();
96};
97
98// ============================================================================
99// wxTaskBarIconCustomStatusItemImpl
100// An implementation using an NSStatusItem with a custom NSView
101// ============================================================================
102class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
103{
104public:
105 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
106 virtual ~wxTaskBarIconCustomStatusItemImpl();
107 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
108 virtual bool RemoveIcon();
109 virtual bool PopupMenu(wxMenu *menu);
110protected:
111 NSStatusItem *m_cocoaNSStatusItem;
112private:
113 wxTaskBarIconCustomStatusItemImpl();
114};
115
116// ============================================================================
117// wxTaskBarIconWindow
118// Used by all implementations to forward events from the wxMenu
119// ============================================================================
120class wxTaskBarIconWindow: public wxWindow
121{
122 DECLARE_EVENT_TABLE()
123public:
124 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
125 : wxWindow(NULL,-1)
126 , m_taskBarIconImpl(taskBarIconImpl)
127 { wxASSERT(m_taskBarIconImpl); }
128
129 void OnMenuEvent(wxCommandEvent& event);
130protected:
131 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
132};
133
134// ============================================================================
135// wxTaskBarIconWindowCustom
136// Used by the CustomStatusIcon implementation for the custom NSView.
137// ============================================================================
138class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
139{
140 DECLARE_EVENT_TABLE()
141public:
142 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
143 : wxTaskBarIconWindow(taskBarIconImpl)
144 {}
145 void SetIcon(const wxIcon& icon)
146 { m_icon = icon; }
147 void OnMouseEvent(wxMouseEvent &event);
148 void OnPaint(wxPaintEvent &event);
149protected:
150 wxIcon m_icon;
151};
152
153// ============================================================================
154// wxTaskBarIcon implementation
155// The facade class.
156// ============================================================================
157IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
158
159wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
160{
161 if(iconType == DOCK)
162 m_impl = new wxTaskBarIconDockImpl(this);
163 else if(iconType == CUSTOM_STATUSITEM)
164 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
165 else
166 { m_impl = NULL;
167 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
168 }
169}
170
171wxTaskBarIcon::~wxTaskBarIcon()
172{
173 delete m_impl;
174}
175
176// Operations
177bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
178{
179 return m_impl->SetIcon(icon,tooltip);
180}
181
182bool wxTaskBarIcon::RemoveIcon()
183{
184 return m_impl->RemoveIcon();
185}
186
187bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
188{
189 return m_impl->PopupMenu(menu);
190}
191
192// ============================================================================
193// wxTaskBarIconCocoaImpl
194// ============================================================================
195
196#if 0
197wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
198: m_taskBarIcon(taskBarIcon)
199, m_iconWindow(NULL)
200{
201}
202#endif
203
204wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
205{
206// wxAutoNSAutoreleasePool pool;
207 delete m_iconWindow;
208}
209
210// ============================================================================
211// wxTaskBarIconDockImpl
212// ============================================================================
213wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
214
215wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
216: wxTaskBarIconCocoaImpl(taskBarIcon)
217{
218 m_originalDockIcon = nil;
04fa0af1 219 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
f1d9e1ec
DE
220 sm_dockIcon = this;
221}
222
223wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
224{
225// wxAutoNSAutoreleasePool pool;
226 if(sm_dockIcon == this)
227 sm_dockIcon = NULL;
228}
229
230WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
231{
232 if(sm_dockIcon)
233 return sm_dockIcon->CocoaDoGetDockNSMenu();
234 return nil;
235}
236
237WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
238{
4092a275 239 wxMenu *dockMenu = CreatePopupMenu();
f1d9e1ec
DE
240 if(!dockMenu)
241 return nil;
242 if(!m_iconWindow)
243 m_iconWindow = new wxTaskBarIconWindow(this);
244 dockMenu->SetInvokingWindow(m_iconWindow);
245 dockMenu->UpdateUI();
246 dockMenu->SetCocoaDeletes(true);
247 return dockMenu->GetNSMenu();
248}
249
250bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
251{
252 wxAutoNSAutoreleasePool pool;
253 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
254 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
255 return true;
256}
257
258bool wxTaskBarIconDockImpl::RemoveIcon()
259{
260 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
261 [m_originalDockIcon release];
262 return true;
263}
264
265bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
266{
267 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
268 return false;
269}
270
271
272// ============================================================================
273// wxTaskBarIconCustomStatusItemImpl
274// ============================================================================
275wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
276: wxTaskBarIconCocoaImpl(taskBarIcon)
277{
278 m_cocoaNSStatusItem = nil;
279}
280
281wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
282{
283}
284
285bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
286{
287 wxAutoNSAutoreleasePool pool;
288 if(!m_cocoaNSStatusItem)
289 {
290 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
291 [m_cocoaNSStatusItem retain];
292 }
293 if(!m_iconWindow)
294 m_iconWindow= new wxTaskBarIconWindowCustom(this);
295 static_cast<wxTaskBarIconWindowCustom*>(m_iconWindow)->SetIcon(icon);
296 // FIXME: no less than 10 because most icon types don't work yet
297 // and this allows us to see how task bar icons would work
298 [m_iconWindow->GetNSView() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
299 [m_cocoaNSStatusItem setView:m_iconWindow->GetNSView()];
300 return true;
301}
302
303bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
304{
305 [m_cocoaNSStatusItem release];
306 m_cocoaNSStatusItem = nil;
307 delete m_iconWindow;
308 m_iconWindow = NULL;
309 return true;
310}
311
312bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
313{
314 wxASSERT(menu);
315 menu->SetInvokingWindow(m_iconWindow);
316 menu->UpdateUI();
317
318 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
319 { // OS X >= 10.3
320 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
321 }
322 else
323 { // pretty good fake for OS X < 10.3
324 NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown
325 location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0
326 windowNumber:[[m_iconWindow->GetNSView() window] windowNumber]
327 context:[NSGraphicsContext currentContext]
328 eventNumber:0 clickCount:1 pressure:0.0];
329 [NSMenu popUpContextMenu:menu->GetNSMenu() withEvent:nsevent forView:m_iconWindow->GetNSView()];
330 }
331 menu->SetInvokingWindow(NULL);
332 return true;
333}
334
335// ============================================================================
336// wxTaskBarIconWindow
337// ============================================================================
338BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
339 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
340END_EVENT_TABLE()
341
342void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
343{
344 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
345}
346
347// ============================================================================
348// wxTaskBarIconWindowCustom
349// ============================================================================
350BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
351 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
352 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
353END_EVENT_TABLE()
354
355void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
356{
357 wxEventType tbEventType = 0;
358 if(event.GetEventType() == wxEVT_MOTION)
359 tbEventType = wxEVT_TASKBAR_MOVE;
360 else if(event.GetEventType() == wxEVT_LEFT_DOWN)
361 tbEventType = wxEVT_TASKBAR_LEFT_DOWN;
362 else if(event.GetEventType() == wxEVT_LEFT_UP)
363 tbEventType = wxEVT_TASKBAR_LEFT_UP;
364 else if(event.GetEventType() == wxEVT_RIGHT_DOWN)
365 tbEventType = wxEVT_TASKBAR_RIGHT_DOWN;
366 else if(event.GetEventType() == wxEVT_RIGHT_UP)
367 tbEventType = wxEVT_TASKBAR_RIGHT_UP;
368 else if(event.GetEventType() == wxEVT_LEFT_DCLICK)
369 tbEventType = wxEVT_TASKBAR_LEFT_DCLICK;
370 else if(event.GetEventType() == wxEVT_RIGHT_DCLICK)
371 tbEventType = wxEVT_TASKBAR_RIGHT_DCLICK;
372 else
373 return;
374 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
375 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
376}
377
378void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
379{
380 wxPaintDC dc(this);
381 // FIXME: This is a temporary hack until we can see real icons
382 dc.SetBackground(wxBrush(*wxBLUE));
383 dc.Clear();
384 dc.DrawIcon(m_icon,0,0);
385}
386
387// ============================================================================
388// wxTaskBarIconNSApplicationDelegateCategory
389// ============================================================================
390
391// This neatly solves the problem of DLL separation. If the wxAdvanced
392// library (which this file is part of) is loaded then this category is
393// defined and we get dock menu behavior without app.mm ever having to
394// know we exist. C++ did sucketh so. :-)
395
396@interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
397- (NSMenu*)applicationDockMenu:(NSApplication *)sender;
398@end
399
400@implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
401- (NSMenu*)applicationDockMenu:(NSApplication *)sender
402{
403 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
404}
405@end
406
407#endif //def wxHAS_TASK_BAR_ICON