]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/taskbar.mm
17147b1fd28b236a82706265dd6da54516bc5726
[wxWidgets.git] / src / cocoa / taskbar.mm
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
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "taskbar.h"
14 #endif
15
16 #include "wx/wxprec.h"
17 #ifdef wxHAS_TASK_BAR_ICON
18
19 #ifndef WX_PRECOMP
20 #include "wx/menu.h"
21 #include "wx/icon.h"
22 #include "wx/log.h"
23 #include "wx/dcclient.h"
24 #endif
25
26 #include "wx/taskbar.h"
27
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>
37
38 #import <AppKit/NSEvent.h>
39 #import <AppKit/NSWindow.h>
40 #import <AppKit/NSGraphicsContext.h>
41
42 #include "wx/cocoa/NSApplication.h"
43 #include "wx/cocoa/autorelease.h"
44
45 // A category for methods that are only present in Panther's SDK
46 @interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
47 - (void)popUpStatusItemMenu:(NSMenu *)menu;
48 @end
49
50 class wxTaskBarIconWindow;
51
52 // ============================================================================
53 // wxTaskBarIconCocoaImpl
54 // Base class for the various Cocoa implementations.
55 // ============================================================================
56 class wxTaskBarIconCocoaImpl
57 {
58 public:
59 wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
60 : m_taskBarIcon(taskBarIcon)
61 , m_iconWindow(NULL)
62 {}
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; }
68 protected:
69 inline wxMenu* CreatePopupMenu()
70 { wxASSERT(m_taskBarIcon);
71 return m_taskBarIcon->CreatePopupMenu();
72 }
73 wxTaskBarIcon *m_taskBarIcon;
74 wxTaskBarIconWindow *m_iconWindow;
75 private:
76 wxTaskBarIconCocoaImpl();
77 };
78
79 // ============================================================================
80 // wxTaskBarIconDockImpl
81 // An implementation using the Dock icon.
82 // ============================================================================
83 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
84 {
85 public:
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);
91
92 static WX_NSMenu CocoaGetDockNSMenu();
93 protected:
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;
98 private:
99 wxTaskBarIconDockImpl();
100 };
101
102 // ============================================================================
103 // wxTaskBarIconCustomStatusItemImpl
104 // An implementation using an NSStatusItem with a custom NSView
105 // ============================================================================
106 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
107 {
108 public:
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);
114 protected:
115 NSStatusItem *m_cocoaNSStatusItem;
116 private:
117 wxTaskBarIconCustomStatusItemImpl();
118 };
119
120 // ============================================================================
121 // wxTaskBarIconWindow
122 // Used by all implementations to forward events from the wxMenu
123 // ============================================================================
124 class wxTaskBarIconWindow: public wxWindow
125 {
126 DECLARE_EVENT_TABLE()
127 public:
128 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
129 : wxWindow(NULL,-1)
130 , m_taskBarIconImpl(taskBarIconImpl)
131 { wxASSERT(m_taskBarIconImpl); }
132
133 void OnMenuEvent(wxCommandEvent& event);
134 protected:
135 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
136 };
137
138 // ============================================================================
139 // wxTaskBarIconWindowCustom
140 // Used by the CustomStatusIcon implementation for the custom NSView.
141 // ============================================================================
142 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
143 {
144 DECLARE_EVENT_TABLE()
145 public:
146 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
147 : wxTaskBarIconWindow(taskBarIconImpl)
148 {}
149 void SetIcon(const wxIcon& icon)
150 { m_icon = icon; }
151 void OnMouseEvent(wxMouseEvent &event);
152 void OnPaint(wxPaintEvent &event);
153 protected:
154 wxIcon m_icon;
155 };
156
157 // ============================================================================
158 // wxTaskBarIcon implementation
159 // The facade class.
160 // ============================================================================
161 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
162
163 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
164 {
165 if(iconType == DOCK)
166 m_impl = new wxTaskBarIconDockImpl(this);
167 else if(iconType == CUSTOM_STATUSITEM)
168 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
169 else
170 { m_impl = NULL;
171 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
172 }
173 }
174
175 wxTaskBarIcon::~wxTaskBarIcon()
176 {
177 delete m_impl;
178 }
179
180 // Operations
181 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
182 {
183 return m_impl->SetIcon(icon,tooltip);
184 }
185
186 bool wxTaskBarIcon::RemoveIcon()
187 {
188 return m_impl->RemoveIcon();
189 }
190
191 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
192 {
193 return m_impl->PopupMenu(menu);
194 }
195
196 // ============================================================================
197 // wxTaskBarIconCocoaImpl
198 // ============================================================================
199
200 #if 0
201 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
202 : m_taskBarIcon(taskBarIcon)
203 , m_iconWindow(NULL)
204 {
205 }
206 #endif
207
208 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
209 {
210 // wxAutoNSAutoreleasePool pool;
211 delete m_iconWindow;
212 }
213
214 // ============================================================================
215 // wxTaskBarIconDockImpl
216 // ============================================================================
217 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
218
219 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
220 : wxTaskBarIconCocoaImpl(taskBarIcon)
221 {
222 m_originalDockIcon = nil;
223 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
224 sm_dockIcon = this;
225 }
226
227 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
228 {
229 // wxAutoNSAutoreleasePool pool;
230 if(sm_dockIcon == this)
231 sm_dockIcon = NULL;
232 }
233
234 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
235 {
236 if(sm_dockIcon)
237 return sm_dockIcon->CocoaDoGetDockNSMenu();
238 return nil;
239 }
240
241 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
242 {
243 wxMenu *dockMenu = CreatePopupMenu();
244 if(!dockMenu)
245 return nil;
246 if(!m_iconWindow)
247 m_iconWindow = new wxTaskBarIconWindow(this);
248 dockMenu->SetInvokingWindow(m_iconWindow);
249 dockMenu->UpdateUI();
250 dockMenu->SetCocoaDeletes(true);
251 return dockMenu->GetNSMenu();
252 }
253
254 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
255 {
256 wxAutoNSAutoreleasePool pool;
257 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
258 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
259 return true;
260 }
261
262 bool wxTaskBarIconDockImpl::RemoveIcon()
263 {
264 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
265 [m_originalDockIcon release];
266 return true;
267 }
268
269 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
270 {
271 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
272 return false;
273 }
274
275
276 // ============================================================================
277 // wxTaskBarIconCustomStatusItemImpl
278 // ============================================================================
279 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
280 : wxTaskBarIconCocoaImpl(taskBarIcon)
281 {
282 m_cocoaNSStatusItem = nil;
283 }
284
285 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
286 {
287 }
288
289 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
290 {
291 wxAutoNSAutoreleasePool pool;
292 if(!m_cocoaNSStatusItem)
293 {
294 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
295 [m_cocoaNSStatusItem retain];
296 }
297 if(!m_iconWindow)
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()];
304 return true;
305 }
306
307 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
308 {
309 [m_cocoaNSStatusItem release];
310 m_cocoaNSStatusItem = nil;
311 delete m_iconWindow;
312 m_iconWindow = NULL;
313 return true;
314 }
315
316 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
317 {
318 wxASSERT(menu);
319 menu->SetInvokingWindow(m_iconWindow);
320 menu->UpdateUI();
321
322 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
323 { // OS X >= 10.3
324 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
325 }
326 else
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()];
334 }
335 menu->SetInvokingWindow(NULL);
336 return true;
337 }
338
339 // ============================================================================
340 // wxTaskBarIconWindow
341 // ============================================================================
342 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
343 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
344 END_EVENT_TABLE()
345
346 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
347 {
348 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
349 }
350
351 // ============================================================================
352 // wxTaskBarIconWindowCustom
353 // ============================================================================
354 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
355 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
356 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
357 END_EVENT_TABLE()
358
359 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
360 {
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;
376 else
377 return;
378 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
379 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
380 }
381
382 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
383 {
384 wxPaintDC dc(this);
385 // FIXME: This is a temporary hack until we can see real icons
386 dc.SetBackground(wxBrush(*wxBLUE));
387 dc.Clear();
388 dc.DrawIcon(m_icon,0,0);
389 }
390
391 // ============================================================================
392 // wxTaskBarIconNSApplicationDelegateCategory
393 // ============================================================================
394
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. :-)
399
400 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
401 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
402 @end
403
404 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
405 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
406 {
407 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
408 }
409 @end
410
411 #endif //def wxHAS_TASK_BAR_ICON