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 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
31 #include <Application.h>
36 #include <LayoutBuilder.h>
39 #include <MediaRoster.h>
43 #include <MessageRunner.h>
44 #include <Messenger.h>
45 #include <PopUpMenu.h>
46 #include <PropertyInfo.h>
47 #include <RecentItems.h>
51 #include <TypeConstants.h>
54 #include "AudioProducer.h"
55 #include "ControllerObserver.h"
56 #include "DurationToString.h"
57 #include "FilePlaylistItem.h"
59 #include "NetworkStreamWin.h"
61 #include "PlaylistItem.h"
62 #include "PlaylistObserver.h"
63 #include "PlaylistWindow.h"
67 #undef B_TRANSLATION_CONTEXT
68 #define B_TRANSLATION_CONTEXT "MediaPlayer-Main"
72 int MainWin::sNoVideoWidth
= MIN_WIDTH
;
75 // XXX TODO: why is lround not defined?
76 #define lround(a) ((int)(0.99999 + (a)))
81 M_NETWORK_STREAM_OPEN
,
88 M_TOGGLE_ALWAYS_ON_TOP
,
89 M_TOGGLE_NO_INTERFACE
,
96 // The common display aspect ratios
97 M_ASPECT_SAME_AS_SOURCE
,
98 M_ASPECT_NO_DISTORTION
,
106 M_SELECT_AUDIO_TRACK
= 0x00000800,
107 M_SELECT_AUDIO_TRACK_END
= 0x00000fff,
108 M_SELECT_VIDEO_TRACK
= 0x00010000,
109 M_SELECT_VIDEO_TRACK_END
= 0x00010fff,
110 M_SELECT_SUB_TITLE_TRACK
= 0x00020000,
111 M_SELECT_SUB_TITLE_TRACK_END
= 0x00020fff,
115 M_SET_PLAYLIST_POSITION
,
120 M_FINISH_SLIDING_CONTROLS
124 static property_info sPropertyInfo
[] = {
125 { B_TRANSLATE("Next"), { B_EXECUTE_PROPERTY
},
126 { B_DIRECT_SPECIFIER
, 0 },
127 B_TRANSLATE("Skip to the next track."), 0
129 { B_TRANSLATE("Prev"), { B_EXECUTE_PROPERTY
},
130 { B_DIRECT_SPECIFIER
, 0 },
131 B_TRANSLATE("Skip to the previous track."), 0
133 { B_TRANSLATE("Play"), { B_EXECUTE_PROPERTY
},
134 { B_DIRECT_SPECIFIER
, 0 },
135 B_TRANSLATE("Start playing."), 0
137 { B_TRANSLATE("Stop"), { B_EXECUTE_PROPERTY
},
138 { B_DIRECT_SPECIFIER
, 0 },
139 B_TRANSLATE("Stop playing."), 0
141 { B_TRANSLATE("Pause"), { B_EXECUTE_PROPERTY
},
142 { B_DIRECT_SPECIFIER
, 0 },
143 B_TRANSLATE("Pause playback."), 0
145 { B_TRANSLATE("TogglePlaying"), { B_EXECUTE_PROPERTY
},
146 { B_DIRECT_SPECIFIER
, 0 },
147 B_TRANSLATE("Toggle pause/play."), 0
149 { B_TRANSLATE("Mute"), { B_EXECUTE_PROPERTY
},
150 { B_DIRECT_SPECIFIER
, 0 },
151 B_TRANSLATE("Toggle mute."), 0
153 { B_TRANSLATE("Volume"), { B_GET_PROPERTY
, B_SET_PROPERTY
, 0 },
154 { B_DIRECT_SPECIFIER
, 0 },
155 B_TRANSLATE("Gets/sets the volume (0.0-2.0)."), 0,
158 { B_TRANSLATE("URI"), { B_GET_PROPERTY
, 0 },
159 { B_DIRECT_SPECIFIER
, 0 },
160 B_TRANSLATE("Gets the URI of the currently playing item."), 0,
163 { B_TRANSLATE("ToggleFullscreen"), { B_EXECUTE_PROPERTY
},
164 { B_DIRECT_SPECIFIER
, 0 },
165 B_TRANSLATE("Toggle fullscreen."), 0
167 { B_TRANSLATE("Duration"), { B_GET_PROPERTY
, 0 },
168 { B_DIRECT_SPECIFIER
, 0 },
169 B_TRANSLATE("Gets the duration of the currently playing item "
170 "in microseconds."), 0,
173 { B_TRANSLATE("Position"), { B_GET_PROPERTY
, B_SET_PROPERTY
, 0 },
174 { B_DIRECT_SPECIFIER
, 0 },
175 B_TRANSLATE("Gets/sets the current playing position in microseconds."),
178 { B_TRANSLATE("Seek"), { B_SET_PROPERTY
},
179 { B_DIRECT_SPECIFIER
, 0 },
180 B_TRANSLATE("Seek by the specified amounts of microseconds."), 0,
188 static const char* kRatingAttrName
= "Media:Rating";
190 static const char* kDisabledSeekMessage
= B_TRANSLATE("Drop files to play");
192 static const char* kApplicationName
= B_TRANSLATE_SYSTEM_NAME(NAME
);
195 MainWin::MainWin(bool isFirstWindow
, BMessage
* message
)
197 BWindow(BRect(100, 100, 400, 300), kApplicationName
, B_TITLED_WINDOW
,
198 B_ASYNCHRONOUS_CONTROLS
),
199 fCreationTime(system_time()),
201 fPlaylistWindow(NULL
),
205 fPlaylist(new Playlist
),
206 fPlaylistObserver(new PlaylistObserver(this)),
207 fController(new Controller
),
208 fControllerObserver(new ControllerObserver(this,
209 OBSERVE_FILE_CHANGES
| OBSERVE_TRACK_CHANGES
210 | OBSERVE_PLAYBACK_STATE_CHANGES
| OBSERVE_POSITION_CHANGES
211 | OBSERVE_VOLUME_CHANGES
)),
212 fIsFullscreen(false),
215 fShowsFullscreenControls(false),
223 fMouseDownTracking(false),
225 fLastMouseMovedTime(system_time()),
228 fGlobalSettingsListener(this),
229 fInitialSeekPosition(0),
232 // Handle window position and size depending on whether this is the
233 // first window or not. Use the window size from the window that was
234 // last resized by the user.
236 MoveBy(pos
* 25, pos
* 25);
237 pos
= (pos
+ 1) % 15;
239 BRect frame
= Settings::Default()->AudioPlayerWindowFrame();
240 if (frame
.IsValid()) {
242 if (message
== NULL
) {
243 MoveTo(frame
.LeftTop());
244 ResizeTo(frame
.Width(), frame
.Height());
246 // Delay moving to the initial position, since we don't
247 // know if we will be playing audio at all.
248 message
->AddRect("window frame", frame
);
251 if (sNoVideoWidth
== MIN_WIDTH
)
252 sNoVideoWidth
= frame
.IntegerWidth();
253 } else if (sNoVideoWidth
> MIN_WIDTH
) {
254 ResizeTo(sNoVideoWidth
, Bounds().Height());
256 fNoVideoWidth
= sNoVideoWidth
;
258 BRect rect
= Bounds();
261 fBackground
= new BView(rect
, "background", B_FOLLOW_ALL
,
262 B_WILL_DRAW
| B_FULL_UPDATE_ON_RESIZE
);
263 fBackground
->SetViewColor(0, 0, 0);
264 AddChild(fBackground
);
267 fMenuBar
= new BMenuBar(fBackground
->Bounds(), "menu");
269 fBackground
->AddChild(fMenuBar
);
270 fMenuBar
->SetResizingMode(B_FOLLOW_NONE
);
271 fMenuBar
->ResizeToPreferred();
272 fMenuBarWidth
= (int)fMenuBar
->Frame().Width() + 1;
273 fMenuBarHeight
= (int)fMenuBar
->Frame().Height() + 1;
276 rect
= BRect(0, fMenuBarHeight
, fBackground
->Bounds().right
,
277 fMenuBarHeight
+ 10);
278 fVideoView
= new VideoView(rect
, "video display", B_FOLLOW_NONE
);
279 fBackground
->AddChild(fVideoView
);
282 rect
= BRect(0, fMenuBarHeight
+ 11, fBackground
->Bounds().right
,
283 fBackground
->Bounds().bottom
);
284 fControls
= new ControllerView(rect
, fController
, fPlaylist
);
285 fBackground
->AddChild(fControls
);
286 fControls
->ResizeToPreferred();
287 fControlsHeight
= (int)fControls
->Frame().Height() + 1;
288 fControlsWidth
= (int)fControls
->Frame().Width() + 1;
289 fControls
->SetResizingMode(B_FOLLOW_BOTTOM
| B_FOLLOW_LEFT_RIGHT
);
290 fControls
->SetDisabledString(kDisabledSeekMessage
);
292 fPlaylist
->AddListener(fPlaylistObserver
);
293 fController
->SetVideoView(fVideoView
);
294 fController
->AddListener(fControllerObserver
);
295 PeakView
* peakView
= fControls
->GetPeakView();
296 peakView
->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION
);
297 fController
->SetPeakListener(peakView
);
301 // setup the playlist window now, we need to have it
302 // running for the undo/redo playlist editing
303 fPlaylistWindow
= new PlaylistWindow(BRect(150, 150, 500, 600), fPlaylist
,
305 fPlaylistWindow
->Hide();
306 fPlaylistWindow
->Show();
307 // this makes sure the window thread is running without
308 // showing the window just yet
310 Settings::Default()->AddListener(&fGlobalSettingsListener
);
311 _AdoptGlobalSettings();
313 AddShortcut('z', B_COMMAND_KEY
, new BMessage(B_UNDO
));
314 AddShortcut('y', B_COMMAND_KEY
, new BMessage(B_UNDO
));
315 AddShortcut('z', B_COMMAND_KEY
| B_SHIFT_KEY
, new BMessage(B_REDO
));
316 AddShortcut('y', B_COMMAND_KEY
| B_SHIFT_KEY
, new BMessage(B_REDO
));
322 PostMessage(message
);
324 BMediaRoster
* roster
= BMediaRoster::Roster();
325 roster
->StartWatching(BMessenger(this, this), B_MEDIA_SERVER_STARTED
);
326 roster
->StartWatching(BMessenger(this, this), B_MEDIA_SERVER_QUIT
);
332 // printf("MainWin::~MainWin\n");
334 BMediaRoster
* roster
= BMediaRoster::CurrentRoster();
335 roster
->StopWatching(BMessenger(this, this), B_MEDIA_SERVER_STARTED
);
336 roster
->StopWatching(BMessenger(this, this), B_MEDIA_SERVER_QUIT
);
338 Settings::Default()->RemoveListener(&fGlobalSettingsListener
);
339 fPlaylist
->RemoveListener(fPlaylistObserver
);
341 fController
->RemoveListener(fControllerObserver
);
342 fController
->SetPeakListener(NULL
);
343 fController
->SetVideoTarget(NULL
);
344 fController
->Unlock();
346 // give the views a chance to detach from any notifiers
347 // before we delete them
348 fBackground
->RemoveSelf();
351 if (fInfoWin
&& fInfoWin
->Lock())
354 if (fPlaylistWindow
&& fPlaylistWindow
->Lock())
355 fPlaylistWindow
->Quit();
360 // quit the Controller looper thread
361 thread_id controllerThread
= fController
->Thread();
362 fController
->PostMessage(B_QUIT_REQUESTED
);
364 wait_for_thread(controllerThread
, &exitValue
);
372 MainWin::FrameResized(float newWidth
, float newHeight
)
374 if (newWidth
!= Bounds().Width() || newHeight
!= Bounds().Height()) {
375 debugger("size wrong\n");
378 bool noMenu
= fNoInterface
|| fIsFullscreen
;
379 bool noControls
= fNoInterface
|| fIsFullscreen
;
381 // printf("FrameResized enter: newWidth %.0f, newHeight %.0f\n",
382 // newWidth, newHeight);
385 sNoVideoWidth
= fNoVideoWidth
= (int)newWidth
;
387 int maxVideoWidth
= int(newWidth
) + 1;
388 int maxVideoHeight
= int(newHeight
) + 1
389 - (noMenu
? 0 : fMenuBarHeight
)
390 - (noControls
? 0 : fControlsHeight
);
392 ASSERT(maxVideoHeight
>= 0);
397 if (!fMenuBar
->IsHidden(fMenuBar
))
400 fMenuBar
->MoveTo(0, y
);
401 fMenuBar
->ResizeTo(newWidth
, fMenuBarHeight
- 1);
402 if (fMenuBar
->IsHidden(fMenuBar
))
407 if (maxVideoHeight
== 0) {
408 if (!fVideoView
->IsHidden(fVideoView
))
411 _ResizeVideoView(0, y
, maxVideoWidth
, maxVideoHeight
);
412 if (fVideoView
->IsHidden(fVideoView
))
418 if (!fControls
->IsHidden(fControls
))
421 fControls
->MoveTo(0, y
);
422 fControls
->ResizeTo(newWidth
, fControlsHeight
- 1);
423 if (fControls
->IsHidden(fControls
))
425 // y += fControlsHeight;
428 // printf("FrameResized leave\n");
433 MainWin::Zoom(BPoint
/*position*/, float /*width*/, float /*height*/)
435 PostMessage(M_TOGGLE_FULLSCREEN
);
440 MainWin::DispatchMessage(BMessage
* msg
, BHandler
* handler
)
442 if ((msg
->what
== B_MOUSE_DOWN
)
443 && (handler
== fBackground
|| handler
== fVideoView
444 || handler
== fControls
)) {
445 _MouseDown(msg
, dynamic_cast<BView
*>(handler
));
448 if ((msg
->what
== B_MOUSE_MOVED
)
449 && (handler
== fBackground
|| handler
== fVideoView
450 || handler
== fControls
)) {
451 _MouseMoved(msg
, dynamic_cast<BView
*>(handler
));
454 if ((msg
->what
== B_MOUSE_UP
)
455 && (handler
== fBackground
|| handler
== fVideoView
)) {
459 if ((msg
->what
== B_KEY_DOWN
)
460 && (handler
== fBackground
|| handler
== fVideoView
)) {
461 // special case for PrintScreen key
462 if (msg
->FindInt32("key") == B_PRINT_KEY
) {
463 fVideoView
->OverlayScreenshotPrepare();
464 BWindow::DispatchMessage(msg
, handler
);
465 fVideoView
->OverlayScreenshotCleanup();
469 // every other key gets dispatched to our _KeyDown first
471 // it got handled, don't pass it on
476 BWindow::DispatchMessage(msg
, handler
);
481 MainWin::MessageReceived(BMessage
* msg
)
483 // msg->PrintToStream();
485 case B_EXECUTE_PROPERTY
:
489 BMessage
reply(B_REPLY
);
490 status_t result
= B_BAD_SCRIPT_SYNTAX
;
494 const char* property
;
496 if (msg
->GetCurrentSpecifier(&index
, &specifier
, &what
,
497 &property
) != B_OK
) {
498 return BWindow::MessageReceived(msg
);
501 BPropertyInfo
propertyInfo(sPropertyInfo
);
502 switch (propertyInfo
.FindMatch(msg
, index
, &specifier
, what
,
505 fControls
->SkipForward();
510 fControls
->SkipBackward();
525 fController
->Pause();
530 fController
->TogglePlaying();
535 fController
->ToggleMute();
541 if (msg
->what
== B_GET_PROPERTY
) {
542 result
= reply
.AddFloat("result",
543 fController
->Volume());
544 } else if (msg
->what
== B_SET_PROPERTY
) {
546 result
= msg
->FindFloat("data", &newVolume
);
548 fController
->SetVolume(newVolume
);
555 if (msg
->what
== B_GET_PROPERTY
) {
556 BAutolock
_(fPlaylist
);
557 const PlaylistItem
* item
= fController
->Item();
563 result
= reply
.AddString("result", item
->LocationURI());
569 PostMessage(M_TOGGLE_FULLSCREEN
);
573 if (msg
->what
!= B_GET_PROPERTY
)
576 result
= reply
.AddInt64("result",
577 fController
->TimeDuration());
582 if (msg
->what
== B_GET_PROPERTY
) {
583 result
= reply
.AddInt64("result",
584 fController
->TimePosition());
585 } else if (msg
->what
== B_SET_PROPERTY
) {
587 result
= msg
->FindInt64("data", &newTime
);
589 fController
->SetTimePosition(newTime
);
597 if (msg
->what
!= B_SET_PROPERTY
)
601 result
= msg
->FindInt64("data", &seekBy
);
610 return BWindow::MessageReceived(msg
);
613 if (result
!= B_OK
) {
614 reply
.what
= B_MESSAGE_NOT_UNDERSTOOD
;
615 reply
.AddString("message", strerror(result
));
616 reply
.AddInt32("error", result
);
619 msg
->SendReply(&reply
);
623 case B_REFS_RECEIVED
:
629 if (msg
->HasRef("refs"))
632 case M_OPEN_PREVIOUS_PLAYLIST
:
638 fPlaylistWindow
->PostMessage(msg
);
641 case B_MEDIA_SERVER_STARTED
:
643 printf("TODO: implement B_MEDIA_SERVER_STARTED\n");
645 // BAutolock _(fPlaylist);
646 // BMessage fakePlaylistMessage(MSG_PLAYLIST_CURRENT_ITEM_CHANGED);
647 // fakePlaylistMessage.AddInt32("index",
648 // fPlaylist->CurrentItemIndex());
649 // PostMessage(&fakePlaylistMessage);
653 case B_MEDIA_SERVER_QUIT
:
654 printf("TODO: implement B_MEDIA_SERVER_QUIT\n");
655 // if (fController->Lock()) {
656 // fController->CleanupNodes();
657 // fController->Unlock();
661 // PlaylistObserver messages
662 case MSG_PLAYLIST_ITEM_ADDED
:
666 if (msg
->FindPointer("item", (void**)&item
) == B_OK
667 && msg
->FindInt32("index", &index
) == B_OK
) {
668 _AddPlaylistItem(item
, index
);
672 case MSG_PLAYLIST_ITEM_REMOVED
:
675 if (msg
->FindInt32("index", &index
) == B_OK
)
676 _RemovePlaylistItem(index
);
679 case MSG_PLAYLIST_CURRENT_ITEM_CHANGED
:
681 BAutolock
_(fPlaylist
);
684 // if false, the message was meant to only update the GUI
686 if (msg
->FindBool("play", &play
) < B_OK
|| !play
)
688 if (msg
->FindInt32("index", &index
) < B_OK
689 || index
!= fPlaylist
->CurrentItemIndex())
691 PlaylistItemRef
item(fPlaylist
->ItemAt(index
));
692 if (item
.Get() != NULL
) {
693 printf("open playlist item: %s\n", item
->Name().String());
694 OpenPlaylistItem(item
);
695 _MarkPlaylistItem(index
);
699 case MSG_PLAYLIST_IMPORT_FAILED
:
701 BAlert
* alert
= new BAlert(B_TRANSLATE("Nothing to Play"),
702 B_TRANSLATE("None of the files you wanted to play appear "
703 "to be media files."), B_TRANSLATE("OK"));
704 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
706 fControls
->SetDisabledString(kDisabledSeekMessage
);
710 // ControllerObserver messages
711 case MSG_CONTROLLER_FILE_FINISHED
:
713 BAutolock
_(fPlaylist
);
715 bool hadNext
= fPlaylist
->SetCurrentItemIndex(
716 fPlaylist
->CurrentItemIndex() + 1);
718 // Reached end of playlist
719 // Handle "quit when done" settings
720 if ((fHasVideo
&& fCloseWhenDonePlayingMovie
)
721 || (!fHasVideo
&& fCloseWhenDonePlayingSound
))
722 PostMessage(B_QUIT_REQUESTED
);
723 // Handle "loop by default" settings
724 if ((fHasVideo
&& fLoopMovies
)
725 || (!fHasVideo
&& fLoopSounds
)) {
726 if (fPlaylist
->CountItems() > 1)
727 fPlaylist
->SetCurrentItemIndex(0);
734 case MSG_CONTROLLER_FILE_CHANGED
:
736 status_t result
= B_ERROR
;
737 msg
->FindInt32("result", &result
);
738 PlaylistItemRef itemRef
;
740 if (msg
->FindPointer("item", (void**)&item
) == B_OK
) {
741 itemRef
.SetTo(item
, true);
742 // The reference was passed along with the message.
744 BAutolock
_(fPlaylist
);
745 itemRef
.SetTo(fPlaylist
->ItemAt(
746 fPlaylist
->CurrentItemIndex()));
748 _PlaylistItemOpened(itemRef
, result
);
751 case MSG_CONTROLLER_VIDEO_TRACK_CHANGED
:
754 if (msg
->FindInt32("index", &index
) == B_OK
) {
756 while (BMenuItem
* item
= fVideoTrackMenu
->ItemAt(i
)) {
757 item
->SetMarked(i
== index
);
763 case MSG_CONTROLLER_AUDIO_TRACK_CHANGED
:
766 if (msg
->FindInt32("index", &index
) == B_OK
) {
768 while (BMenuItem
* item
= fAudioTrackMenu
->ItemAt(i
)) {
769 item
->SetMarked(i
== index
);
772 _UpdateAudioChannelCount(index
);
776 case MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED
:
779 if (msg
->FindInt32("index", &index
) == B_OK
) {
781 while (BMenuItem
* item
= fSubTitleTrackMenu
->ItemAt(i
)) {
782 BMessage
* message
= item
->Message();
783 if (message
!= NULL
) {
784 item
->SetMarked((int32
)message
->what
785 - M_SELECT_SUB_TITLE_TRACK
== index
);
792 case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED
:
795 if (msg
->FindInt32("state", (int32
*)&state
) == B_OK
)
796 fControls
->SetPlaybackState(state
);
799 case MSG_CONTROLLER_POSITION_CHANGED
:
802 if (msg
->FindFloat("position", &position
) == B_OK
) {
803 fControls
->SetPosition(position
, fController
->TimePosition(),
804 fController
->TimeDuration());
805 fAllowWinding
= true;
809 case MSG_CONTROLLER_SEEK_HANDLED
:
812 case MSG_CONTROLLER_VOLUME_CHANGED
:
815 if (msg
->FindFloat("volume", &volume
) == B_OK
)
816 fControls
->SetVolume(volume
);
819 case MSG_CONTROLLER_MUTED_CHANGED
:
822 if (msg
->FindBool("muted", &muted
) == B_OK
)
823 fControls
->SetMuted(muted
);
827 // menu item messages
830 BMessenger
target(this);
831 BMessage
result(B_REFS_RECEIVED
);
832 BMessage
appMessage(M_SHOW_OPEN_PANEL
);
833 appMessage
.AddMessenger("target", target
);
834 appMessage
.AddMessage("message", &result
);
835 appMessage
.AddString("title", B_TRANSLATE("Open clips"));
836 appMessage
.AddString("label", B_TRANSLATE("Open"));
837 be_app
->PostMessage(&appMessage
);
841 case M_NETWORK_STREAM_OPEN
:
843 BMessenger
target(this);
844 NetworkStreamWin
* win
= new NetworkStreamWin(target
);
852 case M_FILE_PLAYLIST
:
853 ShowPlaylistWindow();
856 PostMessage(B_QUIT_REQUESTED
);
859 be_app
->PostMessage(B_QUIT_REQUESTED
);
862 case M_TOGGLE_FULLSCREEN
:
866 case M_TOGGLE_ALWAYS_ON_TOP
:
867 _ToggleAlwaysOnTop();
870 case M_TOGGLE_NO_INTERFACE
:
871 _ToggleNoInterface();
877 if (msg
->FindInt32("size", &size
) == B_OK
) {
888 case B_ACQUIRE_OVERLAY_LOCK:
889 printf("B_ACQUIRE_OVERLAY_LOCK\n");
890 fVideoView->OverlayLockAcquire();
893 case B_RELEASE_OVERLAY_LOCK:
894 printf("B_RELEASE_OVERLAY_LOCK\n");
895 fVideoView->OverlayLockRelease();
898 case B_MOUSE_WHEEL_CHANGED
:
900 float dx
= msg
->FindFloat("be:wheel_delta_x");
901 float dy
= msg
->FindFloat("be:wheel_delta_y");
902 bool inv
= modifiers() & B_COMMAND_KEY
;
904 PostMessage(inv
? M_VOLUME_DOWN
: M_SKIP_PREV
);
906 PostMessage(inv
? M_VOLUME_UP
: M_SKIP_NEXT
);
908 PostMessage(inv
? M_SKIP_PREV
: M_VOLUME_DOWN
);
910 PostMessage(inv
? M_SKIP_NEXT
: M_VOLUME_UP
);
915 fControls
->SkipForward();
919 fControls
->SkipBackward();
926 if (msg
->FindInt64("how much", &howMuch
) != B_OK
927 || msg
->FindInt64("frames", &frames
) != B_OK
) {
931 _Wind(howMuch
, frames
);
936 fController
->VolumeUp();
940 fController
->VolumeDown();
943 case M_ASPECT_SAME_AS_SOURCE
:
949 fController
->GetSize(&width
, &height
,
950 &widthAspect
, &heightAspect
);
951 VideoFormatChange(width
, height
, widthAspect
, heightAspect
);
955 case M_ASPECT_NO_DISTORTION
:
959 fController
->GetSize(&width
, &height
);
960 VideoFormatChange(width
, height
, width
, height
);
965 VideoAspectChange(4, 3);
968 case M_ASPECT_16_9
: // 1.77 : 1
969 VideoAspectChange(16, 9);
972 case M_ASPECT_83_50
: // 1.66 : 1
973 VideoAspectChange(83, 50);
976 case M_ASPECT_7_4
: // 1.75 : 1
977 VideoAspectChange(7, 4);
980 case M_ASPECT_37_20
: // 1.85 : 1
981 VideoAspectChange(37, 20);
984 case M_ASPECT_47_20
: // 2.35 : 1
985 VideoAspectChange(47, 20);
988 case M_SET_PLAYLIST_POSITION
:
990 BAutolock
_(fPlaylist
);
993 if (msg
->FindInt32("index", &index
) == B_OK
)
994 fPlaylist
->SetCurrentItemIndex(index
);
998 case MSG_OBJECT_CHANGED
:
999 // received from fGlobalSettingsListener
1000 // TODO: find out which object, if we ever watch more than
1001 // the global settings instance...
1002 _AdoptGlobalSettings();
1005 case M_SLIDE_CONTROLS
:
1008 if (msg
->FindFloat("offset", &offset
) == B_OK
) {
1009 fControls
->MoveBy(0, offset
);
1010 fVideoView
->SetSubTitleMaxBottom(fControls
->Frame().top
- 1);
1016 case M_FINISH_SLIDING_CONTROLS
:
1020 if (msg
->FindFloat("offset", &offset
) == B_OK
1021 && msg
->FindBool("show", &show
) == B_OK
) {
1023 fControls
->MoveTo(fControls
->Frame().left
, offset
);
1024 fVideoView
->SetSubTitleMaxBottom(offset
- 1);
1026 fVideoView
->SetSubTitleMaxBottom(
1027 fVideoView
->Bounds().bottom
);
1028 fControls
->RemoveSelf();
1029 fControls
->MoveTo(fVideoView
->Frame().left
,
1030 fVideoView
->Frame().bottom
+ 1);
1031 fBackground
->AddChild(fControls
);
1032 fControls
->SetSymbolScale(1.0f
);
1033 while (!fControls
->IsHidden())
1039 case M_HIDE_FULL_SCREEN_CONTROLS
:
1040 if (fIsFullscreen
) {
1041 BPoint videoViewWhere
;
1042 if (msg
->FindPoint("where", &videoViewWhere
) == B_OK
) {
1043 if (msg
->FindBool("force")
1044 || !fControls
->Frame().Contains(videoViewWhere
)) {
1045 _ShowFullscreenControls(false);
1046 // hide the mouse cursor until the user moves it
1047 be_app
->ObscureCursor();
1056 if (msg
->FindInt32("rating", &rating
) == B_OK
)
1062 if (msg
->what
>= M_SELECT_AUDIO_TRACK
1063 && msg
->what
<= M_SELECT_AUDIO_TRACK_END
) {
1064 fController
->SelectAudioTrack(msg
->what
- M_SELECT_AUDIO_TRACK
);
1067 if (msg
->what
>= M_SELECT_VIDEO_TRACK
1068 && msg
->what
<= M_SELECT_VIDEO_TRACK_END
) {
1069 fController
->SelectVideoTrack(msg
->what
- M_SELECT_VIDEO_TRACK
);
1072 if ((int32
)msg
->what
>= M_SELECT_SUB_TITLE_TRACK
- 1
1073 && msg
->what
<= M_SELECT_SUB_TITLE_TRACK_END
) {
1074 fController
->SelectSubTitleTrack((int32
)msg
->what
1075 - M_SELECT_SUB_TITLE_TRACK
);
1078 // let BWindow handle the rest
1079 BWindow::MessageReceived(msg
);
1085 MainWin::WindowActivated(bool active
)
1087 fController
->PlayerActivated(active
);
1092 MainWin::QuitRequested()
1094 BMessage
message(M_PLAYER_QUIT
);
1095 GetQuitMessage(&message
);
1096 be_app
->PostMessage(&message
);
1102 MainWin::MenusBeginning()
1104 _SetupVideoAspectItems(fVideoAspectMenu
);
1112 MainWin::OpenPlaylist(const BMessage
* playlistArchive
)
1114 if (playlistArchive
== NULL
)
1118 BAutolock
playlistLocker(fPlaylist
);
1120 if (fPlaylist
->Unarchive(playlistArchive
) != B_OK
)
1124 if (playlistArchive
->FindInt32("index", ¤tIndex
) != B_OK
)
1126 fPlaylist
->SetCurrentItemIndex(currentIndex
);
1128 playlistLocker
.Unlock();
1130 if (currentIndex
!= -1) {
1131 // Restore the current play position only if we have something to play
1132 playlistArchive
->FindInt64("position", (int64
*)&fInitialSeekPosition
);
1141 MainWin::OpenPlaylistItem(const PlaylistItemRef
& item
)
1143 status_t ret
= fController
->SetToAsync(item
);
1145 fprintf(stderr
, "MainWin::OpenPlaylistItem() - Failed to send message "
1146 "to Controller.\n");
1147 BString message
= B_TRANSLATE("%app% encountered an internal error. "
1148 "The file could not be opened.");
1149 message
.ReplaceFirst("%app%", kApplicationName
);
1150 BAlert
* alert
= new BAlert(kApplicationName
, message
.String(),
1151 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
1152 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1154 _PlaylistItemOpened(item
, ret
);
1157 string
<< "Opening '" << item
->Name() << "'.";
1158 fControls
->SetDisabledString(string
.String());
1164 MainWin::ShowFileInfo()
1167 fInfoWin
= new InfoWin(Frame().LeftTop(), fController
);
1169 if (fInfoWin
->Lock()) {
1170 if (fInfoWin
->IsHidden())
1173 fInfoWin
->Activate();
1180 MainWin::ShowPlaylistWindow()
1182 if (fPlaylistWindow
->Lock()) {
1183 // make sure the window shows on the same workspace as ourself
1184 uint32 workspaces
= Workspaces();
1185 if (fPlaylistWindow
->Workspaces() != workspaces
)
1186 fPlaylistWindow
->SetWorkspaces(workspaces
);
1189 if (fPlaylistWindow
->IsHidden())
1190 fPlaylistWindow
->Show();
1192 fPlaylistWindow
->Activate();
1194 fPlaylistWindow
->Unlock();
1200 MainWin::VideoAspectChange(int forcedWidth
, int forcedHeight
, float widthScale
)
1202 // Force specific source size and pixel width scale.
1206 fController
->GetSize(&width
, &height
);
1207 VideoFormatChange(forcedWidth
, forcedHeight
,
1208 lround(width
* widthScale
), height
);
1214 MainWin::VideoAspectChange(float widthScale
)
1216 // Called when video aspect ratio changes and the original
1217 // width/height should be restored too, display aspect is not known,
1218 // only pixel width scale.
1222 fController
->GetSize(&width
, &height
);
1223 VideoFormatChange(width
, height
, lround(width
* widthScale
), height
);
1229 MainWin::VideoAspectChange(int widthAspect
, int heightAspect
)
1231 // Called when video aspect ratio changes and the original
1232 // width/height should be restored too.
1236 fController
->GetSize(&width
, &height
);
1237 VideoFormatChange(width
, height
, widthAspect
, heightAspect
);
1243 MainWin::VideoFormatChange(int width
, int height
, int widthAspect
,
1246 // Called when video format or aspect ratio changes.
1248 printf("VideoFormatChange enter: width %d, height %d, "
1249 "aspect ratio: %d:%d\n", width
, height
, widthAspect
, heightAspect
);
1251 // remember current view scale
1252 int percent
= _CurrentVideoSizeInPercent();
1254 fSourceWidth
= width
;
1255 fSourceHeight
= height
;
1256 fWidthAspect
= widthAspect
;
1257 fHeightAspect
= heightAspect
;
1262 FrameResized(Bounds().Width(), Bounds().Height());
1264 printf("VideoFormatChange leave\n");
1269 MainWin::GetQuitMessage(BMessage
* message
)
1271 message
->AddPointer("instance", this);
1272 message
->AddRect("window frame", Frame());
1273 message
->AddBool("audio only", !fHasVideo
);
1274 message
->AddInt64("creation time", fCreationTime
);
1276 if (!fHasVideo
&& fHasAudio
) {
1277 // store playlist, current index and position if this is audio
1278 BMessage playlistArchive
;
1280 BAutolock
controllerLocker(fController
);
1281 playlistArchive
.AddInt64("position", fController
->TimePosition());
1282 controllerLocker
.Unlock();
1287 BAutolock
playlistLocker(fPlaylist
);
1288 if (fPlaylist
->Archive(&playlistArchive
) != B_OK
1289 || playlistArchive
.AddInt32("index",
1290 fPlaylist
->CurrentItemIndex()) != B_OK
1291 || message
->AddMessage("playlist", &playlistArchive
) != B_OK
) {
1292 fprintf(stderr
, "Failed to store current playlist.\n");
1299 MainWin::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
1300 int32 what
, const char* property
)
1302 BPropertyInfo
propertyInfo(sPropertyInfo
);
1303 switch (propertyInfo
.FindMatch(message
, index
, specifier
, what
, property
)) {
1320 return BWindow::ResolveSpecifier(message
, index
, specifier
, what
, property
);
1325 MainWin::GetSupportedSuites(BMessage
* data
)
1330 status_t status
= data
->AddString("suites", "suite/vnd.Haiku-MediaPlayer");
1334 BPropertyInfo
propertyInfo(sPropertyInfo
);
1335 status
= data
->AddFlat("messages", &propertyInfo
);
1339 return BWindow::GetSupportedSuites(data
);
1347 MainWin::_RefsReceived(BMessage
* message
)
1349 // the playlist is replaced by dropped files
1350 // or the dropped files are appended to the end
1351 // of the existing playlist if <shift> is pressed
1352 bool append
= false;
1353 if (message
->FindBool("append to playlist", &append
) != B_OK
)
1354 append
= modifiers() & B_SHIFT_KEY
;
1356 BAutolock
_(fPlaylist
);
1357 int32 appendIndex
= append
? APPEND_INDEX_APPEND_LAST
1358 : APPEND_INDEX_REPLACE_PLAYLIST
;
1359 message
->AddInt32("append_index", appendIndex
);
1361 // forward the message to the playlist window,
1362 // so that undo/redo is used for modifying the playlist
1363 fPlaylistWindow
->PostMessage(message
);
1365 if (message
->FindRect("window frame", &fNoVideoFrame
) != B_OK
)
1366 fNoVideoFrame
= BRect();
1371 MainWin::_PlaylistItemOpened(const PlaylistItemRef
& item
, status_t result
)
1373 if (result
!= B_OK
) {
1374 BAutolock
_(fPlaylist
);
1376 item
->SetPlaybackFailed();
1377 bool allItemsFailed
= true;
1378 int32 count
= fPlaylist
->CountItems();
1379 for (int32 i
= 0; i
< count
; i
++) {
1380 if (!fPlaylist
->ItemAtFast(i
)->PlaybackFailed()) {
1381 allItemsFailed
= false;
1386 if (allItemsFailed
) {
1387 // Display error if all files failed to play.
1388 BString
message(B_TRANSLATE(
1389 "The file '%filename' could not be opened.\n\n"));;
1390 message
.ReplaceAll("%filename", item
->Name());
1392 if (result
== B_MEDIA_NO_HANDLER
) {
1393 // give a more detailed message for the most likely of all
1395 message
<< B_TRANSLATE(
1396 "There is no decoder installed to handle the "
1397 "file format, or the decoder has trouble with the "
1398 "specific version of the format.");
1400 message
<< B_TRANSLATE("Error: ") << strerror(result
);
1402 BAlert
* alert
= new BAlert("error", message
.String(),
1403 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
, B_STOP_ALERT
);
1404 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1406 fControls
->SetDisabledString(kDisabledSeekMessage
);
1408 // Just go to the next file and don't bother user (yet)
1409 fPlaylist
->SetCurrentItemIndex(fPlaylist
->CurrentItemIndex() + 1);
1415 SetTitle(kApplicationName
);
1418 fHasVideo
= fController
->VideoTrackCount() != 0;
1419 fHasAudio
= fController
->AudioTrackCount() != 0;
1420 SetTitle(item
->Name().String());
1422 if (fInitialSeekPosition
< 0) {
1423 fInitialSeekPosition
1424 = fController
->TimeDuration() + fInitialSeekPosition
;
1426 fController
->SetTimePosition(fInitialSeekPosition
);
1427 fInitialSeekPosition
= 0;
1432 _UpdatePlaylistItemFile();
1437 MainWin::_SetupWindow()
1439 // printf("MainWin::_SetupWindow\n");
1440 // Populate the track menus
1441 _SetupTrackMenus(fAudioTrackMenu
, fVideoTrackMenu
, fSubTitleTrackMenu
);
1442 _UpdateAudioChannelCount(fController
->CurrentAudioTrack());
1444 fVideoMenu
->SetEnabled(fHasVideo
);
1445 fAudioMenu
->SetEnabled(fHasAudio
);
1446 int previousSourceWidth
= fSourceWidth
;
1447 int previousSourceHeight
= fSourceHeight
;
1448 int previousWidthAspect
= fWidthAspect
;
1449 int previousHeightAspect
= fHeightAspect
;
1451 fController
->GetSize(&fSourceWidth
, &fSourceHeight
,
1452 &fWidthAspect
, &fHeightAspect
);
1459 _UpdateControlsEnabledStatus();
1461 // Adopt the size and window layout if necessary
1462 if (previousSourceWidth
!= fSourceWidth
1463 || previousSourceHeight
!= fSourceHeight
1464 || previousWidthAspect
!= fWidthAspect
1465 || previousHeightAspect
!= fHeightAspect
) {
1467 _SetWindowSizeLimits();
1469 if (!fIsFullscreen
) {
1470 // Resize to 100% but stay on screen
1471 _ResizeWindow(100, !fHasVideo
, true);
1473 // Make sure we relayout the video view when in full screen mode
1474 FrameResized(Frame().Width(), Frame().Height());
1480 fVideoView
->MakeFocus();
1485 MainWin::_CreateMenu()
1487 fFileMenu
= new BMenu(kApplicationName
);
1488 fPlaylistMenu
= new BMenu(B_TRANSLATE("Playlist" B_UTF8_ELLIPSIS
));
1489 fAudioMenu
= new BMenu(B_TRANSLATE("Audio"));
1490 fVideoMenu
= new BMenu(B_TRANSLATE("Video"));
1491 fVideoAspectMenu
= new BMenu(B_TRANSLATE("Aspect ratio"));
1492 fAudioTrackMenu
= new BMenu(B_TRANSLATE_CONTEXT("Track",
1493 "Audio Track Menu"));
1494 fVideoTrackMenu
= new BMenu(B_TRANSLATE_CONTEXT("Track",
1495 "Video Track Menu"));
1496 fSubTitleTrackMenu
= new BMenu(B_TRANSLATE("Subtitles"));
1497 fAttributesMenu
= new BMenu(B_TRANSLATE("Attributes"));
1499 fMenuBar
->AddItem(fFileMenu
);
1500 fMenuBar
->AddItem(fAudioMenu
);
1501 fMenuBar
->AddItem(fVideoMenu
);
1502 fMenuBar
->AddItem(fAttributesMenu
);
1504 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("New player" B_UTF8_ELLIPSIS
),
1505 new BMessage(M_NEW_PLAYER
), 'N');
1506 fFileMenu
->AddItem(item
);
1507 item
->SetTarget(be_app
);
1509 // Add recent files to "Open File" entry as sub-menu.
1510 BRecentFilesList
recentFiles(10, false, NULL
, kAppSig
);
1511 item
= new BMenuItem(recentFiles
.NewFileListMenu(
1512 B_TRANSLATE("Open file" B_UTF8_ELLIPSIS
), NULL
, NULL
, this, 10, true,
1513 NULL
, kAppSig
), new BMessage(M_FILE_OPEN
));
1514 item
->SetShortcut('O', 0);
1515 fFileMenu
->AddItem(item
);
1517 item
= new BMenuItem(B_TRANSLATE("Open network stream"),
1518 new BMessage(M_NETWORK_STREAM_OPEN
));
1519 fFileMenu
->AddItem(item
);
1521 fFileMenu
->AddSeparatorItem();
1523 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("File info" B_UTF8_ELLIPSIS
),
1524 new BMessage(M_FILE_INFO
), 'I'));
1525 fFileMenu
->AddItem(fPlaylistMenu
);
1526 fPlaylistMenu
->Superitem()->SetShortcut('P', B_COMMAND_KEY
);
1527 fPlaylistMenu
->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST
));
1529 fFileMenu
->AddSeparatorItem();
1531 fNoInterfaceMenuItem
= new BMenuItem(B_TRANSLATE("Hide interface"),
1532 new BMessage(M_TOGGLE_NO_INTERFACE
), 'H');
1533 fFileMenu
->AddItem(fNoInterfaceMenuItem
);
1534 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Always on top"),
1535 new BMessage(M_TOGGLE_ALWAYS_ON_TOP
), 'A'));
1537 item
= new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS
),
1538 new BMessage(M_SETTINGS
), 'S');
1539 fFileMenu
->AddItem(item
);
1540 item
->SetTarget(be_app
);
1542 fFileMenu
->AddSeparatorItem();
1544 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1545 new BMessage(M_FILE_CLOSE
), 'W'));
1546 fFileMenu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1547 new BMessage(M_FILE_QUIT
), 'Q'));
1549 fPlaylistMenu
->SetRadioMode(true);
1551 fAudioMenu
->AddItem(fAudioTrackMenu
);
1553 fVideoMenu
->AddItem(fVideoTrackMenu
);
1554 fVideoMenu
->AddItem(fSubTitleTrackMenu
);
1555 fVideoMenu
->AddSeparatorItem();
1556 BMessage
* resizeMessage
= new BMessage(M_VIEW_SIZE
);
1557 resizeMessage
->AddInt32("size", 50);
1558 fVideoMenu
->AddItem(new BMenuItem(
1559 B_TRANSLATE("50% scale"), resizeMessage
, '0'));
1561 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1562 resizeMessage
->AddInt32("size", 100);
1563 fVideoMenu
->AddItem(new BMenuItem(
1564 B_TRANSLATE("100% scale"), resizeMessage
, '1'));
1566 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1567 resizeMessage
->AddInt32("size", 200);
1568 fVideoMenu
->AddItem(new BMenuItem(
1569 B_TRANSLATE("200% scale"), resizeMessage
, '2'));
1571 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1572 resizeMessage
->AddInt32("size", 300);
1573 fVideoMenu
->AddItem(new BMenuItem(
1574 B_TRANSLATE("300% scale"), resizeMessage
, '3'));
1576 resizeMessage
= new BMessage(M_VIEW_SIZE
);
1577 resizeMessage
->AddInt32("size", 400);
1578 fVideoMenu
->AddItem(new BMenuItem(
1579 B_TRANSLATE("400% scale"), resizeMessage
, '4'));
1581 fVideoMenu
->AddSeparatorItem();
1583 fVideoMenu
->AddItem(new BMenuItem(B_TRANSLATE("Full screen"),
1584 new BMessage(M_TOGGLE_FULLSCREEN
), B_ENTER
));
1586 fVideoMenu
->AddSeparatorItem();
1588 _SetupVideoAspectItems(fVideoAspectMenu
);
1589 fVideoMenu
->AddItem(fVideoAspectMenu
);
1591 fRatingMenu
= new BMenu(B_TRANSLATE("Rating"));
1592 fAttributesMenu
->AddItem(fRatingMenu
);
1593 for (int32 i
= 1; i
<= 10; i
++) {
1595 snprintf(label
, sizeof(label
), "%" B_PRId32
, i
);
1596 BMessage
* setRatingMsg
= new BMessage(M_SET_RATING
);
1597 setRatingMsg
->AddInt32("rating", i
);
1598 fRatingMenu
->AddItem(new BMenuItem(label
, setRatingMsg
));
1604 MainWin::_SetupVideoAspectItems(BMenu
* menu
)
1607 while ((item
= menu
->RemoveItem((int32
)0)) != NULL
)
1614 fController
->GetSize(&width
, &height
, &widthAspect
, &heightAspect
);
1615 // We don't care if there is a video track at all. In that
1616 // case we should end up not marking any item.
1618 // NOTE: The item marking may end up marking for example both
1619 // "Stream Settings" and "16 : 9" if the stream settings happen to
1622 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Stream settings"),
1623 new BMessage(M_ASPECT_SAME_AS_SOURCE
), '1', B_SHIFT_KEY
));
1624 item
->SetMarked(widthAspect
== fWidthAspect
1625 && heightAspect
== fHeightAspect
);
1627 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("No aspect correction"),
1628 new BMessage(M_ASPECT_NO_DISTORTION
), '0', B_SHIFT_KEY
));
1629 item
->SetMarked(width
== fWidthAspect
&& height
== fHeightAspect
);
1631 menu
->AddSeparatorItem();
1633 menu
->AddItem(item
= new BMenuItem("4 : 3",
1634 new BMessage(M_ASPECT_4_3
), 2, B_SHIFT_KEY
));
1635 item
->SetMarked(fWidthAspect
== 4 && fHeightAspect
== 3);
1636 menu
->AddItem(item
= new BMenuItem("16 : 9",
1637 new BMessage(M_ASPECT_16_9
), 3, B_SHIFT_KEY
));
1638 item
->SetMarked(fWidthAspect
== 16 && fHeightAspect
== 9);
1640 menu
->AddSeparatorItem();
1642 menu
->AddItem(item
= new BMenuItem("1.66 : 1",
1643 new BMessage(M_ASPECT_83_50
)));
1644 item
->SetMarked(fWidthAspect
== 83 && fHeightAspect
== 50);
1645 menu
->AddItem(item
= new BMenuItem("1.75 : 1",
1646 new BMessage(M_ASPECT_7_4
)));
1647 item
->SetMarked(fWidthAspect
== 7 && fHeightAspect
== 4);
1648 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("1.85 : 1 (American)"),
1649 new BMessage(M_ASPECT_37_20
)));
1650 item
->SetMarked(fWidthAspect
== 37 && fHeightAspect
== 20);
1651 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("2.35 : 1 (Cinemascope)"),
1652 new BMessage(M_ASPECT_47_20
)));
1653 item
->SetMarked(fWidthAspect
== 47 && fHeightAspect
== 20);
1658 MainWin::_SetupTrackMenus(BMenu
* audioTrackMenu
, BMenu
* videoTrackMenu
,
1659 BMenu
* subTitleTrackMenu
)
1661 audioTrackMenu
->RemoveItems(0, audioTrackMenu
->CountItems(), true);
1662 videoTrackMenu
->RemoveItems(0, videoTrackMenu
->CountItems(), true);
1663 subTitleTrackMenu
->RemoveItems(0, subTitleTrackMenu
->CountItems(), true);
1667 int count
= fController
->AudioTrackCount();
1668 int current
= fController
->CurrentAudioTrack();
1669 for (int i
= 0; i
< count
; i
++) {
1671 const char* languageString
= NULL
;
1672 if (fController
->GetAudioMetaData(i
, &metaData
) == B_OK
)
1673 metaData
.FindString("language", &languageString
);
1674 if (languageString
!= NULL
) {
1675 BLanguage
language(languageString
);
1676 BString languageName
;
1677 if (language
.GetName(languageName
) == B_OK
)
1678 languageString
= languageName
.String();
1679 snprintf(s
, sizeof(s
), "%s", languageString
);
1681 snprintf(s
, sizeof(s
), B_TRANSLATE("Track %d"), i
+ 1);
1682 BMenuItem
* item
= new BMenuItem(s
,
1683 new BMessage(M_SELECT_AUDIO_TRACK
+ i
));
1684 item
->SetMarked(i
== current
);
1685 audioTrackMenu
->AddItem(item
);
1688 audioTrackMenu
->AddItem(new BMenuItem(B_TRANSLATE_CONTEXT("none",
1689 "Audio track menu"), new BMessage(M_DUMMY
)));
1690 audioTrackMenu
->ItemAt(0)->SetMarked(true);
1692 audioTrackMenu
->SetEnabled(count
> 1);
1694 count
= fController
->VideoTrackCount();
1695 current
= fController
->CurrentVideoTrack();
1696 for (int i
= 0; i
< count
; i
++) {
1697 snprintf(s
, sizeof(s
), B_TRANSLATE("Track %d"), i
+ 1);
1698 BMenuItem
* item
= new BMenuItem(s
,
1699 new BMessage(M_SELECT_VIDEO_TRACK
+ i
));
1700 item
->SetMarked(i
== current
);
1701 videoTrackMenu
->AddItem(item
);
1704 videoTrackMenu
->AddItem(new BMenuItem(B_TRANSLATE("none"),
1705 new BMessage(M_DUMMY
)));
1706 videoTrackMenu
->ItemAt(0)->SetMarked(true);
1708 videoTrackMenu
->SetEnabled(count
> 1);
1710 count
= fController
->SubTitleTrackCount();
1712 current
= fController
->CurrentSubTitleTrack();
1713 BMenuItem
* item
= new BMenuItem(
1714 B_TRANSLATE_CONTEXT("Off", "Subtitles menu"),
1715 new BMessage(M_SELECT_SUB_TITLE_TRACK
- 1));
1716 subTitleTrackMenu
->AddItem(item
);
1717 item
->SetMarked(current
== -1);
1719 subTitleTrackMenu
->AddSeparatorItem();
1721 for (int i
= 0; i
< count
; i
++) {
1722 const char* name
= fController
->SubTitleTrackName(i
);
1724 snprintf(s
, sizeof(s
), "%s", name
);
1726 snprintf(s
, sizeof(s
), B_TRANSLATE("Track %d"), i
+ 1);
1727 item
= new BMenuItem(s
,
1728 new BMessage(M_SELECT_SUB_TITLE_TRACK
+ i
));
1729 item
->SetMarked(i
== current
);
1730 subTitleTrackMenu
->AddItem(item
);
1733 subTitleTrackMenu
->AddItem(new BMenuItem(
1734 B_TRANSLATE_CONTEXT("none", "Subtitles menu"),
1735 new BMessage(M_DUMMY
)));
1736 subTitleTrackMenu
->ItemAt(0)->SetMarked(true);
1738 subTitleTrackMenu
->SetEnabled(count
> 0);
1743 MainWin::_UpdateAudioChannelCount(int32 audioTrackIndex
)
1745 fControls
->SetAudioChannelCount(fController
->AudioTrackChannelCount());
1750 MainWin::_GetMinimumWindowSize(int& width
, int& height
) const
1754 if (!fNoInterface
) {
1755 width
= max_c(width
, fMenuBarWidth
);
1756 width
= max_c(width
, fControlsWidth
);
1757 height
= fMenuBarHeight
+ fControlsHeight
;
1763 MainWin::_GetUnscaledVideoSize(int& videoWidth
, int& videoHeight
) const
1765 if (fWidthAspect
!= 0 && fHeightAspect
!= 0) {
1766 videoWidth
= fSourceHeight
* fWidthAspect
/ fHeightAspect
;
1767 videoHeight
= fSourceWidth
* fHeightAspect
/ fWidthAspect
;
1768 // Use the scaling which produces an enlarged view.
1769 if (videoWidth
> fSourceWidth
) {
1771 videoHeight
= fSourceHeight
;
1774 videoWidth
= fSourceWidth
;
1777 videoWidth
= fSourceWidth
;
1778 videoHeight
= fSourceHeight
;
1784 MainWin::_SetWindowSizeLimits()
1788 _GetMinimumWindowSize(minWidth
, minHeight
);
1789 SetSizeLimits(minWidth
- 1, 32000, minHeight
- 1,
1790 fHasVideo
? 32000 : minHeight
- 1);
1795 MainWin::_CurrentVideoSizeInPercent() const
1802 _GetUnscaledVideoSize(videoWidth
, videoHeight
);
1804 int viewWidth
= fVideoView
->Bounds().IntegerWidth() + 1;
1805 int viewHeight
= fVideoView
->Bounds().IntegerHeight() + 1;
1807 int widthPercent
= viewWidth
* 100 / videoWidth
;
1808 int heightPercent
= viewHeight
* 100 / videoHeight
;
1810 if (widthPercent
> heightPercent
)
1811 return widthPercent
;
1812 return heightPercent
;
1817 MainWin::_ZoomVideoView(int percentDiff
)
1822 int percent
= _CurrentVideoSizeInPercent();
1823 int newSize
= percent
* (100 + percentDiff
) / 100;
1829 if (newSize
!= percent
) {
1830 BMessage
message(M_VIEW_SIZE
);
1831 message
.AddInt32("size", newSize
);
1832 PostMessage(&message
);
1838 MainWin::_ResizeWindow(int percent
, bool useNoVideoWidth
, bool stayOnScreen
)
1840 // Get required window size
1843 _GetUnscaledVideoSize(videoWidth
, videoHeight
);
1845 videoWidth
= (videoWidth
* percent
) / 100;
1846 videoHeight
= (videoHeight
* percent
) / 100;
1848 // Calculate and set the minimum window size
1851 _GetMinimumWindowSize(width
, height
);
1853 width
= max_c(width
, videoWidth
) - 1;
1854 if (useNoVideoWidth
)
1855 width
= max_c(width
, fNoVideoWidth
);
1856 height
= height
+ videoHeight
- 1;
1859 BRect
screenFrame(BScreen(this).Frame());
1860 BRect
frame(Frame());
1861 BRect
decoratorFrame(DecoratorFrame());
1863 // Shrink the screen frame by the window border size
1864 screenFrame
.top
+= frame
.top
- decoratorFrame
.top
;
1865 screenFrame
.left
+= frame
.left
- decoratorFrame
.left
;
1866 screenFrame
.right
+= frame
.right
- decoratorFrame
.right
;
1867 screenFrame
.bottom
+= frame
.bottom
- decoratorFrame
.bottom
;
1869 // Update frame to what the new size would be
1870 frame
.right
= frame
.left
+ width
;
1871 frame
.bottom
= frame
.top
+ height
;
1873 if (!screenFrame
.Contains(frame
)) {
1874 // Resize the window so it doesn't extend outside the current
1876 // We don't use BWindow::MoveOnScreen() in order to resize the
1877 // window while keeping the same aspect ratio.
1878 if (frame
.Width() > screenFrame
.Width()
1879 || frame
.Height() > screenFrame
.Height()) {
1882 = frame
.IntegerWidth() - screenFrame
.IntegerWidth();
1884 = frame
.IntegerHeight() - screenFrame
.IntegerHeight();
1887 if (widthDiff
> heightDiff
)
1888 shrinkScale
= (float)(width
- widthDiff
) / width
;
1890 shrinkScale
= (float)(height
- heightDiff
) / height
;
1892 // Resize width/height and center window
1893 width
= lround(width
* shrinkScale
);
1894 height
= lround(height
* shrinkScale
);
1895 MoveTo((screenFrame
.left
+ screenFrame
.right
- width
) / 2,
1896 (screenFrame
.top
+ screenFrame
.bottom
- height
) / 2);
1898 // just off-screen on one or more sides
1901 if (frame
.left
< screenFrame
.left
)
1902 offsetX
= (int)(screenFrame
.left
- frame
.left
);
1903 else if (frame
.right
> screenFrame
.right
)
1904 offsetX
= (int)(screenFrame
.right
- frame
.right
);
1905 if (frame
.top
< screenFrame
.top
)
1906 offsetY
= (int)(screenFrame
.top
- frame
.top
);
1907 else if (frame
.bottom
> screenFrame
.bottom
)
1908 offsetY
= (int)(screenFrame
.bottom
- frame
.bottom
);
1909 MoveBy(offsetX
, offsetY
);
1914 ResizeTo(width
, height
);
1919 MainWin::_ResizeVideoView(int x
, int y
, int width
, int height
)
1921 // Keep aspect ratio, place video view inside
1922 // the background area (may create black bars).
1925 _GetUnscaledVideoSize(videoWidth
, videoHeight
);
1926 float scaledWidth
= videoWidth
;
1927 float scaledHeight
= videoHeight
;
1928 float factor
= min_c(width
/ scaledWidth
, height
/ scaledHeight
);
1929 int renderWidth
= lround(scaledWidth
* factor
);
1930 int renderHeight
= lround(scaledHeight
* factor
);
1931 if (renderWidth
> width
)
1932 renderWidth
= width
;
1933 if (renderHeight
> height
)
1934 renderHeight
= height
;
1936 int xOffset
= (width
- renderWidth
) / 2;
1937 int yOffset
= (height
- renderHeight
) / 2;
1939 fVideoView
->MoveTo(x
, y
);
1940 fVideoView
->ResizeTo(width
- 1, height
- 1);
1942 BRect
videoFrame(xOffset
, yOffset
,
1943 xOffset
+ renderWidth
- 1, yOffset
+ renderHeight
- 1);
1945 fVideoView
->SetVideoFrame(videoFrame
);
1946 fVideoView
->SetSubTitleMaxBottom(height
- 1);
1954 MainWin::_MouseDown(BMessage
* msg
, BView
* originalHandler
)
1956 uint32 buttons
= msg
->FindInt32("buttons");
1958 // On Zeta, only "screen_where" is reliable, "where" and "be:view_where"
1959 // seem to be broken
1961 if (msg
->FindPoint("screen_where", &screenWhere
) != B_OK
) {
1963 // Workaround for BeOS R5, it has no "screen_where"
1964 if (!originalHandler
|| msg
->FindPoint("where", &screenWhere
) < B_OK
)
1966 originalHandler
->ConvertToScreen(&screenWhere
);
1969 // double click handling
1971 if (msg
->FindInt32("clicks") % 2 == 0) {
1972 BRect
rect(screenWhere
.x
- 1, screenWhere
.y
- 1, screenWhere
.x
+ 1,
1974 if (rect
.Contains(fMouseDownMousePos
)) {
1975 if (buttons
== B_PRIMARY_MOUSE_BUTTON
)
1976 PostMessage(M_TOGGLE_FULLSCREEN
);
1977 else if (buttons
== B_SECONDARY_MOUSE_BUTTON
)
1978 PostMessage(M_TOGGLE_NO_INTERFACE
);
1984 fMouseDownMousePos
= screenWhere
;
1985 fMouseDownWindowPos
= Frame().LeftTop();
1987 if (buttons
== B_PRIMARY_MOUSE_BUTTON
&& !fIsFullscreen
) {
1988 // start mouse tracking
1989 fVideoView
->SetMouseEventMask(B_POINTER_EVENTS
| B_NO_POINTER_HISTORY
1990 /* | B_LOCK_WINDOW_FOCUS */);
1991 fMouseDownTracking
= true;
1994 // pop up a context menu if right mouse button is down
1996 if ((buttons
& B_SECONDARY_MOUSE_BUTTON
) != 0)
1997 _ShowContextMenu(screenWhere
);
2002 MainWin::_MouseMoved(BMessage
* msg
, BView
* originalHandler
)
2004 // msg->PrintToStream();
2007 uint32 buttons
= msg
->FindInt32("buttons");
2008 // On Zeta, only "screen_where" is reliable, "where"
2009 // and "be:view_where" seem to be broken
2010 if (msg
->FindPoint("screen_where", &mousePos
) != B_OK
) {
2012 // Workaround for BeOS R5, it has no "screen_where"
2013 if (!originalHandler
|| msg
->FindPoint("where", &mousePos
) < B_OK
)
2015 originalHandler
->ConvertToScreen(&mousePos
);
2018 if (buttons
== B_PRIMARY_MOUSE_BUTTON
&& fMouseDownTracking
2019 && !fIsFullscreen
) {
2020 // printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y);
2021 float delta_x
= mousePos
.x
- fMouseDownMousePos
.x
;
2022 float delta_y
= mousePos
.y
- fMouseDownMousePos
.y
;
2023 float x
= fMouseDownWindowPos
.x
+ delta_x
;
2024 float y
= fMouseDownWindowPos
.y
+ delta_y
;
2025 // printf("move window to %.0f, %.0f\n", x, y);
2029 bigtime_t eventTime
;
2030 if (msg
->FindInt64("when", &eventTime
) != B_OK
)
2031 eventTime
= system_time();
2033 if (buttons
== 0 && fIsFullscreen
) {
2034 BPoint moveDelta
= mousePos
- fLastMousePos
;
2036 = sqrtf(moveDelta
.x
* moveDelta
.x
+ moveDelta
.y
* moveDelta
.y
);
2037 if (eventTime
- fLastMouseMovedTime
< 200000)
2038 fMouseMoveDist
+= moveDeltaDist
;
2040 fMouseMoveDist
= moveDeltaDist
;
2041 if (fMouseMoveDist
> 5)
2042 _ShowFullscreenControls(true);
2045 fLastMousePos
= mousePos
;
2046 fLastMouseMovedTime
=eventTime
;
2051 MainWin::_MouseUp(BMessage
* msg
)
2053 fMouseDownTracking
= false;
2058 MainWin::_ShowContextMenu(const BPoint
& screenPoint
)
2060 printf("Show context menu\n");
2061 BPopUpMenu
* menu
= new BPopUpMenu("context menu", false, false);
2063 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Full screen"),
2064 new BMessage(M_TOGGLE_FULLSCREEN
), B_ENTER
));
2065 item
->SetMarked(fIsFullscreen
);
2066 item
->SetEnabled(fHasVideo
);
2068 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Hide interface"),
2069 new BMessage(M_TOGGLE_NO_INTERFACE
), 'H'));
2070 item
->SetMarked(fNoInterface
);
2071 item
->SetEnabled(fHasVideo
&& !fIsFullscreen
);
2073 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Always on top"),
2074 new BMessage(M_TOGGLE_ALWAYS_ON_TOP
), 'A'));
2075 item
->SetMarked(fAlwaysOnTop
);
2076 item
->SetEnabled(fHasVideo
);
2078 BMenu
* aspectSubMenu
= new BMenu(B_TRANSLATE("Aspect ratio"));
2079 _SetupVideoAspectItems(aspectSubMenu
);
2080 aspectSubMenu
->SetTargetForItems(this);
2081 menu
->AddItem(item
= new BMenuItem(aspectSubMenu
));
2082 item
->SetEnabled(fHasVideo
);
2084 menu
->AddSeparatorItem();
2086 // Add track selector menus
2087 BMenu
* audioTrackMenu
= new BMenu(B_TRANSLATE("Audio track"));
2088 BMenu
* videoTrackMenu
= new BMenu(B_TRANSLATE("Video track"));
2089 BMenu
* subTitleTrackMenu
= new BMenu(B_TRANSLATE("Subtitles"));
2090 _SetupTrackMenus(audioTrackMenu
, videoTrackMenu
, subTitleTrackMenu
);
2092 audioTrackMenu
->SetTargetForItems(this);
2093 videoTrackMenu
->SetTargetForItems(this);
2094 subTitleTrackMenu
->SetTargetForItems(this);
2096 menu
->AddItem(item
= new BMenuItem(audioTrackMenu
));
2097 item
->SetEnabled(fHasAudio
);
2099 menu
->AddItem(item
= new BMenuItem(videoTrackMenu
));
2100 item
->SetEnabled(fHasVideo
);
2102 menu
->AddItem(item
= new BMenuItem(subTitleTrackMenu
));
2103 item
->SetEnabled(fHasVideo
);
2105 menu
->AddSeparatorItem();
2106 menu
->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(M_FILE_QUIT
), 'Q'));
2108 menu
->SetTargetForItems(this);
2109 BRect
rect(screenPoint
.x
- 5, screenPoint
.y
- 5, screenPoint
.x
+ 5,
2111 menu
->Go(screenPoint
, true, true, rect
, true);
2115 /*! Trap keys that are about to be send to background or renderer view.
2116 Return true if it shouldn't be passed to the view.
2119 MainWin::_KeyDown(BMessage
* msg
)
2121 uint32 key
= msg
->FindInt32("key");
2122 uint32 rawChar
= msg
->FindInt32("raw_char");
2123 uint32 modifier
= msg
->FindInt32("modifiers");
2125 // printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar,
2128 // ignore the system modifier namespace
2129 if ((modifier
& (B_CONTROL_KEY
| B_COMMAND_KEY
))
2130 == (B_CONTROL_KEY
| B_COMMAND_KEY
))
2135 fController
->TogglePlaying();
2139 fController
->ToggleMute();
2146 PostMessage(M_TOGGLE_FULLSCREEN
);
2149 case B_ENTER
: // Enter / Return
2150 if ((modifier
& B_COMMAND_KEY
) != 0) {
2151 PostMessage(M_TOGGLE_FULLSCREEN
);
2158 if ((modifier
& (B_COMMAND_KEY
| B_CONTROL_KEY
| B_OPTION_KEY
2159 | B_MENU_KEY
)) == 0) {
2160 PostMessage(M_TOGGLE_FULLSCREEN
);
2166 if ((modifier
& B_COMMAND_KEY
) != 0)
2167 PostMessage(M_SKIP_NEXT
);
2169 PostMessage(M_VOLUME_UP
);
2173 if ((modifier
& B_COMMAND_KEY
) != 0)
2174 PostMessage(M_SKIP_PREV
);
2176 PostMessage(M_VOLUME_DOWN
);
2180 if ((modifier
& B_COMMAND_KEY
) != 0)
2181 PostMessage(M_SKIP_NEXT
);
2182 else if (fAllowWinding
) {
2183 BMessage
windMessage(M_WIND
);
2184 if ((modifier
& B_SHIFT_KEY
) != 0) {
2185 windMessage
.AddInt64("how much", 30000000LL);
2186 windMessage
.AddInt64("frames", 5);
2188 windMessage
.AddInt64("how much", 5000000LL);
2189 windMessage
.AddInt64("frames", 1);
2191 PostMessage(&windMessage
);
2196 if ((modifier
& B_COMMAND_KEY
) != 0)
2197 PostMessage(M_SKIP_PREV
);
2198 else if (fAllowWinding
) {
2199 BMessage
windMessage(M_WIND
);
2200 if ((modifier
& B_SHIFT_KEY
) != 0) {
2201 windMessage
.AddInt64("how much", -30000000LL);
2202 windMessage
.AddInt64("frames", -5);
2204 windMessage
.AddInt64("how much", -5000000LL);
2205 windMessage
.AddInt64("frames", -1);
2207 PostMessage(&windMessage
);
2212 PostMessage(M_SKIP_NEXT
);
2216 PostMessage(M_SKIP_PREV
);
2220 if ((modifier
& B_COMMAND_KEY
) == 0) {
2227 if ((modifier
& B_COMMAND_KEY
) == 0) {
2228 _ZoomVideoView(-10);
2234 case 'd': // d for delete
2235 case 't': // t for Trash
2236 if ((modifiers() & B_COMMAND_KEY
) != 0) {
2237 BAutolock
_(fPlaylist
);
2238 BMessage
removeMessage(M_PLAYLIST_MOVE_TO_TRASH
);
2239 removeMessage
.AddInt32("playlist index",
2240 fPlaylist
->CurrentItemIndex());
2241 fPlaylistWindow
->PostMessage(&removeMessage
);
2248 case 0x3a: // numeric keypad +
2249 if ((modifier
& B_COMMAND_KEY
) == 0) {
2255 case 0x25: // numeric keypad -
2256 if ((modifier
& B_COMMAND_KEY
) == 0) {
2257 _ZoomVideoView(-10);
2262 case 0x38: // numeric keypad up arrow
2263 PostMessage(M_VOLUME_UP
);
2266 case 0x59: // numeric keypad down arrow
2267 PostMessage(M_VOLUME_DOWN
);
2270 case 0x39: // numeric keypad page up
2271 case 0x4a: // numeric keypad right arrow
2272 PostMessage(M_SKIP_NEXT
);
2275 case 0x5a: // numeric keypad page down
2276 case 0x48: // numeric keypad left arrow
2277 PostMessage(M_SKIP_PREV
);
2280 // Playback controls along the bottom of the keyboard:
2281 // Z X C (V) B for US International
2283 PostMessage(M_SKIP_PREV
);
2286 fController
->TogglePlaying();
2289 fController
->Pause();
2292 fController
->Stop();
2295 PostMessage(M_SKIP_NEXT
);
2307 MainWin::_ToggleFullscreen()
2309 printf("_ToggleFullscreen enter\n");
2312 printf("_ToggleFullscreen - ignoring, as we don't have a video\n");
2316 fIsFullscreen
= !fIsFullscreen
;
2318 if (fIsFullscreen
) {
2319 // switch to fullscreen
2321 fSavedFrame
= Frame();
2322 printf("saving current frame: %d %d %d %d\n", int(fSavedFrame
.left
),
2323 int(fSavedFrame
.top
), int(fSavedFrame
.right
),
2324 int(fSavedFrame
.bottom
));
2325 BScreen
screen(this);
2326 BRect
rect(screen
.Frame());
2329 MoveTo(rect
.left
, rect
.top
);
2330 ResizeTo(rect
.Width(), rect
.Height());
2334 // switch back from full screen mode
2335 _ShowFullscreenControls(false, false);
2338 MoveTo(fSavedFrame
.left
, fSavedFrame
.top
);
2339 ResizeTo(fSavedFrame
.Width(), fSavedFrame
.Height());
2343 fVideoView
->SetFullscreen(fIsFullscreen
);
2345 _MarkItem(fFileMenu
, M_TOGGLE_FULLSCREEN
, fIsFullscreen
);
2347 printf("_ToggleFullscreen leave\n");
2351 MainWin::_ToggleAlwaysOnTop()
2353 fAlwaysOnTop
= !fAlwaysOnTop
;
2354 SetFeel(fAlwaysOnTop
? B_FLOATING_ALL_WINDOW_FEEL
: B_NORMAL_WINDOW_FEEL
);
2356 _MarkItem(fFileMenu
, M_TOGGLE_ALWAYS_ON_TOP
, fAlwaysOnTop
);
2361 MainWin::_ToggleNoInterface()
2363 printf("_ToggleNoInterface enter\n");
2365 if (fIsFullscreen
|| !fHasVideo
) {
2366 // Fullscreen playback is always without interface and
2367 // audio playback is always with interface. So we ignore these
2369 printf("_ToggleNoControls leave, doing nothing, we are fullscreen\n");
2373 fNoInterface
= !fNoInterface
;
2374 _SetWindowSizeLimits();
2377 MoveBy(0, fMenuBarHeight
);
2378 ResizeBy(0, -(fControlsHeight
+ fMenuBarHeight
));
2379 SetLook(B_BORDERED_WINDOW_LOOK
);
2381 MoveBy(0, -fMenuBarHeight
);
2382 ResizeBy(0, fControlsHeight
+ fMenuBarHeight
);
2383 SetLook(B_TITLED_WINDOW_LOOK
);
2386 _MarkItem(fFileMenu
, M_TOGGLE_NO_INTERFACE
, fNoInterface
);
2388 printf("_ToggleNoInterface leave\n");
2393 MainWin::_ShowIfNeeded()
2395 // Only proceed if the window is already running
2396 if (find_thread(NULL
) != Thread())
2399 if (!fHasVideo
&& fNoVideoFrame
.IsValid()) {
2400 MoveTo(fNoVideoFrame
.LeftTop());
2401 ResizeTo(fNoVideoFrame
.Width(), fNoVideoFrame
.Height());
2402 MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN
);
2403 } else if (fHasVideo
&& IsHidden())
2406 fNoVideoFrame
= BRect();
2416 MainWin::_ShowFullscreenControls(bool show
, bool animate
)
2418 if (fShowsFullscreenControls
== show
)
2421 fShowsFullscreenControls
= show
;
2422 fVideoView
->SetFullscreenControlsVisible(show
);
2425 fControls
->RemoveSelf();
2426 fControls
->MoveTo(fVideoView
->Bounds().left
,
2427 fVideoView
->Bounds().bottom
+ 1);
2428 fVideoView
->AddChild(fControls
);
2429 if (fScaleFullscreenControls
)
2430 fControls
->SetSymbolScale(1.5f
);
2432 while (fControls
->IsHidden())
2437 // Slide the controls into view. We need to do this with
2438 // messages, otherwise we block the video playback for the
2439 // time of the animation.
2440 const float kAnimationOffsets
[] = { 0.05, 0.2, 0.5, 0.2, 0.05 };
2441 const int32 steps
= sizeof(kAnimationOffsets
) / sizeof(float);
2442 float height
= fControls
->Bounds().Height();
2443 float moveDist
= show
? -height
: height
;
2444 float originalY
= fControls
->Frame().top
;
2445 for (int32 i
= 0; i
< steps
; i
++) {
2446 BMessage
message(M_SLIDE_CONTROLS
);
2447 message
.AddFloat("offset",
2448 floorf(moveDist
* kAnimationOffsets
[i
]));
2449 PostMessage(&message
, this);
2451 BMessage
finalMessage(M_FINISH_SLIDING_CONTROLS
);
2452 finalMessage
.AddFloat("offset", originalY
+ moveDist
);
2453 finalMessage
.AddBool("show", show
);
2454 PostMessage(&finalMessage
, this);
2456 fControls
->RemoveSelf();
2457 fControls
->MoveTo(fVideoView
->Frame().left
,
2458 fVideoView
->Frame().bottom
+ 1);
2459 fBackground
->AddChild(fControls
);
2460 fControls
->SetSymbolScale(1.0f
);
2462 while (!fControls
->IsHidden())
2472 MainWin::_Wind(bigtime_t howMuch
, int64 frames
)
2474 if (!fAllowWinding
|| !fController
->Lock())
2477 if (frames
!= 0 && fHasVideo
&& !fController
->IsPlaying()) {
2478 int64 newFrame
= fController
->CurrentFrame() + frames
;
2479 fController
->SetFramePosition(newFrame
);
2481 bigtime_t seekTime
= fController
->TimePosition() + howMuch
;
2483 fInitialSeekPosition
= seekTime
;
2484 PostMessage(M_SKIP_PREV
);
2485 } else if (seekTime
> fController
->TimeDuration()) {
2486 fInitialSeekPosition
= 0;
2487 PostMessage(M_SKIP_NEXT
);
2489 fController
->SetTimePosition(seekTime
);
2492 fController
->Unlock();
2493 fAllowWinding
= false;
2501 MainWin::_UpdatePlaylistItemFile()
2503 BAutolock
locker(fPlaylist
);
2504 const FilePlaylistItem
* item
2505 = dynamic_cast<const FilePlaylistItem
*>(fController
->Item());
2509 if (!fHasVideo
&& !fHasAudio
)
2512 BNode
node(&item
->Ref());
2513 if (node
.InitCheck())
2518 // Set some standard attributes of the currently played file.
2519 // This should only be a temporary solution.
2522 const char* kDurationAttrName
= "Media:Length";
2524 status_t status
= node
.GetAttrInfo(kDurationAttrName
, &info
);
2525 if (status
!= B_OK
|| info
.size
== 0) {
2526 bigtime_t duration
= fController
->TimeDuration();
2527 // TODO: Tracker does not seem to care about endian for scalar types
2528 node
.WriteAttr(kDurationAttrName
, B_INT64_TYPE
, 0, &duration
,
2532 // Write audio bitrate
2534 status
= node
.GetAttrInfo("Audio:Bitrate", &info
);
2535 if (status
!= B_OK
|| info
.size
== 0) {
2536 media_format format
;
2537 if (fController
->GetEncodedAudioFormat(&format
) == B_OK
2538 && format
.type
== B_MEDIA_ENCODED_AUDIO
) {
2539 int32 bitrate
= (int32
)(format
.u
.encoded_audio
.bit_rate
2542 snprintf(text
, sizeof(text
), "%" B_PRId32
" kbit", bitrate
);
2543 node
.WriteAttr("Audio:Bitrate", B_STRING_TYPE
, 0, text
,
2549 // Write video bitrate
2551 status
= node
.GetAttrInfo("Video:Bitrate", &info
);
2552 if (status
!= B_OK
|| info
.size
== 0) {
2553 media_format format
;
2554 if (fController
->GetEncodedVideoFormat(&format
) == B_OK
2555 && format
.type
== B_MEDIA_ENCODED_VIDEO
) {
2556 int32 bitrate
= (int32
)(format
.u
.encoded_video
.avg_bit_rate
2559 snprintf(text
, sizeof(text
), "%" B_PRId32
" kbit", bitrate
);
2560 node
.WriteAttr("Video:Bitrate", B_STRING_TYPE
, 0, text
,
2566 _UpdateAttributesMenu(node
);
2571 MainWin::_UpdateAttributesMenu(const BNode
& node
)
2576 status_t status
= node
.GetAttrInfo(kRatingAttrName
, &info
);
2577 if (status
== B_OK
&& info
.type
== B_INT32_TYPE
) {
2578 // Node has the Rating attribute.
2579 node
.ReadAttr(kRatingAttrName
, B_INT32_TYPE
, 0, &rating
,
2583 for (int32 i
= 0; BMenuItem
* item
= fRatingMenu
->ItemAt(i
); i
++)
2584 item
->SetMarked(i
+ 1 == rating
);
2589 MainWin::_SetRating(int32 rating
)
2591 BAutolock
locker(fPlaylist
);
2592 const FilePlaylistItem
* item
2593 = dynamic_cast<const FilePlaylistItem
*>(fController
->Item());
2597 BNode
node(&item
->Ref());
2598 if (node
.InitCheck())
2603 node
.WriteAttr(kRatingAttrName
, B_INT32_TYPE
, 0, &rating
, sizeof(rating
));
2605 // TODO: The whole mechnism should work like this:
2606 // * There is already an attribute API for PlaylistItem, flesh it out!
2607 // * FilePlaylistItem node-monitors it's file somehow.
2608 // * FilePlaylistItem keeps attributes in sync and sends notications.
2609 // * MainWin updates the menu according to FilePlaylistItem notifications.
2610 // * PlaylistWin shows columns with attribute and other info.
2611 // * PlaylistWin updates also upon FilePlaylistItem notifications.
2612 // * This keeps attributes in sync when another app changes them.
2614 _UpdateAttributesMenu(node
);
2619 MainWin::_UpdateControlsEnabledStatus()
2621 uint32 enabledButtons
= 0;
2622 if (fHasVideo
|| fHasAudio
) {
2623 enabledButtons
|= PLAYBACK_ENABLED
| SEEK_ENABLED
2624 | SEEK_BACK_ENABLED
| SEEK_FORWARD_ENABLED
;
2627 enabledButtons
|= VOLUME_ENABLED
;
2629 BAutolock
_(fPlaylist
);
2630 bool canSkipPrevious
, canSkipNext
;
2631 fPlaylist
->GetSkipInfo(&canSkipPrevious
, &canSkipNext
);
2632 if (canSkipPrevious
)
2633 enabledButtons
|= SKIP_BACK_ENABLED
;
2635 enabledButtons
|= SKIP_FORWARD_ENABLED
;
2637 fControls
->SetEnabled(enabledButtons
);
2639 fNoInterfaceMenuItem
->SetEnabled(fHasVideo
);
2640 fAttributesMenu
->SetEnabled(fHasAudio
|| fHasVideo
);
2645 MainWin::_UpdatePlaylistMenu()
2647 if (!fPlaylist
->Lock())
2650 fPlaylistMenu
->RemoveItems(0, fPlaylistMenu
->CountItems(), true);
2652 int32 count
= fPlaylist
->CountItems();
2653 for (int32 i
= 0; i
< count
; i
++) {
2654 PlaylistItem
* item
= fPlaylist
->ItemAtFast(i
);
2655 _AddPlaylistItem(item
, i
);
2657 fPlaylistMenu
->SetTargetForItems(this);
2659 _MarkPlaylistItem(fPlaylist
->CurrentItemIndex());
2661 fPlaylist
->Unlock();
2666 MainWin::_AddPlaylistItem(PlaylistItem
* item
, int32 index
)
2668 BMessage
* message
= new BMessage(M_SET_PLAYLIST_POSITION
);
2669 message
->AddInt32("index", index
);
2670 BMenuItem
* menuItem
= new BMenuItem(item
->Name().String(), message
);
2671 fPlaylistMenu
->AddItem(menuItem
, index
);
2676 MainWin::_RemovePlaylistItem(int32 index
)
2678 delete fPlaylistMenu
->RemoveItem(index
);
2683 MainWin::_MarkPlaylistItem(int32 index
)
2685 if (BMenuItem
* item
= fPlaylistMenu
->ItemAt(index
)) {
2686 item
->SetMarked(true);
2687 // ... and in case the menu is currently on screen:
2688 if (fPlaylistMenu
->LockLooper()) {
2689 fPlaylistMenu
->Invalidate();
2690 fPlaylistMenu
->UnlockLooper();
2697 MainWin::_MarkItem(BMenu
* menu
, uint32 command
, bool mark
)
2699 if (BMenuItem
* item
= menu
->FindItem(command
))
2700 item
->SetMarked(mark
);
2705 MainWin::_AdoptGlobalSettings()
2707 mpSettings settings
;
2708 Settings::Default()->Get(settings
);
2710 fCloseWhenDonePlayingMovie
= settings
.closeWhenDonePlayingMovie
;
2711 fCloseWhenDonePlayingSound
= settings
.closeWhenDonePlayingSound
;
2712 fLoopMovies
= settings
.loopMovie
;
2713 fLoopSounds
= settings
.loopSound
;
2714 fScaleFullscreenControls
= settings
.scaleFullscreenControls
;