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