xref: /haiku/src/apps/mediaplayer/MainWin.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
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-2008 Stephan Aßmus <superstippi@gmx.de> (GPL->MIT ok)
6  * Copyright (C) 2007-2008 Fredrik Modéen <fredrik@modeen.se>
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 #include "MainWin.h"
24 
25 #include <math.h>
26 #include <stdio.h>
27 #include <string.h>
28 
29 #include <Alert.h>
30 #include <Application.h>
31 #include <Autolock.h>
32 #include <Debug.h>
33 #include <Menu.h>
34 #include <MenuBar.h>
35 #include <MenuItem.h>
36 #include <Messenger.h>
37 #include <PopUpMenu.h>
38 #include <RecentItems.h>
39 #include <Roster.h>
40 #include <Screen.h>
41 #include <String.h>
42 #include <View.h>
43 
44 #include "AudioProducer.h"
45 #include "ControllerObserver.h"
46 #include "MainApp.h"
47 #include "PeakView.h"
48 #include "PlaylistObserver.h"
49 #include "PlaylistWindow.h"
50 #include "Settings.h"
51 
52 #define NAME "MediaPlayer"
53 #define MIN_WIDTH 250
54 
55 
56 // XXX TODO: why is lround not defined?
57 #define lround(a) ((int)(0.99999 + (a)))
58 
59 enum {
60 	M_DUMMY = 0x100,
61 	M_FILE_OPEN = 0x1000,
62 	M_FILE_NEWPLAYER,
63 	M_FILE_INFO,
64 	M_FILE_PLAYLIST,
65 	M_FILE_CLOSE,
66 	M_FILE_QUIT,
67 	M_VIEW_50,
68 	M_VIEW_100,
69 	M_VIEW_200,
70 	M_VIEW_300,
71 	M_VIEW_400,
72 	M_TOGGLE_FULLSCREEN,
73 	M_TOGGLE_NO_BORDER,
74 	M_TOGGLE_NO_MENU,
75 	M_TOGGLE_NO_CONTROLS,
76 	M_TOGGLE_NO_BORDER_NO_MENU_NO_CONTROLS,
77 	M_TOGGLE_ALWAYS_ON_TOP,
78 	M_TOGGLE_KEEP_ASPECT_RATIO,
79 	M_VOLUME_UP,
80 	M_VOLUME_DOWN,
81 	M_SKIP_NEXT,
82 	M_SKIP_PREV,
83 	M_ASPECT_100000_1,
84 	M_ASPECT_106666_1,
85 	M_ASPECT_109091_1,
86 	M_ASPECT_141176_1,
87 	M_ASPECT_720_576,
88 	M_ASPECT_704_576,
89 	M_ASPECT_544_576,
90 	M_SELECT_AUDIO_TRACK		= 0x00000800,
91 	M_SELECT_AUDIO_TRACK_END	= 0x00000fff,
92 	M_SELECT_VIDEO_TRACK		= 0x00010000,
93 	M_SELECT_VIDEO_TRACK_END	= 0x000fffff,
94 
95 	M_SET_PLAYLIST_POSITION
96 };
97 
98 //#define printf(a...)
99 
100 
101 MainWin::MainWin()
102 	: BWindow(BRect(100,100,400,300), NAME, B_TITLED_WINDOW,
103  		B_ASYNCHRONOUS_CONTROLS /* | B_WILL_ACCEPT_FIRST_CLICK */),
104 	  fFilePanel(NULL),
105 	  fInfoWin(NULL),
106 	  fPlaylistWindow(NULL),
107 	  fHasFile(false),
108 	  fHasVideo(false),
109 	  fHasAudio(false),
110 	  fPlaylist(new Playlist),
111 	  fPlaylistObserver(new PlaylistObserver(this)),
112 	  fController(new Controller),
113 	  fControllerObserver(new ControllerObserver(this,
114 		OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES
115 			| OBSERVE_PLAYBACK_STATE_CHANGES | OBSERVE_POSITION_CHANGES
116 			| OBSERVE_VOLUME_CHANGES)),
117 	  fIsFullscreen(false),
118 	  fKeepAspectRatio(true),
119 	  fAlwaysOnTop(false),
120 	  fNoMenu(false),
121 	  fNoBorder(false),
122 	  fNoControls(false),
123 	  fSourceWidth(-1),
124 	  fSourceHeight(-1),
125 	  fWidthScale(1.0),
126 	  fHeightScale(1.0),
127 	  fMouseDownTracking(false),
128 	  fGlobalSettingsListener(this)
129 {
130 	static int pos = 0;
131 	MoveBy(pos * 25, pos * 25);
132 	pos = (pos + 1) % 15;
133 
134 	BRect rect = Bounds();
135 
136 	// background
137 	fBackground = new BView(rect, "background", B_FOLLOW_ALL,
138 		B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE);
139 	fBackground->SetViewColor(0,0,0);
140 	AddChild(fBackground);
141 
142 	// menu
143 	fMenuBar = new BMenuBar(fBackground->Bounds(), "menu");
144 	_CreateMenu();
145 	fBackground->AddChild(fMenuBar);
146 	fMenuBar->ResizeToPreferred();
147 	fMenuBarWidth = (int)fMenuBar->Frame().Width() + 1;
148 	fMenuBarHeight = (int)fMenuBar->Frame().Height() + 1;
149 	fMenuBar->SetResizingMode(B_FOLLOW_NONE);
150 
151 	// video view
152 	rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right,
153 		fMenuBarHeight + 10);
154 	fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE);
155 	fBackground->AddChild(fVideoView);
156 
157 	// controls
158 	rect = BRect(0, fMenuBarHeight + 11, fBackground->Bounds().right,
159 		fBackground->Bounds().bottom);
160 	fControls = new ControllerView(rect, fController, fPlaylist);
161 	fBackground->AddChild(fControls);
162 	fControls->ResizeToPreferred();
163 	fControlsHeight = (int)fControls->Frame().Height() + 1;
164 	fControlsWidth = (int)fControls->Frame().Width() + 1;
165 	fControls->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT);
166 //	fControls->MoveTo(0, fBackground->Bounds().bottom - fControlsHeight + 1);
167 
168 //	fVideoView->ResizeTo(fBackground->Bounds().Width(),
169 //		fBackground->Bounds().Height() - fMenuBarHeight - fControlsHeight);
170 
171 	fPlaylist->AddListener(fPlaylistObserver);
172 	fController->SetVideoView(fVideoView);
173 	fController->AddListener(fControllerObserver);
174 	PeakView* peakView = fControls->GetPeakView();
175 	peakView->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION);
176 	fController->SetPeakListener(peakView);
177 
178 //	printf("fMenuBarHeight %d\n", fMenuBarHeight);
179 //	printf("fControlsHeight %d\n", fControlsHeight);
180 //	printf("fControlsWidth %d\n", fControlsWidth);
181 
182 	_SetupWindow();
183 
184 	// setup the playlist window now, we need to have it
185 	// running for the undo/redo playlist editing
186 	fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 500, 600), fPlaylist,
187 		fController);
188 	fPlaylistWindow->Hide();
189 	fPlaylistWindow->Show();
190 		// this makes sure the window thread is running without
191 		// showing the window just yet
192 
193 	Settings::Default()->AddListener(&fGlobalSettingsListener);
194 	_AdoptGlobalSettings();
195 
196 	Show();
197 }
198 
199 
200 MainWin::~MainWin()
201 {
202 	printf("MainWin::~MainWin\n");
203 
204 	Settings::Default()->RemoveListener(&fGlobalSettingsListener);
205 	fPlaylist->RemoveListener(fPlaylistObserver);
206 	fController->RemoveListener(fControllerObserver);
207 	fController->SetPeakListener(NULL);
208 
209 	// give the views a chance to detach from any notifiers
210 	// before we delete them
211 	fBackground->RemoveSelf();
212 	delete fBackground;
213 
214 	if (fInfoWin && fInfoWin->Lock())
215 		fInfoWin->Quit();
216 
217 	if (fPlaylistWindow && fPlaylistWindow->Lock())
218 		fPlaylistWindow->Quit();
219 
220 	delete fPlaylist;
221 	delete fFilePanel;
222 
223 	// quit the Controller looper thread
224 	thread_id controllerThread = fController->Thread();
225 	fController->PostMessage(B_QUIT_REQUESTED);
226 	status_t exitValue;
227 	wait_for_thread(controllerThread, &exitValue);
228 }
229 
230 
231 // #pragma mark -
232 
233 
234 void
235 MainWin::FrameResized(float newWidth, float newHeight)
236 {
237 	if (newWidth != Bounds().Width() || newHeight != Bounds().Height()) {
238 		debugger("size wrong\n");
239 	}
240 
241 	bool noMenu = fNoMenu || fIsFullscreen;
242 	bool noControls = fNoControls || fIsFullscreen;
243 
244 	printf("FrameResized enter: newWidth %.0f, newHeight %.0f\n",
245 		newWidth, newHeight);
246 
247 	int maxVideoWidth  = int(newWidth) + 1;
248 	int maxVideoHeight = int(newHeight) + 1
249 		- (noMenu  ? 0 : fMenuBarHeight)
250 		- (noControls ? 0 : fControlsHeight);
251 
252 	ASSERT(maxVideoHeight >= 0);
253 
254 	int y = 0;
255 
256 	if (noMenu) {
257 		if (!fMenuBar->IsHidden())
258 			fMenuBar->Hide();
259 	} else {
260 //		fMenuBar->MoveTo(0, y);
261 		fMenuBar->ResizeTo(newWidth, fMenuBarHeight - 1);
262 		if (fMenuBar->IsHidden())
263 			fMenuBar->Show();
264 		y += fMenuBarHeight;
265 	}
266 
267 	if (maxVideoHeight == 0) {
268 		bool hidden = fVideoView->IsHidden();
269 printf("  video view hidden: %d\n", hidden);
270 		if (!hidden)
271 			fVideoView->Hide();
272 	} else {
273 		_ResizeVideoView(0, y, maxVideoWidth, maxVideoHeight);
274 		if (fVideoView->IsHidden())
275 			fVideoView->Show();
276 		y += maxVideoHeight;
277 	}
278 
279 	if (noControls) {
280 		if (!fControls->IsHidden())
281 			fControls->Hide();
282 	} else {
283 		fControls->MoveTo(0, y);
284 		fControls->ResizeTo(newWidth, fControlsHeight - 1);
285 		if (fControls->IsHidden())
286 			fControls->Show();
287 //		y += fControlsHeight;
288 	}
289 
290 	printf("FrameResized leave\n");
291 }
292 
293 
294 void
295 MainWin::Zoom(BPoint rec_position, float rec_width, float rec_height)
296 {
297 	PostMessage(M_TOGGLE_FULLSCREEN);
298 }
299 
300 
301 void
302 MainWin::DispatchMessage(BMessage *msg, BHandler *handler)
303 {
304 	if ((msg->what == B_MOUSE_DOWN)
305 		&& (handler == fBackground || handler == fVideoView
306 				|| handler == fControls))
307 		_MouseDown(msg, dynamic_cast<BView*>(handler));
308 
309 	if ((msg->what == B_MOUSE_MOVED)
310 		&& (handler == fBackground || handler == fVideoView
311 				|| handler == fControls))
312 		_MouseMoved(msg, dynamic_cast<BView*>(handler));
313 
314 	if ((msg->what == B_MOUSE_UP)
315 		&& (handler == fBackground || handler == fVideoView))
316 		_MouseUp(msg);
317 
318 	if ((msg->what == B_KEY_DOWN)
319 		&& (handler == fBackground || handler == fVideoView)) {
320 
321 		// special case for PrintScreen key
322 		if (msg->FindInt32("key") == B_PRINT_KEY) {
323 			fVideoView->OverlayScreenshotPrepare();
324 			BWindow::DispatchMessage(msg, handler);
325 			fVideoView->OverlayScreenshotCleanup();
326 			return;
327 		}
328 
329 		// every other key gets dispatched to our _KeyDown first
330 		if (_KeyDown(msg) == B_OK) {
331 			// it got handled, don't pass it on
332 			return;
333 		}
334 	}
335 
336 	BWindow::DispatchMessage(msg, handler);
337 }
338 
339 
340 void
341 MainWin::MessageReceived(BMessage *msg)
342 {
343 //	msg->PrintToStream();
344 	switch (msg->what) {
345 		case B_REFS_RECEIVED:
346 			printf("MainWin::MessageReceived: B_REFS_RECEIVED\n");
347 			_RefsReceived(msg);
348 			break;
349 		case B_SIMPLE_DATA:
350 			printf("MainWin::MessageReceived: B_SIMPLE_DATA\n");
351 			if (msg->HasRef("refs")) {
352 				// add to recent documents as it's not done with drag-n-drop
353 				entry_ref ref;
354 				for (int32 i = 0; msg->FindRef("refs", i, &ref) == B_OK; i++) {
355 					be_roster->AddToRecentDocuments(&ref, kAppSig);
356 				}
357 				_RefsReceived(msg);
358 			}
359 			break;
360 
361 		case M_MEDIA_SERVER_STARTED:
362 			printf("TODO: implement M_MEDIA_SERVER_STARTED\n");
363 			// fController->...
364 			break;
365 
366 		case M_MEDIA_SERVER_QUIT:
367 			printf("TODO: implement M_MEDIA_SERVER_QUIT\n");
368 			// fController->...
369 			break;
370 
371 		// PlaylistObserver messages
372 		case MSG_PLAYLIST_REF_ADDED: {
373 			entry_ref ref;
374 			int32 index;
375 			if (msg->FindRef("refs", &ref) == B_OK
376 				&& msg->FindInt32("index", &index) == B_OK) {
377 				_AddPlaylistItem(ref, index);
378 			}
379 			break;
380 		}
381 		case MSG_PLAYLIST_REF_REMOVED: {
382 			int32 index;
383 			if (msg->FindInt32("index", &index) == B_OK) {
384 				_RemovePlaylistItem(index);
385 			}
386 			break;
387 		}
388 		case MSG_PLAYLIST_CURRENT_REF_CHANGED: {
389 			BAutolock _(fPlaylist);
390 
391 			int32 index;
392 			if (msg->FindInt32("index", &index) < B_OK
393 				|| index != fPlaylist->CurrentRefIndex())
394 				break;
395 			entry_ref ref;
396 			if (fPlaylist->GetRefAt(index, &ref) == B_OK) {
397 				printf("open ref: %s\n", ref.name);
398 				OpenFile(ref);
399 				_MarkPlaylistItem(index);
400 			}
401 			break;
402 		}
403 
404 		// ControllerObserver messages
405 		case MSG_CONTROLLER_FILE_FINISHED:
406 		{
407 			bool hadNext = fPlaylist->SetCurrentRefIndex(
408 				fPlaylist->CurrentRefIndex() + 1);
409 			if (!hadNext) {
410 				if (fHasVideo) {
411 					if (fCloseWhenDonePlayingMovie)
412 						PostMessage(B_QUIT_REQUESTED);
413 				} else {
414 					if (fCloseWhenDonePlayingSound)
415 						PostMessage(B_QUIT_REQUESTED);
416 				}
417 			}
418 			break;
419 		}
420 		case MSG_CONTROLLER_FILE_CHANGED:
421 			// TODO: move all other GUI changes as a reaction to this
422 			// notification
423 //			_UpdatePlaylistMenu();
424 			break;
425 		case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: {
426 			int32 index;
427 			if (msg->FindInt32("index", &index) == B_OK) {
428 				BMenuItem* item = fVideoTrackMenu->ItemAt(index);
429 				if (item)
430 					item->SetMarked(true);
431 			}
432 			break;
433 		}
434 		case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: {
435 			int32 index;
436 			if (msg->FindInt32("index", &index) == B_OK) {
437 				BMenuItem* item = fAudioTrackMenu->ItemAt(index);
438 				if (item)
439 					item->SetMarked(true);
440 			}
441 			break;
442 		}
443 		case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: {
444 			uint32 state;
445 			if (msg->FindInt32("state", (int32*)&state) == B_OK)
446 				fControls->SetPlaybackState(state);
447 			break;
448 		}
449 		case MSG_CONTROLLER_POSITION_CHANGED: {
450 			float position;
451 			if (msg->FindFloat("position", &position) == B_OK)
452 				fControls->SetPosition(position);
453 			break;
454 		}
455 		case MSG_CONTROLLER_VOLUME_CHANGED: {
456 			float volume;
457 			if (msg->FindFloat("volume", &volume) == B_OK)
458 				fControls->SetVolume(volume);
459 			break;
460 		}
461 		case MSG_CONTROLLER_MUTED_CHANGED: {
462 			bool muted;
463 			if (msg->FindBool("muted", &muted) == B_OK)
464 				fControls->SetMuted(muted);
465 			break;
466 		}
467 
468 		// menu item messages
469 		case M_FILE_NEWPLAYER:
470 			gMainApp->NewWindow();
471 			break;
472 		case M_FILE_OPEN:
473 			if (!fFilePanel) {
474 				fFilePanel = new BFilePanel();
475 				fFilePanel->SetTarget(BMessenger(0, this));
476 				fFilePanel->SetPanelDirectory("/boot/home/");
477 			}
478 			fFilePanel->Show();
479 			break;
480 		case M_FILE_INFO:
481 			ShowFileInfo();
482 			break;
483 		case M_FILE_PLAYLIST:
484 			ShowPlaylistWindow();
485 			break;
486 		case B_ABOUT_REQUESTED:
487 			BAlert *alert;
488 			alert = new BAlert("about", NAME"\n\n Written by Marcus Overhagen "
489 				", Stephan Aßmus and Frederik Modéen", "Thanks");
490 			if (fAlwaysOnTop) {
491 				_ToggleAlwaysOnTop();
492 				alert->Go(NULL);  // Asynchronous mode
493 				_ToggleAlwaysOnTop();
494 			} else {
495 				alert->Go(NULL); // Asynchronous mode
496 			}
497 			break;
498 		case M_FILE_CLOSE:
499 			PostMessage(B_QUIT_REQUESTED);
500 			break;
501 		case M_FILE_QUIT:
502 			be_app->PostMessage(B_QUIT_REQUESTED);
503 			break;
504 
505 		case M_TOGGLE_FULLSCREEN:
506 			_ToggleFullscreen();
507 			break;
508 
509 		case M_TOGGLE_NO_MENU:
510 			_ToggleNoMenu();
511 			break;
512 
513 		case M_TOGGLE_NO_CONTROLS:
514 			_ToggleNoControls();
515 			break;
516 
517 		case M_TOGGLE_NO_BORDER:
518 			_ToggleNoBorder();
519 			break;
520 
521 		case M_TOGGLE_ALWAYS_ON_TOP:
522 			_ToggleAlwaysOnTop();
523 			break;
524 
525 		case M_TOGGLE_KEEP_ASPECT_RATIO:
526 			_ToggleKeepAspectRatio();
527 			break;
528 
529 		case M_TOGGLE_NO_BORDER_NO_MENU_NO_CONTROLS:
530 			_ToggleNoBorderNoMenu();
531 			break;
532 
533 		case M_VIEW_50:
534 			if (!fHasVideo)
535 				break;
536 			if (fIsFullscreen)
537 				_ToggleFullscreen();
538 			_ResizeWindow(50);
539 			break;
540 
541 		case M_VIEW_100:
542 			if (!fHasVideo)
543 				break;
544 			if (fIsFullscreen)
545 				_ToggleFullscreen();
546 			_ResizeWindow(100);
547 			break;
548 
549 		case M_VIEW_200:
550 			if (!fHasVideo)
551 				break;
552 			if (fIsFullscreen)
553 				_ToggleFullscreen();
554 			_ResizeWindow(200);
555 			break;
556 
557 		case M_VIEW_300:
558 			if (!fHasVideo)
559 				break;
560 			if (fIsFullscreen)
561 				_ToggleFullscreen();
562 			_ResizeWindow(300);
563 			break;
564 
565 		case M_VIEW_400:
566 			if (!fHasVideo)
567 				break;
568 			if (fIsFullscreen)
569 				_ToggleFullscreen();
570 			_ResizeWindow(400);
571 			break;
572 
573 /*
574 
575 		case B_ACQUIRE_OVERLAY_LOCK:
576 			printf("B_ACQUIRE_OVERLAY_LOCK\n");
577 			fVideoView->OverlayLockAcquire();
578 			break;
579 
580 		case B_RELEASE_OVERLAY_LOCK:
581 			printf("B_RELEASE_OVERLAY_LOCK\n");
582 			fVideoView->OverlayLockRelease();
583 			break;
584 
585 		case B_MOUSE_WHEEL_CHANGED:
586 		{
587 			printf("B_MOUSE_WHEEL_CHANGED\n");
588 			float dx = msg->FindFloat("be:wheel_delta_x");
589 			float dy = msg->FindFloat("be:wheel_delta_y");
590 			bool inv = modifiers() & B_COMMAND_KEY;
591 			if (dx > 0.1)	PostMessage(inv ? M_VOLUME_DOWN : M_SKIP_PREV);
592 			if (dx < -0.1)	PostMessage(inv ? M_VOLUME_UP : M_SKIP_NEXT);
593 			if (dy > 0.1)	PostMessage(inv ? M_SKIP_PREV : M_VOLUME_DOWN);
594 			if (dy < -0.1)	PostMessage(inv ? M_SKIP_NEXT : M_VOLUME_UP);
595 			break;
596 		}
597 */
598 		case M_SKIP_NEXT:
599 			fControls->SkipForward();
600 			break;
601 
602 		case M_SKIP_PREV:
603 			fControls->SkipBackward();
604 			break;
605 
606 		case M_VOLUME_UP:
607 			fController->VolumeUp();
608 			break;
609 
610 		case M_VOLUME_DOWN:
611 			fController->VolumeDown();
612 			break;
613 
614 		case M_ASPECT_100000_1:
615 			VideoFormatChange(fSourceWidth, fSourceHeight, 1.0, 1.0);
616 			break;
617 
618 		case M_ASPECT_106666_1:
619 			VideoFormatChange(fSourceWidth, fSourceHeight, 1.06666, 1.0);
620 			break;
621 
622 		case M_ASPECT_109091_1:
623 			VideoFormatChange(fSourceWidth, fSourceHeight, 1.09091, 1.0);
624 			break;
625 
626 		case M_ASPECT_141176_1:
627 			VideoFormatChange(fSourceWidth, fSourceHeight, 1.41176, 1.0);
628 			break;
629 
630 		case M_ASPECT_720_576:
631 			VideoFormatChange(720, 576, 1.06666, 1.0);
632 			break;
633 
634 		case M_ASPECT_704_576:
635 			VideoFormatChange(704, 576, 1.09091, 1.0);
636 			break;
637 
638 		case M_ASPECT_544_576:
639 			VideoFormatChange(544, 576, 1.41176, 1.0);
640 			break;
641 
642 /*
643 		default:
644 			if (msg->what >= M_SELECT_CHANNEL
645 				&& msg->what <= M_SELECT_CHANNEL_END) {
646 				SelectChannel(msg->what - M_SELECT_CHANNEL);
647 				break;
648 			}
649 			if (msg->what >= M_SELECT_INTERFACE
650 				&& msg->what <= M_SELECT_INTERFACE_END) {
651 				SelectInterface(msg->what - M_SELECT_INTERFACE - 1);
652 				break;
653 			}
654 */
655 		case M_SET_PLAYLIST_POSITION: {
656 			int32 index;
657 			if (msg->FindInt32("index", &index) == B_OK)
658 				fPlaylist->SetCurrentRefIndex(index);
659 			break;
660 		}
661 
662 		case MSG_OBJECT_CHANGED:
663 			// received from fGlobalSettingsListener
664 			// TODO: find out which object, if we ever watch more than
665 			// the global settings instance...
666 			_AdoptGlobalSettings();
667 			break;
668 
669 		default:
670 			// let BWindow handle the rest
671 			BWindow::MessageReceived(msg);
672 	}
673 }
674 
675 
676 void
677 MainWin::WindowActivated(bool active)
678 {
679 	if (active) {
680 		BScreen screen(this);
681 		BRect screenFrame = screen.Frame();
682 		BRect frame = Frame();
683 		float diffX = 0.0;
684 		float diffY = 0.0;
685 
686 		// If the frame is off the edge of the screen at all
687 		// we will move it so all the window is on the screen.
688 		if (frame.right > screenFrame.right)
689 			// Move left
690 			diffX = screenFrame.right - frame.right;
691 		if (frame.bottom > screenFrame.bottom)
692 			// Move up
693 			diffY = screenFrame.bottom - frame.bottom;
694 		if (frame.left < screenFrame.left)
695 			// Move right
696 			diffX = screenFrame.left - frame.left;
697 		if (frame.top < screenFrame.top)
698 			// Move down
699 			diffY = screenFrame.top - frame.top;
700 
701 		MoveBy(diffX, diffY);
702 	}
703 
704 	fController->PlayerActivated(active || gMainApp->PlayerCount() <= 1);
705 }
706 
707 
708 bool
709 MainWin::QuitRequested()
710 {
711 	be_app->PostMessage(M_PLAYER_QUIT);
712 	return true;
713 }
714 
715 
716 // #pragma mark -
717 
718 
719 void
720 MainWin::OpenFile(const entry_ref &ref)
721 {
722 	printf("MainWin::OpenFile\n");
723 
724 	status_t err = fController->SetTo(ref);
725 	if (err != B_OK) {
726 		if (fPlaylist->CountItems() == 1) {
727 			// display error if this is the only file we're supposed to play
728 			BString message;
729 			message << "The file '";
730 			message << ref.name;
731 			message << "' could not be opened.\n\n";
732 
733 			if (err == B_MEDIA_NO_HANDLER) {
734 				// give a more detailed message for the most likely of all
735 				// errors
736 				message << "There is no decoder installed to handle the "
737 					"file format, or the decoder has trouble with the specific "
738 					"version of the format.";
739 			} else {
740 				message << "Error: " << strerror(err);
741 			}
742 			(new BAlert("error", message.String(), "OK"))->Go();
743 		} else {
744 			// just go to the next file and don't bother user
745 			fPlaylist->SetCurrentRefIndex(fPlaylist->CurrentRefIndex() + 1);
746 		}
747 		fHasFile = false;
748 		fHasVideo = false;
749 		fHasAudio = false;
750 		SetTitle(NAME);
751 	} else {
752 		fHasFile = true;
753 		fHasVideo = fController->VideoTrackCount() != 0;
754 		fHasAudio = fController->AudioTrackCount() != 0;
755 		SetTitle(ref.name);
756 	}
757 	_SetupWindow();
758 }
759 
760 
761 void
762 MainWin::ShowFileInfo()
763 {
764 	if (!fInfoWin)
765 		fInfoWin = new InfoWin(Frame().LeftTop(), fController);
766 
767 	if (fInfoWin->Lock()) {
768 		if (fInfoWin->IsHidden())
769 			fInfoWin->Show();
770 		else
771 			fInfoWin->Activate();
772 		fInfoWin->Unlock();
773 	}
774 }
775 
776 
777 void
778 MainWin::ShowPlaylistWindow()
779 {
780 	if (fPlaylistWindow->Lock()) {
781 		if (fPlaylistWindow->IsHidden())
782 			fPlaylistWindow->Show();
783 		else
784 			fPlaylistWindow->Activate();
785 		fPlaylistWindow->Unlock();
786 	}
787 }
788 
789 
790 void
791 MainWin::VideoFormatChange(int width, int height, float width_scale,
792 	float height_scale)
793 {
794 	// called when video format or aspect ratio changes
795 
796 	printf("VideoFormatChange enter: width %d, height %d, width_scale %.6f, "
797 		"height_scale %.6f\n", width, height, width_scale, height_scale);
798 
799 	if (width_scale < 1.0 && height_scale >= 1.0) {
800 		width_scale  = 1.0 / width_scale;
801 		height_scale = 1.0 / height_scale;
802 		printf("inverting! new values: width_scale %.6f, height_scale %.6f\n",
803 			width_scale, height_scale);
804 	}
805 
806  	fSourceWidth  = width;
807  	fSourceHeight = height;
808  	fWidthScale   = width_scale;
809  	fHeightScale  = height_scale;
810 
811  	FrameResized(Bounds().Width(), Bounds().Height());
812 
813 	printf("VideoFormatChange leave\n");
814 }
815 
816 
817 // #pragma mark -
818 
819 
820 void
821 MainWin::_RefsReceived(BMessage* msg)
822 {
823 	// the playlist ist replaced by dropped files
824 	// or the dropped files are appended to the end
825 	// of the existing playlist if <shift> is pressed
826 	int32 appendIndex = modifiers() & B_SHIFT_KEY ?
827 		fPlaylist->CountItems() : -1;
828 	msg->AddInt32("append_index", appendIndex);
829 
830 	// forward the message to the playlist window,
831 	// so that undo/redo is used for modifying the playlist
832 	fPlaylistWindow->PostMessage(msg);
833 }
834 
835 
836 void
837 MainWin::_SetupWindow()
838 {
839 	printf("MainWin::_SetupWindow\n");
840 	// Populate the track menus
841 	_SetupTrackMenus();
842 	// Enable both if a file was loaded
843 	fAudioTrackMenu->SetEnabled(fHasFile);
844 	fVideoTrackMenu->SetEnabled(fHasFile);
845 
846 	fVideoMenu->SetEnabled(fHasVideo);
847 	fAudioMenu->SetEnabled(fHasAudio);
848 //	fDebugMenu->SetEnabled(fHasVideo);
849 	int previousSourceWidth = fSourceWidth;
850 	int previousSourceHeight = fSourceHeight;
851 	if (fHasVideo) {
852 		fController->GetSize(&fSourceWidth, &fSourceHeight);
853 		fWidthScale = 1.0;
854 		fHeightScale = 1.0;
855 			// TODO: implement aspect ratio
856 	} else {
857 		fSourceWidth = 0;
858 		fSourceHeight = 0;
859 		fWidthScale = 1.0;
860 		fHeightScale = 1.0;
861 	}
862 	_UpdateControlsEnabledStatus();
863 
864 	// adopt the size and window layout if necessary
865 	if (!fIsFullscreen && (previousSourceWidth != fSourceWidth
866 			|| previousSourceHeight != fSourceHeight)) {
867 		_ResizeWindow(100);
868 	}
869 
870 	fVideoView->MakeFocus();
871 }
872 
873 
874 void
875 MainWin::_CreateMenu()
876 {
877 	fFileMenu = new BMenu(NAME);
878 	fPlaylistMenu = new BMenu("Playlist"B_UTF8_ELLIPSIS);
879 	fAudioMenu = new BMenu("Audio");
880 	fVideoMenu = new BMenu("Video");
881 	fSettingsMenu = new BMenu("Settings");
882 	fAudioTrackMenu = new BMenu("Track");
883 	fVideoTrackMenu = new BMenu("Track");
884 //	fDebugMenu = new BMenu("Debug");
885 
886 	fMenuBar->AddItem(fFileMenu);
887 	fMenuBar->AddItem(fAudioMenu);
888 	fMenuBar->AddItem(fVideoMenu);
889 	fMenuBar->AddItem(fSettingsMenu);
890 //	fMenuBar->AddItem(fDebugMenu);
891 
892 	fFileMenu->AddItem(new BMenuItem("New Player"B_UTF8_ELLIPSIS,
893 		new BMessage(M_FILE_NEWPLAYER), 'N'));
894 	fFileMenu->AddSeparatorItem();
895 
896 //	fFileMenu->AddItem(new BMenuItem("Open File"B_UTF8_ELLIPSIS,
897 //		new BMessage(M_FILE_OPEN), 'O'));
898 	// Add recent files
899 	BRecentFilesList recentFiles(10, false, NULL, kAppSig);
900 	BMenuItem *item = new BMenuItem(recentFiles.NewFileListMenu(
901 		"Open File"B_UTF8_ELLIPSIS, new BMessage(B_REFS_RECEIVED),
902 		NULL, this, 10, false, NULL, 0, kAppSig), new BMessage(M_FILE_OPEN));
903 	item->SetShortcut('O', 0);
904 	fFileMenu->AddItem(item);
905 
906 	fFileMenu->AddItem(new BMenuItem("File Info"B_UTF8_ELLIPSIS,
907 		new BMessage(M_FILE_INFO), 'I'));
908 	fFileMenu->AddItem(fPlaylistMenu);
909 	fPlaylistMenu->Superitem()->SetShortcut('P', B_COMMAND_KEY);
910 	fPlaylistMenu->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST));
911 
912 	fFileMenu->AddSeparatorItem();
913 	fFileMenu->AddItem(new BMenuItem("About " NAME B_UTF8_ELLIPSIS,
914 		new BMessage(B_ABOUT_REQUESTED)));
915 	fFileMenu->AddSeparatorItem();
916 	fFileMenu->AddItem(new BMenuItem("Close", new BMessage(M_FILE_CLOSE), 'W'));
917 	fFileMenu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q'));
918 
919 	fPlaylistMenu->SetRadioMode(true);
920 
921 	fAudioMenu->AddItem(fAudioTrackMenu);
922 
923 	fVideoMenu->AddItem(fVideoTrackMenu);
924 	fVideoMenu->AddSeparatorItem();
925 	fVideoMenu->AddItem(new BMenuItem("50% scale",
926 		new BMessage(M_VIEW_50), '0'));
927 	fVideoMenu->AddItem(new BMenuItem("100% scale",
928 		new BMessage(M_VIEW_100), '1'));
929 	fVideoMenu->AddItem(new BMenuItem("200% scale",
930 		new BMessage(M_VIEW_200), '2'));
931 	fVideoMenu->AddItem(new BMenuItem("300% scale",
932 		new BMessage(M_VIEW_300), '3'));
933 	fVideoMenu->AddItem(new BMenuItem("400% scale",
934 		new BMessage(M_VIEW_400), '4'));
935 	fVideoMenu->AddSeparatorItem();
936 	fVideoMenu->AddItem(new BMenuItem("Full Screen",
937 		new BMessage(M_TOGGLE_FULLSCREEN), 'F'));
938 	fVideoMenu->AddItem(new BMenuItem("Keep Aspect Ratio",
939 		new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K'));
940 
941 	fSettingsMenu->AddItem(new BMenuItem("No Menu",
942 		new BMessage(M_TOGGLE_NO_MENU), 'M'));
943 	fSettingsMenu->AddItem(new BMenuItem("No Border",
944 		new BMessage(M_TOGGLE_NO_BORDER), 'B'));
945 	fSettingsMenu->AddItem(new BMenuItem("No Controls",
946 		new BMessage(M_TOGGLE_NO_CONTROLS), 'C'));
947 	fSettingsMenu->AddItem(new BMenuItem("Always on Top",
948 		new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T'));
949 	fSettingsMenu->AddSeparatorItem();
950 	item = new BMenuItem("Settings"B_UTF8_ELLIPSIS,
951 		new BMessage(M_SETTINGS), 'S');
952 	fSettingsMenu->AddItem(item);
953 	item->SetTarget(be_app);
954 
955 //	fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.00000:1",
956 //		new BMessage(M_ASPECT_100000_1)));
957 //	fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.06666:1",
958 //		new BMessage(M_ASPECT_106666_1)));
959 //	fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.09091:1",
960 //		new BMessage(M_ASPECT_109091_1)));
961 //	fDebugMenu->AddItem(new BMenuItem("pixel aspect ratio 1.41176:1",
962 //		new BMessage(M_ASPECT_141176_1)));
963 //	fDebugMenu->AddItem(new BMenuItem("force 720 x 576, display aspect 4:3",
964 //		new BMessage(M_ASPECT_720_576)));
965 //	fDebugMenu->AddItem(new BMenuItem("force 704 x 576, display aspect 4:3",
966 //		new BMessage(M_ASPECT_704_576)));
967 //	fDebugMenu->AddItem(new BMenuItem("force 544 x 576, display aspect 4:3",
968 //		new BMessage(M_ASPECT_544_576)));
969 
970 	fAudioTrackMenu->SetRadioMode(true);
971 	fVideoTrackMenu->SetRadioMode(true);
972 }
973 
974 
975 void
976 MainWin::_SetupTrackMenus()
977 {
978 	fAudioTrackMenu->RemoveItems(0, fAudioTrackMenu->CountItems(), true);
979 	fVideoTrackMenu->RemoveItems(0, fVideoTrackMenu->CountItems(), true);
980 
981 	char s[100];
982 
983 	int count = fController->AudioTrackCount();
984 	int current = fController->CurrentAudioTrack();
985 	for (int i = 0; i < count; i++) {
986 		sprintf(s, "Track %d", i + 1);
987 		BMenuItem* item = new BMenuItem(s,
988 			new BMessage(M_SELECT_AUDIO_TRACK + i));
989 		item->SetMarked(i == current);
990 		fAudioTrackMenu->AddItem(item);
991 	}
992 	if (!count) {
993 		fAudioTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY)));
994 		fAudioTrackMenu->ItemAt(0)->SetMarked(true);
995 	}
996 
997 
998 	count = fController->VideoTrackCount();
999 	current = fController->CurrentVideoTrack();
1000 	for (int i = 0; i < count; i++) {
1001 		sprintf(s, "Track %d", i + 1);
1002 		BMenuItem* item = new BMenuItem(s,
1003 			new BMessage(M_SELECT_VIDEO_TRACK + i));
1004 		item->SetMarked(i == current);
1005 		fVideoTrackMenu->AddItem(item);
1006 	}
1007 	if (!count) {
1008 		fVideoTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY)));
1009 		fVideoTrackMenu->ItemAt(0)->SetMarked(true);
1010 	}
1011 }
1012 
1013 
1014 void
1015 MainWin::_SetWindowSizeLimits()
1016 {
1017 	int minWidth = fNoControls  ? MIN_WIDTH : fControlsWidth;
1018 	if (!fNoMenu)
1019 		minWidth = max_c(minWidth, fMenuBarWidth);
1020 	int minHeight = (fNoMenu ? 0 : fMenuBarHeight)
1021 		+ (fNoControls ? 0 : fControlsHeight);
1022 
1023 	SetSizeLimits(minWidth - 1, 32000, minHeight - 1, fHasVideo ?
1024 		32000 : minHeight - 1);
1025 }
1026 
1027 
1028 void
1029 MainWin::_ResizeWindow(int percent)
1030 {
1031 	// Get required window size
1032 	int videoWidth = lround(fSourceWidth * fWidthScale);
1033 	int videoHeight = lround(fSourceHeight * fHeightScale);
1034 
1035 	videoWidth = (videoWidth * percent) / 100;
1036 	videoHeight = (videoHeight * percent) / 100;
1037 
1038 	// Calculate and set the initial window size
1039 	int width = max_c(fControlsWidth, videoWidth);
1040 	int height = (fNoControls ? 0 : fControlsHeight) + videoHeight;
1041 	if (!fNoMenu) {
1042 		width = max_c(width, fMenuBarWidth);
1043 		height += fMenuBarHeight;
1044 	}
1045 	_SetWindowSizeLimits();
1046 	ResizeTo(width - 1, height - 1);
1047 }
1048 
1049 
1050 void
1051 MainWin::_ResizeVideoView(int x, int y, int width, int height)
1052 {
1053 	printf("_ResizeVideoView: %d,%d, width %d, height %d\n", x, y,
1054 		width, height);
1055 
1056 	if (fKeepAspectRatio) {
1057 		// Keep aspect ratio, place video view inside
1058 		// the background area (may create black bars).
1059 		float scaledWidth  = fSourceWidth * fWidthScale;
1060 		float scaledHeight = fSourceHeight * fHeightScale;
1061 		float factor = min_c(width / scaledWidth, height / scaledHeight);
1062 		int renderWidth = lround(scaledWidth * factor);
1063 		int renderHeight = lround(scaledHeight * factor);
1064 		if (renderWidth > width)
1065 			renderWidth = width;
1066 		if (renderHeight > height)
1067 			renderHeight = height;
1068 
1069 		int xOffset = x + (width - renderWidth) / 2;
1070 		int yOffset = y + (height - renderHeight) / 2;
1071 
1072 		fVideoView->MoveTo(xOffset, yOffset);
1073 		fVideoView->ResizeTo(renderWidth - 1, renderHeight - 1);
1074 
1075 	} else {
1076 		fVideoView->MoveTo(x, y);
1077 		fVideoView->ResizeTo(width - 1, height - 1);
1078 	}
1079 }
1080 
1081 
1082 // #pragma mark -
1083 
1084 
1085 void
1086 MainWin::_MouseDown(BMessage *msg, BView* originalHandler)
1087 {
1088 	BPoint screen_where;
1089 	uint32 buttons = msg->FindInt32("buttons");
1090 
1091 	// On Zeta, only "screen_where" is relyable, "where" and "be:view_where"
1092 	// seem to be broken
1093 	if (B_OK != msg->FindPoint("screen_where", &screen_where)) {
1094 		// Workaround for BeOS R5, it has no "screen_where"
1095 		if (!originalHandler || msg->FindPoint("where", &screen_where) < B_OK)
1096 			return;
1097 		originalHandler->ConvertToScreen(&screen_where);
1098 	}
1099 
1100 //	msg->PrintToStream();
1101 
1102 //	if (1 == msg->FindInt32("buttons") && msg->FindInt32("clicks") == 1) {
1103 
1104 	if (1 == buttons && msg->FindInt32("clicks") % 2 == 0) {
1105 		BRect r(screen_where.x - 1, screen_where.y - 1, screen_where.x + 1,
1106 			screen_where.y + 1);
1107 		if (r.Contains(fMouseDownMousePos)) {
1108 			PostMessage(M_TOGGLE_FULLSCREEN);
1109 			return;
1110 		}
1111 	}
1112 
1113 	if (2 == buttons && msg->FindInt32("clicks") % 2 == 0) {
1114 		BRect r(screen_where.x - 1, screen_where.y - 1, screen_where.x + 1,
1115 			screen_where.y + 1);
1116 		if (r.Contains(fMouseDownMousePos)) {
1117 			PostMessage(M_TOGGLE_NO_BORDER_NO_MENU_NO_CONTROLS);
1118 			return;
1119 		}
1120 	}
1121 
1122 /*
1123 		// very broken in Zeta:
1124 		fMouseDownMousePos = fVideoView->ConvertToScreen(
1125 			msg->FindPoint("where"));
1126 */
1127 	fMouseDownMousePos = screen_where;
1128 	fMouseDownWindowPos = Frame().LeftTop();
1129 
1130 	if (buttons == 1 && !fIsFullscreen) {
1131 		// start mouse tracking
1132 		fVideoView->SetMouseEventMask(B_POINTER_EVENTS | B_NO_POINTER_HISTORY
1133 			/* | B_LOCK_WINDOW_FOCUS */);
1134 		fMouseDownTracking = true;
1135 	}
1136 
1137 	// pop up a context menu if right mouse button is down for 200 ms
1138 
1139 	if ((buttons & 2) == 0)
1140 		return;
1141 	bigtime_t start = system_time();
1142 	bigtime_t delay = 200000;
1143 	BPoint location;
1144 	do {
1145 		fVideoView->GetMouse(&location, &buttons);
1146 		if ((buttons & 2) == 0)
1147 			break;
1148 		snooze(1000);
1149 	} while (system_time() - start < delay);
1150 
1151 	if (buttons & 2)
1152 		_ShowContextMenu(screen_where);
1153 }
1154 
1155 
1156 void
1157 MainWin::_MouseMoved(BMessage *msg, BView* originalHandler)
1158 {
1159 //	msg->PrintToStream();
1160 
1161 	BPoint mousePos;
1162 	uint32 buttons = msg->FindInt32("buttons");
1163 
1164 	if (1 == buttons && fMouseDownTracking && !fIsFullscreen) {
1165 /*
1166 		// very broken in Zeta:
1167 		BPoint mousePos = msg->FindPoint("where");
1168 		printf("view where: %.0f, %.0f => ", mousePos.x, mousePos.y);
1169 		fVideoView->ConvertToScreen(&mousePos);
1170 */
1171 		// On Zeta, only "screen_where" is relyable, "where"
1172 		// and "be:view_where" seem to be broken
1173 		if (B_OK != msg->FindPoint("screen_where", &mousePos)) {
1174 			// Workaround for BeOS R5, it has no "screen_where"
1175 			if (!originalHandler || msg->FindPoint("where", &mousePos) < B_OK)
1176 				return;
1177 			originalHandler->ConvertToScreen(&mousePos);
1178 		}
1179 //		printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y);
1180 		float delta_x = mousePos.x - fMouseDownMousePos.x;
1181 		float delta_y = mousePos.y - fMouseDownMousePos.y;
1182 		float x = fMouseDownWindowPos.x + delta_x;
1183 		float y = fMouseDownWindowPos.y + delta_y;
1184 //		printf("move window to %.0f, %.0f\n", x, y);
1185 		MoveTo(x, y);
1186 	}
1187 }
1188 
1189 
1190 void
1191 MainWin::_MouseUp(BMessage *msg)
1192 {
1193 //	msg->PrintToStream();
1194 	fMouseDownTracking = false;
1195 }
1196 
1197 
1198 void
1199 MainWin::_ShowContextMenu(const BPoint &screen_point)
1200 {
1201 	printf("Show context menu\n");
1202 	BPopUpMenu *menu = new BPopUpMenu("context menu", false, false);
1203 	BMenuItem *item;
1204 	menu->AddItem(item = new BMenuItem("Full Screen",
1205 		new BMessage(M_TOGGLE_FULLSCREEN), 'F'));
1206 	item->SetMarked(fIsFullscreen);
1207 	item->SetEnabled(fHasVideo);
1208 	menu->AddItem(item = new BMenuItem("Keep Aspect Ratio",
1209 		new BMessage(M_TOGGLE_KEEP_ASPECT_RATIO), 'K'));
1210 	item->SetMarked(fKeepAspectRatio);
1211 	item->SetEnabled(fHasVideo);
1212 
1213 	menu->AddSeparatorItem();
1214 	menu->AddItem(item = new BMenuItem("No Menu",
1215 		new BMessage(M_TOGGLE_NO_MENU), 'M'));
1216 	item->SetMarked(fNoMenu);
1217 	menu->AddItem(item = new BMenuItem("No Border",
1218 		new BMessage(M_TOGGLE_NO_BORDER), 'B'));
1219 	item->SetMarked(fNoBorder);
1220 	menu->AddItem(item = new BMenuItem("Always on Top",
1221 		new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T'));
1222 	item->SetMarked(fAlwaysOnTop);
1223 
1224 	menu->AddSeparatorItem();
1225 	menu->AddItem(new BMenuItem("About " NAME B_UTF8_ELLIPSIS,
1226 		new BMessage(B_ABOUT_REQUESTED)));
1227 	menu->AddSeparatorItem();
1228 	menu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q'));
1229 
1230 	menu->AddSeparatorItem();
1231 	menu->AddItem(new BMenuItem("pixel aspect ratio 1.00000:1",
1232 		new BMessage(M_ASPECT_100000_1)));
1233 	menu->AddItem(new BMenuItem("pixel aspect ratio 1.06666:1",
1234 		new BMessage(M_ASPECT_106666_1)));
1235 	menu->AddItem(new BMenuItem("pixel aspect ratio 1.09091:1",
1236 		new BMessage(M_ASPECT_109091_1)));
1237 	menu->AddItem(new BMenuItem("pixel aspect ratio 1.41176:1",
1238 		new BMessage(M_ASPECT_141176_1)));
1239 	menu->AddItem(new BMenuItem("force 720 x 576, display aspect 4:3",
1240 		new BMessage(M_ASPECT_720_576)));
1241 	menu->AddItem(new BMenuItem("force 704 x 576, display aspect 4:3",
1242 		new BMessage(M_ASPECT_704_576)));
1243 	menu->AddItem(new BMenuItem("force 544 x 576, display aspect 4:3",
1244 		new BMessage(M_ASPECT_544_576)));
1245 
1246 	menu->SetTargetForItems(this);
1247 	BRect r(screen_point.x - 5, screen_point.y - 5, screen_point.x + 5,
1248 		screen_point.y + 5);
1249 	menu->Go(screen_point, true, true, r, true);
1250 }
1251 
1252 
1253 /* Trap keys that are about to be send to background or renderer view.
1254  * Return B_OK if it shouldn't be passed to the view
1255  */
1256 status_t
1257 MainWin::_KeyDown(BMessage *msg)
1258 {
1259 //	msg->PrintToStream();
1260 
1261 	uint32 key		 = msg->FindInt32("key");
1262 	uint32 raw_char  = msg->FindInt32("raw_char");
1263 	uint32 modifiers = msg->FindInt32("modifiers");
1264 
1265 	printf("key 0x%lx, raw_char 0x%lx, modifiers 0x%lx\n", key, raw_char,
1266 		modifiers);
1267 
1268 	switch (raw_char) {
1269 		case B_SPACE:
1270 			fController->TogglePlaying();
1271 			return B_OK;
1272 
1273 		case B_ESCAPE:
1274 			if (fIsFullscreen) {
1275 				PostMessage(M_TOGGLE_FULLSCREEN);
1276 				return B_OK;
1277 			} else
1278 				break;
1279 
1280 		case B_ENTER:		// Enter / Return
1281 			if (modifiers & B_COMMAND_KEY) {
1282 				PostMessage(M_TOGGLE_FULLSCREEN);
1283 				return B_OK;
1284 			} else
1285 				break;
1286 
1287 		case B_TAB:
1288 			if ((modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY
1289 					| B_MENU_KEY)) == 0) {
1290 				PostMessage(M_TOGGLE_FULLSCREEN);
1291 				return B_OK;
1292 			} else
1293 				break;
1294 
1295 		case B_UP_ARROW:
1296 			if (modifiers & B_COMMAND_KEY) {
1297 				PostMessage(M_SKIP_NEXT);
1298 			} else {
1299 				PostMessage(M_VOLUME_UP);
1300 			}
1301 			return B_OK;
1302 
1303 		case B_DOWN_ARROW:
1304 			if (modifiers & B_COMMAND_KEY) {
1305 				PostMessage(M_SKIP_PREV);
1306 			} else {
1307 				PostMessage(M_VOLUME_DOWN);
1308 			}
1309 			return B_OK;
1310 
1311 		case B_RIGHT_ARROW:
1312 			if (modifiers & B_COMMAND_KEY) {
1313 				PostMessage(M_VOLUME_UP);
1314 			} else {
1315 				PostMessage(M_SKIP_NEXT);
1316 			}
1317 			return B_OK;
1318 
1319 		case B_LEFT_ARROW:
1320 			if (modifiers & B_COMMAND_KEY) {
1321 				PostMessage(M_VOLUME_DOWN);
1322 			} else {
1323 				PostMessage(M_SKIP_PREV);
1324 			}
1325 			return B_OK;
1326 
1327 		case B_PAGE_UP:
1328 			PostMessage(M_SKIP_NEXT);
1329 			return B_OK;
1330 
1331 		case B_PAGE_DOWN:
1332 			PostMessage(M_SKIP_PREV);
1333 			return B_OK;
1334 	}
1335 
1336 	switch (key) {
1337 		case 0x3a:  		// numeric keypad +
1338 			if ((modifiers & B_COMMAND_KEY) == 0) {
1339 				printf("if\n");
1340 				PostMessage(M_VOLUME_UP);
1341 				return B_OK;
1342 			} else {
1343 				printf("else\n");
1344 				break;
1345 			}
1346 
1347 		case 0x25:  		// numeric keypad -
1348 			if ((modifiers & B_COMMAND_KEY) == 0) {
1349 				PostMessage(M_VOLUME_DOWN);
1350 				return B_OK;
1351 			} else {
1352 				break;
1353 			}
1354 
1355 		case 0x38:			// numeric keypad up arrow
1356 			PostMessage(M_VOLUME_UP);
1357 			return B_OK;
1358 
1359 		case 0x59:			// numeric keypad down arrow
1360 			PostMessage(M_VOLUME_DOWN);
1361 			return B_OK;
1362 
1363 		case 0x39:			// numeric keypad page up
1364 		case 0x4a:			// numeric keypad right arrow
1365 			PostMessage(M_SKIP_NEXT);
1366 			return B_OK;
1367 
1368 		case 0x5a:			// numeric keypad page down
1369 		case 0x48:			// numeric keypad left arrow
1370 			PostMessage(M_SKIP_PREV);
1371 			return B_OK;
1372 	}
1373 
1374 	return B_ERROR;
1375 }
1376 
1377 
1378 // #pragma mark -
1379 
1380 
1381 void
1382 MainWin::_ToggleNoBorderNoMenu()
1383 {
1384 	if (!fNoMenu && !fNoBorder && !fNoControls) {
1385 		PostMessage(M_TOGGLE_NO_MENU);
1386 		PostMessage(M_TOGGLE_NO_BORDER);
1387 		PostMessage(M_TOGGLE_NO_CONTROLS);
1388 	} else {
1389 		if (!fNoMenu)
1390 			PostMessage(M_TOGGLE_NO_MENU);
1391 		if (!fNoBorder)
1392 			PostMessage(M_TOGGLE_NO_BORDER);
1393 		if (!fNoControls)
1394 			PostMessage(M_TOGGLE_NO_CONTROLS);
1395 	}
1396 }
1397 
1398 
1399 void
1400 MainWin::_ToggleFullscreen()
1401 {
1402 	printf("_ToggleFullscreen enter\n");
1403 
1404 	if (!fHasVideo) {
1405 		printf("_ToggleFullscreen - ignoring, as we don't have a video\n");
1406 		return;
1407 	}
1408 
1409 	fIsFullscreen = !fIsFullscreen;
1410 
1411 	if (fIsFullscreen) {
1412 		// switch to fullscreen
1413 
1414 		fSavedFrame = Frame();
1415 		printf("saving current frame: %d %d %d %d\n", int(fSavedFrame.left),
1416 			int(fSavedFrame.top), int(fSavedFrame.right),
1417 			int(fSavedFrame.bottom));
1418 		BScreen screen(this);
1419 		BRect rect(screen.Frame());
1420 
1421 		Hide();
1422 		MoveTo(rect.left, rect.top);
1423 		ResizeTo(rect.Width(), rect.Height());
1424 		Show();
1425 
1426 	} else {
1427 		// switch back from full screen mode
1428 
1429 		Hide();
1430 		MoveTo(fSavedFrame.left, fSavedFrame.top);
1431 		ResizeTo(fSavedFrame.Width(), fSavedFrame.Height());
1432 		Show();
1433 	}
1434 
1435 	_MarkSettingsItem(M_TOGGLE_FULLSCREEN, fIsFullscreen);
1436 
1437 	printf("_ToggleFullscreen leave\n");
1438 }
1439 
1440 void
1441 MainWin::_ToggleNoControls()
1442 {
1443 	printf("_ToggleNoControls enter\n");
1444 
1445 	if (fIsFullscreen) {
1446 		// fullscreen is always without menu
1447 		printf("_ToggleNoControls leave, doing nothing, we are fullscreen\n");
1448 		return;
1449 	}
1450 
1451 	fNoControls = !fNoControls;
1452 	_SetWindowSizeLimits();
1453 
1454 	if (fNoControls) {
1455 		ResizeBy(0, - fControlsHeight);
1456 	} else {
1457 		ResizeBy(0, fControlsHeight);
1458 	}
1459 
1460 	_MarkSettingsItem(M_TOGGLE_NO_CONTROLS, fNoControls);
1461 
1462 	printf("_ToggleNoControls leave\n");
1463 }
1464 
1465 void
1466 MainWin::_ToggleNoMenu()
1467 {
1468 	printf("_ToggleNoMenu enter\n");
1469 
1470 	if (fIsFullscreen) {
1471 		// fullscreen is always without menu
1472 		printf("_ToggleNoMenu leave, doing nothing, we are fullscreen\n");
1473 		return;
1474 	}
1475 
1476 	fNoMenu = !fNoMenu;
1477 	_SetWindowSizeLimits();
1478 
1479 	if (fNoMenu) {
1480 		MoveBy(0, fMenuBarHeight);
1481 		ResizeBy(0, - fMenuBarHeight);
1482 	} else {
1483 		MoveBy(0, - fMenuBarHeight);
1484 		ResizeBy(0, fMenuBarHeight);
1485 	}
1486 
1487 	_MarkSettingsItem(M_TOGGLE_NO_MENU, fNoMenu);
1488 
1489 	printf("_ToggleNoMenu leave\n");
1490 }
1491 
1492 
1493 void
1494 MainWin::_ToggleNoBorder()
1495 {
1496 	fNoBorder = !fNoBorder;
1497 	SetLook(fNoBorder ? B_BORDERED_WINDOW_LOOK : B_TITLED_WINDOW_LOOK);
1498 
1499 	_MarkSettingsItem(M_TOGGLE_NO_BORDER, fNoBorder);
1500 }
1501 
1502 
1503 void
1504 MainWin::_ToggleAlwaysOnTop()
1505 {
1506 	fAlwaysOnTop = !fAlwaysOnTop;
1507 	SetFeel(fAlwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL);
1508 
1509 	_MarkSettingsItem(M_TOGGLE_ALWAYS_ON_TOP, fAlwaysOnTop);
1510 }
1511 
1512 
1513 void
1514 MainWin::_ToggleKeepAspectRatio()
1515 {
1516 	fKeepAspectRatio = !fKeepAspectRatio;
1517 	FrameResized(Bounds().Width(), Bounds().Height());
1518 
1519 	_MarkSettingsItem(M_TOGGLE_KEEP_ASPECT_RATIO, fKeepAspectRatio);
1520 }
1521 
1522 
1523 // #pragma mark -
1524 
1525 
1526 void
1527 MainWin::_UpdateControlsEnabledStatus()
1528 {
1529 	uint32 enabledButtons = 0;
1530 	if (fHasVideo || fHasAudio) {
1531 		enabledButtons |= PLAYBACK_ENABLED | SEEK_ENABLED
1532 			| SEEK_BACK_ENABLED | SEEK_FORWARD_ENABLED;
1533 	}
1534 	if (fHasAudio)
1535 		enabledButtons |= VOLUME_ENABLED;
1536 
1537 	bool canSkipPrevious, canSkipNext;
1538 	fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext);
1539 	if (canSkipPrevious)
1540 		enabledButtons |= SKIP_BACK_ENABLED;
1541 	if (canSkipNext)
1542 		enabledButtons |= SKIP_FORWARD_ENABLED;
1543 
1544 	fControls->SetEnabled(enabledButtons);
1545 }
1546 
1547 
1548 void
1549 MainWin::_UpdatePlaylistMenu()
1550 {
1551 	if (!fPlaylist->Lock())
1552 		return;
1553 
1554 	fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true);
1555 
1556 	int32 count = fPlaylist->CountItems();
1557 	for (int32 i = 0; i < count; i++) {
1558 		entry_ref ref;
1559 		if (fPlaylist->GetRefAt(i, &ref) < B_OK)
1560 			continue;
1561 		_AddPlaylistItem(ref, i);
1562 	}
1563 	fPlaylistMenu->SetTargetForItems(this);
1564 
1565 	_MarkPlaylistItem(fPlaylist->CurrentRefIndex());
1566 
1567 	fPlaylist->Unlock();
1568 }
1569 
1570 
1571 void
1572 MainWin::_AddPlaylistItem(const entry_ref& ref, int32 index)
1573 {
1574 	BMessage* message = new BMessage(M_SET_PLAYLIST_POSITION);
1575 	message->AddInt32("index", index);
1576 	BMenuItem* item = new BMenuItem(ref.name, message);
1577 	fPlaylistMenu->AddItem(item, index);
1578 }
1579 
1580 
1581 void
1582 MainWin::_RemovePlaylistItem(int32 index)
1583 {
1584 	delete fPlaylistMenu->RemoveItem(index);
1585 }
1586 
1587 
1588 void
1589 MainWin::_MarkPlaylistItem(int32 index)
1590 {
1591 	if (BMenuItem* item = fPlaylistMenu->ItemAt(index)) {
1592 		item->SetMarked(true);
1593 		// ... and in case the menu is currently on screen:
1594 		if (fPlaylistMenu->LockLooper()) {
1595 			fPlaylistMenu->Invalidate();
1596 			fPlaylistMenu->UnlockLooper();
1597 		}
1598 	}
1599 }
1600 
1601 
1602 void
1603 MainWin::_MarkSettingsItem(uint32 command, bool mark)
1604 {
1605 	if (BMenuItem* item = fSettingsMenu->FindItem(command))
1606 		item->SetMarked(mark);
1607 }
1608 
1609 
1610 void
1611 MainWin::_AdoptGlobalSettings()
1612 {
1613 	mpSettings settings = Settings::CurrentSettings();
1614 		// thread safe
1615 
1616 	fCloseWhenDonePlayingMovie = settings.closeWhenDonePlayingMovie;
1617 	fCloseWhenDonePlayingSound = settings.closeWhenDonePlayingSound;
1618 }
1619 
1620 
1621