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