MainWin.cpp revision 6ba5b421
1/*
2 * MainWin.cpp - Media Player for the Haiku Operating System
3 *
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)
7 *
8 * Released under the terms of the MIT license.
9 */
10
11
12#include "MainWin.h"
13
14#include <math.h>
15#include <stdio.h>
16#include <string.h>
17
18#include <Alert.h>
19#include <Application.h>
20#include <Autolock.h>
21#include <Catalog.h>
22#include <Debug.h>
23#include <fs_attr.h>
24#include <LayoutBuilder.h>
25#include <Language.h>
26#include <Locale.h>
27#include <MediaRoster.h>
28#include <Menu.h>
29#include <MenuBar.h>
30#include <MenuItem.h>
31#include <MessageRunner.h>
32#include <Messenger.h>
33#include <PopUpMenu.h>
34#include <PropertyInfo.h>
35#include <RecentItems.h>
36#include <Roster.h>
37#include <Screen.h>
38#include <String.h>
39#include <TypeConstants.h>
40#include <View.h>
41
42#include "AudioProducer.h"
43#include "ControllerObserver.h"
44#include "DurationToString.h"
45#include "FilePlaylistItem.h"
46#include "MainApp.h"
47#include "NetworkStreamWin.h"
48#include "PeakView.h"
49#include "PlaylistItem.h"
50#include "PlaylistObserver.h"
51#include "PlaylistWindow.h"
52#include "Settings.h"
53
54
55#undef B_TRANSLATION_CONTEXT
56#define B_TRANSLATION_CONTEXT "MediaPlayer-Main"
57#define MIN_WIDTH 250
58
59
60int MainWin::sNoVideoWidth = MIN_WIDTH;
61
62
63// XXX TODO: why is lround not defined?
64#define lround(a) ((int)(0.99999 + (a)))
65
66enum {
67	M_DUMMY = 0x100,
68	M_FILE_OPEN = 0x1000,
69	M_NETWORK_STREAM_OPEN,
70	M_FILE_INFO,
71	M_FILE_PLAYLIST,
72	M_FILE_CLOSE,
73	M_FILE_QUIT,
74	M_VIEW_SIZE,
75	M_TOGGLE_FULLSCREEN,
76	M_TOGGLE_ALWAYS_ON_TOP,
77	M_TOGGLE_NO_INTERFACE,
78	M_VOLUME_UP,
79	M_VOLUME_DOWN,
80	M_SKIP_NEXT,
81	M_SKIP_PREV,
82	M_WIND,
83
84	// The common display aspect ratios
85	M_ASPECT_SAME_AS_SOURCE,
86	M_ASPECT_NO_DISTORTION,
87	M_ASPECT_4_3,
88	M_ASPECT_16_9,
89	M_ASPECT_83_50,
90	M_ASPECT_7_4,
91	M_ASPECT_37_20,
92	M_ASPECT_47_20,
93
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,
100
101	M_SET_RATING,
102
103	M_SET_PLAYLIST_POSITION,
104
105	M_FILE_DELETE,
106
107	M_SLIDE_CONTROLS,
108	M_FINISH_SLIDING_CONTROLS
109};
110
111
112static property_info sPropertyInfo[] = {
113	{ "Next", { B_EXECUTE_PROPERTY },
114		{ B_DIRECT_SPECIFIER, 0 },
115		"Skip to the next track.", 0
116	},
117	{ "Prev", { B_EXECUTE_PROPERTY },
118		{ B_DIRECT_SPECIFIER, 0 },
119		"Skip to the previous track.", 0
120	},
121	{ "Play", { B_EXECUTE_PROPERTY },
122		{ B_DIRECT_SPECIFIER, 0 },
123		"Start playing.", 0
124	},
125	{ "Stop", { B_EXECUTE_PROPERTY },
126		{ B_DIRECT_SPECIFIER, 0 },
127		"Stop playing.", 0
128	},
129	{ "Pause", { B_EXECUTE_PROPERTY },
130		{ B_DIRECT_SPECIFIER, 0 },
131		"Pause playback.", 0
132	},
133	{ "TogglePlaying", { B_EXECUTE_PROPERTY },
134		{ B_DIRECT_SPECIFIER, 0 },
135		"Toggle pause/play.", 0
136	},
137	{ "Mute", { B_EXECUTE_PROPERTY },
138		{ B_DIRECT_SPECIFIER, 0 },
139		"Toggle mute.", 0
140	},
141	{ "Volume", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
142		{ B_DIRECT_SPECIFIER, 0 },
143		"Gets/sets the volume (0.0-2.0).", 0,
144		{ B_FLOAT_TYPE }
145	},
146	{ "URI", { B_GET_PROPERTY, 0 },
147		{ B_DIRECT_SPECIFIER, 0 },
148		"Gets the URI of the currently playing item.", 0,
149		{ B_STRING_TYPE }
150	},
151	{ "ToggleFullscreen", { B_EXECUTE_PROPERTY },
152		{ B_DIRECT_SPECIFIER, 0 },
153		"Toggle fullscreen.", 0
154	},
155	{ "Duration", { B_GET_PROPERTY, 0 },
156		{ B_DIRECT_SPECIFIER, 0 },
157		"Gets the duration of the currently playing item "
158			"in microseconds.", 0,
159		{ B_INT64_TYPE }
160	},
161	{ "Position", { B_GET_PROPERTY, B_SET_PROPERTY, 0 },
162		{ B_DIRECT_SPECIFIER, 0 },
163		"Gets/sets the current playing position in microseconds.",
164		0, { B_INT64_TYPE }
165	},
166	{ "Seek", { B_SET_PROPERTY },
167		{ B_DIRECT_SPECIFIER, 0 },
168		"Seek by the specified amounts of microseconds.", 0,
169		{ B_INT64_TYPE }
170	},
171	{ "PlaylistTrackCount", { B_GET_PROPERTY, 0 },
172		{ B_DIRECT_SPECIFIER, 0 },
173		"Gets the number of tracks in Playlist.", 0,
174		{ B_INT16_TYPE }
175	},
176	{ "PlaylistTrackTitle", { B_GET_PROPERTY, 0 },
177		{ B_INDEX_SPECIFIER, 0 },
178		"Gets the title of the nth track in Playlist.", 0,
179		{ B_STRING_TYPE }
180	},
181
182	{ 0 }
183};
184
185
186static const char* kRatingAttrName = "Media:Rating";
187
188static const char* kDisabledSeekMessage = B_TRANSLATE("Drop files to play");
189
190static const char* kApplicationName = B_TRANSLATE_SYSTEM_NAME(NAME);
191
192
193MainWin::MainWin(bool isFirstWindow, BMessage* message)
194	:
195	BWindow(BRect(100, 100, 400, 300), kApplicationName, B_TITLED_WINDOW,
196 		B_ASYNCHRONOUS_CONTROLS),
197 	fCreationTime(system_time()),
198	fInfoWin(NULL),
199	fPlaylistWindow(NULL),
200	fHasFile(false),
201	fHasVideo(false),
202	fHasAudio(false),
203	fPlaylist(new Playlist),
204	fPlaylistObserver(new PlaylistObserver(this)),
205	fController(new Controller),
206	fControllerObserver(new ControllerObserver(this,
207		OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES
208			| OBSERVE_PLAYBACK_STATE_CHANGES | OBSERVE_POSITION_CHANGES
209			| OBSERVE_VOLUME_CHANGES)),
210	fIsFullscreen(false),
211	fAlwaysOnTop(false),
212	fNoInterface(false),
213	fShowsFullscreenControls(false),
214	fSourceWidth(-1),
215	fSourceHeight(-1),
216	fWidthAspect(0),
217	fHeightAspect(0),
218	fSavedFrame(),
219	fNoVideoFrame(),
220
221	fMouseDownTracking(false),
222	fLastMousePos(0, 0),
223	fLastMouseMovedTime(system_time()),
224	fMouseMoveDist(0),
225
226	fGlobalSettingsListener(this),
227	fInitialSeekPosition(0),
228	fAllowWinding(true)
229{
230	// Handle window position and size depending on whether this is the
231	// first window or not. Use the window size from the window that was
232	// last resized by the user.
233	static int pos = 0;
234	MoveBy(pos * 25, pos * 25);
235	pos = (pos + 1) % 15;
236
237	BRect frame = Settings::Default()->AudioPlayerWindowFrame();
238	if (frame.IsValid()) {
239		if (isFirstWindow) {
240			if (message == NULL) {
241				MoveTo(frame.LeftTop());
242				ResizeTo(frame.Width(), frame.Height());
243			} else {
244				// Delay moving to the initial position, since we don't
245				// know if we will be playing audio at all.
246				message->AddRect("window frame", frame);
247			}
248		}
249		if (sNoVideoWidth == MIN_WIDTH)
250			sNoVideoWidth = frame.IntegerWidth();
251	} else if (sNoVideoWidth > MIN_WIDTH) {
252		ResizeTo(sNoVideoWidth, Bounds().Height());
253	}
254	fNoVideoWidth = sNoVideoWidth;
255
256	BRect rect = Bounds();
257
258	// background
259	fBackground = new BView(rect, "background", B_FOLLOW_ALL,
260		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
261	fBackground->SetViewColor(0, 0, 0);
262	AddChild(fBackground);
263
264	// menu
265	fMenuBar = new BMenuBar(fBackground->Bounds(), "menu");
266	_CreateMenu();
267	fBackground->AddChild(fMenuBar);
268	fMenuBar->SetResizingMode(B_FOLLOW_NONE);
269	fMenuBar->ResizeToPreferred();
270	fMenuBarWidth = (int)fMenuBar->Frame().Width() + 1;
271	fMenuBarHeight = (int)fMenuBar->Frame().Height() + 1;
272
273	// video view
274	rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right,
275		fMenuBarHeight + 10);
276	fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE);
277	fBackground->AddChild(fVideoView);
278
279	// controls
280	rect = BRect(0, fMenuBarHeight + 11, fBackground->Bounds().right,
281		fBackground->Bounds().bottom);
282	fControls = new ControllerView(rect, fController, fPlaylist);
283	fBackground->AddChild(fControls);
284	fControls->ResizeToPreferred();
285	fControlsHeight = (int)fControls->Frame().Height() + 1;
286	fControlsWidth = (int)fControls->Frame().Width() + 1;
287	fControls->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT);
288	fControls->SetDisabledString(kDisabledSeekMessage);
289
290	fPlaylist->AddListener(fPlaylistObserver);
291	fController->SetVideoView(fVideoView);
292	fController->AddListener(fControllerObserver);
293	PeakView* peakView = fControls->GetPeakView();
294	peakView->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION);
295	fController->SetPeakListener(peakView);
296
297	_SetupWindow();
298
299	// setup the playlist window now, we need to have it
300	// running for the undo/redo playlist editing
301	fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 500, 600), fPlaylist,
302		fController);
303	fPlaylistWindow->Hide();
304	fPlaylistWindow->Show();
305		// this makes sure the window thread is running without
306		// showing the window just yet
307
308	Settings::Default()->AddListener(&fGlobalSettingsListener);
309	_AdoptGlobalSettings();
310
311	AddShortcut('z', B_COMMAND_KEY, new BMessage(B_UNDO));
312	AddShortcut('y', B_COMMAND_KEY, new BMessage(B_UNDO));
313	AddShortcut('z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO));
314	AddShortcut('y', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO));
315
316	Hide();
317	Show();
318
319	if (message != NULL)
320		PostMessage(message);
321
322	BMediaRoster* roster = BMediaRoster::Roster();
323	roster->StartWatching(BMessenger(this, this), B_MEDIA_SERVER_STARTED);
324	roster->StartWatching(BMessenger(this, this),  B_MEDIA_SERVER_QUIT);
325}
326
327
328MainWin::~MainWin()
329{
330//	printf("MainWin::~MainWin\n");
331
332	BMediaRoster* roster = BMediaRoster::CurrentRoster();
333	roster->StopWatching(BMessenger(this, this), B_MEDIA_SERVER_STARTED);
334	roster->StopWatching(BMessenger(this, this), B_MEDIA_SERVER_QUIT);
335
336	Settings::Default()->RemoveListener(&fGlobalSettingsListener);
337	fPlaylist->RemoveListener(fPlaylistObserver);
338	fController->Lock();
339	fController->RemoveListener(fControllerObserver);
340	fController->SetPeakListener(NULL);
341	fController->SetVideoTarget(NULL);
342	fController->Unlock();
343
344	// give the views a chance to detach from any notifiers
345	// before we delete them
346	fBackground->RemoveSelf();
347	delete fBackground;
348
349	if (fInfoWin && fInfoWin->Lock())
350		fInfoWin->Quit();
351
352	if (fPlaylistWindow && fPlaylistWindow->Lock())
353		fPlaylistWindow->Quit();
354
355	delete fPlaylist;
356	fPlaylist = NULL;
357
358	// quit the Controller looper thread
359	thread_id controllerThread = fController->Thread();
360	fController->PostMessage(B_QUIT_REQUESTED);
361	status_t exitValue;
362	wait_for_thread(controllerThread, &exitValue);
363}
364
365
366// #pragma mark -
367
368
369void
370MainWin::FrameResized(float newWidth, float newHeight)
371{
372	if (newWidth != Bounds().Width() || newHeight != Bounds().Height()) {
373		debugger("size wrong\n");
374	}
375
376	bool noMenu = fNoInterface || fIsFullscreen;
377	bool noControls = fNoInterface || fIsFullscreen;
378
379//	printf("FrameResized enter: newWidth %.0f, newHeight %.0f\n",
380//		newWidth, newHeight);
381
382	if (!fHasVideo)
383		sNoVideoWidth = fNoVideoWidth = (int)newWidth;
384
385	int maxVideoWidth  = int(newWidth) + 1;
386	int maxVideoHeight = int(newHeight) + 1
387		- (noMenu  ? 0 : fMenuBarHeight)
388		- (noControls ? 0 : fControlsHeight);
389
390	ASSERT(maxVideoHeight >= 0);
391
392	int y = 0;
393
394	if (noMenu) {
395		if (!fMenuBar->IsHidden(fMenuBar))
396			fMenuBar->Hide();
397	} else {
398		fMenuBar->MoveTo(0, y);
399		fMenuBar->ResizeTo(newWidth, fMenuBarHeight - 1);
400		if (fMenuBar->IsHidden(fMenuBar))
401			fMenuBar->Show();
402		y += fMenuBarHeight;
403	}
404
405	if (maxVideoHeight == 0) {
406		if (!fVideoView->IsHidden(fVideoView))
407			fVideoView->Hide();
408	} else {
409		_ResizeVideoView(0, y, maxVideoWidth, maxVideoHeight);
410		if (fVideoView->IsHidden(fVideoView))
411			fVideoView->Show();
412		y += maxVideoHeight;
413	}
414
415	if (noControls) {
416		if (!fControls->IsHidden(fControls))
417			fControls->Hide();
418	} else {
419		fControls->MoveTo(0, y);
420		fControls->ResizeTo(newWidth, fControlsHeight - 1);
421		if (fControls->IsHidden(fControls))
422			fControls->Show();
423//		y += fControlsHeight;
424	}
425
426//	printf("FrameResized leave\n");
427}
428
429
430void
431MainWin::Zoom(BPoint /*position*/, float /*width*/, float /*height*/)
432{
433	PostMessage(M_TOGGLE_FULLSCREEN);
434}
435
436
437void
438MainWin::DispatchMessage(BMessage* msg, BHandler* handler)
439{
440	if ((msg->what == B_MOUSE_DOWN)
441		&& (handler == fBackground || handler == fVideoView
442			|| handler == fControls)) {
443		_MouseDown(msg, dynamic_cast<BView*>(handler));
444	}
445
446	if ((msg->what == B_MOUSE_MOVED)
447		&& (handler == fBackground || handler == fVideoView
448			|| handler == fControls)) {
449		_MouseMoved(msg, dynamic_cast<BView*>(handler));
450	}
451
452	if ((msg->what == B_MOUSE_UP)
453		&& (handler == fBackground || handler == fVideoView)) {
454		_MouseUp(msg);
455	}
456
457	if ((msg->what == B_KEY_DOWN)
458		&& (handler == fBackground || handler == fVideoView)) {
459		// special case for PrintScreen key
460		if (msg->FindInt32("key") == B_PRINT_KEY) {
461			fVideoView->OverlayScreenshotPrepare();
462			BWindow::DispatchMessage(msg, handler);
463			fVideoView->OverlayScreenshotCleanup();
464			return;
465		}
466
467		// every other key gets dispatched to our _KeyDown first
468		if (_KeyDown(msg)) {
469			// it got handled, don't pass it on
470			return;
471		}
472	}
473
474	BWindow::DispatchMessage(msg, handler);
475}
476
477
478void
479MainWin::MessageReceived(BMessage* msg)
480{
481//	msg->PrintToStream();
482	switch (msg->what) {
483		case B_EXECUTE_PROPERTY:
484		case B_GET_PROPERTY:
485		case B_SET_PROPERTY:
486		{
487			BMessage reply(B_REPLY);
488			status_t result = B_BAD_SCRIPT_SYNTAX;
489			int32 index;
490			BMessage specifier;
491			int32 what;
492			const char* property;
493
494			if (msg->GetCurrentSpecifier(&index, &specifier, &what,
495					&property) != B_OK) {
496				return BWindow::MessageReceived(msg);
497			}
498
499			BPropertyInfo propertyInfo(sPropertyInfo);
500			switch (propertyInfo.FindMatch(msg, index, &specifier, what,
501					property)) {
502				case 0:
503					fControls->SkipForward();
504					result = B_OK;
505					break;
506
507				case 1:
508					fControls->SkipBackward();
509					result = B_OK;
510					break;
511
512				case 2:
513					fController->Play();
514					result = B_OK;
515					break;
516
517				case 3:
518					fController->Stop();
519					result = B_OK;
520					break;
521
522				case 4:
523					fController->Pause();
524					result = B_OK;
525					break;
526
527				case 5:
528					fController->TogglePlaying();
529					result = B_OK;
530					break;
531
532				case 6:
533					fController->ToggleMute();
534					result = B_OK;
535					break;
536
537				case 7:
538				{
539					if (msg->what == B_GET_PROPERTY) {
540						result = reply.AddFloat("result",
541							fController->Volume());
542					} else if (msg->what == B_SET_PROPERTY) {
543						float newVolume;
544						result = msg->FindFloat("data", &newVolume);
545						if (result == B_OK)
546							fController->SetVolume(newVolume);
547					}
548					break;
549				}
550
551				case 8:
552				{
553					if (msg->what == B_GET_PROPERTY) {
554						BAutolock _(fPlaylist);
555						const PlaylistItem* item = fController->Item();
556						if (item == NULL) {
557							result = B_NO_INIT;
558							break;
559						}
560
561						result = reply.AddString("result", item->LocationURI());
562					}
563					break;
564				}
565
566				case 9:
567					PostMessage(M_TOGGLE_FULLSCREEN);
568					break;
569
570				case 10:
571					if (msg->what != B_GET_PROPERTY)
572						break;
573
574					result = reply.AddInt64("result",
575						fController->TimeDuration());
576					break;
577
578				case 11:
579				{
580					if (msg->what == B_GET_PROPERTY) {
581						result = reply.AddInt64("result",
582							fController->TimePosition());
583					} else if (msg->what == B_SET_PROPERTY) {
584						int64 newTime;
585						result = msg->FindInt64("data", &newTime);
586						if (result == B_OK)
587							fController->SetTimePosition(newTime);
588					}
589
590					break;
591				}
592
593				case 12:
594				{
595					if (msg->what != B_SET_PROPERTY)
596						break;
597
598					bigtime_t seekBy;
599					result = msg->FindInt64("data", &seekBy);
600					if (result != B_OK)
601						break;
602
603					_Wind(seekBy, 0);
604					break;
605				}
606
607				case 13:
608					result = reply.AddInt16("result", fPlaylist->CountItems());
609					break;
610
611				case 14:
612				{
613					int32 i = specifier.GetInt32("index", 0);
614					if (i >= fPlaylist->CountItems()) {
615						result = B_NO_INIT;
616						break;
617					}
618
619					BAutolock _(fPlaylist);
620					const PlaylistItem* item = fPlaylist->ItemAt(i);
621					result = item == NULL ? B_NO_INIT
622						: reply.AddString("result", item->Title());
623					break;
624				}
625
626				default:
627					return BWindow::MessageReceived(msg);
628			}
629
630			if (result != B_OK) {
631				reply.what = B_MESSAGE_NOT_UNDERSTOOD;
632				reply.AddString("message", strerror(result));
633				reply.AddInt32("error", result);
634			}
635
636			msg->SendReply(&reply);
637			break;
638		}
639
640		case B_REFS_RECEIVED:
641		case M_URL_RECEIVED:
642			_RefsReceived(msg);
643			break;
644
645		case B_SIMPLE_DATA:
646			if (msg->HasRef("refs"))
647				_RefsReceived(msg);
648			break;
649		case M_OPEN_PREVIOUS_PLAYLIST:
650			OpenPlaylist(msg);
651			break;
652
653		case B_UNDO:
654		case B_REDO:
655			fPlaylistWindow->PostMessage(msg);
656			break;
657
658		case B_MEDIA_SERVER_STARTED:
659		{
660			printf("TODO: implement B_MEDIA_SERVER_STARTED\n");
661//
662//			BAutolock _(fPlaylist);
663//			BMessage fakePlaylistMessage(MSG_PLAYLIST_CURRENT_ITEM_CHANGED);
664//			fakePlaylistMessage.AddInt32("index",
665//				fPlaylist->CurrentItemIndex());
666//			PostMessage(&fakePlaylistMessage);
667			break;
668		}
669
670		case B_MEDIA_SERVER_QUIT:
671			printf("TODO: implement B_MEDIA_SERVER_QUIT\n");
672//			if (fController->Lock()) {
673//				fController->CleanupNodes();
674//				fController->Unlock();
675//			}
676			break;
677
678		// PlaylistObserver messages
679		case MSG_PLAYLIST_ITEM_ADDED:
680		{
681			PlaylistItem* item;
682			int32 index;
683			if (msg->FindPointer("item", (void**)&item) == B_OK
684				&& msg->FindInt32("index", &index) == B_OK) {
685				_AddPlaylistItem(item, index);
686			}
687			break;
688		}
689		case MSG_PLAYLIST_ITEM_REMOVED:
690		{
691			int32 index;
692			if (msg->FindInt32("index", &index) == B_OK)
693				_RemovePlaylistItem(index);
694			break;
695		}
696		case MSG_PLAYLIST_CURRENT_ITEM_CHANGED:
697		{
698			BAutolock _(fPlaylist);
699
700			int32 index;
701			// if false, the message was meant to only update the GUI
702			bool play;
703			if (msg->FindBool("play", &play) < B_OK || !play)
704				break;
705			if (msg->FindInt32("index", &index) < B_OK
706				|| index != fPlaylist->CurrentItemIndex())
707				break;
708			PlaylistItemRef item(fPlaylist->ItemAt(index));
709			if (item.Get() != NULL) {
710				printf("open playlist item: %s\n", item->Name().String());
711				OpenPlaylistItem(item);
712				_MarkPlaylistItem(index);
713			}
714			break;
715		}
716		case MSG_PLAYLIST_IMPORT_FAILED:
717		{
718			BAlert* alert = new BAlert(B_TRANSLATE("Nothing to Play"),
719				B_TRANSLATE("None of the files you wanted to play appear "
720				"to be media files."), B_TRANSLATE("OK"));
721			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
722			alert->Go();
723			fControls->SetDisabledString(kDisabledSeekMessage);
724			break;
725		}
726
727		// ControllerObserver messages
728		case MSG_CONTROLLER_FILE_FINISHED:
729		{
730			BAutolock _(fPlaylist);
731
732			bool hadNext = fPlaylist->SetCurrentItemIndex(
733				fPlaylist->CurrentItemIndex() + 1);
734			if (!hadNext) {
735				// Reached end of playlist
736				// Handle "quit when done" settings
737				if ((fHasVideo && fCloseWhenDonePlayingMovie)
738					|| (!fHasVideo && fCloseWhenDonePlayingSound))
739					PostMessage(B_QUIT_REQUESTED);
740				// Handle "loop by default" settings
741				if ((fHasVideo && fLoopMovies)
742					|| (!fHasVideo && fLoopSounds)) {
743					if (fPlaylist->CountItems() > 1)
744						fPlaylist->SetCurrentItemIndex(0);
745					else
746						fController->Play();
747				}
748			}
749			break;
750		}
751		case MSG_CONTROLLER_FILE_CHANGED:
752		{
753			status_t result = B_ERROR;
754			msg->FindInt32("result", &result);
755			PlaylistItemRef itemRef;
756			PlaylistItem* item;
757			if (msg->FindPointer("item", (void**)&item) == B_OK) {
758				itemRef.SetTo(item, true);
759					// The reference was passed along with the message.
760			} else {
761				BAutolock _(fPlaylist);
762				itemRef.SetTo(fPlaylist->ItemAt(
763					fPlaylist->CurrentItemIndex()));
764			}
765			_PlaylistItemOpened(itemRef, result);
766			break;
767		}
768		case MSG_CONTROLLER_VIDEO_TRACK_CHANGED:
769		{
770			int32 index;
771			if (msg->FindInt32("index", &index) == B_OK) {
772				int32 i = 0;
773				while (BMenuItem* item = fVideoTrackMenu->ItemAt(i)) {
774					item->SetMarked(i == index);
775					i++;
776				}
777			}
778			break;
779		}
780		case MSG_CONTROLLER_AUDIO_TRACK_CHANGED:
781		{
782			int32 index;
783			if (msg->FindInt32("index", &index) == B_OK) {
784				int32 i = 0;
785				while (BMenuItem* item = fAudioTrackMenu->ItemAt(i)) {
786					item->SetMarked(i == index);
787					i++;
788				}
789				_UpdateAudioChannelCount(index);
790			}
791			break;
792		}
793		case MSG_CONTROLLER_SUB_TITLE_TRACK_CHANGED:
794		{
795			int32 index;
796			if (msg->FindInt32("index", &index) == B_OK) {
797				int32 i = 0;
798				while (BMenuItem* item = fSubTitleTrackMenu->ItemAt(i)) {
799					BMessage* message = item->Message();
800					if (message != NULL) {
801						item->SetMarked((int32)message->what
802							- M_SELECT_SUB_TITLE_TRACK == index);
803					}
804					i++;
805				}
806			}
807			break;
808		}
809		case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED:
810		{
811			uint32 state;
812			if (msg->FindInt32("state", (int32*)&state) == B_OK)
813				fControls->SetPlaybackState(state);
814			break;
815		}
816		case MSG_CONTROLLER_POSITION_CHANGED:
817		{
818			float position;
819			if (msg->FindFloat("position", &position) == B_OK) {
820				fControls->SetPosition(position, fController->TimePosition(),
821					fController->TimeDuration());
822				fAllowWinding = true;
823			}
824			break;
825		}
826		case MSG_CONTROLLER_SEEK_HANDLED:
827			break;
828
829		case MSG_CONTROLLER_VOLUME_CHANGED:
830		{
831			float volume;
832			if (msg->FindFloat("volume", &volume) == B_OK)
833				fControls->SetVolume(volume);
834			break;
835		}
836		case MSG_CONTROLLER_MUTED_CHANGED:
837		{
838			bool muted;
839			if (msg->FindBool("muted", &muted) == B_OK)
840				fControls->SetMuted(muted);
841			break;
842		}
843
844		// menu item messages
845		case M_FILE_OPEN:
846		{
847			BMessenger target(this);
848			BMessage result(B_REFS_RECEIVED);
849			BMessage appMessage(M_SHOW_OPEN_PANEL);
850			appMessage.AddMessenger("target", target);
851			appMessage.AddMessage("message", &result);
852			appMessage.AddString("title", B_TRANSLATE("Open clips"));
853			appMessage.AddString("label", B_TRANSLATE("Open"));
854			be_app->PostMessage(&appMessage);
855			break;
856		}
857
858		case M_NETWORK_STREAM_OPEN:
859		{
860			BMessenger target(this);
861			NetworkStreamWin* win = new NetworkStreamWin(target);
862			win->Show();
863			break;
864		}
865
866		case M_FILE_INFO:
867			ShowFileInfo();
868			break;
869		case M_FILE_PLAYLIST:
870			ShowPlaylistWindow();
871			break;
872		case M_FILE_CLOSE:
873			PostMessage(B_QUIT_REQUESTED);
874			break;
875		case M_FILE_QUIT:
876			be_app->PostMessage(B_QUIT_REQUESTED);
877			break;
878
879		case M_TOGGLE_FULLSCREEN:
880			_ToggleFullscreen();
881			break;
882
883		case M_TOGGLE_ALWAYS_ON_TOP:
884			_ToggleAlwaysOnTop();
885			break;
886
887		case M_TOGGLE_NO_INTERFACE:
888			_ToggleNoInterface();
889			break;
890
891		case M_VIEW_SIZE:
892		{
893			int32 size;
894			if (msg->FindInt32("size", &size) == B_OK) {
895				if (!fHasVideo)
896					break;
897				if (fIsFullscreen)
898					_ToggleFullscreen();
899				_ResizeWindow(size);
900			}
901			break;
902		}
903
904/*
905		case B_ACQUIRE_OVERLAY_LOCK:
906			printf("B_ACQUIRE_OVERLAY_LOCK\n");
907			fVideoView->OverlayLockAcquire();
908			break;
909
910		case B_RELEASE_OVERLAY_LOCK:
911			printf("B_RELEASE_OVERLAY_LOCK\n");
912			fVideoView->OverlayLockRelease();
913			break;
914*/
915		case B_MOUSE_WHEEL_CHANGED:
916		{
917			float dx = msg->FindFloat("be:wheel_delta_x");
918			float dy = msg->FindFloat("be:wheel_delta_y");
919			bool inv = modifiers() & B_COMMAND_KEY;
920			if (dx > 0.1)
921				PostMessage(inv ? M_VOLUME_DOWN : M_SKIP_PREV);
922			if (dx < -0.1)
923				PostMessage(inv ? M_VOLUME_UP : M_SKIP_NEXT);
924			if (dy > 0.1)
925				PostMessage(inv ? M_SKIP_PREV : M_VOLUME_DOWN);
926			if (dy < -0.1)
927				PostMessage(inv ? M_SKIP_NEXT : M_VOLUME_UP);
928			break;
929		}
930
931		case M_SKIP_NEXT:
932			fControls->SkipForward();
933			break;
934
935		case M_SKIP_PREV:
936			fControls->SkipBackward();
937			break;
938
939		case M_WIND:
940		{
941			bigtime_t howMuch;
942			int64 frames;
943			if (msg->FindInt64("how much", &howMuch) != B_OK
944				|| msg->FindInt64("frames", &frames) != B_OK) {
945				break;
946			}
947
948			_Wind(howMuch, frames);
949			break;
950		}
951
952		case M_VOLUME_UP:
953			fController->VolumeUp();
954			break;
955
956		case M_VOLUME_DOWN:
957			fController->VolumeDown();
958			break;
959
960		case M_ASPECT_SAME_AS_SOURCE:
961			if (fHasVideo) {
962				int width;
963				int height;
964				int widthAspect;
965				int heightAspect;
966				fController->GetSize(&width, &height,
967					&widthAspect, &heightAspect);
968				VideoFormatChange(width, height, widthAspect, heightAspect);
969			}
970			break;
971
972		case M_ASPECT_NO_DISTORTION:
973			if (fHasVideo) {
974				int width;
975				int height;
976				fController->GetSize(&width, &height);
977				VideoFormatChange(width, height, width, height);
978			}
979			break;
980
981		case M_ASPECT_4_3:
982			VideoAspectChange(4, 3);
983			break;
984
985		case M_ASPECT_16_9: // 1.77 : 1
986			VideoAspectChange(16, 9);
987			break;
988
989		case M_ASPECT_83_50: // 1.66 : 1
990			VideoAspectChange(83, 50);
991			break;
992
993		case M_ASPECT_7_4: // 1.75 : 1
994			VideoAspectChange(7, 4);
995			break;
996
997		case M_ASPECT_37_20: // 1.85 : 1
998			VideoAspectChange(37, 20);
999			break;
1000
1001		case M_ASPECT_47_20: // 2.35 : 1
1002			VideoAspectChange(47, 20);
1003			break;
1004
1005		case M_SET_PLAYLIST_POSITION:
1006		{
1007			BAutolock _(fPlaylist);
1008
1009			int32 index;
1010			if (msg->FindInt32("index", &index) == B_OK)
1011				fPlaylist->SetCurrentItemIndex(index);
1012			break;
1013		}
1014
1015		case MSG_OBJECT_CHANGED:
1016			// received from fGlobalSettingsListener
1017			// TODO: find out which object, if we ever watch more than
1018			// the global settings instance...
1019			_AdoptGlobalSettings();
1020			break;
1021
1022		case M_SLIDE_CONTROLS:
1023		{
1024			float offset;
1025			if (msg->FindFloat("offset", &offset) == B_OK) {
1026				fControls->MoveBy(0, offset);
1027				fVideoView->SetSubTitleMaxBottom(fControls->Frame().top - 1);
1028				UpdateIfNeeded();
1029				snooze(15000);
1030			}
1031			break;
1032		}
1033		case M_FINISH_SLIDING_CONTROLS:
1034		{
1035			float offset;
1036			bool show;
1037			if (msg->FindFloat("offset", &offset) == B_OK
1038				&& msg->FindBool("show", &show) == B_OK) {
1039				if (show) {
1040					fControls->MoveTo(fControls->Frame().left, offset);
1041					fVideoView->SetSubTitleMaxBottom(offset - 1);
1042				} else {
1043					fVideoView->SetSubTitleMaxBottom(
1044						fVideoView->Bounds().bottom);
1045					fControls->RemoveSelf();
1046					fControls->MoveTo(fVideoView->Frame().left,
1047						fVideoView->Frame().bottom + 1);
1048					fBackground->AddChild(fControls);
1049					fControls->SetSymbolScale(1.0f);
1050					while (!fControls->IsHidden())
1051						fControls->Hide();
1052				}
1053			}
1054			break;
1055		}
1056		case M_HIDE_FULL_SCREEN_CONTROLS:
1057			if (fIsFullscreen) {
1058				BPoint videoViewWhere;
1059				if (msg->FindPoint("where", &videoViewWhere) == B_OK) {
1060					if (msg->FindBool("force")
1061						|| !fControls->Frame().Contains(videoViewWhere)) {
1062						_ShowFullscreenControls(false);
1063						// hide the mouse cursor until the user moves it
1064						be_app->ObscureCursor();
1065					}
1066				}
1067			}
1068			break;
1069
1070		case M_SET_RATING:
1071		{
1072			int32 rating;
1073			if (msg->FindInt32("rating", &rating) == B_OK)
1074				_SetRating(rating);
1075			break;
1076		}
1077
1078		default:
1079			if (msg->what >= M_SELECT_AUDIO_TRACK
1080				&& msg->what <= M_SELECT_AUDIO_TRACK_END) {
1081				fController->SelectAudioTrack(msg->what - M_SELECT_AUDIO_TRACK);
1082				break;
1083			}
1084			if (msg->what >= M_SELECT_VIDEO_TRACK
1085				&& msg->what <= M_SELECT_VIDEO_TRACK_END) {
1086				fController->SelectVideoTrack(msg->what - M_SELECT_VIDEO_TRACK);
1087				break;
1088			}
1089			if ((int32)msg->what >= M_SELECT_SUB_TITLE_TRACK - 1
1090				&& msg->what <= M_SELECT_SUB_TITLE_TRACK_END) {
1091				fController->SelectSubTitleTrack((int32)msg->what
1092					- M_SELECT_SUB_TITLE_TRACK);
1093				break;
1094			}
1095			// let BWindow handle the rest
1096			BWindow::MessageReceived(msg);
1097	}
1098}
1099
1100
1101void
1102MainWin::WindowActivated(bool active)
1103{
1104	fController->PlayerActivated(active);
1105}
1106
1107
1108bool
1109MainWin::QuitRequested()
1110{
1111	BMessage message(M_PLAYER_QUIT);
1112	GetQuitMessage(&message);
1113	be_app->PostMessage(&message);
1114	return true;
1115}
1116
1117
1118void
1119MainWin::MenusBeginning()
1120{
1121	_SetupVideoAspectItems(fVideoAspectMenu);
1122}
1123
1124
1125// #pragma mark -
1126
1127
1128void
1129MainWin::OpenPlaylist(const BMessage* playlistArchive)
1130{
1131	if (playlistArchive == NULL)
1132		return;
1133
1134	BAutolock _(this);
1135	BAutolock playlistLocker(fPlaylist);
1136
1137	if (fPlaylist->Unarchive(playlistArchive) != B_OK)
1138		return;
1139
1140	int32 currentIndex;
1141	if (playlistArchive->FindInt32("index", &currentIndex) != B_OK)
1142		currentIndex = 0;
1143	fPlaylist->SetCurrentItemIndex(currentIndex);
1144
1145	playlistLocker.Unlock();
1146
1147	if (currentIndex != -1) {
1148		// Restore the current play position only if we have something to play
1149		playlistArchive->FindInt64("position", (int64*)&fInitialSeekPosition);
1150	}
1151
1152	if (IsHidden())
1153		Show();
1154}
1155
1156
1157void
1158MainWin::OpenPlaylistItem(const PlaylistItemRef& item)
1159{
1160	status_t ret = fController->SetToAsync(item);
1161	if (ret != B_OK) {
1162		fprintf(stderr, "MainWin::OpenPlaylistItem() - Failed to send message "
1163			"to Controller.\n");
1164		BString message = B_TRANSLATE("%app% encountered an internal error. "
1165			"The file could not be opened.");
1166		message.ReplaceFirst("%app%", kApplicationName);
1167		BAlert* alert = new BAlert(kApplicationName, message.String(),
1168			B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1169		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1170		alert->Go();
1171		_PlaylistItemOpened(item, ret);
1172	} else {
1173		BString string;
1174		string << "Opening '" << item->Name() << "'.";
1175		fControls->SetDisabledString(string.String());
1176	}
1177}
1178
1179
1180void
1181MainWin::ShowFileInfo()
1182{
1183	if (!fInfoWin)
1184		fInfoWin = new InfoWin(Frame().LeftTop(), fController);
1185
1186	if (fInfoWin->Lock()) {
1187		if (fInfoWin->IsHidden())
1188			fInfoWin->Show();
1189		else
1190			fInfoWin->Activate();
1191		fInfoWin->Unlock();
1192	}
1193}
1194
1195
1196void
1197MainWin::ShowPlaylistWindow()
1198{
1199	if (fPlaylistWindow->Lock()) {
1200		// make sure the window shows on the same workspace as ourself
1201		uint32 workspaces = Workspaces();
1202		if (fPlaylistWindow->Workspaces() != workspaces)
1203			fPlaylistWindow->SetWorkspaces(workspaces);
1204
1205		// show or activate
1206		if (fPlaylistWindow->IsHidden())
1207			fPlaylistWindow->Show();
1208		else
1209			fPlaylistWindow->Activate();
1210
1211		fPlaylistWindow->Unlock();
1212	}
1213}
1214
1215
1216void
1217MainWin::VideoAspectChange(int forcedWidth, int forcedHeight, float widthScale)
1218{
1219	// Force specific source size and pixel width scale.
1220	if (fHasVideo) {
1221		int width;
1222		int height;
1223		fController->GetSize(&width, &height);
1224		VideoFormatChange(forcedWidth, forcedHeight,
1225			lround(width * widthScale), height);
1226	}
1227}
1228
1229
1230void
1231MainWin::VideoAspectChange(float widthScale)
1232{
1233	// Called when video aspect ratio changes and the original
1234	// width/height should be restored too, display aspect is not known,
1235	// only pixel width scale.
1236	if (fHasVideo) {
1237		int width;
1238		int height;
1239		fController->GetSize(&width, &height);
1240		VideoFormatChange(width, height, lround(width * widthScale), height);
1241	}
1242}
1243
1244
1245void
1246MainWin::VideoAspectChange(int widthAspect, int heightAspect)
1247{
1248	// Called when video aspect ratio changes and the original
1249	// width/height should be restored too.
1250	if (fHasVideo) {
1251		int width;
1252		int height;
1253		fController->GetSize(&width, &height);
1254		VideoFormatChange(width, height, widthAspect, heightAspect);
1255	}
1256}
1257
1258
1259void
1260MainWin::VideoFormatChange(int width, int height, int widthAspect,
1261	int heightAspect)
1262{
1263	// Called when video format or aspect ratio changes.
1264
1265	printf("VideoFormatChange enter: width %d, height %d, "
1266		"aspect ratio: %d:%d\n", width, height, widthAspect, heightAspect);
1267
1268	// remember current view scale
1269	int percent = _CurrentVideoSizeInPercent();
1270
1271 	fSourceWidth = width;
1272 	fSourceHeight = height;
1273 	fWidthAspect = widthAspect;
1274 	fHeightAspect = heightAspect;
1275
1276	if (percent == 100)
1277		_ResizeWindow(100);
1278	else
1279	 	FrameResized(Bounds().Width(), Bounds().Height());
1280
1281	printf("VideoFormatChange leave\n");
1282}
1283
1284
1285void
1286MainWin::GetQuitMessage(BMessage* message)
1287{
1288	message->AddPointer("instance", this);
1289	message->AddRect("window frame", Frame());
1290	message->AddBool("audio only", !fHasVideo);
1291	message->AddInt64("creation time", fCreationTime);
1292
1293	if (!fHasVideo && fHasAudio) {
1294		// store playlist, current index and position if this is audio
1295		BMessage playlistArchive;
1296
1297		BAutolock controllerLocker(fController);
1298		playlistArchive.AddInt64("position", fController->TimePosition());
1299		controllerLocker.Unlock();
1300
1301		if (!fPlaylist)
1302			return;
1303
1304		BAutolock playlistLocker(fPlaylist);
1305		if (fPlaylist->Archive(&playlistArchive) != B_OK
1306			|| playlistArchive.AddInt32("index",
1307				fPlaylist->CurrentItemIndex()) != B_OK
1308			|| message->AddMessage("playlist", &playlistArchive) != B_OK) {
1309			fprintf(stderr, "Failed to store current playlist.\n");
1310		}
1311	}
1312}
1313
1314
1315BHandler*
1316MainWin::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
1317	int32 what, const char* property)
1318{
1319	BPropertyInfo propertyInfo(sPropertyInfo);
1320	if (propertyInfo.FindMatch(message, index, specifier, what, property)
1321		< propertyInfo.CountProperties())
1322		return this;
1323
1324	return BWindow::ResolveSpecifier(message, index, specifier, what, property);
1325}
1326
1327
1328status_t
1329MainWin::GetSupportedSuites(BMessage* data)
1330{
1331	if (data == NULL)
1332		return B_BAD_VALUE;
1333
1334	status_t status = data->AddString("suites", "suite/vnd.Haiku-MediaPlayer");
1335	if (status != B_OK)
1336		return status;
1337
1338	BPropertyInfo propertyInfo(sPropertyInfo);
1339	status = data->AddFlat("messages", &propertyInfo);
1340	if (status != B_OK)
1341		return status;
1342
1343	return BWindow::GetSupportedSuites(data);
1344}
1345
1346
1347// #pragma mark -
1348
1349
1350void
1351MainWin::_RefsReceived(BMessage* message)
1352{
1353	// the playlist is replaced by dropped files
1354	// or the dropped files are appended to the end
1355	// of the existing playlist if <shift> is pressed
1356	bool append = false;
1357	if (message->FindBool("append to playlist", &append) != B_OK)
1358		append = modifiers() & B_SHIFT_KEY;
1359
1360	BAutolock _(fPlaylist);
1361	int32 appendIndex = append ? APPEND_INDEX_APPEND_LAST
1362		: APPEND_INDEX_REPLACE_PLAYLIST;
1363	message->AddInt32("append_index", appendIndex);
1364
1365	// forward the message to the playlist window,
1366	// so that undo/redo is used for modifying the playlist
1367	fPlaylistWindow->PostMessage(message);
1368
1369	if (message->FindRect("window frame", &fNoVideoFrame) != B_OK)
1370		fNoVideoFrame = BRect();
1371}
1372
1373
1374void
1375MainWin::_PlaylistItemOpened(const PlaylistItemRef& item, status_t result)
1376{
1377	if (result != B_OK) {
1378		BAutolock _(fPlaylist);
1379
1380		item->SetPlaybackFailed();
1381		bool allItemsFailed = true;
1382		int32 count = fPlaylist->CountItems();
1383		for (int32 i = 0; i < count; i++) {
1384			if (!fPlaylist->ItemAtFast(i)->PlaybackFailed()) {
1385				allItemsFailed = false;
1386				break;
1387			}
1388		}
1389
1390		if (allItemsFailed) {
1391			// Display error if all files failed to play.
1392			BString message(B_TRANSLATE(
1393				"The file '%filename' could not be opened.\n\n"));;
1394			message.ReplaceAll("%filename", item->Name());
1395
1396			if (result == B_MEDIA_NO_HANDLER) {
1397				// give a more detailed message for the most likely of all
1398				// errors
1399				message << B_TRANSLATE(
1400					"There is no decoder installed to handle the "
1401					"file format, or the decoder has trouble with the "
1402					"specific version of the format.");
1403			} else {
1404				message << B_TRANSLATE("Error: ") << strerror(result);
1405			}
1406			BAlert* alert = new BAlert("error", message.String(),
1407				B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1408			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1409			alert->Go();
1410			fControls->SetDisabledString(kDisabledSeekMessage);
1411		} else {
1412			// Just go to the next file and don't bother user (yet)
1413			fPlaylist->SetCurrentItemIndex(fPlaylist->CurrentItemIndex() + 1);
1414		}
1415
1416		fHasFile = false;
1417		fHasVideo = false;
1418		fHasAudio = false;
1419		SetTitle(kApplicationName);
1420	} else {
1421		fHasFile = true;
1422		fHasVideo = fController->VideoTrackCount() != 0;
1423		fHasAudio = fController->AudioTrackCount() != 0;
1424		SetTitle(item->Name().String());
1425
1426		if (fInitialSeekPosition < 0) {
1427			fInitialSeekPosition
1428				= fController->TimeDuration() + fInitialSeekPosition;
1429		}
1430		fController->SetTimePosition(fInitialSeekPosition);
1431		fInitialSeekPosition = 0;
1432	}
1433	_SetupWindow();
1434
1435	if (result == B_OK)
1436		_UpdatePlaylistItemFile();
1437}
1438
1439
1440void
1441MainWin::_SetupWindow()
1442{
1443//	printf("MainWin::_SetupWindow\n");
1444	// Populate the track menus
1445	_SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu, fSubTitleTrackMenu);
1446	_UpdateAudioChannelCount(fController->CurrentAudioTrack());
1447
1448	fVideoMenu->SetEnabled(fHasVideo);
1449	fAudioMenu->SetEnabled(fHasAudio);
1450	int previousSourceWidth = fSourceWidth;
1451	int previousSourceHeight = fSourceHeight;
1452	int previousWidthAspect = fWidthAspect;
1453	int previousHeightAspect = fHeightAspect;
1454	if (fHasVideo) {
1455		fController->GetSize(&fSourceWidth, &fSourceHeight,
1456			&fWidthAspect, &fHeightAspect);
1457	} else {
1458		fSourceWidth = 0;
1459		fSourceHeight = 0;
1460		fWidthAspect = 1;
1461		fHeightAspect = 1;
1462	}
1463	_UpdateControlsEnabledStatus();
1464
1465	// Adopt the size and window layout if necessary
1466	if (previousSourceWidth != fSourceWidth
1467		|| previousSourceHeight != fSourceHeight
1468		|| previousWidthAspect != fWidthAspect
1469		|| previousHeightAspect != fHeightAspect) {
1470
1471		_SetWindowSizeLimits();
1472
1473		if (!fIsFullscreen) {
1474			// Resize to 100% but stay on screen
1475			_ResizeWindow(100, !fHasVideo, true);
1476		} else {
1477			// Make sure we relayout the video view when in full screen mode
1478			FrameResized(Frame().Width(), Frame().Height());
1479		}
1480	}
1481
1482	_ShowIfNeeded();
1483
1484	fVideoView->MakeFocus();
1485}
1486
1487
1488void
1489MainWin::_CreateMenu()
1490{
1491	fFileMenu = new BMenu(kApplicationName);
1492	fPlaylistMenu = new BMenu(B_TRANSLATE("Playlist" B_UTF8_ELLIPSIS));
1493	fAudioMenu = new BMenu(B_TRANSLATE("Audio"));
1494	fVideoMenu = new BMenu(B_TRANSLATE("Video"));
1495	fVideoAspectMenu = new BMenu(B_TRANSLATE("Aspect ratio"));
1496	fAudioTrackMenu = new BMenu(B_TRANSLATE_CONTEXT("Track",
1497		"Audio Track Menu"));
1498	fVideoTrackMenu = new BMenu(B_TRANSLATE_CONTEXT("Track",
1499		"Video Track Menu"));
1500	fSubTitleTrackMenu = new BMenu(B_TRANSLATE("Subtitles"));
1501	fAttributesMenu = new BMenu(B_TRANSLATE("Attributes"));
1502
1503	fMenuBar->AddItem(fFileMenu);
1504	fMenuBar->AddItem(fAudioMenu);
1505	fMenuBar->AddItem(fVideoMenu);
1506	fMenuBar->AddItem(fAttributesMenu);
1507
1508	BMenuItem* item = new BMenuItem(B_TRANSLATE("New player" B_UTF8_ELLIPSIS),
1509		new BMessage(M_NEW_PLAYER), 'N');
1510	fFileMenu->AddItem(item);
1511	item->SetTarget(be_app);
1512
1513	// Add recent files to "Open File" entry as sub-menu.
1514	BRecentFilesList recentFiles(10, false, NULL, kAppSig);
1515	item = new BMenuItem(recentFiles.NewFileListMenu(
1516		B_TRANSLATE("Open file" B_UTF8_ELLIPSIS), NULL, NULL, this, 10, true,
1517		NULL, kAppSig), new BMessage(M_FILE_OPEN));
1518	item->SetShortcut('O', 0);
1519	fFileMenu->AddItem(item);
1520
1521	item = new BMenuItem(B_TRANSLATE("Open network stream"),
1522		new BMessage(M_NETWORK_STREAM_OPEN));
1523	fFileMenu->AddItem(item);
1524
1525	fFileMenu->AddSeparatorItem();
1526
1527	fFileMenu->AddItem(new BMenuItem(B_TRANSLATE("File info" B_UTF8_ELLIPSIS),
1528		new BMessage(M_FILE_INFO), 'I'));
1529	fFileMenu->AddItem(fPlaylistMenu);
1530	fPlaylistMenu->Superitem()->SetShortcut('P', B_COMMAND_KEY);
1531	fPlaylistMenu->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST));
1532
1533	fFileMenu->AddSeparatorItem();
1534
1535	fNoInterfaceMenuItem = new BMenuItem(B_TRANSLATE("Hide interface"),
1536		new BMessage(M_TOGGLE_NO_INTERFACE), 'H');
1537	fFileMenu->AddItem(fNoInterfaceMenuItem);
1538	fFileMenu->AddItem(new BMenuItem(B_TRANSLATE("Always on top"),
1539		new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'A'));
1540
1541	item = new BMenuItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS),
1542		new BMessage(M_SETTINGS), 'S');
1543	fFileMenu->AddItem(item);
1544	item->SetTarget(be_app);
1545
1546	fFileMenu->AddSeparatorItem();
1547
1548	fFileMenu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1549		new BMessage(M_FILE_CLOSE), 'W'));
1550	fFileMenu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1551		new BMessage(M_FILE_QUIT), 'Q'));
1552
1553	fPlaylistMenu->SetRadioMode(true);
1554
1555	fAudioMenu->AddItem(fAudioTrackMenu);
1556
1557	fVideoMenu->AddItem(fVideoTrackMenu);
1558	fVideoMenu->AddItem(fSubTitleTrackMenu);
1559	fVideoMenu->AddSeparatorItem();
1560	BMessage* resizeMessage = new BMessage(M_VIEW_SIZE);
1561	resizeMessage->AddInt32("size", 50);
1562	fVideoMenu->AddItem(new BMenuItem(
1563		B_TRANSLATE("50% scale"), resizeMessage, '0'));
1564
1565	resizeMessage = new BMessage(M_VIEW_SIZE);
1566	resizeMessage->AddInt32("size", 100);
1567	fVideoMenu->AddItem(new BMenuItem(
1568		B_TRANSLATE("100% scale"), resizeMessage, '1'));
1569
1570	resizeMessage = new BMessage(M_VIEW_SIZE);
1571	resizeMessage->AddInt32("size", 200);
1572	fVideoMenu->AddItem(new BMenuItem(
1573		B_TRANSLATE("200% scale"), resizeMessage, '2'));
1574
1575	resizeMessage = new BMessage(M_VIEW_SIZE);
1576	resizeMessage->AddInt32("size", 300);
1577	fVideoMenu->AddItem(new BMenuItem(
1578		B_TRANSLATE("300% scale"), resizeMessage, '3'));
1579
1580	resizeMessage = new BMessage(M_VIEW_SIZE);
1581	resizeMessage->AddInt32("size", 400);
1582	fVideoMenu->AddItem(new BMenuItem(
1583		B_TRANSLATE("400% scale"), resizeMessage, '4'));
1584
1585	fVideoMenu->AddSeparatorItem();
1586
1587	fVideoMenu->AddItem(new BMenuItem(B_TRANSLATE("Full screen"),
1588		new BMessage(M_TOGGLE_FULLSCREEN), B_ENTER));
1589
1590	fVideoMenu->AddSeparatorItem();
1591
1592	_SetupVideoAspectItems(fVideoAspectMenu);
1593	fVideoMenu->AddItem(fVideoAspectMenu);
1594
1595	fRatingMenu = new BMenu(B_TRANSLATE("Rating"));
1596	fAttributesMenu->AddItem(fRatingMenu);
1597	for (int32 i = 1; i <= 10; i++) {
1598		char label[16];
1599		snprintf(label, sizeof(label), "%" B_PRId32, i);
1600		BMessage* setRatingMsg = new BMessage(M_SET_RATING);
1601		setRatingMsg->AddInt32("rating", i);
1602		fRatingMenu->AddItem(new BMenuItem(label, setRatingMsg));
1603	}
1604}
1605
1606
1607void
1608MainWin::_SetupVideoAspectItems(BMenu* menu)
1609{
1610	BMenuItem* item;
1611	while ((item = menu->RemoveItem((int32)0)) != NULL)
1612		delete item;
1613
1614	int width;
1615	int height;
1616	int widthAspect;
1617	int heightAspect;
1618	fController->GetSize(&width, &height, &widthAspect, &heightAspect);
1619		// We don't care if there is a video track at all. In that
1620		// case we should end up not marking any item.
1621
1622	// NOTE: The item marking may end up marking for example both
1623	// "Stream Settings" and "16 : 9" if the stream settings happen to
1624	// be "16 : 9".
1625
1626	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Stream settings"),
1627		new BMessage(M_ASPECT_SAME_AS_SOURCE), '1', B_SHIFT_KEY));
1628	item->SetMarked(widthAspect == fWidthAspect
1629		&& heightAspect == fHeightAspect);
1630
1631	menu->AddItem(item = new BMenuItem(B_TRANSLATE("No aspect correction"),
1632		new BMessage(M_ASPECT_NO_DISTORTION), '0', B_SHIFT_KEY));
1633	item->SetMarked(width == fWidthAspect && height == fHeightAspect);
1634
1635	menu->AddSeparatorItem();
1636
1637	menu->AddItem(item = new BMenuItem("4 : 3",
1638		new BMessage(M_ASPECT_4_3), 2, B_SHIFT_KEY));
1639	item->SetMarked(fWidthAspect == 4 && fHeightAspect == 3);
1640	menu->AddItem(item = new BMenuItem("16 : 9",
1641		new BMessage(M_ASPECT_16_9), 3, B_SHIFT_KEY));
1642	item->SetMarked(fWidthAspect == 16 && fHeightAspect == 9);
1643
1644	menu->AddSeparatorItem();
1645
1646	menu->AddItem(item = new BMenuItem("1.66 : 1",
1647		new BMessage(M_ASPECT_83_50)));
1648	item->SetMarked(fWidthAspect == 83 && fHeightAspect == 50);
1649	menu->AddItem(item = new BMenuItem("1.75 : 1",
1650		new BMessage(M_ASPECT_7_4)));
1651	item->SetMarked(fWidthAspect == 7 && fHeightAspect == 4);
1652	menu->AddItem(item = new BMenuItem(B_TRANSLATE("1.85 : 1 (American)"),
1653		new BMessage(M_ASPECT_37_20)));
1654	item->SetMarked(fWidthAspect == 37 && fHeightAspect == 20);
1655	menu->AddItem(item = new BMenuItem(B_TRANSLATE("2.35 : 1 (Cinemascope)"),
1656		new BMessage(M_ASPECT_47_20)));
1657	item->SetMarked(fWidthAspect == 47 && fHeightAspect == 20);
1658}
1659
1660
1661void
1662MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu,
1663	BMenu* subTitleTrackMenu)
1664{
1665	audioTrackMenu->RemoveItems(0, audioTrackMenu->CountItems(), true);
1666	videoTrackMenu->RemoveItems(0, videoTrackMenu->CountItems(), true);
1667	subTitleTrackMenu->RemoveItems(0, subTitleTrackMenu->CountItems(), true);
1668
1669	char s[100];
1670
1671	int count = fController->AudioTrackCount();
1672	int current = fController->CurrentAudioTrack();
1673	for (int i = 0; i < count; i++) {
1674		BMessage metaData;
1675		const char* languageString = NULL;
1676		if (fController->GetAudioMetaData(i, &metaData) == B_OK)
1677			metaData.FindString("language", &languageString);
1678		if (languageString != NULL) {
1679			BLanguage language(languageString);
1680			BString languageName;
1681			if (language.GetName(languageName) == B_OK)
1682				languageString = languageName.String();
1683			snprintf(s, sizeof(s), "%s", languageString);
1684		} else
1685			snprintf(s, sizeof(s), B_TRANSLATE("Track %d"), i + 1);
1686		BMenuItem* item = new BMenuItem(s,
1687			new BMessage(M_SELECT_AUDIO_TRACK + i));
1688		item->SetMarked(i == current);
1689		audioTrackMenu->AddItem(item);
1690	}
1691	if (count == 0) {
1692		audioTrackMenu->AddItem(new BMenuItem(B_TRANSLATE_CONTEXT("none",
1693			"Audio track menu"), new BMessage(M_DUMMY)));
1694		audioTrackMenu->ItemAt(0)->SetMarked(true);
1695	}
1696	audioTrackMenu->SetEnabled(count > 1);
1697
1698	count = fController->VideoTrackCount();
1699	current = fController->CurrentVideoTrack();
1700	for (int i = 0; i < count; i++) {
1701		snprintf(s, sizeof(s), B_TRANSLATE("Track %d"), i + 1);
1702		BMenuItem* item = new BMenuItem(s,
1703			new BMessage(M_SELECT_VIDEO_TRACK + i));
1704		item->SetMarked(i == current);
1705		videoTrackMenu->AddItem(item);
1706	}
1707	if (count == 0) {
1708		videoTrackMenu->AddItem(new BMenuItem(B_TRANSLATE("none"),
1709			new BMessage(M_DUMMY)));
1710		videoTrackMenu->ItemAt(0)->SetMarked(true);
1711	}
1712	videoTrackMenu->SetEnabled(count > 1);
1713
1714	count = fController->SubTitleTrackCount();
1715	if (count > 0) {
1716		current = fController->CurrentSubTitleTrack();
1717		BMenuItem* item = new BMenuItem(
1718			B_TRANSLATE_CONTEXT("Off", "Subtitles menu"),
1719			new BMessage(M_SELECT_SUB_TITLE_TRACK - 1));
1720		subTitleTrackMenu->AddItem(item);
1721		item->SetMarked(current == -1);
1722
1723		subTitleTrackMenu->AddSeparatorItem();
1724
1725		for (int i = 0; i < count; i++) {
1726			const char* name = fController->SubTitleTrackName(i);
1727			if (name != NULL)
1728				snprintf(s, sizeof(s), "%s", name);
1729			else
1730				snprintf(s, sizeof(s), B_TRANSLATE("Track %d"), i + 1);
1731			item = new BMenuItem(s,
1732				new BMessage(M_SELECT_SUB_TITLE_TRACK + i));
1733			item->SetMarked(i == current);
1734			subTitleTrackMenu->AddItem(item);
1735		}
1736	} else {
1737		subTitleTrackMenu->AddItem(new BMenuItem(
1738			B_TRANSLATE_CONTEXT("none", "Subtitles menu"),
1739			new BMessage(M_DUMMY)));
1740		subTitleTrackMenu->ItemAt(0)->SetMarked(true);
1741	}
1742	subTitleTrackMenu->SetEnabled(count > 0);
1743}
1744
1745
1746void
1747MainWin::_UpdateAudioChannelCount(int32 audioTrackIndex)
1748{
1749	fControls->SetAudioChannelCount(fController->AudioTrackChannelCount());
1750}
1751
1752
1753void
1754MainWin::_GetMinimumWindowSize(int& width, int& height) const
1755{
1756	width = MIN_WIDTH;
1757	height = 0;
1758	if (!fNoInterface) {
1759		width = max_c(width, fMenuBarWidth);
1760		width = max_c(width, fControlsWidth);
1761		height = fMenuBarHeight + fControlsHeight;
1762	}
1763}
1764
1765
1766void
1767MainWin::_GetUnscaledVideoSize(int& videoWidth, int& videoHeight) const
1768{
1769	if (fWidthAspect != 0 && fHeightAspect != 0) {
1770		videoWidth = fSourceHeight * fWidthAspect / fHeightAspect;
1771		videoHeight = fSourceWidth * fHeightAspect / fWidthAspect;
1772		// Use the scaling which produces an enlarged view.
1773		if (videoWidth > fSourceWidth) {
1774			// Enlarge width
1775			videoHeight = fSourceHeight;
1776		} else {
1777			// Enlarge height
1778			videoWidth = fSourceWidth;
1779		}
1780	} else {
1781		videoWidth = fSourceWidth;
1782		videoHeight = fSourceHeight;
1783	}
1784}
1785
1786
1787void
1788MainWin::_SetWindowSizeLimits()
1789{
1790	int minWidth;
1791	int minHeight;
1792	_GetMinimumWindowSize(minWidth, minHeight);
1793	SetSizeLimits(minWidth - 1, 32000, minHeight - 1,
1794		fHasVideo ? 32000 : minHeight - 1);
1795}
1796
1797
1798int
1799MainWin::_CurrentVideoSizeInPercent() const
1800{
1801	if (!fHasVideo)
1802		return 0;
1803
1804	int videoWidth;
1805	int videoHeight;
1806	_GetUnscaledVideoSize(videoWidth, videoHeight);
1807
1808	int viewWidth = fVideoView->Bounds().IntegerWidth() + 1;
1809	int viewHeight = fVideoView->Bounds().IntegerHeight() + 1;
1810
1811	int widthPercent = viewWidth * 100 / videoWidth;
1812	int heightPercent = viewHeight * 100 / videoHeight;
1813
1814	if (widthPercent > heightPercent)
1815		return widthPercent;
1816	return heightPercent;
1817}
1818
1819
1820void
1821MainWin::_ZoomVideoView(int percentDiff)
1822{
1823	if (!fHasVideo)
1824		return;
1825
1826	int percent = _CurrentVideoSizeInPercent();
1827	int newSize = percent * (100 + percentDiff) / 100;
1828
1829	if (newSize < 25)
1830		newSize = 25;
1831	if (newSize > 400)
1832		newSize = 400;
1833	if (newSize != percent) {
1834		BMessage message(M_VIEW_SIZE);
1835		message.AddInt32("size", newSize);
1836		PostMessage(&message);
1837	}
1838}
1839
1840
1841void
1842MainWin::_ResizeWindow(int percent, bool useNoVideoWidth, bool stayOnScreen)
1843{
1844	// Get required window size
1845	int videoWidth;
1846	int videoHeight;
1847	_GetUnscaledVideoSize(videoWidth, videoHeight);
1848
1849	videoWidth = (videoWidth * percent) / 100;
1850	videoHeight = (videoHeight * percent) / 100;
1851
1852	// Calculate and set the minimum window size
1853	int width;
1854	int height;
1855	_GetMinimumWindowSize(width, height);
1856
1857	width = max_c(width, videoWidth) - 1;
1858	if (useNoVideoWidth)
1859		width = max_c(width, fNoVideoWidth);
1860	height = height + videoHeight - 1;
1861
1862	if (stayOnScreen) {
1863		BRect screenFrame(BScreen(this).Frame());
1864		BRect frame(Frame());
1865		BRect decoratorFrame(DecoratorFrame());
1866
1867		// Shrink the screen frame by the window border size
1868		screenFrame.top += frame.top - decoratorFrame.top;
1869		screenFrame.left += frame.left - decoratorFrame.left;
1870		screenFrame.right += frame.right - decoratorFrame.right;
1871		screenFrame.bottom += frame.bottom - decoratorFrame.bottom;
1872
1873		// Update frame to what the new size would be
1874		frame.right = frame.left + width;
1875		frame.bottom = frame.top + height;
1876
1877		if (!screenFrame.Contains(frame)) {
1878			// Resize the window so it doesn't extend outside the current
1879			// screen frame.
1880			// We don't use BWindow::MoveOnScreen() in order to resize the
1881			// window while keeping the same aspect ratio.
1882			if (frame.Width() > screenFrame.Width()
1883				|| frame.Height() > screenFrame.Height()) {
1884				// too large
1885				int widthDiff
1886					= frame.IntegerWidth() - screenFrame.IntegerWidth();
1887				int heightDiff
1888					= frame.IntegerHeight() - screenFrame.IntegerHeight();
1889
1890				float shrinkScale;
1891				if (widthDiff > heightDiff)
1892					shrinkScale = (float)(width - widthDiff) / width;
1893				else
1894					shrinkScale = (float)(height - heightDiff) / height;
1895
1896				// Resize width/height and center window
1897				width = lround(width * shrinkScale);
1898				height = lround(height * shrinkScale);
1899				MoveTo((screenFrame.left + screenFrame.right - width) / 2,
1900					(screenFrame.top + screenFrame.bottom - height) / 2);
1901			} else {
1902				// just off-screen on one or more sides
1903				int offsetX = 0;
1904				int offsetY = 0;
1905				if (frame.left < screenFrame.left)
1906					offsetX = (int)(screenFrame.left - frame.left);
1907				else if (frame.right > screenFrame.right)
1908					offsetX = (int)(screenFrame.right - frame.right);
1909				if (frame.top < screenFrame.top)
1910					offsetY = (int)(screenFrame.top - frame.top);
1911				else if (frame.bottom > screenFrame.bottom)
1912					offsetY = (int)(screenFrame.bottom - frame.bottom);
1913				MoveBy(offsetX, offsetY);
1914			}
1915		}
1916	}
1917
1918	ResizeTo(width, height);
1919}
1920
1921
1922void
1923MainWin::_ResizeVideoView(int x, int y, int width, int height)
1924{
1925	// Keep aspect ratio, place video view inside
1926	// the background area (may create black bars).
1927	int videoWidth;
1928	int videoHeight;
1929	_GetUnscaledVideoSize(videoWidth, videoHeight);
1930	float scaledWidth  = videoWidth;
1931	float scaledHeight = videoHeight;
1932	float factor = min_c(width / scaledWidth, height / scaledHeight);
1933	int renderWidth = lround(scaledWidth * factor);
1934	int renderHeight = lround(scaledHeight * factor);
1935	if (renderWidth > width)
1936		renderWidth = width;
1937	if (renderHeight > height)
1938		renderHeight = height;
1939
1940	int xOffset = (width - renderWidth) / 2;
1941	int yOffset = (height - renderHeight) / 2;
1942
1943	fVideoView->MoveTo(x, y);
1944	fVideoView->ResizeTo(width - 1, height - 1);
1945
1946	BRect videoFrame(xOffset, yOffset,
1947		xOffset + renderWidth - 1, yOffset + renderHeight - 1);
1948
1949	fVideoView->SetVideoFrame(videoFrame);
1950	fVideoView->SetSubTitleMaxBottom(height - 1);
1951}
1952
1953
1954// #pragma mark -
1955
1956
1957void
1958MainWin::_MouseDown(BMessage* msg, BView* originalHandler)
1959{
1960	uint32 buttons = msg->FindInt32("buttons");
1961
1962	// On Zeta, only "screen_where" is reliable, "where" and "be:view_where"
1963	// seem to be broken
1964	BPoint screenWhere;
1965	if (msg->FindPoint("screen_where", &screenWhere) != B_OK) {
1966		// TODO: remove
1967		// Workaround for BeOS R5, it has no "screen_where"
1968		if (!originalHandler || msg->FindPoint("where", &screenWhere) < B_OK)
1969			return;
1970		originalHandler->ConvertToScreen(&screenWhere);
1971	}
1972
1973	// double click handling
1974
1975	if (msg->FindInt32("clicks") % 2 == 0) {
1976		BRect rect(screenWhere.x - 1, screenWhere.y - 1, screenWhere.x + 1,
1977			screenWhere.y + 1);
1978		if (rect.Contains(fMouseDownMousePos)) {
1979			if (buttons == B_PRIMARY_MOUSE_BUTTON)
1980				PostMessage(M_TOGGLE_FULLSCREEN);
1981			else if (buttons == B_SECONDARY_MOUSE_BUTTON)
1982				PostMessage(M_TOGGLE_NO_INTERFACE);
1983
1984			return;
1985		}
1986	}
1987
1988	fMouseDownMousePos = screenWhere;
1989	fMouseDownWindowPos = Frame().LeftTop();
1990
1991	if (buttons == B_PRIMARY_MOUSE_BUTTON && !fIsFullscreen) {
1992		// start mouse tracking
1993		fVideoView->SetMouseEventMask(B_POINTER_EVENTS | B_NO_POINTER_HISTORY
1994			/* | B_LOCK_WINDOW_FOCUS */);
1995		fMouseDownTracking = true;
1996	}
1997
1998	// pop up a context menu if right mouse button is down
1999
2000	if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0)
2001		_ShowContextMenu(screenWhere);
2002}
2003
2004
2005void
2006MainWin::_MouseMoved(BMessage* msg, BView* originalHandler)
2007{
2008//	msg->PrintToStream();
2009
2010	BPoint mousePos;
2011	uint32 buttons = msg->FindInt32("buttons");
2012	// On Zeta, only "screen_where" is reliable, "where"
2013	// and "be:view_where" seem to be broken
2014	if (msg->FindPoint("screen_where", &mousePos) != B_OK) {
2015		// TODO: remove
2016		// Workaround for BeOS R5, it has no "screen_where"
2017		if (!originalHandler || msg->FindPoint("where", &mousePos) < B_OK)
2018			return;
2019		originalHandler->ConvertToScreen(&mousePos);
2020	}
2021
2022	if (buttons == B_PRIMARY_MOUSE_BUTTON && fMouseDownTracking
2023		&& !fIsFullscreen) {
2024//		printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y);
2025		float delta_x = mousePos.x - fMouseDownMousePos.x;
2026		float delta_y = mousePos.y - fMouseDownMousePos.y;
2027		float x = fMouseDownWindowPos.x + delta_x;
2028		float y = fMouseDownWindowPos.y + delta_y;
2029//		printf("move window to %.0f, %.0f\n", x, y);
2030		MoveTo(x, y);
2031	}
2032
2033	bigtime_t eventTime;
2034	if (msg->FindInt64("when", &eventTime) != B_OK)
2035		eventTime = system_time();
2036
2037	if (buttons == 0 && fIsFullscreen) {
2038		BPoint moveDelta = mousePos - fLastMousePos;
2039		float moveDeltaDist
2040			= sqrtf(moveDelta.x * moveDelta.x + moveDelta.y * moveDelta.y);
2041		if (eventTime - fLastMouseMovedTime < 200000)
2042			fMouseMoveDist += moveDeltaDist;
2043		else
2044			fMouseMoveDist = moveDeltaDist;
2045		if (fMouseMoveDist > 5)
2046			_ShowFullscreenControls(true);
2047	}
2048
2049	fLastMousePos = mousePos;
2050	fLastMouseMovedTime =eventTime;
2051}
2052
2053
2054void
2055MainWin::_MouseUp(BMessage* msg)
2056{
2057	fMouseDownTracking = false;
2058}
2059
2060
2061void
2062MainWin::_ShowContextMenu(const BPoint& screenPoint)
2063{
2064	printf("Show context menu\n");
2065	BPopUpMenu* menu = new BPopUpMenu("context menu", false, false);
2066	BMenuItem* item;
2067	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Full screen"),
2068		new BMessage(M_TOGGLE_FULLSCREEN), B_ENTER));
2069	item->SetMarked(fIsFullscreen);
2070	item->SetEnabled(fHasVideo);
2071
2072	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Hide interface"),
2073		new BMessage(M_TOGGLE_NO_INTERFACE), 'H'));
2074	item->SetMarked(fNoInterface);
2075	item->SetEnabled(fHasVideo && !fIsFullscreen);
2076
2077	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Always on top"),
2078		new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'A'));
2079	item->SetMarked(fAlwaysOnTop);
2080	item->SetEnabled(fHasVideo);
2081
2082	BMenu* aspectSubMenu = new BMenu(B_TRANSLATE("Aspect ratio"));
2083	_SetupVideoAspectItems(aspectSubMenu);
2084	aspectSubMenu->SetTargetForItems(this);
2085	menu->AddItem(item = new BMenuItem(aspectSubMenu));
2086	item->SetEnabled(fHasVideo);
2087
2088	menu->AddSeparatorItem();
2089
2090	// Add track selector menus
2091	BMenu* audioTrackMenu = new BMenu(B_TRANSLATE("Audio track"));
2092	BMenu* videoTrackMenu = new BMenu(B_TRANSLATE("Video track"));
2093	BMenu* subTitleTrackMenu = new BMenu(B_TRANSLATE("Subtitles"));
2094	_SetupTrackMenus(audioTrackMenu, videoTrackMenu, subTitleTrackMenu);
2095
2096	audioTrackMenu->SetTargetForItems(this);
2097	videoTrackMenu->SetTargetForItems(this);
2098	subTitleTrackMenu->SetTargetForItems(this);
2099
2100	menu->AddItem(item = new BMenuItem(audioTrackMenu));
2101	item->SetEnabled(fHasAudio);
2102
2103	menu->AddItem(item = new BMenuItem(videoTrackMenu));
2104	item->SetEnabled(fHasVideo);
2105
2106	menu->AddItem(item = new BMenuItem(subTitleTrackMenu));
2107	item->SetEnabled(fHasVideo);
2108
2109	menu->AddSeparatorItem();
2110	menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), new BMessage(M_FILE_QUIT), 'Q'));
2111
2112	menu->SetTargetForItems(this);
2113	BRect rect(screenPoint.x - 5, screenPoint.y - 5, screenPoint.x + 5,
2114		screenPoint.y + 5);
2115	menu->Go(screenPoint, true, true, rect, true);
2116}
2117
2118
2119/*!	Trap keys that are about to be send to background or renderer view.
2120	Return true if it shouldn't be passed to the view.
2121*/
2122bool
2123MainWin::_KeyDown(BMessage* msg)
2124{
2125	uint32 key = msg->FindInt32("key");
2126	uint32 rawChar = msg->FindInt32("raw_char");
2127	uint32 modifier = msg->FindInt32("modifiers");
2128
2129//	printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar,
2130//		modifier);
2131
2132	// ignore the system modifier namespace
2133	if ((modifier & (B_CONTROL_KEY | B_COMMAND_KEY))
2134			== (B_CONTROL_KEY | B_COMMAND_KEY))
2135		return false;
2136
2137	switch (rawChar) {
2138		case B_SPACE:
2139			fController->TogglePlaying();
2140			return true;
2141
2142		case 'm':
2143			fController->ToggleMute();
2144			return true;
2145
2146		case B_ESCAPE:
2147			if (!fIsFullscreen)
2148				break;
2149
2150			PostMessage(M_TOGGLE_FULLSCREEN);
2151			return true;
2152
2153		case B_ENTER:		// Enter / Return
2154			if ((modifier & B_COMMAND_KEY) != 0) {
2155				PostMessage(M_TOGGLE_FULLSCREEN);
2156				return true;
2157			}
2158			break;
2159
2160		case B_TAB:
2161		case 'f':
2162			if ((modifier & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY
2163					| B_MENU_KEY)) == 0) {
2164				PostMessage(M_TOGGLE_FULLSCREEN);
2165				return true;
2166			}
2167			break;
2168
2169		case B_UP_ARROW:
2170			if ((modifier & B_COMMAND_KEY) != 0)
2171				PostMessage(M_SKIP_NEXT);
2172			else
2173				PostMessage(M_VOLUME_UP);
2174			return true;
2175
2176		case B_DOWN_ARROW:
2177			if ((modifier & B_COMMAND_KEY) != 0)
2178				PostMessage(M_SKIP_PREV);
2179			else
2180				PostMessage(M_VOLUME_DOWN);
2181			return true;
2182
2183		case B_RIGHT_ARROW:
2184			if ((modifier & B_COMMAND_KEY) != 0)
2185				PostMessage(M_SKIP_NEXT);
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);
2191				} else {
2192					windMessage.AddInt64("how much", 5000000LL);
2193					windMessage.AddInt64("frames", 1);
2194				}
2195				PostMessage(&windMessage);
2196			}
2197			return true;
2198
2199		case B_LEFT_ARROW:
2200			if ((modifier & B_COMMAND_KEY) != 0)
2201				PostMessage(M_SKIP_PREV);
2202			else if (fAllowWinding) {
2203				BMessage windMessage(M_WIND);
2204				if ((modifier & B_SHIFT_KEY) != 0) {
2205					windMessage.AddInt64("how much", -30000000LL);
2206					windMessage.AddInt64("frames", -5);
2207				} else {
2208					windMessage.AddInt64("how much", -5000000LL);
2209					windMessage.AddInt64("frames", -1);
2210				}
2211				PostMessage(&windMessage);
2212			}
2213			return true;
2214
2215		case B_PAGE_UP:
2216			PostMessage(M_SKIP_NEXT);
2217			return true;
2218
2219		case B_PAGE_DOWN:
2220			PostMessage(M_SKIP_PREV);
2221			return true;
2222
2223		case '+':
2224			if ((modifier & B_COMMAND_KEY) == 0) {
2225				_ZoomVideoView(10);
2226				return true;
2227			}
2228			break;
2229
2230		case '-':
2231			if ((modifier & B_COMMAND_KEY) == 0) {
2232				_ZoomVideoView(-10);
2233				return true;
2234			}
2235			break;
2236
2237		case B_DELETE:
2238		case 'd': 			// d for delete
2239		case 't':			// t for Trash
2240			if ((modifiers() & B_COMMAND_KEY) != 0) {
2241				BAutolock _(fPlaylist);
2242				BMessage removeMessage(M_PLAYLIST_MOVE_TO_TRASH);
2243				removeMessage.AddInt32("playlist index",
2244					fPlaylist->CurrentItemIndex());
2245				fPlaylistWindow->PostMessage(&removeMessage);
2246				return true;
2247			}
2248			break;
2249	}
2250
2251	switch (key) {
2252		case 0x3a:  		// numeric keypad +
2253			if ((modifier & B_COMMAND_KEY) == 0) {
2254				_ZoomVideoView(10);
2255				return true;
2256			}
2257			break;
2258
2259		case 0x25:  		// numeric keypad -
2260			if ((modifier & B_COMMAND_KEY) == 0) {
2261				_ZoomVideoView(-10);
2262				return true;
2263			}
2264			break;
2265
2266		case 0x38:			// numeric keypad up arrow
2267			PostMessage(M_VOLUME_UP);
2268			return true;
2269
2270		case 0x59:			// numeric keypad down arrow
2271			PostMessage(M_VOLUME_DOWN);
2272			return true;
2273
2274		case 0x39:			// numeric keypad page up
2275		case 0x4a:			// numeric keypad right arrow
2276			PostMessage(M_SKIP_NEXT);
2277			return true;
2278
2279		case 0x5a:			// numeric keypad page down
2280		case 0x48:			// numeric keypad left arrow
2281			PostMessage(M_SKIP_PREV);
2282			return true;
2283
2284		// Playback controls along the bottom of the keyboard:
2285		// Z X C (V) B  for US International
2286		case 0x4c:
2287			PostMessage(M_SKIP_PREV);
2288			return true;
2289		case 0x4d:
2290			fController->TogglePlaying();
2291			return true;
2292		case 0x4e:
2293			fController->Pause();
2294			return true;
2295		case 0x4f:
2296			fController->Stop();
2297			return true;
2298		case 0x50:
2299			PostMessage(M_SKIP_NEXT);
2300			return true;
2301	}
2302
2303	return false;
2304}
2305
2306
2307// #pragma mark -
2308
2309
2310void
2311MainWin::_ToggleFullscreen()
2312{
2313	printf("_ToggleFullscreen enter\n");
2314
2315	if (!fHasVideo) {
2316		printf("_ToggleFullscreen - ignoring, as we don't have a video\n");
2317		return;
2318	}
2319
2320	fIsFullscreen = !fIsFullscreen;
2321
2322	if (fIsFullscreen) {
2323		// switch to fullscreen
2324
2325		fSavedFrame = Frame();
2326		printf("saving current frame: %d %d %d %d\n", int(fSavedFrame.left),
2327			int(fSavedFrame.top), int(fSavedFrame.right),
2328			int(fSavedFrame.bottom));
2329		BScreen screen(this);
2330		BRect rect(screen.Frame());
2331
2332		Hide();
2333		MoveTo(rect.left, rect.top);
2334		ResizeTo(rect.Width(), rect.Height());
2335		Show();
2336
2337	} else {
2338		// switch back from full screen mode
2339		_ShowFullscreenControls(false, false);
2340
2341		Hide();
2342		MoveTo(fSavedFrame.left, fSavedFrame.top);
2343		ResizeTo(fSavedFrame.Width(), fSavedFrame.Height());
2344		Show();
2345	}
2346
2347	fVideoView->SetFullscreen(fIsFullscreen);
2348
2349	_MarkItem(fFileMenu, M_TOGGLE_FULLSCREEN, fIsFullscreen);
2350
2351	printf("_ToggleFullscreen leave\n");
2352}
2353
2354void
2355MainWin::_ToggleAlwaysOnTop()
2356{
2357	fAlwaysOnTop = !fAlwaysOnTop;
2358	SetFeel(fAlwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL);
2359
2360	_MarkItem(fFileMenu, M_TOGGLE_ALWAYS_ON_TOP, fAlwaysOnTop);
2361}
2362
2363
2364void
2365MainWin::_ToggleNoInterface()
2366{
2367	printf("_ToggleNoInterface enter\n");
2368
2369	if (fIsFullscreen || !fHasVideo) {
2370		// Fullscreen playback is always without interface and
2371		// audio playback is always with interface. So we ignore these
2372		// two states here.
2373		printf("_ToggleNoControls leave, doing nothing, we are fullscreen\n");
2374		return;
2375	}
2376
2377	fNoInterface = !fNoInterface;
2378	_SetWindowSizeLimits();
2379
2380	if (fNoInterface) {
2381		MoveBy(0, fMenuBarHeight);
2382		ResizeBy(0, -(fControlsHeight + fMenuBarHeight));
2383		SetLook(B_BORDERED_WINDOW_LOOK);
2384	} else {
2385		MoveBy(0, -fMenuBarHeight);
2386		ResizeBy(0, fControlsHeight + fMenuBarHeight);
2387		SetLook(B_TITLED_WINDOW_LOOK);
2388	}
2389
2390	_MarkItem(fFileMenu, M_TOGGLE_NO_INTERFACE, fNoInterface);
2391
2392	printf("_ToggleNoInterface leave\n");
2393}
2394
2395
2396void
2397MainWin::_ShowIfNeeded()
2398{
2399	// Only proceed if the window is already running
2400	if (find_thread(NULL) != Thread())
2401		return;
2402
2403	if (!fHasVideo && fNoVideoFrame.IsValid()) {
2404		MoveTo(fNoVideoFrame.LeftTop());
2405		ResizeTo(fNoVideoFrame.Width(), fNoVideoFrame.Height());
2406		MoveOnScreen(B_MOVE_IF_PARTIALLY_OFFSCREEN);
2407	} else if (fHasVideo && IsHidden())
2408		CenterOnScreen();
2409
2410	fNoVideoFrame = BRect();
2411
2412	if (IsHidden()) {
2413		Show();
2414		UpdateIfNeeded();
2415	}
2416}
2417
2418
2419void
2420MainWin::_ShowFullscreenControls(bool show, bool animate)
2421{
2422	if (fShowsFullscreenControls == show)
2423		return;
2424
2425	fShowsFullscreenControls = show;
2426	fVideoView->SetFullscreenControlsVisible(show);
2427
2428	if (show) {
2429		fControls->RemoveSelf();
2430		fControls->MoveTo(fVideoView->Bounds().left,
2431			fVideoView->Bounds().bottom + 1);
2432		fVideoView->AddChild(fControls);
2433		if (fScaleFullscreenControls)
2434			fControls->SetSymbolScale(1.5f);
2435
2436		while (fControls->IsHidden())
2437			fControls->Show();
2438	}
2439
2440	if (animate) {
2441		// Slide the controls into view. We need to do this with
2442		// messages, otherwise we block the video playback for the
2443		// time of the animation.
2444		const float kAnimationOffsets[] = { 0.05, 0.2, 0.5, 0.2, 0.05 };
2445		const int32 steps = sizeof(kAnimationOffsets) / sizeof(float);
2446		float height = fControls->Bounds().Height();
2447		float moveDist = show ? -height : height;
2448		float originalY = fControls->Frame().top;
2449		for (int32 i = 0; i < steps; i++) {
2450			BMessage message(M_SLIDE_CONTROLS);
2451			message.AddFloat("offset",
2452				floorf(moveDist * kAnimationOffsets[i]));
2453			PostMessage(&message, this);
2454		}
2455		BMessage finalMessage(M_FINISH_SLIDING_CONTROLS);
2456		finalMessage.AddFloat("offset", originalY + moveDist);
2457		finalMessage.AddBool("show", show);
2458		PostMessage(&finalMessage, this);
2459	} else if (!show) {
2460		fControls->RemoveSelf();
2461		fControls->MoveTo(fVideoView->Frame().left,
2462			fVideoView->Frame().bottom + 1);
2463		fBackground->AddChild(fControls);
2464		fControls->SetSymbolScale(1.0f);
2465
2466		while (!fControls->IsHidden())
2467			fControls->Hide();
2468	}
2469}
2470
2471
2472// #pragma mark -
2473
2474
2475void
2476MainWin::_Wind(bigtime_t howMuch, int64 frames)
2477{
2478	if (!fAllowWinding || !fController->Lock())
2479		return;
2480
2481	if (frames != 0 && fHasVideo && !fController->IsPlaying()) {
2482		int64 newFrame = fController->CurrentFrame() + frames;
2483		fController->SetFramePosition(newFrame);
2484	} else {
2485		bigtime_t seekTime = fController->TimePosition() + howMuch;
2486		if (seekTime < 0) {
2487			fInitialSeekPosition = seekTime;
2488			PostMessage(M_SKIP_PREV);
2489		} else if (seekTime > fController->TimeDuration()) {
2490			fInitialSeekPosition = 0;
2491			PostMessage(M_SKIP_NEXT);
2492		} else
2493			fController->SetTimePosition(seekTime);
2494	}
2495
2496	fController->Unlock();
2497	fAllowWinding = false;
2498}
2499
2500
2501// #pragma mark -
2502
2503
2504void
2505MainWin::_UpdatePlaylistItemFile()
2506{
2507	BAutolock locker(fPlaylist);
2508	const FilePlaylistItem* item
2509		= dynamic_cast<const FilePlaylistItem*>(fController->Item());
2510	if (item == NULL)
2511		return;
2512
2513	if (!fHasVideo && !fHasAudio)
2514		return;
2515
2516	BNode node(&item->Ref());
2517	if (node.InitCheck())
2518		return;
2519
2520	locker.Unlock();
2521
2522	// Set some standard attributes of the currently played file.
2523	// This should only be a temporary solution.
2524
2525	// Write duration
2526	const char* kDurationAttrName = "Media:Length";
2527	attr_info info;
2528	status_t status = node.GetAttrInfo(kDurationAttrName, &info);
2529	if (status != B_OK || info.size == 0) {
2530		bigtime_t duration = fController->TimeDuration();
2531		// TODO: Tracker does not seem to care about endian for scalar types
2532		node.WriteAttr(kDurationAttrName, B_INT64_TYPE, 0, &duration,
2533			sizeof(int64));
2534	}
2535
2536	// Write audio bitrate
2537	if (fHasAudio) {
2538		status = node.GetAttrInfo("Audio:Bitrate", &info);
2539		if (status != B_OK || info.size == 0) {
2540			media_format format;
2541			if (fController->GetEncodedAudioFormat(&format) == B_OK
2542				&& format.type == B_MEDIA_ENCODED_AUDIO) {
2543				int32 bitrate = (int32)(format.u.encoded_audio.bit_rate
2544					/ 1000);
2545				char text[256];
2546				snprintf(text, sizeof(text), "%" B_PRId32 " kbit", bitrate);
2547				node.WriteAttr("Audio:Bitrate", B_STRING_TYPE, 0, text,
2548					strlen(text) + 1);
2549			}
2550		}
2551	}
2552
2553	// Write video bitrate
2554	if (fHasVideo) {
2555		status = node.GetAttrInfo("Video:Bitrate", &info);
2556		if (status != B_OK || info.size == 0) {
2557			media_format format;
2558			if (fController->GetEncodedVideoFormat(&format) == B_OK
2559				&& format.type == B_MEDIA_ENCODED_VIDEO) {
2560				int32 bitrate = (int32)(format.u.encoded_video.avg_bit_rate
2561					/ 1000);
2562				char text[256];
2563				snprintf(text, sizeof(text), "%" B_PRId32 " kbit", bitrate);
2564				node.WriteAttr("Video:Bitrate", B_STRING_TYPE, 0, text,
2565					strlen(text) + 1);
2566			}
2567		}
2568	}
2569
2570	_UpdateAttributesMenu(node);
2571}
2572
2573
2574void
2575MainWin::_UpdateAttributesMenu(const BNode& node)
2576{
2577	int32 rating = -1;
2578
2579	attr_info info;
2580	status_t status = node.GetAttrInfo(kRatingAttrName, &info);
2581	if (status == B_OK && info.type == B_INT32_TYPE) {
2582		// Node has the Rating attribute.
2583		node.ReadAttr(kRatingAttrName, B_INT32_TYPE, 0, &rating,
2584			sizeof(rating));
2585	}
2586
2587	for (int32 i = 0; BMenuItem* item = fRatingMenu->ItemAt(i); i++)
2588		item->SetMarked(i + 1 == rating);
2589}
2590
2591
2592void
2593MainWin::_SetRating(int32 rating)
2594{
2595	BAutolock locker(fPlaylist);
2596	const FilePlaylistItem* item
2597		= dynamic_cast<const FilePlaylistItem*>(fController->Item());
2598	if (item == NULL)
2599		return;
2600
2601	BNode node(&item->Ref());
2602	if (node.InitCheck())
2603		return;
2604
2605	locker.Unlock();
2606
2607	node.WriteAttr(kRatingAttrName, B_INT32_TYPE, 0, &rating, sizeof(rating));
2608
2609	// TODO: The whole mechnism should work like this:
2610	// * There is already an attribute API for PlaylistItem, flesh it out!
2611	// * FilePlaylistItem node-monitors it's file somehow.
2612	// * FilePlaylistItem keeps attributes in sync and sends notications.
2613	// * MainWin updates the menu according to FilePlaylistItem notifications.
2614	// * PlaylistWin shows columns with attribute and other info.
2615	// * PlaylistWin updates also upon FilePlaylistItem notifications.
2616	// * This keeps attributes in sync when another app changes them.
2617
2618	_UpdateAttributesMenu(node);
2619}
2620
2621
2622void
2623MainWin::_UpdateControlsEnabledStatus()
2624{
2625	uint32 enabledButtons = 0;
2626	if (fHasVideo || fHasAudio) {
2627		enabledButtons |= PLAYBACK_ENABLED | SEEK_ENABLED
2628			| SEEK_BACK_ENABLED | SEEK_FORWARD_ENABLED;
2629	}
2630	if (fHasAudio)
2631		enabledButtons |= VOLUME_ENABLED;
2632
2633	BAutolock _(fPlaylist);
2634	bool canSkipPrevious, canSkipNext;
2635	fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext);
2636	if (canSkipPrevious)
2637		enabledButtons |= SKIP_BACK_ENABLED;
2638	if (canSkipNext)
2639		enabledButtons |= SKIP_FORWARD_ENABLED;
2640
2641	fControls->SetEnabled(enabledButtons);
2642
2643	fNoInterfaceMenuItem->SetEnabled(fHasVideo);
2644	fAttributesMenu->SetEnabled(fHasAudio || fHasVideo);
2645}
2646
2647
2648void
2649MainWin::_UpdatePlaylistMenu()
2650{
2651	if (!fPlaylist->Lock())
2652		return;
2653
2654	fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true);
2655
2656	int32 count = fPlaylist->CountItems();
2657	for (int32 i = 0; i < count; i++) {
2658		PlaylistItem* item = fPlaylist->ItemAtFast(i);
2659		_AddPlaylistItem(item, i);
2660	}
2661	fPlaylistMenu->SetTargetForItems(this);
2662
2663	_MarkPlaylistItem(fPlaylist->CurrentItemIndex());
2664
2665	fPlaylist->Unlock();
2666}
2667
2668
2669void
2670MainWin::_AddPlaylistItem(PlaylistItem* item, int32 index)
2671{
2672	BMessage* message = new BMessage(M_SET_PLAYLIST_POSITION);
2673	message->AddInt32("index", index);
2674	BMenuItem* menuItem = new BMenuItem(item->Name().String(), message);
2675	fPlaylistMenu->AddItem(menuItem, index);
2676}
2677
2678
2679void
2680MainWin::_RemovePlaylistItem(int32 index)
2681{
2682	delete fPlaylistMenu->RemoveItem(index);
2683}
2684
2685
2686void
2687MainWin::_MarkPlaylistItem(int32 index)
2688{
2689	if (BMenuItem* item = fPlaylistMenu->ItemAt(index)) {
2690		item->SetMarked(true);
2691		// ... and in case the menu is currently on screen:
2692		if (fPlaylistMenu->LockLooper()) {
2693			fPlaylistMenu->Invalidate();
2694			fPlaylistMenu->UnlockLooper();
2695		}
2696	}
2697}
2698
2699
2700void
2701MainWin::_MarkItem(BMenu* menu, uint32 command, bool mark)
2702{
2703	if (BMenuItem* item = menu->FindItem(command))
2704		item->SetMarked(mark);
2705}
2706
2707
2708void
2709MainWin::_AdoptGlobalSettings()
2710{
2711	mpSettings settings;
2712	Settings::Default()->Get(settings);
2713
2714	fCloseWhenDonePlayingMovie = settings.closeWhenDonePlayingMovie;
2715	fCloseWhenDonePlayingSound = settings.closeWhenDonePlayingSound;
2716	fLoopMovies = settings.loopMovie;
2717	fLoopSounds = settings.loopSound;
2718	fScaleFullscreenControls = settings.scaleFullscreenControls;
2719}
2720
2721
2722