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