]> git.saurik.com Git - wxWidgets.git/blob - src/cocoa/taskbar.mm
Began work on print dialogs. Now wxPrintDialog
[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 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 wxTaskBarIcon *m_taskBarIcon;
65 wxTaskBarIconWindow *m_iconWindow;
66 private:
67 wxTaskBarIconCocoaImpl();
68 };
69
70 // ============================================================================
71 // wxTaskBarIconDockImpl
72 // An implementation using the Dock icon.
73 // ============================================================================
74 class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
75 {
76 public:
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);
82
83 static WX_NSMenu CocoaGetDockNSMenu();
84 protected:
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;
89 private:
90 wxTaskBarIconDockImpl();
91 };
92
93 // ============================================================================
94 // wxTaskBarIconCustomStatusItemImpl
95 // An implementation using an NSStatusItem with a custom NSView
96 // ============================================================================
97 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
98 {
99 public:
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);
105 protected:
106 NSStatusItem *m_cocoaNSStatusItem;
107 private:
108 wxTaskBarIconCustomStatusItemImpl();
109 };
110
111 // ============================================================================
112 // wxTaskBarIconWindow
113 // Used by all implementations to forward events from the wxMenu
114 // ============================================================================
115 class wxTaskBarIconWindow: public wxWindow
116 {
117 DECLARE_EVENT_TABLE()
118 public:
119 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
120 : wxWindow(NULL,-1)
121 , m_taskBarIconImpl(taskBarIconImpl)
122 { wxASSERT(m_taskBarIconImpl); }
123
124 void OnMenuEvent(wxCommandEvent& event);
125 protected:
126 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
127 };
128
129 // ============================================================================
130 // wxTaskBarIconWindowCustom
131 // Used by the CustomStatusIcon implementation for the custom NSView.
132 // ============================================================================
133 class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
134 {
135 DECLARE_EVENT_TABLE()
136 public:
137 wxTaskBarIconWindowCustom(wxTaskBarIconCocoaImpl *taskBarIconImpl)
138 : wxTaskBarIconWindow(taskBarIconImpl)
139 {}
140 void SetIcon(const wxIcon& icon)
141 { m_icon = icon; }
142 void OnMouseEvent(wxMouseEvent &event);
143 void OnPaint(wxPaintEvent &event);
144 protected:
145 wxIcon m_icon;
146 };
147
148 // ============================================================================
149 // wxTaskBarIcon implementation
150 // The facade class.
151 // ============================================================================
152 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
153
154 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
155 {
156 if(iconType == DOCK)
157 m_impl = new wxTaskBarIconDockImpl(this);
158 else if(iconType == CUSTOM_STATUSITEM)
159 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
160 else
161 { m_impl = NULL;
162 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
163 }
164 }
165
166 wxTaskBarIcon::~wxTaskBarIcon()
167 {
168 delete m_impl;
169 }
170
171 // Operations
172 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
173 {
174 return m_impl->SetIcon(icon,tooltip);
175 }
176
177 bool wxTaskBarIcon::RemoveIcon()
178 {
179 return m_impl->RemoveIcon();
180 }
181
182 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
183 {
184 return m_impl->PopupMenu(menu);
185 }
186
187 // ============================================================================
188 // wxTaskBarIconCocoaImpl
189 // ============================================================================
190
191 #if 0
192 wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
193 : m_taskBarIcon(taskBarIcon)
194 , m_iconWindow(NULL)
195 {
196 }
197 #endif
198
199 wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
200 {
201 // wxAutoNSAutoreleasePool pool;
202 delete m_iconWindow;
203 }
204
205 // ============================================================================
206 // wxTaskBarIconDockImpl
207 // ============================================================================
208 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
209
210 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
211 : wxTaskBarIconCocoaImpl(taskBarIcon)
212 {
213 m_originalDockIcon = nil;
214 wxASSERT_MSG(!sm_dockIcon,"You should never have more than one dock icon!");
215 sm_dockIcon = this;
216 }
217
218 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
219 {
220 // wxAutoNSAutoreleasePool pool;
221 if(sm_dockIcon == this)
222 sm_dockIcon = NULL;
223 }
224
225 WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
226 {
227 if(sm_dockIcon)
228 return sm_dockIcon->CocoaDoGetDockNSMenu();
229 return nil;
230 }
231
232 WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
233 {
234 wxMenu *dockMenu = m_taskBarIcon->CreatePopupMenu();
235 if(!dockMenu)
236 return nil;
237 if(!m_iconWindow)
238 m_iconWindow = new wxTaskBarIconWindow(this);
239 dockMenu->SetInvokingWindow(m_iconWindow);
240 dockMenu->UpdateUI();
241 dockMenu->SetCocoaDeletes(true);
242 return dockMenu->GetNSMenu();
243 }
244
245 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
246 {
247 wxAutoNSAutoreleasePool pool;
248 m_originalDockIcon = [[[NSApplication sharedApplication] applicationIconImage] retain];
249 [[NSApplication sharedApplication] setApplicationIconImage:icon.GetNSImage()];
250 return true;
251 }
252
253 bool wxTaskBarIconDockImpl::RemoveIcon()
254 {
255 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
256 [m_originalDockIcon release];
257 return true;
258 }
259
260 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *menu)
261 {
262 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
263 return false;
264 }
265
266
267 // ============================================================================
268 // wxTaskBarIconCustomStatusItemImpl
269 // ============================================================================
270 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
271 : wxTaskBarIconCocoaImpl(taskBarIcon)
272 {
273 m_cocoaNSStatusItem = nil;
274 }
275
276 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
277 {
278 }
279
280 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
281 {
282 wxAutoNSAutoreleasePool pool;
283 if(!m_cocoaNSStatusItem)
284 {
285 m_cocoaNSStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
286 [m_cocoaNSStatusItem retain];
287 }
288 if(!m_iconWindow)
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()];
295 return true;
296 }
297
298 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
299 {
300 [m_cocoaNSStatusItem release];
301 m_cocoaNSStatusItem = nil;
302 delete m_iconWindow;
303 m_iconWindow = NULL;
304 return true;
305 }
306
307 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
308 {
309 wxASSERT(menu);
310 menu->SetInvokingWindow(m_iconWindow);
311 menu->UpdateUI();
312
313 if([m_cocoaNSStatusItem respondsToSelector:@selector(popUpStatusItemMenu:)])
314 { // OS X >= 10.3
315 [m_cocoaNSStatusItem popUpStatusItemMenu:menu->GetNSMenu()];
316 }
317 else
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()];
325 }
326 menu->SetInvokingWindow(NULL);
327 return true;
328 }
329
330 // ============================================================================
331 // wxTaskBarIconWindow
332 // ============================================================================
333 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
334 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
335 END_EVENT_TABLE()
336
337 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
338 {
339 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
340 }
341
342 // ============================================================================
343 // wxTaskBarIconWindowCustom
344 // ============================================================================
345 BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
346 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
347 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
348 END_EVENT_TABLE()
349
350 void wxTaskBarIconWindowCustom::OnMouseEvent(wxMouseEvent &event)
351 {
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;
367 else
368 return;
369 wxTaskBarIconEvent tbiEvent(tbEventType,m_taskBarIconImpl->GetTaskBarIcon());
370 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(tbiEvent);
371 }
372
373 void wxTaskBarIconWindowCustom::OnPaint(wxPaintEvent &event)
374 {
375 wxPaintDC dc(this);
376 // FIXME: This is a temporary hack until we can see real icons
377 dc.SetBackground(wxBrush(*wxBLUE));
378 dc.Clear();
379 dc.DrawIcon(m_icon,0,0);
380 }
381
382 // ============================================================================
383 // wxTaskBarIconNSApplicationDelegateCategory
384 // ============================================================================
385
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. :-)
390
391 @interface wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
392 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
393 @end
394
395 @implementation wxNSApplicationDelegate(wxTaskBarIconNSApplicationDelegateCategory)
396 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
397 {
398 return wxTaskBarIconDockImpl::CocoaGetDockNSMenu();
399 }
400 @end
401
402 #endif //def wxHAS_TASK_BAR_ICON