Add wxMessageDialog::GetEffectiveIcon() and use it in all ports.
[wxWidgets.git] / src / cocoa / sound.mm
1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        sound.cpp
3 // Purpose:     wxSound class implementation: optional
4 // Authors:     David Elliott, Ryan Norton
5 // Modified by: 
6 // Created:     2004-10-02
7 // RCS-ID:      $Id$
8 // Copyright:   (c) 2004 David Elliott, Ryan Norton
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #include "wx/wxprec.h"
13 #if wxUSE_SOUND
14
15 #ifndef WX_PRECOMP
16     #include "wx/app.h"
17     #include "wx/log.h"
18 #endif //ndef WX_PRECOMP
19 #include "wx/sound.h"
20 #include "wx/evtloop.h"
21
22 #include "wx/cocoa/autorelease.h"
23 #include "wx/cocoa/string.h"
24 #include "wx/cocoa/log.h"
25
26 #include "wx/cocoa/objc/objc_uniquifying.h"
27
28 #import <AppKit/NSSound.h>
29 #import <Foundation/NSData.h>
30
31 static WX_NSSound s_currentSound = nil;
32 static bool s_loopCurrentSound = false;
33
34 // ========================================================================
35 // wxNSSoundDelegate
36 // ========================================================================
37 @interface wxNSSoundDelegate : NSObject
38 {
39 }
40
41 // Delegate methods
42 - (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying;
43 @end // interface wxNSSoundDelegate : NSObject
44 WX_DECLARE_GET_OBJC_CLASS(wxNSSoundDelegate,NSObject)
45
46 @implementation wxNSSoundDelegate : NSObject
47
48 - (void)sound:(NSSound *)theSound didFinishPlaying:(BOOL)finishedPlaying
49 {
50     // If s_currentSound is not us then some other sound has played.
51     // We can safely ignore this as s_currentSound will have been released
52     // before being set to a different value.
53     if(s_currentSound!=theSound)
54         return;
55     // If playing finished successfully and we are looping, play again.
56     if (finishedPlaying && s_loopCurrentSound)
57         [s_currentSound play];
58     // Otherwise we are done, there is no more current sound playing.
59     else
60     {
61         if(s_currentSound) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("[wxNSSoundDelegate -sound:didFinishPlaying:] [s_currentSound=%p retainCount]=%d (about to release)"),s_currentSound,[s_currentSound retainCount]);
62         [s_currentSound release];
63         s_currentSound = nil;
64         // Make sure we get out of any modal event loops immediately.
65         // NOTE: When the sound finishes playing Cocoa normally does have
66         // an event so this is probably not necessary.
67         wxTheApp->WakeUpIdle();
68     }
69 }
70
71 @end // wxNSSoundDelegate
72 WX_IMPLEMENT_GET_OBJC_CLASS(wxNSSoundDelegate,NSObject)
73
74 const wxObjcAutoRefFromAlloc<struct objc_object*> wxSound::sm_cocoaDelegate = [[WX_GET_OBJC_CLASS(wxNSSoundDelegate) alloc] init];
75
76 // ------------------------------------------------------------------
77 //          wxSound
78 // ------------------------------------------------------------------
79
80 wxSound::wxSound(const wxSound& sound)
81 :   m_cocoaNSSound(sound.m_cocoaNSSound)
82 {
83     [m_cocoaNSSound retain];
84 }
85
86 wxSound::~wxSound()
87 {
88     SetNSSound(nil);
89 }
90
91 bool wxSound::Create(const wxString& fileName, bool isResource)
92 {
93     wxAutoNSAutoreleasePool thePool;
94
95     if (isResource)
96         SetNSSound([NSSound soundNamed:wxNSStringWithWxString(fileName)]);
97     else
98     {
99         SetNSSound([[NSSound alloc] initWithContentsOfFile:wxNSStringWithWxString(fileName) byReference:YES]);
100         [m_cocoaNSSound release];
101     }
102
103     return m_cocoaNSSound;
104 }
105
106 bool wxSound::LoadWAV(const wxUint8 *data, size_t length, bool copyData)
107 {
108     NSData* theData;
109     if(copyData)
110         theData = [[NSData alloc] initWithBytes:const_cast<wxUint8*>(data) length:length];
111     else
112         theData = [[NSData alloc] initWithBytesNoCopy:const_cast<wxUint8*>(data) length:length];
113     SetNSSound([[NSSound alloc] initWithData:theData]);
114     [m_cocoaNSSound release];
115     [theData release];
116     return m_cocoaNSSound;
117 }
118
119 void wxSound::SetNSSound(WX_NSSound cocoaNSSound)
120 {
121     bool need_debug = cocoaNSSound || m_cocoaNSSound;
122     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::SetNSSound [m_cocoaNSSound=%p retainCount]=%d (about to release)"),this,m_cocoaNSSound,[m_cocoaNSSound retainCount]);
123     [cocoaNSSound retain];
124     [m_cocoaNSSound release];
125     m_cocoaNSSound = cocoaNSSound;
126     [m_cocoaNSSound setDelegate:sm_cocoaDelegate];
127     if(need_debug) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::SetNSSound [cocoaNSSound=%p retainCount]=%d (just retained)"),this,cocoaNSSound,[cocoaNSSound retainCount]);
128 }
129
130 bool wxSound::DoPlay(unsigned flags) const
131 {
132     Stop(); // this releases and nils s_currentSound
133
134     // NOTE: We set s_currentSound to the current sound in all cases so that
135     // functions like Stop and IsPlaying can work.  It is NOT necessary for
136     // the NSSound to be retained by us for it to continue playing.  Cocoa
137     // retains the NSSound when it is played and relases it when finished.
138
139     wxASSERT(!s_currentSound);
140     s_currentSound = [m_cocoaNSSound retain];
141     wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound=%p::DoPlay [s_currentSound=%p retainCount]=%d (just retained)"),this,s_currentSound,[s_currentSound retainCount]);
142     s_loopCurrentSound = (flags & wxSOUND_LOOP) == wxSOUND_LOOP;
143
144     if (flags & wxSOUND_ASYNC)
145         return [m_cocoaNSSound play];
146     else
147     {
148         wxASSERT_MSG(!s_loopCurrentSound,wxT("It is silly to block waiting for a looping sound to finish.  Disabling looping"));
149         // actually, it'd probably work although it's kind of stupid to
150         // block here waiting for a sound that's never going to end.
151         // Granted Stop() could be called somehow, but again, silly.
152         s_loopCurrentSound = false;
153
154         if(![m_cocoaNSSound play])
155             return false;
156
157         // Process events until the delegate sets s_currentSound to nil
158         // and/or a different sound plays.
159         while (s_currentSound==m_cocoaNSSound)
160             wxEventLoop::GetActive()->Dispatch();
161         return true;
162     }
163 }
164
165 bool wxSound::IsPlaying()
166 {
167     // Normally you can send a message to a nil object and it will return
168     // nil.  That behavior would probably be okay here but in general it's
169     // not recommended to send a message to a nil object if the return
170     // value is not an object.  Better safe than sorry.
171     if(s_currentSound)
172         return [s_currentSound isPlaying];
173     else
174         return false;
175 }
176
177 void wxSound::Stop()
178 {
179     // Clear the looping flag so that if the sound finishes playing before
180     // stop is called the sound will already be released and niled.
181     s_loopCurrentSound = false;
182     [s_currentSound stop];
183     /* It's possible that sound:didFinishPlaying: was called and released
184        s_currentSound but it doesn't matter since it will have set it to nil */
185     if(s_currentSound) wxLogTrace(wxTRACE_COCOA_RetainRelease,wxT("wxSound::Stop [s_currentSound=%p retainCount]=%d (about to release)"),s_currentSound,[s_currentSound retainCount]);
186     [s_currentSound release];
187     s_currentSound = nil;
188 }
189
190 #endif //wxUSE_SOUND