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