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