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