]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/taskbar.mm
use inches2mm instead of literal constant in wxGetDisplayPPI()
[wxWidgets.git] / src / osx / 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: taskbar.mm 35650 2005-09-23 12:56:45Z MR $
8 // Copyright: (c) 2004 David Elliott
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #ifdef wxUSE_TASKBARICON
14
15 #ifndef WX_PRECOMP
16 #include "wx/menu.h"
17 #include "wx/icon.h"
18 #include "wx/log.h"
19 #include "wx/dcclient.h"
20 #endif
21
22 #include "wx/taskbar.h"
23
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>
33
34 #import <AppKit/NSEvent.h>
35 #import <AppKit/NSWindow.h>
36 #import <AppKit/NSGraphicsContext.h>
37
38 #include "wx/cocoa/NSApplication.h"
39 #include "wx/cocoa/autorelease.h"
40
41 // A category for methods that are only present in Panther's SDK
42 @interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
43 - (void)popUpStatusItemMenu:(NSMenu *)menu;
44 @end
45
46 class wxTaskBarIconWindow;
47
48 // ============================================================================
49 // wxTaskBarIconImpl
50 // Base class for the various Cocoa implementations.
51 // ============================================================================
52 class wxTaskBarIconImpl
53 {
54 public:
55 wxTaskBarIconImpl(wxTaskBarIcon *taskBarIcon)
56 : m_taskBarIcon(taskBarIcon)
57 , m_iconWindow(NULL)
58 {}
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 ~wxTaskBarIconImpl();
63 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; }
64 wxMenu * CreatePopupMenu()
65 { return m_taskBarIcon->CreatePopupMenu(); }
66
67 DECLARE_NO_COPY_CLASS(wxTaskBarIconImpl)
68
69 protected:
70 wxTaskBarIcon *m_taskBarIcon;
71 wxTaskBarIconWindow *m_iconWindow;
72 private:
73 wxTaskBarIconImpl();
74 };
75
76 // ============================================================================
77 // wxTaskBarIconDockImpl
78 // An implementation using the Dock icon.
79 // ============================================================================
80 class wxTaskBarIconDockImpl: public wxTaskBarIconImpl
81 {
82 public:
83 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
84 virtual ~wxTaskBarIconDockImpl();
85 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
86 virtual bool RemoveIcon();
87 virtual bool PopupMenu(wxMenu *menu);
88
89 static WX_NSMenu CocoaGetDockNSMenu();
90 protected:
91 WX_NSMenu CocoaDoGetDockNSMenu();
92 WX_NSImage m_originalDockIcon;
93 // There can be only one Dock icon, so make sure we keep it that way
94 static wxTaskBarIconDockImpl *sm_dockIcon;
95 private:
96 wxTaskBarIconDockImpl();
97 };
98
99 // ============================================================================
100 // wxTaskBarIconCustomStatusItemImpl
101 // An implementation using an NSStatusItem with a custom NSView
102 // ============================================================================
103 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconImpl
104 {
105 public:
106 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
107 virtual ~wxTaskBarIconCustomStatusItemImpl();
108 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
109 virtual bool RemoveIcon();
110 virtual bool PopupMenu(wxMenu *menu);
111 protected:
112 NSStatusItem *m_cocoaNSStatusItem;
113 private:
114 wxTaskBarIconCustomStatusItemImpl();
115 };
116
117 // ============================================================================
118 // wxTaskBarIconWindow
119 // Used by all implementations to forward events from the wxMenu
120 // ============================================================================
121 class wxTaskBarIconWindow: public wxWindow
122 {
123 DECLARE_EVENT_TABLE()
124 public:
125 wxTaskBarIconWindow(wxTaskBarIconImpl *taskBarIconImpl)
126 : wxWindow(NULL,-1)
127 , m_taskBarIconImpl(taskBarIconImpl)
128 { wxASSERT(m_taskBarIconImpl); }
129
130 void OnMenuEvent(wxCommandEvent& event);
131 protected:
132 wxTaskBarIconImpl *m_taskBarIconImpl;
133 };
134
135 // ============================================================================
136 // wxTaskBarIconWindowCustom
137 // Used by the CustomStatusIcon implementation for the custom NSView.
138 // ============================================================================
139 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
140 {
141 DECLARE_EVENT_TABLE()
142 public:
143 wxTaskBarIconWindowCustom(wxTaskBarIconImpl *taskBarIconImpl)
144 : wxTaskBarIconWindow(taskBarIconImpl)
145 {}
146 void SetIcon(const wxIcon& icon)
147 { m_icon = icon; }
148 void OnMouseEvent(wxMouseEvent &event);
149 void OnPaint(wxPaintEvent &event);
150 protected:
151 wxIcon m_icon;
152 };
153
154 // ============================================================================
155 // wxTaskBarIcon implementation
156 // The facade class.
157 // ============================================================================
158 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
159
160 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
161 {
162 if(iconType == DOCK)
163 m_impl = new wxTaskBarIconDockImpl(this);
164 else if(iconType == CUSTOM_STATUSITEM)
165 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
166 else
167 { m_impl = NULL;
168 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
169 }
170 }
171
172 wxTaskBarIcon::~wxTaskBarIcon()
173 {
174 delete m_impl;
175 }
176
177 // Operations
178
179 bool wxTaskBarIcon::IsIconInstalled() const
180 {
181 return false;
182 }
183
184 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
185 {
186 return m_impl->SetIcon(icon,tooltip);
187 }
188
189 bool wxTaskBarIcon::RemoveIcon()
190 {
191 return m_impl->RemoveIcon();
192 }
193
194 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
195 {
196 return m_impl->PopupMenu(menu);
197 }
198
199 // ============================================================================
200 // wxTaskBarIconImpl
201 // ============================================================================
202
203 wxTaskBarIconImpl::~wxTaskBarIconImpl()
204 {
205 // wxAutoNSAutoreleasePool pool;
206 delete m_iconWindow;
207 }
208
209 // ============================================================================
210 // wxTaskBarIconDockImpl
211 // ============================================================================
212 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
213
214 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
215 : wxTaskBarIconImpl(taskBarIcon)
216 {
217 m_originalDockIcon = nil;
218 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
219 sm_dockIcon = this;
220 }
221
222 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
223 {
224 // wxAutoNSAutoreleasePool pool;
225 if(sm_dockIcon == this)
226 sm_dockIcon = NULL;
227 }
228
229 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
230 {
231 if(sm_dockIcon)
232 return sm_dockIcon->CocoaDoGetDockNSMenu();
233 return nil;
234 }
235
236 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
237 {
238 wxMenu *dockMenu = CreatePopupMenu();
239 if(!dockMenu)
240 return nil;
241 if(!m_iconWindow)
242 m_iconWindow = new wxTaskBarIconWindow(this);
243 dockMenu->SetInvokingWindow(m_iconWindow);
244 dockMenu->UpdateUI();
245 //dockMenu->SetCocoaDeletes(true);
246 return (WX_NSMenu)dockMenu->GetHMenu();
247 }
248
249 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
250 {
251 wxAutoNSAutoreleasePool pool;
252 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
253 //[[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
254 return true;
255 }
256
257 bool wxTaskBarIconDockImpl::RemoveIcon()
258 {
259 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
260 [m_originalDockIcon release];
261 return true;
262 }
263
264 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
265 {
266 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
267 return false;
268 }
269
270
271 // ============================================================================
272 // wxTaskBarIconCustomStatusItemImpl
273 // ============================================================================
274 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
275 : wxTaskBarIconImpl(taskBarIcon)
276 {
277 m_cocoaNSStatusItem = nil;
278 }
279
280 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
281 {
282 }
283
284 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
285 {
286 wxAutoNSAutoreleasePool pool;
287 if(!m_cocoaNSStatusItem)
288 {
289 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
290 [m_cocoaNSStatusItem retain];
291 }
292 if(!m_iconWindow)
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 [(NSView*)m_iconWindow->GetHandle() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
298 [m_cocoaNSStatusItem setView:(NSView*)m_iconWindow->GetHandle()];
299 return true;
300 }
301
302 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
303 {
304 [m_cocoaNSStatusItem release];
305 m_cocoaNSStatusItem = nil;
306 delete m_iconWindow;
307 m_iconWindow = NULL;
308 return true;
309 }
310
311 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
312 {
313 wxASSERT(menu);
314 menu->SetInvokingWindow(m_iconWindow);
315 menu->UpdateUI();
316
317 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
318 { // OS X >= 10.3
319 [m_cocoaNSStatusItem popUpStatusItemMenu:(NSMenu*)menu->GetHMenu()];
320 }
321 else
322 { // pretty good fake for OS X < 10.3
323 NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown
324 location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0
325 windowNumber:[[(NSView*)m_iconWindow->GetHandle() window] windowNumber]
326 context:[NSGraphicsContext currentContext]
327 eventNumber:0 clickCount:1 pressure:0.0];
328 [NSMenu popUpContextMenu:menu->GetHMenu() withEvent:nsevent forView:(NSView*)m_iconWindow->GetHandle()];
329 }
330 menu->SetInvokingWindow(NULL);
331 return true;
332 }
333
334 // ============================================================================
335 // wxTaskBarIconWindow
336 // ============================================================================
337 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
338 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
339 END_EVENT_TABLE()
340
341 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
342 {
343 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
344 }
345
346 // ============================================================================
347 // wxTaskBarIconWindowCustom
348 // ============================================================================
349 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
350 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
351 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
352 END_EVENT_TABLE()
353
354 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
355 {
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;
371 else
372 return;
373 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
374 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
375 }
376
377 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
378 {
379 wxPaintDC dc(this);
380 // FIXME: This is a temporary hack until we can see real icons
381 dc.SetBackground(wxBrush(*wxBLUE));
382 dc.Clear();
383 dc.DrawIcon(m_icon,0,0);
384 }
385
386 // ============================================================================
387 // wxTaskBarIconNSApplicationDelegateCategory
388 // ============================================================================
389
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 behavior without app.mm ever having to
393 // know we exist. C++ did sucketh so. :-)
394
395 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
396 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
397 @end
398
399 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
400 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
401 {
402 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
403 }
404 @end
405
406 #endif //def wxHAS_TASK_BAR_ICON