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