warning fix for 10.2
[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