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