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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "taskbar.h"
16 #include "wx/wxprec.h"
17 #ifdef wxHAS_TASK_BAR_ICON
23 #include "wx/dcclient.h"
26 #include "wx/taskbar.h"
28 #import <AppKit/NSApplication.h>
29 #import <AppKit/NSImage.h>
30 #import <AppKit/NSMenu.h>
31 #import <AppKit/NSMenuItem.h>
32 #import <AppKit/NSStatusBar.h>
33 #import <AppKit/NSStatusItem.h>
34 #import <AppKit/NSView.h>
35 #import <Foundation/NSArray.h>
36 #import <Foundation/NSEnumerator.h>
38 #import <AppKit/NSEvent.h>
39 #import <AppKit/NSWindow.h>
40 #import <AppKit/NSGraphicsContext.h>
42 #include "wx/cocoa/NSApplication.h"
43 #include "wx/cocoa/autorelease.h"
45 // A category for methods that are only present in Panther's SDK
46 @interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
47 - (void)popUpStatusItemMenu:(NSMenu *)menu;
50 class wxTaskBarIconWindow;
52 // ============================================================================
53 // wxTaskBarIconCocoaImpl
54 // Base class for the various Cocoa implementations.
55 // ============================================================================
56 class wxTaskBarIconCocoaImpl
59 wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
60 : m_taskBarIcon(taskBarIcon)
63 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0;
64 virtual bool RemoveIcon() = 0;
65 virtual bool PopupMenu(wxMenu *menu) = 0;
66 virtual ~wxTaskBarIconCocoaImpl();
67 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; }
69 inline wxMenu* CreatePopupMenu()
70 { wxASSERT(m_taskBarIcon);
71 return m_taskBarIcon->CreatePopupMenu();
73 wxTaskBarIcon *m_taskBarIcon;
74 wxTaskBarIconWindow *m_iconWindow;
76 wxTaskBarIconCocoaImpl();
79 // ============================================================================
80 // wxTaskBarIconDockImpl
81 // An implementation using the Dock icon.
82 // ============================================================================
83 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
86 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
87 virtual ~wxTaskBarIconDockImpl();
88 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
89 virtual bool RemoveIcon();
90 virtual bool PopupMenu(wxMenu *menu);
92 static WX_NSMenu CocoaGetDockNSMenu();
94 WX_NSMenu CocoaDoGetDockNSMenu();
95 WX_NSImage m_originalDockIcon;
96 // There can be only one Dock icon, so make sure we keep it that way
97 static wxTaskBarIconDockImpl *sm_dockIcon;
99 wxTaskBarIconDockImpl();
102 // ============================================================================
103 // wxTaskBarIconCustomStatusItemImpl
104 // An implementation using an NSStatusItem with a custom NSView
105 // ============================================================================
106 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
109 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
110 virtual ~wxTaskBarIconCustomStatusItemImpl();
111 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
112 virtual bool RemoveIcon();
113 virtual bool PopupMenu(wxMenu *menu);
115 NSStatusItem *m_cocoaNSStatusItem;
117 wxTaskBarIconCustomStatusItemImpl();
120 // ============================================================================
121 // wxTaskBarIconWindow
122 // Used by all implementations to forward events from the wxMenu
123 // ============================================================================
124 class wxTaskBarIconWindow: public wxWindow
126 DECLARE_EVENT_TABLE()
128 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
130 , m_taskBarIconImpl(taskBarIconImpl)
131 { wxASSERT(m_taskBarIconImpl); }
133 void OnMenuEvent(wxCommandEvent& event);
135 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
138 // ============================================================================
139 // wxTaskBarIconWindowCustom
140 // Used by the CustomStatusIcon implementation for the custom NSView.
141 // ============================================================================
142 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
144 DECLARE_EVENT_TABLE()
146 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
147 : wxTaskBarIconWindow(taskBarIconImpl)
149 void SetIcon(const wxIcon& icon)
151 void OnMouseEvent(wxMouseEvent &event);
152 void OnPaint(wxPaintEvent &event);
157 // ============================================================================
158 // wxTaskBarIcon implementation
160 // ============================================================================
161 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
163 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
166 m_impl = new wxTaskBarIconDockImpl(this);
167 else if(iconType == CUSTOM_STATUSITEM)
168 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
171 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
175 wxTaskBarIcon::~wxTaskBarIcon()
181 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
183 return m_impl->SetIcon(icon,tooltip);
186 bool wxTaskBarIcon::RemoveIcon()
188 return m_impl->RemoveIcon();
191 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
193 return m_impl->PopupMenu(menu);
196 // ============================================================================
197 // wxTaskBarIconCocoaImpl
198 // ============================================================================
201 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
202 : m_taskBarIcon(taskBarIcon)
208 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
210 // wxAutoNSAutoreleasePool pool;
214 // ============================================================================
215 // wxTaskBarIconDockImpl
216 // ============================================================================
217 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
219 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
220 : wxTaskBarIconCocoaImpl(taskBarIcon)
222 m_originalDockIcon = nil;
223 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
227 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
229 // wxAutoNSAutoreleasePool pool;
230 if(sm_dockIcon == this)
234 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
237 return sm_dockIcon->CocoaDoGetDockNSMenu();
241 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
243 wxMenu *dockMenu = CreatePopupMenu();
247 m_iconWindow = new wxTaskBarIconWindow(this);
248 dockMenu->SetInvokingWindow(m_iconWindow);
249 dockMenu->UpdateUI();
250 dockMenu->SetCocoaDeletes(true);
251 return dockMenu->GetNSMenu();
254 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
256 wxAutoNSAutoreleasePool pool;
257 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
258 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
262 bool wxTaskBarIconDockImpl::RemoveIcon()
264 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
265 [m_originalDockIcon release];
269 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
271 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
276 // ============================================================================
277 // wxTaskBarIconCustomStatusItemImpl
278 // ============================================================================
279 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
280 : wxTaskBarIconCocoaImpl(taskBarIcon)
282 m_cocoaNSStatusItem = nil;
285 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
289 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
291 wxAutoNSAutoreleasePool pool;
292 if(!m_cocoaNSStatusItem)
294 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
295 [m_cocoaNSStatusItem retain];
298 m_iconWindow= new wxTaskBarIconWindowCustom(this);
299 static_cast<wxTaskBarIconWindowCustom*>(m_iconWindow)->SetIcon(icon);
300 // FIXME: no less than 10 because most icon types don't work yet
301 // and this allows us to see how task bar icons would work
302 [m_iconWindow->GetNSView() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
303 [m_cocoaNSStatusItem setView:m_iconWindow->GetNSView()];
307 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
309 [m_cocoaNSStatusItem release];
310 m_cocoaNSStatusItem = nil;
316 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
319 menu->SetInvokingWindow(m_iconWindow);
322 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
324 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
327 { // pretty good fake for OS X < 10.3
328 NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown
329 location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0
330 windowNumber:[[m_iconWindow->GetNSView() window] windowNumber]
331 context:[NSGraphicsContext currentContext]
332 eventNumber:0 clickCount:1 pressure:0.0];
333 [NSMenu popUpContextMenu:menu->GetNSMenu() withEvent:nsevent forView:m_iconWindow->GetNSView()];
335 menu->SetInvokingWindow(NULL);
339 // ============================================================================
340 // wxTaskBarIconWindow
341 // ============================================================================
342 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
343 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
346 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
348 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
351 // ============================================================================
352 // wxTaskBarIconWindowCustom
353 // ============================================================================
354 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
355 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
356 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
359 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
361 wxEventType tbEventType = 0;
362 if(event.GetEventType() == wxEVT_MOTION)
363 tbEventType = wxEVT_TASKBAR_MOVE;
364 else if(event.GetEventType() == wxEVT_LEFT_DOWN)
365 tbEventType = wxEVT_TASKBAR_LEFT_DOWN;
366 else if(event.GetEventType() == wxEVT_LEFT_UP)
367 tbEventType = wxEVT_TASKBAR_LEFT_UP;
368 else if(event.GetEventType() == wxEVT_RIGHT_DOWN)
369 tbEventType = wxEVT_TASKBAR_RIGHT_DOWN;
370 else if(event.GetEventType() == wxEVT_RIGHT_UP)
371 tbEventType = wxEVT_TASKBAR_RIGHT_UP;
372 else if(event.GetEventType() == wxEVT_LEFT_DCLICK)
373 tbEventType = wxEVT_TASKBAR_LEFT_DCLICK;
374 else if(event.GetEventType() == wxEVT_RIGHT_DCLICK)
375 tbEventType = wxEVT_TASKBAR_RIGHT_DCLICK;
378 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
379 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
382 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
385 // FIXME: This is a temporary hack until we can see real icons
386 dc.SetBackground(wxBrush(*wxBLUE));
388 dc.DrawIcon(m_icon,0,0);
391 // ============================================================================
392 // wxTaskBarIconNSApplicationDelegateCategory
393 // ============================================================================
395 // This neatly solves the problem of DLL separation. If the wxAdvanced
396 // library (which this file is part of) is loaded then this category is
397 // defined and we get dock menu behavior without app.mm ever having to
398 // know we exist. C++ did sucketh so. :-)
400 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
401 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
404 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
405 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
407 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
411 #endif //def wxHAS_TASK_BAR_ICON