]> git.saurik.com Git - wxWidgets.git/blob - src/osx/cocoa/taskbar.mm
follow up parent chain to properly support modal dialog parents, see #15383
[wxWidgets.git] / src / osx / cocoa / taskbar.mm
1 /////////////////////////////////////////////////////////////////////////
2 // File: src/osx/cocoa/taskbar.mm
3 // Purpose: Implements wxTaskBarIcon class
4 // Author: David Elliott, Stefan Csomor
5 // Modified by:
6 // Created: 2004/01/24
7 // Copyright: (c) 2004 David Elliott, Stefan Csomor
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////
10
11 #include "wx/wxprec.h"
12 #if wxUSE_TASKBARICON
13
14 #ifndef WX_PRECOMP
15 #include "wx/toplevel.h"
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 #include "wx/osx/private.h"
25
26 class wxTaskBarIconWindow;
27
28 //-----------------------------------------------------------------------------
29 //
30 // wxTaskBarIconWindow
31 //
32 // Event handler for menus
33 // NB: Since wxWindows in Mac HAVE to have parents we need this to be
34 // a top level window...
35 //-----------------------------------------------------------------------------
36
37 class wxTaskBarIconWindow : public wxTopLevelWindow
38 {
39 public:
40 wxTaskBarIconWindow(wxTaskBarIconImpl *impl);
41
42 void OnMenuEvent(wxCommandEvent& event);
43 void OnUpdateUIEvent(wxUpdateUIEvent& event);
44
45 private:
46 wxTaskBarIconImpl *m_impl;
47 DECLARE_EVENT_TABLE()
48 };
49
50 // ============================================================================
51 // wxTaskBarIconImpl
52 // Base class for the various Cocoa implementations.
53 // ============================================================================
54 class wxTaskBarIconImpl
55 {
56 public:
57 wxTaskBarIconImpl(wxTaskBarIcon *taskBarIcon);
58
59 virtual bool IsStatusItem() const { return false; }
60
61 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString) = 0;
62 virtual bool RemoveIcon() = 0;
63
64 bool IsIconInstalled() const { return m_icon.IsOk(); }
65
66 virtual bool PopupMenu(wxMenu *menu) = 0;
67 virtual ~wxTaskBarIconImpl();
68 inline wxTaskBarIcon* GetTaskBarIcon() { return m_taskBarIcon; }
69 wxMenu * CreatePopupMenu()
70 { return m_taskBarIcon->CreatePopupMenu(); }
71
72 wxDECLARE_NO_COPY_CLASS(wxTaskBarIconImpl);
73
74 protected:
75 wxTaskBarIcon *m_taskBarIcon;
76 wxBitmap m_icon;
77 wxTaskBarIconWindow *m_eventWindow;
78 private:
79 wxTaskBarIconImpl();
80 };
81
82 // ============================================================================
83 // wxTaskBarIconDockImpl
84 // An implementation using the Dock icon.
85 // ============================================================================
86 class wxTaskBarIconDockImpl: public wxTaskBarIconImpl
87 {
88 public:
89 wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon);
90 virtual ~wxTaskBarIconDockImpl();
91 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
92 virtual bool RemoveIcon();
93 virtual bool PopupMenu(wxMenu *menu);
94
95 static WX_NSMenu OSXGetDockHMenu();
96 protected:
97 WX_NSMenu OSXDoGetDockHMenu();
98 // There can be only one Dock icon, so make sure we keep it that way
99 static wxTaskBarIconDockImpl *sm_dockIcon;
100 private:
101 wxTaskBarIconDockImpl();
102 wxMenu *m_pMenu;
103 };
104
105 class wxTaskBarIconCustomStatusItemImpl;
106
107 @interface wxOSXStatusItemTarget : NSObject
108 {
109 wxTaskBarIconCustomStatusItemImpl* impl;
110 }
111 @end
112
113 // ============================================================================
114 // wxTaskBarIconCustomStatusItemImpl
115 // An implementation using an NSStatusItem with a custom NSView
116 // ============================================================================
117 class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconImpl
118 {
119 public:
120 wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon);
121 virtual ~wxTaskBarIconCustomStatusItemImpl();
122
123 virtual bool IsStatusItem() const { return true; }
124
125 virtual bool SetIcon(const wxIcon& icon, const wxString& tooltip = wxEmptyString);
126 virtual bool RemoveIcon();
127 virtual bool PopupMenu(wxMenu *menu);
128 protected:
129 NSStatusItem *m_statusItem;
130 wxOSXStatusItemTarget *m_target;
131 private:
132 wxTaskBarIconCustomStatusItemImpl();
133 };
134
135 // ============================================================================
136 // wxTaskBarIcon implementation
137 // The facade class.
138 // ============================================================================
139 IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
140
141 wxTaskBarIcon::wxTaskBarIcon(wxTaskBarIconType iconType)
142 {
143 if(iconType == wxTBI_DOCK)
144 m_impl = new wxTaskBarIconDockImpl(this);
145 else if(iconType == wxTBI_CUSTOM_STATUSITEM)
146 m_impl = new wxTaskBarIconCustomStatusItemImpl(this);
147 else
148 { m_impl = NULL;
149 wxFAIL_MSG(wxT("Invalid wxTaskBarIcon type"));
150 }
151 }
152
153 wxTaskBarIcon::~wxTaskBarIcon()
154 {
155 if ( m_impl )
156 {
157 if ( m_impl->IsIconInstalled() )
158 m_impl->RemoveIcon();
159 delete m_impl;
160 m_impl = NULL;
161 }
162 }
163
164 bool wxTaskBarIcon::OSXIsStatusItem()
165 {
166 if ( m_impl )
167 return m_impl->IsStatusItem();
168
169 return false;
170 }
171
172 // Operations
173
174 bool wxTaskBarIcon::IsIconInstalled() const
175 {
176 if ( m_impl )
177 return m_impl->IsIconInstalled();
178
179 return false;
180 }
181
182 bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
183 {
184 if ( m_impl )
185 return m_impl->SetIcon(icon,tooltip);
186
187 return false;
188 }
189
190 bool wxTaskBarIcon::RemoveIcon()
191 {
192 if ( m_impl )
193 return m_impl->RemoveIcon();
194
195 return false;
196 }
197
198 bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
199 {
200 if ( m_impl )
201 return m_impl->PopupMenu(menu);
202
203 return false;
204 }
205
206 // ============================================================================
207 // wxTaskBarIconImpl
208 // ============================================================================
209
210 wxTaskBarIconImpl::wxTaskBarIconImpl(wxTaskBarIcon* taskBarIcon)
211 : m_taskBarIcon(taskBarIcon), m_eventWindow(new wxTaskBarIconWindow(this))
212 {
213 }
214
215 wxTaskBarIconImpl::~wxTaskBarIconImpl()
216 {
217 delete m_eventWindow;
218 }
219
220 // ============================================================================
221 // wxTaskBarIconDockImpl
222 // ============================================================================
223 wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
224
225 wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
226 : wxTaskBarIconImpl(taskBarIcon)
227 {
228 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
229 sm_dockIcon = this;
230 m_pMenu = NULL;
231 }
232
233 wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
234 {
235 if(sm_dockIcon == this)
236 sm_dockIcon = NULL;
237 }
238
239 WX_NSMenu wxTaskBarIconDockImpl::OSXGetDockHMenu()
240 {
241 if(sm_dockIcon)
242 return sm_dockIcon->OSXDoGetDockHMenu();
243
244 return nil;
245 }
246
247 WX_NSMenu wxTaskBarIconDockImpl::OSXDoGetDockHMenu()
248 {
249 wxMenu *dockMenu = CreatePopupMenu();
250
251 if(!dockMenu)
252 return nil;
253
254 wxDELETE(m_pMenu);
255
256 m_pMenu = dockMenu;
257
258 m_pMenu->SetInvokingWindow(m_eventWindow);
259
260 m_pMenu->UpdateUI();
261
262 return (WX_NSMenu)dockMenu->GetHMenu();
263 }
264
265 bool wxTaskBarIconDockImpl::SetIcon(const wxIcon& icon, const wxString& WXUNUSED(tooltip))
266 {
267 m_icon.CopyFromIcon(icon);
268 [[NSApplication sharedApplication] setApplicationIconImage:m_icon.GetNSImage()];
269 return true;
270 }
271
272 bool wxTaskBarIconDockImpl::RemoveIcon()
273 {
274 wxDELETE(m_pMenu);
275 m_icon = wxBitmap();
276 [[NSApplication sharedApplication] setApplicationIconImage:nil];
277 return true;
278 }
279
280 bool wxTaskBarIconDockImpl::PopupMenu(wxMenu *WXUNUSED(menu))
281 {
282 wxFAIL_MSG(wxT("You cannot force the Dock icon menu to popup"));
283 return false;
284 }
285
286 @interface wxNSAppController(wxTaskBarIconNSApplicationDelegateCategory)
287 - (NSMenu*)applicationDockMenu:(NSApplication *)sender;
288 @end
289
290 @implementation wxNSAppController(wxTaskBarIconNSApplicationDelegateCategory)
291 - (NSMenu*)applicationDockMenu:(NSApplication *)sender
292 {
293 wxUnusedVar(sender);
294
295 return wxTaskBarIconDockImpl::OSXGetDockHMenu();
296 }
297 @end
298
299 // ============================================================================
300 // wxTaskBarIconCustomStatusItemImpl
301 // ============================================================================
302
303 @implementation wxOSXStatusItemTarget
304
305 - (void) clickedAction: (id) sender
306 {
307 wxUnusedVar(sender);
308 wxMenu *menu = impl->CreatePopupMenu();
309 if (menu)
310 {
311 impl->PopupMenu(menu);
312 delete menu;
313 }
314 }
315
316 - (void)setImplementation: (wxTaskBarIconCustomStatusItemImpl *) theImplementation
317 {
318 impl = theImplementation;
319 }
320
321 - (wxTaskBarIconCustomStatusItemImpl*) implementation
322 {
323 return impl;
324 }
325
326 @end
327
328
329 wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
330 : wxTaskBarIconImpl(taskBarIcon)
331 {
332 m_statusItem = nil;
333 m_target = nil;
334 }
335
336 wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
337 {
338 }
339
340 bool wxTaskBarIconCustomStatusItemImpl::SetIcon(const wxIcon& icon, const wxString& tooltip)
341 {
342 if(!m_statusItem)
343 {
344 m_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
345 [m_statusItem retain];
346
347 m_target = [[wxOSXStatusItemTarget alloc] init];
348 [m_target setImplementation:this];
349 [m_statusItem setHighlightMode:YES];
350 [m_statusItem setTarget:m_target];
351 [m_statusItem setAction:@selector(clickedAction:)];
352 [m_statusItem sendActionOn:NSLeftMouseDownMask];
353 }
354
355 m_icon.CopyFromIcon(icon);
356
357 // status item doesn't scale automatically
358
359 int dimension = m_icon.GetHeight();
360 if ( m_icon.GetWidth() > dimension )
361 dimension = m_icon.GetWidth();
362 if ( dimension > 16 )
363 {
364 wxImage img = m_icon.ConvertToImage();
365 int factor = (dimension+15)/16;
366 m_icon = img.ShrinkBy(factor, factor);
367 }
368
369 [m_statusItem setImage:m_icon.GetNSImage()];
370 wxCFStringRef cfTooltip(tooltip);
371 [m_statusItem setToolTip:cfTooltip.AsNSString()];
372 return true;
373 }
374
375 bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
376 {
377 [m_statusItem release];
378 m_statusItem = nil;
379 [m_target release];
380 m_target = nil;
381
382 m_icon = wxBitmap();
383
384 return true;
385 }
386
387 bool wxTaskBarIconCustomStatusItemImpl::PopupMenu(wxMenu *menu)
388 {
389 wxASSERT(menu);
390
391 menu->SetInvokingWindow(m_eventWindow);
392 menu->UpdateUI();
393
394 [m_statusItem popUpStatusItemMenu:(NSMenu*)menu->GetHMenu()];
395
396 menu->SetInvokingWindow(NULL);
397 return true;
398 }
399
400 // ============================================================================
401 // wxTaskBarIconWindow
402 // ============================================================================
403
404 BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
405 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
406 EVT_UPDATE_UI(-1, wxTaskBarIconWindow::OnUpdateUIEvent)
407 END_EVENT_TABLE()
408
409 wxTaskBarIconWindow::wxTaskBarIconWindow(wxTaskBarIconImpl *impl)
410 : m_impl(impl)
411 {
412 }
413
414 void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent& event)
415 {
416 m_impl->GetTaskBarIcon()->ProcessEvent(event);
417 }
418
419 void wxTaskBarIconWindow::OnUpdateUIEvent(wxUpdateUIEvent& event)
420 {
421 m_impl->GetTaskBarIcon()->ProcessEvent(event);
422 }
423
424 #endif //def wxHAS_TASK_BAR_ICON