]> git.saurik.com Git - wxWidgets.git/blob - src/mac/carbon/mediactrl.cpp
8c56a16366685e8742be0210b1ee7b7358fa955b
[wxWidgets.git] / src / mac / carbon / mediactrl.cpp
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
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 // There are several known bugs with CreateMovieControl
14 // on systems > 10.2 - see main.c of QTCarbonShell sample for details
15 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16
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
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
36 //---------------------------------------------------------------------------
37 // Includes
38 //---------------------------------------------------------------------------
39 #include "wx/mediactrl.h"
40
41 //---------------------------------------------------------------------------
42 // Compilation guard
43 //---------------------------------------------------------------------------
44 #if wxUSE_MEDIACTRL
45
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
51 # if defined( __WXMAC_OSX__ ) && \
52 ( MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2 )
53 # define wxUSE_CREATEMOVIECONTROL 1
54 # else
55 # define wxUSE_CREATEMOVIECONTROL 0
56 # endif
57 #endif
58
59 //---------------------------------------------------------------------------
60 // Height and Width of movie controller in the movie control
61 //---------------------------------------------------------------------------
62 #define wxMCWIDTH 320
63 #define wxMCHEIGHT 16
64
65 //===========================================================================
66 // BACKEND DECLARATIONS
67 //===========================================================================
68
69 //---------------------------------------------------------------------------
70 //
71 // wxQTMediaBackend
72 //
73 //---------------------------------------------------------------------------
74
75 //---------------------------------------------------------------------------
76 // QT Includes
77 //---------------------------------------------------------------------------
78 //uma is for wxMacFSSpec
79 #include "wx/mac/uma.h"
80 #include "wx/timer.h"
81 #ifndef __DARWIN__
82 #include <Movies.h>
83 #include <Gestalt.h>
84 #include <QuickTimeComponents.h> //Standard QT stuff
85 #else
86 #include <QuickTime/QuickTimeComponents.h>
87 #endif
88
89 class WXDLLIMPEXP_MEDIA wxQTMediaBackend : public wxMediaBackendCommonBase
90 {
91 public:
92 wxQTMediaBackend();
93 ~wxQTMediaBackend();
94
95 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
96 wxWindowID id,
97 const wxPoint& pos,
98 const wxSize& size,
99 long style,
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
122 virtual double GetVolume();
123 virtual bool SetVolume(double);
124
125 void Cleanup();
126 void FinishLoad();
127
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
150 #ifdef __WXMAC_OSX__
151 struct MovieType** m_movie; // QT Movie handle/instance
152 #else
153 Movie m_movie; // Movie instance
154 #endif
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
161
162 friend class wxQTMediaEvtHandler;
163 #endif
164 DECLARE_DYNAMIC_CLASS(wxQTMediaBackend)
165 };
166
167 #if !wxUSE_CREATEMOVIECONTROL
168 // helper to hijack background erasing for the QT window
169 class WXDLLIMPEXP_MEDIA wxQTMediaEvtHandler : public wxEvtHandler
170 {
171 public:
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
183 private:
184 wxQTMediaBackend *m_qtb;
185
186 DECLARE_NO_COPY_CLASS(wxQTMediaEvtHandler)
187 };
188
189 // Window event handler
190 static pascal OSStatus wxQTMediaWindowEventHandler(
191 EventHandlerCallRef inHandlerCallRef,
192 EventRef inEvent, void *inUserData);
193 DEFINE_ONE_SHOT_HANDLER_GETTER( wxQTMediaWindowEventHandler );
194
195 #endif
196
197 //===========================================================================
198 // IMPLEMENTATION
199 //===========================================================================
200
201
202 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
203 //
204 // wxQTMediaBackend
205 //
206 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
207
208 IMPLEMENT_DYNAMIC_CLASS(wxQTMediaBackend, wxMediaBackend);
209
210 //Time between timer calls - this is the Apple recommondation to the TCL
211 //team I believe
212 #define MOVIE_DELAY 20
213
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 //---------------------------------------------------------------------------
223 class wxQTMediaLoadTimer : public wxTimer
224 {
225 public:
226 wxQTMediaLoadTimer(Movie movie, wxQTMediaBackend* parent) :
227 m_movie(movie), m_parent(parent) {}
228
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)
240 {
241 m_parent->FinishLoad();
242 delete this;
243 }
244 }
245
246 protected:
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 // --------------------------------------------------------------------------
259 class wxQTMediaPlayTimer : public wxTimer
260 {
261 public:
262 wxQTMediaPlayTimer(Movie movie, wxQTMediaBackend* parent) :
263 m_movie(movie), m_parent(parent) {}
264
265 void Notify()
266 {
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
283
284 //
285 // Handle the stop event - if the movie has reached
286 // the end, notify our handler
287 //
288 if(::IsMovieDone(m_movie))
289 {
290 if ( m_parent->SendStopEvent() )
291 {
292 m_parent->Stop();
293 wxASSERT(::GetMoviesError() == noErr);
294
295 m_parent->QueueFinishEvent();
296 }
297 }
298 }
299
300 protected:
301 Movie m_movie; //Our movie instance
302 wxQTMediaBackend* m_parent; //Backend pointer
303 };
304
305
306 //---------------------------------------------------------------------------
307 // wxQTMediaBackend Constructor
308 //
309 // Sets m_timer to NULL signifying we havn't loaded anything yet
310 //---------------------------------------------------------------------------
311 wxQTMediaBackend::wxQTMediaBackend()
312 : m_movie(NULL), m_bPlaying(false), m_timer(NULL)
313 , m_mc(NULL), m_interfaceflags(wxMEDIACTRLPLAYERCONTROLS_NONE)
314 {
315 }
316
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 //---------------------------------------------------------------------------
326 wxQTMediaBackend::~wxQTMediaBackend()
327 {
328 if(m_movie)
329 Cleanup();
330
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
342 //Note that ExitMovies() is not necessary...
343 ExitMovies();
344 }
345
346 //---------------------------------------------------------------------------
347 // wxQTMediaBackend::CreateControl
348 //
349 // 1) Intializes QuickTime
350 // 2) Creates the control window
351 //---------------------------------------------------------------------------
352 bool wxQTMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
353 wxWindowID id,
354 const wxPoint& pos,
355 const wxSize& size,
356 long style,
357 const wxValidator& validator,
358 const wxString& name)
359 {
360 //Don't bother in Native control mode
361 #if !wxUSE_CREATEMOVIECONTROL
362 if (!IsQuickTime4Installed())
363 return false;
364 #endif
365
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 //
376 if ( !ctrl->wxControl::Create(parent, id, pos, size,
377 wxWindow::MacRemoveBordersFromStyle(style),
378 validator, name)
379 )
380 return false;
381
382 #if wxUSE_VALIDATORS
383 ctrl->SetValidator(validator);
384 #endif
385
386 m_ctrl = (wxMediaCtrl*)ctrl;
387 return true;
388 }
389
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
397 Boolean 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
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
413 // 4) Close the movie resource
414 // 5) Finish loading
415 //---------------------------------------------------------------------------
416 bool wxQTMediaBackend::Load(const wxString& fileName)
417 {
418 if(m_movie)
419 Cleanup();
420
421 OSErr err = noErr;
422 short movieResFile;
423 FSSpec sfFile;
424
425 //FIXME:wxMacFilename2FSSpec crashes on empty string -
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;
430
431 wxMacFilename2FSSpec( fileName , &sfFile );
432
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
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);
452
453 // Create movie controller/control
454 #if wxUSE_CREATEMOVIECONTROL
455 DoCreateMovieControl();
456 #else
457 DoNewMovieController();
458 #endif
459 FinishLoad();
460 return true;
461 }
462 else
463 {
464 return false;
465 }
466 }
467
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
476 void 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);
491 }
492 #endif
493
494 //---------------------------------------------------------------------------
495 // wxQTMediaBackend::Load (URL Version)
496 //
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
504 //---------------------------------------------------------------------------
505 bool wxQTMediaBackend::Load(const wxURI& location)
506 {
507 if(m_movie)
508 Cleanup();
509
510 wxString theURI = location.BuildURI();
511
512 OSErr err = noErr;
513
514 Handle theHandle = ::NewHandleClear(theURI.length() + 1);
515 wxASSERT(theHandle);
516
517 ::BlockMove(theURI.mb_str(), *theHandle, theURI.length() + 1);
518
519 //create the movie from the handle that refers to the URI
520 err = ::NewMovieFromDataRef(&m_movie, newMovieActive |
521 newMovieAsyncOK
522 /*|newMovieIdleImportOK*/,
523 NULL, theHandle,
524 URLDataHandlerSubType);
525
526 ::DisposeHandle(theHandle);
527
528 if (err == noErr)
529 {
530 #if wxUSE_CREATEMOVIECONTROL
531 // Movie control resets prerolling, so we must create first
532 DoCreateMovieControl();
533
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;
542 Fixed playRate;
543
544 timeNow = ::GetMovieTime(m_movie, NULL);
545 wxASSERT(::GetMoviesError() == noErr);
546
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;
567 }
568
569 //---------------------------------------------------------------------------
570 // wxQTMediaBackend::DoCreateMovieControl
571 //
572 // Calls CreateMovieControl and performs setup related to it
573 //
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
579 //---------------------------------------------------------------------------
580 #if wxUSE_CREATEMOVIECONTROL
581 void wxQTMediaBackend::DoCreateMovieControl()
582 {
583 //
584 //Native CreateMovieControl QT control (Thanks to Kevin Olliver's
585 //wxQTMovie for some of this).
586 //
587 Rect bounds = wxMacGetBoundsForControl(m_ctrl,
588 m_ctrl->GetPosition(),
589 m_ctrl->GetSize());
590
591 //Dispose of old control for new one
592 if (m_ctrl->m_peer && m_ctrl->m_peer->Ok() )
593 m_ctrl->m_peer->Dispose();
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
603 ::CreateMovieControl(
604 (WindowRef)
605 m_ctrl->MacGetTopLevelWindowRef(), //parent
606 &bounds, //control bounds
607 m_movie, //movie handle
608 kMovieControlOptionHideController
609 | kMovieControlOptionLocateTopLeft
610 | kMovieControlOptionSetKeysEnabled
611 // | kMovieControlOptionManuallyIdled
612 , //flags
613 m_ctrl->m_peer->GetControlRefAddr() );
614
615 ::EmbedControl(m_ctrl->m_peer->GetControlRef(),
616 (ControlRef)m_ctrl->GetParent()->GetHandle());
617
618 //
619 // Setup MovieController for the new movie
620 //
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 }
633 #endif
634
635 //---------------------------------------------------------------------------
636 // wxQTMediaBackend::DoNewMovieController
637 //
638 // Attaches movie to moviecontroller or creates moviecontroller
639 // if not created yet
640 //---------------------------------------------------------------------------
641 #if !wxUSE_CREATEMOVIECONTROL
642 void 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);
710 }
711 }
712 #endif
713
714 //---------------------------------------------------------------------------
715 // wxQTMediaBackend::FinishLoad
716 //
717 // Performs operations after a movie ready to play/loaded.
718 //---------------------------------------------------------------------------
719 void 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);
727
728 //we want millisecond precision
729 ::SetMovieTimeScale(m_movie, 1000);
730 wxASSERT(::GetMoviesError() == noErr);
731
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 //---------------------------------------------------------------------------
746 void 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;
756 }
757
758 //---------------------------------------------------------------------------
759 // wxQTMediaBackend::Play
760 //
761 // Start the QT movie
762 //---------------------------------------------------------------------------
763 bool wxQTMediaBackend::Play()
764 {
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;
776 return ::GetMoviesError() == noErr;
777 }
778
779 //---------------------------------------------------------------------------
780 // wxQTMediaBackend::Pause
781 //
782 // Stop the movie
783 //---------------------------------------------------------------------------
784 bool wxQTMediaBackend::Pause()
785 {
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;
792 return ::GetMoviesError() == noErr;
793 }
794 return true; //already paused
795 }
796
797 //---------------------------------------------------------------------------
798 // wxQTMediaBackend::Stop
799 //
800 // 1) Stop the movie
801 // 2) Seek to the beginning of the movie
802 //---------------------------------------------------------------------------
803 bool wxQTMediaBackend::Stop()
804 {
805 if(!wxQTMediaBackend::Pause())
806 return false;
807
808 ::GoToBeginningOfMovie(m_movie);
809 return ::GetMoviesError() == noErr;
810 }
811
812 //---------------------------------------------------------------------------
813 // wxQTMediaBackend::GetPlaybackRate
814 //
815 // 1) Get the movie playback rate from ::GetMovieRate
816 //---------------------------------------------------------------------------
817 double wxQTMediaBackend::GetPlaybackRate()
818 {
819 return ( ((double)::GetMovieRate(m_movie)) / 0x10000);
820 }
821
822 //---------------------------------------------------------------------------
823 // wxQTMediaBackend::SetPlaybackRate
824 //
825 // 1) Convert dRate to Fixed and Set the movie rate through SetMovieRate
826 //---------------------------------------------------------------------------
827 bool wxQTMediaBackend::SetPlaybackRate(double dRate)
828 {
829 ::SetMovieRate(m_movie, (Fixed) (dRate * 0x10000));
830 return ::GetMoviesError() == noErr;
831 }
832
833 //---------------------------------------------------------------------------
834 // wxQTMediaBackend::SetPosition
835 //
836 // 1) Create a time record struct (TimeRecord) with appropriate values
837 // 2) Pass struct to SetMovieTime
838 //---------------------------------------------------------------------------
839 bool wxQTMediaBackend::SetPosition(wxLongLong where)
840 {
841 TimeRecord theTimeRecord;
842 memset(&theTimeRecord, 0, sizeof(TimeRecord));
843 theTimeRecord.value.lo = where.GetValue();
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
854 //---------------------------------------------------------------------------
855 // wxQTMediaBackend::GetPosition
856 //
857 // Calls GetMovieTime
858 //---------------------------------------------------------------------------
859 wxLongLong wxQTMediaBackend::GetPosition()
860 {
861 return ::GetMovieTime(m_movie, NULL);
862 }
863
864 //---------------------------------------------------------------------------
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 //---------------------------------------------------------------------------
879 double wxQTMediaBackend::GetVolume()
880 {
881 short sVolume = ::GetMovieVolume(m_movie);
882
883 if(sVolume & (128 << 8)) //negative - no sound
884 return 0.0;
885
886 return sVolume/256.0;
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 //---------------------------------------------------------------------------
904 bool wxQTMediaBackend::SetVolume(double dVolume)
905 {
906 ::SetMovieVolume(m_movie, (short) (dVolume * 256));
907 return true;
908 }
909
910 //---------------------------------------------------------------------------
911 // wxQTMediaBackend::GetDuration
912 //
913 // Calls GetMovieDuration
914 //---------------------------------------------------------------------------
915 wxLongLong wxQTMediaBackend::GetDuration()
916 {
917 return ::GetMovieDuration(m_movie);
918 }
919
920 //---------------------------------------------------------------------------
921 // wxQTMediaBackend::GetState
922 //
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)
925 //---------------------------------------------------------------------------
926 wxMediaState wxQTMediaBackend::GetState()
927 {
928 // Could use
929 // GetMovieActive/IsMovieDone/SetMovieActive
930 // combo if implemented that way
931 if (m_bPlaying == true)
932 return wxMEDIASTATE_PLAYING;
933 else if ( !m_movie || wxQTMediaBackend::GetPosition() == 0)
934 return wxMEDIASTATE_STOPPED;
935 else
936 return wxMEDIASTATE_PAUSED;
937 }
938
939 //---------------------------------------------------------------------------
940 // wxQTMediaBackend::Cleanup
941 //
942 // Diposes of the movie timer, Control if native, and stops and disposes
943 // of the QT movie
944 //---------------------------------------------------------------------------
945 void wxQTMediaBackend::Cleanup()
946 {
947 m_bPlaying = false;
948 if(m_timer)
949 {
950 delete m_timer;
951 m_timer = NULL;
952 }
953
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 //
965 #if wxUSE_CREATEMOVIECONTROL
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);
973 #endif
974
975 ::DisposeMovie(m_movie);
976 }
977
978 //---------------------------------------------------------------------------
979 // wxQTMediaBackend::MCFilterProc (static)
980 //
981 // Callback for when the movie controller recieves a message
982 //---------------------------------------------------------------------------
983 Boolean 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;
1004 }
1005
1006 //---------------------------------------------------------------------------
1007 // wxQTMediaBackend::GetVideoSize
1008 //
1009 // Returns the actual size of the QT movie
1010 //---------------------------------------------------------------------------
1011 wxSize wxQTMediaBackend::GetVideoSize() const
1012 {
1013 return m_bestSize;
1014 }
1015
1016 //---------------------------------------------------------------------------
1017 // wxQTMediaBackend::Move
1018 //
1019 // We need to do this even when using native qt control because
1020 // CreateMovieControl is broken in this regard...
1021 //---------------------------------------------------------------------------
1022 void wxQTMediaBackend::Move(int x, int y, int w, int h)
1023 {
1024 #if !wxUSE_CREATEMOVIECONTROL
1025 if(m_timer)
1026 {
1027 m_ctrl->GetParent()->MacWindowToRootWindow(&x, &y);
1028 Rect theRect = {y, x, y+h, x+w};
1029
1030 ::MCSetControllerBoundsRect(m_mc, &theRect);
1031 wxASSERT(::GetMoviesError() == noErr);
1032 }
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 }
1042 #endif
1043 }
1044
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 //---------------------------------------------------------------------------
1051 void 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 //---------------------------------------------------------------------------
1090 bool 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
1129 void 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
1149 OSStatus 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
1164
1165 //in source file that contains stuff you don't directly use
1166 #include "wx/html/forcelnk.h"
1167 FORCE_LINK_ME(basewxmediabackends);
1168
1169 #endif //wxUSE_MEDIACTRL