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