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