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