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