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