/** * @file llpanelprimmediacontrols.cpp * @brief media controls popup panel * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llagent.h" #include "llagentcamera.h" #include "llparcel.h" #include "llpanel.h" #include "llselectmgr.h" #include "llmediaentry.h" #include "llrender.h" #include "lldrawable.h" #include "llviewerwindow.h" #include "lluictrlfactory.h" #include "llbutton.h" #include "llface.h" #include "llcombobox.h" #include "lllayoutstack.h" #include "llslider.h" #include "llhudview.h" #include "lliconctrl.h" #include "lltoolpie.h" #include "llviewercamera.h" #include "llviewerobjectlist.h" #include "llpanelprimmediacontrols.h" #include "llpluginclassmedia.h" #include "llprogressbar.h" #include "llsliderctrl.h" #include "llstring.h" #include "llviewercontrol.h" #include "llviewerdisplay.h" #include "llviewerparcelmgr.h" #include "llviewermedia.h" #include "llviewermediafocus.h" #include "llvovolume.h" #include "llweb.h" #include "llwindow.h" #include "llwindowshade.h" #include "llfloatertools.h" // to enable hide if build tools are up #include "llvector4a.h" // Functions pulled from pipeline.cpp glh::matrix4f glh_get_current_modelview(); glh::matrix4f glh_get_current_projection(); // Functions pulled from llviewerdisplay.cpp bool get_hud_matrices(glh::matrix4f &proj, glh::matrix4f &model); // Warning: make sure these two match! const LLPanelPrimMediaControls::EZoomLevel LLPanelPrimMediaControls::kZoomLevels[] = { ZOOM_NONE, ZOOM_MEDIUM }; const int LLPanelPrimMediaControls::kNumZoomLevels = 2; const F32 EXCEEDING_ZOOM_DISTANCE = 0.5f; // // LLPanelPrimMediaControls // LLPanelPrimMediaControls::LLPanelPrimMediaControls() : mAlpha(1.f), mCurrentURL(""), mPreviousURL(""), mPauseFadeout(false), mUpdateSlider(true), mClearFaceOnFade(false), mCurrentRate(0.0), mMovieDuration(0.0), mTargetObjectID(LLUUID::null), mTargetObjectFace(0), mTargetImplID(LLUUID::null), mTargetObjectNormal(LLVector3::zero), mZoomObjectID(LLUUID::null), mZoomObjectFace(0), mVolumeSliderVisible(0), mZoomedCameraPos(), mWindowShade(NULL), mHideImmediately(false), mSecureURL(false), mMediaPlaySliderCtrlMouseDownValue(0.0) { mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); mCommitCallbackRegistrar.add("MediaCtrl.Forward", boost::bind(&LLPanelPrimMediaControls::onClickForward, this)); mCommitCallbackRegistrar.add("MediaCtrl.Home", boost::bind(&LLPanelPrimMediaControls::onClickHome, this)); mCommitCallbackRegistrar.add("MediaCtrl.Stop", boost::bind(&LLPanelPrimMediaControls::onClickStop, this)); mCommitCallbackRegistrar.add("MediaCtrl.MediaStop", boost::bind(&LLPanelPrimMediaControls::onClickMediaStop, this)); mCommitCallbackRegistrar.add("MediaCtrl.Reload", boost::bind(&LLPanelPrimMediaControls::onClickReload, this)); mCommitCallbackRegistrar.add("MediaCtrl.Play", boost::bind(&LLPanelPrimMediaControls::onClickPlay, this)); mCommitCallbackRegistrar.add("MediaCtrl.Pause", boost::bind(&LLPanelPrimMediaControls::onClickPause, this)); mCommitCallbackRegistrar.add("MediaCtrl.Open", boost::bind(&LLPanelPrimMediaControls::onClickOpen, this)); mCommitCallbackRegistrar.add("MediaCtrl.Zoom", boost::bind(&LLPanelPrimMediaControls::onClickZoom, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitURL", boost::bind(&LLPanelPrimMediaControls::onCommitURL, this)); mCommitCallbackRegistrar.add("MediaCtrl.MouseDown", boost::bind(&LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseDown, this)); mCommitCallbackRegistrar.add("MediaCtrl.MouseUp", boost::bind(&LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseUp, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeUp", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeUp, this)); mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeDown", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeDown, this)); mCommitCallbackRegistrar.add("MediaCtrl.Volume", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeSlider, this)); mCommitCallbackRegistrar.add("MediaCtrl.ToggleMute", boost::bind(&LLPanelPrimMediaControls::onToggleMute, this)); mCommitCallbackRegistrar.add("MediaCtrl.ShowVolumeSlider", boost::bind(&LLPanelPrimMediaControls::showVolumeSlider, this)); mCommitCallbackRegistrar.add("MediaCtrl.HideVolumeSlider", boost::bind(&LLPanelPrimMediaControls::hideVolumeSlider, this)); mCommitCallbackRegistrar.add("MediaCtrl.SkipBack", boost::bind(&LLPanelPrimMediaControls::onClickSkipBack, this)); mCommitCallbackRegistrar.add("MediaCtrl.SkipForward", boost::bind(&LLPanelPrimMediaControls::onClickSkipForward, this)); buildFromFile( "panel_prim_media_controls.xml"); mInactivityTimer.reset(); mFadeTimer.stop(); mCurrentZoom = ZOOM_NONE; mScrollState = SCROLL_NONE; mPanelHandle.bind(this); mInactiveTimeout = gSavedSettings.getF32("MediaControlTimeout"); mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); } LLPanelPrimMediaControls::~LLPanelPrimMediaControls() { } BOOL LLPanelPrimMediaControls::postBuild() { mMediaRegion = getChild("media_region"); mBackCtrl = getChild("back"); mFwdCtrl = getChild("fwd"); mReloadCtrl = getChild("reload"); mPlayCtrl = getChild("play"); mPauseCtrl = getChild("pause"); mStopCtrl = getChild("stop"); mMediaStopCtrl = getChild("media_stop"); mHomeCtrl = getChild("home"); mUnzoomCtrl = getChild("close"); // This is actually "unzoom" mOpenCtrl = getChild("new_window"); mZoomCtrl = getChild("zoom_frame"); mMediaProgressPanel = getChild("media_progress_indicator"); mMediaProgressBar = getChild("media_progress_bar"); mMediaAddressCtrl = getChild("media_address"); mMediaAddress = getChild("media_address_url"); mMediaPlaySliderPanel = getChild("media_play_position"); mMediaPlaySliderCtrl = getChild("media_play_slider"); mSkipFwdCtrl = getChild("skip_forward"); mSkipBackCtrl = getChild("skip_back"); mVolumeCtrl = getChild("media_volume"); mMuteBtn = getChild("media_mute_button"); mVolumeSliderCtrl = getChild("volume_slider"); mWhitelistIcon = getChild("media_whitelist_flag"); mSecureLockIcon = getChild("media_secure_lock_flag"); mMediaControlsStack = getChild("media_controls"); mLeftBookend = getChild("left_bookend"); mRightBookend = getChild("right_bookend"); mBackgroundImage = LLUI::getUIImage(getString("control_background_image_name")); mVolumeSliderBackgroundImage = LLUI::getUIImage(getString("control_background_image_name")); LLStringUtil::convertToF32(getString("skip_step"), mSkipStep); LLStringUtil::convertToS32(getString("min_width"), mMinWidth); LLStringUtil::convertToS32(getString("min_height"), mMinHeight); LLStringUtil::convertToF32(getString("zoom_near_padding"), mZoomNearPadding); LLStringUtil::convertToF32(getString("zoom_medium_padding"), mZoomMediumPadding); LLStringUtil::convertToF32(getString("zoom_far_padding"), mZoomFarPadding); LLStringUtil::convertToS32(getString("top_world_view_avoid_zone"), mTopWorldViewAvoidZone); // These are currently removed...but getChild creates a "dummy" widget. // This class handles them missing. mMediaPanelScroll = findChild("media_panel_scroll"); mScrollUpCtrl = findChild("scrollup"); mScrollLeftCtrl = findChild("scrollleft"); mScrollRightCtrl = findChild("scrollright"); mScrollDownCtrl = findChild("scrolldown"); if (mScrollUpCtrl) { mScrollUpCtrl->setClickedCallback(onScrollUp, this); mScrollUpCtrl->setHeldDownCallback(onScrollUpHeld, this); mScrollUpCtrl->setMouseUpCallback(onScrollStop, this); } if (mScrollLeftCtrl) { mScrollLeftCtrl->setClickedCallback(onScrollLeft, this); mScrollLeftCtrl->setHeldDownCallback(onScrollLeftHeld, this); mScrollLeftCtrl->setMouseUpCallback(onScrollStop, this); } if (mScrollRightCtrl) { mScrollRightCtrl->setClickedCallback(onScrollRight, this); mScrollRightCtrl->setHeldDownCallback(onScrollRightHeld, this); mScrollRightCtrl->setMouseUpCallback(onScrollStop, this); } if (mScrollDownCtrl) { mScrollDownCtrl->setClickedCallback(onScrollDown, this); mScrollDownCtrl->setHeldDownCallback(onScrollDownHeld, this); mScrollDownCtrl->setMouseUpCallback(onScrollStop, this); } mMediaAddress->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this )); gAgent.setMouselookModeInCallback(boost::bind(&LLPanelPrimMediaControls::onMouselookModeIn, this)); LLWindowShade::Params window_shade_params; window_shade_params.name = "window_shade"; mCurrentZoom = ZOOM_NONE; // clicks on buttons do not remove keyboard focus from media setIsChrome(TRUE); return TRUE; } void LLPanelPrimMediaControls::setMediaFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) { if (media_impl.notNull() && objectp.notNull()) { LLUUID prev_id = mTargetImplID; mTargetImplID = media_impl->getMediaTextureID(); mTargetObjectID = objectp->getID(); mTargetObjectFace = face; mTargetObjectNormal = pick_normal; mClearFaceOnFade = false; if (prev_id != mTargetImplID) mVolumeSliderCtrl->setValue(media_impl->getVolume()); } else { // This happens on a timer now. // mTargetImplID = LLUUID::null; // mTargetObjectID = LLUUID::null; // mTargetObjectFace = 0; mClearFaceOnFade = true; } updateShape(); } void LLPanelPrimMediaControls::focusOnTarget() { // Sets the media focus to the current target of the LLPanelPrimMediaControls. // This is how we transition from hover to focus when the user clicks on a control. LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if(media_impl) { if (!media_impl->hasFocus() || isZoomDistExceeding()) { // The current target doesn't have media focus -- focus on it. LLViewerObject* objectp = getTargetObject(); LLViewerMediaFocus::getInstance()->setFocusFace(objectp, mTargetObjectFace, media_impl, mTargetObjectNormal); } } } LLViewerMediaImpl* LLPanelPrimMediaControls::getTargetMediaImpl() { return LLViewerMedia::getInstance()->getMediaImplFromTextureID(mTargetImplID); } LLViewerObject* LLPanelPrimMediaControls::getTargetObject() { return gObjectList.findObject(mTargetObjectID); } LLPluginClassMedia* LLPanelPrimMediaControls::getTargetMediaPlugin() { LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl && impl->hasMedia()) { return impl->getMediaPlugin(); } return NULL; } void LLPanelPrimMediaControls::updateShape() { LLViewerMediaImpl* media_impl = getTargetMediaImpl(); LLViewerObject* objectp = getTargetObject(); if(!media_impl || gFloaterTools->getVisible()) { setVisible(FALSE); return; } LLPluginClassMedia* media_plugin = NULL; if(media_impl->hasMedia()) { media_plugin = media_impl->getMediaPlugin(); } LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); bool can_navigate = parcel->getMediaAllowNavigate(); bool enabled = false; bool is_zoomed = (mCurrentZoom != ZOOM_NONE) && (mTargetObjectID == mZoomObjectID) && (mTargetObjectFace == mZoomObjectFace) && !isZoomDistExceeding(); // There is no such thing as "has_focus" being different from normal controls set // anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus' // to 'true' (or, actually, we use a setting) bool has_focus = (gSavedSettings.getBOOL("PrimMediaControlsUseHoverControlSet")) ? media_impl->hasFocus() : true; setVisible(enabled); if (objectp) { bool hasPermsControl = true; bool mini_controls = false; LLMediaEntry *media_data = objectp->getTE(mTargetObjectFace)->getMediaData(); if (media_data && NULL != dynamic_cast(objectp)) { // Don't show the media controls if we do not have permissions enabled = dynamic_cast(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL); hasPermsControl = dynamic_cast(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL); mini_controls = (LLMediaEntry::MINI == media_data->getControls()); } const bool is_hud = objectp->isHUDAttachment(); // // Set the state of the buttons // // XXX RSP: TODO: FIXME: clean this up so that it is clearer what mode we are in, // and that only the proper controls get made visible/enabled according to that mode. mBackCtrl->setVisible(has_focus); mFwdCtrl->setVisible(has_focus); mReloadCtrl->setVisible(has_focus); mStopCtrl->setVisible(false); mHomeCtrl->setVisible(has_focus); mZoomCtrl->setVisible(!is_zoomed); mUnzoomCtrl->setVisible(is_zoomed); mOpenCtrl->setVisible(true); mMediaAddressCtrl->setVisible(has_focus && !mini_controls); mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); mVolumeCtrl->setVisible(false); mWhitelistIcon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); // Disable zoom if HUD mZoomCtrl->setEnabled(!is_hud); mUnzoomCtrl->setEnabled(!is_hud); mSecureURL = false; mCurrentURL = media_impl->getCurrentMediaURL(); mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate); mFwdCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateForward() && can_navigate); mStopCtrl->setEnabled(has_focus && can_navigate); mHomeCtrl->setEnabled(has_focus && can_navigate); LLPluginClassMediaOwner::EMediaStatus result = ((media_impl != NULL) && media_impl->hasMedia()) ? media_plugin->getStatus() : LLPluginClassMediaOwner::MEDIA_NONE; mVolumeCtrl->setVisible(has_focus); mVolumeCtrl->setEnabled(has_focus); mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); if(media_plugin && media_plugin->pluginSupportsMediaTime()) { mReloadCtrl->setEnabled(false); mReloadCtrl->setVisible(false); mMediaStopCtrl->setVisible(has_focus); mHomeCtrl->setVisible(has_focus); mBackCtrl->setVisible(false); mFwdCtrl->setVisible(false); mMediaAddressCtrl->setVisible(false); mMediaAddressCtrl->setEnabled(false); mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); mMediaPlaySliderPanel->setEnabled(has_focus && !mini_controls); mSkipFwdCtrl->setVisible(has_focus && !mini_controls); mSkipFwdCtrl->setEnabled(has_focus && !mini_controls); mSkipBackCtrl->setVisible(has_focus && !mini_controls); mSkipBackCtrl->setEnabled(has_focus && !mini_controls); mVolumeCtrl->setVisible(has_focus); mVolumeCtrl->setEnabled(has_focus); mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); mWhitelistIcon->setVisible(false); mSecureURL = false; if (mMediaPanelScroll) { mMediaPanelScroll->setVisible(false); mScrollUpCtrl->setVisible(false); mScrollDownCtrl->setVisible(false); mScrollRightCtrl->setVisible(false); mScrollDownCtrl->setVisible(false); } F32 volume = media_impl->getVolume(); // movie's url changed if(mCurrentURL!=mPreviousURL) { mMovieDuration = media_plugin->getDuration(); mPreviousURL = mCurrentURL; } if(mMovieDuration == 0) { mMovieDuration = media_plugin->getDuration(); mMediaPlaySliderCtrl->setValue(0); mMediaPlaySliderCtrl->setEnabled(false); } // TODO: What if it's not fully loaded if(mUpdateSlider && mMovieDuration!= 0) { F64 current_time = media_plugin->getCurrentTime(); F32 percent = current_time / mMovieDuration; mMediaPlaySliderCtrl->setValue(percent); mMediaPlaySliderCtrl->setEnabled(true); } // video volume if(volume <= 0.0) { mMuteBtn->setToggleState(true); } else if (volume >= 1.0) { mMuteBtn->setToggleState(false); } else { mMuteBtn->setToggleState(false); } switch(result) { case LLPluginClassMediaOwner::MEDIA_PLAYING: mPlayCtrl->setEnabled(FALSE); mPlayCtrl->setVisible(FALSE); mPauseCtrl->setEnabled(TRUE); mPauseCtrl->setVisible(has_focus); break; case LLPluginClassMediaOwner::MEDIA_PAUSED: default: mPauseCtrl->setEnabled(FALSE); mPauseCtrl->setVisible(FALSE); mPlayCtrl->setEnabled(TRUE); mPlayCtrl->setVisible(has_focus); break; } } else // web based { if(media_plugin) { mCurrentURL = media_plugin->getLocation(); } else { mCurrentURL.clear(); } mPlayCtrl->setVisible(FALSE); mPauseCtrl->setVisible(FALSE); mMediaStopCtrl->setVisible(FALSE); mMediaAddressCtrl->setVisible(has_focus && !mini_controls); mMediaAddressCtrl->setEnabled(has_focus && !mini_controls); mMediaPlaySliderPanel->setVisible(FALSE); mMediaPlaySliderPanel->setEnabled(FALSE); mSkipFwdCtrl->setVisible(FALSE); mSkipFwdCtrl->setEnabled(FALSE); mSkipBackCtrl->setVisible(FALSE); mSkipBackCtrl->setEnabled(FALSE); if(media_impl->getVolume() <= 0.0) { mMuteBtn->setToggleState(true); } else { mMuteBtn->setToggleState(false); } if (mMediaPanelScroll) { mMediaPanelScroll->setVisible(has_focus); mScrollUpCtrl->setVisible(has_focus); mScrollDownCtrl->setVisible(has_focus); mScrollRightCtrl->setVisible(has_focus); mScrollDownCtrl->setVisible(has_focus); } // TODO: get the secure lock bool from media plug in std::string prefix = std::string("https://"); std::string test_prefix = mCurrentURL.substr(0, prefix.length()); LLStringUtil::toLower(test_prefix); mSecureURL = has_focus && (test_prefix == prefix); mCurrentURL = (mSecureURL ? " " + mCurrentURL : mCurrentURL); if(mCurrentURL!=mPreviousURL) { setCurrentURL(); mPreviousURL = mCurrentURL; } if(result == LLPluginClassMediaOwner::MEDIA_LOADING) { mReloadCtrl->setEnabled(FALSE); mReloadCtrl->setVisible(FALSE); mStopCtrl->setEnabled(TRUE); mStopCtrl->setVisible(has_focus); } else { mReloadCtrl->setEnabled(TRUE); mReloadCtrl->setVisible(has_focus); mStopCtrl->setEnabled(FALSE); mStopCtrl->setVisible(FALSE); } } if(media_plugin) { // // Handle progress bar // if(LLPluginClassMediaOwner::MEDIA_LOADING == media_plugin->getStatus()) { mMediaProgressPanel->setVisible(true); mMediaProgressBar->setValue(media_plugin->getProgressPercent()); } else { mMediaProgressPanel->setVisible(false); } } if(media_impl) { // // Handle Scrolling // switch (mScrollState) { case SCROLL_UP: media_impl->scrollWheel(0, 0, 0, -1, MASK_NONE); break; case SCROLL_DOWN: media_impl->scrollWheel(0, 0, 0, 1, MASK_NONE); break; case SCROLL_LEFT: media_impl->scrollWheel(0, 0, 1, 0, MASK_NONE); // media_impl->handleKeyHere(KEY_LEFT, MASK_NONE); break; case SCROLL_RIGHT: media_impl->scrollWheel(0, 0, -1, 0, MASK_NONE); // media_impl->handleKeyHere(KEY_RIGHT, MASK_NONE); break; case SCROLL_NONE: default: break; } } // Web plugins and HUD may have media controls invisible for user, but still need scroll mouse events. // LLView checks for visibleEnabledAndContains() and won't pass events to invisible panel, so instead // of hiding whole panel hide each control instead (if user has no perms). // Note: It might be beneficial to keep panel visible for all plugins to make behavior consistent, but // for now limiting change to cases that need events. if (!is_hud && (!media_plugin || media_plugin->pluginSupportsMediaTime())) { setVisible(enabled); } else { if( !hasPermsControl ) { mBackCtrl->setVisible(false); mFwdCtrl->setVisible(false); mReloadCtrl->setVisible(false); mStopCtrl->setVisible(false); mHomeCtrl->setVisible(false); mZoomCtrl->setVisible(false); mUnzoomCtrl->setVisible(false); mOpenCtrl->setVisible(false); mMediaAddressCtrl->setVisible(false); mMediaPlaySliderPanel->setVisible(false); mVolumeCtrl->setVisible(false); mMediaProgressPanel->setVisible(false); mVolumeSliderCtrl->setVisible(false); } setVisible(true); } // // Calculate position and shape of the controls // std::vector::iterator vert_it; std::vector::iterator vert_end; std::vector vect_face; LLVolume* volume = objectp->getVolume(); if (volume) { const LLVolumeFace& vf = volume->getVolumeFace(mTargetObjectFace); LLVector3 ext[2]; ext[0].set(vf.mExtents[0].getF32ptr()); ext[1].set(vf.mExtents[1].getF32ptr()); LLVector3 center = (ext[0]+ext[1])*0.5f; LLVector3 size = (ext[1]-ext[0])*0.5f; LLVector3 vert[] = { center + size.scaledVec(LLVector3(1,1,1)), center + size.scaledVec(LLVector3(-1,1,1)), center + size.scaledVec(LLVector3(1,-1,1)), center + size.scaledVec(LLVector3(-1,-1,1)), center + size.scaledVec(LLVector3(1,1,-1)), center + size.scaledVec(LLVector3(-1,1,-1)), center + size.scaledVec(LLVector3(1,-1,-1)), center + size.scaledVec(LLVector3(-1,-1,-1)), }; LLVOVolume* vo = (LLVOVolume*) objectp; for (U32 i = 0; i < 8; i++) { vect_face.push_back(vo->volumePositionToAgent(vert[i])); } } vert_it = vect_face.begin(); vert_end = vect_face.end(); glh::matrix4f mat; if (!is_hud) { mat = glh_get_current_projection() * glh_get_current_modelview(); } else { glh::matrix4f proj, modelview; if (get_hud_matrices(proj, modelview)) mat = proj * modelview; } LLVector3 min = LLVector3(1,1,1); LLVector3 max = LLVector3(-1,-1,-1); for(; vert_it != vert_end; ++vert_it) { // project silhouette vertices into screen space glh::vec3f screen_vert = glh::vec3f(vert_it->mV); mat.mult_matrix_vec(screen_vert); // add to screenspace bounding box update_min_max(min, max, LLVector3(screen_vert.v)); } // convert screenspace bbox to pixels (in screen coords) LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); LLCoordGL screen_min; screen_min.mX = ll_round((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.mV[VX] + 1.f) * 0.5f); screen_min.mY = ll_round((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.mV[VY] + 1.f) * 0.5f); LLCoordGL screen_max; screen_max.mX = ll_round((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.mV[VX] + 1.f) * 0.5f); screen_max.mY = ll_round((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.mV[VY] + 1.f) * 0.5f); // grow panel so that screenspace bounding box fits inside "media_region" element of panel LLRect media_panel_rect; // Get the height of the controls (less the volume slider) S32 controls_height = mMediaControlsStack->getRect().getHeight() - mVolumeSliderCtrl->getRect().getHeight(); getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_panel_rect); media_panel_rect.mTop += controls_height; // keep all parts of panel on-screen // Area of the top of the world view to avoid putting the controls window_rect.mTop -= mTopWorldViewAvoidZone; // Don't include "spacing" bookends on left & right of the media controls window_rect.mLeft -= mLeftBookend->getRect().getWidth(); window_rect.mRight += mRightBookend->getRect().getWidth(); // Don't include the volume slider window_rect.mBottom -= mVolumeSliderCtrl->getRect().getHeight(); media_panel_rect.intersectWith(window_rect); // clamp to minimum size, keeping rect inside window S32 centerX = media_panel_rect.getCenterX(); S32 centerY = media_panel_rect.getCenterY(); // Shrink screen rect by min width and height, to ensure containment window_rect.stretch(-mMinWidth/2, -mMinHeight/2); window_rect.clampPointToRect(centerX, centerY); media_panel_rect.setCenterAndSize(centerX, centerY, llmax(mMinWidth, media_panel_rect.getWidth()), llmax(mMinHeight, media_panel_rect.getHeight())); // Finally set the size of the panel setShape(media_panel_rect, true); // Test mouse position to see if the cursor is stationary LLCoordWindow cursor_pos_window; getWindow()->getCursorPosition(&cursor_pos_window); // If last pos is not equal to current pos, the mouse has moved // We need to reset the timer, and make sure the panel is visible if(cursor_pos_window.mX != mLastCursorPos.mX || cursor_pos_window.mY != mLastCursorPos.mY || mScrollState != SCROLL_NONE) { mInactivityTimer.start(); mLastCursorPos = cursor_pos_window; } if(isMouseOver() || hasFocus()) { // Never fade the controls if the mouse is over them or they have keyboard focus. mFadeTimer.stop(); } else if(!mClearFaceOnFade && (mInactivityTimer.getElapsedTimeF32() < mInactiveTimeout)) { // Mouse is over the object, but has not been stationary for long enough to fade the UI mFadeTimer.stop(); } else if(! mFadeTimer.getStarted() ) { // we need to start fading the UI (and we have not already started) mFadeTimer.reset(); mFadeTimer.start(); } else { // I don't think this is correct anymore. This is done in draw() after the fade has completed. // setVisible(FALSE); } } } /*virtual*/ void LLPanelPrimMediaControls::draw() { LLViewerMediaImpl* impl = getTargetMediaImpl(); if (impl) { LLNotificationPtr notification = impl->getCurrentNotification(); if (notification != mActiveNotification) { mActiveNotification = notification; if (notification) { showNotification(notification); } else { hideNotification(); } } } F32 alpha = getDrawContext().mAlpha; if(mHideImmediately) { //hide this panel clearFaceOnFade(); mHideImmediately = false; } else if(mFadeTimer.getStarted()) { F32 time = mFadeTimer.getElapsedTimeF32(); alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); if(time >= mControlFadeTime) { //hide this panel clearFaceOnFade(); } } // Show/hide the lock icon for secure browsing mSecureLockIcon->setVisible(mSecureURL && !mMediaAddress->hasFocus()); // Build rect for icon area in coord system of this panel // Assumes layout_stack is a direct child of this panel mMediaControlsStack->updateLayout(); // adjust for layout stack spacing S32 space = mMediaControlsStack->getPanelSpacing() + 2; LLRect controls_bg_area = mMediaControlsStack->getRect(); controls_bg_area.mTop += space + 2; // adjust to ignore space from volume slider controls_bg_area.mBottom += mVolumeSliderCtrl->getRect().getHeight(); // adjust to ignore space from left bookend padding controls_bg_area.mLeft += mLeftBookend->getRect().getWidth() - space; // ignore space from right bookend padding controls_bg_area.mRight -= mRightBookend->getRect().getWidth() - space - 2; // draw control background UI image LLViewerObject* objectp = getTargetObject(); LLMediaEntry *media_data(0); if( objectp ) media_data = objectp->getTE(mTargetObjectFace)->getMediaData(); if( !dynamic_cast(objectp) || !media_data || dynamic_cast(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL) ) mBackgroundImage->draw( controls_bg_area, UI_VERTEX_COLOR % alpha); // draw volume slider background UI image if (mVolumeSliderCtrl->getVisible()) { LLRect volume_slider_rect; screenRectToLocal(mVolumeSliderCtrl->calcScreenRect(), &volume_slider_rect); mVolumeSliderBackgroundImage->draw(volume_slider_rect, UI_VERTEX_COLOR % alpha); } { LLViewDrawContext context(alpha); LLPanel::draw(); } } BOOL LLPanelPrimMediaControls::handleScrollWheel(S32 x, S32 y, S32 clicks) { mInactivityTimer.start(); return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); } BOOL LLPanelPrimMediaControls::handleMouseDown(S32 x, S32 y, MASK mask) { mInactivityTimer.start(); return LLPanel::handleMouseDown(x, y, mask); } BOOL LLPanelPrimMediaControls::handleMouseUp(S32 x, S32 y, MASK mask) { mInactivityTimer.start(); return LLPanel::handleMouseUp(x, y, mask); } BOOL LLPanelPrimMediaControls::handleKeyHere( KEY key, MASK mask ) { mInactivityTimer.start(); return LLPanel::handleKeyHere(key, mask); } bool LLPanelPrimMediaControls::isMouseOver() { bool result = false; if( getVisible() ) { LLCoordWindow cursor_pos_window; LLCoordScreen cursor_pos_screen; LLCoordGL cursor_pos_gl; S32 x, y; getWindow()->getCursorPosition(&cursor_pos_window); cursor_pos_gl = cursor_pos_window.convert(); if(mMediaControlsStack->getVisible()) { mMediaControlsStack->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); LLView *hit_child = mMediaControlsStack->childFromPoint(x, y); if(hit_child && hit_child->getVisible()) { // This was useful for debugging both coordinate translation and view hieararchy problems... // LL_INFOS() << "mouse coords: " << x << ", " << y << " hit child " << hit_child->getName() << LL_ENDL; // This will be a direct child of the LLLayoutStack, which should be a layout_panel. // These may not shown/hidden by the logic in updateShape(), so we need to do another hit test on the children of the layout panel, // which are the actual controls. hit_child->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); LLView *hit_child_2 = hit_child->childFromPoint(x, y); if(hit_child_2 && hit_child_2->getVisible()) { // This was useful for debugging both coordinate translation and view hieararchy problems... // LL_INFOS() << " mouse coords: " << x << ", " << y << " hit child 2 " << hit_child_2->getName() << LL_ENDL; result = true; } } } } return result; } void LLPanelPrimMediaControls::onClickClose() { close(); } void LLPanelPrimMediaControls::close() { resetZoomLevel(true); LLViewerMediaFocus::getInstance()->clearFocus(); setVisible(FALSE); } void LLPanelPrimMediaControls::onClickBack() { focusOnTarget(); LLViewerMediaImpl* impl =getTargetMediaImpl(); if (impl) { impl->navigateBack(); } } void LLPanelPrimMediaControls::onClickForward() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if (impl) { impl->navigateForward(); } } void LLPanelPrimMediaControls::onClickHome() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { impl->navigateHome(); } } void LLPanelPrimMediaControls::onClickOpen() { LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { LLWeb::loadURL(impl->getCurrentMediaURL()); } } void LLPanelPrimMediaControls::onClickReload() { focusOnTarget(); //LLViewerMedia::navigateHome(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { impl->navigateReload(); } } void LLPanelPrimMediaControls::onClickPlay() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { impl->play(); } } void LLPanelPrimMediaControls::onClickPause() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { impl->pause(); } } void LLPanelPrimMediaControls::onClickStop() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { impl->navigateStop(); } } void LLPanelPrimMediaControls::onClickMediaStop() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if(impl) { impl->stop(); } } void LLPanelPrimMediaControls::onClickSkipBack() { focusOnTarget(); LLViewerMediaImpl* impl =getTargetMediaImpl(); if (impl) { impl->skipBack(mSkipStep); } } void LLPanelPrimMediaControls::onClickSkipForward() { focusOnTarget(); LLViewerMediaImpl* impl = getTargetMediaImpl(); if (impl) { impl->skipForward(mSkipStep); } } void LLPanelPrimMediaControls::onClickZoom() { focusOnTarget(); if(mCurrentZoom == ZOOM_NONE) { nextZoomLevel(); } } void LLPanelPrimMediaControls::nextZoomLevel() { LLViewerObject* objectp = getTargetObject(); if(objectp && objectp->isHUDAttachment()) { // Never allow zooming on HUD attachments. return; } int index = 0; while (index < kNumZoomLevels) { if (kZoomLevels[index] == mCurrentZoom) { index++; break; } index++; } mCurrentZoom = kZoomLevels[index % kNumZoomLevels]; updateZoom(); } void LLPanelPrimMediaControls::resetZoomLevel(bool reset_camera) { if(mCurrentZoom != ZOOM_NONE) { mCurrentZoom = ZOOM_NONE; if(reset_camera) { updateZoom(); } } } void LLPanelPrimMediaControls::updateZoom() { F32 zoom_padding = 0.0f; switch (mCurrentZoom) { case ZOOM_NONE: { gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); break; } case ZOOM_FAR: { zoom_padding = mZoomFarPadding; break; } case ZOOM_MEDIUM: { zoom_padding = mZoomMediumPadding; break; } case ZOOM_NEAR: { zoom_padding = mZoomNearPadding; break; } default: { gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); break; } } if (zoom_padding > 0.0f) { // since we only zoom into medium for now, always set zoom_in constraint to true mZoomedCameraPos = LLViewerMediaFocus::setCameraZoom(getTargetObject(), mTargetObjectNormal, zoom_padding, true); } // Remember the object ID/face we zoomed into, so we can update the zoom icon appropriately mZoomObjectID = mTargetObjectID; mZoomObjectFace = mTargetObjectFace; } void LLPanelPrimMediaControls::onScrollUp(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->focusOnTarget(); LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); if(impl) { impl->scrollWheel(0, 0, 0, -1, MASK_NONE); } } void LLPanelPrimMediaControls::onScrollUpHeld(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->mScrollState = SCROLL_UP; } void LLPanelPrimMediaControls::onScrollRight(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->focusOnTarget(); LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); if(impl) { impl->scrollWheel(0, 0, -1, 0, MASK_NONE); // impl->handleKeyHere(KEY_RIGHT, MASK_NONE); } } void LLPanelPrimMediaControls::onScrollRightHeld(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->mScrollState = SCROLL_RIGHT; } void LLPanelPrimMediaControls::onScrollLeft(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->focusOnTarget(); LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); if(impl) { impl->scrollWheel(0, 0, 1, 0, MASK_NONE); // impl->handleKeyHere(KEY_LEFT, MASK_NONE); } } void LLPanelPrimMediaControls::onScrollLeftHeld(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->mScrollState = SCROLL_LEFT; } void LLPanelPrimMediaControls::onScrollDown(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->focusOnTarget(); LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); if(impl) { impl->scrollWheel(0, 0, 0, 1, MASK_NONE); } } void LLPanelPrimMediaControls::onScrollDownHeld(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->mScrollState = SCROLL_DOWN; } void LLPanelPrimMediaControls::onScrollStop(void* user_data) { LLPanelPrimMediaControls* this_panel = static_cast (user_data); this_panel->mScrollState = SCROLL_NONE; } void LLPanelPrimMediaControls::onCommitURL() { focusOnTarget(); std::string url = mMediaAddress->getValue().asString(); if(getTargetMediaImpl() && !url.empty()) { getTargetMediaImpl()->navigateTo( url, "", true); // Make sure keyboard focus is set to the media focus object. gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); } mPauseFadeout = false; mFadeTimer.start(); } void LLPanelPrimMediaControls::onInputURL(LLFocusableElement* caller, void *userdata) { LLPanelPrimMediaControls* this_panel = static_cast (userdata); this_panel->focusOnTarget(); this_panel->mPauseFadeout = true; this_panel->mFadeTimer.stop(); this_panel->mFadeTimer.reset(); } void LLPanelPrimMediaControls::setCurrentURL() { #ifdef USE_COMBO_BOX_FOR_MEDIA_URL // LLComboBox* media_address_combo = getChild("media_address_combo"); // // redirects will navigate momentarily to about:blank, don't add to history // if (media_address_combo && mCurrentURL != "about:blank") // { // media_address_combo->remove(mCurrentURL); // media_address_combo->add(mCurrentURL); // media_address_combo->selectByValue(mCurrentURL); // } #else // USE_COMBO_BOX_FOR_MEDIA_URL if (mMediaAddress && mCurrentURL != "about:blank") { mMediaAddress->setValue(mCurrentURL); } #endif // USE_COMBO_BOX_FOR_MEDIA_URL } void LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseDown() { mMediaPlaySliderCtrlMouseDownValue = mMediaPlaySliderCtrl->getValue().asReal(); mUpdateSlider = false; } void LLPanelPrimMediaControls::onMediaPlaySliderCtrlMouseUp() { F64 cur_value = mMediaPlaySliderCtrl->getValue().asReal(); if (mMediaPlaySliderCtrlMouseDownValue != cur_value) { focusOnTarget(); LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if (media_impl) { if (cur_value <= 0.0) { media_impl->stop(); } else { media_impl->seek(cur_value * mMovieDuration); } } mUpdateSlider = true; } } void LLPanelPrimMediaControls::onCommitVolumeUp() { focusOnTarget(); LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if (media_impl) { F32 volume = media_impl->getVolume(); volume += 0.1f; if(volume >= 1.0f) { volume = 1.0f; } media_impl->setVolume(volume); mMuteBtn->setToggleState(false); } } void LLPanelPrimMediaControls::onCommitVolumeDown() { focusOnTarget(); LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if (media_impl) { F32 volume = media_impl->getVolume(); volume -= 0.1f; if(volume <= 0.0f) { volume = 0.0f; } media_impl->setVolume(volume); mMuteBtn->setToggleState(false); } } void LLPanelPrimMediaControls::onCommitVolumeSlider() { focusOnTarget(); LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if (media_impl) { media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); } } void LLPanelPrimMediaControls::onToggleMute() { focusOnTarget(); LLViewerMediaImpl* media_impl = getTargetMediaImpl(); if (media_impl) { F32 volume = media_impl->getVolume(); if(volume > 0.0) { media_impl->setVolume(0.0); } else if (mVolumeSliderCtrl->getValueF32() == 0.0) { media_impl->setVolume(1.0); mVolumeSliderCtrl->setValue(1.0); } else { media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); } } } void LLPanelPrimMediaControls::showVolumeSlider() { mVolumeSliderVisible++; } void LLPanelPrimMediaControls::hideVolumeSlider() { mVolumeSliderVisible--; } bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible() { return mVolumeSliderVisible > 0; } bool LLPanelPrimMediaControls::isZoomDistExceeding() { return (gAgentCamera.getCameraPositionGlobal() - mZoomedCameraPos).normalize() >= EXCEEDING_ZOOM_DISTANCE; } void LLPanelPrimMediaControls::clearFaceOnFade() { if(mClearFaceOnFade) { // Hiding this object makes scroll events go missing after it fades out // (see DEV-41755 for a full description of the train wreck). // Only hide the controls when we're untargeting. setVisible(FALSE); mClearFaceOnFade = false; mVolumeSliderVisible = 0; mTargetImplID = LLUUID::null; mTargetObjectID = LLUUID::null; mTargetObjectFace = 0; } } void LLPanelPrimMediaControls::onMouselookModeIn() { LLViewerMediaFocus::getInstance()->clearHover(); mHideImmediately = true; } void LLPanelPrimMediaControls::showNotification(LLNotificationPtr notify) { delete mWindowShade; LLWindowShade::Params params; params.rect = mMediaRegion->getLocalRect(); params.follows.flags = FOLLOWS_ALL; //HACK: don't hardcode this if (notify->getIcon() == "Popup_Caution") { params.bg_image.name = "Yellow_Gradient"; params.text_color = LLColor4::black; } else { //HACK: make this a property of the notification itself, "cancellable" params.can_close = false; params.text_color.control = "LabelTextColor"; } mWindowShade = LLUICtrlFactory::create(params); mMediaRegion->addChild(mWindowShade); mWindowShade->show(notify); } void LLPanelPrimMediaControls::hideNotification() { if (mWindowShade) { mWindowShade->hide(); } }