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