Update OpenVMS makefile
[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 // Copyright:   (c) 2004 David Elliott
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12 #ifdef wxHAS_TASK_BAR_ICON
13
14 #ifndef WX_PRECOMP
15     #include "wx/menu.h"
16     #include "wx/icon.h"
17     #include "wx/log.h"
18     #include "wx/dcclient.h"
19 #endif
20
21 #include "wx/taskbar.h"
22
23 #import <AppKit/NSApplication.h>
24 #import <AppKit/NSImage.h>
25 #import <AppKit/NSMenu.h>
26 #import <AppKit/NSMenuItem.h>
27 #import <AppKit/NSStatusBar.h>
28 #import <AppKit/NSStatusItem.h>
29 #import <AppKit/NSView.h>
30 #import <Foundation/NSArray.h>
31 #import <Foundation/NSEnumerator.h>
32
33 #import <AppKit/NSEvent.h>
34 #import <AppKit/NSWindow.h>
35 #import <AppKit/NSGraphicsContext.h>
36
37 #include "wx/cocoa/NSApplication.h"
38 #include "wx/cocoa/autorelease.h"
39
40 // A category for methods that are only present in Panther's SDK
41 @interface NSStatusItem(wxNSStatusItemPrePantherCompatibility)
42 - (void)popUpStatusItemMenu:(NSMenu *)menu;
43 @end
44
45 class wxTaskBarIconWindow;
46
47 // ============================================================================
48 // wxTaskBarIconCocoaImpl
49 //     Base class for the various Cocoa implementations.
50 // ============================================================================
51 class wxTaskBarIconCocoaImpl
52 {
53 public:
54     wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
55     :   m_taskBarIcon(taskBarIcon)
56     ,   m_iconWindow(NULL)
57     {}
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; }
63 protected:
64     inline wxMenu* CreatePopupMenu()
65     {   wxASSERT(m_taskBarIcon);
66         return m_taskBarIcon->CreatePopupMenu();
67     }
68     wxTaskBarIcon *m_taskBarIcon;
69     wxTaskBarIconWindow *m_iconWindow;
70 private:
71     wxTaskBarIconCocoaImpl();
72 };
73
74 // ============================================================================
75 // wxTaskBarIconDockImpl
76 //     An implementation using the Dock icon.
77 // ============================================================================
78 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
79 {
80 public:
81     wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
82     virtual ~wxTaskBarIconDockImpl();
83     virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
84     virtual bool RemoveIcon();
85     virtual bool PopupMenu(wxMenu *menu);
86
87     static WX_NSMenu CocoaGetDockNSMenu();
88 protected:
89     WX_NSMenu CocoaDoGetDockNSMenu();
90     WX_NSImage m_originalDockIcon;
91     // There can be only one Dock icon, so make sure we keep it that way
92     static wxTaskBarIconDockImpl *sm_dockIcon;
93 private:
94     wxTaskBarIconDockImpl();
95 };
96
97 // ============================================================================
98 // wxTaskBarIconCustomStatusItemImpl
99 //     An implementation using an NSStatusItem with a custom NSView
100 // ============================================================================
101 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
102 {
103 public:
104     wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
105     virtual ~wxTaskBarIconCustomStatusItemImpl();
106     virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
107     virtual bool RemoveIcon();
108     virtual bool PopupMenu(wxMenu *menu);
109 protected:
110     NSStatusItem *m_cocoaNSStatusItem;
111 private:
112     wxTaskBarIconCustomStatusItemImpl();
113 };
114
115 // ============================================================================
116 // wxTaskBarIconWindow
117 //     Used by all implementations to forward events from the wxMenu
118 // ============================================================================
119 class wxTaskBarIconWindow: public wxWindow
120 {
121     DECLARE_EVENT_TABLE()
122 public:
123     wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
124     :   wxWindow(NULL,-1)
125     ,   m_taskBarIconImpl(taskBarIconImpl)
126     {   wxASSERT(m_taskBarIconImpl); }
127
128     void OnMenuEvent(wxCommandEvent& event);
129 protected:
130     wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
131 };
132
133 // ============================================================================
134 // wxTaskBarIconWindowCustom
135 //     Used by the CustomStatusIcon implementation for the custom NSView.
136 // ============================================================================
137 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
138 {
139     DECLARE_EVENT_TABLE()
140 public:
141     wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
142     :   wxTaskBarIconWindow(taskBarIconImpl)
143     {}
144     void SetIcon(const wxIcon& icon)
145     {   m_icon = icon; }
146     void OnMouseEvent(wxMouseEvent &event);
147     void OnPaint(wxPaintEvent &event);
148 protected:
149     wxIcon m_icon;
150 };
151
152 // ============================================================================
153 // wxTaskBarIcon implementation
154 //     The facade class.
155 // ============================================================================
156 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
157
158 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
159 {
160     if(iconType == DOCK)
161         m_impl = new wxTaskBarIconDockImpl(this);
162     else if(iconType == CUSTOM_STATUSITEM)
163         m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
164     else
165     {   m_impl = NULL;
166         wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
167     }
168 }
169
170 wxTaskBarIcon::~wxTaskBarIcon()
171 {
172     delete m_impl;
173 }
174
175 // Operations
176 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
177 {
178     return m_impl->SetIcon(icon,tooltip);
179 }
180
181 bool wxTaskBarIcon::RemoveIcon()
182 {
183     return m_impl->RemoveIcon();
184 }
185
186 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
187 {
188     return m_impl->PopupMenu(menu);
189 }
190
191 // ============================================================================
192 // wxTaskBarIconCocoaImpl
193 // ============================================================================
194
195 #if 0
196 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
197 :   m_taskBarIcon(taskBarIcon)
198 ,   m_iconWindow(NULL)
199 {
200 }
201 #endif
202
203 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
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 :   wxTaskBarIconCocoaImpl(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 dockMenu->GetNSMenu();
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 :   wxTaskBarIconCocoaImpl(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     [m_iconWindow->GetNSView() setFrame:NSMakeRect(0.0,0.0,wxMax(10,icon.GetWidth()),[[NSStatusBar systemStatusBar] thickness])];
298     [m_cocoaNSStatusItem setView:m_iconWindow->GetNSView()];
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     wxCHECK_MSG(menu, false, "can't popup a NULL menu");
314
315     wxMenuInvokingWindowSetter setInvokingWin(*menu, m_iconWindow);
316     menu->UpdateUI();
317
318     if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
319     {   // OS X >= 10.3
320         [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
321     }
322     else
323     {   // pretty good fake for OS X < 10.3
324         NSEvent *nsevent = [NSEvent mouseEventWithType:NSLeftMouseDown
325             location:NSMakePoint(-1.0,-4.0) modifierFlags:0 timestamp:0
326             windowNumber:[[m_iconWindow->GetNSView() window] windowNumber]
327             context:[NSGraphicsContext currentContext]
328             eventNumber:0 clickCount:1 pressure:0.0];
329         [NSMenu popUpContextMenu:menu->GetNSMenu() withEvent:nsevent forView:m_iconWindow->GetNSView()];
330     }
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 behaviour 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