]> git.saurik.com Git - wxWidgets.git/blame - src/cocoa/taskbar.mm
Applied patch [ 1163322 ] Patch to prevent MDI app from opening the same file twice
[wxWidgets.git] / src / cocoa / taskbar.mm
CommitLineData
f1d9e1ec
DE
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
04fa0af1
RN
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
f1d9e1ec
DE
50class wxTaskBarIconWindow;
51
52// ============================================================================
53// wxTaskBarIconCocoaImpl
54// Base class for the various Cocoa implementations.
55// ============================================================================
56class wxTaskBarIconCocoaImpl
57{
58public:
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; }
68protected:
4092a275
DE
69 inline wxMenu* CreatePopupMenu()
70 { wxASSERT(m_taskBarIcon);
9d1e4703 71 return m_taskBarIcon->CreatePopupMenu();
4092a275 72 }
f1d9e1ec
DE
73 wxTaskBarIcon *m_taskBarIcon;
74 wxTaskBarIconWindow *m_iconWindow;
75private:
76 wxTaskBarIconCocoaImpl();
77};
78
79// ============================================================================
80// wxTaskBarIconDockImpl
81// An implementation using the Dock icon.
82// ============================================================================
83class wxTaskBarIconDockImpl: public wxTaskBarIconCocoaImpl
84{
85public:
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();
93protected:
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;
98private:
99 wxTaskBarIconDockImpl();
100};
101
102// ============================================================================
103// wxTaskBarIconCustomStatusItemImpl
104// An implementation using an NSStatusItem with a custom NSView
105// ============================================================================
106class wxTaskBarIconCustomStatusItemImpl: public wxTaskBarIconCocoaImpl
107{
108public:
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);
114protected:
115 NSStatusItem *m_cocoaNSStatusItem;
116private:
117 wxTaskBarIconCustomStatusItemImpl();
118};
119
120// ============================================================================
121// wxTaskBarIconWindow
122// Used by all implementations to forward events from the wxMenu
123// ============================================================================
124class wxTaskBarIconWindow: public wxWindow
125{
126 DECLARE_EVENT_TABLE()
127public:
128 wxTaskBarIconWindow(wxTaskBarIconCocoaImpl *taskBarIconImpl)
129 : wxWindow(NULL,-1)
130 , m_taskBarIconImpl(taskBarIconImpl)
131 { wxASSERT(m_taskBarIconImpl); }
132
133 void OnMenuEvent(wxCommandEvent& event);
134protected:
135 wxTaskBarIconCocoaImpl *m_taskBarIconImpl;
136};
137
138// ============================================================================
139// wxTaskBarIconWindowCustom
140// Used by the CustomStatusIcon implementation for the custom NSView.
141// ============================================================================
142class wxTaskBarIconWindowCustom: public wxTaskBarIconWindow
143{
144 DECLARE_EVENT_TABLE()
145public:
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);
153protected:
154 wxIcon m_icon;
155};
156
157// ============================================================================
158// wxTaskBarIcon implementation
159// The facade class.
160// ============================================================================
161IMPLEMENT_DYNAMIC_CLASS(wxTaskBarIcon, wxEvtHandler)
162
163wxTaskBarIcon::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
175wxTaskBarIcon::~wxTaskBarIcon()
176{
177 delete m_impl;
178}
179
180// Operations
181bool wxTaskBarIcon::SetIcon(const wxIcon& icon, const wxString& tooltip)
182{
183 return m_impl->SetIcon(icon,tooltip);
184}
185
186bool wxTaskBarIcon::RemoveIcon()
187{
188 return m_impl->RemoveIcon();
189}
190
191bool wxTaskBarIcon::PopupMenu(wxMenu *menu)
192{
193 return m_impl->PopupMenu(menu);
194}
195
196// ============================================================================
197// wxTaskBarIconCocoaImpl
198// ============================================================================
199
200#if 0
201wxTaskBarIconCocoaImpl::wxTaskBarIconCocoaImpl(wxTaskBarIcon *taskBarIcon)
202: m_taskBarIcon(taskBarIcon)
203, m_iconWindow(NULL)
204{
205}
206#endif
207
208wxTaskBarIconCocoaImpl::~wxTaskBarIconCocoaImpl()
209{
210// wxAutoNSAutoreleasePool pool;
211 delete m_iconWindow;
212}
213
214// ============================================================================
215// wxTaskBarIconDockImpl
216// ============================================================================
217wxTaskBarIconDockImpl *wxTaskBarIconDockImpl::sm_dockIcon = NULL;
218
219wxTaskBarIconDockImpl::wxTaskBarIconDockImpl(wxTaskBarIcon *taskBarIcon)
220: wxTaskBarIconCocoaImpl(taskBarIcon)
221{
222 m_originalDockIcon = nil;
04fa0af1 223 wxASSERT_MSG(!sm_dockIcon, wxT("You should never have more than one dock icon!"));
f1d9e1ec
DE
224 sm_dockIcon = this;
225}
226
227wxTaskBarIconDockImpl::~wxTaskBarIconDockImpl()
228{
229// wxAutoNSAutoreleasePool pool;
230 if(sm_dockIcon == this)
231 sm_dockIcon = NULL;
232}
233
234WX_NSMenu wxTaskBarIconDockImpl::CocoaGetDockNSMenu()
235{
236 if(sm_dockIcon)
237 return sm_dockIcon->CocoaDoGetDockNSMenu();
238 return nil;
239}
240
241WX_NSMenu wxTaskBarIconDockImpl::CocoaDoGetDockNSMenu()
242{
4092a275 243 wxMenu *dockMenu = CreatePopupMenu();
f1d9e1ec
DE
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
254bool 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
262bool wxTaskBarIconDockImpl::RemoveIcon()
263{
264 [[NSApplication sharedApplication] setApplicationIconImage:m_originalDockIcon];
265 [m_originalDockIcon release];
266 return true;
267}
268
269bool 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// ============================================================================
279wxTaskBarIconCustomStatusItemImpl::wxTaskBarIconCustomStatusItemImpl(wxTaskBarIcon *taskBarIcon)
280: wxTaskBarIconCocoaImpl(taskBarIcon)
281{
282 m_cocoaNSStatusItem = nil;
283}
284
285wxTaskBarIconCustomStatusItemImpl::~wxTaskBarIconCustomStatusItemImpl()
286{
287}
288
289bool 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
307bool wxTaskBarIconCustomStatusItemImpl::RemoveIcon()
308{
309 [m_cocoaNSStatusItem release];
310 m_cocoaNSStatusItem = nil;
311 delete m_iconWindow;
312 m_iconWindow = NULL;
313 return true;
314}
315
316bool 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// ============================================================================
342BEGIN_EVENT_TABLE(wxTaskBarIconWindow, wxWindow)
343 EVT_MENU(-1, wxTaskBarIconWindow::OnMenuEvent)
344END_EVENT_TABLE()
345
346void wxTaskBarIconWindow::OnMenuEvent(wxCommandEvent &event)
347{
348 m_taskBarIconImpl->GetTaskBarIcon()->ProcessEvent(event);
349}
350
351// ============================================================================
352// wxTaskBarIconWindowCustom
353// ============================================================================
354BEGIN_EVENT_TABLE(wxTaskBarIconWindowCustom, wxTaskBarIconWindow)
355 EVT_MOUSE_EVENTS(wxTaskBarIconWindowCustom::OnMouseEvent)
356 EVT_PAINT(wxTaskBarIconWindowCustom::OnPaint)
357END_EVENT_TABLE()
358
359void 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
382void 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