1 /////////////////////////////////////////////////////////////////////////
2 // File: src/cocoa/taskbar.mm
3 // Purpose: Implements wxTaskBarIcon class
4 // Author: David Elliott
7 // Copyright: (c) 2004 David Elliott
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////
11 #include "wx/wxprec.h"
12 #ifdef wxHAS_TASK_BAR_ICON
18 #include "wx/dcclient.h"
21 #include "wx/taskbar.h"
23 #import <AppKit/NSApplication.h>
24 #import <AppKit/NSImage.h>
25 #import <AppKit/NSMenu.h>
26 #import <AppKit/NSMenuItem.h>
27 #import <AppKit/NSStatusBar.h>
28 #import <AppKit/NSStatusItem.h>
29 #import <AppKit/NSView.h>
30 #import <Foundation/NSArray.h>
31 #import <Foundation/NSEnumerator.h>
33 #import <AppKit/NSEvent.h>
34 #import <AppKit/NSWindow.h>
35 #import <AppKit/NSGraphicsContext.h>
37 #include "wx/cocoa/NSApplication.h"
38 #include "wx/cocoa/autorelease.h"
40 // A category for methods that are only present in Panther's SDK
41 @interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
42 - (void)popUpStatusItemMenu:(NSMenu *)menu;
45 class wxTaskBarIconWindow;
47 // ============================================================================
48 // wxTaskBarIconCocoaImpl
49 // Base class for the various Cocoa implementations.
50 // ============================================================================
51 class wxTaskBarIconCocoaImpl
54 wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
55 : m_taskBarIcon(taskBarIcon)
58 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0;
59 virtual bool RemoveIcon() = 0;
60 virtual bool PopupMenu(wxMenu *menu) = 0;
61 virtual ~wxTaskBarIconCocoaImpl();
62 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; }
64 inline wxMenu* CreatePopupMenu()
65 { wxASSERT(m_taskBarIcon);
66 return m_taskBarIcon->CreatePopupMenu();
68 wxTaskBarIcon *m_taskBarIcon;
69 wxTaskBarIconWindow *m_iconWindow;
71 wxTaskBarIconCocoaImpl();
74 // ============================================================================
75 // wxTaskBarIconDockImpl
76 // An implementation using the Dock icon.
77 // ============================================================================
78 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
81 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
82 virtual ~wxTaskBarIconDockImpl();
83 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
84 virtual bool RemoveIcon();
85 virtual bool PopupMenu(wxMenu *menu);
87 static WX_NSMenu CocoaGetDockNSMenu();
89 WX_NSMenu CocoaDoGetDockNSMenu();
90 WX_NSImage m_originalDockIcon;
91 // There can be only one Dock icon, so make sure we keep it that way
92 static wxTaskBarIconDockImpl *sm_dockIcon;
94 wxTaskBarIconDockImpl();
97 // ============================================================================
98 // wxTaskBarIconCustomStatusItemImpl
99 // An implementation using an NSStatusItem with a custom NSView
100 // ============================================================================
101 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
104 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
105 virtual ~wxTaskBarIconCustomStatusItemImpl();
106 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
107 virtual bool RemoveIcon();
108 virtual bool PopupMenu(wxMenu *menu);
110 NSStatusItem *m_cocoaNSStatusItem;
112 wxTaskBarIconCustomStatusItemImpl();
115 // ============================================================================
116 // wxTaskBarIconWindow
117 // Used by all implementations to forward events from the wxMenu
118 // ============================================================================
119 class wxTaskBarIconWindow: public wxWindow
121 DECLARE_EVENT_TABLE()
123 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
125 , m_taskBarIconImpl(taskBarIconImpl)
126 { wxASSERT(m_taskBarIconImpl); }
128 void OnMenuEvent(wxCommandEvent& event);
130 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
133 // ============================================================================
134 // wxTaskBarIconWindowCustom
135 // Used by the CustomStatusIcon implementation for the custom NSView.
136 // ============================================================================
137 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
139 DECLARE_EVENT_TABLE()
141 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
142 : wxTaskBarIconWindow(taskBarIconImpl)
144 void SetIcon(const wxIcon& icon)
146 void OnMouseEvent(wxMouseEvent &event);
147 void OnPaint(wxPaintEvent &event);
152 // ============================================================================
153 // wxTaskBarIcon implementation
155 // ============================================================================
156 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
158 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
161 m_impl = new wxTaskBarIconDockImpl(this);
162 else if(iconType == CUSTOM_STATUSITEM)
163 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
166 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
170 wxTaskBarIcon::~wxTaskBarIcon()
176 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
178 return m_impl->SetIcon(icon,tooltip);
181 bool wxTaskBarIcon::RemoveIcon()
183 return m_impl->RemoveIcon();
186 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
188 return m_impl->PopupMenu(menu);
191 // ============================================================================
192 // wxTaskBarIconCocoaImpl
193 // ============================================================================
196 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
197 : m_taskBarIcon(taskBarIcon)
203 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
205 // wxAutoNSAutoreleasePool pool;
209 // ============================================================================
210 // wxTaskBarIconDockImpl
211 // ============================================================================
212 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
214 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
215 : wxTaskBarIconCocoaImpl(taskBarIcon)
217 m_originalDockIcon = nil;
218 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
222 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
224 // wxAutoNSAutoreleasePool pool;
225 if(sm_dockIcon == this)
229 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
232 return sm_dockIcon->CocoaDoGetDockNSMenu();
236 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
238 wxMenu *dockMenu = CreatePopupMenu();
242 m_iconWindow = new wxTaskBarIconWindow(this);
243 dockMenu->SetInvokingWindow(m_iconWindow);
244 dockMenu->UpdateUI();
245 dockMenu->SetCocoaDeletes(true);
246 return dockMenu->GetNSMenu();
249 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
251 wxAutoNSAutoreleasePool pool;
252 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
253 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
257 bool wxTaskBarIconDockImpl::RemoveIcon()
259 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
260 [m_originalDockIcon release];
264 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
266 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
271 // ============================================================================
272 // wxTaskBarIconCustomStatusItemImpl
273 // ============================================================================
274 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
275 : wxTaskBarIconCocoaImpl(taskBarIcon)
277 m_cocoaNSStatusItem = nil;
280 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
284 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
286 wxAutoNSAutoreleasePool pool;
287 if(!m_cocoaNSStatusItem)
289 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
290 [m_cocoaNSStatusItem retain];
293 m_iconWindow= new wxTaskBarIconWindowCustom(this);
294 static_cast<wxTaskBarIconWindowCustom*>(m_iconWindow)->SetIcon(icon);
295 // FIXME: no less than 10 because most icon types don't work yet
296 // and this allows us to see how task bar icons would work
297 [m_iconWindow->GetNSView() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
298 [m_cocoaNSStatusItem setView:m_iconWindow->GetNSView()];
302 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
304 [m_cocoaNSStatusItem release];
305 m_cocoaNSStatusItem = nil;
311 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
313 wxCHECK_MSG(menu, false, "can't popup a NULL menu");
315 wxMenuInvokingWindowSetter setInvokingWin(*menu, m_iconWindow);
318 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
320 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
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()];
334 // ============================================================================
335 // wxTaskBarIconWindow
336 // ============================================================================
337 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
338 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
341 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
343 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
346 // ============================================================================
347 // wxTaskBarIconWindowCustom
348 // ============================================================================
349 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
350 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
351 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
354 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
356 wxEventType tbEventType = 0;
357 if(event.GetEventType() == wxEVT_MOTION)
358 tbEventType = wxEVT_TASKBAR_MOVE;
359 else if(event.GetEventType() == wxEVT_LEFT_DOWN)
360 tbEventType = wxEVT_TASKBAR_LEFT_DOWN;
361 else if(event.GetEventType() == wxEVT_LEFT_UP)
362 tbEventType = wxEVT_TASKBAR_LEFT_UP;
363 else if(event.GetEventType() == wxEVT_RIGHT_DOWN)
364 tbEventType = wxEVT_TASKBAR_RIGHT_DOWN;
365 else if(event.GetEventType() == wxEVT_RIGHT_UP)
366 tbEventType = wxEVT_TASKBAR_RIGHT_UP;
367 else if(event.GetEventType() == wxEVT_LEFT_DCLICK)
368 tbEventType = wxEVT_TASKBAR_LEFT_DCLICK;
369 else if(event.GetEventType() == wxEVT_RIGHT_DCLICK)
370 tbEventType = wxEVT_TASKBAR_RIGHT_DCLICK;
373 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
374 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
377 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
380 // FIXME: This is a temporary hack until we can see real icons
381 dc.SetBackground(wxBrush(*wxBLUE));
383 dc.DrawIcon(m_icon,0,0);
386 // ============================================================================
387 // wxTaskBarIconNSApplicationDelegateCategory
388 // ============================================================================
390 // This neatly solves the problem of DLL separation. If the wxAdvanced
391 // library (which this file is part of) is loaded then this category is
392 // defined and we get dock menu behaviour without app.mm ever having to
393 // know we exist. C++ did sucketh so. :-)
395 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
396 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
399 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
400 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
402 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
406 #endif //def wxHAS_TASK_BAR_ICON