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-2009 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 <Debug.h> 34 #include <fs_attr.h> 35 #include <Menu.h> 36 #include <MenuBar.h> 37 #include <MenuItem.h> 38 #include <MessageRunner.h> 39 #include <Messenger.h> 40 #include <PopUpMenu.h> 41 #include <PropertyInfo.h> 42 #include <RecentItems.h> 43 #include <Roster.h> 44 #include <Screen.h> 45 #include <String.h> 46 #include <View.h> 47 48 #include "AudioProducer.h" 49 #include "ControllerObserver.h" 50 #include "FilePlaylistItem.h" 51 #include "MainApp.h" 52 #include "PeakView.h" 53 #include "PlaylistItem.h" 54 #include "PlaylistObserver.h" 55 #include "PlaylistWindow.h" 56 #include "Settings.h" 57 58 59 #define MIN_WIDTH 250 60 61 62 int MainWin::sNoVideoWidth = MIN_WIDTH; 63 64 65 // XXX TODO: why is lround not defined? 66 #define lround(a) ((int)(0.99999 + (a))) 67 68 enum { 69 M_DUMMY = 0x100, 70 M_FILE_OPEN = 0x1000, 71 M_FILE_NEWPLAYER, 72 M_FILE_INFO, 73 M_FILE_PLAYLIST, 74 M_FILE_CLOSE, 75 M_FILE_QUIT, 76 M_VIEW_SIZE, 77 M_TOGGLE_FULLSCREEN, 78 M_TOGGLE_ALWAYS_ON_TOP, 79 M_TOGGLE_NO_INTERFACE, 80 M_VOLUME_UP, 81 M_VOLUME_DOWN, 82 M_SKIP_NEXT, 83 M_SKIP_PREV, 84 85 // The common display aspect ratios 86 M_ASPECT_SAME_AS_SOURCE, 87 M_ASPECT_NO_DISTORTION, 88 M_ASPECT_4_3, 89 M_ASPECT_16_9, 90 M_ASPECT_83_50, 91 M_ASPECT_7_4, 92 M_ASPECT_37_20, 93 M_ASPECT_47_20, 94 95 M_SELECT_AUDIO_TRACK = 0x00000800, 96 M_SELECT_AUDIO_TRACK_END = 0x00000fff, 97 M_SELECT_VIDEO_TRACK = 0x00010000, 98 M_SELECT_VIDEO_TRACK_END = 0x000fffff, 99 100 M_SET_PLAYLIST_POSITION, 101 102 M_FILE_DELETE, 103 104 M_SHOW_IF_NEEDED 105 }; 106 107 108 static property_info sPropertyInfo[] = { 109 { "Next", { B_EXECUTE_PROPERTY }, 110 { B_DIRECT_SPECIFIER, 0 }, "Skip to the next track.", 0 111 }, 112 { "Prev", { B_EXECUTE_PROPERTY }, 113 { B_DIRECT_SPECIFIER, 0 }, "Skip to the previous track.", 0 114 }, 115 { "Play", { B_EXECUTE_PROPERTY }, 116 { B_DIRECT_SPECIFIER, 0 }, "Start playing.", 0 117 }, 118 { "Stop", { B_EXECUTE_PROPERTY }, 119 { B_DIRECT_SPECIFIER, 0 }, "Stop playing.", 0 120 }, 121 { "Pause", { B_EXECUTE_PROPERTY }, 122 { B_DIRECT_SPECIFIER, 0 }, "Pause playback.", 0 123 }, 124 { "TogglePlaying", { B_EXECUTE_PROPERTY }, 125 { B_DIRECT_SPECIFIER, 0 }, "Toggle pause/play.", 0 126 }, 127 { "Mute", { B_EXECUTE_PROPERTY }, 128 { B_DIRECT_SPECIFIER, 0 }, "Toggle mute.", 0 129 }, 130 { "Volume", { B_GET_PROPERTY, B_SET_PROPERTY, 0 }, 131 { B_DIRECT_SPECIFIER, 0 }, "Gets/sets the volume (0.0-2.0).", 0, 132 { B_FLOAT_TYPE } 133 }, 134 { "URI", { B_GET_PROPERTY, 0 }, 135 { B_DIRECT_SPECIFIER, 0 }, 136 "Gets the URI of the currently playing item.", 0, { B_STRING_TYPE } 137 }, 138 { 0, { 0 }, { 0 }, 0, 0 } 139 }; 140 141 142 //#define printf(a...) 143 144 145 MainWin::MainWin(bool isFirstWindow, BMessage* message) 146 : 147 BWindow(BRect(100, 100, 400, 300), NAME, B_TITLED_WINDOW, 148 B_ASYNCHRONOUS_CONTROLS /* | B_WILL_ACCEPT_FIRST_CLICK */), 149 fCreationTime(system_time()), 150 fInfoWin(NULL), 151 fPlaylistWindow(NULL), 152 fHasFile(false), 153 fHasVideo(false), 154 fHasAudio(false), 155 fPlaylist(new Playlist), 156 fPlaylistObserver(new PlaylistObserver(this)), 157 fController(new Controller), 158 fControllerObserver(new ControllerObserver(this, 159 OBSERVE_FILE_CHANGES | OBSERVE_TRACK_CHANGES 160 | OBSERVE_PLAYBACK_STATE_CHANGES | OBSERVE_POSITION_CHANGES 161 | OBSERVE_VOLUME_CHANGES)), 162 fIsFullscreen(false), 163 fAlwaysOnTop(false), 164 fNoInterface(false), 165 fSourceWidth(-1), 166 fSourceHeight(-1), 167 fWidthAspect(0), 168 fHeightAspect(0), 169 fSavedFrame(), 170 fNoVideoFrame(), 171 fMouseDownTracking(false), 172 fGlobalSettingsListener(this), 173 fInitialSeekPosition(0) 174 { 175 // Handle window position and size depending on whether this is the 176 // first window or not. Use the window size from the window that was 177 // last resized by the user. 178 static int pos = 0; 179 MoveBy(pos * 25, pos * 25); 180 pos = (pos + 1) % 15; 181 182 BRect frame = Settings::Default()->CurrentSettings() 183 .audioPlayerWindowFrame; 184 if (frame.IsValid()) { 185 if (isFirstWindow) { 186 if (message == NULL) { 187 MoveTo(frame.LeftTop()); 188 ResizeTo(frame.Width(), frame.Height()); 189 } else { 190 // Delay moving to the initial position, since we don't 191 // know if we will be playing audio at all. 192 message->AddRect("window frame", frame); 193 } 194 } 195 if (sNoVideoWidth == MIN_WIDTH) 196 sNoVideoWidth = frame.IntegerWidth(); 197 } else if (sNoVideoWidth > MIN_WIDTH) { 198 ResizeTo(sNoVideoWidth, Bounds().Height()); 199 } 200 fNoVideoWidth = sNoVideoWidth; 201 202 BRect rect = Bounds(); 203 204 // background 205 fBackground = new BView(rect, "background", B_FOLLOW_ALL, 206 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE); 207 fBackground->SetViewColor(0, 0, 0); 208 AddChild(fBackground); 209 210 // menu 211 fMenuBar = new BMenuBar(fBackground->Bounds(), "menu"); 212 _CreateMenu(); 213 fBackground->AddChild(fMenuBar); 214 fMenuBar->SetResizingMode(B_FOLLOW_NONE); 215 fMenuBar->ResizeToPreferred(); 216 fMenuBarWidth = (int)fMenuBar->Frame().Width() + 1; 217 fMenuBarHeight = (int)fMenuBar->Frame().Height() + 1; 218 219 // video view 220 rect = BRect(0, fMenuBarHeight, fBackground->Bounds().right, 221 fMenuBarHeight + 10); 222 fVideoView = new VideoView(rect, "video display", B_FOLLOW_NONE); 223 fBackground->AddChild(fVideoView); 224 225 // controls 226 rect = BRect(0, fMenuBarHeight + 11, fBackground->Bounds().right, 227 fBackground->Bounds().bottom); 228 fControls = new ControllerView(rect, fController, fPlaylist); 229 fBackground->AddChild(fControls); 230 fControls->ResizeToPreferred(); 231 fControlsHeight = (int)fControls->Frame().Height() + 1; 232 fControlsWidth = (int)fControls->Frame().Width() + 1; 233 fControls->SetResizingMode(B_FOLLOW_BOTTOM | B_FOLLOW_LEFT_RIGHT); 234 235 fPlaylist->AddListener(fPlaylistObserver); 236 fController->SetVideoView(fVideoView); 237 fController->AddListener(fControllerObserver); 238 PeakView* peakView = fControls->GetPeakView(); 239 peakView->SetPeakNotificationWhat(MSG_PEAK_NOTIFICATION); 240 fController->SetPeakListener(peakView); 241 242 _SetupWindow(); 243 244 // setup the playlist window now, we need to have it 245 // running for the undo/redo playlist editing 246 fPlaylistWindow = new PlaylistWindow(BRect(150, 150, 500, 600), fPlaylist, 247 fController); 248 fPlaylistWindow->Hide(); 249 fPlaylistWindow->Show(); 250 // this makes sure the window thread is running without 251 // showing the window just yet 252 253 Settings::Default()->AddListener(&fGlobalSettingsListener); 254 _AdoptGlobalSettings(); 255 256 AddShortcut('z', B_COMMAND_KEY, new BMessage(B_UNDO)); 257 AddShortcut('y', B_COMMAND_KEY, new BMessage(B_UNDO)); 258 AddShortcut('z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO)); 259 AddShortcut('y', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(B_REDO)); 260 261 Hide(); 262 Show(); 263 264 if (message != NULL) 265 PostMessage(message); 266 } 267 268 269 MainWin::~MainWin() 270 { 271 // printf("MainWin::~MainWin\n"); 272 273 Settings::Default()->RemoveListener(&fGlobalSettingsListener); 274 fPlaylist->RemoveListener(fPlaylistObserver); 275 fController->Lock(); 276 fController->RemoveListener(fControllerObserver); 277 fController->SetPeakListener(NULL); 278 fController->SetVideoTarget(NULL); 279 fController->Unlock(); 280 281 // give the views a chance to detach from any notifiers 282 // before we delete them 283 fBackground->RemoveSelf(); 284 delete fBackground; 285 286 if (fInfoWin && fInfoWin->Lock()) 287 fInfoWin->Quit(); 288 289 if (fPlaylistWindow && fPlaylistWindow->Lock()) 290 fPlaylistWindow->Quit(); 291 292 delete fPlaylist; 293 fPlaylist = NULL; 294 295 // quit the Controller looper thread 296 thread_id controllerThread = fController->Thread(); 297 fController->PostMessage(B_QUIT_REQUESTED); 298 status_t exitValue; 299 wait_for_thread(controllerThread, &exitValue); 300 } 301 302 303 // #pragma mark - 304 305 306 void 307 MainWin::FrameResized(float newWidth, float newHeight) 308 { 309 if (newWidth != Bounds().Width() || newHeight != Bounds().Height()) { 310 debugger("size wrong\n"); 311 } 312 313 bool noMenu = fNoInterface || fIsFullscreen; 314 bool noControls = fNoInterface || fIsFullscreen; 315 316 // printf("FrameResized enter: newWidth %.0f, newHeight %.0f\n", 317 // newWidth, newHeight); 318 319 if (!fHasVideo) 320 sNoVideoWidth = fNoVideoWidth = (int)newWidth; 321 322 int maxVideoWidth = int(newWidth) + 1; 323 int maxVideoHeight = int(newHeight) + 1 324 - (noMenu ? 0 : fMenuBarHeight) 325 - (noControls ? 0 : fControlsHeight); 326 327 ASSERT(maxVideoHeight >= 0); 328 329 int y = 0; 330 331 if (noMenu) { 332 if (!fMenuBar->IsHidden(fMenuBar)) 333 fMenuBar->Hide(); 334 } else { 335 fMenuBar->MoveTo(0, y); 336 fMenuBar->ResizeTo(newWidth, fMenuBarHeight - 1); 337 if (fMenuBar->IsHidden(fMenuBar)) 338 fMenuBar->Show(); 339 y += fMenuBarHeight; 340 } 341 342 if (maxVideoHeight == 0) { 343 if (!fVideoView->IsHidden(fVideoView)) 344 fVideoView->Hide(); 345 } else { 346 _ResizeVideoView(0, y, maxVideoWidth, maxVideoHeight); 347 if (fVideoView->IsHidden(fVideoView)) 348 fVideoView->Show(); 349 y += maxVideoHeight; 350 } 351 352 if (noControls) { 353 if (!fControls->IsHidden(fControls)) 354 fControls->Hide(); 355 } else { 356 fControls->MoveTo(0, y); 357 fControls->ResizeTo(newWidth, fControlsHeight - 1); 358 if (fControls->IsHidden(fControls)) 359 fControls->Show(); 360 // y += fControlsHeight; 361 } 362 363 // printf("FrameResized leave\n"); 364 } 365 366 367 void 368 MainWin::Zoom(BPoint /*position*/, float /*width*/, float /*height*/) 369 { 370 PostMessage(M_TOGGLE_FULLSCREEN); 371 } 372 373 374 void 375 MainWin::DispatchMessage(BMessage* msg, BHandler* handler) 376 { 377 if ((msg->what == B_MOUSE_DOWN) 378 && (handler == fBackground || handler == fVideoView 379 || handler == fControls)) 380 _MouseDown(msg, dynamic_cast<BView*>(handler)); 381 382 if ((msg->what == B_MOUSE_MOVED) 383 && (handler == fBackground || handler == fVideoView 384 || handler == fControls)) 385 _MouseMoved(msg, dynamic_cast<BView*>(handler)); 386 387 if ((msg->what == B_MOUSE_UP) 388 && (handler == fBackground || handler == fVideoView)) 389 _MouseUp(msg); 390 391 if ((msg->what == B_KEY_DOWN) 392 && (handler == fBackground || handler == fVideoView)) { 393 // special case for PrintScreen key 394 if (msg->FindInt32("key") == B_PRINT_KEY) { 395 fVideoView->OverlayScreenshotPrepare(); 396 BWindow::DispatchMessage(msg, handler); 397 fVideoView->OverlayScreenshotCleanup(); 398 return; 399 } 400 401 // every other key gets dispatched to our _KeyDown first 402 if (_KeyDown(msg)) { 403 // it got handled, don't pass it on 404 return; 405 } 406 } 407 408 BWindow::DispatchMessage(msg, handler); 409 } 410 411 412 void 413 MainWin::MessageReceived(BMessage* msg) 414 { 415 // msg->PrintToStream(); 416 switch (msg->what) { 417 case B_EXECUTE_PROPERTY: 418 case B_GET_PROPERTY: 419 case B_SET_PROPERTY: 420 { 421 BMessage reply(B_REPLY); 422 status_t result = B_BAD_SCRIPT_SYNTAX; 423 int32 index; 424 BMessage specifier; 425 int32 what; 426 const char* property; 427 428 if (msg->GetCurrentSpecifier(&index, &specifier, &what, 429 &property) != B_OK) { 430 return BWindow::MessageReceived(msg); 431 } 432 433 BPropertyInfo propertyInfo(sPropertyInfo); 434 switch (propertyInfo.FindMatch(msg, index, &specifier, what, 435 property)) { 436 case 0: 437 fControls->SkipForward(); 438 result = B_OK; 439 break; 440 441 case 1: 442 fControls->SkipBackward(); 443 result = B_OK; 444 break; 445 446 case 2: 447 fController->Play(); 448 result = B_OK; 449 break; 450 451 case 3: 452 fController->Stop(); 453 result = B_OK; 454 break; 455 456 case 4: 457 fController->Pause(); 458 result = B_OK; 459 break; 460 461 case 5: 462 fController->TogglePlaying(); 463 result = B_OK; 464 break; 465 466 case 6: 467 fController->ToggleMute(); 468 result = B_OK; 469 break; 470 471 case 7: 472 { 473 if (msg->what == B_GET_PROPERTY) { 474 result = reply.AddFloat("result", 475 fController->Volume()); 476 } else if (msg->what == B_SET_PROPERTY) { 477 float newVolume; 478 result = msg->FindFloat("data", &newVolume); 479 if (result == B_OK) 480 fController->SetVolume(newVolume); 481 } 482 break; 483 } 484 485 case 8: 486 { 487 if (msg->what == B_GET_PROPERTY) { 488 BAutolock _(fPlaylist); 489 const PlaylistItem* item = fController->Item(); 490 if (item == NULL) { 491 result = B_NO_INIT; 492 break; 493 } 494 495 result = reply.AddString("result", item->LocationURI()); 496 } 497 break; 498 } 499 500 default: 501 return BWindow::MessageReceived(msg); 502 } 503 504 if (result != B_OK) { 505 reply.what = B_MESSAGE_NOT_UNDERSTOOD; 506 reply.AddString("message", strerror(result)); 507 reply.AddInt32("error", result); 508 } 509 510 msg->SendReply(&reply); 511 break; 512 } 513 514 case B_REFS_RECEIVED: 515 printf("MainWin::MessageReceived: B_REFS_RECEIVED\n"); 516 _RefsReceived(msg); 517 break; 518 case B_SIMPLE_DATA: 519 printf("MainWin::MessageReceived: B_SIMPLE_DATA\n"); 520 if (msg->HasRef("refs")) { 521 // add to recent documents as it's not done with drag-n-drop 522 entry_ref ref; 523 for (int32 i = 0; msg->FindRef("refs", i, &ref) == B_OK; i++) { 524 be_roster->AddToRecentDocuments(&ref, kAppSig); 525 } 526 _RefsReceived(msg); 527 } 528 break; 529 case M_OPEN_PREVIOUS_PLAYLIST: 530 OpenPlaylist(msg); 531 break; 532 533 case B_UNDO: 534 case B_REDO: 535 fPlaylistWindow->PostMessage(msg); 536 break; 537 538 case M_MEDIA_SERVER_STARTED: 539 { 540 printf("TODO: implement M_MEDIA_SERVER_STARTED\n"); 541 // 542 // BAutolock _(fPlaylist); 543 // BMessage fakePlaylistMessage(MSG_PLAYLIST_CURRENT_ITEM_CHANGED); 544 // fakePlaylistMessage.AddInt32("index", 545 // fPlaylist->CurrentItemIndex()); 546 // PostMessage(&fakePlaylistMessage); 547 break; 548 } 549 550 case M_MEDIA_SERVER_QUIT: 551 printf("TODO: implement M_MEDIA_SERVER_QUIT\n"); 552 // if (fController->Lock()) { 553 // fController->CleanupNodes(); 554 // fController->Unlock(); 555 // } 556 break; 557 558 // PlaylistObserver messages 559 case MSG_PLAYLIST_ITEM_ADDED: 560 { 561 PlaylistItem* item; 562 int32 index; 563 if (msg->FindPointer("item", (void**)&item) == B_OK 564 && msg->FindInt32("index", &index) == B_OK) { 565 _AddPlaylistItem(item, index); 566 } 567 break; 568 } 569 case MSG_PLAYLIST_ITEM_REMOVED: 570 { 571 int32 index; 572 if (msg->FindInt32("index", &index) == B_OK) 573 _RemovePlaylistItem(index); 574 break; 575 } 576 case MSG_PLAYLIST_CURRENT_ITEM_CHANGED: 577 { 578 BAutolock _(fPlaylist); 579 580 int32 index; 581 if (msg->FindInt32("index", &index) < B_OK 582 || index != fPlaylist->CurrentItemIndex()) 583 break; 584 PlaylistItemRef item(fPlaylist->ItemAt(index)); 585 if (item.Get() != NULL) { 586 printf("open playlist item: %s\n", item->Name().String()); 587 OpenPlaylistItem(item); 588 _MarkPlaylistItem(index); 589 } 590 break; 591 } 592 593 // ControllerObserver messages 594 case MSG_CONTROLLER_FILE_FINISHED: 595 { 596 BAutolock _(fPlaylist); 597 598 bool hadNext = fPlaylist->SetCurrentItemIndex( 599 fPlaylist->CurrentItemIndex() + 1); 600 if (!hadNext) { 601 if ((fHasVideo && fCloseWhenDonePlayingMovie) 602 || (!fHasVideo && fCloseWhenDonePlayingSound)) 603 PostMessage(B_QUIT_REQUESTED); 604 } 605 break; 606 } 607 case MSG_CONTROLLER_FILE_CHANGED: 608 { 609 status_t result = B_ERROR; 610 msg->FindInt32("result", &result); 611 PlaylistItemRef itemRef; 612 PlaylistItem* item; 613 if (msg->FindPointer("item", (void**)&item) == B_OK) { 614 itemRef.SetTo(item, true); 615 // The reference was passed along with the message. 616 } else { 617 BAutolock _(fPlaylist); 618 itemRef.SetTo(fPlaylist->ItemAt( 619 fPlaylist->CurrentItemIndex())); 620 } 621 _PlaylistItemOpened(itemRef, result); 622 break; 623 } 624 case MSG_CONTROLLER_VIDEO_TRACK_CHANGED: 625 { 626 int32 index; 627 if (msg->FindInt32("index", &index) == B_OK) { 628 int32 i = 0; 629 while (BMenuItem* item = fVideoTrackMenu->ItemAt(i)) { 630 item->SetMarked(i == index); 631 i++; 632 } 633 } 634 break; 635 } 636 case MSG_CONTROLLER_AUDIO_TRACK_CHANGED: 637 { 638 int32 index; 639 if (msg->FindInt32("index", &index) == B_OK) { 640 int32 i = 0; 641 while (BMenuItem* item = fAudioTrackMenu->ItemAt(i)) { 642 item->SetMarked(i == index); 643 i++; 644 } 645 } 646 break; 647 } 648 case MSG_CONTROLLER_PLAYBACK_STATE_CHANGED: 649 { 650 uint32 state; 651 if (msg->FindInt32("state", (int32*)&state) == B_OK) 652 fControls->SetPlaybackState(state); 653 break; 654 } 655 case MSG_CONTROLLER_POSITION_CHANGED: 656 { 657 float position; 658 if (msg->FindFloat("position", &position) == B_OK) { 659 fControls->SetPosition(position, fController->TimePosition(), 660 fController->TimeDuration()); 661 } 662 break; 663 } 664 case MSG_CONTROLLER_VOLUME_CHANGED: 665 { 666 float volume; 667 if (msg->FindFloat("volume", &volume) == B_OK) 668 fControls->SetVolume(volume); 669 break; 670 } 671 case MSG_CONTROLLER_MUTED_CHANGED: 672 { 673 bool muted; 674 if (msg->FindBool("muted", &muted) == B_OK) 675 fControls->SetMuted(muted); 676 break; 677 } 678 679 // menu item messages 680 case M_FILE_NEWPLAYER: 681 gMainApp->NewWindow(); 682 break; 683 case M_FILE_OPEN: 684 { 685 BMessenger target(this); 686 BMessage result(B_REFS_RECEIVED); 687 BMessage appMessage(M_SHOW_OPEN_PANEL); 688 appMessage.AddMessenger("target", target); 689 appMessage.AddMessage("message", &result); 690 appMessage.AddString("title", "Open Clips"); 691 appMessage.AddString("label", "Open"); 692 be_app->PostMessage(&appMessage); 693 break; 694 } 695 case M_FILE_INFO: 696 ShowFileInfo(); 697 break; 698 case M_FILE_PLAYLIST: 699 ShowPlaylistWindow(); 700 break; 701 case B_ABOUT_REQUESTED: 702 be_app->PostMessage(msg); 703 break; 704 case M_FILE_CLOSE: 705 PostMessage(B_QUIT_REQUESTED); 706 break; 707 case M_FILE_QUIT: 708 be_app->PostMessage(B_QUIT_REQUESTED); 709 break; 710 711 case M_TOGGLE_FULLSCREEN: 712 _ToggleFullscreen(); 713 break; 714 715 case M_TOGGLE_ALWAYS_ON_TOP: 716 _ToggleAlwaysOnTop(); 717 break; 718 719 case M_TOGGLE_NO_INTERFACE: 720 _ToggleNoInterface(); 721 break; 722 723 case M_VIEW_SIZE: 724 { 725 int32 size; 726 if (msg->FindInt32("size", &size) == B_OK) { 727 if (!fHasVideo) 728 break; 729 if (fIsFullscreen) 730 _ToggleFullscreen(); 731 _ResizeWindow(size); 732 } 733 break; 734 } 735 736 /* 737 case B_ACQUIRE_OVERLAY_LOCK: 738 printf("B_ACQUIRE_OVERLAY_LOCK\n"); 739 fVideoView->OverlayLockAcquire(); 740 break; 741 742 case B_RELEASE_OVERLAY_LOCK: 743 printf("B_RELEASE_OVERLAY_LOCK\n"); 744 fVideoView->OverlayLockRelease(); 745 break; 746 */ 747 case B_MOUSE_WHEEL_CHANGED: 748 { 749 float dx = msg->FindFloat("be:wheel_delta_x"); 750 float dy = msg->FindFloat("be:wheel_delta_y"); 751 bool inv = modifiers() & B_COMMAND_KEY; 752 if (dx > 0.1) PostMessage(inv ? M_VOLUME_DOWN : M_SKIP_PREV); 753 if (dx < -0.1) PostMessage(inv ? M_VOLUME_UP : M_SKIP_NEXT); 754 if (dy > 0.1) PostMessage(inv ? M_SKIP_PREV : M_VOLUME_DOWN); 755 if (dy < -0.1) PostMessage(inv ? M_SKIP_NEXT : M_VOLUME_UP); 756 break; 757 } 758 759 case M_SKIP_NEXT: 760 fControls->SkipForward(); 761 break; 762 763 case M_SKIP_PREV: 764 fControls->SkipBackward(); 765 break; 766 767 case M_VOLUME_UP: 768 fController->VolumeUp(); 769 break; 770 771 case M_VOLUME_DOWN: 772 fController->VolumeDown(); 773 break; 774 775 case M_ASPECT_SAME_AS_SOURCE: 776 if (fHasVideo) { 777 int width; 778 int height; 779 int widthAspect; 780 int heightAspect; 781 fController->GetSize(&width, &height, 782 &widthAspect, &heightAspect); 783 VideoFormatChange(width, height, widthAspect, heightAspect); 784 } 785 break; 786 787 case M_ASPECT_NO_DISTORTION: 788 if (fHasVideo) { 789 int width; 790 int height; 791 fController->GetSize(&width, &height); 792 VideoFormatChange(width, height, width, height); 793 } 794 break; 795 796 case M_ASPECT_4_3: 797 VideoAspectChange(4, 3); 798 break; 799 800 case M_ASPECT_16_9: // 1.77 : 1 801 VideoAspectChange(16, 9); 802 break; 803 804 case M_ASPECT_83_50: // 1.66 : 1 805 VideoAspectChange(83, 50); 806 break; 807 808 case M_ASPECT_7_4: // 1.75 : 1 809 VideoAspectChange(7, 4); 810 break; 811 812 case M_ASPECT_37_20: // 1.85 : 1 813 VideoAspectChange(37, 20); 814 break; 815 816 case M_ASPECT_47_20: // 2.35 : 1 817 VideoAspectChange(47, 20); 818 break; 819 820 case M_SET_PLAYLIST_POSITION: 821 { 822 BAutolock _(fPlaylist); 823 824 int32 index; 825 if (msg->FindInt32("index", &index) == B_OK) 826 fPlaylist->SetCurrentItemIndex(index); 827 break; 828 } 829 830 case MSG_OBJECT_CHANGED: 831 // received from fGlobalSettingsListener 832 // TODO: find out which object, if we ever watch more than 833 // the global settings instance... 834 _AdoptGlobalSettings(); 835 break; 836 837 case M_SHOW_IF_NEEDED: 838 _ShowIfNeeded(); 839 break; 840 841 default: 842 if (msg->what >= M_SELECT_AUDIO_TRACK 843 && msg->what <= M_SELECT_AUDIO_TRACK_END) { 844 fController->SelectAudioTrack(msg->what - M_SELECT_AUDIO_TRACK); 845 break; 846 } 847 if (msg->what >= M_SELECT_VIDEO_TRACK 848 && msg->what <= M_SELECT_VIDEO_TRACK_END) { 849 fController->SelectVideoTrack(msg->what - M_SELECT_VIDEO_TRACK); 850 break; 851 } 852 // let BWindow handle the rest 853 BWindow::MessageReceived(msg); 854 } 855 } 856 857 858 void 859 MainWin::WindowActivated(bool active) 860 { 861 fController->PlayerActivated(active); 862 } 863 864 865 bool 866 MainWin::QuitRequested() 867 { 868 BMessage message(M_PLAYER_QUIT); 869 GetQuitMessage(&message); 870 be_app->PostMessage(&message); 871 return true; 872 } 873 874 875 void 876 MainWin::MenusBeginning() 877 { 878 _SetupVideoAspectItems(fVideoAspectMenu); 879 } 880 881 882 // #pragma mark - 883 884 885 void 886 MainWin::OpenPlaylist(const BMessage* playlistArchive) 887 { 888 if (playlistArchive == NULL) 889 return; 890 891 BAutolock _(this); 892 BAutolock playlistLocker(fPlaylist); 893 894 if (fPlaylist->Unarchive(playlistArchive) != B_OK) 895 return; 896 897 int32 currentIndex; 898 if (playlistArchive->FindInt32("index", ¤tIndex) != B_OK) 899 currentIndex = 0; 900 fPlaylist->SetCurrentItemIndex(currentIndex); 901 902 playlistLocker.Unlock(); 903 904 if (currentIndex != -1) { 905 // Restore the current play position only if we have something to play 906 playlistArchive->FindInt64("position", (int64*)&fInitialSeekPosition); 907 } 908 909 if (IsHidden()) 910 Show(); 911 } 912 913 914 void 915 MainWin::OpenPlaylistItem(const PlaylistItemRef& item) 916 { 917 status_t ret = fController->SetToAsync(item); 918 if (ret != B_OK) { 919 fprintf(stderr, "MainWin::OpenPlaylistItem() - Failed to send message " 920 "to Controller.\n"); 921 (new BAlert("error", NAME" encountered an internal error. " 922 "The file could not be opened.", "OK"))->Go(); 923 _PlaylistItemOpened(item, ret); 924 } else { 925 BString string; 926 string << "Opening '" << item->Name() << "'."; 927 fControls->SetDisabledString(string.String()); 928 929 if (IsHidden()) { 930 BMessage showMessage(M_SHOW_IF_NEEDED); 931 BMessageRunner::StartSending(BMessenger(this), &showMessage, 932 150000, 1); 933 } 934 } 935 } 936 937 938 void 939 MainWin::ShowFileInfo() 940 { 941 if (!fInfoWin) 942 fInfoWin = new InfoWin(Frame().LeftTop(), fController); 943 944 if (fInfoWin->Lock()) { 945 if (fInfoWin->IsHidden()) 946 fInfoWin->Show(); 947 else 948 fInfoWin->Activate(); 949 fInfoWin->Unlock(); 950 } 951 } 952 953 954 void 955 MainWin::ShowPlaylistWindow() 956 { 957 if (fPlaylistWindow->Lock()) { 958 // make sure the window shows on the same workspace as ourself 959 uint32 workspaces = Workspaces(); 960 if (fPlaylistWindow->Workspaces() != workspaces) 961 fPlaylistWindow->SetWorkspaces(workspaces); 962 963 // show or activate 964 if (fPlaylistWindow->IsHidden()) 965 fPlaylistWindow->Show(); 966 else 967 fPlaylistWindow->Activate(); 968 969 fPlaylistWindow->Unlock(); 970 } 971 } 972 973 974 void 975 MainWin::VideoAspectChange(int forcedWidth, int forcedHeight, float widthScale) 976 { 977 // Force specific source size and pixel width scale. 978 if (fHasVideo) { 979 int width; 980 int height; 981 fController->GetSize(&width, &height); 982 VideoFormatChange(forcedWidth, forcedHeight, 983 lround(width * widthScale), height); 984 } 985 } 986 987 988 void 989 MainWin::VideoAspectChange(float widthScale) 990 { 991 // Called when video aspect ratio changes and the original 992 // width/height should be restored too, display aspect is not known, 993 // only pixel width scale. 994 if (fHasVideo) { 995 int width; 996 int height; 997 fController->GetSize(&width, &height); 998 VideoFormatChange(width, height, lround(width * widthScale), height); 999 } 1000 } 1001 1002 1003 void 1004 MainWin::VideoAspectChange(int widthAspect, int heightAspect) 1005 { 1006 // Called when video aspect ratio changes and the original 1007 // width/height should be restored too. 1008 if (fHasVideo) { 1009 int width; 1010 int height; 1011 fController->GetSize(&width, &height); 1012 VideoFormatChange(width, height, widthAspect, heightAspect); 1013 } 1014 } 1015 1016 1017 void 1018 MainWin::VideoFormatChange(int width, int height, int widthAspect, 1019 int heightAspect) 1020 { 1021 // Called when video format or aspect ratio changes. 1022 1023 printf("VideoFormatChange enter: width %d, height %d, " 1024 "aspect ratio: %d:%d\n", width, height, widthAspect, heightAspect); 1025 1026 // remember current view scale 1027 int percent = _CurrentVideoSizeInPercent(); 1028 1029 fSourceWidth = width; 1030 fSourceHeight = height; 1031 fWidthAspect = widthAspect; 1032 fHeightAspect = heightAspect; 1033 1034 if (percent == 100) 1035 _ResizeWindow(100); 1036 else 1037 FrameResized(Bounds().Width(), Bounds().Height()); 1038 1039 printf("VideoFormatChange leave\n"); 1040 } 1041 1042 1043 void 1044 MainWin::GetQuitMessage(BMessage* message) 1045 { 1046 message->AddPointer("instance", this); 1047 message->AddRect("window frame", Frame()); 1048 message->AddBool("audio only", !fHasVideo); 1049 message->AddInt64("creation time", fCreationTime); 1050 1051 if (!fHasVideo && fHasAudio) { 1052 // store playlist, current index and position if this is audio 1053 BMessage playlistArchive; 1054 1055 BAutolock controllerLocker(fController); 1056 playlistArchive.AddInt64("position", fController->TimePosition()); 1057 controllerLocker.Unlock(); 1058 1059 if (!fPlaylist) 1060 return; 1061 1062 BAutolock playlistLocker(fPlaylist); 1063 if (fPlaylist->Archive(&playlistArchive) != B_OK 1064 || playlistArchive.AddInt32("index", 1065 fPlaylist->CurrentItemIndex()) != B_OK 1066 || message->AddMessage("playlist", &playlistArchive) != B_OK) { 1067 fprintf(stderr, "Failed to store current playlist.\n"); 1068 } 1069 } 1070 } 1071 1072 1073 BHandler* 1074 MainWin::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier, 1075 int32 what, const char* property) 1076 { 1077 BPropertyInfo propertyInfo(sPropertyInfo); 1078 switch (propertyInfo.FindMatch(message, index, specifier, what, property)) { 1079 case 0: 1080 case 1: 1081 case 2: 1082 case 3: 1083 case 4: 1084 case 5: 1085 case 6: 1086 case 7: 1087 case 8: 1088 return this; 1089 } 1090 1091 return BWindow::ResolveSpecifier(message, index, specifier, what, property); 1092 } 1093 1094 1095 status_t 1096 MainWin::GetSupportedSuites(BMessage* data) 1097 { 1098 if (data == NULL) 1099 return B_BAD_VALUE; 1100 1101 status_t status = data->AddString("suites", "suite/vnd.Haiku-MediaPlayer"); 1102 if (status != B_OK) 1103 return status; 1104 1105 BPropertyInfo propertyInfo(sPropertyInfo); 1106 status = data->AddFlat("messages", &propertyInfo); 1107 if (status != B_OK) 1108 return status; 1109 1110 return BWindow::GetSupportedSuites(data); 1111 } 1112 1113 1114 // #pragma mark - 1115 1116 1117 void 1118 MainWin::_RefsReceived(BMessage* message) 1119 { 1120 // the playlist is replaced by dropped files 1121 // or the dropped files are appended to the end 1122 // of the existing playlist if <shift> is pressed 1123 bool append = false; 1124 if (message->FindBool("append to playlist", &append) != B_OK) 1125 append = modifiers() & B_SHIFT_KEY; 1126 1127 BAutolock _(fPlaylist); 1128 int32 appendIndex = append ? APPEND_INDEX_APPEND_LAST 1129 : APPEND_INDEX_REPLACE_PLAYLIST; 1130 message->AddInt32("append_index", appendIndex); 1131 1132 // forward the message to the playlist window, 1133 // so that undo/redo is used for modifying the playlist 1134 fPlaylistWindow->PostMessage(message); 1135 1136 if (message->FindRect("window frame", &fNoVideoFrame) != B_OK) { 1137 fNoVideoFrame = BRect(); 1138 _ShowIfNeeded(); 1139 } 1140 } 1141 1142 1143 void 1144 MainWin::_PlaylistItemOpened(const PlaylistItemRef& item, status_t result) 1145 { 1146 if (result != B_OK) { 1147 BAutolock _(fPlaylist); 1148 1149 item->SetPlaybackFailed(); 1150 bool allItemsFailed = true; 1151 int32 count = fPlaylist->CountItems(); 1152 for (int32 i = 0; i < count; i++) { 1153 if (!fPlaylist->ItemAtFast(i)->PlaybackFailed()) { 1154 allItemsFailed = false; 1155 break; 1156 } 1157 } 1158 1159 if (allItemsFailed) { 1160 // Display error if all files failed to play. 1161 BString message; 1162 message << "The file '"; 1163 message << item->Name(); 1164 message << "' could not be opened.\n\n"; 1165 1166 if (result == B_MEDIA_NO_HANDLER) { 1167 // give a more detailed message for the most likely of all 1168 // errors 1169 message << "There is no decoder installed to handle the " 1170 "file format, or the decoder has trouble with the " 1171 "specific version of the format."; 1172 } else { 1173 message << "Error: " << strerror(result); 1174 } 1175 (new BAlert("error", message.String(), "OK"))->Go(); 1176 } else { 1177 // Just go to the next file and don't bother user (yet) 1178 fPlaylist->SetCurrentItemIndex(fPlaylist->CurrentItemIndex() + 1); 1179 } 1180 1181 fHasFile = false; 1182 fHasVideo = false; 1183 fHasAudio = false; 1184 SetTitle(NAME); 1185 } else { 1186 fHasFile = true; 1187 fHasVideo = fController->VideoTrackCount() != 0; 1188 fHasAudio = fController->AudioTrackCount() != 0; 1189 SetTitle(item->Name().String()); 1190 1191 fController->SetTimePosition(fInitialSeekPosition); 1192 fInitialSeekPosition = 0; 1193 } 1194 _SetupWindow(); 1195 1196 if (result == B_OK) 1197 _SetFileAttributes(); 1198 } 1199 1200 1201 void 1202 MainWin::_SetupWindow() 1203 { 1204 // printf("MainWin::_SetupWindow\n"); 1205 // Populate the track menus 1206 _SetupTrackMenus(fAudioTrackMenu, fVideoTrackMenu); 1207 1208 fVideoMenu->SetEnabled(fHasVideo); 1209 fAudioMenu->SetEnabled(fHasAudio); 1210 int previousSourceWidth = fSourceWidth; 1211 int previousSourceHeight = fSourceHeight; 1212 int previousWidthAspect = fWidthAspect; 1213 int previousHeightAspect = fHeightAspect; 1214 if (fHasVideo) { 1215 fController->GetSize(&fSourceWidth, &fSourceHeight, 1216 &fWidthAspect, &fHeightAspect); 1217 } else { 1218 fSourceWidth = 0; 1219 fSourceHeight = 0; 1220 fWidthAspect = 1; 1221 fHeightAspect = 1; 1222 } 1223 _UpdateControlsEnabledStatus(); 1224 1225 if (!fHasVideo && fNoVideoFrame.IsValid()) { 1226 MoveTo(fNoVideoFrame.LeftTop()); 1227 ResizeTo(fNoVideoFrame.Width(), fNoVideoFrame.Height()); 1228 } 1229 fNoVideoFrame = BRect(); 1230 _ShowIfNeeded(); 1231 1232 // Adopt the size and window layout if necessary 1233 if (previousSourceWidth != fSourceWidth 1234 || previousSourceHeight != fSourceHeight 1235 || previousWidthAspect != fWidthAspect 1236 || previousHeightAspect != fHeightAspect) { 1237 1238 _SetWindowSizeLimits(); 1239 1240 if (!fIsFullscreen) { 1241 // Resize to 100% but stay on screen 1242 _ResizeWindow(100, !fHasVideo, true); 1243 } else { 1244 // Make sure we relayout the video view when in full screen mode 1245 FrameResized(Frame().Width(), Frame().Height()); 1246 } 1247 } 1248 1249 fVideoView->MakeFocus(); 1250 } 1251 1252 1253 void 1254 MainWin::_CreateMenu() 1255 { 1256 fFileMenu = new BMenu(NAME); 1257 fPlaylistMenu = new BMenu("Playlist"B_UTF8_ELLIPSIS); 1258 fAudioMenu = new BMenu("Audio"); 1259 fVideoMenu = new BMenu("Video"); 1260 fVideoAspectMenu = new BMenu("Aspect ratio"); 1261 fSettingsMenu = new BMenu("Settings"); 1262 fAudioTrackMenu = new BMenu("Track"); 1263 fVideoTrackMenu = new BMenu("Track"); 1264 1265 fMenuBar->AddItem(fFileMenu); 1266 fMenuBar->AddItem(fAudioMenu); 1267 fMenuBar->AddItem(fVideoMenu); 1268 fMenuBar->AddItem(fSettingsMenu); 1269 1270 fFileMenu->AddItem(new BMenuItem("New player"B_UTF8_ELLIPSIS, 1271 new BMessage(M_FILE_NEWPLAYER), 'N')); 1272 fFileMenu->AddSeparatorItem(); 1273 1274 // fFileMenu->AddItem(new BMenuItem("Open File"B_UTF8_ELLIPSIS, 1275 // new BMessage(M_FILE_OPEN), 'O')); 1276 // Add recent files 1277 BRecentFilesList recentFiles(10, false, NULL, kAppSig); 1278 BMenuItem* item = new BMenuItem(recentFiles.NewFileListMenu( 1279 "Open file"B_UTF8_ELLIPSIS, new BMessage(B_REFS_RECEIVED), 1280 NULL, this, 10, false, NULL, 0, kAppSig), new BMessage(M_FILE_OPEN)); 1281 item->SetShortcut('O', 0); 1282 fFileMenu->AddItem(item); 1283 1284 fFileMenu->AddItem(new BMenuItem("File info"B_UTF8_ELLIPSIS, 1285 new BMessage(M_FILE_INFO), 'I')); 1286 fFileMenu->AddItem(fPlaylistMenu); 1287 fPlaylistMenu->Superitem()->SetShortcut('P', B_COMMAND_KEY); 1288 fPlaylistMenu->Superitem()->SetMessage(new BMessage(M_FILE_PLAYLIST)); 1289 1290 fFileMenu->AddSeparatorItem(); 1291 fFileMenu->AddItem(new BMenuItem("About " NAME B_UTF8_ELLIPSIS, 1292 new BMessage(B_ABOUT_REQUESTED))); 1293 fFileMenu->AddSeparatorItem(); 1294 fFileMenu->AddItem(new BMenuItem("Close", new BMessage(M_FILE_CLOSE), 'W')); 1295 fFileMenu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q')); 1296 1297 fPlaylistMenu->SetRadioMode(true); 1298 1299 fAudioMenu->AddItem(fAudioTrackMenu); 1300 1301 fVideoMenu->AddItem(fVideoTrackMenu); 1302 fVideoMenu->AddSeparatorItem(); 1303 BMessage* resizeMessage = new BMessage(M_VIEW_SIZE); 1304 resizeMessage->AddInt32("size", 50); 1305 fVideoMenu->AddItem(new BMenuItem("50% scale", resizeMessage, '0')); 1306 1307 resizeMessage = new BMessage(M_VIEW_SIZE); 1308 resizeMessage->AddInt32("size", 100); 1309 fVideoMenu->AddItem(new BMenuItem("100% scale", resizeMessage, '1')); 1310 1311 resizeMessage = new BMessage(M_VIEW_SIZE); 1312 resizeMessage->AddInt32("size", 200); 1313 fVideoMenu->AddItem(new BMenuItem("200% scale", resizeMessage, '2')); 1314 1315 resizeMessage = new BMessage(M_VIEW_SIZE); 1316 resizeMessage->AddInt32("size", 300); 1317 fVideoMenu->AddItem(new BMenuItem("300% scale", resizeMessage, '3')); 1318 1319 resizeMessage = new BMessage(M_VIEW_SIZE); 1320 resizeMessage->AddInt32("size", 400); 1321 fVideoMenu->AddItem(new BMenuItem("400% scale", resizeMessage, '4')); 1322 1323 fVideoMenu->AddSeparatorItem(); 1324 1325 fVideoMenu->AddItem(new BMenuItem("Full screen", 1326 new BMessage(M_TOGGLE_FULLSCREEN), 'F')); 1327 1328 fVideoMenu->AddSeparatorItem(); 1329 1330 _SetupVideoAspectItems(fVideoAspectMenu); 1331 fVideoMenu->AddItem(fVideoAspectMenu); 1332 1333 fNoInterfaceMenuItem = new BMenuItem("No interface", 1334 new BMessage(M_TOGGLE_NO_INTERFACE), 'B'); 1335 fSettingsMenu->AddItem(fNoInterfaceMenuItem); 1336 fSettingsMenu->AddItem(new BMenuItem("Always on top", 1337 new BMessage(M_TOGGLE_ALWAYS_ON_TOP), 'T')); 1338 fSettingsMenu->AddSeparatorItem(); 1339 item = new BMenuItem("Settings"B_UTF8_ELLIPSIS, 1340 new BMessage(M_SETTINGS), 'S'); 1341 fSettingsMenu->AddItem(item); 1342 item->SetTarget(be_app); 1343 } 1344 1345 1346 void 1347 MainWin::_SetupVideoAspectItems(BMenu* menu) 1348 { 1349 BMenuItem* item; 1350 while ((item = menu->RemoveItem(0L)) != NULL) 1351 delete item; 1352 1353 int width; 1354 int height; 1355 int widthAspect; 1356 int heightAspect; 1357 fController->GetSize(&width, &height, &widthAspect, &heightAspect); 1358 // We don't care if there is a video track at all. In that 1359 // case we should end up not marking any item. 1360 1361 // NOTE: The item marking may end up marking for example both 1362 // "Stream Settings" and "16 : 9" if the stream settings happen to 1363 // be "16 : 9". 1364 1365 menu->AddItem(item = new BMenuItem("Stream settings", 1366 new BMessage(M_ASPECT_SAME_AS_SOURCE))); 1367 item->SetMarked(widthAspect == fWidthAspect 1368 && heightAspect == fHeightAspect); 1369 1370 menu->AddItem(item = new BMenuItem("No aspect correction", 1371 new BMessage(M_ASPECT_NO_DISTORTION))); 1372 item->SetMarked(width == fWidthAspect && height == fHeightAspect); 1373 1374 menu->AddSeparatorItem(); 1375 1376 menu->AddItem(item = new BMenuItem("4 : 3", 1377 new BMessage(M_ASPECT_4_3))); 1378 item->SetMarked(fWidthAspect == 4 && fHeightAspect == 3); 1379 menu->AddItem(item = new BMenuItem("16 : 9", 1380 new BMessage(M_ASPECT_16_9))); 1381 item->SetMarked(fWidthAspect == 16 && fHeightAspect == 9); 1382 1383 menu->AddSeparatorItem(); 1384 1385 menu->AddItem(item = new BMenuItem("1.66 : 1", 1386 new BMessage(M_ASPECT_83_50))); 1387 item->SetMarked(fWidthAspect == 83 && fHeightAspect == 50); 1388 menu->AddItem(item = new BMenuItem("1.75 : 1", 1389 new BMessage(M_ASPECT_7_4))); 1390 item->SetMarked(fWidthAspect == 7 && fHeightAspect == 4); 1391 menu->AddItem(item = new BMenuItem("1.85 : 1 (American)", 1392 new BMessage(M_ASPECT_37_20))); 1393 item->SetMarked(fWidthAspect == 37 && fHeightAspect == 20); 1394 menu->AddItem(item = new BMenuItem("2.35 : 1 (Cinemascope)", 1395 new BMessage(M_ASPECT_47_20))); 1396 item->SetMarked(fWidthAspect == 47 && fHeightAspect == 20); 1397 } 1398 1399 1400 void 1401 MainWin::_SetupTrackMenus(BMenu* audioTrackMenu, BMenu* videoTrackMenu) 1402 { 1403 audioTrackMenu->RemoveItems(0, audioTrackMenu->CountItems(), true); 1404 videoTrackMenu->RemoveItems(0, videoTrackMenu->CountItems(), true); 1405 1406 char s[100]; 1407 1408 int count = fController->AudioTrackCount(); 1409 int current = fController->CurrentAudioTrack(); 1410 for (int i = 0; i < count; i++) { 1411 sprintf(s, "Track %d", i + 1); 1412 BMenuItem* item = new BMenuItem(s, 1413 new BMessage(M_SELECT_AUDIO_TRACK + i)); 1414 item->SetMarked(i == current); 1415 audioTrackMenu->AddItem(item); 1416 } 1417 if (!count) { 1418 audioTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); 1419 audioTrackMenu->ItemAt(0)->SetMarked(true); 1420 } 1421 audioTrackMenu->SetEnabled(count > 1); 1422 1423 1424 count = fController->VideoTrackCount(); 1425 current = fController->CurrentVideoTrack(); 1426 for (int i = 0; i < count; i++) { 1427 sprintf(s, "Track %d", i + 1); 1428 BMenuItem* item = new BMenuItem(s, 1429 new BMessage(M_SELECT_VIDEO_TRACK + i)); 1430 item->SetMarked(i == current); 1431 videoTrackMenu->AddItem(item); 1432 } 1433 if (!count) { 1434 videoTrackMenu->AddItem(new BMenuItem("none", new BMessage(M_DUMMY))); 1435 videoTrackMenu->ItemAt(0)->SetMarked(true); 1436 } 1437 videoTrackMenu->SetEnabled(count > 1); 1438 } 1439 1440 1441 void 1442 MainWin::_GetMinimumWindowSize(int& width, int& height) const 1443 { 1444 width = MIN_WIDTH; 1445 height = 0; 1446 if (!fNoInterface) { 1447 width = max_c(width, fMenuBarWidth); 1448 width = max_c(width, fControlsWidth); 1449 height = fMenuBarHeight + fControlsHeight; 1450 } 1451 } 1452 1453 1454 void 1455 MainWin::_GetUnscaledVideoSize(int& videoWidth, int& videoHeight) const 1456 { 1457 if (fWidthAspect != 0 && fHeightAspect != 0) { 1458 videoWidth = fSourceHeight * fWidthAspect / fHeightAspect; 1459 videoHeight = fSourceWidth * fHeightAspect / fWidthAspect; 1460 // Use the scaling which produces an enlarged view. 1461 if (videoWidth > fSourceWidth) { 1462 // Enlarge width 1463 videoHeight = fSourceHeight; 1464 } else { 1465 // Enlarge height 1466 videoWidth = fSourceWidth; 1467 } 1468 } else { 1469 videoWidth = fSourceWidth; 1470 videoHeight = fSourceHeight; 1471 } 1472 } 1473 1474 1475 void 1476 MainWin::_SetWindowSizeLimits() 1477 { 1478 int minWidth; 1479 int minHeight; 1480 _GetMinimumWindowSize(minWidth, minHeight); 1481 SetSizeLimits(minWidth - 1, 32000, minHeight - 1, 1482 fHasVideo ? 32000 : minHeight - 1); 1483 } 1484 1485 1486 int 1487 MainWin::_CurrentVideoSizeInPercent() const 1488 { 1489 if (!fHasVideo) 1490 return 0; 1491 1492 int videoWidth; 1493 int videoHeight; 1494 _GetUnscaledVideoSize(videoWidth, videoHeight); 1495 1496 int viewWidth = fVideoView->Bounds().IntegerWidth() + 1; 1497 int viewHeight = fVideoView->Bounds().IntegerHeight() + 1; 1498 1499 int widthPercent = videoWidth * 100 / viewWidth; 1500 int heightPercent = videoHeight * 100 / viewHeight; 1501 1502 if (widthPercent > heightPercent) 1503 return widthPercent; 1504 return heightPercent; 1505 } 1506 1507 1508 void 1509 MainWin::_ResizeWindow(int percent, bool useNoVideoWidth, bool stayOnScreen) 1510 { 1511 // Get required window size 1512 int videoWidth; 1513 int videoHeight; 1514 _GetUnscaledVideoSize(videoWidth, videoHeight); 1515 1516 videoWidth = (videoWidth * percent) / 100; 1517 videoHeight = (videoHeight * percent) / 100; 1518 1519 // Calculate and set the minimum window size 1520 int width; 1521 int height; 1522 _GetMinimumWindowSize(width, height); 1523 1524 width = max_c(width, videoWidth) - 1; 1525 if (useNoVideoWidth) 1526 width = max_c(width, fNoVideoWidth); 1527 height = height + videoHeight - 1; 1528 1529 if (stayOnScreen) { 1530 BRect screenFrame(BScreen(this).Frame()); 1531 BRect frame(Frame()); 1532 BRect decoratorFrame(DecoratorFrame()); 1533 1534 // Shrink the screen frame by the window border size 1535 screenFrame.top += frame.top - decoratorFrame.top; 1536 screenFrame.left += frame.left - decoratorFrame.left; 1537 screenFrame.right += frame.right - decoratorFrame.right; 1538 screenFrame.bottom += frame.bottom - decoratorFrame.bottom; 1539 1540 // Update frame to what the new size would be 1541 frame.right = frame.left + width; 1542 frame.bottom = frame.top + height; 1543 1544 if (!screenFrame.Contains(frame)) { 1545 // Resize the window so it doesn't extend outside the current 1546 // screen frame. 1547 if (frame.Width() > screenFrame.Width() 1548 || frame.Height() > screenFrame.Height()) { 1549 // too large 1550 int widthDiff 1551 = frame.IntegerWidth() - screenFrame.IntegerWidth(); 1552 int heightDiff 1553 = frame.IntegerHeight() - screenFrame.IntegerHeight(); 1554 1555 float shrinkScale; 1556 if (widthDiff > heightDiff) 1557 shrinkScale = (float)(width - widthDiff) / width; 1558 else 1559 shrinkScale = (float)(height - heightDiff) / height; 1560 1561 // Resize width/height and center window 1562 width = lround(width * shrinkScale); 1563 height = lround(height * shrinkScale); 1564 MoveTo((screenFrame.left + screenFrame.right - width) / 2, 1565 (screenFrame.top + screenFrame.bottom - height) / 2); 1566 } else { 1567 // just off-screen on one or more sides 1568 int offsetX = 0; 1569 int offsetY = 0; 1570 if (frame.left < screenFrame.left) 1571 offsetX = (int)(screenFrame.left - frame.left); 1572 else if (frame.right > screenFrame.right) 1573 offsetX = (int)(screenFrame.right - frame.right); 1574 if (frame.top < screenFrame.top) 1575 offsetY = (int)(screenFrame.top - frame.top); 1576 else if (frame.bottom > screenFrame.bottom) 1577 offsetY = (int)(screenFrame.bottom - frame.bottom); 1578 MoveBy(offsetX, offsetY); 1579 } 1580 } 1581 } 1582 1583 ResizeTo(width, height); 1584 } 1585 1586 1587 void 1588 MainWin::_ResizeVideoView(int x, int y, int width, int height) 1589 { 1590 printf("_ResizeVideoView: %d,%d, width %d, height %d\n", x, y, 1591 width, height); 1592 1593 // Keep aspect ratio, place video view inside 1594 // the background area (may create black bars). 1595 int videoWidth; 1596 int videoHeight; 1597 _GetUnscaledVideoSize(videoWidth, videoHeight); 1598 float scaledWidth = videoWidth; 1599 float scaledHeight = videoHeight; 1600 float factor = min_c(width / scaledWidth, height / scaledHeight); 1601 int renderWidth = lround(scaledWidth * factor); 1602 int renderHeight = lround(scaledHeight * factor); 1603 if (renderWidth > width) 1604 renderWidth = width; 1605 if (renderHeight > height) 1606 renderHeight = height; 1607 1608 int xOffset = x + (width - renderWidth) / 2; 1609 int yOffset = y + (height - renderHeight) / 2; 1610 1611 fVideoView->MoveTo(xOffset, yOffset); 1612 fVideoView->ResizeTo(renderWidth - 1, renderHeight - 1); 1613 } 1614 1615 1616 // #pragma mark - 1617 1618 1619 void 1620 MainWin::_MouseDown(BMessage* msg, BView* originalHandler) 1621 { 1622 uint32 buttons = msg->FindInt32("buttons"); 1623 1624 // On Zeta, only "screen_where" is reliable, "where" and "be:view_where" 1625 // seem to be broken 1626 BPoint screenWhere; 1627 if (msg->FindPoint("screen_where", &screenWhere) != B_OK) { 1628 // TODO: remove 1629 // Workaround for BeOS R5, it has no "screen_where" 1630 if (!originalHandler || msg->FindPoint("where", &screenWhere) < B_OK) 1631 return; 1632 originalHandler->ConvertToScreen(&screenWhere); 1633 } 1634 1635 // double click handling 1636 1637 if (msg->FindInt32("clicks") % 2 == 0) { 1638 BRect rect(screenWhere.x - 1, screenWhere.y - 1, screenWhere.x + 1, 1639 screenWhere.y + 1); 1640 if (rect.Contains(fMouseDownMousePos)) { 1641 if (buttons == B_PRIMARY_MOUSE_BUTTON) 1642 PostMessage(M_TOGGLE_FULLSCREEN); 1643 else if (buttons == B_SECONDARY_MOUSE_BUTTON) 1644 PostMessage(M_TOGGLE_NO_INTERFACE); 1645 1646 return; 1647 } 1648 } 1649 1650 fMouseDownMousePos = screenWhere; 1651 fMouseDownWindowPos = Frame().LeftTop(); 1652 1653 if (buttons == B_PRIMARY_MOUSE_BUTTON && !fIsFullscreen) { 1654 // start mouse tracking 1655 fVideoView->SetMouseEventMask(B_POINTER_EVENTS | B_NO_POINTER_HISTORY 1656 /* | B_LOCK_WINDOW_FOCUS */); 1657 fMouseDownTracking = true; 1658 } 1659 1660 // pop up a context menu if right mouse button is down for 200 ms 1661 1662 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 1663 return; 1664 1665 bigtime_t start = system_time(); 1666 bigtime_t delay = 200000; 1667 BPoint location; 1668 do { 1669 fVideoView->GetMouse(&location, &buttons); 1670 if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) 1671 break; 1672 snooze(1000); 1673 } while (system_time() - start < delay); 1674 1675 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) 1676 _ShowContextMenu(screenWhere); 1677 } 1678 1679 1680 void 1681 MainWin::_MouseMoved(BMessage* msg, BView* originalHandler) 1682 { 1683 // msg->PrintToStream(); 1684 1685 BPoint mousePos; 1686 uint32 buttons = msg->FindInt32("buttons"); 1687 1688 if (buttons == B_PRIMARY_MOUSE_BUTTON && fMouseDownTracking 1689 && !fIsFullscreen) { 1690 // On Zeta, only "screen_where" is reliable, "where" 1691 // and "be:view_where" seem to be broken 1692 if (msg->FindPoint("screen_where", &mousePos) != B_OK) { 1693 // TODO: remove 1694 // Workaround for BeOS R5, it has no "screen_where" 1695 if (!originalHandler || msg->FindPoint("where", &mousePos) < B_OK) 1696 return; 1697 originalHandler->ConvertToScreen(&mousePos); 1698 } 1699 // printf("screen where: %.0f, %.0f => ", mousePos.x, mousePos.y); 1700 float delta_x = mousePos.x - fMouseDownMousePos.x; 1701 float delta_y = mousePos.y - fMouseDownMousePos.y; 1702 float x = fMouseDownWindowPos.x + delta_x; 1703 float y = fMouseDownWindowPos.y + delta_y; 1704 // printf("move window to %.0f, %.0f\n", x, y); 1705 MoveTo(x, y); 1706 } 1707 } 1708 1709 1710 void 1711 MainWin::_MouseUp(BMessage* msg) 1712 { 1713 fMouseDownTracking = false; 1714 } 1715 1716 1717 void 1718 MainWin::_ShowContextMenu(const BPoint& screenPoint) 1719 { 1720 printf("Show context menu\n"); 1721 BPopUpMenu* menu = new BPopUpMenu("context menu", false, false); 1722 BMenuItem* item; 1723 menu->AddItem(item = new BMenuItem("Full screen", 1724 new BMessage(M_TOGGLE_FULLSCREEN), 'F')); 1725 item->SetMarked(fIsFullscreen); 1726 item->SetEnabled(fHasVideo); 1727 1728 BMenu* aspectSubMenu = new BMenu("Aspect ratio"); 1729 _SetupVideoAspectItems(aspectSubMenu); 1730 aspectSubMenu->SetTargetForItems(this); 1731 menu->AddItem(item = new BMenuItem(aspectSubMenu)); 1732 item->SetEnabled(fHasVideo); 1733 1734 menu->AddItem(item = new BMenuItem("No interface", 1735 new BMessage(M_TOGGLE_NO_INTERFACE), 'B')); 1736 item->SetMarked(fNoInterface); 1737 item->SetEnabled(fHasVideo); 1738 1739 menu->AddSeparatorItem(); 1740 1741 // Add track selector menus 1742 BMenu* audioTrackMenu = new BMenu("Audio track"); 1743 BMenu* videoTrackMenu = new BMenu("Video track"); 1744 _SetupTrackMenus(audioTrackMenu, videoTrackMenu); 1745 1746 audioTrackMenu->SetTargetForItems(this); 1747 videoTrackMenu->SetTargetForItems(this); 1748 1749 menu->AddItem(item = new BMenuItem(audioTrackMenu)); 1750 item->SetEnabled(fHasAudio); 1751 1752 menu->AddItem(item = new BMenuItem(videoTrackMenu)); 1753 item->SetEnabled(fHasVideo); 1754 1755 menu->AddSeparatorItem(); 1756 1757 menu->AddItem(new BMenuItem("About " NAME B_UTF8_ELLIPSIS, 1758 new BMessage(B_ABOUT_REQUESTED))); 1759 menu->AddSeparatorItem(); 1760 menu->AddItem(new BMenuItem("Quit", new BMessage(M_FILE_QUIT), 'Q')); 1761 1762 menu->SetTargetForItems(this); 1763 BRect rect(screenPoint.x - 5, screenPoint.y - 5, screenPoint.x + 5, 1764 screenPoint.y + 5); 1765 menu->Go(screenPoint, true, true, rect, true); 1766 } 1767 1768 1769 /*! Trap keys that are about to be send to background or renderer view. 1770 Return true if it shouldn't be passed to the view. 1771 */ 1772 bool 1773 MainWin::_KeyDown(BMessage* msg) 1774 { 1775 // TODO: use the shortcut mechanism instead! 1776 1777 uint32 key = msg->FindInt32("key"); 1778 uint32 rawChar = msg->FindInt32("raw_char"); 1779 uint32 modifier = msg->FindInt32("modifiers"); 1780 1781 printf("key 0x%lx, rawChar 0x%lx, modifiers 0x%lx\n", key, rawChar, 1782 modifier); 1783 1784 // ignore the system modifier namespace 1785 if ((modifier & (B_CONTROL_KEY | B_COMMAND_KEY)) 1786 == (B_CONTROL_KEY | B_COMMAND_KEY)) 1787 return false; 1788 1789 switch (rawChar) { 1790 case B_SPACE: 1791 fController->TogglePlaying(); 1792 return true; 1793 1794 case B_ESCAPE: 1795 if (!fIsFullscreen) 1796 break; 1797 1798 PostMessage(M_TOGGLE_FULLSCREEN); 1799 return true; 1800 1801 case B_ENTER: // Enter / Return 1802 if ((modifier & B_COMMAND_KEY) != 0) { 1803 PostMessage(M_TOGGLE_FULLSCREEN); 1804 return true; 1805 } 1806 break; 1807 1808 case B_TAB: 1809 if ((modifier & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY 1810 | B_MENU_KEY)) == 0) { 1811 PostMessage(M_TOGGLE_FULLSCREEN); 1812 return true; 1813 } 1814 break; 1815 1816 case B_UP_ARROW: 1817 if ((modifier & B_COMMAND_KEY) != 0) 1818 PostMessage(M_SKIP_NEXT); 1819 else 1820 PostMessage(M_VOLUME_UP); 1821 return true; 1822 1823 case B_DOWN_ARROW: 1824 if ((modifier & B_COMMAND_KEY) != 0) 1825 PostMessage(M_SKIP_PREV); 1826 else 1827 PostMessage(M_VOLUME_DOWN); 1828 return true; 1829 1830 case B_RIGHT_ARROW: 1831 if ((modifier & B_COMMAND_KEY) != 0) 1832 PostMessage(M_VOLUME_UP); 1833 else 1834 PostMessage(M_SKIP_NEXT); 1835 return true; 1836 1837 case B_LEFT_ARROW: 1838 if ((modifier & B_COMMAND_KEY) != 0) 1839 PostMessage(M_VOLUME_DOWN); 1840 else 1841 PostMessage(M_SKIP_PREV); 1842 return true; 1843 1844 case B_PAGE_UP: 1845 PostMessage(M_SKIP_NEXT); 1846 return true; 1847 1848 case B_PAGE_DOWN: 1849 PostMessage(M_SKIP_PREV); 1850 return true; 1851 } 1852 1853 switch (key) { 1854 case 0x3a: // numeric keypad + 1855 if ((modifier & B_COMMAND_KEY) == 0) { 1856 PostMessage(M_VOLUME_UP); 1857 return true; 1858 } 1859 break; 1860 1861 case 0x25: // numeric keypad - 1862 if ((modifier & B_COMMAND_KEY) == 0) { 1863 PostMessage(M_VOLUME_DOWN); 1864 return true; 1865 } 1866 break; 1867 1868 case 0x38: // numeric keypad up arrow 1869 PostMessage(M_VOLUME_UP); 1870 return true; 1871 1872 case 0x59: // numeric keypad down arrow 1873 PostMessage(M_VOLUME_DOWN); 1874 return true; 1875 1876 case 0x39: // numeric keypad page up 1877 case 0x4a: // numeric keypad right arrow 1878 PostMessage(M_SKIP_NEXT); 1879 return true; 1880 1881 case 0x5a: // numeric keypad page down 1882 case 0x48: // numeric keypad left arrow 1883 PostMessage(M_SKIP_PREV); 1884 return true; 1885 1886 case 0x34: // delete button 1887 case 0x3e: // d for delete 1888 case 0x2b: // t for Trash 1889 if ((modifiers() & B_COMMAND_KEY) != 0) { 1890 BAutolock _(fPlaylist); 1891 BMessage removeMessage(M_PLAYLIST_REMOVE_AND_PUT_INTO_TRASH); 1892 removeMessage.AddInt32("playlist index", 1893 fPlaylist->CurrentItemIndex()); 1894 fPlaylistWindow->PostMessage(&removeMessage); 1895 return true; 1896 } 1897 break; 1898 } 1899 1900 return false; 1901 } 1902 1903 1904 // #pragma mark - 1905 1906 1907 void 1908 MainWin::_ToggleFullscreen() 1909 { 1910 printf("_ToggleFullscreen enter\n"); 1911 1912 if (!fHasVideo) { 1913 printf("_ToggleFullscreen - ignoring, as we don't have a video\n"); 1914 return; 1915 } 1916 1917 fIsFullscreen = !fIsFullscreen; 1918 1919 if (fIsFullscreen) { 1920 // switch to fullscreen 1921 1922 fSavedFrame = Frame(); 1923 printf("saving current frame: %d %d %d %d\n", int(fSavedFrame.left), 1924 int(fSavedFrame.top), int(fSavedFrame.right), 1925 int(fSavedFrame.bottom)); 1926 BScreen screen(this); 1927 BRect rect(screen.Frame()); 1928 1929 Hide(); 1930 MoveTo(rect.left, rect.top); 1931 ResizeTo(rect.Width(), rect.Height()); 1932 Show(); 1933 1934 } else { 1935 // switch back from full screen mode 1936 1937 Hide(); 1938 MoveTo(fSavedFrame.left, fSavedFrame.top); 1939 ResizeTo(fSavedFrame.Width(), fSavedFrame.Height()); 1940 Show(); 1941 } 1942 1943 fVideoView->SetFullscreen(fIsFullscreen); 1944 1945 _MarkItem(fSettingsMenu, M_TOGGLE_FULLSCREEN, fIsFullscreen); 1946 1947 printf("_ToggleFullscreen leave\n"); 1948 } 1949 1950 void 1951 MainWin::_ToggleAlwaysOnTop() 1952 { 1953 fAlwaysOnTop = !fAlwaysOnTop; 1954 SetFeel(fAlwaysOnTop ? B_FLOATING_ALL_WINDOW_FEEL : B_NORMAL_WINDOW_FEEL); 1955 1956 _MarkItem(fSettingsMenu, M_TOGGLE_ALWAYS_ON_TOP, fAlwaysOnTop); 1957 } 1958 1959 1960 void 1961 MainWin::_ToggleNoInterface() 1962 { 1963 printf("_ToggleNoInterface enter\n"); 1964 1965 if (fIsFullscreen || !fHasVideo) { 1966 // Fullscreen playback is always without interface and 1967 // audio playback is always with interface. So we ignore these 1968 // two states here. 1969 printf("_ToggleNoControls leave, doing nothing, we are fullscreen\n"); 1970 return; 1971 } 1972 1973 fNoInterface = !fNoInterface; 1974 _SetWindowSizeLimits(); 1975 1976 if (fNoInterface) { 1977 MoveBy(0, fMenuBarHeight); 1978 ResizeBy(0, -(fControlsHeight + fMenuBarHeight)); 1979 SetLook(B_BORDERED_WINDOW_LOOK); 1980 } else { 1981 MoveBy(0, -fMenuBarHeight); 1982 ResizeBy(0, fControlsHeight + fMenuBarHeight); 1983 SetLook(B_TITLED_WINDOW_LOOK); 1984 } 1985 1986 _MarkItem(fSettingsMenu, M_TOGGLE_NO_INTERFACE, fNoInterface); 1987 1988 printf("_ToggleNoInterface leave\n"); 1989 } 1990 1991 1992 void 1993 MainWin::_ShowIfNeeded() 1994 { 1995 if (find_thread(NULL) != Thread()) 1996 return; 1997 1998 if (IsHidden()) { 1999 Show(); 2000 UpdateIfNeeded(); 2001 } 2002 } 2003 2004 2005 // #pragma mark - 2006 2007 2008 /*! Sets some standard attributes of the currently played file. 2009 This should only be a temporary solution. 2010 */ 2011 void 2012 MainWin::_SetFileAttributes() 2013 { 2014 BAutolock locker(fPlaylist); 2015 const FilePlaylistItem* item 2016 = dynamic_cast<const FilePlaylistItem*>(fController->Item()); 2017 if (item == NULL) 2018 return; 2019 2020 if (!fHasVideo && fHasAudio) { 2021 BNode node(&item->Ref()); 2022 if (node.InitCheck()) 2023 return; 2024 2025 locker.Unlock(); 2026 2027 // write duration 2028 2029 attr_info info; 2030 status_t status = node.GetAttrInfo("Audio:Length", &info); 2031 if (status != B_OK || info.size == 0) { 2032 time_t duration = fController->TimeDuration() / 1000000L; 2033 2034 char text[256]; 2035 snprintf(text, sizeof(text), "%02ld:%02ld", duration / 60, 2036 duration % 60); 2037 node.WriteAttr("Audio:Length", B_STRING_TYPE, 0, text, 2038 strlen(text) + 1); 2039 } 2040 2041 // write bitrate 2042 2043 status = node.GetAttrInfo("Audio:Bitrate", &info); 2044 if (status != B_OK || info.size == 0) { 2045 media_format format; 2046 if (fController->GetEncodedAudioFormat(&format) == B_OK 2047 && format.type == B_MEDIA_ENCODED_AUDIO) { 2048 int32 bitrate = (int32)(format.u.encoded_audio.bit_rate / 1000); 2049 char text[256]; 2050 snprintf(text, sizeof(text), "%ld kbit", bitrate); 2051 node.WriteAttr("Audio:Bitrate", B_STRING_TYPE, 0, text, 2052 strlen(text) + 1); 2053 } 2054 } 2055 } 2056 } 2057 2058 2059 void 2060 MainWin::_UpdateControlsEnabledStatus() 2061 { 2062 uint32 enabledButtons = 0; 2063 if (fHasVideo || fHasAudio) { 2064 enabledButtons |= PLAYBACK_ENABLED | SEEK_ENABLED 2065 | SEEK_BACK_ENABLED | SEEK_FORWARD_ENABLED; 2066 } 2067 if (fHasAudio) 2068 enabledButtons |= VOLUME_ENABLED; 2069 2070 BAutolock _(fPlaylist); 2071 bool canSkipPrevious, canSkipNext; 2072 fPlaylist->GetSkipInfo(&canSkipPrevious, &canSkipNext); 2073 if (canSkipPrevious) 2074 enabledButtons |= SKIP_BACK_ENABLED; 2075 if (canSkipNext) 2076 enabledButtons |= SKIP_FORWARD_ENABLED; 2077 2078 fControls->SetEnabled(enabledButtons); 2079 2080 fNoInterfaceMenuItem->SetEnabled(fHasVideo); 2081 } 2082 2083 2084 void 2085 MainWin::_UpdatePlaylistMenu() 2086 { 2087 if (!fPlaylist->Lock()) 2088 return; 2089 2090 fPlaylistMenu->RemoveItems(0, fPlaylistMenu->CountItems(), true); 2091 2092 int32 count = fPlaylist->CountItems(); 2093 for (int32 i = 0; i < count; i++) { 2094 PlaylistItem* item = fPlaylist->ItemAtFast(i); 2095 _AddPlaylistItem(item, i); 2096 } 2097 fPlaylistMenu->SetTargetForItems(this); 2098 2099 _MarkPlaylistItem(fPlaylist->CurrentItemIndex()); 2100 2101 fPlaylist->Unlock(); 2102 } 2103 2104 2105 void 2106 MainWin::_AddPlaylistItem(PlaylistItem* item, int32 index) 2107 { 2108 BMessage* message = new BMessage(M_SET_PLAYLIST_POSITION); 2109 message->AddInt32("index", index); 2110 BMenuItem* menuItem = new BMenuItem(item->Name().String(), message); 2111 fPlaylistMenu->AddItem(menuItem, index); 2112 } 2113 2114 2115 void 2116 MainWin::_RemovePlaylistItem(int32 index) 2117 { 2118 delete fPlaylistMenu->RemoveItem(index); 2119 } 2120 2121 2122 void 2123 MainWin::_MarkPlaylistItem(int32 index) 2124 { 2125 if (BMenuItem* item = fPlaylistMenu->ItemAt(index)) { 2126 item->SetMarked(true); 2127 // ... and in case the menu is currently on screen: 2128 if (fPlaylistMenu->LockLooper()) { 2129 fPlaylistMenu->Invalidate(); 2130 fPlaylistMenu->UnlockLooper(); 2131 } 2132 } 2133 } 2134 2135 2136 void 2137 MainWin::_MarkItem(BMenu* menu, uint32 command, bool mark) 2138 { 2139 if (BMenuItem* item = menu->FindItem(command)) 2140 item->SetMarked(mark); 2141 } 2142 2143 2144 void 2145 MainWin::_AdoptGlobalSettings() 2146 { 2147 mpSettings settings = Settings::CurrentSettings(); 2148 // thread safe 2149 2150 fCloseWhenDonePlayingMovie = settings.closeWhenDonePlayingMovie; 2151 fCloseWhenDonePlayingSound = settings.closeWhenDonePlayingSound; 2152 } 2153 2154 2155