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