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