1 /////////////////////////////////////////////////////////////////////////
2 // File: src/cocoa/taskbar.mm
3 // Purpose: Implements wxTaskBarIcon class
4 // Author: David Elliott
8 // Copyright: (c) 2004 David Elliott
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
13 #ifdef wxHAS_TASK_BAR_ICON
19 #include "wx/dcclient.h"
22 #include "wx/taskbar.h"
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>
34 #import <AppKit/NSEvent.h>
35 #import <AppKit/NSWindow.h>
36 #import <AppKit/NSGraphicsContext.h>
38 #include "wx/cocoa/NSApplication.h"
39 #include "wx/cocoa/autorelease.h"
41 // A category for methods that are only present in Panther's SDK
42 @interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
43 - (void)popUpStatusItemMenu:(NSMenu *)menu;
46 class wxTaskBarIconWindow;
48 // ============================================================================
49 // wxTaskBarIconCocoaImpl
50 // Base class for the various Cocoa implementations.
51 // ============================================================================
52 class wxTaskBarIconCocoaImpl
55 wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
56 : m_taskBarIcon(taskBarIcon)
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; }
65 inline wxMenu* CreatePopupMenu()
66 { wxASSERT(m_taskBarIcon);
67 return m_taskBarIcon->CreatePopupMenu();
69 wxTaskBarIcon *m_taskBarIcon;
70 wxTaskBarIconWindow *m_iconWindow;
72 wxTaskBarIconCocoaImpl();
75 // ============================================================================
76 // wxTaskBarIconDockImpl
77 // An implementation using the Dock icon.
78 // ============================================================================
79 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
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);
88 static WX_NSMenu CocoaGetDockNSMenu();
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;
95 wxTaskBarIconDockImpl();
98 // ============================================================================
99 // wxTaskBarIconCustomStatusItemImpl
100 // An implementation using an NSStatusItem with a custom NSView
101 // ============================================================================
102 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
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);
111 NSStatusItem *m_cocoaNSStatusItem;
113 wxTaskBarIconCustomStatusItemImpl();
116 // ============================================================================
117 // wxTaskBarIconWindow
118 // Used by all implementations to forward events from the wxMenu
119 // ============================================================================
120 class wxTaskBarIconWindow: public wxWindow
122 DECLARE_EVENT_TABLE()
124 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
126 , m_taskBarIconImpl(taskBarIconImpl)
127 { wxASSERT(m_taskBarIconImpl); }
129 void OnMenuEvent(wxCommandEvent& event);
131 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
134 // ============================================================================
135 // wxTaskBarIconWindowCustom
136 // Used by the CustomStatusIcon implementation for the custom NSView.
137 // ============================================================================
138 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
140 DECLARE_EVENT_TABLE()
142 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
143 : wxTaskBarIconWindow(taskBarIconImpl)
145 void SetIcon(const wxIcon& icon)
147 void OnMouseEvent(wxMouseEvent &event);
148 void OnPaint(wxPaintEvent &event);
153 // ============================================================================
154 // wxTaskBarIcon implementation
156 // ============================================================================
157 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
159 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
162 m_impl = new wxTaskBarIconDockImpl(this);
163 else if(iconType == CUSTOM_STATUSITEM)
164 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
167 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
171 wxTaskBarIcon::~wxTaskBarIcon()
177 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
179 return m_impl->SetIcon(icon,tooltip);
182 bool wxTaskBarIcon::RemoveIcon()
184 return m_impl->RemoveIcon();
187 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
189 return m_impl->PopupMenu(menu);
192 // ============================================================================
193 // wxTaskBarIconCocoaImpl
194 // ============================================================================
197 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
198 : m_taskBarIcon(taskBarIcon)
204 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
206 // wxAutoNSAutoreleasePool pool;
210 // ============================================================================
211 // wxTaskBarIconDockImpl
212 // ============================================================================
213 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
215 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
216 : wxTaskBarIconCocoaImpl(taskBarIcon)
218 m_originalDockIcon = nil;
219 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
223 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
225 // wxAutoNSAutoreleasePool pool;
226 if(sm_dockIcon == this)
230 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
233 return sm_dockIcon->CocoaDoGetDockNSMenu();
237 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
239 wxMenu *dockMenu = CreatePopupMenu();
243 m_iconWindow = new wxTaskBarIconWindow(this);
244 dockMenu->SetInvokingWindow(m_iconWindow);
245 dockMenu->UpdateUI();
246 dockMenu->SetCocoaDeletes(true);
247 return dockMenu->GetNSMenu();
250 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
252 wxAutoNSAutoreleasePool pool;
253 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
254 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
258 bool wxTaskBarIconDockImpl::RemoveIcon()
260 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
261 [m_originalDockIcon release];
265 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
267 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
272 // ============================================================================
273 // wxTaskBarIconCustomStatusItemImpl
274 // ============================================================================
275 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
276 : wxTaskBarIconCocoaImpl(taskBarIcon)
278 m_cocoaNSStatusItem = nil;
281 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
285 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
287 wxAutoNSAutoreleasePool pool;
288 if(!m_cocoaNSStatusItem)
290 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
291 [m_cocoaNSStatusItem retain];
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()];
303 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
305 [m_cocoaNSStatusItem release];
306 m_cocoaNSStatusItem = nil;
312 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
315 menu->SetInvokingWindow(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()];
331 menu->SetInvokingWindow(NULL);
335 // ============================================================================
336 // wxTaskBarIconWindow
337 // ============================================================================
338 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
339 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
342 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
344 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
347 // ============================================================================
348 // wxTaskBarIconWindowCustom
349 // ============================================================================
350 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
351 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
352 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
355 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
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;
374 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
375 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
378 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
381 // FIXME: This is a temporary hack until we can see real icons
382 dc.SetBackground(wxBrush(*wxBLUE));
384 dc.DrawIcon(m_icon,0,0);
387 // ============================================================================
388 // wxTaskBarIconNSApplicationDelegateCategory
389 // ============================================================================
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. :-)
396 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
397 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
400 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
401 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
403 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
407 #endif //def wxHAS_TASK_BAR_ICON