]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/mediactrl.cpp
correcting Drag Data handling for 'TEXT' and 'utxt'
[wxWidgets.git] / src / mac / carbon / mediactrl.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: mac/carbon/mediactrl.cpp
3// Purpose: Built-in Media Backends for Mac
4// Author: Ryan Norton <wxprojects@comcast.net>
5// Modified by:
6// Created: 11/07/04
7// RCS-ID: $Id$
8// Copyright: (c) 2004-2005 Ryan Norton, portions (c) 2004 Kevin Olliver
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
12//===========================================================================
13// DECLARATIONS
14//===========================================================================
15
16//---------------------------------------------------------------------------
17// Pre-compiled header stuff
18//---------------------------------------------------------------------------
19
20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21#pragma implementation "mediactrl.h"
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
28#pragma hdrstop
29#endif
30
31//---------------------------------------------------------------------------
32// Includes
33//---------------------------------------------------------------------------
34#include "wx/mediactrl.h"
35
36//---------------------------------------------------------------------------
37// Compilation guard
38//---------------------------------------------------------------------------
39#if wxUSE_MEDIACTRL
40
41//---------------------------------------------------------------------------
42// Whether or not to use OSX 10.2's CreateMovieControl for native QuickTime
43// control - i.e. native positioning and event handling etc..
44//---------------------------------------------------------------------------
45#ifndef wxUSE_CREATEMOVIECONTROL
46# if defined( __WXMAC_OSX__ ) && ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
47# define wxUSE_CREATEMOVIECONTROL 1
48# else
49# define wxUSE_CREATEMOVIECONTROL 0
50# endif
51#endif
52
53//===========================================================================
54// BACKEND DECLARATIONS
55//===========================================================================
56
57//---------------------------------------------------------------------------
58//
59// wxQTMediaBackend
60//
61//---------------------------------------------------------------------------
62
63//---------------------------------------------------------------------------
64// QT Includes
65//---------------------------------------------------------------------------
66//uma is for wxMacFSSpec
67#include "wx/mac/uma.h"
68#include "wx/timer.h"
69#ifndef __DARWIN__
70#include <Movies.h>
71#include <Gestalt.h>
72#include <QuickTimeComponents.h> //Standard QT stuff
73#else
74#include <QuickTime/QuickTimeComponents.h>
75#endif
76
77//Determines whether version 4 of QT is installed (Pretty much for classic only)
78Boolean _wxIsQuickTime4Installed (void)
79{
80 short error;
81 long result;
82
83 error = Gestalt (gestaltQuickTime, &result);
84 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
85}
86
87class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackend
88{
89public:
90
91 wxQTMediaBackend();
92 ~wxQTMediaBackend();
93
94 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
95 wxWindowID id,
96 const wxPoint& pos,
97 const wxSize& size,
98 long style,
99 const wxValidator& validator,
100 const wxString& name);
101
102 virtual bool Play();
103 virtual bool Pause();
104 virtual bool Stop();
105
106 virtual bool Load(const wxString& fileName);
107 virtual bool Load(const wxURI& location);
108
109 virtual wxMediaState GetState();
110
111 virtual bool SetPosition(wxLongLong where);
112 virtual wxLongLong GetPosition();
113 virtual wxLongLong GetDuration();
114
115 virtual void Move(int x, int y, int w, int h);
116 wxSize GetVideoSize() const;
117
118 virtual double GetPlaybackRate();
119 virtual bool SetPlaybackRate(double dRate);
120
121 virtual double GetVolume();
122 virtual bool SetVolume(double);
123
124 void Cleanup();
125 void FinishLoad();
126
127 wxSize m_bestSize; //Original movie size
128#ifdef __WXMAC_OSX__
129 struct MovieType** m_movie; //QT Movie handle/instance
130#else
131 Movie m_movie ;
132#endif
133 wxControl* m_ctrl; //Parent control
134 bool m_bVideo; //Whether or not we have video
135 class _wxQTTimer* m_timer; //Timer for streaming the movie
136
137 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
138};
139
140
141//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
142//
143// wxQTMediaBackend
144//
145//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
146
147IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
148
149//Time between timer calls
150#define MOVIE_DELAY 100
151
152// --------------------------------------------------------------------------
153// wxQTTimer - Handle Asyncronous Playing
154// --------------------------------------------------------------------------
155class _wxQTTimer : public wxTimer
156{
157public:
158 _wxQTTimer(Movie movie, wxQTMediaBackend* parent) :
159 m_movie(movie), m_bPaused(false), m_parent(parent)
160 {
161 }
162
163 ~_wxQTTimer()
164 {
165 }
166
167 bool GetPaused() {return m_bPaused;}
168 void SetPaused(bool bPaused) {m_bPaused = bPaused;}
169
170 //-----------------------------------------------------------------------
171 // _wxQTTimer::Notify
172 //
173 // 1) Checks to see if the movie is done, and if not continues
174 // streaming the movie
175 // 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
176 // the movie.
177 //-----------------------------------------------------------------------
178 void Notify()
179 {
180 if (!m_bPaused)
181 {
182 if(!IsMovieDone(m_movie))
183 MoviesTask(m_movie, MOVIE_DELAY);
184 else
185 {
186 wxMediaEvent theEvent(wxEVT_MEDIA_STOP,
187 m_parent->m_ctrl->GetId());
188 m_parent->m_ctrl->ProcessEvent(theEvent);
189
190 if(theEvent.IsAllowed())
191 {
192 Stop();
193 m_parent->Stop();
194 wxASSERT(::GetMoviesError() == noErr);
195
196 //send the event to our child
197 wxMediaEvent theEvent(wxEVT_MEDIA_FINISHED,
198 m_parent->m_ctrl->GetId());
199 m_parent->m_ctrl->ProcessEvent(theEvent);
200 }
201 }
202 }
203 }
204
205protected:
206 Movie m_movie; //Our movie instance
207 bool m_bPaused; //Whether we are paused or not
208 wxQTMediaBackend* m_parent; //Backend pointer
209};
210
211//---------------------------------------------------------------------------
212// wxQTMediaBackend Constructor
213//
214// Sets m_timer to NULL signifying we havn't loaded anything yet
215//---------------------------------------------------------------------------
216wxQTMediaBackend::wxQTMediaBackend() : m_timer(NULL)
217{
218}
219
220//---------------------------------------------------------------------------
221// wxQTMediaBackend Destructor
222//
223// 1) Cleans up the QuickTime movie instance
224// 2) Decrements the QuickTime reference counter - if this reaches
225// 0, QuickTime shuts down
226// 3) Decrements the QuickTime Windows Media Layer reference counter -
227// if this reaches 0, QuickTime shuts down the Windows Media Layer
228//---------------------------------------------------------------------------
229wxQTMediaBackend::~wxQTMediaBackend()
230{
231 if(m_timer)
232 Cleanup();
233
234 //Note that ExitMovies() is not necessary...
235 ExitMovies();
236}
237
238//---------------------------------------------------------------------------
239// wxQTMediaBackend::CreateControl
240//
241// 1) Intializes QuickTime
242// 2) Creates the control window
243//---------------------------------------------------------------------------
244bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
245 wxWindowID id,
246 const wxPoint& pos,
247 const wxSize& size,
248 long style,
249 const wxValidator& validator,
250 const wxString& name)
251{
252 //Don't bother in Native control mode
253#if defined( __WXMAC__ ) && TARGET_API_MAC_OSX && ( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_2 )
254 if (!_wxIsQuickTime4Installed())
255 return false;
256#endif
257
258 EnterMovies();
259
260 //
261 // Create window
262 // By default wxWindow(s) is created with a border -
263 // so we need to get rid of those
264 //
265 // Since we don't have a child window like most other
266 // backends, we don't need wxCLIP_CHILDREN
267 //
268 if ( !
269
270#if wxUSE_CREATEMOVIECONTROL
271 ctrl->wxWindow::Create(parent, id, pos, size,
272 wxWindow::MacRemoveBordersFromStyle(style),
273 name)
274#else
275 ctrl->wxControl::Create(parent, id, pos, size,
276 wxWindow::MacRemoveBordersFromStyle(style),
277 validator, name)
278#endif
279 )
280 return false;
281
282#if wxUSE_VALIDATORS
283 ctrl->SetValidator(validator);
284#endif
285
286 m_ctrl = ctrl;
287 return true;
288}
289
290//---------------------------------------------------------------------------
291// wxQTMediaBackend::Load (file version)
292//
293// 1) Get an FSSpec from the Windows path name
294// 2) Open the movie
295// 3) Obtain the movie instance from the movie resource
296// 4) Close the movie resource
297// 5) Finish loading
298//---------------------------------------------------------------------------
299bool wxQTMediaBackend::Load(const wxString& fileName)
300{
301 if(m_timer)
302 Cleanup();
303
304 OSErr err = noErr;
305 short movieResFile;
306 FSSpec sfFile;
307
308 //FIXME:wxMacFilename2FSSpec crashes on empty string -
309 //does it crash on other strings too and should this
310 //"fix" be put in the carbon wxSound?
311 if (fileName.empty())
312 return false;
313
314 wxMacFilename2FSSpec( fileName , &sfFile );
315
316 if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
317 return false;
318
319 short movieResID = 0;
320 Str255 movieName;
321
322 err = NewMovieFromFile (
323 &m_movie,
324 movieResFile,
325 &movieResID,
326 movieName,
327 newMovieActive,
328 NULL); //wasChanged
329
330 CloseMovieFile (movieResFile);
331
332 if (err != noErr)
333 return false;
334
335 FinishLoad();
336
337 return ::GetMoviesError() == noErr;
338}
339
340//---------------------------------------------------------------------------
341// wxQTMediaBackend::Load (URL Version)
342//
343// 1) Build an escaped URI from location
344// 2) Create a handle to store the URI string
345// 3) Put the URI string inside the handle
346// 4) Make a QuickTime URL data ref from the handle with the URI in it
347// 5) Clean up the URI string handle
348// 6) Do some prerolling
349// 7) Finish Loading
350//---------------------------------------------------------------------------
351bool wxQTMediaBackend::Load(const wxURI& location)
352{
353 if(m_timer)
354 Cleanup();
355
356 wxString theURI = location.BuildURI();
357
358 OSErr err = noErr;
359
360 Handle theHandle = NewHandleClear(theURI.length() + 1);
361 wxASSERT(theHandle);
362
363 BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
364
365 //create the movie from the handle that refers to the URI
366 err = NewMovieFromDataRef(&m_movie, newMovieActive,
367 NULL, theHandle,
368 URLDataHandlerSubType);
369
370 DisposeHandle(theHandle);
371
372 if (err != noErr)
373 return false;
374
375 //preroll movie for streaming
376 //TODO:Async this using threads?
377 TimeValue timeNow;
378 Fixed playRate;
379 timeNow = GetMovieTime(m_movie, NULL);
380 playRate = GetMoviePreferredRate(m_movie);
381 PrePrerollMovie(m_movie, timeNow, playRate, NULL, NULL);
382 PrerollMovie(m_movie, timeNow, playRate);
383 SetMovieRate(m_movie, playRate);
384
385 FinishLoad();
386
387 return ::GetMoviesError() == noErr;
388}
389
390//---------------------------------------------------------------------------
391// wxQTMediaBackend::FinishLoad
392//
393// 1) Create the movie timer
394// 2) Get real size of movie for GetBestSize/sizers
395// 3) See if there is video in the movie, and if so then either
396// SetMovieGWorld if < 10.2 or use Native CreateMovieControl
397// 4) Set the movie time scale to something usable so that seeking
398// etc. will work correctly
399// 5) Refresh parent window
400//---------------------------------------------------------------------------
401void wxQTMediaBackend::FinishLoad()
402{
403 m_timer = new _wxQTTimer(m_movie, (wxQTMediaBackend*) this);
404 wxASSERT(m_timer);
405
406 //get the real size of the movie
407 Rect outRect;
408 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
409 wxASSERT(::GetMoviesError() == noErr);
410
411 m_bestSize.x = outRect.right - outRect.left;
412 m_bestSize.y = outRect.bottom - outRect.top;
413
414 //reparent movie/*AudioMediaCharacteristic*/
415 if(GetMovieIndTrackType(m_movie, 1,
416 VisualMediaCharacteristic,
417 movieTrackCharacteristic |
418 movieTrackEnabledOnly) != NULL)
419 {
420#if wxUSE_CREATEMOVIECONTROL
421 //
422 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
423 //wxQTMovie for some of this).
424 //
425 #define GetControlPeer(whatever) ctrl->m_peer
426 wxMediaCtrl* ctrl = (wxMediaCtrl*) m_ctrl;
427 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
428 m_ctrl->GetPosition(),
429 m_ctrl->GetSize());
430
431 //Dispose of old control for new one
432 if (GetControlPeer(m_ctrl) && GetControlPeer(m_ctrl)->Ok() )
433 GetControlPeer(m_ctrl)->Dispose();
434
435 //Options-
436 //kMovieControlOptionXXX
437 //HideController - hide the movie controller
438 //LocateTopLeft - movie is pinned to top left rather than centered in the control
439 //EnableEditing - Allows programmatic editing and dragn'drop
440 //HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
441 //SetKeysEnabled - Allows keyboard input
442 //ManuallyIdled - app handles movie idling rather than internal timer event loop
443 ::CreateMovieControl(
444 (WindowRef)
445 ctrl->MacGetTopLevelWindowRef(), //parent
446 &bounds, //control bounds
447 m_movie, //movie handle
448 kMovieControlOptionHideController
449 | kMovieControlOptionLocateTopLeft
450 | kMovieControlOptionSetKeysEnabled
451// | kMovieControlOptionManuallyIdled
452 , //flags
453 ctrl->m_peer->GetControlRefAddr() );
454
455 ::EmbedControl(ctrl->m_peer->GetControlRef(), (ControlRef)ctrl->GetParent()->GetHandle());
456#else
457 //
458 //"Emulation"
459 //
460 SetMovieGWorld(m_movie,
461 (CGrafPtr)
462 GetWindowPort(
463 (WindowRef)
464 m_ctrl->MacGetTopLevelWindowRef()
465 ),
466 nil);
467#endif
468 }
469
470 //we want millisecond precision
471 ::SetMovieTimeScale(m_movie, 1000);
472 wxASSERT(::GetMoviesError() == noErr);
473
474 //
475 //Here, if the parent of the control has a sizer - we
476 //tell it to recalculate the size of this control since
477 //the user opened a separate media file
478 //
479 m_ctrl->InvalidateBestSize();
480 m_ctrl->GetParent()->Layout();
481 m_ctrl->GetParent()->Refresh();
482 m_ctrl->GetParent()->Update();
483
484 //send loaded event
485 wxMediaEvent theEvent(wxEVT_MEDIA_LOADED,
486 m_ctrl->GetId());
487 m_ctrl->AddPendingEvent(theEvent);
488}
489
490//---------------------------------------------------------------------------
491// wxQTMediaBackend::Play
492//
493// 1) Start the QT movie
494// 2) Start the movie loading timer
495//---------------------------------------------------------------------------
496bool wxQTMediaBackend::Play()
497{
498 ::StartMovie(m_movie);
499 m_timer->SetPaused(false);
500 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
501 return ::GetMoviesError() == noErr;
502}
503
504//---------------------------------------------------------------------------
505// wxQTMediaBackend::Pause
506//
507// 1) Stop the movie
508// 2) Stop the movie timer
509//---------------------------------------------------------------------------
510bool wxQTMediaBackend::Pause()
511{
512 ::StopMovie(m_movie);
513 m_timer->SetPaused(true);
514 m_timer->Stop();
515 return ::GetMoviesError() == noErr;
516}
517
518//---------------------------------------------------------------------------
519// wxQTMediaBackend::Stop
520//
521// 1) Stop the movie
522// 2) Stop the movie timer
523// 3) Seek to the beginning of the movie
524//---------------------------------------------------------------------------
525bool wxQTMediaBackend::Stop()
526{
527 m_timer->SetPaused(false);
528 m_timer->Stop();
529
530 ::StopMovie(m_movie);
531 if(::GetMoviesError() != noErr)
532 return false;
533
534 ::GoToBeginningOfMovie(m_movie);
535 return ::GetMoviesError() == noErr;
536}
537
538//---------------------------------------------------------------------------
539// wxQTMediaBackend::GetPlaybackRate
540//
541// 1) Get the movie playback rate from ::GetMovieRate
542//---------------------------------------------------------------------------
543double wxQTMediaBackend::GetPlaybackRate()
544{
545 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
546}
547
548//---------------------------------------------------------------------------
549// wxQTMediaBackend::SetPlaybackRate
550//
551// 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
552//---------------------------------------------------------------------------
553bool wxQTMediaBackend::SetPlaybackRate(double dRate)
554{
555 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
556 return ::GetMoviesError() == noErr;
557}
558
559//---------------------------------------------------------------------------
560// wxQTMediaBackend::SetPosition
561//
562// 1) Create a time record struct (TimeRecord) with appropriate values
563// 2) Pass struct to SetMovieTime
564//---------------------------------------------------------------------------
565bool wxQTMediaBackend::SetPosition(wxLongLong where)
566{
567 TimeRecord theTimeRecord;
568 memset(&theTimeRecord, 0, sizeof(TimeRecord));
569 theTimeRecord.value.lo = where.GetValue();
570 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
571 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
572 ::SetMovieTime(m_movie, &theTimeRecord);
573
574 if (::GetMoviesError() != noErr)
575 return false;
576
577 return true;
578}
579
580//---------------------------------------------------------------------------
581// wxQTMediaBackend::GetPosition
582//
583// Calls GetMovieTime
584//---------------------------------------------------------------------------
585wxLongLong wxQTMediaBackend::GetPosition()
586{
587 return ::GetMovieTime(m_movie, NULL);
588}
589
590//---------------------------------------------------------------------------
591// wxQTMediaBackend::GetVolume
592//
593// Gets the volume through GetMovieVolume - which returns a 16 bit short -
594//
595// +--------+--------+
596// + (1) + (2) +
597// +--------+--------+
598//
599// (1) first 8 bits are value before decimal
600// (2) second 8 bits are value after decimal
601//
602// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
603// 1 (full gain and sound)
604//---------------------------------------------------------------------------
605double wxQTMediaBackend::GetVolume()
606{
607 short sVolume = GetMovieVolume(m_movie);
608
609 if(sVolume & (128 << 8)) //negative - no sound
610 return 0.0;
611
612 return (sVolume & (127 << 8)) ? 1.0 : ((double)(sVolume & 255)) / 255.0;
613}
614
615//---------------------------------------------------------------------------
616// wxQTMediaBackend::SetVolume
617//
618// Sets the volume through SetMovieVolume - which takes a 16 bit short -
619//
620// +--------+--------+
621// + (1) + (2) +
622// +--------+--------+
623//
624// (1) first 8 bits are value before decimal
625// (2) second 8 bits are value after decimal
626//
627// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
628// 1 (full gain and sound)
629//---------------------------------------------------------------------------
630bool wxQTMediaBackend::SetVolume(double dVolume)
631{
632 short sVolume = (short) (dVolume >= .9999 ? 1 << 8 : (dVolume * 255) );
633 SetMovieVolume(m_movie, sVolume);
634 return true;
635}
636
637 //---------------------------------------------------------------------------
638// wxQTMediaBackend::GetDuration
639//
640// Calls GetMovieDuration
641//---------------------------------------------------------------------------
642wxLongLong wxQTMediaBackend::GetDuration()
643{
644 return ::GetMovieDuration(m_movie);
645}
646
647//---------------------------------------------------------------------------
648// wxQTMediaBackend::GetState
649//
650// Determines the current state - the timer keeps track of whether or not
651// we are paused or stopped (if the timer is running we are playing)
652//---------------------------------------------------------------------------
653wxMediaState wxQTMediaBackend::GetState()
654{
655 if ( !m_timer || (m_timer->IsRunning() == false &&
656 m_timer->GetPaused() == false) )
657 return wxMEDIASTATE_STOPPED;
658
659 if( m_timer->IsRunning() )
660 return wxMEDIASTATE_PLAYING;
661 else
662 return wxMEDIASTATE_PAUSED;
663}
664
665//---------------------------------------------------------------------------
666// wxQTMediaBackend::Cleanup
667//
668// Diposes of the movie timer, Control if native, and stops and disposes
669// of the QT movie
670//---------------------------------------------------------------------------
671void wxQTMediaBackend::Cleanup()
672{
673 delete m_timer;
674 m_timer = NULL;
675
676#if wxUSE_CREATEMOVIECONTROL
677 DisposeControl(((wxMediaCtrl*)m_ctrl)->m_peer->GetControlRef());
678#endif
679
680 StopMovie(m_movie);
681 DisposeMovie(m_movie);
682}
683
684//---------------------------------------------------------------------------
685// wxQTMediaBackend::GetVideoSize
686//
687// Returns the actual size of the QT movie
688//---------------------------------------------------------------------------
689wxSize wxQTMediaBackend::GetVideoSize() const
690{
691 return m_bestSize;
692}
693
694//---------------------------------------------------------------------------
695// wxQTMediaBackend::Move
696//
697// We need to do this even when using native qt control because
698// CreateMovieControl is broken in this regard...
699//---------------------------------------------------------------------------
700void wxQTMediaBackend::Move(int x, int y, int w, int h)
701{
702#if !wxUSE_CREATEMOVIECONTROL
703 if(m_timer)
704 {
705 if ( m_ctrl )
706 {
707 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
708 }
709
710 Rect theRect = {y, x, y+h, x+w};
711
712 ::SetMovieBox(m_movie, &theRect);
713 wxASSERT(::GetMoviesError() == noErr);
714 }
715#else
716 if(m_timer && m_ctrl)
717 {
718 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
719
720 ::MoveControl( (ControlRef) m_ctrl->GetHandle(), x, y );
721 m_ctrl->GetParent()->Refresh();
722 m_ctrl->GetParent()->Update();
723 }
724#endif
725}
726
727
728//in source file that contains stuff you don't directly use
729#include "wx/html/forcelnk.h"
730FORCE_LINK_ME(basewxmediabackends);
731
732#endif //wxUSE_MEDIACTRL