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