2 * MainWin.cpp - Media Player for the Haiku Operating System
4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5 * Copyright (C) 2007-2010 Stephan Aßmus <superstippi@gmx.de> (GPL->MIT ok)
6 * Copyright (C) 2007-2009 Fredrik Modéen <[FirstName]@[LastName].se> (MIT ok)
8 * Released under the terms of the MIT license.
19 #include <Application.h>
24 #include <LayoutBuilder.h>
27 #include <MediaRoster.h>
31 #include <MessageRunner.h>
32 #include <Messenger.h>
33 #include <PopUpMenu.h>
34 #include <PropertyInfo.h>
35 #include <RecentItems.h>
39 #include <TypeConstants.h>
42 #include "AudioProducer.h"
43 #include "ControllerObserver.h"
44 #include "DurationToString.h"
45 #include "FilePlaylistItem.h"
47 #include "NetworkStreamWin.h"
49 #include "PlaylistItem.h"
50 #include "PlaylistObserver.h"
51 #include "PlaylistWindow.h"
55 #undef B_TRANSLATION_CONTEXT
56 #define B_TRANSLATION_CONTEXT "MediaPlayer-Main"
60 int MainWin::sNoVideoWidth
= MIN_WIDTH
;
63 // XXX TODO: why is lround not defined?
64 #define lround(a) ((int)(0.99999 + (a)))
69 M_NETWORK_STREAM_OPEN
,
76 M_TOGGLE_ALWAYS_ON_TOP
,
77 M_TOGGLE_NO_INTERFACE
,
84 // The common display aspect ratios
85 M_ASPECT_SAME_AS_SOURCE
,
86 M_ASPECT_NO_DISTORTION
,
94 M_SELECT_AUDIO_TRACK
= 0x00000800,
95 M_SELECT_AUDIO_TRACK_END
= 0x00000fff,
96 M_SELECT_VIDEO_TRACK
= 0x00010000,
97 M_SELECT_VIDEO_TRACK_END
= 0x00010fff,
98 M_SELECT_SUB_TITLE_TRACK
= 0x00020000,
99 M_SELECT_SUB_TITLE_TRACK_END
= 0x00020fff,
103 M_SET_PLAYLIST_POSITION
,
108 M_FINISH_SLIDING_CONTROLS
112 static property_info sPropertyInfo
[] = {
113 { B_TRANSLATE("Next"), { B_EXECUTE_PROPERTY
},
114 { B_DIRECT_SPECIFIER
, 0 },
115 B_TRANSLATE("Skip to the next track."), 0
117 { B_TRANSLATE("Prev"), { B_EXECUTE_PROPERTY
},
118 { B_DIRECT_SPECIFIER
, 0 },
119 B_TRANSLATE("Skip to the previous track."), 0
121 { B_TRANSLATE("Play"), { B_EXECUTE_PROPERTY
},
122 { B_DIRECT_SPECIFIER
, 0 },
123 B_TRANSLATE("Start playing."), 0
125 { B_TRANSLATE("Stop"), { B_EXECUTE_PROPERTY
},
126 { B_DIRECT_SPECIFIER
, 0 },
127 B_TRANSLATE("Stop playing."), 0
129 { B_TRANSLATE("Pause"), { B_EXECUTE_PROPERTY
},
130 { B_DIRECT_SPECIFIER
, 0 },
131 B_TRANSLATE("Pause playback."), 0
133 { B_TRANSLATE("TogglePlaying"), { B_EXECUTE_PROPERTY
},
134 { B_DIRECT_SPECIFIER
, 0 },
135 B_TRANSLATE("Toggle pause/play."), 0
137 { B_TRANSLATE("Mute"), { B_EXECUTE_PROPERTY
},
138 { B_DIRECT_SPECIFIER
, 0 },
139 B_TRANSLATE("Toggle mute."), 0
141 { B_TRANSLATE("Volume"), { B_GET_PROPERTY
, B_SET_PROPERTY
, 0 },
142 { B_DIRECT_SPECIFIER
, 0 },
143 B_TRANSLATE("Gets/sets the volume (0.0-2.0)."), 0,
146 { B_TRANSLATE("URI"), { B_GET_PROPERTY
, 0 },
147 { B_DIRECT_SPECIFIER
, 0 },
148 B_TRANSLATE("Gets the URI of the currently playing item."), 0,
151 { B_TRANSLATE("ToggleFullscreen"), { B_EXECUTE_PROPERTY
},
152 { B_DIRECT_SPECIFIER
, 0 },
153 B_TRANSLATE("Toggle fullscreen."), 0
155 { B_TRANSLATE("Duration"), { B_GET_PROPERTY
, 0 },
156 { B_DIRECT_SPECIFIER
, 0 },
157 B_TRANSLATE("Gets the duration of the currently playing item "
158 "in microseconds."), 0,
161 { B_TRANSLATE("Position"), { B_GET_PROPERTY
, B_SET_PROPERTY
, 0 },
162 { B_DIRECT_SPECIFIER
, 0 },
163 B_TRANSLATE("Gets/sets the current playing position in microseconds."),
166 { B_TRANSLATE("Seek"), { B_SET_PROPERTY
},
167 { B_DIRECT_SPECIFIER
, 0 },
168 B_TRANSLATE("Seek by the specified amounts of microseconds."), 0,
176 static const char* kRatingAttrName
= "Media:Rating";
178 static const char* kDisabledSeekMessage
= B_TRANSLATE("Drop files to play");
180 static const char* kApplicationName
= B_TRANSLATE_SYSTEM_NAME(NAME
);
183 MainWin::MainWin(bool isFirstWindow
, BMessage
* message
)
185 BWindow(BRect(100, 100, 400, 300), kApplicationName
, B_TITLED_WINDOW
,
186 B_ASYNCHRONOUS_CONTROLS
),
187 fCreationTime(system_time()),
189 fPlaylistWindow(NULL
),
193 fPlaylist(new Playlist
),
194 fPlaylistObserver(new PlaylistObserver(this)),
195 fController(new Controller
),
196 fControllerObserver(new ControllerObserver(this,
197 OBSERVE_FILE_CHANGES
| OBSERVE_TRACK_CHANGES
198 | OBSERVE_PLAYBACK_STATE_CHANGES
| OBSERVE_POSITION_CHANGES
199 | OBSERVE_VOLUME_CHANGES
)),
200 fIsFullscreen(false),
203 fShowsFullscreenControls(false),
211 fMouseDownTracking(false),
213 fLastMouseMovedTime(system_time()),
216 fGlobalSettingsListener(this),
217 fInitialSeekPosition(0),
220 // Handle window position and size depending on whether this is the
221 // first window or not. Use the window size from the window that was
222 // last resized by the user.
224 MoveBy(pos
* 25, pos
* 25);
225 pos
= (pos
+ 1) % 15;
227 BRect frame
= Settings::Default()->AudioPlayerWindowFrame();
228 if (frame
.IsValid()) {
230 if (message
== NULL
) {
231 MoveTo(frame
.LeftTop());
232 ResizeTo(frame
.Width(), frame
.Height());
234 // Delay moving to the initial position, since we don't
235 // know if we will be playing audio at all.
236 message
->AddRect("window frame", frame
);
239 if (sNoVideoWidth
== MIN_WIDTH
)
240 sNoVideoWidth
= frame
.IntegerWidth();
241 } else if (sNoVideoWidth
> MIN_WIDTH
) {
242 ResizeTo(sNoVideoWidth
, Bounds().Height());
244 fNoVideoWidth
= sNoVideoWidth
;
246 BRect rect
= Bounds();
249 fBackground
= new BView(rect
, "background", B_FOLLOW_ALL
,
250 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
);
251 fBackground
->SetViewColor(0, 0, 0);
252 AddChild(fBackground
);
255 fMenuBar
= new BMenuBar(fBackground
->Bounds(), "menu");
257 fBackground
->AddChild(fMenuBar
);
258 fMenuBar
->SetResizingMode(B_FOLLOW_NONE
);
259 fMenuBar
->ResizeToPreferred();
260 fMenuBarWidth
= (int)fMenuBar
->Frame().Width() + 1;
261 fMenuBarHeight
= (int)fMenuBar
->Frame().Height() + 1;
264 rect
= BRect(0, fMenuBarHeight
, fBackground
->Bounds().right
,
265 fMenuBarHeight
+ 10);
266 fVideoView
= new VideoView(rect
, "video display", B_FOLLOW_NONE
);
267 fBackground
->AddChild(fVideoView
);
270 rect
= BRect(0, fMenuBarHeight
+ 11, fBackground
->Bounds().right
,
271 fBackground
->Bounds().bottom
);
272 fControls
= new ControllerView(rect
, fController
, fPlaylist
);
273 fBackground
->AddChild(fControls
);
274 fControls
->ResizeToPreferred();
275 fControlsHeight
= (int)fControls
->Frame().Height() + 1;
276 fControlsWidth
= (int)fControls
->Frame().Width() + 1;
277 fControls
->SetResizingMode(B_FOLLOW_BOTTOM
| B_FOLLOW_LEFT_RIGHT
);
278 fControls
->SetDisabledString(kDisabledSeekMessage
);
280 fPlaylist
->AddListener(fPlaylistObserver
);
281 fController
->SetVideoView(fVideoView
);
282 fController
->AddListener(fControllerObserver
);
283 PeakView
* peakView
= fControls
->GetPeakView();
284 peakView
->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION
);
285 fController
->SetPeakListener(peakView
);
289 // setup the playlist window now, we need to have it
290 // running for the undo/redo playlist editing
291 fPlaylistWindow
= new PlaylistWindow(BRect(150, 150, 500, 600), fPlaylist
,
293 fPlaylistWindow
->Hide();
294 fPlaylistWindow
->Show();
295 // this makes sure the window thread is running without
296 // showing the window just yet
298 Settings::Default()->AddListener(&fGlobalSettingsListener
);
299 _AdoptGlobalSettings();
301 AddShortcut('z', B_COMMAND_KEY
, new BMessage(B_UNDO
));
302 AddShortcut('y', B_COMMAND_KEY
, new BMessage(B_UNDO
));
303 AddShortcut('z', B_COMMAND_KEY
| B_SHIFT_KEY
, new BMessage(B_REDO
));
304 AddShortcut('y', B_COMMAND_KEY
| B_SHIFT_KEY
, new BMessage(B_REDO
));
310 PostMessage(message
);
312 BMediaRoster
* roster
= BMediaRoster::Roster();
313 roster
->StartWatching(BMessenger(this, this), B_MEDIA_SERVER_STARTED
);
314 roster
->StartWatching(BMessenger(this, this), B_MEDIA_SERVER_QUIT
);
320 // printf("MainWin::~MainWin\n");
322 BMediaRoster
* roster
= BMediaRoster::CurrentRoster();
323 roster
->StopWatching(BMessenger(this, this), B_MEDIA_SERVER_STARTED
);
324 roster
->StopWatching(BMessenger(this, this), B_MEDIA_SERVER_QUIT
);
326 Settings::Default()->RemoveListener(&fGlobalSettingsListener
);
327 fPlaylist
->RemoveListener(fPlaylistObserver
);
329 fController
->RemoveListener(fControllerObserver
);
330 fController
->SetPeakListener(NULL
);
331 fController
->SetVideoTarget(NULL
);
332 fController
->Unlock();
334 // give the views a chance to detach from any notifiers
335 // before we delete them
336 fBackground
->RemoveSelf();
339 if (fInfoWin
&& fInfoWin
->Lock())
342 if (fPlaylistWindow
&& fPlaylistWindow
->Lock())
343 fPlaylistWindow
->Quit();
348 // quit the Controller looper thread
349 thread_id controllerThread
= fController
->Thread();
350 fController
->PostMessage(B_QUIT_REQUESTED
);
352 wait_for_thread(controllerThread
, &exitValue
);
360 MainWin::FrameResized(float newWidth
, float newHeight
)
362 if (newWidth
!= Bounds().Width() || newHeight
!= Bounds().Height()) {
363 debugger("size wrong\n");
366 bool noMenu
= fNoInterface
|| fIsFullscreen
;
367 bool noControls
= fNoInterface
|| fIsFullscreen
;
369 // printf("FrameResized enter: newWidth %.0f, newHeight %.0f\n",
370 // newWidth, newHeight);
373 sNoVideoWidth
= fNoVideoWidth
= (int)newWidth
;
375 int maxVideoWidth
= int(newWidth
) + 1;
376 int maxVideoHeight
= int(newHeight
) + 1
377 - (noMenu
? 0 : fMenuBarHeight
)
378 - (noControls
? 0 : fControlsHeight
);
380 ASSERT(maxVideoHeight
>= 0);
385 if (!fMenuBar
->IsHidden(fMenuBar
))
388 fMenuBar
->MoveTo(0, y
);
389 fMenuBar
->ResizeTo(newWidth
, fMenuBarHeight
- 1);
390 if (fMenuBar
->IsHidden(fMenuBar
))
395 if (maxVideoHeight
== 0) {
396 if (!fVideoView
->IsHidden(fVideoView
))
399 _ResizeVideoView(0, y
, maxVideoWidth
, maxVideoHeight
);
400 if (fVideoView
->IsHidden(fVideoView
))
406 if (!fControls
->IsHidden(fControls
))
409 fControls
->MoveTo(0, y
);
410 fControls
->ResizeTo(newWidth
, fControlsHeight
- 1);
411 if (fControls
->IsHidden(fControls
))
413 // y += fControlsHeight;
416 // printf("FrameResized leave\n");
421 MainWin::Zoom(BPoint
/*position*/, float /*width*/, float /*height*/)
423 PostMessage(M_TOGGLE_FULLSCREEN
);
428 MainWin::DispatchMessage(BMessage
* msg
, BHandler
* handler
)
430 if ((msg
->what
== B_MOUSE_DOWN
)
431 && (handler
== fBackground
|| handler
== fVideoView
432 || handler
== fControls
)) {
433 _MouseDown(msg
, dynamic_cast<BView
*>(handler
));
436 if ((msg
->what
== B_MOUSE_MOVED
)
437 && (handler
== fBackground
|| handler
== fVideoView
438 || handler
== fControls
)) {
439 _MouseMoved(msg
, dynamic_cast<BView
*>(handler
));
442 if ((msg
->what
== B_MOUSE_UP
)
443 && (handler
== fBackground
|| handler
== fVideoView
)) {
447 if ((msg
->what
== B_KEY_DOWN
)
448 && (handler
== fBackground
|| handler
== fVideoView
)) {
449 // special case for PrintScreen key
450 if (msg
->FindInt32("key") == B_PRINT_KEY
) {
451 fVideoView
->OverlayScreenshotPrepare();
452 BWindow::DispatchMessage(msg
, handler
);
453 fVideoView
->OverlayScreenshotCleanup();
457 // every other key gets dispatched to our _KeyDown first
459 // it got handled, don't pass it on
464 BWindow::DispatchMessage(msg
, handler
);
469 MainWin::MessageReceived(BMessage
* msg
)
471 // msg->PrintToStream();
473 case B_EXECUTE_PROPERTY
:
477 BMessage
reply(B_REPLY
);
478 status_t result
= B_BAD_SCRIPT_SYNTAX
;
482 const char* property
;
484 if (msg
->GetCurrentSpecifier(&index
, &specifier
, &what
,
485 &property
) != B_OK
) {
486 return BWindow::MessageReceived(msg
);
489 BPropertyInfo
propertyInfo(sPropertyInfo
);
490 switch (propertyInfo
.FindMatch(msg
, index
, &specifier
, what
,
493 fControls
->SkipForward();
498 fControls
->SkipBackward();
513 fController
->Pause();
518 fController
->TogglePlaying();
523 fController
->ToggleMute();
529 if (msg
->what
== B_GET_PROPERTY
) {
530 result
= reply
.AddFloat("result",
531 fController
->Volume());
532 } else if (msg
->what
== B_SET_PROPERTY
) {
534 result
= msg
->FindFloat("data", &newVolume
);
536 fController
->SetVolume(newVolume
);
543 if (msg
->what
== B_GET_PROPERTY
) {
544 BAutolock
_(fPlaylist
);
545 const PlaylistItem
* item
= fController
->Item();
551 result
= reply
.AddString("result", item
->LocationURI());
557 PostMessage(M_TOGGLE_FULLSCREEN
);
561 if (msg
->what
!= B_GET_PROPERTY
)
564 result
= reply
.AddInt64("result",
565 fController
->TimeDuration());
570 if (msg
->what
== B_GET_PROPERTY
) {
571 result
= reply
.AddInt64("result",
572 fController
->TimePosition());
573 } else if (msg
->what
== B_SET_PROPERTY
) {
575 result
= msg
->FindInt64("data", &newTime
);
577 fController
->SetTimePosition(newTime
);
585 if (msg
->what
!= B_SET_PROPERTY
)
589 result
= msg
->FindInt64("data", &seekBy
);
598 return BWindow::MessageReceived(msg
);
601 if (result
!= B_OK
) {
602 reply
.what
= B_MESSAGE_NOT_UNDERSTOOD
;
603 reply
.AddString("message", strerror(result
));
604 reply
.AddInt32("error", result
);
607 msg
->SendReply(&reply
);
611 case B_REFS_RECEIVED
:
617 if (msg
->HasRef("refs"))
620 case M_OPEN_PREVIOUS_PLAYLIST
:
626 fPlaylistWindow
->PostMessage(msg
);
629 case B_MEDIA_SERVER_STARTED
:
631 printf("TODO: implement B_MEDIA_SERVER_STARTED\n");
633 // BAutolock _(fPlaylist);
634 // BMessage fakePlaylistMessage(MSG_PLAYLIST_CURRENT_ITEM_CHANGED);
635 // fakePlaylistMessage.AddInt32("index",
636 // fPlaylist->CurrentItemIndex());
637 // PostMessage(&fakePlaylistMessage);
641 case B_MEDIA_SERVER_QUIT
:
642 printf("TODO: implement B_MEDIA_SERVER_QUIT\n");
643 // if (fController->Lock()) {
644 // fController->CleanupNodes();
645 // fController->Unlock();
649 // PlaylistObserver messages
650 case MSG_PLAYLIST_ITEM_ADDED
:
654 if (msg
->FindPointer("item", (void**)&item
) == B_OK
655 && msg
->FindInt32("index", &index
) == B_OK
) {
656 _AddPlaylistItem(item
, index
);
660 case MSG_PLAYLIST_ITEM_REMOVED
:
663 if (msg
->FindInt32("index", &index
) == B_OK
)
664 _RemovePlaylistItem(index
);
667 case MSG_PLAYLIST_CURRENT_ITEM_CHANGED
:
669 BAutolock
_(fPlaylist
);
672 // if false, the message was meant to only update the GUI
674 if (msg
->FindBool("play", &play
) < B_OK
|| !play
)
676 if (msg
->FindInt32("index", &index
) < B_OK
677 || index
!= fPlaylist
->CurrentItemIndex())
679 PlaylistItemRef
item(fPlaylist
->ItemAt(index
));
680 if (item
.Get() != NULL
) {
681 printf("open playlist item: %s\n", item
->Name().String());
682 OpenPlaylistItem(item
);
683 _MarkPlaylistItem(index
);
687 case MSG_PLAYLIST_IMPORT_FAILED
:
689 BAlert
* alert
= new BAlert(B_TRANSLATE("Nothing to Play"),
690 B_TRANSLATE("None of the files you wanted to play appear "
691 "to be media files."), B_TRANSLATE("OK"));
692 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
694 fControls
->SetDisabledString(kDisabledSeekMessage
);
698 // ControllerObserver messages
699 case MSG_CONTROLLER_FILE_FINISHED
:
701 BAutolock
_(fPlaylist
);
703 bool hadNext
= fPlaylist
->SetCurrentItemIndex(
704 fPlaylist
->CurrentItemIndex() + 1);
706 // Reached end of playlist
707 // Handle "quit when done" settings
708 if ((fHasVideo
&& fCloseWhenDonePlayingMovie
)
709 || (!fHasVideo
&& fCloseWhenDonePlayingSound
))
710 PostMessage(B_QUIT_REQUESTED
);
711 // Handle "loop by default" settings
712 if ((fHasVideo
&& fLoopMovies
)
713 || (!fHasVideo
&& fLoopSounds
)) {
714 if (fPlaylist
->CountItems() > 1)
715 fPlaylist
->SetCurrentItemIndex(0);
722 case MSG_CONTROLLER_FILE_CHANGED
:
724 status_t result
= B_ERROR
;
725 msg
->FindInt32("result", &result
);
726 PlaylistItemRef itemRef
;
728 if (msg
->FindPointer("item", (void**)&item
) == B_OK
) {
729 itemRef
.SetTo(item
, true);
730 // The reference was passed along with the message.
732 BAutolock
_(fPlaylist
);
733 itemRef
.SetTo(fPlaylist
->ItemAt(
734 fPlaylist
->CurrentItemIndex()));
736 _PlaylistItemOpened(itemRef
, result
);
739 case MSG_CONTROLLER_VIDEO_TRACK_CHANGED
:
742 if (msg
->FindInt32("index", &index
) == B_OK
) {
744 while (BMenuItem
* item
= fVideoTrackMenu
->ItemAt(i
)) {
745 item
->SetMarked(i
== index
);
751 case MSG_CONTROLLER_AUDIO_TRACK_CHANGED
:
754 if (msg
->FindInt32("index", &index
) == B_OK
) {
756 while (BMenuItem
* item
= fAudioTrackMenu
->ItemAt(i
)) {
757 item
->SetMarked(i
== index
);
760 _UpdateAudioChannelCount(index
);
764 case MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED
:
767 if (msg
->FindInt32("index", &index
) == B_OK
) {
769 while (BMenuItem
* item
= fSubTitleTrackMenu
->ItemAt(i
)) {
770 BMessage
* message
= item
->Message();
771 if (message
!= NULL
) {
772 item
->SetMarked((int32
)message
->what
773 - M_SELECT_SUB_TITLE_TRACK
== index
);
780 case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED
:
783 if (msg
->FindInt32("state", (int32
*)&state
) == B_OK
)
784 fControls
->SetPlaybackState(state
);
787 case MSG_CONTROLLER_POSITION_CHANGED
:
790 if (msg
->FindFloat("position", &position
) == B_OK
) {
791 fControls
->SetPosition(position
, fController
->TimePosition(),
792 fController
->TimeDuration());
793 fAllowWinding
= true;
797 case MSG_CONTROLLER_SEEK_HANDLED
:
800 case MSG_CONTROLLER_VOLUME_CHANGED
:
803 if (msg
->FindFloat("volume", &volume
) == B_OK
)
804 fControls
->SetVolume(volume
);
807 case MSG_CONTROLLER_MUTED_CHANGED
:
810 if (msg
->FindBool("muted", &muted
) == B_OK
)
811 fControls
->SetMuted(muted
);
815 // menu item messages
818 BMessenger
target(this);
819 BMessage
result(B_REFS_RECEIVED
);
820 BMessage
appMessage(M_SHOW_OPEN_PANEL
);
821 appMessage
.AddMessenger("target", target
);
822 appMessage
.AddMessage("message", &result
);
823 appMessage
.AddString("title", B_TRANSLATE("Open clips"));
824 appMessage
.AddString("label", B_TRANSLATE("Open"));
825 be_app
->PostMessage(&appMessage
);
829 case M_NETWORK_STREAM_OPEN
:
831 BMessenger
target(this);
832 NetworkStreamWin
* win
= new NetworkStreamWin(target
);
840 case M_FILE_PLAYLIST
:
841 ShowPlaylistWindow();
844 PostMessage(B_QUIT_REQUESTED
);
847 be_app
->PostMessage(B_QUIT_REQUESTED
);
850 case M_TOGGLE_FULLSCREEN
:
854 case M_TOGGLE_ALWAYS_ON_TOP
:
855 _ToggleAlwaysOnTop();
858 case M_TOGGLE_NO_INTERFACE
:
859 _ToggleNoInterface();
865 if (msg
->FindInt32("size", &size
) == B_OK
) {
876 case B_ACQUIRE_OVERLAY_LOCK:
877 printf("B_ACQUIRE_OVERLAY_LOCK\n");
878 fVideoView->OverlayLockAcquire();
881 case B_RELEASE_OVERLAY_LOCK:
882 printf("B_RELEASE_OVERLAY_LOCK\n");
883 fVideoView->OverlayLockRelease();
886 case B_MOUSE_WHEEL_CHANGED
:
888 float dx
= msg
->FindFloat("be:wheel_delta_x");
889 float dy
= msg
->FindFloat("be:wheel_delta_y");
890 bool inv
= modifiers() & B_COMMAND_KEY
;
892 PostMessage(inv
? M_VOLUME_DOWN
: M_SKIP_PREV
);
894 PostMessage(inv
? M_VOLUME_UP
: M_SKIP_NEXT
);
896 PostMessage(inv
? M_SKIP_PREV
: M_VOLUME_DOWN
);
898 PostMessage(inv
? M_SKIP_NEXT
: M_VOLUME_UP
);
903 fControls
->SkipForward();
907 fControls
->SkipBackward();
914 if (msg
->FindInt64("how much", &howMuch
) != B_OK
915 || msg
->FindInt64("frames", &frames
) != B_OK
) {
919 _Wind(howMuch
, frames
);
924 fController
->VolumeUp();
928 fController
->VolumeDown();
931 case M_ASPECT_SAME_AS_SOURCE
:
937 fController
->GetSize(&width
, &height
,
938 &widthAspect
, &heightAspect
);
939 VideoFormatChange(width
, height
, widthAspect
, heightAspect
);
943 case M_ASPECT_NO_DISTORTION
:
947 fController
->GetSize(&width
, &height
);
948 VideoFormatChange(width
, height
, width
, height
);
953 VideoAspectChange(4, 3);
956 case M_ASPECT_16_9
: // 1.77 : 1
957 VideoAspectChange(16, 9);
960 case M_ASPECT_83_50
: // 1.66 : 1
961 VideoAspectChange(83, 50);
964 case M_ASPECT_7_4
: // 1.75 : 1
965 VideoAspectChange(7, 4);
968 case M_ASPECT_37_20
: // 1.85 : 1
969 VideoAspectChange(37, 20);
972 case M_ASPECT_47_20
: // 2.35 : 1
973 VideoAspectChange(47, 20);
976 case M_SET_PLAYLIST_POSITION
:
978 BAutolock
_(fPlaylist
);
981 if (msg
->FindInt32("index", &index
) == B_OK
)
982 fPlaylist
->SetCurrentItemIndex(index
);
986 case MSG_OBJECT_CHANGED
:
987 // received from fGlobalSettingsListener
988 // TODO: find out which object, if we ever watch more than
989 // the global settings instance...
990 _AdoptGlobalSettings();
993 case M_SLIDE_CONTROLS
:
996 if (msg
->FindFloat("offset", &offset
) == B_OK
) {
997 fControls
->MoveBy(0, offset
);
998 fVideoView
->SetSubTitleMaxBottom(fControls
->Frame().top
- 1);
1004 case M_FINISH_SLIDING_CONTROLS
:
1008 if (msg
->FindFloat("offset", &offset
) == B_OK
1009 && msg
->FindBool("show", &show
) == B_OK
) {
1011 fControls
->MoveTo(fControls
->Frame().left
, offset
);
1012 fVideoView
->SetSubTitleMaxBottom(offset
- 1);
1014 fVideoView
->SetSubTitleMaxBottom(
1015 fVideoView
->Bounds().bottom
);
1016 fControls
->RemoveSelf();
1017 fControls
->MoveTo(fVideoView
->Frame().left
,
1018 fVideoView
->Frame().bottom
+ 1);
1019 fBackground
->AddChild(fControls
);
1020 fControls
->SetSymbolScale(1.0f
);
1021 while (!fControls
->IsHidden())
1027 case M_HIDE_FULL_SCREEN_CONTROLS
:
1028 if (fIsFullscreen
) {
1029 BPoint videoViewWhere
;
1030 if (msg
->FindPoint("where", &videoViewWhere
) == B_OK
) {
1031 if (msg
->FindBool("force")
1032 || !fControls
->Frame().Contains(videoViewWhere
)) {
1033 _ShowFullscreenControls(false);
1034 // hide the mouse cursor until the user moves it
1035 be_app
->ObscureCursor();
1044 if (msg
->FindInt32("rating", &rating
) == B_OK
)
1050 if (msg
->what
>= M_SELECT_AUDIO_TRACK
1051 && msg
->what
<= M_SELECT_AUDIO_TRACK_END
) {
1052 fController
->SelectAudioTrack(msg
->what
- M_SELECT_AUDIO_TRACK
);
1055 if (msg
->what
>= M_SELECT_VIDEO_TRACK
1056 && msg
->what
<= M_SELECT_VIDEO_TRACK_END
) {
1057 fController
->SelectVideoTrack(msg
->what
- M_SELECT_VIDEO_TRACK
);
1060 if ((int32
)msg
->what
>= M_SELECT_SUB_TITLE_TRACK
- 1
1061 && msg
->what
<= M_SELECT_SUB_TITLE_TRACK_END
) {
1062 fController
->SelectSubTitleTrack((int32
)msg
->what
1063 - M_SELECT_SUB_TITLE_TRACK
);
1066 // let BWindow handle the rest
1067 BWindow::MessageReceived(msg
);
1073 MainWin::WindowActivated(bool active
)
1075 fController
->PlayerActivated(active
);
1080 MainWin::QuitRequested()
1082 BMessage
message(M_PLAYER_QUIT
);
1083 GetQuitMessage(&message
);
1084 be_app
->PostMessage(&message
);
1090 MainWin::MenusBeginning()
1092 _SetupVideoAspectItems(fVideoAspectMenu
);
1100 MainWin::OpenPlaylist(const BMessage
* playlistArchive
)
1102 if (playlistArchive
== NULL
)
1106 BAutolock
playlistLocker(fPlaylist
);
1108 if (fPlaylist
->Unarchive(playlistArchive
) != B_OK
)
1112 if (playlistArchive
->FindInt32("index", ¤tIndex
) != B_OK
)
1114 fPlaylist
->SetCurrentItemIndex(currentIndex
);
1116 playlistLocker
.Unlock();
1118 if (currentIndex
!= -1) {
1119 // Restore the current play position only if we have something to play
1120 playlistArchive
->FindInt64("position", (int64
*)&fInitialSeekPosition
);
1129 MainWin::OpenPlaylistItem(const PlaylistItemRef
& item
)
1131 status_t ret
= fController
->SetToAsync(item
);
1133 fprintf(stderr
, "MainWin::OpenPlaylistItem() - Failed to send message "
1134 "to Controller.\n");
1135 BString message
= B_TRANSLATE("%app% encountered an internal error. "
1136 "The file could not be opened.");
1137 message
.ReplaceFirst("%app%", kApplicationName
);
1138 BAlert
* alert
= new BAlert(kApplicationName
, message
.String(),
1139 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
1140 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1142 _PlaylistItemOpened(item
, ret
);
1145 string
<< "Opening '" << item
->Name() << "'.";
1146 fControls
->SetDisabledString(string
.String());
1152 MainWin::ShowFileInfo()
1155 fInfoWin
= new InfoWin(Frame().LeftTop(), fController
);
1157 if (fInfoWin
->Lock()) {
1158 if (fInfoWin
->IsHidden())
1161 fInfoWin
->Activate();
1168 MainWin::ShowPlaylistWindow()
1170 if (fPlaylistWindow
->Lock()) {
1171 // make sure the window shows on the same workspace as ourself
1172 uint32 workspaces
= Workspaces();
1173 if (fPlaylistWindow
->Workspaces() != workspaces
)
1174 fPlaylistWindow
->SetWorkspaces(workspaces
);
1177 if (fPlaylistWindow
->IsHidden())
1178 fPlaylistWindow
->Show();
1180 fPlaylistWindow
->Activate();
1182 fPlaylistWindow
->Unlock();
1188 MainWin::VideoAspectChange(int forcedWidth
, int forcedHeight
, float widthScale
)
1190 // Force specific source size and pixel width scale.
1194 fController
->GetSize(&width
, &height
);
1195 VideoFormatChange(forcedWidth
, forcedHeight
,
1196 lround(width
* widthScale
), height
);
1202 MainWin::VideoAspectChange(float widthScale
)
1204 // Called when video aspect ratio changes and the original
1205 // width/height should be restored too, display aspect is not known,
1206 // only pixel width scale.
1210 fController
->GetSize(&width
, &height
);
1211 VideoFormatChange(width
, height
, lround(width
* widthScale
), height
);
1217 MainWin::VideoAspectChange(int widthAspect
, int heightAspect
)
1219 // Called when video aspect ratio changes and the original
1220 // width/height should be restored too.
1224 fController
->GetSize(&width
, &height
);
1225 VideoFormatChange(width
, height
, widthAspect
, heightAspect
);
1231 MainWin::VideoFormatChange(int width
, int height
, int widthAspect
,
1234 // Called when video format or aspect ratio changes.
1236 printf("VideoFormatChange enter: width %d, height %d, "
1237 "aspect ratio: %d:%d\n", width
, height
, widthAspect
, heightAspect
);
1239 // remember current view scale
1240 int percent
= _CurrentVideoSizeInPercent();
1242 fSourceWidth
= width
;
1243 fSourceHeight
= height
;
1244 fWidthAspect
= widthAspect
;
1245 fHeightAspect
= heightAspect
;
1250 FrameResized(Bounds().Width(), Bounds().Height());
1252 printf("VideoFormatChange leave\n");
1257 MainWin::GetQuitMessage(BMessage
* message
)
1259 message
->AddPointer("instance", this);
1260 message
->AddRect("window frame", Frame());
1261 message
->AddBool("audio only", !fHasVideo
);
1262 message
->AddInt64("creation time", fCreationTime
);
1264 if (!fHasVideo
&& fHasAudio
) {
1265 // store playlist, current index and position if this is audio
1266 BMessage playlistArchive
;
1268 BAutolock
controllerLocker(fController
);
1269 playlistArchive
.AddInt64("position", fController
->TimePosition());
1270 controllerLocker
.Unlock();
1275 BAutolock
playlistLocker(fPlaylist
);
1276 if (fPlaylist
->Archive(&playlistArchive
) != B_OK
1277 || playlistArchive
.AddInt32("index",
1278 fPlaylist
->CurrentItemIndex()) != B_OK
1279 || message
->AddMessage("playlist", &playlistArchive
) != B_OK
) {
1280 fprintf(stderr
, "Failed to store current playlist.\n");
1287 MainWin::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
1288 int32 what
, const char* property
)
1290 BPropertyInfo
propertyInfo(sPropertyInfo
);
1291 switch (propertyInfo
.FindMatch(message
, index
, specifier
, what
, property
)) {
1308 return BWindow::ResolveSpecifier(message
, index
, specifier
, what
, property
);
1313 MainWin::GetSupportedSuites(BMessage
* data
)
1318 status_t status
= data
->AddString("suites", "suite/vnd.Haiku-MediaPlayer");
1322 BPropertyInfo
propertyInfo(sPropertyInfo
);
1323 status
= data
->AddFlat("messages", &propertyInfo
);
1327 return BWindow::GetSupportedSuites(data
);
1335 MainWin::_RefsReceived(BMessage
* message
)
1337 // the playlist is replaced by dropped files
1338 // or the dropped files are appended to the end
1339 // of the existing playlist if <shift> is pressed
1340 bool append
= false;
1341 if (message
->FindBool("append to playlist", &append
) != B_OK
)
1342 append
= modifiers() & B_SHIFT_KEY
;
1344 BAutolock
_(fPlaylist
);
1345 int32 appendIndex
= append
? APPEND_INDEX_APPEND_LAST
1346 : APPEND_INDEX_REPLACE_PLAYLIST
;
1347 message
->AddInt32("append_index", appendIndex
);
1349 // forward the message to the playlist window,
1350 // so that undo/redo is used for modifying the playlist
1351 fPlaylistWindow
->PostMessage(message
);
1353 if (message
->FindRect("window frame", &fNoVideoFrame
) != B_OK
)
1354 fNoVideoFrame
= BRect();
1359 MainWin::_PlaylistItemOpened(const PlaylistItemRef
& item
, status_t result
)
1361 if (result
!= B_OK
) {
1362 BAutolock
_(fPlaylist
);
1364 item
->SetPlaybackFailed();
1365 bool allItemsFailed
= true;
1366 int32 count
= fPlaylist
->CountItems();
1367 for (int32 i
= 0; i
< count
; i
++) {
1368 if (!fPlaylist
->ItemAtFast(i
)->PlaybackFailed()) {
1369 allItemsFailed
= false;
1374 if (allItemsFailed
) {
1375 // Display error if all files failed to play.
1376 BString
message(B_TRANSLATE(
1377 "The file '%filename' could not be opened.\n\n"));;
1378 message
.ReplaceAll("%filename", item
->Name());
1380 if (result
== B_MEDIA_NO_HANDLER
) {
1381 // give a more detailed message for the most likely of all
1383 message
<< B_TRANSLATE(
1384 "There is no decoder installed to handle the "
1385 "file format, or the decoder has trouble with the "
1386 "specific version of the format.");
1388 message
<< B_TRANSLATE("Error: ") << strerror(result
);
1390 BAlert
* alert
= new BAlert("error", message
.String(),
1391 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
1392 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1394 fControls
->SetDisabledString(kDisabledSeekMessage
);
1396 // Just go to the next file and don't bother user (yet)
1397 fPlaylist
->SetCurrentItemIndex(fPlaylist
->CurrentItemIndex() + 1);
1403 SetTitle(kApplicationName
);
1406 fHasVideo
= fController
->VideoTrackCount() != 0;
1407 fHasAudio
= fController
->AudioTrackCount() != 0;
1408 SetTitle(item
->Name().String());
1410 if (fInitialSeekPosition
< 0) {
1411 fInitialSeekPosition
1412 = fController
->TimeDuration() + fInitialSeekPosition
;
1414 fController
->SetTimePosition(fInitialSeekPosition
);
1415 fInitialSeekPosition
= 0;
1420 _UpdatePlaylistItemFile();
1425 MainWin::_SetupWindow()
1427 // printf("MainWin::_SetupWindow\n");
1428 // Populate the track menus
1429 _SetupTrackMenus(fAudioTrackMenu
, fVideoTrackMenu
, fSubTitleTrackMenu
);
1430 _UpdateAudioChannelCount(fController
->CurrentAudioTrack());
1432 fVideoMenu
->SetEnabled(fHasVideo
);
1433 fAudioMenu
->SetEnabled(fHasAudio
);
1434 int previousSourceWidth
= fSourceWidth
;
1435 int previousSourceHeight
= fSourceHeight
;
1436 int previousWidthAspect
= fWidthAspect
;
1437 int previousHeightAspect
= fHeightAspect
;
1439 fController
->GetSize(&fSourceWidth
, &fSourceHeight
,
1440 &fWidthAspect
, &fHeightAspect
);
1447 _UpdateControlsEnabledStatus();
1449 // Adopt the size and window layout if necessary
1450 if (previousSourceWidth
!= fSourceWidth
1451 || previousSourceHeight
!= fSourceHeight
1452 || previousWidthAspect
!= fWidthAspect
1453 || previousHeightAspect
!= fHeightAspect
) {
1455 _SetWindowSizeLimits();
1457 if (!fIsFullscreen
) {
1458 // Resize to 100% but stay on screen
1459 _ResizeWindow(100, !fHasVideo
, true);
1461 // Make sure we relayout the video view when in full screen mode
1462 FrameResized(Frame().Width(), Frame().Height());
1468 fVideoView
->MakeFocus();
1473 MainWin::_CreateMenu()
1475 fFileMenu
= new BMenu(kApplicationName
);
1476 fPlaylistMenu
= new BMenu(B_TRANSLATE("Playlist" B_UTF8_ELLIPSIS
));
1477 fAudioMenu
= new BMenu(B_TRANSLATE("Audio"));
1478 fVideoMenu
= new BMenu(B_TRANSLATE("Video"));
1479 fVideoAspectMenu
= new BMenu(B_TRANSLATE("Aspect ratio"));
1480 fAudioTrackMenu
= new BMenu(B_TRANSLATE_CONTEXT("Track",
1481 "Audio Track Menu"));
1482 fVideoTrackMenu
= new BMenu(B_TRANSLATE_CONTEXT("Track",
1483 "Video Track Menu"));
1484 fSubTitleTrackMenu
= new BMenu(B_TRANSLATE("Subtitles"));
1485 fAttributesMenu
= new BMenu(B_TRANSLATE("Attributes"));
1487 fMenuBar
->AddItem(fFileMenu
);
1488 fMenuBar
->AddItem(fAudioMenu
);
1489 fMenuBar
->AddItem(fVideoMenu
);
1490 fMenuBar
->AddItem(fAttributesMenu
);
1492 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("New player" B_UTF8_ELLIPSIS
),
1493 new BMessage(M_NEW_PLAYER
), 'N');
1494 fFileMenu
->AddItem(item
);
1495 item
->SetTarget(be_app
);
1497 // Add recent files to "Open File" entry as sub-menu.
1498 BRecentFilesList
recentFiles(10, false, NULL
, kAppSig
);
1499 item
= new BMenuItem(recentFiles
.NewFileListMenu(
1500 B_TRANSLATE("Open file" B_UTF8_ELLIPSIS
), NULL
, NULL
, this, 10, true,
1501 NULL
, kAppSig
), new BMessage(M_FILE_OPEN
));
1502 item
->SetShortcut('O', 0);
1503 fFileMenu
->AddItem(item
);
1505 item
= new BMenuItem(B_TRANSLATE("Open network stream"),
1506 new BMessage(M_NETWORK_STREAM_OPEN
));
1507 fFileMenu
->AddItem(item
);
1509 fFileMenu
->AddSeparatorItem();
1511 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("File info" B_UTF8_ELLIPSIS
),
1512 new BMessage(M_FILE_INFO
), 'I'));
1513 fFileMenu
->AddItem(fPlaylistMenu
);
1514 fPlaylistMenu
->Superitem()->SetShortcut('P', B_COMMAND_KEY
);
1515 fPlaylistMenu
->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST
));
1517 fFileMenu
->AddSeparatorItem();
1519 fNoInterfaceMenuItem
= new BMenuItem(B_TRANSLATE("Hide interface"),
1520 new BMessage(M_TOGGLE_NO_INTERFACE
), 'H');
1521 fFileMenu
->AddItem(fNoInterfaceMenuItem
);
1522 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Always on top"),
1523 new BMessage(M_TOGGLE_ALWAYS_ON_TOP
), 'A'));
1525 item
= new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS
),
1526 new BMessage(M_SETTINGS
), 'S');
1527 fFileMenu
->AddItem(item
);
1528 item
->SetTarget(be_app
);
1530 fFileMenu
->AddSeparatorItem();
1532 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1533 new BMessage(M_FILE_CLOSE
), 'W'));
1534 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1535 new BMessage(M_FILE_QUIT
), 'Q'));
1537 fPlaylistMenu
->SetRadioMode(true);
1539 fAudioMenu
->AddItem(fAudioTrackMenu
);
1541 fVideoMenu
->AddItem(fVideoTrackMenu
);
1542 fVideoMenu
->AddItem(fSubTitleTrackMenu
);
1543 fVideoMenu
->AddSeparatorItem();
1544 BMessage
* resizeMessage
= new BMessage(M_VIEW_SIZE
);
1545 resizeMessage
->AddInt32("size", 50);
1546 fVideoMenu
->AddItem(new BMenuItem(
1547 B_TRANSLATE("50% scale"), resizeMessage
, '0'));
1549 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1550 resizeMessage
->AddInt32("size", 100);
1551 fVideoMenu
->AddItem(new BMenuItem(
1552 B_TRANSLATE("100% scale"), resizeMessage
, '1'));
1554 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1555 resizeMessage
->AddInt32("size", 200);
1556 fVideoMenu
->AddItem(new BMenuItem(
1557 B_TRANSLATE("200% scale"), resizeMessage
, '2'));
1559 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1560 resizeMessage
->AddInt32("size", 300);
1561 fVideoMenu
->AddItem(new BMenuItem(
1562 B_TRANSLATE("300% scale"), resizeMessage
, '3'));
1564 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1565 resizeMessage
->AddInt32("size", 400);
1566 fVideoMenu
->AddItem(new BMenuItem(
1567 B_TRANSLATE("400% scale"), resizeMessage
, '4'));
1569 fVideoMenu
->AddSeparatorItem();
1571 fVideoMenu
->AddItem(new BMenuItem(B_TRANSLATE("Full screen"),
1572 new BMessage(M_TOGGLE_FULLSCREEN
), B_ENTER
));
1574 fVideoMenu
->AddSeparatorItem();
1576 _SetupVideoAspectItems(fVideoAspectMenu
);
1577 fVideoMenu
->AddItem(fVideoAspectMenu
);
1579 fRatingMenu
= new BMenu(B_TRANSLATE("Rating"));
1580 fAttributesMenu
->AddItem(fRatingMenu
);
1581 for (int32 i
= 1; i
<= 10; i
++) {
1583 snprintf(label
, sizeof(label
), "%" B_PRId32
, i
);
1584 BMessage
* setRatingMsg
= new BMessage(M_SET_RATING
);
1585 setRatingMsg
->AddInt32("rating", i
);
1586 fRatingMenu
->AddItem(new BMenuItem(label
, setRatingMsg
));
1592 MainWin::_SetupVideoAspectItems(BMenu
* menu
)
1595 while ((item
= menu
->RemoveItem((int32
)0)) != NULL
)
1602 fController
->GetSize(&width
, &height
, &widthAspect
, &heightAspect
);
1603 // We don't care if there is a video track at all. In that
1604 // case we should end up not marking any item.
1606 // NOTE: The item marking may end up marking for example both
1607 // "Stream Settings" and "16 : 9" if the stream settings happen to
1610 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Stream settings"),
1611 new BMessage(M_ASPECT_SAME_AS_SOURCE
), '1', B_SHIFT_KEY
));
1612 item
->SetMarked(widthAspect
== fWidthAspect
1613 && heightAspect
== fHeightAspect
);
1615 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("No aspect correction"),
1616 new BMessage(M_ASPECT_NO_DISTORTION
), '0', B_SHIFT_KEY
));
1617 item
->SetMarked(width
== fWidthAspect
&& height
== fHeightAspect
);
1619 menu
->AddSeparatorItem();
1621 menu
->AddItem(item
= new BMenuItem("4 : 3",
1622 new BMessage(M_ASPECT_4_3
), 2, B_SHIFT_KEY
));
1623 item
->SetMarked(fWidthAspect
== 4 && fHeightAspect
== 3);
1624 menu
->AddItem(item
= new BMenuItem("16 : 9",
1625 new BMessage(M_ASPECT_16_9
), 3, B_SHIFT_KEY
));
1626 item
->SetMarked(fWidthAspect
== 16 && fHeightAspect
== 9);
1628 menu
->AddSeparatorItem();
1630 menu
->AddItem(item
= new BMenuItem("1.66 : 1",
1631 new BMessage(M_ASPECT_83_50
)));
1632 item
->SetMarked(fWidthAspect
== 83 && fHeightAspect
== 50);
1633 menu
->AddItem(item
= new BMenuItem("1.75 : 1",
1634 new BMessage(M_ASPECT_7_4
)));
1635 item
->SetMarked(fWidthAspect
== 7 && fHeightAspect
== 4);
1636 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("1.85 : 1 (American)"),
1637 new BMessage(M_ASPECT_37_20
)));
1638 item
->SetMarked(fWidthAspect
== 37 && fHeightAspect
== 20);
1639 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("2.35 : 1 (Cinemascope)"),
1640 new BMessage(M_ASPECT_47_20
)));
1641 item
->SetMarked(fWidthAspect
== 47 && fHeightAspect
== 20);
1646 MainWin::_SetupTrackMenus(BMenu
* audioTrackMenu
, BMenu
* videoTrackMenu
,
1647 BMenu
* subTitleTrackMenu
)
1649 audioTrackMenu
->RemoveItems(0, audioTrackMenu
->CountItems(), true);
1650 videoTrackMenu
->RemoveItems(0, videoTrackMenu
->CountItems(), true);
1651 subTitleTrackMenu
->RemoveItems(0, subTitleTrackMenu
->CountItems(), true);
1655 int count
= fController
->AudioTrackCount();
1656 int current
= fController
->CurrentAudioTrack();
1657 for (int i
= 0; i
< count
; i
++) {
1659 const char* languageString
= NULL
;
1660 if (fController
->GetAudioMetaData(i
, &metaData
) == B_OK
)
1661 metaData
.FindString("language", &languageString
);
1662 if (languageString
!= NULL
) {
1663 BLanguage
language(languageString
);
1664 BString languageName
;
1665 if (language
.GetName(languageName
) == B_OK
)
1666 languageString
= languageName
.String();
1667 snprintf(s
, sizeof(s
), "%s", languageString
);
1669 snprintf(s
, sizeof(s
), B_TRANSLATE("Track %d"), i
+ 1);
1670 BMenuItem
* item
= new BMenuItem(s
,
1671 new BMessage(M_SELECT_AUDIO_TRACK
+ i
));
1672 item
->SetMarked(i
== current
);
1673 audioTrackMenu
->AddItem(item
);
1676 audioTrackMenu
->AddItem(new BMenuItem(B_TRANSLATE_CONTEXT("none",
1677 "Audio track menu"), new BMessage(M_DUMMY
)));
1678 audioTrackMenu
->ItemAt(0)->SetMarked(true);
1680 audioTrackMenu
->SetEnabled(count
> 1);
1682 count
= fController
->VideoTrackCount();
1683 current
= fController
->CurrentVideoTrack();
1684 for (int i
= 0; i
< count
; i
++) {
1685 snprintf(s
, sizeof(s
), B_TRANSLATE("Track %d"), i
+ 1);
1686 BMenuItem
* item
= new BMenuItem(s
,
1687 new BMessage(M_SELECT_VIDEO_TRACK
+ i
));
1688 item
->SetMarked(i
== current
);
1689 videoTrackMenu
->AddItem(item
);
1692 videoTrackMenu
->AddItem(new BMenuItem(B_TRANSLATE("none"),
1693 new BMessage(M_DUMMY
)));
1694 videoTrackMenu
->ItemAt(0)->SetMarked(true);
1696 videoTrackMenu
->SetEnabled(count
> 1);
1698 count
= fController
->SubTitleTrackCount();
1700 current
= fController
->CurrentSubTitleTrack();
1701 BMenuItem
* item
= new BMenuItem(
1702 B_TRANSLATE_CONTEXT("Off", "Subtitles menu"),
1703 new BMessage(M_SELECT_SUB_TITLE_TRACK
- 1));
1704 subTitleTrackMenu
->AddItem(item
);
1705 item
->SetMarked(current
== -1);
1707 subTitleTrackMenu
->AddSeparatorItem();
1709 for (int i
= 0; i
< count
; i
++) {
1710 const char* name
= fController
->SubTitleTrackName(i
);
1712 snprintf(s
, sizeof(s
), "%s", name
);
1714 snprintf(s
, sizeof(s
), B_TRANSLATE("Track %d"), i
+ 1);
1715 item
= new BMenuItem(s
,
1716 new BMessage(M_SELECT_SUB_TITLE_TRACK
+ i
));
1717 item
->SetMarked(i
== current
);
1718 subTitleTrackMenu
->AddItem(item
);
1721 subTitleTrackMenu
->AddItem(new BMenuItem(
1722 B_TRANSLATE_CONTEXT("none", "Subtitles menu"),
1723 new BMessage(M_DUMMY
)));
1724 subTitleTrackMenu
->ItemAt(0)->SetMarked(true);
1726 subTitleTrackMenu
->SetEnabled(count
> 0);
1731 MainWin::_UpdateAudioChannelCount(int32 audioTrackIndex
)
1733 fControls
->SetAudioChannelCount(fController
->AudioTrackChannelCount());
1738 MainWin::_GetMinimumWindowSize(int& width
, int& height
) const
1742 if (!fNoInterface
) {
1743 width
= max_c(width
, fMenuBarWidth
);
1744 width
= max_c(width
, fControlsWidth
);
1745 height
= fMenuBarHeight
+ fControlsHeight
;
1751 MainWin::_GetUnscaledVideoSize(int& videoWidth
, int& videoHeight
) const
1753 if (fWidthAspect
!= 0 && fHeightAspect
!= 0) {
1754 videoWidth
= fSourceHeight
* fWidthAspect
/ fHeightAspect
;
1755 videoHeight
= fSourceWidth
* fHeightAspect
/ fWidthAspect
;
1756 // Use the scaling which produces an enlarged view.
1757 if (videoWidth
> fSourceWidth
) {
1759 videoHeight
= fSourceHeight
;
1762 videoWidth
= fSourceWidth
;
1765 videoWidth
= fSourceWidth
;
1766 videoHeight
= fSourceHeight
;
1772 MainWin::_SetWindowSizeLimits()
1776 _GetMinimumWindowSize(minWidth
, minHeight
);
1777 SetSizeLimits(minWidth
- 1, 32000, minHeight
- 1,
1778 fHasVideo
? 32000 : minHeight
- 1);
1783 MainWin::_CurrentVideoSizeInPercent() const
1790 _GetUnscaledVideoSize(videoWidth
, videoHeight
);
1792 int viewWidth
= fVideoView
->Bounds().IntegerWidth() + 1;
1793 int viewHeight
= fVideoView
->Bounds().IntegerHeight() + 1;
1795 int widthPercent
= viewWidth
* 100 / videoWidth
;
1796 int heightPercent
= viewHeight
* 100 / videoHeight
;
1798 if (widthPercent
> heightPercent
)
1799 return widthPercent
;
1800 return heightPercent
;
1805 MainWin::_ZoomVideoView(int percentDiff
)
1810 int percent
= _CurrentVideoSizeInPercent();
1811 int newSize
= percent
* (100 + percentDiff
) / 100;
1817 if (newSize
!= percent
) {
1818 BMessage
message(M_VIEW_SIZE
);
1819 message
.AddInt32("size", newSize
);
1820 PostMessage(&message
);
1826 MainWin::_ResizeWindow(int percent
, bool useNoVideoWidth
, bool stayOnScreen
)
1828 // Get required window size
1831 _GetUnscaledVideoSize(videoWidth
, videoHeight
);
1833 videoWidth
= (videoWidth
* percent
) / 100;
1834 videoHeight
= (videoHeight
* percent
) / 100;
1836 // Calculate and set the minimum window size
1839 _GetMinimumWindowSize(width
, height
);
1841 width
= max_c(width
, videoWidth
) - 1;
1842 if (useNoVideoWidth
)
1843 width
= max_c(width
, fNoVideoWidth
);
1844 height
= height
+ videoHeight
- 1;
1847 BRect
screenFrame(BScreen(this).Frame());
1848 BRect
frame(Frame());
1849 BRect
decoratorFrame(DecoratorFrame());
1851 // Shrink the screen frame by the window border size
1852 screenFrame
.top
+= frame
.top
- decoratorFrame
.top
;
1853 screenFrame
.left
+= frame
.left
- decoratorFrame
.left
;
1854 screenFrame
.right
+= frame
.right
- decoratorFrame
.right
;
1855 screenFrame
.bottom
+= frame
.bottom
- decoratorFrame
.bottom
;
1857 // Update frame to what the new size would be
1858 frame
.right
= frame
.left
+ width
;
1859 frame
.bottom
= frame
.top
+ height
;
1861 if (!screenFrame
.Contains(frame
)) {
1862 // Resize the window so it doesn't extend outside the current
1864 // We don't use BWindow::MoveOnScreen() in order to resize the
1865 // window while keeping the same aspect ratio.
1866 if (frame
.Width() > screenFrame
.Width()
1867 || frame
.Height() > screenFrame
.Height()) {
1870 = frame
.IntegerWidth() - screenFrame
.IntegerWidth();
1872 = frame
.IntegerHeight() - screenFrame
.IntegerHeight();
1875 if (widthDiff
> heightDiff
)
1876 shrinkScale
= (float)(width
- widthDiff
) / width
;
1878 shrinkScale
= (float)(height
- heightDiff
) / height
;
1880 // Resize width/height and center window
1881 width
= lround(width
* shrinkScale
);
1882 height
= lround(height
* shrinkScale
);
1883 MoveTo((screenFrame
.left
+ screenFrame
.right
- width
) / 2,
1884 (screenFrame
.top
+ screenFrame
.bottom
- height
) / 2);
1886 // just off-screen on one or more sides
1889 if (frame
.left
< screenFrame
.left
)
1890 offsetX
= (int)(screenFrame
.left
- frame
.left
);
1891 else if (frame
.right
> screenFrame
.right
)
1892 offsetX
= (int)(screenFrame
.right
- frame
.right
);
1893 if (frame
.top
< screenFrame
.top
)
1894 offsetY
= (int)(screenFrame
.top
- frame
.top
);
1895 else if (frame
.bottom
> screenFrame
.bottom
)
1896 offsetY
= (int)(screenFrame
.bottom
- frame
.bottom
);
1897 MoveBy(offsetX
, offsetY
);
1902 ResizeTo(width
, height
);
1907 MainWin::_ResizeVideoView(int x
, int y
, int width
, int height
)
1909 // Keep aspect ratio, place video view inside
1910 // the background area (may create black bars).
1913 _GetUnscaledVideoSize(videoWidth
, videoHeight
);
1914 float scaledWidth
= videoWidth
;
1915 float scaledHeight
= videoHeight
;
1916 float factor
= min_c(width
/ scaledWidth
, height
/ scaledHeight
);
1917 int renderWidth
= lround(scaledWidth
* factor
);
1918 int renderHeight
= lround(scaledHeight
* factor
);
1919 if (renderWidth
> width
)
1920 renderWidth
= width
;
1921 if (renderHeight
> height
)
1922 renderHeight
= height
;
1924 int xOffset
= (width
- renderWidth
) / 2;
1925 int yOffset
= (height
- renderHeight
) / 2;
1927 fVideoView
->MoveTo(x
, y
);
1928 fVideoView
->ResizeTo(width
- 1, height
- 1);
1930 BRect
videoFrame(xOffset
, yOffset
,
1931 xOffset
+ renderWidth
- 1, yOffset
+ renderHeight
- 1);
1933 fVideoView
->SetVideoFrame(videoFrame
);
1934 fVideoView
->SetSubTitleMaxBottom(height
- 1);
1942 MainWin::_MouseDown(BMessage
* msg
, BView
* originalHandler
)
1944 uint32 buttons
= msg
->FindInt32("buttons");
1946 // On Zeta, only "screen_where" is reliable, "where" and "be:view_where"
1947 // seem to be broken
1949 if (msg
->FindPoint("screen_where", &screenWhere
) != B_OK
) {
1951 // Workaround for BeOS R5, it has no "screen_where"
1952 if (!originalHandler
|| msg
->FindPoint("where", &screenWhere
) < B_OK
)
1954 originalHandler
->ConvertToScreen(&screenWhere
);
1957 // double click handling
1959 if (msg
->FindInt32("clicks") % 2 == 0) {
1960 BRect
rect(screenWhere
.x
- 1, screenWhere
.y
- 1, screenWhere
.x
+ 1,
1962 if (rect
.Contains(fMouseDownMousePos
)) {
1963 if (buttons
== B_PRIMARY_MOUSE_BUTTON
)
1964 PostMessage(M_TOGGLE_FULLSCREEN
);
1965 else if (buttons
== B_SECONDARY_MOUSE_BUTTON
)
1966 PostMessage(M_TOGGLE_NO_INTERFACE
);
1972 fMouseDownMousePos
= screenWhere
;
1973 fMouseDownWindowPos
= Frame().LeftTop();
1975 if (buttons
== B_PRIMARY_MOUSE_BUTTON
&& !fIsFullscreen
) {
1976 // start mouse tracking
1977 fVideoView
->SetMouseEventMask(B_POINTER_EVENTS
| B_NO_POINTER_HISTORY
1978 /* | B_LOCK_WINDOW_FOCUS */);
1979 fMouseDownTracking
= true;
1982 // pop up a context menu if right mouse button is down
1984 if ((buttons
& B_SECONDARY_MOUSE_BUTTON
) != 0)
1985 _ShowContextMenu(screenWhere
);
1990 MainWin::_MouseMoved(BMessage
* msg
, BView
* originalHandler
)
1992 // msg->PrintToStream();
1995 uint32 buttons
= msg
->FindInt32("buttons");
1996 // On Zeta, only "screen_where" is reliable, "where"
1997 // and "be:view_where" seem to be broken
1998 if (msg
->FindPoint("screen_where", &mousePos
) != B_OK
) {
2000 // Workaround for BeOS R5, it has no "screen_where"
2001 if (!originalHandler
|| msg
->FindPoint("where", &mousePos
) < B_OK
)
2003 originalHandler
->ConvertToScreen(&mousePos
);
2006 if (buttons
== B_PRIMARY_MOUSE_BUTTON
&& fMouseDownTracking
2007 && !fIsFullscreen
) {
2008 // printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y);
2009 float delta_x
= mousePos
.x
- fMouseDownMousePos
.x
;
2010 float delta_y
= mousePos
.y
- fMouseDownMousePos
.y
;
2011 float x
= fMouseDownWindowPos
.x
+ delta_x
;
2012 float y
= fMouseDownWindowPos
.y
+ delta_y
;
2013 // printf("move window to %.0f, %.0f\n", x, y);
2017 bigtime_t eventTime
;
2018 if (msg
->FindInt64("when", &eventTime
) != B_OK
)
2019 eventTime
= system_time();
2021 if (buttons
== 0 && fIsFullscreen
) {
2022 BPoint moveDelta
= mousePos
- fLastMousePos
;
2024 = sqrtf(moveDelta
.x
* moveDelta
.x
+ moveDelta
.y
* moveDelta
.y
);
2025 if (eventTime
- fLastMouseMovedTime
< 200000)
2026 fMouseMoveDist
+= moveDeltaDist
;
2028 fMouseMoveDist
= moveDeltaDist
;
2029 if (fMouseMoveDist
> 5)
2030 _ShowFullscreenControls(true);
2033 fLastMousePos
= mousePos
;
2034 fLastMouseMovedTime
=eventTime
;
2039 MainWin::_MouseUp(BMessage
* msg
)
2041 fMouseDownTracking
= false;
2046 MainWin::_ShowContextMenu(const BPoint
& screenPoint
)
2048 printf("Show context menu\n");
2049 BPopUpMenu
* menu
= new BPopUpMenu("context menu", false, false);
2051 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Full screen"),
2052 new BMessage(M_TOGGLE_FULLSCREEN
), B_ENTER
));
2053 item
->SetMarked(fIsFullscreen
);
2054 item
->SetEnabled(fHasVideo
);
2056 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Hide interface"),
2057 new BMessage(M_TOGGLE_NO_INTERFACE
), 'H'));
2058 item
->SetMarked(fNoInterface
);
2059 item
->SetEnabled(fHasVideo
&& !fIsFullscreen
);
2061 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Always on top"),
2062 new BMessage(M_TOGGLE_ALWAYS_ON_TOP
), 'A'));
2063 item
->SetMarked(fAlwaysOnTop
);
2064 item
->SetEnabled(fHasVideo
);
2066 BMenu
* aspectSubMenu
= new BMenu(B_TRANSLATE("Aspect ratio"));
2067 _SetupVideoAspectItems(aspectSubMenu
);
2068 aspectSubMenu
->SetTargetForItems(this);
2069 menu
->AddItem(item
= new BMenuItem(aspectSubMenu
));
2070 item
->SetEnabled(fHasVideo
);
2072 menu
->AddSeparatorItem();
2074 // Add track selector menus
2075 BMenu
* audioTrackMenu
= new BMenu(B_TRANSLATE("Audio track"));
2076 BMenu
* videoTrackMenu
= new BMenu(B_TRANSLATE("Video track"));
2077 BMenu
* subTitleTrackMenu
= new BMenu(B_TRANSLATE("Subtitles"));
2078 _SetupTrackMenus(audioTrackMenu
, videoTrackMenu
, subTitleTrackMenu
);
2080 audioTrackMenu
->SetTargetForItems(this);
2081 videoTrackMenu
->SetTargetForItems(this);
2082 subTitleTrackMenu
->SetTargetForItems(this);
2084 menu
->AddItem(item
= new BMenuItem(audioTrackMenu
));
2085 item
->SetEnabled(fHasAudio
);
2087 menu
->AddItem(item
= new BMenuItem(videoTrackMenu
));
2088 item
->SetEnabled(fHasVideo
);
2090 menu
->AddItem(item
= new BMenuItem(subTitleTrackMenu
));
2091 item
->SetEnabled(fHasVideo
);
2093 menu
->AddSeparatorItem();
2094 menu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(M_FILE_QUIT
), 'Q'));
2096 menu
->SetTargetForItems(this);
2097 BRect
rect(screenPoint
.x
- 5, screenPoint
.y
- 5, screenPoint
.x
+ 5,
2099 menu
->Go(screenPoint
, true, true, rect
, true);
2103 /*! Trap keys that are about to be send to background or renderer view.
2104 Return true if it shouldn't be passed to the view.
2107 MainWin::_KeyDown(BMessage
* msg
)
2109 uint32 key
= msg
->FindInt32("key");
2110 uint32 rawChar
= msg
->FindInt32("raw_char");
2111 uint32 modifier
= msg
->FindInt32("modifiers");
2113 // printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar,
2116 // ignore the system modifier namespace
2117 if ((modifier
& (B_CONTROL_KEY
| B_COMMAND_KEY
))
2118 == (B_CONTROL_KEY
| B_COMMAND_KEY
))
2123 fController
->TogglePlaying();
2127 fController
->ToggleMute();
2134 PostMessage(M_TOGGLE_FULLSCREEN
);
2137 case B_ENTER
: // Enter / Return
2138 if ((modifier
& B_COMMAND_KEY
) != 0) {
2139 PostMessage(M_TOGGLE_FULLSCREEN
);
2146 if ((modifier
& (B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
2147 | B_MENU_KEY
)) == 0) {
2148 PostMessage(M_TOGGLE_FULLSCREEN
);
2154 if ((modifier
& B_COMMAND_KEY
) != 0)
2155 PostMessage(M_SKIP_NEXT
);
2157 PostMessage(M_VOLUME_UP
);
2161 if ((modifier
& B_COMMAND_KEY
) != 0)
2162 PostMessage(M_SKIP_PREV
);
2164 PostMessage(M_VOLUME_DOWN
);
2168 if ((modifier
& B_COMMAND_KEY
) != 0)
2169 PostMessage(M_SKIP_NEXT
);
2170 else if (fAllowWinding
) {
2171 BMessage
windMessage(M_WIND
);
2172 if ((modifier
& B_SHIFT_KEY
) != 0) {
2173 windMessage
.AddInt64("how much", 30000000LL);
2174 windMessage
.AddInt64("frames", 5);
2176 windMessage
.AddInt64("how much", 5000000LL);
2177 windMessage
.AddInt64("frames", 1);
2179 PostMessage(&windMessage
);
2184 if ((modifier
& B_COMMAND_KEY
) != 0)
2185 PostMessage(M_SKIP_PREV
);
2186 else if (fAllowWinding
) {
2187 BMessage
windMessage(M_WIND
);
2188 if ((modifier
& B_SHIFT_KEY
) != 0) {
2189 windMessage
.AddInt64("how much", -30000000LL);
2190 windMessage
.AddInt64("frames", -5);
2192 windMessage
.AddInt64("how much", -5000000LL);
2193 windMessage
.AddInt64("frames", -1);
2195 PostMessage(&windMessage
);
2200 PostMessage(M_SKIP_NEXT
);
2204 PostMessage(M_SKIP_PREV
);
2208 if ((modifier
& B_COMMAND_KEY
) == 0) {
2215 if ((modifier
& B_COMMAND_KEY
) == 0) {
2216 _ZoomVideoView(-10);
2222 case 'd': // d for delete
2223 case 't': // t for Trash
2224 if ((modifiers() & B_COMMAND_KEY
) != 0) {
2225 BAutolock
_(fPlaylist
);
2226 BMessage
removeMessage(M_PLAYLIST_MOVE_TO_TRASH
);
2227 removeMessage
.AddInt32("playlist index",
2228 fPlaylist
->CurrentItemIndex());
2229 fPlaylistWindow
->PostMessage(&removeMessage
);
2236 case 0x3a: // numeric keypad +
2237 if ((modifier
& B_COMMAND_KEY
) == 0) {
2243 case 0x25: // numeric keypad -
2244 if ((modifier
& B_COMMAND_KEY
) == 0) {
2245 _ZoomVideoView(-10);
2250 case 0x38: // numeric keypad up arrow
2251 PostMessage(M_VOLUME_UP
);
2254 case 0x59: // numeric keypad down arrow
2255 PostMessage(M_VOLUME_DOWN
);
2258 case 0x39: // numeric keypad page up
2259 case 0x4a: // numeric keypad right arrow
2260 PostMessage(M_SKIP_NEXT
);
2263 case 0x5a: // numeric keypad page down
2264 case 0x48: // numeric keypad left arrow
2265 PostMessage(M_SKIP_PREV
);
2268 // Playback controls along the bottom of the keyboard:
2269 // Z X C (V) B for US International
2271 PostMessage(M_SKIP_PREV
);
2274 fController
->TogglePlaying();
2277 fController
->Pause();
2280 fController
->Stop();
2283 PostMessage(M_SKIP_NEXT
);
2295 MainWin::_ToggleFullscreen()
2297 printf("_ToggleFullscreen enter\n");
2300 printf("_ToggleFullscreen - ignoring, as we don't have a video\n");
2304 fIsFullscreen
= !fIsFullscreen
;
2306 if (fIsFullscreen
) {
2307 // switch to fullscreen
2309 fSavedFrame
= Frame();
2310 printf("saving current frame: %d %d %d %d\n", int(fSavedFrame
.left
),
2311 int(fSavedFrame
.top
), int(fSavedFrame
.right
),
2312 int(fSavedFrame
.bottom
));
2313 BScreen
screen(this);
2314 BRect
rect(screen
.Frame());
2317 MoveTo(rect
.left
, rect
.top
);
2318 ResizeTo(rect
.Width(), rect
.Height());
2322 // switch back from full screen mode
2323 _ShowFullscreenControls(false, false);
2326 MoveTo(fSavedFrame
.left
, fSavedFrame
.top
);
2327 ResizeTo(fSavedFrame
.Width(), fSavedFrame
.Height());
2331 fVideoView
->SetFullscreen(fIsFullscreen
);
2333 _MarkItem(fFileMenu
, M_TOGGLE_FULLSCREEN
, fIsFullscreen
);
2335 printf("_ToggleFullscreen leave\n");
2339 MainWin::_ToggleAlwaysOnTop()
2341 fAlwaysOnTop
= !fAlwaysOnTop
;
2342 SetFeel(fAlwaysOnTop
? B_FLOATING_ALL_WINDOW_FEEL
: B_NORMAL_WINDOW_FEEL
);
2344 _MarkItem(fFileMenu
, M_TOGGLE_ALWAYS_ON_TOP
, fAlwaysOnTop
);
2349 MainWin::_ToggleNoInterface()
2351 printf("_ToggleNoInterface enter\n");
2353 if (fIsFullscreen
|| !fHasVideo
) {
2354 // Fullscreen playback is always without interface and
2355 // audio playback is always with interface. So we ignore these
2357 printf("_ToggleNoControls leave, doing nothing, we are fullscreen\n");
2361 fNoInterface
= !fNoInterface
;
2362 _SetWindowSizeLimits();
2365 MoveBy(0, fMenuBarHeight
);
2366 ResizeBy(0, -(fControlsHeight
+ fMenuBarHeight
));
2367 SetLook(B_BORDERED_WINDOW_LOOK
);
2369 MoveBy(0, -fMenuBarHeight
);
2370 ResizeBy(0, fControlsHeight
+ fMenuBarHeight
);
2371 SetLook(B_TITLED_WINDOW_LOOK
);
2374 _MarkItem(fFileMenu
, M_TOGGLE_NO_INTERFACE
, fNoInterface
);
2376 printf("_ToggleNoInterface leave\n");
2381 MainWin::_ShowIfNeeded()
2383 // Only proceed if the window is already running
2384 if (find_thread(NULL
) != Thread())
2387 if (!fHasVideo
&& fNoVideoFrame
.IsValid()) {
2388 MoveTo(fNoVideoFrame
.LeftTop());
2389 ResizeTo(fNoVideoFrame
.Width(), fNoVideoFrame
.Height());
2390 MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN
);
2391 } else if (fHasVideo
&& IsHidden())
2394 fNoVideoFrame
= BRect();
2404 MainWin::_ShowFullscreenControls(bool show
, bool animate
)
2406 if (fShowsFullscreenControls
== show
)
2409 fShowsFullscreenControls
= show
;
2410 fVideoView
->SetFullscreenControlsVisible(show
);
2413 fControls
->RemoveSelf();
2414 fControls
->MoveTo(fVideoView
->Bounds().left
,
2415 fVideoView
->Bounds().bottom
+ 1);
2416 fVideoView
->AddChild(fControls
);
2417 if (fScaleFullscreenControls
)
2418 fControls
->SetSymbolScale(1.5f
);
2420 while (fControls
->IsHidden())
2425 // Slide the controls into view. We need to do this with
2426 // messages, otherwise we block the video playback for the
2427 // time of the animation.
2428 const float kAnimationOffsets
[] = { 0.05, 0.2, 0.5, 0.2, 0.05 };
2429 const int32 steps
= sizeof(kAnimationOffsets
) / sizeof(float);
2430 float height
= fControls
->Bounds().Height();
2431 float moveDist
= show
? -height
: height
;
2432 float originalY
= fControls
->Frame().top
;
2433 for (int32 i
= 0; i
< steps
; i
++) {
2434 BMessage
message(M_SLIDE_CONTROLS
);
2435 message
.AddFloat("offset",
2436 floorf(moveDist
* kAnimationOffsets
[i
]));
2437 PostMessage(&message
, this);
2439 BMessage
finalMessage(M_FINISH_SLIDING_CONTROLS
);
2440 finalMessage
.AddFloat("offset", originalY
+ moveDist
);
2441 finalMessage
.AddBool("show", show
);
2442 PostMessage(&finalMessage
, this);
2444 fControls
->RemoveSelf();
2445 fControls
->MoveTo(fVideoView
->Frame().left
,
2446 fVideoView
->Frame().bottom
+ 1);
2447 fBackground
->AddChild(fControls
);
2448 fControls
->SetSymbolScale(1.0f
);
2450 while (!fControls
->IsHidden())
2460 MainWin::_Wind(bigtime_t howMuch
, int64 frames
)
2462 if (!fAllowWinding
|| !fController
->Lock())
2465 if (frames
!= 0 && fHasVideo
&& !fController
->IsPlaying()) {
2466 int64 newFrame
= fController
->CurrentFrame() + frames
;
2467 fController
->SetFramePosition(newFrame
);
2469 bigtime_t seekTime
= fController
->TimePosition() + howMuch
;
2471 fInitialSeekPosition
= seekTime
;
2472 PostMessage(M_SKIP_PREV
);
2473 } else if (seekTime
> fController
->TimeDuration()) {
2474 fInitialSeekPosition
= 0;
2475 PostMessage(M_SKIP_NEXT
);
2477 fController
->SetTimePosition(seekTime
);
2480 fController
->Unlock();
2481 fAllowWinding
= false;
2489 MainWin::_UpdatePlaylistItemFile()
2491 BAutolock
locker(fPlaylist
);
2492 const FilePlaylistItem
* item
2493 = dynamic_cast<const FilePlaylistItem
*>(fController
->Item());
2497 if (!fHasVideo
&& !fHasAudio
)
2500 BNode
node(&item
->Ref());
2501 if (node
.InitCheck())
2506 // Set some standard attributes of the currently played file.
2507 // This should only be a temporary solution.
2510 const char* kDurationAttrName
= "Media:Length";
2512 status_t status
= node
.GetAttrInfo(kDurationAttrName
, &info
);
2513 if (status
!= B_OK
|| info
.size
== 0) {
2514 bigtime_t duration
= fController
->TimeDuration();
2515 // TODO: Tracker does not seem to care about endian for scalar types
2516 node
.WriteAttr(kDurationAttrName
, B_INT64_TYPE
, 0, &duration
,
2520 // Write audio bitrate
2522 status
= node
.GetAttrInfo("Audio:Bitrate", &info
);
2523 if (status
!= B_OK
|| info
.size
== 0) {
2524 media_format format
;
2525 if (fController
->GetEncodedAudioFormat(&format
) == B_OK
2526 && format
.type
== B_MEDIA_ENCODED_AUDIO
) {
2527 int32 bitrate
= (int32
)(format
.u
.encoded_audio
.bit_rate
2530 snprintf(text
, sizeof(text
), "%" B_PRId32
" kbit", bitrate
);
2531 node
.WriteAttr("Audio:Bitrate", B_STRING_TYPE
, 0, text
,
2537 // Write video bitrate
2539 status
= node
.GetAttrInfo("Video:Bitrate", &info
);
2540 if (status
!= B_OK
|| info
.size
== 0) {
2541 media_format format
;
2542 if (fController
->GetEncodedVideoFormat(&format
) == B_OK
2543 && format
.type
== B_MEDIA_ENCODED_VIDEO
) {
2544 int32 bitrate
= (int32
)(format
.u
.encoded_video
.avg_bit_rate
2547 snprintf(text
, sizeof(text
), "%" B_PRId32
" kbit", bitrate
);
2548 node
.WriteAttr("Video:Bitrate", B_STRING_TYPE
, 0, text
,
2554 _UpdateAttributesMenu(node
);
2559 MainWin::_UpdateAttributesMenu(const BNode
& node
)
2564 status_t status
= node
.GetAttrInfo(kRatingAttrName
, &info
);
2565 if (status
== B_OK
&& info
.type
== B_INT32_TYPE
) {
2566 // Node has the Rating attribute.
2567 node
.ReadAttr(kRatingAttrName
, B_INT32_TYPE
, 0, &rating
,
2571 for (int32 i
= 0; BMenuItem
* item
= fRatingMenu
->ItemAt(i
); i
++)
2572 item
->SetMarked(i
+ 1 == rating
);
2577 MainWin::_SetRating(int32 rating
)
2579 BAutolock
locker(fPlaylist
);
2580 const FilePlaylistItem
* item
2581 = dynamic_cast<const FilePlaylistItem
*>(fController
->Item());
2585 BNode
node(&item
->Ref());
2586 if (node
.InitCheck())
2591 node
.WriteAttr(kRatingAttrName
, B_INT32_TYPE
, 0, &rating
, sizeof(rating
));
2593 // TODO: The whole mechnism should work like this:
2594 // * There is already an attribute API for PlaylistItem, flesh it out!
2595 // * FilePlaylistItem node-monitors it's file somehow.
2596 // * FilePlaylistItem keeps attributes in sync and sends notications.
2597 // * MainWin updates the menu according to FilePlaylistItem notifications.
2598 // * PlaylistWin shows columns with attribute and other info.
2599 // * PlaylistWin updates also upon FilePlaylistItem notifications.
2600 // * This keeps attributes in sync when another app changes them.
2602 _UpdateAttributesMenu(node
);
2607 MainWin::_UpdateControlsEnabledStatus()
2609 uint32 enabledButtons
= 0;
2610 if (fHasVideo
|| fHasAudio
) {
2611 enabledButtons
|= PLAYBACK_ENABLED
| SEEK_ENABLED
2612 | SEEK_BACK_ENABLED
| SEEK_FORWARD_ENABLED
;
2615 enabledButtons
|= VOLUME_ENABLED
;
2617 BAutolock
_(fPlaylist
);
2618 bool canSkipPrevious
, canSkipNext
;
2619 fPlaylist
->GetSkipInfo(&canSkipPrevious
, &canSkipNext
);
2620 if (canSkipPrevious
)
2621 enabledButtons
|= SKIP_BACK_ENABLED
;
2623 enabledButtons
|= SKIP_FORWARD_ENABLED
;
2625 fControls
->SetEnabled(enabledButtons
);
2627 fNoInterfaceMenuItem
->SetEnabled(fHasVideo
);
2628 fAttributesMenu
->SetEnabled(fHasAudio
|| fHasVideo
);
2633 MainWin::_UpdatePlaylistMenu()
2635 if (!fPlaylist
->Lock())
2638 fPlaylistMenu
->RemoveItems(0, fPlaylistMenu
->CountItems(), true);
2640 int32 count
= fPlaylist
->CountItems();
2641 for (int32 i
= 0; i
< count
; i
++) {
2642 PlaylistItem
* item
= fPlaylist
->ItemAtFast(i
);
2643 _AddPlaylistItem(item
, i
);
2645 fPlaylistMenu
->SetTargetForItems(this);
2647 _MarkPlaylistItem(fPlaylist
->CurrentItemIndex());
2649 fPlaylist
->Unlock();
2654 MainWin::_AddPlaylistItem(PlaylistItem
* item
, int32 index
)
2656 BMessage
* message
= new BMessage(M_SET_PLAYLIST_POSITION
);
2657 message
->AddInt32("index", index
);
2658 BMenuItem
* menuItem
= new BMenuItem(item
->Name().String(), message
);
2659 fPlaylistMenu
->AddItem(menuItem
, index
);
2664 MainWin::_RemovePlaylistItem(int32 index
)
2666 delete fPlaylistMenu
->RemoveItem(index
);
2671 MainWin::_MarkPlaylistItem(int32 index
)
2673 if (BMenuItem
* item
= fPlaylistMenu
->ItemAt(index
)) {
2674 item
->SetMarked(true);
2675 // ... and in case the menu is currently on screen:
2676 if (fPlaylistMenu
->LockLooper()) {
2677 fPlaylistMenu
->Invalidate();
2678 fPlaylistMenu
->UnlockLooper();
2685 MainWin::_MarkItem(BMenu
* menu
, uint32 command
, bool mark
)
2687 if (BMenuItem
* item
= menu
->FindItem(command
))
2688 item
->SetMarked(mark
);
2693 MainWin::_AdoptGlobalSettings()
2695 mpSettings settings
;
2696 Settings::Default()->Get(settings
);
2698 fCloseWhenDonePlayingMovie
= settings
.closeWhenDonePlayingMovie
;
2699 fCloseWhenDonePlayingSound
= settings
.closeWhenDonePlayingSound
;
2700 fLoopMovies
= settings
.loopMovie
;
2701 fLoopSounds
= settings
.loopSound
;
2702 fScaleFullscreenControls
= settings
.scaleFullscreenControls
;