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 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 wxTaskBarIcon *m_taskBarIcon;
65 wxTaskBarIconWindow *m_iconWindow;
67 wxTaskBarIconCocoaImpl();
70 // ============================================================================
71 // wxTaskBarIconDockImpl
72 // An implementation using the Dock icon.
73 // ============================================================================
74 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
77 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
78 virtual ~wxTaskBarIconDockImpl();
79 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
80 virtual bool RemoveIcon();
81 virtual bool PopupMenu(wxMenu *menu);
83 static WX_NSMenu CocoaGetDockNSMenu();
85 WX_NSMenu CocoaDoGetDockNSMenu();
86 WX_NSImage m_originalDockIcon;
87 // There can be only one Dock icon, so make sure we keep it that way
88 static wxTaskBarIconDockImpl *sm_dockIcon;
90 wxTaskBarIconDockImpl();
93 // ============================================================================
94 // wxTaskBarIconCustomStatusItemImpl
95 // An implementation using an NSStatusItem with a custom NSView
96 // ============================================================================
97 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
100 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
101 virtual ~wxTaskBarIconCustomStatusItemImpl();
102 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
103 virtual bool RemoveIcon();
104 virtual bool PopupMenu(wxMenu *menu);
106 NSStatusItem *m_cocoaNSStatusItem;
108 wxTaskBarIconCustomStatusItemImpl();
111 // ============================================================================
112 // wxTaskBarIconWindow
113 // Used by all implementations to forward events from the wxMenu
114 // ============================================================================
115 class wxTaskBarIconWindow: public wxWindow
117 DECLARE_EVENT_TABLE()
119 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
121 , m_taskBarIconImpl(taskBarIconImpl)
122 { wxASSERT(m_taskBarIconImpl); }
124 void OnMenuEvent(wxCommandEvent& event);
126 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
129 // ============================================================================
130 // wxTaskBarIconWindowCustom
131 // Used by the CustomStatusIcon implementation for the custom NSView.
132 // ============================================================================
133 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
135 DECLARE_EVENT_TABLE()
137 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
138 : wxTaskBarIconWindow(taskBarIconImpl)
140 void SetIcon(const wxIcon& icon)
142 void OnMouseEvent(wxMouseEvent &event);
143 void OnPaint(wxPaintEvent &event);
148 // ============================================================================
149 // wxTaskBarIcon implementation
151 // ============================================================================
152 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
154 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
157 m_impl = new wxTaskBarIconDockImpl(this);
158 else if(iconType == CUSTOM_STATUSITEM)
159 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
162 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
166 wxTaskBarIcon::~wxTaskBarIcon()
172 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
174 return m_impl->SetIcon(icon,tooltip);
177 bool wxTaskBarIcon::RemoveIcon()
179 return m_impl->RemoveIcon();
182 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
184 return m_impl->PopupMenu(menu);
187 // ============================================================================
188 // wxTaskBarIconCocoaImpl
189 // ============================================================================
192 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
193 : m_taskBarIcon(taskBarIcon)
199 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
201 // wxAutoNSAutoreleasePool pool;
205 // ============================================================================
206 // wxTaskBarIconDockImpl
207 // ============================================================================
208 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
210 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
211 : wxTaskBarIconCocoaImpl(taskBarIcon)
213 m_originalDockIcon = nil;
214 wxASSERT_MSG(!sm_dockIcon,"You should never have more than one dock icon!");
218 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
220 // wxAutoNSAutoreleasePool pool;
221 if(sm_dockIcon == this)
225 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
228 return sm_dockIcon->CocoaDoGetDockNSMenu();
232 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
234 wxMenu *dockMenu = m_taskBarIcon->CreatePopupMenu();
238 m_iconWindow = new wxTaskBarIconWindow(this);
239 dockMenu->SetInvokingWindow(m_iconWindow);
240 dockMenu->UpdateUI();
241 dockMenu->SetCocoaDeletes(true);
242 return dockMenu->GetNSMenu();
245 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
247 wxAutoNSAutoreleasePool pool;
248 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
249 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
253 bool wxTaskBarIconDockImpl::RemoveIcon()
255 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
256 [m_originalDockIcon release];
260 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
262 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
267 // ============================================================================
268 // wxTaskBarIconCustomStatusItemImpl
269 // ============================================================================
270 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
271 : wxTaskBarIconCocoaImpl(taskBarIcon)
273 m_cocoaNSStatusItem = nil;
276 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
280 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
282 wxAutoNSAutoreleasePool pool;
283 if(!m_cocoaNSStatusItem)
285 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
286 [m_cocoaNSStatusItem retain];
289 m_iconWindow= new wxTaskBarIconWindowCustom(this);
290 static_cast<wxTaskBarIconWindowCustom*>(m_iconWindow)->SetIcon(icon);
291 // FIXME: no less than 10 because most icon types don't work yet
292 // and this allows us to see how task bar icons would work
293 [m_iconWindow->GetNSView() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
294 [m_cocoaNSStatusItem setView:m_iconWindow->GetNSView()];
298 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
300 [m_cocoaNSStatusItem release];
301 m_cocoaNSStatusItem = nil;
307 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
310 menu->SetInvokingWindow(m_iconWindow);
313 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
315 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
318 { // pretty good fake for OS X < 10.3
319 NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown
320 location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0
321 windowNumber:[[m_iconWindow->GetNSView() window] windowNumber]
322 context:[NSGraphicsContext currentContext]
323 eventNumber:0 clickCount:1 pressure:0.0];
324 [NSMenu popUpContextMenu:menu->GetNSMenu() withEvent:nsevent forView:m_iconWindow->GetNSView()];
326 menu->SetInvokingWindow(NULL);
330 // ============================================================================
331 // wxTaskBarIconWindow
332 // ============================================================================
333 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
334 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
337 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
339 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
342 // ============================================================================
343 // wxTaskBarIconWindowCustom
344 // ============================================================================
345 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
346 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
347 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
350 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
352 wxEventType tbEventType = 0;
353 if(event.GetEventType() == wxEVT_MOTION)
354 tbEventType = wxEVT_TASKBAR_MOVE;
355 else if(event.GetEventType() == wxEVT_LEFT_DOWN)
356 tbEventType = wxEVT_TASKBAR_LEFT_DOWN;
357 else if(event.GetEventType() == wxEVT_LEFT_UP)
358 tbEventType = wxEVT_TASKBAR_LEFT_UP;
359 else if(event.GetEventType() == wxEVT_RIGHT_DOWN)
360 tbEventType = wxEVT_TASKBAR_RIGHT_DOWN;
361 else if(event.GetEventType() == wxEVT_RIGHT_UP)
362 tbEventType = wxEVT_TASKBAR_RIGHT_UP;
363 else if(event.GetEventType() == wxEVT_LEFT_DCLICK)
364 tbEventType = wxEVT_TASKBAR_LEFT_DCLICK;
365 else if(event.GetEventType() == wxEVT_RIGHT_DCLICK)
366 tbEventType = wxEVT_TASKBAR_RIGHT_DCLICK;
369 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
370 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
373 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
376 // FIXME: This is a temporary hack until we can see real icons
377 dc.SetBackground(wxBrush(*wxBLUE));
379 dc.DrawIcon(m_icon,0,0);
382 // ============================================================================
383 // wxTaskBarIconNSApplicationDelegateCategory
384 // ============================================================================
386 // This neatly solves the problem of DLL separation. If the wxAdvanced
387 // library (which this file is part of) is loaded then this category is
388 // defined and we get dock menu behavior without app.mm ever having to
389 // know we exist. C++ did sucketh so. :-)
391 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
392 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
395 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
396 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
398 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
402 #endif //def wxHAS_TASK_BAR_ICON