/** * @file llstatusbar.cpp * @brief LLStatusBar class implementation * * $LicenseInfo:firstyear=2002&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 "llstatusbar.h" // viewer includes #include "llagent.h" #include "llagentcamera.h" #include "llbutton.h" #include "llcommandhandler.h" #include "llfirstuse.h" #include "llviewercontrol.h" #include "llfloaterbuycurrency.h" #include "llbuycurrencyhtml.h" #include "llpanelnearbymedia.h" #include "llpanelpresetscamerapulldown.h" #include "llpanelpresetspulldown.h" #include "llpanelvolumepulldown.h" #include "llfloatermarketplace.h" #include "llfloaterregioninfo.h" #include "llfloaterscriptdebug.h" #include "llhints.h" #include "llhudicon.h" #include "llnavigationbar.h" #include "llkeyboard.h" #include "lllineeditor.h" #include "llmenugl.h" #include "llrootview.h" #include "llsd.h" #include "lltextbox.h" #include "llui.h" #include "llviewerparceloverlay.h" #include "llviewerregion.h" #include "llviewerstats.h" #include "llviewerwindow.h" #include "llframetimer.h" #include "llvoavatarself.h" #include "llresmgr.h" #include "llworld.h" #include "llstatgraph.h" #include "llviewermedia.h" #include "llviewermenu.h" // for gMenuBarView #include "llviewerparcelmgr.h" #include "llviewerthrottle.h" #include "lluictrlfactory.h" #include "lltoolmgr.h" #include "llfocusmgr.h" #include "llappviewer.h" #include "lltrans.h" // library includes #include "llfloaterreg.h" #include "llfontgl.h" #include "llrect.h" #include "llerror.h" #include "llnotificationsutil.h" #include "llparcel.h" #include "llstring.h" #include "message.h" #include "llsearchableui.h" #include "llsearcheditor.h" #include "bufferarray.h" #include "httphandler.h" #include "httpoptions.h" #include "httprequest.h" #include "httpresponse.h" // system includes #include #include // // Globals // LLStatusBar *gStatusBar = NULL; S32 STATUS_BAR_HEIGHT = 26; extern S32 MENU_BAR_HEIGHT; // TODO: these values ought to be in the XML too const S32 SIM_STAT_WIDTH = 8; const LLColor4 SIM_OK_COLOR(0.f, 1.f, 0.f, 1.f); const LLColor4 SIM_WARN_COLOR(1.f, 1.f, 0.f, 1.f); const LLColor4 SIM_FULL_COLOR(1.f, 0.f, 0.f, 1.f); const F32 ICON_TIMER_EXPIRY = 3.f; // How long the balance and health icons should flash after a change. static void onClickVolume(void*); LLStatusBar::LLStatusBar(const LLRect& rect) : LLPanel(), mTextTime(NULL), mTextFps(NULL), mSGBandwidth(NULL), mSGPacketLoss(NULL), mBtnVolume(NULL), mBoxBalance(NULL), mBalance(0), mExchangeRate(0.0), mExchangeRateFetchInFlight(false), mExchangeRateRequest(nullptr), mBalanceClicked(false), mObscureBalance(false), mHealth(100), mSquareMetersCredit(0), mSquareMetersCommitted(0), mFilterEdit(NULL), // Edit for filtering mSearchPanel(NULL) // Panel for filtering { setRect(rect); // status bar can possible overlay menus? setMouseOpaque(false); mFpsUpdateTimer = new LLFrameTimer(); buildFromFile("panel_status_bar.xml"); } LLStatusBar::~LLStatusBar() { delete mExchangeRateRequest; mExchangeRateRequest = nullptr; // LLView destructor cleans up children } //----------------------------------------------------------------------- // Overrides //----------------------------------------------------------------------- // virtual void LLStatusBar::draw() { LL_PROFILE_ZONE_SCOPED_CATEGORY_UI; // Deliver the LindeX reply on this thread. update() only invokes the handler. if (mExchangeRateRequest && mExchangeRateFetchInFlight) { mExchangeRateRequest->update(0); } refresh(); LLPanel::draw(); } bool LLStatusBar::handleRightMouseDown(S32 x, S32 y, MASK mask) { show_navbar_context_menu(this,x,y); return true; } bool LLStatusBar::postBuild() { gMenuBarView->setRightMouseDownCallback(boost::bind(&show_navbar_context_menu, _1, _2, _3)); mTextTime = getChild("TimeText" ); mTextFps = getChild("FpsText"); mTextFps->setClickedCallback( &LLStatusBar::onClickFps, this ); getChild("buyL")->setCommitCallback( boost::bind(&LLStatusBar::onClickBuyCurrency, this)); getChild("goShop")->setCommitCallback( boost::bind(&LLStatusBar::onClickShop, this)); mBoxBalance = getChild("balance"); mBoxBalance->setClickedCallback(&LLStatusBar::onClickRefreshBalance, this); mBoxBalance->setDoubleClickCallback([this](LLUICtrl*, S32 x, S32 y, MASK mask) { onClickToggleBalance(); }); mIconPresetsCamera = getChild( "presets_icon_camera" ); mIconPresetsCamera->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterPresetsCamera, this)); mIconPresetsGraphic = getChild( "presets_icon_graphic" ); mIconPresetsGraphic->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterPresets, this)); mBtnVolume = getChild( "volume_btn" ); mBtnVolume->setClickedCallback( onClickVolume, this ); mBtnVolume->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterVolume, this)); mMediaToggle = getChild("media_toggle_btn"); mMediaToggle->setClickedCallback( &LLStatusBar::onClickMediaToggle, this ); mMediaToggle->setMouseEnterCallback(boost::bind(&LLStatusBar::onMouseEnterNearbyMedia, this)); LLHints::getInstance()->registerHintTarget("linden_balance", getChild("balance_bg")->getHandle()); gSavedSettings.getControl("MuteAudio")->getSignal()->connect(boost::bind(&LLStatusBar::onVolumeChanged, this, _2)); gSavedSettings.getControl("EnableVoiceChat")->getSignal()->connect(boost::bind(&LLStatusBar::onVoiceChanged, this, _2)); gSavedSettings.getControl("ObscureBalanceInStatusBar")->getSignal()->connect(boost::bind(&LLStatusBar::onObscureBalanceChanged, this, _2)); if (!gSavedSettings.getBOOL("EnableVoiceChat") && LLAppViewer::instance()->isSecondInstance()) { // Indicate that second instance started without sound mBtnVolume->setImageUnselected(LLUI::getUIImage("VoiceMute_Off")); } mObscureBalance = gSavedSettings.getBOOL("ObscureBalanceInStatusBar"); requestExchangeRate(); // Adding Net Stat Graph S32 x = getRect().getWidth() - 2; S32 y = 0; LLRect r; r.set( x-SIM_STAT_WIDTH, y+MENU_BAR_HEIGHT-1, x, y+1); LLStatGraph::Params sgp; sgp.name("BandwidthGraph"); sgp.rect(r); sgp.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_RIGHT); sgp.mouse_opaque(false); sgp.stat.count_stat_float(&LLStatViewer::ACTIVE_MESSAGE_DATA_RECEIVED); sgp.units("Kbps"); sgp.precision(0); sgp.per_sec(true); mSGBandwidth = LLUICtrlFactory::create(sgp); addChild(mSGBandwidth); x -= SIM_STAT_WIDTH + 2; r.set( x-SIM_STAT_WIDTH, y+MENU_BAR_HEIGHT-1, x, y+1); //these don't seem to like being reused LLStatGraph::Params pgp; pgp.name("PacketLossPercent"); pgp.rect(r); pgp.follows.flags(FOLLOWS_BOTTOM | FOLLOWS_RIGHT); pgp.mouse_opaque(false); pgp.stat.sample_stat_float(&LLStatViewer::PACKETS_LOST_PERCENT); pgp.units("%"); pgp.min(0.f); pgp.max(5.f); pgp.precision(1); pgp.per_sec(false); LLStatGraph::Thresholds thresholds; thresholds.threshold.add(LLStatGraph::ThresholdParams().value(0.1).color(LLColor4::green)) .add(LLStatGraph::ThresholdParams().value(0.25f).color(LLColor4::yellow)) .add(LLStatGraph::ThresholdParams().value(0.6f).color(LLColor4::red)); pgp.thresholds(thresholds); mSGPacketLoss = LLUICtrlFactory::create(pgp); addChild(mSGPacketLoss); mPanelPresetsCameraPulldown = new LLPanelPresetsCameraPulldown(); addChild(mPanelPresetsCameraPulldown); mPanelPresetsCameraPulldown->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT); mPanelPresetsCameraPulldown->setVisible(false); mPanelPresetsPulldown = new LLPanelPresetsPulldown(); addChild(mPanelPresetsPulldown); mPanelPresetsPulldown->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT); mPanelPresetsPulldown->setVisible(false); mPanelVolumePulldown = new LLPanelVolumePulldown(); addChild(mPanelVolumePulldown); mPanelVolumePulldown->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT); mPanelVolumePulldown->setVisible(false); mPanelNearByMedia = new LLPanelNearByMedia(); addChild(mPanelNearByMedia); mPanelNearByMedia->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT); mPanelNearByMedia->setVisible(false); updateBalancePanelPosition(); // Hook up and init for filtering mFilterEdit = getChild( "search_menu_edit" ); mSearchPanel = getChild( "menu_search_panel" ); bool search_panel_visible = gSavedSettings.getBOOL("MenuSearch"); mSearchPanel->setVisible(search_panel_visible); mFilterEdit->setKeystrokeCallback(boost::bind(&LLStatusBar::onUpdateFilterTerm, this)); mFilterEdit->setCommitCallback(boost::bind(&LLStatusBar::onUpdateFilterTerm, this)); collectSearchableItems(); gSavedSettings.getControl("MenuSearch")->getCommitSignal()->connect(boost::bind(&LLStatusBar::updateMenuSearchVisibility, this, _2)); if (search_panel_visible) { updateMenuSearchPosition(); } return true; } // Per-frame updates of visibility void LLStatusBar::refresh() { static LLCachedControl show_net_stats(gSavedSettings, "ShowNetStats", false); bool net_stats_visible = show_net_stats; if (net_stats_visible) { // Adding Net Stat Meter back in F32 bwtotal = gViewerThrottle.getMaxBandwidth() / 1000.f; mSGBandwidth->setMin(0.f); mSGBandwidth->setMax(bwtotal*1.25f); //mSGBandwidth->setThreshold(0, bwtotal*0.75f); //mSGBandwidth->setThreshold(1, bwtotal); //mSGBandwidth->setThreshold(2, bwtotal); } if(mFpsUpdateTimer->getElapsedTimeF32() >= 1.0f) { mFpsUpdateTimer->reset(); S32 fps = (S32) llround(LLTrace::get_frame_recording().getPeriodMedianPerSec(LLStatViewer::FPS, 50)); if(fps < 1) fps = 1; S32 minFps = (S32) llround(LLTrace::get_frame_recording().getPeriodMinPerSec(LLStatViewer::FPS, 50)); S32 maxFps = (S32) llround(LLTrace::get_frame_recording().getPeriodMaxPerSec(LLStatViewer::FPS, 50)); F32 fpsQuality = (F32)minFps / (F32)fps; std::string fpsStr = std::to_string(fps); mTextFps->setLabel(fpsStr); if(fpsQuality < 0.5) mTextFps->setColor(LLColor4(0.6,0.0,0.0)); else if(fpsQuality < 0.7) mTextFps->setColor(LLColor4(0.7,0.5,0.0)); else if(fpsQuality < 0.9) mTextFps->setColor(LLColor4(0.5,0.7,0.0)); else mTextFps->setColor(LLColor4(0.0, 0.7, 0.15)); } // update clock every 10 seconds if(mClockUpdateTimer.getElapsedTimeF32() > 10.f) { mClockUpdateTimer.reset(); // Get current UTC time, adjusted for the user's clock // being off. time_t utc_time; utc_time = time_corrected(); static bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); std::string timeStr = use_24h ? getString("time") : getString("time_ampm"); LLSD substitution; substitution["datetime"] = (S32) utc_time; LLStringUtil::format (timeStr, substitution); mTextTime->setText(timeStr); // set the tooltip to have the date std::string dtStr = getString("timeTooltip"); LLStringUtil::format (dtStr, substitution); mTextTime->setToolTip (dtStr); } // The LindeX feed updates about every few minutes. Refresh the rate // a few times an hour so the USD figure stays current. Retry sooner // until the first rate arrives. const F32 rate_refresh_delay = (mExchangeRate > 0.0) ? (15.f * 60.f) : 30.f; if (!mExchangeRateFetchInFlight && mExchangeRateTimer.getElapsedTimeF32() > rate_refresh_delay) { requestExchangeRate(); } if (mBalanceClicked && mBalanceClickTimer.getElapsedTimeF32() > 1.f) { mBalanceClicked = false; sendMoneyBalanceRequest(); } LLRect r; const S32 MENU_RIGHT = gMenuBarView->getRightmostMenuEdge(); // reshape menu bar to its content's width if (MENU_RIGHT != gMenuBarView->getRect().getWidth()) { gMenuBarView->reshape(MENU_RIGHT, gMenuBarView->getRect().getHeight()); } mSGBandwidth->setVisible(net_stats_visible); mSGPacketLoss->setVisible(net_stats_visible); // update the master volume button state bool mute_audio = LLAppViewer::instance()->getMasterSystemAudioMute(); mBtnVolume->setToggleState(mute_audio); LLViewerMedia* media_inst = LLViewerMedia::getInstance(); // Disable media toggle if there's no media, parcel media, and no parcel audio // (or if media is disabled) bool button_enabled = (gSavedSettings.getBOOL("AudioStreamingMusic")||gSavedSettings.getBOOL("AudioStreamingMedia")) && (media_inst->hasInWorldMedia() || media_inst->hasParcelMedia() || media_inst->hasParcelAudio()); mMediaToggle->setEnabled(button_enabled); // Note the "sense" of the toggle is opposite whether media is playing or not bool any_media_playing = (media_inst->isAnyMediaPlaying() || media_inst->isParcelMediaPlaying() || media_inst->isParcelAudioPlaying()); mMediaToggle->setValue(!any_media_playing); } void LLStatusBar::setVisibleForMouselook(bool visible) { mTextTime->setVisible(visible); getChild("balance_bg")->setVisible(visible); mBoxBalance->setVisible(visible); mBtnVolume->setVisible(visible); mMediaToggle->setVisible(visible); mSGBandwidth->setVisible(visible); mSGPacketLoss->setVisible(visible); mSearchPanel->setVisible(visible && gSavedSettings.getBOOL("MenuSearch")); setBackgroundVisible(visible); mIconPresetsCamera->setVisible(visible); mIconPresetsGraphic->setVisible(visible); } void LLStatusBar::debitBalance(S32 debit) { setBalance(getBalance() - debit); } void LLStatusBar::creditBalance(S32 credit) { setBalance(getBalance() + credit); } // Same thousands separators as L$. Cents stay two digits after the locale decimal point. static std::string format_usd_amount(F64 usd) { const bool negative = usd < 0.0; const F64 magnitude = negative ? -usd : usd; const S64 cents_total = (S64)ll_round(magnitude * 100.0); const S32 dollars = (S32)(cents_total / 100); const S32 cents = (S32)(cents_total % 100); LLResMgr* res = LLResMgr::getInstance(); std::string whole = res->getMonetaryString(dollars); char decimal = res->getMonetaryDecimalPoint(); if (decimal == '\0') { decimal = '.'; } std::string amount = llformat("%s%c%02d", whole.c_str(), decimal, cents); if (negative) { amount.insert(0, "-"); } return amount; } void LLStatusBar::setBalance(S32 balance) { if (balance > getBalance() && getBalance() != 0) { LLFirstUse::receiveLindens(); } std::string money_str = LLResMgr::getInstance()->getMonetaryString( balance ); std::string usd_str; if (!mObscureBalance && mExchangeRate > 0.0) { usd_str = format_usd_amount((F64)balance / mExchangeRate); } LLStringUtil::format_map_t string_args; if (mObscureBalance) { string_args["[AMT]"] = "****"; string_args["[USD]"] = "****"; } else { string_args["[AMT]"] = money_str; if (!usd_str.empty()) { string_args["[USD]"] = usd_str; } } const char* label_name = (!mObscureBalance && mExchangeRate > 0.0) ? "buycurrencylabel_usd" : "buycurrencylabel"; std::string label_str = getString(label_name, string_args); mBoxBalance->setValue(label_str); mBoxBalance->setToolTipArg(LLStringExplicit("[AMT]"), llformat("%s", money_str.c_str())); if (!usd_str.empty()) { mBoxBalance->setToolTip(llformat( "L$ %s / $USD %s\nLindeX rate: L$ %.2f per USD\nClick to refresh your L$ balance.\nDouble-click to display or hide your L$ balance.", money_str.c_str(), usd_str.c_str(), mExchangeRate)); } updateBalancePanelPosition(); // If the search panel is shown, move this according to the new balance width. Parcel text will reshape itself in setParcelInfoText if (mSearchPanel && mSearchPanel->getVisible()) { updateMenuSearchPosition(); } if (mBalance && (fabs((F32)(mBalance - balance)) > gSavedSettings.getF32("UISndMoneyChangeThreshold"))) { if (mBalance > balance) make_ui_sound("UISndMoneyChangeDown"); else make_ui_sound("UISndMoneyChangeUp"); } if( balance != mBalance ) { mBalance = balance; } } // static void LLStatusBar::sendMoneyBalanceRequest() { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_MoneyBalanceRequest); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_MoneyData); msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null ); if (gDisconnected) { LL_DEBUGS() << "Trying to send message when disconnected, skipping balance request!" << LL_ENDL; return; } if (!gAgent.getRegion()) { LL_DEBUGS() << "LLAgent::sendReliableMessage No region for agent yet, skipping balance request!" << LL_ENDL; return; } // Double amount of retries due to this request initially happening during busy stage // Ideally this should be turned into a capability gMessageSystem->sendReliable(gAgent.getRegionHost(), LL_DEFAULT_RELIABLE_RETRIES * 2, true, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL); } void LLStatusBar::setHealth(S32 health) { //LL_INFOS() << "Setting health to: " << buffer << LL_ENDL; if( mHealth > health ) { if (mHealth > (health + gSavedSettings.getF32("UISndHealthReductionThreshold"))) { if (isAgentAvatarValid()) { if (gAgentAvatarp->getSex() == SEX_FEMALE) { make_ui_sound("UISndHealthReductionF"); } else { make_ui_sound("UISndHealthReductionM"); } } } } mHealth = health; } S32 LLStatusBar::getBalance() const { return mBalance; } void LLStatusBar::setExchangeRate(F64 lindens_per_usd) { if (lindens_per_usd <= 0.0) { return; } mExchangeRate = lindens_per_usd; setBalance(mBalance); } namespace { const char* const LINDEX_HOMEPAGE_URL = "http://secondlife.com/httprequest/homepage.php"; class ExchangeRateHandler : public LLCore::HttpHandler { public: void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse* response) override { if (!gStatusBar) { return; } if (!response) { gStatusBar->finishExchangeRateRequest(0.0, "empty LindeX response"); return; } const LLCore::HttpStatus status = response->getStatus(); if (!status) { gStatusBar->finishExchangeRateRequest(0.0, status.toString()); return; } std::string body; LLCore::BufferArray* raw = response->getBody(); if (raw && raw->size() > 0) { body.resize(raw->size()); raw->read(0, body.data(), body.size()); } std::istringstream lines(body); std::string line; std::string previous; F64 rate = 0.0; while (std::getline(lines, line)) { if (!line.empty() && line.back() == '\r') { line.pop_back(); } if (previous == "exchange_rate") { rate = std::strtod(line.c_str(), nullptr); break; } previous = line; } if (rate > 0.0) { gStatusBar->finishExchangeRateRequest(rate, std::string()); } else { gStatusBar->finishExchangeRateRequest(0.0, "LindeX feed did not contain an exchange rate"); } } }; } // namespace void LLStatusBar::finishExchangeRateRequest(F64 lindens_per_usd, const std::string& error) { mExchangeRateFetchInFlight = false; if (!error.empty()) { LL_WARNS("StatusBar") << "LindeX rate request failed: " << error << LL_ENDL; return; } setExchangeRate(lindens_per_usd); } void LLStatusBar::requestExchangeRate() { if (mExchangeRateFetchInFlight) { return; } if (!mExchangeRateRequest) { mExchangeRateRequest = new LLCore::HttpRequest(); } LLCore::HttpOptions::ptr_t httpOpts = std::make_shared(); httpOpts->setFollowRedirects(true); httpOpts->setTimeout(30); const LLCore::HttpHandle handle = mExchangeRateRequest->requestGet( LLCore::HttpRequest::DEFAULT_POLICY_ID, LINDEX_HOMEPAGE_URL, httpOpts, LLCore::HttpHeaders::ptr_t(), std::make_shared()); if (handle == LLCORE_HTTP_HANDLE_INVALID) { LL_WARNS("StatusBar") << "LindeX rate request was not queued: " << mExchangeRateRequest->getStatus().toString() << LL_ENDL; mExchangeRateTimer.reset(); return; } mExchangeRateFetchInFlight = true; mExchangeRateTimer.reset(); } S32 LLStatusBar::getHealth() const { return mHealth; } void LLStatusBar::setLandCredit(S32 credit) { mSquareMetersCredit = credit; } void LLStatusBar::setLandCommitted(S32 committed) { mSquareMetersCommitted = committed; } bool LLStatusBar::isUserTiered() const { return (mSquareMetersCredit > 0); } S32 LLStatusBar::getSquareMetersCredit() const { return mSquareMetersCredit; } S32 LLStatusBar::getSquareMetersCommitted() const { return mSquareMetersCommitted; } S32 LLStatusBar::getSquareMetersLeft() const { return mSquareMetersCredit - mSquareMetersCommitted; } void LLStatusBar::onClickBuyCurrency() { // open a currency floater - actual one open depends on // value specified in settings.xml LLBuyCurrencyHTML::openCurrencyFloater(); LLFirstUse::receiveLindens(false); } void LLStatusBar::onClickShop() { LLFloaterReg::showInstanceOrBringToFront("marketplace"); if (LLFloaterMarketplace* marketplace = LLFloaterReg::getTypedInstance("marketplace")) { marketplace->openMarketplace(); } } void LLStatusBar::onMouseEnterPresetsCamera() { LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder"); LLIconCtrl* icon = getChild( "presets_icon_camera" ); LLRect icon_rect = icon->getRect(); LLRect pulldown_rect = mPanelPresetsCameraPulldown->getRect(); pulldown_rect.setLeftTopAndSize(icon_rect.mLeft - (pulldown_rect.getWidth() - icon_rect.getWidth()), icon_rect.mBottom, pulldown_rect.getWidth(), pulldown_rect.getHeight()); pulldown_rect.translate(popup_holder->getRect().getWidth() - pulldown_rect.mRight, 0); mPanelPresetsCameraPulldown->setShape(pulldown_rect); // show the master presets pull-down LLUI::getInstance()->clearPopups(); LLUI::getInstance()->addPopup(mPanelPresetsCameraPulldown); mPanelNearByMedia->setVisible(false); mPanelVolumePulldown->setVisible(false); mPanelPresetsPulldown->setVisible(false); mPanelPresetsCameraPulldown->setVisible(true); } void LLStatusBar::onMouseEnterPresets() { LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder"); LLIconCtrl* icon = getChild( "presets_icon_graphic" ); LLRect icon_rect = icon->getRect(); LLRect pulldown_rect = mPanelPresetsPulldown->getRect(); pulldown_rect.setLeftTopAndSize(icon_rect.mLeft - (pulldown_rect.getWidth() - icon_rect.getWidth()), icon_rect.mBottom, pulldown_rect.getWidth(), pulldown_rect.getHeight()); pulldown_rect.translate(popup_holder->getRect().getWidth() - pulldown_rect.mRight, 0); mPanelPresetsPulldown->setShape(pulldown_rect); // show the master presets pull-down LLUI::getInstance()->clearPopups(); LLUI::getInstance()->addPopup(mPanelPresetsPulldown); mPanelNearByMedia->setVisible(false); mPanelVolumePulldown->setVisible(false); mPanelPresetsPulldown->setVisible(true); } void LLStatusBar::onMouseEnterVolume() { LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder"); LLButton* volbtn = getChild( "volume_btn" ); LLRect vol_btn_rect = volbtn->getRect(); LLRect volume_pulldown_rect = mPanelVolumePulldown->getRect(); volume_pulldown_rect.setLeftTopAndSize(vol_btn_rect.mLeft - (volume_pulldown_rect.getWidth() - vol_btn_rect.getWidth()), vol_btn_rect.mBottom, volume_pulldown_rect.getWidth(), volume_pulldown_rect.getHeight()); volume_pulldown_rect.translate(popup_holder->getRect().getWidth() - volume_pulldown_rect.mRight, 0); mPanelVolumePulldown->setShape(volume_pulldown_rect); // show the master volume pull-down LLUI::getInstance()->clearPopups(); LLUI::getInstance()->addPopup(mPanelVolumePulldown); mPanelPresetsCameraPulldown->setVisible(false); mPanelPresetsPulldown->setVisible(false); mPanelNearByMedia->setVisible(false); mPanelVolumePulldown->setVisible(true); } void LLStatusBar::onMouseEnterNearbyMedia() { LLView* popup_holder = gViewerWindow->getRootView()->getChildView("popup_holder"); LLRect nearby_media_rect = mPanelNearByMedia->getRect(); LLButton* nearby_media_btn = getChild( "media_toggle_btn" ); LLRect nearby_media_btn_rect = nearby_media_btn->getRect(); nearby_media_rect.setLeftTopAndSize(nearby_media_btn_rect.mLeft - (nearby_media_rect.getWidth() - nearby_media_btn_rect.getWidth())/2, nearby_media_btn_rect.mBottom, nearby_media_rect.getWidth(), nearby_media_rect.getHeight()); // force onscreen nearby_media_rect.translate(popup_holder->getRect().getWidth() - nearby_media_rect.mRight, 0); // show the master volume pull-down mPanelNearByMedia->setShape(nearby_media_rect); LLUI::getInstance()->clearPopups(); LLUI::getInstance()->addPopup(mPanelNearByMedia); mPanelPresetsCameraPulldown->setVisible(false); mPanelPresetsPulldown->setVisible(false); mPanelVolumePulldown->setVisible(false); mPanelNearByMedia->setVisible(true); } static void onClickVolume(void* data) { // toggle the master mute setting bool mute_audio = LLAppViewer::instance()->getMasterSystemAudioMute(); LLAppViewer::instance()->setMasterSystemAudioMute(!mute_audio); } //static void LLStatusBar::onClickRefreshBalance(void* data) { LLStatusBar* status_bar = (LLStatusBar*)data; if (!status_bar->mBalanceClicked) { // Schedule a balance request message: status_bar->mBalanceClicked = true; status_bar->mBalanceClickTimer.reset(); } // The refresh of the display (call to setBalance()) will be done by process_money_balance_reply() } void LLStatusBar::onClickToggleBalance() { mObscureBalance = !mObscureBalance; gSavedSettings.setBOOL("ObscureBalanceInStatusBar", mObscureBalance); setBalance(mBalance); mBalanceClicked = false; // supress click } //static void LLStatusBar::onClickMediaToggle(void* data) { LLStatusBar *status_bar = (LLStatusBar*)data; // "Selected" means it was showing the "play" icon (so media was playing), and now it shows "pause", so turn off media bool pause = status_bar->mMediaToggle->getValue(); LLViewerMedia::getInstance()->setAllMediaPaused(pause); } //static void LLStatusBar::onClickFps(void* data) { LLFloaterReg::toggleInstance("mpv_performance"); } bool can_afford_transaction(S32 cost) { return((cost <= 0)||((gStatusBar) && (gStatusBar->getBalance() >=cost))); } void LLStatusBar::onVolumeChanged(const LLSD& newvalue) { refresh(); } void LLStatusBar::onVoiceChanged(const LLSD& newvalue) { if (newvalue.asBoolean()) { // Second instance starts with "VoiceMute_Off" icon, fix it mBtnVolume->setImageUnselected(LLUI::getUIImage("Audio_Off")); } refresh(); } void LLStatusBar::onObscureBalanceChanged(const LLSD& newvalue) { mObscureBalance = newvalue.asBoolean(); setBalance(mBalance); } void LLStatusBar::onUpdateFilterTerm() { LLWString searchValue = utf8str_to_wstring( mFilterEdit->getValue() ); LLWStringUtil::toLower( searchValue ); if( !mSearchData || mSearchData->mLastFilter == searchValue ) return; mSearchData->mLastFilter = searchValue; mSearchData->mRootMenu->hightlightAndHide( searchValue ); gMenuBarView->needsArrange(); } void collectChildren( LLMenuGL *aMenu, ll::statusbar::SearchableItemPtr aParentMenu ) { for( U32 i = 0; i < aMenu->getItemCount(); ++i ) { LLMenuItemGL *pMenu = aMenu->getItem( i ); ll::statusbar::SearchableItemPtr pItem = std::make_shared(); pItem->mCtrl = pMenu; pItem->mMenu = pMenu; pItem->mLabel = utf8str_to_wstring( pMenu->ll::ui::SearchableControl::getSearchText() ); LLWStringUtil::toLower( pItem->mLabel ); aParentMenu->mChildren.push_back( pItem ); LLMenuItemBranchGL *pBranch = dynamic_cast< LLMenuItemBranchGL* >( pMenu ); if( pBranch ) collectChildren( pBranch->getBranch(), pItem ); } } void LLStatusBar::collectSearchableItems() { mSearchData = std::make_unique(); ll::statusbar::SearchableItemPtr pItem = std::make_shared(); mSearchData->mRootMenu = pItem; collectChildren( gMenuBarView, pItem ); } void LLStatusBar::updateMenuSearchVisibility(const LLSD& data) { bool visible = data.asBoolean(); mSearchPanel->setVisible(visible); if (!visible) { mFilterEdit->setText(LLStringUtil::null); onUpdateFilterTerm(); } else { updateMenuSearchPosition(); } } void LLStatusBar::updateMenuSearchPosition() { const S32 HPAD = 12; LLRect balanceRect = getChildView("balance_bg")->getRect(); LLRect searchRect = mSearchPanel->getRect(); S32 w = searchRect.getWidth(); searchRect.mLeft = balanceRect.mLeft - w - HPAD; searchRect.mRight = searchRect.mLeft + w; mSearchPanel->setShape( searchRect ); } void LLStatusBar::updateBalancePanelPosition() { // Resize the L$ balance background to be wide enough for your balance plus the buy button const S32 HPAD = 24; LLRect balance_rect = mBoxBalance->getTextBoundingRect(); LLRect buy_rect = getChildView("buyL")->getRect(); LLRect shop_rect = getChildView("goShop")->getRect(); LLView* balance_bg_view = getChildView("balance_bg"); LLRect balance_bg_rect = balance_bg_view->getRect(); balance_bg_rect.mLeft = balance_bg_rect.mRight - (buy_rect.getWidth() + shop_rect.getWidth() + balance_rect.getWidth() + HPAD); balance_bg_view->setShape(balance_bg_rect); } void LLStatusBar::setBalanceVisible(bool visible) { mBoxBalance->setVisible(visible); } // Implements secondlife:///app/balance/request to request a L$ balance // update via UDP message system. JC class LLBalanceHandler : public LLCommandHandler { public: // Requires "trusted" browser/URL source LLBalanceHandler() : LLCommandHandler("balance", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (tokens.size() == 1 && tokens[0].asString() == "request") { LLStatusBar::sendMoneyBalanceRequest(); return true; } return false; } }; // register with command dispatch system LLBalanceHandler gBalanceHandler;