1 /* 2 * MainWin.cpp - Media Player for the Haiku Operating System 3 * 4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de> 5 * Copyright (C) 2007-2009 Stephan Aßmus <superstippi@gmx.de> (GPL->MIT ok) 6 * Copyright (C) 2007-2009 Fredrik Modéen <[FirstName]@[LastName].se> (MIT ok) 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * version 2 as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 20 * USA. 21 */ 22 23 24 #include "MainWin.h" 25 26 #include <math.h> 27 #include <stdio.h> 28 #include <string.h> 29 30 #include <Alert.h> 31 #include <Application.h> 32 #include <Autolock.h> 33 #include <Debug.h> 34 #include <Menu.h> 35 #include <MenuBar.h> 36 #include <MenuItem.h> 37 #include <Messenger.h> 38 #include <PopUpMenu.h> 39 #include <RecentItems.h> 40 #include <Roster.h> 41 #include <Screen.h> 42 #include <String.h> 43 #include <View.h> 44 45 #include "AudioProducer.h" 46 #include "ControllerObserver.h" 47 #include "MainApp.h" 48 #include "PeakView.h" 49 #include "PlaylistItem.h" 50 #include "PlaylistObserver.h" 51 #include "PlaylistWindow.h" 52 #include "Settings.h" 53 54 #define NAME "MediaPlayer" 55 #define MIN_WIDTH 250 56 57 58 // XXX TODO: why is lround not defined? 59 #define lround(a) ((int)(0.99999 + (a))) 60 61 enum { 62 M_DUMMY = 0x100, 63 M_FILE_OPEN = 0x1000, 64 M_FILE_NEWPLAYER, 65 M_FILE_INFO, 66 M_FILE_PLAYLIST, 67 M_FILE_CLOSE, 68 M_FILE_QUIT, 69 M_VIEW_SIZE, 70 M_TOGGLE_FULLSCREEN, 71 M_TOGGLE_ALWAYS_ON_TOP, 72 M_TOGGLE_NO_INTERFACE, 73 M_VOLUME_UP, 74 M_VOLUME_DOWN, 75 M_SKIP_NEXT, 76 M_SKIP_PREV, 77 78 // The common display aspect ratios 79 M_ASPECT_SAME_AS_SOURCE, 80 M_ASPECT_NO_DISTORTION, 81 M_ASPECT_4_3, 82 M_ASPECT_16_9, 83 M_ASPECT_83_50, 84 M_ASPECT_7_4, 85 M_ASPECT_37_20, 86 M_ASPECT_47_20, 87 88 M_SELECT_AUDIO_TRACK = 0x00000800, 89 M_SELECT_AUDIO_TRACK_END = 0x00000fff, 90 M_SELECT_VIDEO_TRACK = 0x00010000, 91 M_SELECT_VIDEO_TRACK_END = 0x000fffff, 92 93 M_SET_PLAYLIST_POSITION, 94 95 M_FILE_DELETE 96 }; 97 98 //#define printf(a...) 99 100 101 MainWin::MainWin() 102 : 103 BWindow(BRect(100,100,400,300), NAME, B_TITLED_WINDOW, 104 B_ASYNCHRONOUS_CONTROLS /* | B_WILL_ACCEPT_FIRST_CLICK */), 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 fAlwaysOnTop(false), 119 fNoInterface(false), 120 fSourceWidth(-1), 121 fSourceHeight(-1), 122 fWidthAspect(0), 123 fHeightAspect(0), 124 fMouseDownTracking(false), 125 fGlobalSettingsListener(this) 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->SetResizingMode(B_FOLLOW_NONE); 144 fMenuBar->ResizeToPreferred(); 145 fMenuBarWidth = (int)fMenuBar->Frame().Width() + 1; 146 fMenuBarHeight = (int)fMenuBar->Frame().Height() + 1; 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 fBackground->AddChild(fVideoView); 153 154 // controls 155 rect = BRect(0, fMenuBarHeight + 11, fBackground->Bounds().right, 156 fBackground->Bounds().bottom); 157 fControls = new ControllerView(rect, fController, fPlaylist); 158 fBackground->AddChild(fControls); 159 fControls->ResizeToPreferred(); 160 fControlsHeight = (int)fControls->Frame().Height() + 1; 161 fControlsWidth = (int)fControls->Frame().Width() + 1; 162 fControls->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT); 163 // fControls->MoveTo(0, fBackground->Bounds().bottom - fControlsHeight + 1); 164 165 // fVideoView->ResizeTo(fBackground->Bounds().Width(), 166 // fBackground->Bounds().Height() - fMenuBarHeight - fControlsHeight); 167 168 fPlaylist->AddListener(fPlaylistObserver); 169 fController->SetVideoView(fVideoView); 170 fController->AddListener(fControllerObserver); 171 PeakView* peakView = fControls->GetPeakView(); 172 peakView->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION); 173 fController->SetPeakListener(peakView); 174 175 // printf("fMenuBarHeight %d\n", fMenuBarHeight); 176 // printf("fControlsHeight %d\n", fControlsHeight); 177 // printf("fControlsWidth %d\n", fControlsWidth); 178 179 _SetupWindow(); 180 181 // setup the playlist window now, we need to have it 182 // running for the undo/redo playlist editing 183 fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 500, 600), fPlaylist, 184 fController); 185 fPlaylistWindow->Hide(); 186 fPlaylistWindow->Show(); 187 // this makes sure the window thread is running without 188 // showing the window just yet 189 190 Settings::Default()->AddListener(&fGlobalSettingsListener); 191 _AdoptGlobalSettings(); 192 193 AddShortcut('z', B_COMMAND_KEY, new BMessage(B_UNDO)); 194 AddShortcut('y', B_COMMAND_KEY, new BMessage(B_UNDO)); 195 AddShortcut('z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO)); 196 AddShortcut('y', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO)); 197 198 Show(); 199 } 200 201 202 MainWin::~MainWin() 203 { 204 // printf("MainWin::~MainWin\n"); 205 206 Settings::Default()->RemoveListener(&fGlobalSettingsListener); 207 fPlaylist->RemoveListener(fPlaylistObserver); 208 fController->RemoveListener(fControllerObserver); 209 fController->SetPeakListener(NULL); 210 211 // give the views a chance to detach from any notifiers 212 // before we delete them 213 fBackground->RemoveSelf(); 214 delete fBackground; 215 216 if (fInfoWin && fInfoWin->Lock()) 217 fInfoWin->Quit(); 218 219 if (fPlaylistWindow && fPlaylistWindow->Lock()) 220 fPlaylistWindow->Quit(); 221 222 delete fPlaylist; 223 224 // quit the Controller looper thread 225 thread_id controllerThread = fController->Thread(); 226 fController->PostMessage(B_QUIT_REQUESTED); 227 status_t exitValue; 228 wait_for_thread(controllerThread, &exitValue); 229 } 230 231 232 // #pragma mark - 233 234 235 void 236 MainWin::FrameResized(float newWidth, float newHeight) 237 { 238 if (newWidth != Bounds().Width() || newHeight != Bounds().Height()) { 239 debugger("size wrong\n"); 240 } 241 242 bool noMenu = fNoInterface || fIsFullscreen; 243 bool noControls = fNoInterface || fIsFullscreen; 244 245 // printf("FrameResized enter: newWidth %.0f, newHeight %.0f\n", 246 // newWidth, newHeight); 247 248 int maxVideoWidth = int(newWidth) + 1; 249 int maxVideoHeight = int(newHeight) + 1 250 - (noMenu ? 0 : fMenuBarHeight) 251 - (noControls ? 0 : fControlsHeight); 252 253 ASSERT(maxVideoHeight >= 0); 254 255 int y = 0; 256 257 if (noMenu) { 258 if (!fMenuBar->IsHidden()) 259 fMenuBar->Hide(); 260 } else { 261 fMenuBar->MoveTo(0, y); 262 fMenuBar->ResizeTo(newWidth, fMenuBarHeight - 1); 263 if (fMenuBar->IsHidden()) 264 fMenuBar->Show(); 265 y += fMenuBarHeight; 266 } 267 268 if (maxVideoHeight == 0) { 269 if (!fVideoView->IsHidden()) 270 fVideoView->Hide(); 271 } else { 272 _ResizeVideoView(0, y, maxVideoWidth, maxVideoHeight); 273 if (fVideoView->IsHidden()) 274 fVideoView->Show(); 275 y += maxVideoHeight; 276 } 277 278 if (noControls) { 279 if (!fControls->IsHidden()) 280 fControls->Hide(); 281 } else { 282 fControls->MoveTo(0, y); 283 fControls->ResizeTo(newWidth, 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)) { 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 // add to recent documents as it's not done with drag-n-drop 352 entry_ref ref; 353 for (int32 i = 0; msg->FindRef("refs", i, &ref) == B_OK; i++) { 354 be_roster->AddToRecentDocuments(&ref, kAppSig); 355 } 356 _RefsReceived(msg); 357 } 358 break; 359 360 case B_UNDO: 361 case B_REDO: 362 fPlaylistWindow->PostMessage(msg); 363 break; 364 365 case M_MEDIA_SERVER_STARTED: 366 printf("TODO: implement M_MEDIA_SERVER_STARTED\n"); 367 // fController->... 368 break; 369 370 case M_MEDIA_SERVER_QUIT: 371 printf("TODO: implement M_MEDIA_SERVER_QUIT\n"); 372 // fController->... 373 break; 374 375 // PlaylistObserver messages 376 case MSG_PLAYLIST_ITEM_ADDED: 377 { 378 PlaylistItem* item; 379 int32 index; 380 if (msg->FindPointer("item", (void**)&item) == B_OK 381 && msg->FindInt32("index", &index) == B_OK) { 382 _AddPlaylistItem(item, index); 383 } 384 break; 385 } 386 case MSG_PLAYLIST_ITEM_REMOVED: 387 { 388 int32 index; 389 if (msg->FindInt32("index", &index) == B_OK) 390 _RemovePlaylistItem(index); 391 break; 392 } 393 case MSG_PLAYLIST_CURRENT_ITEM_CHANGED: 394 { 395 BAutolock _(fPlaylist); 396 397 int32 index; 398 if (msg->FindInt32("index", &index) < B_OK 399 || index != fPlaylist->CurrentItemIndex()) 400 break; 401 PlaylistItemRef item(fPlaylist->ItemAt(index)); 402 if (item.Get() != NULL) { 403 printf("open playlist item: %s\n", item->Name().String()); 404 OpenPlaylistItem(item); 405 _MarkPlaylistItem(index); 406 } 407 break; 408 } 409 410 // ControllerObserver messages 411 case MSG_CONTROLLER_FILE_FINISHED: 412 { 413 BAutolock _(fPlaylist); 414 415 bool hadNext = fPlaylist->SetCurrentItemIndex( 416 fPlaylist->CurrentItemIndex() + 1); 417 if (!hadNext) { 418 if (fHasVideo) { 419 if (fCloseWhenDonePlayingMovie) 420 PostMessage(B_QUIT_REQUESTED); 421 } else { 422 if (fCloseWhenDonePlayingSound) 423 PostMessage(B_QUIT_REQUESTED); 424 } 425 } 426 break; 427 } 428 case MSG_CONTROLLER_FILE_CHANGED: 429 // TODO: move all other GUI changes as a reaction to this 430 // notification 431 // _UpdatePlaylistMenu(); 432 break; 433 case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: 434 { 435 int32 index; 436 if (msg->FindInt32("index", &index) == B_OK) { 437 int32 i = 0; 438 while (BMenuItem* item = fVideoTrackMenu->ItemAt(i)) { 439 item->SetMarked(i == index); 440 i++; 441 } 442 } 443 break; 444 } 445 case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: 446 { 447 int32 index; 448 if (msg->FindInt32("index", &index) == B_OK) { 449 int32 i = 0; 450 while (BMenuItem* item = fAudioTrackMenu->ItemAt(i)) { 451 item->SetMarked(i == index); 452 i++; 453 } 454 } 455 break; 456 } 457 case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: 458 { 459 uint32 state; 460 if (msg->FindInt32("state", (int32*)&state) == B_OK) 461 fControls->SetPlaybackState(state); 462 break; 463 } 464 case MSG_CONTROLLER_POSITION_CHANGED: 465 { 466 float position; 467 if (msg->FindFloat("position", &position) == B_OK) 468 fControls->SetPosition(position); 469 break; 470 } 471 case MSG_CONTROLLER_VOLUME_CHANGED: 472 { 473 float volume; 474 if (msg->FindFloat("volume", &volume) == B_OK) 475 fControls->SetVolume(volume); 476 break; 477 } 478 case MSG_CONTROLLER_MUTED_CHANGED: 479 { 480 bool muted; 481 if (msg->FindBool("muted", &muted) == B_OK) 482 fControls->SetMuted(muted); 483 break; 484 } 485 486 // menu item messages 487 case M_FILE_NEWPLAYER: 488 gMainApp->NewWindow(); 489 break; 490 case M_FILE_OPEN: 491 { 492 BMessenger target(this); 493 BMessage result(B_REFS_RECEIVED); 494 BMessage appMessage(M_SHOW_OPEN_PANEL); 495 appMessage.AddMessenger("target", target); 496 appMessage.AddMessage("message", &result); 497 appMessage.AddString("title", "Open Clips"); 498 appMessage.AddString("label", "Open"); 499 be_app->PostMessage(&appMessage); 500 break; 501 } 502 case M_FILE_INFO: 503 ShowFileInfo(); 504 break; 505 case M_FILE_PLAYLIST: 506 ShowPlaylistWindow(); 507 break; 508 case B_ABOUT_REQUESTED: 509 { 510 BAlert *alert; 511 alert = new BAlert("about", NAME"\n\n Written by Marcus Overhagen " 512 ", Stephan Aßmus and Frederik Modéen", "Thanks"); 513 if (fAlwaysOnTop) { 514 _ToggleAlwaysOnTop(); 515 alert->Go(NULL); // Asynchronous mode 516 _ToggleAlwaysOnTop(); 517 } else { 518 alert->Go(NULL); // Asynchronous mode 519 } 520 break; 521 } 522 case M_FILE_CLOSE: 523 PostMessage(B_QUIT_REQUESTED); 524 break; 525 case M_FILE_QUIT: 526 be_app->PostMessage(B_QUIT_REQUESTED); 527 break; 528 529 case M_TOGGLE_FULLSCREEN: 530 _ToggleFullscreen(); 531 break; 532 533 case M_TOGGLE_ALWAYS_ON_TOP: 534 _ToggleAlwaysOnTop(); 535 break; 536 537 case M_TOGGLE_NO_INTERFACE: 538 _ToggleNoInterface(); 539 break; 540 541 case M_VIEW_SIZE: 542 { 543 int32 size; 544 if (msg->FindInt32("size", &size) == B_OK) { 545 if (!fHasVideo) 546 break; 547 if (fIsFullscreen) 548 _ToggleFullscreen(); 549 _ResizeWindow(size); 550 } 551 break; 552 } 553 554 /* 555 case B_ACQUIRE_OVERLAY_LOCK: 556 printf("B_ACQUIRE_OVERLAY_LOCK\n"); 557 fVideoView->OverlayLockAcquire(); 558 break; 559 560 case B_RELEASE_OVERLAY_LOCK: 561 printf("B_RELEASE_OVERLAY_LOCK\n"); 562 fVideoView->OverlayLockRelease(); 563 break; 564 */ 565 case B_MOUSE_WHEEL_CHANGED: 566 { 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_SAME_AS_SOURCE: 594 if (fHasVideo) { 595 int width; 596 int height; 597 int widthAspect; 598 int heightAspect; 599 fController->GetSize(&width, &height, 600 &widthAspect, &heightAspect); 601 VideoFormatChange(width, height, widthAspect, heightAspect); 602 } 603 break; 604 605 case M_ASPECT_NO_DISTORTION: 606 if (fHasVideo) { 607 int width; 608 int height; 609 fController->GetSize(&width, &height); 610 VideoFormatChange(width, height, width, height); 611 } 612 break; 613 614 case M_ASPECT_4_3: 615 VideoAspectChange(4, 3); 616 break; 617 618 case M_ASPECT_16_9: // 1.77 : 1 619 VideoAspectChange(16, 9); 620 break; 621 622 case M_ASPECT_83_50: // 1.66 : 1 623 VideoAspectChange(83, 50); 624 break; 625 626 case M_ASPECT_7_4: // 1.75 : 1 627 VideoAspectChange(7, 4); 628 break; 629 630 case M_ASPECT_37_20: // 1.85 : 1 631 VideoAspectChange(37, 20); 632 break; 633 634 case M_ASPECT_47_20: // 2.35 : 1 635 VideoAspectChange(47, 20); 636 break; 637 638 case M_SET_PLAYLIST_POSITION: 639 { 640 BAutolock _(fPlaylist); 641 642 int32 index; 643 if (msg->FindInt32("index", &index) == B_OK) 644 fPlaylist->SetCurrentItemIndex(index); 645 break; 646 } 647 648 case MSG_OBJECT_CHANGED: 649 // received from fGlobalSettingsListener 650 // TODO: find out which object, if we ever watch more than 651 // the global settings instance... 652 _AdoptGlobalSettings(); 653 break; 654 655 default: 656 if (msg->what >= M_SELECT_AUDIO_TRACK 657 && msg->what <= M_SELECT_AUDIO_TRACK_END) { 658 fController->SelectAudioTrack(msg->what - M_SELECT_AUDIO_TRACK); 659 break; 660 } 661 if (msg->what >= M_SELECT_VIDEO_TRACK 662 && msg->what <= M_SELECT_VIDEO_TRACK_END) { 663 fController->SelectVideoTrack(msg->what - M_SELECT_VIDEO_TRACK); 664 break; 665 } 666 // let BWindow handle the rest 667 BWindow::MessageReceived(msg); 668 } 669 } 670 671 672 void 673 MainWin::WindowActivated(bool active) 674 { 675 fController->PlayerActivated(active); 676 } 677 678 679 bool 680 MainWin::QuitRequested() 681 { 682 be_app->PostMessage(M_PLAYER_QUIT); 683 return true; 684 } 685 686 687 void 688 MainWin::MenusBeginning() 689 { 690 _SetupVideoAspectItems(fVideoAspectMenu); 691 } 692 693 694 // #pragma mark - 695 696 697 void 698 MainWin::OpenPlaylistItem(const PlaylistItemRef& item) 699 { 700 printf("MainWin::OpenPlaylistItem\n"); 701 702 status_t err = fController->SetTo(item); 703 if (err != B_OK) { 704 BAutolock _(fPlaylist); 705 if (fPlaylist->CountItems() == 1) { 706 // display error if this is the only file we're supposed to play 707 BString message; 708 message << "The file '"; 709 message << item->Name(); 710 message << "' could not be opened.\n\n"; 711 712 if (err == B_MEDIA_NO_HANDLER) { 713 // give a more detailed message for the most likely of all 714 // errors 715 message << "There is no decoder installed to handle the " 716 "file format, or the decoder has trouble with the specific " 717 "version of the format."; 718 } else { 719 message << "Error: " << strerror(err); 720 } 721 (new BAlert("error", message.String(), "OK"))->Go(); 722 } else { 723 // just go to the next file and don't bother user 724 fPlaylist->SetCurrentItemIndex(fPlaylist->CurrentItemIndex() + 1); 725 } 726 fHasFile = false; 727 fHasVideo = false; 728 fHasAudio = false; 729 SetTitle(NAME); 730 } else { 731 fHasFile = true; 732 fHasVideo = fController->VideoTrackCount() != 0; 733 fHasAudio = fController->AudioTrackCount() != 0; 734 SetTitle(item->Name().String()); 735 } 736 _SetupWindow(); 737 } 738 739 740 void 741 MainWin::ShowFileInfo() 742 { 743 if (!fInfoWin) 744 fInfoWin = new InfoWin(Frame().LeftTop(), fController); 745 746 if (fInfoWin->Lock()) { 747 if (fInfoWin->IsHidden()) 748 fInfoWin->Show(); 749 else 750 fInfoWin->Activate(); 751 fInfoWin->Unlock(); 752 } 753 } 754 755 756 void 757 MainWin::ShowPlaylistWindow() 758 { 759 if (fPlaylistWindow->Lock()) { 760 // make sure the window shows on the same workspace as ourself 761 uint32 workspaces = Workspaces(); 762 if (fPlaylistWindow->Workspaces() != workspaces) 763 fPlaylistWindow->SetWorkspaces(workspaces); 764 765 // show or activate 766 if (fPlaylistWindow->IsHidden()) 767 fPlaylistWindow->Show(); 768 else 769 fPlaylistWindow->Activate(); 770 771 fPlaylistWindow->Unlock(); 772 } 773 } 774 775 776 void 777 MainWin::VideoAspectChange(int forcedWidth, int forcedHeight, float widthScale) 778 { 779 // Force specific source size and pixel width scale. 780 if (fHasVideo) { 781 int width; 782 int height; 783 fController->GetSize(&width, &height); 784 VideoFormatChange(forcedWidth, forcedHeight, 785 lround(width * widthScale), height); 786 } 787 } 788 789 790 void 791 MainWin::VideoAspectChange(float widthScale) 792 { 793 // Called when video aspect ratio changes and the original 794 // width/height should be restored too, display aspect is not known, 795 // only pixel width scale. 796 if (fHasVideo) { 797 int width; 798 int height; 799 fController->GetSize(&width, &height); 800 VideoFormatChange(width, height, lround(width * widthScale), height); 801 } 802 } 803 804 805 void 806 MainWin::VideoAspectChange(int widthAspect, int heightAspect) 807 { 808 // Called when video aspect ratio changes and the original 809 // width/height should be restored too. 810 if (fHasVideo) { 811 int width; 812 int height; 813 fController->GetSize(&width, &height); 814 VideoFormatChange(width, height, widthAspect, heightAspect); 815 } 816 } 817 818 819 void 820 MainWin::VideoFormatChange(int width, int height, int widthAspect, 821 int heightAspect) 822 { 823 // Called when video format or aspect ratio changes. 824 825 printf("VideoFormatChange enter: width %d, height %d, " 826 "aspect ratio: %d:%d\n", width, height, widthAspect, heightAspect); 827 828 // remember current view scale 829 int percent = _CurrentVideoSizeInPercent(); 830 831 fSourceWidth = width; 832 fSourceHeight = height; 833 fWidthAspect = widthAspect; 834 fHeightAspect = heightAspect; 835 836 if (percent == 100) 837 _ResizeWindow(100); 838 else 839 FrameResized(Bounds().Width(), Bounds().Height()); 840 841 printf("VideoFormatChange leave\n"); 842 } 843 844 845 // #pragma mark - 846 847 848 void 849 MainWin::_RefsReceived(BMessage* msg) 850 { 851 // the playlist ist replaced by dropped files 852 // or the dropped files are appended to the end 853 // of the existing playlist if <shift> is pressed 854 BAutolock _(fPlaylist); 855 int32 appendIndex = modifiers() & B_SHIFT_KEY ? 856 fPlaylist->CountItems() : -1; 857 msg->AddInt32("append_index", appendIndex); 858 859 // forward the message to the playlist window, 860 // so that undo/redo is used for modifying the playlist 861 fPlaylistWindow->PostMessage(msg); 862 } 863 864 865 void 866 MainWin::_SetupWindow() 867 { 868 // printf("MainWin::_SetupWindow\n"); 869 // Populate the track menus 870 _SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu); 871 // Enable both if a file was loaded 872 fAudioTrackMenu->SetEnabled(fHasFile); 873 fVideoTrackMenu->SetEnabled(fHasFile); 874 875 fVideoMenu->SetEnabled(fHasVideo); 876 fAudioMenu->SetEnabled(fHasAudio); 877 int previousSourceWidth = fSourceWidth; 878 int previousSourceHeight = fSourceHeight; 879 int previousWidthAspect = fWidthAspect; 880 int previousHeightAspect = fHeightAspect; 881 if (fHasVideo) { 882 fController->GetSize(&fSourceWidth, &fSourceHeight, 883 &fWidthAspect, &fHeightAspect); 884 } else { 885 fSourceWidth = 0; 886 fSourceHeight = 0; 887 fWidthAspect = 1; 888 fHeightAspect = 1; 889 } 890 _UpdateControlsEnabledStatus(); 891 892 // Adopt the size and window layout if necessary 893 if (previousSourceWidth != fSourceWidth 894 || previousSourceHeight != fSourceHeight 895 || previousWidthAspect != fWidthAspect 896 || previousHeightAspect != fHeightAspect) { 897 898 _SetWindowSizeLimits(); 899 900 if (!fIsFullscreen) { 901 // Resize to 100% but stay on screen 902 _ResizeWindow(100, true); 903 } else { 904 // Make sure we relayout the video view when in full screen mode 905 FrameResized(Frame().Width(), Frame().Height()); 906 } 907 } 908 909 fVideoView->MakeFocus(); 910 } 911 912 913 void 914 MainWin::_CreateMenu() 915 { 916 fFileMenu = new BMenu(NAME); 917 fPlaylistMenu = new BMenu("Playlist"B_UTF8_ELLIPSIS); 918 fAudioMenu = new BMenu("Audio"); 919 fVideoMenu = new BMenu("Video"); 920 fVideoAspectMenu = new BMenu("Aspect Ratio"); 921 fSettingsMenu = new BMenu("Settings"); 922 fAudioTrackMenu = new BMenu("Track"); 923 fVideoTrackMenu = new BMenu("Track"); 924 925 fMenuBar->AddItem(fFileMenu); 926 fMenuBar->AddItem(fAudioMenu); 927 fMenuBar->AddItem(fVideoMenu); 928 fMenuBar->AddItem(fSettingsMenu); 929 930 fFileMenu->AddItem(new BMenuItem("New Player"B_UTF8_ELLIPSIS, 931 new BMessage(M_FILE_NEWPLAYER), 'N')); 932 fFileMenu->AddSeparatorItem(); 933 934 // fFileMenu->AddItem(new BMenuItem("Open File"B_UTF8_ELLIPSIS, 935 // new BMessage(M_FILE_OPEN), 'O')); 936 // Add recent files 937 BRecentFilesList recentFiles(10, false, NULL, kAppSig); 938 BMenuItem *item = new BMenuItem(recentFiles.NewFileListMenu( 939 "Open File"B_UTF8_ELLIPSIS, new BMessage(B_REFS_RECEIVED), 940 NULL, this, 10, false, NULL, 0, kAppSig), new BMessage(M_FILE_OPEN)); 941 item->SetShortcut('O', 0); 942 fFileMenu->AddItem(item); 943 944 fFileMenu->AddItem(new BMenuItem("File Info"B_UTF8_ELLIPSIS, 945 new BMessage(M_FILE_INFO), 'I')); 946 fFileMenu->AddItem(fPlaylistMenu); 947 fPlaylistMenu->Superitem()->SetShortcut('P', B_COMMAND_KEY); 948 fPlaylistMenu->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST)); 949 950 fFileMenu->AddSeparatorItem(); 951 fFileMenu->AddItem(new BMenuItem("About " NAME B_UTF8_ELLIPSIS, 952 new BMessage(B_ABOUT_REQUESTED))); 953 fFileMenu->AddSeparatorItem(); 954 fFileMenu->AddItem(new BMenuItem("Close", new BMessage(M_FILE_CLOSE), 'W')); 955 fFileMenu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q')); 956 957 fPlaylistMenu->SetRadioMode(true); 958 959 fAudioMenu->AddItem(fAudioTrackMenu); 960 961 fVideoMenu->AddItem(fVideoTrackMenu); 962 fVideoMenu->AddSeparatorItem(); 963 BMessage* resizeMessage = new BMessage(M_VIEW_SIZE); 964 resizeMessage->AddInt32("size", 50); 965 fVideoMenu->AddItem(new BMenuItem("50% scale", resizeMessage, '0')); 966 967 resizeMessage = new BMessage(M_VIEW_SIZE); 968 resizeMessage->AddInt32("size", 100); 969 fVideoMenu->AddItem(new BMenuItem("100% scale", resizeMessage, '1')); 970 971 resizeMessage = new BMessage(M_VIEW_SIZE); 972 resizeMessage->AddInt32("size", 200); 973 fVideoMenu->AddItem(new BMenuItem("200% scale", resizeMessage, '2')); 974 975 resizeMessage = new BMessage(M_VIEW_SIZE); 976 resizeMessage->AddInt32("size", 300); 977 fVideoMenu->AddItem(new BMenuItem("300% scale", resizeMessage, '3')); 978 979 resizeMessage = new BMessage(M_VIEW_SIZE); 980 resizeMessage->AddInt32("size", 400); 981 fVideoMenu->AddItem(new BMenuItem("400% scale", resizeMessage, '4')); 982 983 fVideoMenu->AddSeparatorItem(); 984 985 fVideoMenu->AddItem(new BMenuItem("Full Screen", 986 new BMessage(M_TOGGLE_FULLSCREEN), 'F')); 987 988 fVideoMenu->AddSeparatorItem(); 989 990 _SetupVideoAspectItems(fVideoAspectMenu); 991 fVideoMenu->AddItem(fVideoAspectMenu); 992 993 fNoInterfaceMenuItem = new BMenuItem("No Interface", 994 new BMessage(M_TOGGLE_NO_INTERFACE), 'B'); 995 fSettingsMenu->AddItem(fNoInterfaceMenuItem); 996 fSettingsMenu->AddItem(new BMenuItem("Always on Top", 997 new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T')); 998 fSettingsMenu->AddSeparatorItem(); 999 item = new BMenuItem("Settings"B_UTF8_ELLIPSIS, 1000 new BMessage(M_SETTINGS), 'S'); 1001 fSettingsMenu->AddItem(item); 1002 item->SetTarget(be_app); 1003 } 1004 1005 1006 void 1007 MainWin::_SetupVideoAspectItems(BMenu* menu) 1008 { 1009 BMenuItem* item; 1010 while ((item = menu->RemoveItem(0L)) != NULL) 1011 delete item; 1012 1013 int width; 1014 int height; 1015 int widthAspect; 1016 int heightAspect; 1017 fController->GetSize(&width, &height, &widthAspect, &heightAspect); 1018 // We don't care if there is a video track at all. In that 1019 // case we should end up not marking any item. 1020 1021 // NOTE: The item marking may end up marking for example both 1022 // "Stream Settings" and "16 : 9" if the stream settings happen to 1023 // be "16 : 9". 1024 1025 menu->AddItem(item = new BMenuItem("Stream Settings", 1026 new BMessage(M_ASPECT_SAME_AS_SOURCE))); 1027 item->SetMarked(widthAspect == fWidthAspect 1028 && heightAspect == fHeightAspect); 1029 1030 menu->AddItem(item = new BMenuItem("No Aspect Correction", 1031 new BMessage(M_ASPECT_NO_DISTORTION))); 1032 item->SetMarked(width == fWidthAspect && height == fHeightAspect); 1033 1034 menu->AddSeparatorItem(); 1035 1036 menu->AddItem(item = new BMenuItem("4 : 3", 1037 new BMessage(M_ASPECT_4_3))); 1038 item->SetMarked(fWidthAspect == 4 && fHeightAspect == 3); 1039 menu->AddItem(item = new BMenuItem("16 : 9", 1040 new BMessage(M_ASPECT_16_9))); 1041 item->SetMarked(fWidthAspect == 16 && fHeightAspect == 9); 1042 1043 menu->AddSeparatorItem(); 1044 1045 menu->AddItem(item = new BMenuItem("1.66 : 1", 1046 new BMessage(M_ASPECT_83_50))); 1047 item->SetMarked(fWidthAspect == 83 && fHeightAspect == 50); 1048 menu->AddItem(item = new BMenuItem("1.75 : 1", 1049 new BMessage(M_ASPECT_7_4))); 1050 item->SetMarked(fWidthAspect == 7 && fHeightAspect == 4); 1051 menu->AddItem(item = new BMenuItem("1.85 : 1 (American)", 1052 new BMessage(M_ASPECT_37_20))); 1053 item->SetMarked(fWidthAspect == 37 && fHeightAspect == 20); 1054 menu->AddItem(item = new BMenuItem("2.35 : 1 (Cinemascope)", 1055 new BMessage(M_ASPECT_47_20))); 1056 item->SetMarked(fWidthAspect == 47 && fHeightAspect == 20); 1057 } 1058 1059 1060 void 1061 MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu) 1062 { 1063 audioTrackMenu->RemoveItems(0, audioTrackMenu->CountItems(), true); 1064 videoTrackMenu->RemoveItems(0, videoTrackMenu->CountItems(), true); 1065 1066 char s[100]; 1067 1068 int count = fController->AudioTrackCount(); 1069 int current = fController->CurrentAudioTrack(); 1070 for (int i = 0; i < count; i++) { 1071 sprintf(s, "Track %d", i + 1); 1072 BMenuItem* item = new BMenuItem(s, 1073 new BMessage(M_SELECT_AUDIO_TRACK + i)); 1074 item->SetMarked(i == current); 1075 audioTrackMenu->AddItem(item); 1076 } 1077 if (!count) { 1078 audioTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); 1079 audioTrackMenu->ItemAt(0)->SetMarked(true); 1080 } 1081 1082 1083 count = fController->VideoTrackCount(); 1084 current = fController->CurrentVideoTrack(); 1085 for (int i = 0; i < count; i++) { 1086 sprintf(s, "Track %d", i + 1); 1087 BMenuItem* item = new BMenuItem(s, 1088 new BMessage(M_SELECT_VIDEO_TRACK + i)); 1089 item->SetMarked(i == current); 1090 videoTrackMenu->AddItem(item); 1091 } 1092 if (!count) { 1093 videoTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); 1094 videoTrackMenu->ItemAt(0)->SetMarked(true); 1095 } 1096 } 1097 1098 1099 void 1100 MainWin::_GetMinimumWindowSize(int& width, int& height) const 1101 { 1102 width = MIN_WIDTH; 1103 height = 0; 1104 if (!fNoInterface) { 1105 width = max_c(width, fMenuBarWidth); 1106 width = max_c(width, fControlsWidth); 1107 height = fMenuBarHeight + fControlsHeight; 1108 } 1109 } 1110 1111 1112 void 1113 MainWin::_GetUnscaledVideoSize(int& videoWidth, int& videoHeight) const 1114 { 1115 if (fWidthAspect != 0 && fHeightAspect != 0) { 1116 videoWidth = fSourceHeight * fWidthAspect / fHeightAspect; 1117 videoHeight = fSourceWidth * fHeightAspect / fWidthAspect; 1118 // Use the scaling which produces an enlarged view. 1119 if (videoWidth > fSourceWidth) { 1120 // Enlarge width 1121 videoHeight = fSourceHeight; 1122 } else { 1123 // Enlarge height 1124 videoWidth = fSourceWidth; 1125 } 1126 } else { 1127 videoWidth = fSourceWidth; 1128 videoHeight = fSourceHeight; 1129 } 1130 } 1131 1132 void 1133 MainWin::_SetWindowSizeLimits() 1134 { 1135 int minWidth; 1136 int minHeight; 1137 _GetMinimumWindowSize(minWidth, minHeight); 1138 SetSizeLimits(minWidth - 1, 32000, minHeight - 1, fHasVideo ? 1139 32000 : minHeight - 1); 1140 } 1141 1142 1143 int 1144 MainWin::_CurrentVideoSizeInPercent() const 1145 { 1146 if (!fHasVideo) 1147 return 0; 1148 1149 int videoWidth; 1150 int videoHeight; 1151 _GetUnscaledVideoSize(videoWidth, videoHeight); 1152 1153 int viewWidth = fVideoView->Bounds().IntegerWidth() + 1; 1154 int viewHeight = fVideoView->Bounds().IntegerHeight() + 1; 1155 1156 int widthPercent = videoWidth * 100 / viewWidth; 1157 int heightPercent = videoHeight * 100 / viewHeight; 1158 1159 if (widthPercent > heightPercent) 1160 return widthPercent; 1161 return heightPercent; 1162 } 1163 1164 1165 void 1166 MainWin::_ResizeWindow(int percent, bool stayOnScreen) 1167 { 1168 // Get required window size 1169 int videoWidth; 1170 int videoHeight; 1171 _GetUnscaledVideoSize(videoWidth, videoHeight); 1172 1173 videoWidth = (videoWidth * percent) / 100; 1174 videoHeight = (videoHeight * percent) / 100; 1175 1176 // Calculate and set the minimum window size 1177 int width; 1178 int height; 1179 _GetMinimumWindowSize(width, height); 1180 1181 width = max_c(width, videoWidth) - 1; 1182 height = height + videoHeight - 1; 1183 1184 if (stayOnScreen) { 1185 BRect screenFrame(BScreen(this).Frame()); 1186 BRect frame(Frame()); 1187 BRect decoratorFrame(DecoratorFrame()); 1188 1189 // Shrink the screen frame by the window border size 1190 screenFrame.top += frame.top - decoratorFrame.top; 1191 screenFrame.left += frame.left - decoratorFrame.left; 1192 screenFrame.right += frame.right - decoratorFrame.right; 1193 screenFrame.bottom += frame.bottom - decoratorFrame.bottom; 1194 1195 // Update frame to what the new size would be 1196 frame.right = frame.left + width; 1197 frame.bottom = frame.top + height; 1198 1199 if (!screenFrame.Contains(frame)) { 1200 // Resize the window so it doesn't extend outside the current 1201 // screen frame. 1202 if (frame.Width() > screenFrame.Width() 1203 || frame.Height() > screenFrame.Height()) { 1204 // too large 1205 int widthDiff 1206 = frame.IntegerWidth() - screenFrame.IntegerWidth(); 1207 int heightDiff 1208 = frame.IntegerHeight() - screenFrame.IntegerHeight(); 1209 1210 float shrinkScale; 1211 if (widthDiff > heightDiff) 1212 shrinkScale = (float)(width - widthDiff) / width; 1213 else 1214 shrinkScale = (float)(height - heightDiff) / height; 1215 1216 // Resize width/height and center window 1217 width = lround(width * shrinkScale); 1218 height = lround(height * shrinkScale); 1219 MoveTo((screenFrame.left + screenFrame.right - width) / 2, 1220 (screenFrame.top + screenFrame.bottom - height) / 2); 1221 } else { 1222 // just off-screen on one or more sides 1223 int offsetX = 0; 1224 int offsetY = 0; 1225 if (frame.left < screenFrame.left) 1226 offsetX = (int)(screenFrame.left - frame.left); 1227 else if (frame.right > screenFrame.right) 1228 offsetX = (int)(screenFrame.right - frame.right); 1229 if (frame.top < screenFrame.top) 1230 offsetX = (int)(screenFrame.top - frame.top); 1231 else if (frame.bottom > screenFrame.bottom) 1232 offsetX = (int)(screenFrame.bottom - frame.bottom); 1233 MoveBy(offsetX, offsetY); 1234 } 1235 } 1236 } 1237 1238 ResizeTo(width, height); 1239 } 1240 1241 1242 void 1243 MainWin::_ResizeVideoView(int x, int y, int width, int height) 1244 { 1245 printf("_ResizeVideoView: %d,%d, width %d, height %d\n", x, y, 1246 width, height); 1247 1248 // Keep aspect ratio, place video view inside 1249 // the background area (may create black bars). 1250 int videoWidth; 1251 int videoHeight; 1252 _GetUnscaledVideoSize(videoWidth, videoHeight); 1253 float scaledWidth = videoWidth; 1254 float scaledHeight = videoHeight; 1255 float factor = min_c(width / scaledWidth, height / scaledHeight); 1256 int renderWidth = lround(scaledWidth * factor); 1257 int renderHeight = lround(scaledHeight * factor); 1258 if (renderWidth > width) 1259 renderWidth = width; 1260 if (renderHeight > height) 1261 renderHeight = height; 1262 1263 int xOffset = x + (width - renderWidth) / 2; 1264 int yOffset = y + (height - renderHeight) / 2; 1265 1266 fVideoView->MoveTo(xOffset, yOffset); 1267 fVideoView->ResizeTo(renderWidth - 1, renderHeight - 1); 1268 } 1269 1270 1271 // #pragma mark - 1272 1273 1274 void 1275 MainWin::_MouseDown(BMessage *msg, BView* originalHandler) 1276 { 1277 BPoint screen_where; 1278 uint32 buttons = msg->FindInt32("buttons"); 1279 1280 // On Zeta, only "screen_where" is relyable, "where" and "be:view_where" 1281 // seem to be broken 1282 if (B_OK != msg->FindPoint("screen_where", &screen_where)) { 1283 // Workaround for BeOS R5, it has no "screen_where" 1284 if (!originalHandler || msg->FindPoint("where", &screen_where) < B_OK) 1285 return; 1286 originalHandler->ConvertToScreen(&screen_where); 1287 } 1288 1289 // msg->PrintToStream(); 1290 1291 // if (1 == msg->FindInt32("buttons") && msg->FindInt32("clicks") == 1) { 1292 1293 if (1 == buttons && msg->FindInt32("clicks") % 2 == 0) { 1294 BRect r(screen_where.x - 1, screen_where.y - 1, screen_where.x + 1, 1295 screen_where.y + 1); 1296 if (r.Contains(fMouseDownMousePos)) { 1297 PostMessage(M_TOGGLE_FULLSCREEN); 1298 return; 1299 } 1300 } 1301 1302 if (2 == buttons && msg->FindInt32("clicks") % 2 == 0) { 1303 BRect r(screen_where.x - 1, screen_where.y - 1, screen_where.x + 1, 1304 screen_where.y + 1); 1305 if (r.Contains(fMouseDownMousePos)) { 1306 PostMessage(M_TOGGLE_NO_INTERFACE); 1307 return; 1308 } 1309 } 1310 1311 /* 1312 // very broken in Zeta: 1313 fMouseDownMousePos = fVideoView->ConvertToScreen( 1314 msg->FindPoint("where")); 1315 */ 1316 fMouseDownMousePos = screen_where; 1317 fMouseDownWindowPos = Frame().LeftTop(); 1318 1319 if (buttons == 1 && !fIsFullscreen) { 1320 // start mouse tracking 1321 fVideoView->SetMouseEventMask(B_POINTER_EVENTS | B_NO_POINTER_HISTORY 1322 /* | B_LOCK_WINDOW_FOCUS */); 1323 fMouseDownTracking = true; 1324 } 1325 1326 // pop up a context menu if right mouse button is down for 200 ms 1327 1328 if ((buttons & 2) == 0) 1329 return; 1330 bigtime_t start = system_time(); 1331 bigtime_t delay = 200000; 1332 BPoint location; 1333 do { 1334 fVideoView->GetMouse(&location, &buttons); 1335 if ((buttons & 2) == 0) 1336 break; 1337 snooze(1000); 1338 } while (system_time() - start < delay); 1339 1340 if (buttons & 2) 1341 _ShowContextMenu(screen_where); 1342 } 1343 1344 1345 void 1346 MainWin::_MouseMoved(BMessage *msg, BView* originalHandler) 1347 { 1348 // msg->PrintToStream(); 1349 1350 BPoint mousePos; 1351 uint32 buttons = msg->FindInt32("buttons"); 1352 1353 if (1 == buttons && fMouseDownTracking && !fIsFullscreen) { 1354 /* 1355 // very broken in Zeta: 1356 BPoint mousePos = msg->FindPoint("where"); 1357 printf("view where: %.0f, %.0f => ", mousePos.x, mousePos.y); 1358 fVideoView->ConvertToScreen(&mousePos); 1359 */ 1360 // On Zeta, only "screen_where" is relyable, "where" 1361 // and "be:view_where" seem to be broken 1362 if (B_OK != msg->FindPoint("screen_where", &mousePos)) { 1363 // Workaround for BeOS R5, it has no "screen_where" 1364 if (!originalHandler || msg->FindPoint("where", &mousePos) < B_OK) 1365 return; 1366 originalHandler->ConvertToScreen(&mousePos); 1367 } 1368 // printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y); 1369 float delta_x = mousePos.x - fMouseDownMousePos.x; 1370 float delta_y = mousePos.y - fMouseDownMousePos.y; 1371 float x = fMouseDownWindowPos.x + delta_x; 1372 float y = fMouseDownWindowPos.y + delta_y; 1373 // printf("move window to %.0f, %.0f\n", x, y); 1374 MoveTo(x, y); 1375 } 1376 } 1377 1378 1379 void 1380 MainWin::_MouseUp(BMessage *msg) 1381 { 1382 // msg->PrintToStream(); 1383 fMouseDownTracking = false; 1384 } 1385 1386 1387 void 1388 MainWin::_ShowContextMenu(const BPoint &screen_point) 1389 { 1390 printf("Show context menu\n"); 1391 BPopUpMenu *menu = new BPopUpMenu("context menu", false, false); 1392 BMenuItem *item; 1393 menu->AddItem(item = new BMenuItem("Full Screen", 1394 new BMessage(M_TOGGLE_FULLSCREEN), 'F')); 1395 item->SetMarked(fIsFullscreen); 1396 item->SetEnabled(fHasVideo); 1397 1398 BMenu* aspectSubMenu = new BMenu("Aspect Ratio"); 1399 _SetupVideoAspectItems(aspectSubMenu); 1400 aspectSubMenu->SetTargetForItems(this); 1401 menu->AddItem(item = new BMenuItem(aspectSubMenu)); 1402 item->SetEnabled(fHasVideo); 1403 1404 menu->AddItem(item = new BMenuItem("No Interface", 1405 new BMessage(M_TOGGLE_NO_INTERFACE), 'B')); 1406 item->SetMarked(fNoInterface); 1407 item->SetEnabled(fHasVideo); 1408 1409 menu->AddSeparatorItem(); 1410 1411 // Add track selector menus 1412 BMenu* audioTrackMenu = new BMenu("Audio Track"); 1413 BMenu* videoTrackMenu = new BMenu("Video Track"); 1414 _SetupTrackMenus(audioTrackMenu, videoTrackMenu); 1415 1416 audioTrackMenu->SetTargetForItems(this); 1417 videoTrackMenu->SetTargetForItems(this); 1418 1419 menu->AddItem(item = new BMenuItem(audioTrackMenu)); 1420 item->SetEnabled(fHasAudio); 1421 1422 menu->AddItem(item = new BMenuItem(videoTrackMenu)); 1423 item->SetEnabled(fHasVideo); 1424 1425 menu->AddSeparatorItem(); 1426 1427 menu->AddItem(new BMenuItem("About " NAME B_UTF8_ELLIPSIS, 1428 new BMessage(B_ABOUT_REQUESTED))); 1429 menu->AddSeparatorItem(); 1430 menu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q')); 1431 1432 menu->SetTargetForItems(this); 1433 BRect r(screen_point.x - 5, screen_point.y - 5, screen_point.x + 5, 1434 screen_point.y + 5); 1435 menu->Go(screen_point, true, true, r, true); 1436 } 1437 1438 1439 /*! Trap keys that are about to be send to background or renderer view. 1440 Return true if it shouldn't be passed to the view. 1441 */ 1442 bool 1443 MainWin::_KeyDown(BMessage *msg) 1444 { 1445 // TODO: use the shortcut mechanism instead! 1446 1447 uint32 key = msg->FindInt32("key"); 1448 uint32 rawChar = msg->FindInt32("raw_char"); 1449 uint32 modifier = msg->FindInt32("modifiers"); 1450 1451 printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar, 1452 modifier); 1453 1454 // ignore the system modifier namespace 1455 if ((modifier & (B_CONTROL_KEY | B_COMMAND_KEY)) 1456 == (B_CONTROL_KEY | B_COMMAND_KEY)) 1457 return false; 1458 1459 switch (rawChar) { 1460 case B_SPACE: 1461 fController->TogglePlaying(); 1462 return true; 1463 1464 case B_ESCAPE: 1465 if (!fIsFullscreen) 1466 break; 1467 1468 PostMessage(M_TOGGLE_FULLSCREEN); 1469 return true; 1470 1471 case B_ENTER: // Enter / Return 1472 if (modifier & B_COMMAND_KEY) { 1473 PostMessage(M_TOGGLE_FULLSCREEN); 1474 return true; 1475 } else 1476 break; 1477 1478 case B_TAB: 1479 if ((modifier & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY 1480 | B_MENU_KEY)) == 0) { 1481 PostMessage(M_TOGGLE_FULLSCREEN); 1482 return true; 1483 } else 1484 break; 1485 1486 case B_UP_ARROW: 1487 if ((modifier & B_COMMAND_KEY) != 0) 1488 PostMessage(M_SKIP_NEXT); 1489 else 1490 PostMessage(M_VOLUME_UP); 1491 return true; 1492 1493 case B_DOWN_ARROW: 1494 if ((modifier & B_COMMAND_KEY) != 0) 1495 PostMessage(M_SKIP_PREV); 1496 else 1497 PostMessage(M_VOLUME_DOWN); 1498 return true; 1499 1500 case B_RIGHT_ARROW: 1501 if ((modifier & B_COMMAND_KEY) != 0) 1502 PostMessage(M_VOLUME_UP); 1503 else 1504 PostMessage(M_SKIP_NEXT); 1505 return true; 1506 1507 case B_LEFT_ARROW: 1508 if ((modifier & B_COMMAND_KEY) != 0) 1509 PostMessage(M_VOLUME_DOWN); 1510 else 1511 PostMessage(M_SKIP_PREV); 1512 return true; 1513 1514 case B_PAGE_UP: 1515 PostMessage(M_SKIP_NEXT); 1516 return true; 1517 1518 case B_PAGE_DOWN: 1519 PostMessage(M_SKIP_PREV); 1520 return true; 1521 } 1522 1523 switch (key) { 1524 case 0x3a: // numeric keypad + 1525 if ((modifier & B_COMMAND_KEY) == 0) { 1526 PostMessage(M_VOLUME_UP); 1527 return true; 1528 } else { 1529 break; 1530 } 1531 1532 case 0x25: // numeric keypad - 1533 if ((modifier & B_COMMAND_KEY) == 0) { 1534 PostMessage(M_VOLUME_DOWN); 1535 return true; 1536 } else { 1537 break; 1538 } 1539 1540 case 0x38: // numeric keypad up arrow 1541 PostMessage(M_VOLUME_UP); 1542 return true; 1543 1544 case 0x59: // numeric keypad down arrow 1545 PostMessage(M_VOLUME_DOWN); 1546 return true; 1547 1548 case 0x39: // numeric keypad page up 1549 case 0x4a: // numeric keypad right arrow 1550 PostMessage(M_SKIP_NEXT); 1551 return true; 1552 1553 case 0x5a: // numeric keypad page down 1554 case 0x48: // numeric keypad left arrow 1555 PostMessage(M_SKIP_PREV); 1556 return true; 1557 1558 case 0x34: // delete button 1559 case 0x3e: // d for delete 1560 case 0x2b: // t for Trash 1561 if ((modifiers() & B_COMMAND_KEY) != 0) { 1562 BAutolock _(fPlaylist); 1563 BMessage removeMessage(M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH); 1564 removeMessage.AddInt32("playlist index", 1565 fPlaylist->CurrentItemIndex()); 1566 fPlaylistWindow->PostMessage(&removeMessage); 1567 return true; 1568 } 1569 break; 1570 } 1571 1572 return false; 1573 } 1574 1575 1576 // #pragma mark - 1577 1578 1579 void 1580 MainWin::_ToggleFullscreen() 1581 { 1582 printf("_ToggleFullscreen enter\n"); 1583 1584 if (!fHasVideo) { 1585 printf("_ToggleFullscreen - ignoring, as we don't have a video\n"); 1586 return; 1587 } 1588 1589 fIsFullscreen = !fIsFullscreen; 1590 1591 if (fIsFullscreen) { 1592 // switch to fullscreen 1593 1594 fSavedFrame = Frame(); 1595 printf("saving current frame: %d %d %d %d\n", int(fSavedFrame.left), 1596 int(fSavedFrame.top), int(fSavedFrame.right), 1597 int(fSavedFrame.bottom)); 1598 BScreen screen(this); 1599 BRect rect(screen.Frame()); 1600 1601 Hide(); 1602 MoveTo(rect.left, rect.top); 1603 ResizeTo(rect.Width(), rect.Height()); 1604 Show(); 1605 1606 } else { 1607 // switch back from full screen mode 1608 1609 Hide(); 1610 MoveTo(fSavedFrame.left, fSavedFrame.top); 1611 ResizeTo(fSavedFrame.Width(), fSavedFrame.Height()); 1612 Show(); 1613 } 1614 1615 fVideoView->SetFullscreen(fIsFullscreen); 1616 1617 _MarkItem(fSettingsMenu, M_TOGGLE_FULLSCREEN, fIsFullscreen); 1618 1619 printf("_ToggleFullscreen leave\n"); 1620 } 1621 1622 void 1623 MainWin::_ToggleAlwaysOnTop() 1624 { 1625 fAlwaysOnTop = !fAlwaysOnTop; 1626 SetFeel(fAlwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); 1627 1628 _MarkItem(fSettingsMenu, M_TOGGLE_ALWAYS_ON_TOP, fAlwaysOnTop); 1629 } 1630 1631 1632 void 1633 MainWin::_ToggleNoInterface() 1634 { 1635 printf("_ToggleNoInterface enter\n"); 1636 1637 if (fIsFullscreen || !fHasVideo) { 1638 // Fullscreen playback is always without interface and 1639 // audio playback is always with interface. So we ignore these 1640 // two states here. 1641 printf("_ToggleNoControls leave, doing nothing, we are fullscreen\n"); 1642 return; 1643 } 1644 1645 fNoInterface = !fNoInterface; 1646 _SetWindowSizeLimits(); 1647 1648 if (fNoInterface) { 1649 MoveBy(0, fMenuBarHeight); 1650 ResizeBy(0, -(fControlsHeight + fMenuBarHeight)); 1651 SetLook(B_BORDERED_WINDOW_LOOK); 1652 } else { 1653 MoveBy(0, -fMenuBarHeight); 1654 ResizeBy(0, fControlsHeight + fMenuBarHeight); 1655 SetLook(B_TITLED_WINDOW_LOOK); 1656 } 1657 1658 _MarkItem(fSettingsMenu, M_TOGGLE_NO_INTERFACE, fNoInterface); 1659 1660 printf("_ToggleNoInterface leave\n"); 1661 } 1662 1663 1664 // #pragma mark - 1665 1666 1667 void 1668 MainWin::_UpdateControlsEnabledStatus() 1669 { 1670 uint32 enabledButtons = 0; 1671 if (fHasVideo || fHasAudio) { 1672 enabledButtons |= PLAYBACK_ENABLED | SEEK_ENABLED 1673 | SEEK_BACK_ENABLED | SEEK_FORWARD_ENABLED; 1674 } 1675 if (fHasAudio) 1676 enabledButtons |= VOLUME_ENABLED; 1677 1678 BAutolock _(fPlaylist); 1679 bool canSkipPrevious, canSkipNext; 1680 fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext); 1681 if (canSkipPrevious) 1682 enabledButtons |= SKIP_BACK_ENABLED; 1683 if (canSkipNext) 1684 enabledButtons |= SKIP_FORWARD_ENABLED; 1685 1686 fControls->SetEnabled(enabledButtons); 1687 1688 fNoInterfaceMenuItem->SetEnabled(fHasVideo); 1689 } 1690 1691 1692 void 1693 MainWin::_UpdatePlaylistMenu() 1694 { 1695 if (!fPlaylist->Lock()) 1696 return; 1697 1698 fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true); 1699 1700 int32 count = fPlaylist->CountItems(); 1701 for (int32 i = 0; i < count; i++) { 1702 PlaylistItem* item = fPlaylist->ItemAtFast(i); 1703 _AddPlaylistItem(item, i); 1704 } 1705 fPlaylistMenu->SetTargetForItems(this); 1706 1707 _MarkPlaylistItem(fPlaylist->CurrentItemIndex()); 1708 1709 fPlaylist->Unlock(); 1710 } 1711 1712 1713 void 1714 MainWin::_AddPlaylistItem(PlaylistItem* item, int32 index) 1715 { 1716 BMessage* message = new BMessage(M_SET_PLAYLIST_POSITION); 1717 message->AddInt32("index", index); 1718 BMenuItem* menuItem = new BMenuItem(item->Name().String(), message); 1719 fPlaylistMenu->AddItem(menuItem, index); 1720 } 1721 1722 1723 void 1724 MainWin::_RemovePlaylistItem(int32 index) 1725 { 1726 delete fPlaylistMenu->RemoveItem(index); 1727 } 1728 1729 1730 void 1731 MainWin::_MarkPlaylistItem(int32 index) 1732 { 1733 if (BMenuItem* item = fPlaylistMenu->ItemAt(index)) { 1734 item->SetMarked(true); 1735 // ... and in case the menu is currently on screen: 1736 if (fPlaylistMenu->LockLooper()) { 1737 fPlaylistMenu->Invalidate(); 1738 fPlaylistMenu->UnlockLooper(); 1739 } 1740 } 1741 } 1742 1743 1744 void 1745 MainWin::_MarkItem(BMenu* menu, uint32 command, bool mark) 1746 { 1747 if (BMenuItem* item = menu->FindItem(command)) 1748 item->SetMarked(mark); 1749 } 1750 1751 1752 void 1753 MainWin::_AdoptGlobalSettings() 1754 { 1755 mpSettings settings = Settings::CurrentSettings(); 1756 // thread safe 1757 1758 fCloseWhenDonePlayingMovie = settings.closeWhenDonePlayingMovie; 1759 fCloseWhenDonePlayingSound = settings.closeWhenDonePlayingSound; 1760 } 1761 1762 1763