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