]> git.saurik.com Git - wxWidgets.git/blame - src/mac/carbon/mediactrl.cpp
Added simple instructions and autogen.mk to rebuild configure script.
[wxWidgets.git] / src / mac / carbon / mediactrl.cpp
CommitLineData
1a680109 1/////////////////////////////////////////////////////////////////////////////
ff4aedc5
RN
2// Name: mac/carbon/mediactrl.cpp
3// Purpose: Built-in Media Backends for Mac
1a680109 4// Author: Ryan Norton <wxprojects@comcast.net>
ff4aedc5 5// Modified by:
1a680109
RN
6// Created: 11/07/04
7// RCS-ID: $Id$
bf354396 8// Copyright: (c) 2004-2005 Ryan Norton
1a680109
RN
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
bf354396
VZ
12//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13// There are several known bugs with CreateMovieControl
14// on systems > 10.2 - see main.c of QTCarbonShell sample for details
15//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16
ff4aedc5
RN
17//===========================================================================
18// DECLARATIONS
19//===========================================================================
20
21//---------------------------------------------------------------------------
22// Pre-compiled header stuff
23//---------------------------------------------------------------------------
24
25#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
26#pragma implementation "mediactrl.h"
27#endif
1a680109
RN
28
29// For compilers that support precompilation, includes "wx.h".
30#include "wx/wxprec.h"
31
32#ifdef __BORLANDC__
33#pragma hdrstop
34#endif
35
ff4aedc5
RN
36//---------------------------------------------------------------------------
37// Includes
38//---------------------------------------------------------------------------
39#include "wx/mediactrl.h"
1a680109 40
ff4aedc5
RN
41//---------------------------------------------------------------------------
42// Compilation guard
43//---------------------------------------------------------------------------
1a680109
RN
44#if wxUSE_MEDIACTRL
45
3b5023b9
RN
46//---------------------------------------------------------------------------
47// Whether or not to use OSX 10.2's CreateMovieControl for native QuickTime
48// control - i.e. native positioning and event handling etc..
49//---------------------------------------------------------------------------
50#ifndef wxUSE_CREATEMOVIECONTROL
bf354396
VZ
51# if defined( __WXMAC_OSX__ ) && \
52 ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
d0ee33f5
WS
53# define wxUSE_CREATEMOVIECONTROL 1
54# else
55# define wxUSE_CREATEMOVIECONTROL 0
56# endif
3b5023b9
RN
57#endif
58
bf354396
VZ
59//---------------------------------------------------------------------------
60// Height and Width of movie controller in the movie control
61//---------------------------------------------------------------------------
62#define wxMCWIDTH 320
63#define wxMCHEIGHT 16
64
ff4aedc5
RN
65//===========================================================================
66// BACKEND DECLARATIONS
67//===========================================================================
212945de 68
ff4aedc5
RN
69//---------------------------------------------------------------------------
70//
71// wxQTMediaBackend
72//
73//---------------------------------------------------------------------------
1a680109 74
ff4aedc5
RN
75//---------------------------------------------------------------------------
76// QT Includes
77//---------------------------------------------------------------------------
1a680109 78//uma is for wxMacFSSpec
1a680109 79#include "wx/mac/uma.h"
ff4aedc5 80#include "wx/timer.h"
768c6e8b 81#ifndef __DARWIN__
1a680109
RN
82#include <Movies.h>
83#include <Gestalt.h>
ff4aedc5 84#include <QuickTimeComponents.h> //Standard QT stuff
768c6e8b
SC
85#else
86#include <QuickTime/QuickTimeComponents.h>
87#endif
1a680109 88
bf354396 89class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
ff4aedc5
RN
90{
91public:
ff4aedc5
RN
92 wxQTMediaBackend();
93 ~wxQTMediaBackend();
94
d0ee33f5 95 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 96 wxWindowID id,
d0ee33f5 97 const wxPoint& pos,
ff4aedc5 98 const wxSize& size,
d0ee33f5 99 long style,
ff4aedc5
RN
100 const wxValidator& validator,
101 const wxString& name);
102
103 virtual bool Play();
104 virtual bool Pause();
105 virtual bool Stop();
106
107 virtual bool Load(const wxString& fileName);
108 virtual bool Load(const wxURI& location);
109
110 virtual wxMediaState GetState();
111
112 virtual bool SetPosition(wxLongLong where);
113 virtual wxLongLong GetPosition();
114 virtual wxLongLong GetDuration();
115
116 virtual void Move(int x, int y, int w, int h);
117 wxSize GetVideoSize() const;
118
119 virtual double GetPlaybackRate();
120 virtual bool SetPlaybackRate(double dRate);
121
c5191fbd
VZ
122 virtual double GetVolume();
123 virtual bool SetVolume(double);
124
ff4aedc5
RN
125 void Cleanup();
126 void FinishLoad();
127
bf354396
VZ
128 virtual bool ShowPlayerControls(wxMediaCtrlPlayerControls flags);
129
130
131 //
132 // ------ Implementation from now on --------
133 //
134 void DoLoadBestSize();
135 void DoSetControllerVisible(wxMediaCtrlPlayerControls flags);
136
137 //TODO: Last param actually long - does this work on 64bit machines?
138 static Boolean MCFilterProc (MovieController theController,
139 short action, void *params, long refCon);
140
141#if wxUSE_CREATEMOVIECONTROL
142 void DoCreateMovieControl();
143#else
144 Boolean IsQuickTime4Installed();
145 void DoNewMovieController();
146 static void PPRMProc (Movie theMovie, OSErr theErr, void* theRefCon);
147#endif
148
149 wxSize m_bestSize; // Original movie size
20b69855 150#ifdef __WXMAC_OSX__
bf354396 151 struct MovieType** m_movie; // QT Movie handle/instance
20b69855 152#else
bf354396 153 Movie m_movie; // Movie instance
20b69855 154#endif
bf354396
VZ
155 bool m_bPlaying; // Whether media is playing or not
156 class wxTimer* m_timer; // Timer for streaming the movie
157 MovieController m_mc; // MovieController instance
158 wxMediaCtrlPlayerControls m_interfaceflags; // Saved interface flags
159#if !wxUSE_CREATEMOVIECONTROL
160 EventHandlerRef m_pEventHandlerRef; // Event handler to cleanup
ff4aedc5 161
bf354396
VZ
162 friend class wxQTMediaEvtHandler;
163#endif
d0ee33f5 164 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
ff4aedc5
RN
165};
166
bf354396
VZ
167#if !wxUSE_CREATEMOVIECONTROL
168// helper to hijack background erasing for the QT window
169class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
170{
171public:
172 wxQTMediaEvtHandler(wxQTMediaBackend *qtb)
173 {
174 m_qtb = qtb;
175
176 qtb->m_ctrl->Connect(qtb->m_ctrl->GetId(), wxEVT_ERASE_BACKGROUND,
177 wxEraseEventHandler(wxQTMediaEvtHandler::OnEraseBackground),
178 NULL, this);
179 }
180
181 void OnEraseBackground(wxEraseEvent& event);
182
183private:
184 wxQTMediaBackend *m_qtb;
185
186 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
187};
188
189// Window event handler
190static pascal OSStatus wxQTMediaWindowEventHandler(
191 EventHandlerCallRef inHandlerCallRef,
192 EventRef inEvent, void *inUserData);
193DEFINE_ONE_SHOT_HANDLER_GETTER( wxQTMediaWindowEventHandler );
194
195#endif
196
197//===========================================================================
198// IMPLEMENTATION
199//===========================================================================
200
ff4aedc5
RN
201
202//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203//
204// wxQTMediaBackend
d0ee33f5 205//
ff4aedc5
RN
206//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
207
208IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
1a680109 209
bf354396
VZ
210//Time between timer calls - this is the Apple recommondation to the TCL
211//team I believe
212#define MOVIE_DELAY 20
1a680109 213
bf354396
VZ
214//---------------------------------------------------------------------------
215// wxQTMediaLoadTimer
216//
217// QT, esp. QT for Windows is very picky about how you go about
218// async loading. If you were to go through a Windows message loop
219// or a MoviesTask or both and then check the movie load state
220// it would still return 1000 (loading)... even (pre)prerolling doesn't
221// help. However, making a load timer like this works
222//---------------------------------------------------------------------------
223class wxQTMediaLoadTimer : public wxTimer
1a680109
RN
224{
225public:
bf354396
VZ
226 wxQTMediaLoadTimer(Movie movie, wxQTMediaBackend* parent) :
227 m_movie(movie), m_parent(parent) {}
1a680109 228
bf354396
VZ
229 void Notify()
230 {
231 //Note that the CreateMovieControl variety performs
232 //its own custom idleing
233#if !wxUSE_CREATEMOVIECONTROL
234 ::MCIdle(m_parent->m_mc);
235#endif
236 //kMovieLoadStatePlayable is not enough on MAC
237 //- it plays, but IsMovieDone might return true (!)
238 //sure we need to wait until kMovieLoadStatePlaythroughOK
239 if(::GetMovieLoadState(m_movie) >= 20000)
1a680109 240 {
bf354396
VZ
241 m_parent->FinishLoad();
242 delete this;
243 }
1a680109
RN
244 }
245
bf354396
VZ
246protected:
247 Movie m_movie; //Our movie instance
248 wxQTMediaBackend* m_parent; //Backend pointer
249};
250
251// --------------------------------------------------------------------------
252// wxQTMediaPlayTimer - Handle Asyncronous Playing
253//
254// 1) Checks to see if the movie is done, and if not continues
255// streaming the movie
256// 2) Sends the wxEVT_MEDIA_STOP event if we have reached the end of
257// the movie.
258// --------------------------------------------------------------------------
259class wxQTMediaPlayTimer : public wxTimer
260{
261public:
262 wxQTMediaPlayTimer(Movie movie, wxQTMediaBackend* parent) :
263 m_movie(movie), m_parent(parent) {}
1a680109
RN
264
265 void Notify()
1a680109 266 {
bf354396
VZ
267 //Note that CreateMovieControl performs its own idleing
268#if !wxUSE_CREATEMOVIECONTROL
269 //
270 // OK, a little explaining - basically originally
271 // we only called MoviesTask if the movie was actually
272 // playing (not paused or stopped)... this was before
273 // we realized MoviesTask actually handles repainting
274 // of the current frame - so if you were to resize
275 // or something it would previously not redraw that
276 // portion of the movie.
277 //
278 // So now we call MoviesTask always so that it repaints
279 // correctly.
280 //
281 ::MCIdle(m_parent->m_mc);
282#endif
ff4aedc5 283
bf354396
VZ
284 //
285 // Handle the stop event - if the movie has reached
286 // the end, notify our handler
287 //
288 if(::IsMovieDone(m_movie))
ff4aedc5 289 {
bf354396
VZ
290 if ( m_parent->SendStopEvent() )
291 {
ff4aedc5
RN
292 m_parent->Stop();
293 wxASSERT(::GetMoviesError() == noErr);
294
bf354396 295 m_parent->QueueFinishEvent();
1a680109
RN
296 }
297 }
298 }
299
300protected:
ff4aedc5 301 Movie m_movie; //Our movie instance
ff4aedc5 302 wxQTMediaBackend* m_parent; //Backend pointer
1a680109
RN
303};
304
bf354396 305
ff4aedc5 306//---------------------------------------------------------------------------
e7b97da3 307// wxQTMediaBackend Constructor
ff4aedc5
RN
308//
309// Sets m_timer to NULL signifying we havn't loaded anything yet
310//---------------------------------------------------------------------------
bf354396
VZ
311wxQTMediaBackend::wxQTMediaBackend()
312 : m_movie(NULL), m_bPlaying(false), m_timer(NULL)
313 , m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE)
1a680109 314{
1a680109
RN
315}
316
ff4aedc5
RN
317//---------------------------------------------------------------------------
318// wxQTMediaBackend Destructor
319//
320// 1) Cleans up the QuickTime movie instance
321// 2) Decrements the QuickTime reference counter - if this reaches
322// 0, QuickTime shuts down
323// 3) Decrements the QuickTime Windows Media Layer reference counter -
324// if this reaches 0, QuickTime shuts down the Windows Media Layer
325//---------------------------------------------------------------------------
326wxQTMediaBackend::~wxQTMediaBackend()
1a680109 327{
bf354396 328 if(m_movie)
ff4aedc5 329 Cleanup();
1a680109 330
bf354396
VZ
331#if !wxUSE_CREATEMOVIECONTROL
332 // Cleanup for moviecontroller
333 if(m_mc)
334 {
335 // destroy wxQTMediaEvtHandler we pushed on it
336 m_ctrl->PopEventHandler(true);
337 RemoveEventHandler((EventHandlerRef&)m_pEventHandlerRef);
338 ::DisposeMovieController(m_mc);
339 }
340#endif
341
3103e8a9 342 //Note that ExitMovies() is not necessary...
ff4aedc5 343 ExitMovies();
1a680109
RN
344}
345
ff4aedc5
RN
346//---------------------------------------------------------------------------
347// wxQTMediaBackend::CreateControl
348//
349// 1) Intializes QuickTime
350// 2) Creates the control window
351//---------------------------------------------------------------------------
d0ee33f5 352bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
ff4aedc5 353 wxWindowID id,
d0ee33f5 354 const wxPoint& pos,
ff4aedc5 355 const wxSize& size,
d0ee33f5 356 long style,
ff4aedc5
RN
357 const wxValidator& validator,
358 const wxString& name)
1a680109 359{
3b5023b9 360 //Don't bother in Native control mode
bf354396
VZ
361#if !wxUSE_CREATEMOVIECONTROL
362 if (!IsQuickTime4Installed())
1a680109 363 return false;
3b5023b9 364#endif
1a680109 365
ff4aedc5
RN
366 EnterMovies();
367
368 //
369 // Create window
370 // By default wxWindow(s) is created with a border -
371 // so we need to get rid of those
372 //
373 // Since we don't have a child window like most other
374 // backends, we don't need wxCLIP_CHILDREN
375 //
bf354396 376 if ( !ctrl->wxControl::Create(parent, id, pos, size,
3dee36ae 377 wxWindow::MacRemoveBordersFromStyle(style),
3dee36ae 378 validator, name)
3b5023b9 379 )
1a680109
RN
380 return false;
381
ecd20d4a 382#if wxUSE_VALIDATORS
3dee36ae 383 ctrl->SetValidator(validator);
ecd20d4a
RN
384#endif
385
bf354396 386 m_ctrl = (wxMediaCtrl*)ctrl;
1a680109
RN
387 return true;
388}
389
bf354396
VZ
390//---------------------------------------------------------------------------
391// wxQTMediaBackend::IsQuickTime4Installed
392//
393// Determines whether version 4 of QT is installed
394// (Pretty much for classic only)
395//---------------------------------------------------------------------------
396#if !wxUSE_CREATEMOVIECONTROL
397Boolean wxQTMediaBackend::IsQuickTime4Installed()
398{
399 short error;
400 long result;
401
402 error = Gestalt (gestaltQuickTime, &result);
403 return (error == noErr) && (((result >> 16) & 0xffff) >= 0x0400);
404}
405#endif
406
ff4aedc5
RN
407//---------------------------------------------------------------------------
408// wxQTMediaBackend::Load (file version)
409//
410// 1) Get an FSSpec from the Windows path name
411// 2) Open the movie
412// 3) Obtain the movie instance from the movie resource
3b5023b9
RN
413// 4) Close the movie resource
414// 5) Finish loading
ff4aedc5
RN
415//---------------------------------------------------------------------------
416bool wxQTMediaBackend::Load(const wxString& fileName)
1a680109 417{
bf354396 418 if(m_movie)
1a680109
RN
419 Cleanup();
420
1a680109
RN
421 OSErr err = noErr;
422 short movieResFile;
423 FSSpec sfFile;
d0ee33f5
WS
424
425 //FIXME:wxMacFilename2FSSpec crashes on empty string -
099c3b27
RN
426 //does it crash on other strings too and should this
427 //"fix" be put in the carbon wxSound?
428 if (fileName.empty())
429 return false;
d0ee33f5 430
ff4aedc5 431 wxMacFilename2FSSpec( fileName , &sfFile );
d0ee33f5 432
1a680109
RN
433 if (OpenMovieFile (&sfFile, &movieResFile, fsRdPerm) != noErr)
434 return false;
435
436 short movieResID = 0;
437 Str255 movieName;
438
439 err = NewMovieFromFile (
440 &m_movie,
441 movieResFile,
442 &movieResID,
443 movieName,
444 newMovieActive,
445 NULL); //wasChanged
446
bf354396
VZ
447 //No ::GetMoviesStickyError() here because it returns -2009
448 // a.k.a. invalid track on valid mpegs
449 if(err == noErr)
450 {
451 ::CloseMovieFile (movieResFile);
1a680109 452
bf354396
VZ
453 // Create movie controller/control
454#if wxUSE_CREATEMOVIECONTROL
455 DoCreateMovieControl();
456#else
457 DoNewMovieController();
458#endif
1a680109 459 FinishLoad();
bf354396
VZ
460 return true;
461 }
462 else
463 {
464 return false;
465 }
466}
1a680109 467
bf354396
VZ
468//---------------------------------------------------------------------------
469// wxQTMediaBackend::PPRMProc (static)
470//
471// Called when done PrePrerolling the movie.
472// Note that in 99% of the cases this does nothing...
473// Anyway we set up the loading timer here to tell us when the movie is done
474//---------------------------------------------------------------------------
475#if !wxUSE_CREATEMOVIECONTROL
476void wxQTMediaBackend::PPRMProc (Movie theMovie,
477 OSErr WXUNUSED_UNLESS_DEBUG(theErr),
478 void* theRefCon)
479{
480 wxASSERT( theMovie );
481 wxASSERT( theRefCon );
482 wxASSERT( theErr == noErr );
483
484 wxQTMediaBackend* pBE = (wxQTMediaBackend*) theRefCon;
485
486 long lTime = ::GetMovieTime(theMovie,NULL);
487 Fixed rate = ::GetMoviePreferredRate(theMovie);
488 ::PrerollMovie(theMovie,lTime,rate);
489 pBE->m_timer = new wxQTMediaLoadTimer(pBE->m_movie, pBE);
490 pBE->m_timer->Start(MOVIE_DELAY);
1a680109 491}
bf354396 492#endif
1a680109 493
ff4aedc5 494//---------------------------------------------------------------------------
3b5023b9 495// wxQTMediaBackend::Load (URL Version)
ff4aedc5 496//
3b5023b9
RN
497// 1) Build an escaped URI from location
498// 2) Create a handle to store the URI string
499// 3) Put the URI string inside the handle
500// 4) Make a QuickTime URL data ref from the handle with the URI in it
501// 5) Clean up the URI string handle
502// 6) Do some prerolling
503// 7) Finish Loading
ff4aedc5
RN
504//---------------------------------------------------------------------------
505bool wxQTMediaBackend::Load(const wxURI& location)
1a680109 506{
bf354396 507 if(m_movie)
1a680109
RN
508 Cleanup();
509
1a680109
RN
510 wxString theURI = location.BuildURI();
511
512 OSErr err = noErr;
513
bf354396 514 Handle theHandle = ::NewHandleClear(theURI.length() + 1);
1a680109
RN
515 wxASSERT(theHandle);
516
bf354396 517 ::BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
1a680109
RN
518
519 //create the movie from the handle that refers to the URI
bf354396
VZ
520 err = ::NewMovieFromDataRef(&m_movie, newMovieActive |
521 newMovieAsyncOK
522 /*|newMovieIdleImportOK*/,
d0ee33f5 523 NULL, theHandle,
1a680109
RN
524 URLDataHandlerSubType);
525
bf354396 526 ::DisposeHandle(theHandle);
1a680109 527
bf354396
VZ
528 if (err == noErr)
529 {
530#if wxUSE_CREATEMOVIECONTROL
531 // Movie control resets prerolling, so we must create first
532 DoCreateMovieControl();
1a680109 533
bf354396
VZ
534 // Setup timer to catch load event
535 m_timer = new wxQTMediaLoadTimer(m_movie, this);
536 m_timer->Start(MOVIE_DELAY);
537#else
538 // Movie controller resets prerolling, so we must create first
539 DoNewMovieController();
540
541 long timeNow;
1a680109 542 Fixed playRate;
1a680109 543
bf354396
VZ
544 timeNow = ::GetMovieTime(m_movie, NULL);
545 wxASSERT(::GetMoviesError() == noErr);
1a680109 546
bf354396
VZ
547 playRate = ::GetMoviePreferredRate(m_movie);
548 wxASSERT(::GetMoviesError() == noErr);
549
550 //
551 // Note that the callback here is optional,
552 // but without it PrePrerollMovie can be buggy
553 // (see Apple ml). Also, some may wonder
554 // why we need this at all - this is because
555 // Apple docs say QuickTime streamed movies
556 // require it if you don't use a Movie Controller,
557 // which we don't by default.
558 //
559 ::PrePrerollMovie(m_movie, timeNow, playRate,
560 wxQTMediaBackend::PPRMProc,
561 (void*)this);
562#endif
563 return true;
564 }
565 else
566 return false;
1a680109
RN
567}
568
ff4aedc5 569//---------------------------------------------------------------------------
bf354396
VZ
570// wxQTMediaBackend::DoCreateMovieControl
571//
572// Calls CreateMovieControl and performs setup related to it
ff4aedc5 573//
bf354396
VZ
574// Note that we always hide the controller initially becuase when loading
575// from a url it displays about a 40x40 box with the word loading... in it,
576// but the box is outside the range of the control, which is bad (0,0
577// i believe), so we need to wait until finishload to actually display
578// the movie controller in this instance
ff4aedc5 579//---------------------------------------------------------------------------
d0ee33f5 580#if wxUSE_CREATEMOVIECONTROL
bf354396
VZ
581void wxQTMediaBackend::DoCreateMovieControl()
582{
3dee36ae
WS
583 //
584 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
585 //wxQTMovie for some of this).
586 //
3dee36ae
WS
587 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
588 m_ctrl->GetPosition(),
589 m_ctrl->GetSize());
590
591 //Dispose of old control for new one
bf354396
VZ
592 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
593 m_ctrl->m_peer->Dispose();
3dee36ae
WS
594
595 //Options-
596 //kMovieControlOptionXXX
597 //HideController - hide the movie controller
598 //LocateTopLeft - movie is pinned to top left rather than centered in the control
599 //EnableEditing - Allows programmatic editing and dragn'drop
600 //HandleEditingHI- Installs event stuff for edit menu - forces EnableEditing also
601 //SetKeysEnabled - Allows keyboard input
602 //ManuallyIdled - app handles movie idling rather than internal timer event loop
d0ee33f5 603 ::CreateMovieControl(
3b5023b9 604 (WindowRef)
bf354396 605 m_ctrl->MacGetTopLevelWindowRef(), //parent
d0ee33f5
WS
606 &bounds, //control bounds
607 m_movie, //movie handle
608 kMovieControlOptionHideController
609 | kMovieControlOptionLocateTopLeft
610 | kMovieControlOptionSetKeysEnabled
bf354396 611 // | kMovieControlOptionManuallyIdled
d0ee33f5 612 , //flags
bf354396 613 m_ctrl->m_peer->GetControlRefAddr() );
d0ee33f5 614
bf354396
VZ
615 ::EmbedControl(m_ctrl->m_peer->GetControlRef(),
616 (ControlRef)m_ctrl->GetParent()->GetHandle());
617
3dee36ae 618 //
bf354396 619 // Setup MovieController for the new movie
3dee36ae 620 //
bf354396
VZ
621 long dataSize;
622
623 //Get movie controller from our control
624 ::GetControlData( m_ctrl->m_peer->GetControlRef(), 0,
625 kMovieControlDataMovieController,
626 sizeof(MovieController), (Ptr)&m_mc, &dataSize );
627
628 // Setup a callback so we can tell when the user presses
629 // play on the player controls
630 ::MCSetActionFilterWithRefCon(m_mc,
631 wxQTMediaBackend::MCFilterProc, (long)this);
632}
3b5023b9 633#endif
bf354396
VZ
634
635//---------------------------------------------------------------------------
636// wxQTMediaBackend::DoNewMovieController
637//
638// Attaches movie to moviecontroller or creates moviecontroller
639// if not created yet
640//---------------------------------------------------------------------------
641#if !wxUSE_CREATEMOVIECONTROL
642void wxQTMediaBackend::DoNewMovieController()
643{
644 if(!m_mc)
645 {
646 // Get top level window ref for some mac functions
647 WindowRef wrTLW = (WindowRef) m_ctrl->MacGetTopLevelWindowRef();
648
649 // MovieController not setup yet -
650 // so we need to create a new one.
651 // You have to pass a valid movie to
652 // NewMovieController, evidently
653 ::SetMovieGWorld(m_movie,
654 (CGrafPtr) GetWindowPort(wrTLW),
655 NULL);
656 wxASSERT(::GetMoviesError() == noErr);
657
658 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
659 m_ctrl->GetPosition(),
660 m_ctrl->GetSize());
661
662 m_mc = ::NewMovieController(m_movie, &bounds, mcTopLeftMovie |
663 //mcWithFrame |
664 mcNotVisible);
665 wxASSERT(::GetMoviesError() == noErr);
666 ::MCDoAction(m_mc, 32, (void*)true); //mcActionSetKeysEnabled
667 wxASSERT(::GetMoviesError() == noErr);
668
669 // Setup a callback so we can tell when the user presses
670 // play on the player controls
671 ::MCSetActionFilterWithRefCon(m_mc,
672 wxQTMediaBackend::MCFilterProc, (long)this);
673 wxASSERT(::GetMoviesError() == noErr);
674
675 //Part of a suggestion from Greg Hazel to repaint
676 //movie when idle
677 m_ctrl->PushEventHandler(new wxQTMediaEvtHandler(this));
678
679 // Event types to catch from the TLW
680 // for the moviecontroller
681 EventTypeSpec theEventTypes[] = {
682 { kEventClassMouse, kEventMouseDown },
683 { kEventClassMouse, kEventMouseUp },
684 { kEventClassKeyboard, kEventRawKeyDown },
685 { kEventClassKeyboard, kEventRawKeyRepeat },
686 { kEventClassKeyboard, kEventRawKeyUp },
687 { kEventClassWindow, kEventWindowUpdate },
688 { kEventClassWindow, kEventWindowActivated },
689 { kEventClassWindow, kEventWindowDeactivated }
690 };
691
692 // Catch window messages -
693 // if we do not do this and if the user clicks the play
694 // button on the controller, for instance, nothing will happen...
695 InstallWindowEventHandler( wrTLW,
696 GetwxQTMediaWindowEventHandlerUPP(),
697 GetEventTypeCount( theEventTypes ), theEventTypes,
698 m_mc, (&(EventHandlerRef&)m_pEventHandlerRef) );
699 }
700 else
701 {
702 // MovieController already created -
703 // Just change the movie in it and we're good to go
704 Point thePoint;
705 thePoint.h = thePoint.v = 0;
706 ::MCSetMovie(m_mc, m_movie,
707 (WindowRef)m_ctrl->MacGetTopLevelWindowRef(),
708 thePoint);
709 wxASSERT(::GetMoviesError() == noErr);
1a680109 710 }
bf354396
VZ
711}
712#endif
713
714//---------------------------------------------------------------------------
715// wxQTMediaBackend::FinishLoad
716//
717// Performs operations after a movie ready to play/loaded.
718//---------------------------------------------------------------------------
719void wxQTMediaBackend::FinishLoad()
720{
721 // get the real size of the movie
722 DoLoadBestSize();
723
724 // Show the player controls if the user wants to
725 if(m_interfaceflags)
726 DoSetControllerVisible(m_interfaceflags);
1a680109 727
1a680109
RN
728 //we want millisecond precision
729 ::SetMovieTimeScale(m_movie, 1000);
ff4aedc5
RN
730 wxASSERT(::GetMoviesError() == noErr);
731
bf354396
VZ
732 // Start movie progress timer
733 m_timer = new wxQTMediaPlayTimer(m_movie, (wxQTMediaBackend*) this);
734 wxASSERT(m_timer);
735 m_timer->Start(MOVIE_DELAY, wxTIMER_CONTINUOUS);
736
737 //send loaded event & refresh size
738 NotifyMovieLoaded();
739}
740
741//---------------------------------------------------------------------------
742// wxQTMediaBackend::DoLoadBestSize
743//
744// Sets the best size of the control from the real size of the movie
745//---------------------------------------------------------------------------
746void wxQTMediaBackend::DoLoadBestSize()
747{
748 //get the real size of the movie
749 Rect outRect;
750 ::GetMovieNaturalBoundsRect (m_movie, &outRect);
751 wxASSERT(::GetMoviesError() == noErr);
752
753 //determine best size
754 m_bestSize.x = outRect.right - outRect.left;
755 m_bestSize.y = outRect.bottom - outRect.top;
1a680109
RN
756}
757
ff4aedc5 758//---------------------------------------------------------------------------
e7b97da3 759// wxQTMediaBackend::Play
ff4aedc5 760//
bf354396 761// Start the QT movie
ff4aedc5
RN
762//---------------------------------------------------------------------------
763bool wxQTMediaBackend::Play()
1a680109 764{
bf354396
VZ
765 Fixed fixRate = (Fixed) (wxQTMediaBackend::GetPlaybackRate() * 0x10000);
766 if(!fixRate)
767 fixRate = ::GetMoviePreferredRate(m_movie);
768
769 wxASSERT(fixRate != 0);
770
771 if(!m_bPlaying)
772 ::MCDoAction( m_mc, 8, // mcActionPlay
773 (void *) fixRate);
774
775 m_bPlaying = true;
1a680109
RN
776 return ::GetMoviesError() == noErr;
777}
778
ff4aedc5 779//---------------------------------------------------------------------------
e7b97da3 780// wxQTMediaBackend::Pause
ff4aedc5 781//
bf354396 782// Stop the movie
ff4aedc5
RN
783//---------------------------------------------------------------------------
784bool wxQTMediaBackend::Pause()
1a680109 785{
bf354396
VZ
786 //Stop the movie A.K.A. ::StopMovie(m_movie);
787 if(m_bPlaying)
788 {
789 ::MCDoAction( m_mc, 8 /*mcActionPlay*/,
790 (void *) 0);
791 m_bPlaying = false;
1a680109 792 return ::GetMoviesError() == noErr;
bf354396
VZ
793 }
794 return true; //already paused
1a680109
RN
795}
796
ff4aedc5 797//---------------------------------------------------------------------------
e7b97da3 798// wxQTMediaBackend::Stop
ff4aedc5 799//
3b5023b9 800// 1) Stop the movie
bf354396 801// 2) Seek to the beginning of the movie
ff4aedc5
RN
802//---------------------------------------------------------------------------
803bool wxQTMediaBackend::Stop()
1a680109 804{
bf354396 805 if(!wxQTMediaBackend::Pause())
1a680109 806 return false;
d0ee33f5 807
1a680109
RN
808 ::GoToBeginningOfMovie(m_movie);
809 return ::GetMoviesError() == noErr;
810}
811
ff4aedc5 812//---------------------------------------------------------------------------
e7b97da3 813// wxQTMediaBackend::GetPlaybackRate
ff4aedc5 814//
3b5023b9 815// 1) Get the movie playback rate from ::GetMovieRate
ff4aedc5
RN
816//---------------------------------------------------------------------------
817double wxQTMediaBackend::GetPlaybackRate()
1a680109 818{
1a680109
RN
819 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
820}
821
ff4aedc5 822//---------------------------------------------------------------------------
e7b97da3 823// wxQTMediaBackend::SetPlaybackRate
ff4aedc5 824//
3b5023b9 825// 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
ff4aedc5
RN
826//---------------------------------------------------------------------------
827bool wxQTMediaBackend::SetPlaybackRate(double dRate)
1a680109 828{
1a680109
RN
829 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
830 return ::GetMoviesError() == noErr;
831}
832
ff4aedc5 833//---------------------------------------------------------------------------
e7b97da3 834// wxQTMediaBackend::SetPosition
ff4aedc5 835//
3b5023b9
RN
836// 1) Create a time record struct (TimeRecord) with appropriate values
837// 2) Pass struct to SetMovieTime
ff4aedc5
RN
838//---------------------------------------------------------------------------
839bool wxQTMediaBackend::SetPosition(wxLongLong where)
1a680109 840{
1a680109
RN
841 TimeRecord theTimeRecord;
842 memset(&theTimeRecord, 0, sizeof(TimeRecord));
ff4aedc5 843 theTimeRecord.value.lo = where.GetValue();
1a680109
RN
844 theTimeRecord.scale = ::GetMovieTimeScale(m_movie);
845 theTimeRecord.base = ::GetMovieTimeBase(m_movie);
846 ::SetMovieTime(m_movie, &theTimeRecord);
847
848 if (::GetMoviesError() != noErr)
849 return false;
850
851 return true;
852}
853
ff4aedc5 854//---------------------------------------------------------------------------
e7b97da3 855// wxQTMediaBackend::GetPosition
ff4aedc5 856//
3b5023b9 857// Calls GetMovieTime
ff4aedc5
RN
858//---------------------------------------------------------------------------
859wxLongLong wxQTMediaBackend::GetPosition()
1a680109 860{
1a680109
RN
861 return ::GetMovieTime(m_movie, NULL);
862}
863
ff4aedc5 864//---------------------------------------------------------------------------
c5191fbd
VZ
865// wxQTMediaBackend::GetVolume
866//
867// Gets the volume through GetMovieVolume - which returns a 16 bit short -
868//
869// +--------+--------+
870// + (1) + (2) +
871// +--------+--------+
872//
873// (1) first 8 bits are value before decimal
874// (2) second 8 bits are value after decimal
875//
876// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
877// 1 (full gain and sound)
878//---------------------------------------------------------------------------
879double wxQTMediaBackend::GetVolume()
880{
bf354396 881 short sVolume = ::GetMovieVolume(m_movie);
c5191fbd
VZ
882
883 if(sVolume & (128 << 8)) //negative - no sound
884 return 0.0;
885
bf354396 886 return sVolume/256.0;
c5191fbd
VZ
887}
888
889//---------------------------------------------------------------------------
890// wxQTMediaBackend::SetVolume
891//
892// Sets the volume through SetMovieVolume - which takes a 16 bit short -
893//
894// +--------+--------+
895// + (1) + (2) +
896// +--------+--------+
897//
898// (1) first 8 bits are value before decimal
899// (2) second 8 bits are value after decimal
900//
901// Volume ranges from -1.0 (gain but no sound), 0 (no sound and no gain) to
902// 1 (full gain and sound)
903//---------------------------------------------------------------------------
904bool wxQTMediaBackend::SetVolume(double dVolume)
905{
bf354396 906 ::SetMovieVolume(m_movie, (short) (dVolume * 256));
c5191fbd
VZ
907 return true;
908}
909
910 //---------------------------------------------------------------------------
e7b97da3 911// wxQTMediaBackend::GetDuration
ff4aedc5 912//
3b5023b9 913// Calls GetMovieDuration
ff4aedc5
RN
914//---------------------------------------------------------------------------
915wxLongLong wxQTMediaBackend::GetDuration()
1a680109 916{
1a680109
RN
917 return ::GetMovieDuration(m_movie);
918}
919
ff4aedc5 920//---------------------------------------------------------------------------
e7b97da3 921// wxQTMediaBackend::GetState
ff4aedc5 922//
3b5023b9
RN
923// Determines the current state - the timer keeps track of whether or not
924// we are paused or stopped (if the timer is running we are playing)
ff4aedc5
RN
925//---------------------------------------------------------------------------
926wxMediaState wxQTMediaBackend::GetState()
1a680109 927{
bf354396
VZ
928 // Could use
929 // GetMovieActive/IsMovieDone/SetMovieActive
930 // combo if implemented that way
931 if (m_bPlaying == true)
1a680109 932 return wxMEDIASTATE_PLAYING;
bf354396
VZ
933 else if ( !m_movie || wxQTMediaBackend::GetPosition() == 0)
934 return wxMEDIASTATE_STOPPED;
1a680109
RN
935 else
936 return wxMEDIASTATE_PAUSED;
937}
938
ff4aedc5 939//---------------------------------------------------------------------------
e7b97da3 940// wxQTMediaBackend::Cleanup
ff4aedc5 941//
3b5023b9
RN
942// Diposes of the movie timer, Control if native, and stops and disposes
943// of the QT movie
ff4aedc5
RN
944//---------------------------------------------------------------------------
945void wxQTMediaBackend::Cleanup()
1a680109 946{
bf354396
VZ
947 m_bPlaying = false;
948 if(m_timer)
949 {
1a680109
RN
950 delete m_timer;
951 m_timer = NULL;
bf354396 952 }
d0ee33f5 953
bf354396
VZ
954 // Stop the movie
955 // Apple samples with CreateMovieControl typically
956 // install a event handler and do this on the dispose
957 // event, but we do it here for simplicity
958 // (It might keep playing for several seconds after
959 // control destruction if not)
960 wxQTMediaBackend::Pause();
961
962 //
963 // Dispose of control or remove movie from MovieController
964 //
d0ee33f5 965#if wxUSE_CREATEMOVIECONTROL
bf354396
VZ
966 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
967 m_ctrl->m_peer->Dispose();
968#else
969 Point thePoint;
970 thePoint.h = thePoint.v = 0;
971 ::MCSetVisible(m_mc, false);
972 ::MCSetMovie(m_mc, NULL, NULL, thePoint);
3b5023b9 973#endif
1a680109 974
bf354396
VZ
975 ::DisposeMovie(m_movie);
976}
977
978//---------------------------------------------------------------------------
979// wxQTMediaBackend::MCFilterProc (static)
980//
981// Callback for when the movie controller recieves a message
982//---------------------------------------------------------------------------
983Boolean wxQTMediaBackend::MCFilterProc(
984 MovieController WXUNUSED(theController),
985 short action,
986 void * WXUNUSED(params),
987 long refCon)
988{
989 if(action != 1) //don't process idle events
990 {
991 wxQTMediaBackend* pThis = (wxQTMediaBackend*)refCon;
992
993 switch(action)
994 {
995 case 8: //play button triggered - MC will set movie to opposite state
996 //of current - playing ? paused : playing
997 pThis->m_bPlaying = !(pThis->m_bPlaying);
998 break;
999 default:
1000 break;
1001 }
1002 }
1003 return 0;
1a680109
RN
1004}
1005
ff4aedc5 1006//---------------------------------------------------------------------------
e7b97da3 1007// wxQTMediaBackend::GetVideoSize
ff4aedc5 1008//
3b5023b9 1009// Returns the actual size of the QT movie
ff4aedc5
RN
1010//---------------------------------------------------------------------------
1011wxSize wxQTMediaBackend::GetVideoSize() const
1a680109
RN
1012{
1013 return m_bestSize;
1014}
1015
ff4aedc5
RN
1016//---------------------------------------------------------------------------
1017// wxQTMediaBackend::Move
1018//
d0ee33f5 1019// We need to do this even when using native qt control because
4b60077b 1020// CreateMovieControl is broken in this regard...
ff4aedc5
RN
1021//---------------------------------------------------------------------------
1022void wxQTMediaBackend::Move(int x, int y, int w, int h)
1a680109 1023{
d0ee33f5 1024#if !wxUSE_CREATEMOVIECONTROL
ff4aedc5 1025 if(m_timer)
1a680109 1026 {
e7b97da3 1027 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1a680109 1028 Rect theRect = {y, x, y+h, x+w};
ff4aedc5 1029
bf354396 1030 ::MCSetControllerBoundsRect(m_mc, &theRect);
1a680109
RN
1031 wxASSERT(::GetMoviesError() == noErr);
1032 }
4b60077b
RN
1033#else
1034 if(m_timer && m_ctrl)
1035 {
1036 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1037
1038 ::MoveControl( (ControlRef) m_ctrl->GetHandle(), x, y );
1039 m_ctrl->GetParent()->Refresh();
1040 m_ctrl->GetParent()->Update();
1041 }
3b5023b9 1042#endif
1a680109
RN
1043}
1044
bf354396
VZ
1045//---------------------------------------------------------------------------
1046// wxQTMediaBackend::DoSetControllerVisible
1047//
1048// Utility function that takes care of showing the moviecontroller
1049// and showing/hiding the particular controls on it
1050//---------------------------------------------------------------------------
1051void wxQTMediaBackend::DoSetControllerVisible(wxMediaCtrlPlayerControls flags)
1052{
1053 ::MCSetVisible(m_mc, TRUE);
1054
1055 //
1056 // Take care of subcontrols
1057 //
1058 if(::GetMoviesError() == noErr)
1059 {
1060 long mcFlags = 0;
1061 ::MCDoAction(m_mc, 39/*mcActionGetFlags*/, (void*)&mcFlags);
1062
1063 if(::GetMoviesError() == noErr)
1064 {
1065 mcFlags |= ( //(1<<0)/*mcFlagSuppressMovieFrame*/ |
1066 (1<<3)/*mcFlagsUseWindowPalette*/
1067 | ((flags & wxMEDIACTRLPLAYERCONTROLS_STEP)
1068 ? 0 : (1<<1)/*mcFlagSuppressStepButtons*/)
1069 | ((flags & wxMEDIACTRLPLAYERCONTROLS_VOLUME)
1070 ? 0 : (1<<2)/*mcFlagSuppressSpeakerButton*/)
1071 // | (1<<4) /*mcFlagDontInvalidate*/ //if we take care of repainting ourselves
1072 );
1073 ::MCDoAction(m_mc, 38/*mcActionSetFlags*/, (void*)mcFlags);
1074 }
1075 }
1076
1077 //
1078 //Adjust height and width of best size for movie controller
1079 //if the user wants it shown
1080 //
1081 m_bestSize.x = m_bestSize.x > wxMCWIDTH ? m_bestSize.x : wxMCWIDTH;
1082 m_bestSize.y += wxMCHEIGHT;
1083}
1084
1085//---------------------------------------------------------------------------
1086// wxQTMediaBackend::ShowPlayerControls
1087//
1088// Shows/Hides subcontrols on the media control
1089//---------------------------------------------------------------------------
1090bool wxQTMediaBackend::ShowPlayerControls(wxMediaCtrlPlayerControls flags)
1091{
1092 if(!m_mc)
1093 return false; //no movie controller...
1094
1095 bool bSizeChanged = false;
1096
1097 //if the controller is visible and we want to hide it do so
1098 if(m_interfaceflags && !flags)
1099 {
1100 bSizeChanged = true;
1101 DoLoadBestSize();
1102 ::MCSetVisible(m_mc, FALSE);
1103 }
1104 else if(!m_interfaceflags && flags) //show controller if hidden
1105 {
1106 bSizeChanged = true;
1107 DoSetControllerVisible(flags);
1108 }
1109
1110 //readjust parent sizers
1111 if(bSizeChanged)
1112 {
1113 NotifyMovieSizeChanged();
1114
1115 //remember state in case of loading new media
1116 m_interfaceflags = flags;
1117 }
1118
1119 return ::GetMoviesError() == noErr;
1120}
1121
1122//---------------------------------------------------------------------------
1123// wxQTMediaBackend::OnEraseBackground
1124//
1125// Suggestion from Greg Hazel to repaint the movie when idle
1126// (on pause also)
1127//---------------------------------------------------------------------------
1128#if !wxUSE_CREATEMOVIECONTROL
1129void wxQTMediaEvtHandler::OnEraseBackground(wxEraseEvent& evt)
1130{
1131 // Work around Nasty OSX drawing bug -
1132 // http://lists.apple.com/archives/QuickTime-API/2002/Feb/msg00311.html
1133 WindowRef wrTLW =
1134 (WindowRef) m_qtb->m_ctrl->MacGetTopLevelWindowRef();
1135
1136 RgnHandle region = MCGetControllerBoundsRgn(m_qtb->m_mc);
1137 MCInvalidate(m_qtb->m_mc, wrTLW, region);
1138 MCIdle(m_qtb->m_mc);
1139}
1140#endif
1141
1142//---------------------------------------------------------------------------
1143// wxQTMediaWindowEventHandler
1144//
1145// Event callback for the top level window of our control that passes
1146// messages to our moviecontroller so it can recieve mouse clicks etc.
1147//---------------------------------------------------------------------------
1148#if !wxUSE_CREATEMOVIECONTROL
1149OSStatus wxQTMediaWindowEventHandler(EventHandlerCallRef inHandlerCallRef,
1150 EventRef inEvent, void *inUserData)
1151{
1152 EventRecord theEvent;
1153 ConvertEventRefToEventRecord( inEvent, &theEvent );
1154 OSStatus err;
1155 err = ::MCIsPlayerEvent( (MovieController) inUserData, &theEvent );
1156
1157 // pass on to other event handlers if not handled- i.e. wx
1158 if(err)
1159 return noErr;
1160 else
1161 return eventNotHandledErr;
1162}
1163#endif
ff4aedc5
RN
1164
1165//in source file that contains stuff you don't directly use
f572d649 1166#include "wx/html/forcelnk.h"
ff4aedc5
RN
1167FORCE_LINK_ME(basewxmediabackends);
1168
1169#endif //wxUSE_MEDIACTRL