/** * @file llnavigationbar.cpp * @brief Navigation bar implementation * * $LicenseInfo:firstyear=2009&license=viewergpl$ * * Copyright (c) 2009, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at * http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llnavigationbar.h" #include #include #include #include #include #include "llagent.h" #include "llfloaterhtmlhelp.h" #include "lllocationhistory.h" #include "lllocationinputctrl.h" #include "llteleporthistory.h" #include "llslurl.h" #include "llurlsimstring.h" #include "llviewerinventory.h" #include "llviewermenu.h" #include "llviewerparcelmgr.h" #include "llworldmap.h" //-- LLTeleportHistoryMenuItem ----------------------------------------------- /** * Item look varies depending on the type (backward/current/forward). */ class LLTeleportHistoryMenuItem : public LLMenuItemCallGL { public: typedef enum e_item_type { TYPE_BACKWARD, TYPE_CURRENT, TYPE_FORWARD, } EType; struct Params : public LLInitParam::Block { Mandatory item_type; Params() {} Params(EType type, std::string title); }; /*virtual*/ void draw(); /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); private: LLTeleportHistoryMenuItem(const Params&); friend class LLUICtrlFactory; static const S32 ICON_WIDTH = 16; static const S32 ICON_HEIGHT = 16; static const std::string ICON_IMG_BACKWARD; static const std::string ICON_IMG_FORWARD; LLIconCtrl* mArrowIcon; }; const std::string LLTeleportHistoryMenuItem::ICON_IMG_BACKWARD("teleport_history_backward.tga"); const std::string LLTeleportHistoryMenuItem::ICON_IMG_FORWARD("teleport_history_forward.tga"); LLTeleportHistoryMenuItem::Params::Params(EType type, std::string title) { item_type(type); font.name("SansSerif"); if (type == TYPE_CURRENT) font.style("BOLD"); else title = " " + title; name(title); label(title); } LLTeleportHistoryMenuItem::LLTeleportHistoryMenuItem(const Params& p) : LLMenuItemCallGL(p), mArrowIcon(NULL) { LLIconCtrl::Params icon_params; icon_params.name("icon"); icon_params.rect(LLRect(0, ICON_HEIGHT, ICON_WIDTH, 0)); icon_params.mouse_opaque(false); icon_params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); icon_params.tab_stop(false); icon_params.visible(false); mArrowIcon = LLUICtrlFactory::create (icon_params); // no image for the current item if (p.item_type == TYPE_BACKWARD) mArrowIcon->setValue(ICON_IMG_BACKWARD); else if (p.item_type == TYPE_FORWARD) mArrowIcon->setValue(ICON_IMG_FORWARD); addChild(mArrowIcon); } void LLTeleportHistoryMenuItem::draw() { // Draw menu item itself. LLMenuItemCallGL::draw(); // Draw children if any. *TODO: move this to LLMenuItemGL? LLUICtrl::draw(); } void LLTeleportHistoryMenuItem::onMouseEnter(S32 x, S32 y, MASK mask) { mArrowIcon->setVisible(TRUE); } void LLTeleportHistoryMenuItem::onMouseLeave(S32 x, S32 y, MASK mask) { mArrowIcon->setVisible(FALSE); } //-- LNavigationBar ---------------------------------------------------------- /* TODO: - Load navbar height from saved settings (as it's done for status bar) or think of a better way. */ S32 NAVIGATION_BAR_HEIGHT = 60; // *HACK LLNavigationBar* LLNavigationBar::sInstance = 0; LLNavigationBar* LLNavigationBar::getInstance() { if (!sInstance) sInstance = new LLNavigationBar(); return sInstance; } LLNavigationBar::LLNavigationBar() : mTeleportHistoryMenu(NULL), mLocationContextMenu(NULL), mBtnBack(NULL), mBtnForward(NULL), mBtnHome(NULL), mBtnHelp(NULL), mCmbLocation(NULL), mLeSearch(NULL) { setIsChrome(TRUE); // Register callbacks and load the location field context menu (NB: the order matters). mCommitCallbackRegistrar.add("Navbar.Action", boost::bind(&LLNavigationBar::onLocationContextMenuItemClicked, this, _2)); mEnableCallbackRegistrar.add("Navbar.EnableMenuItem", boost::bind(&LLNavigationBar::onLocationContextMenuItemEnabled, this, _2)); LLUICtrlFactory::getInstance()->buildPanel(this, "panel_navigation_bar.xml"); // navigation bar can never get a tab setFocusRoot(FALSE); } LLNavigationBar::~LLNavigationBar() { sInstance = 0; } BOOL LLNavigationBar::postBuild() { mBtnBack = getChild("back_btn"); mBtnForward = getChild("forward_btn"); mBtnHome = getChild("home_btn"); mBtnHelp = getChild("help_btn"); mCmbLocation= getChild("location_combo"); mLeSearch = getChild("search_input"); LLButton* search_btn = getChild("search_btn"); if (!mBtnBack || !mBtnForward || !mBtnHome || !mBtnHelp || !mCmbLocation || !mLeSearch || !search_btn) { llwarns << "Malformed navigation bar" << llendl; return FALSE; } mBtnBack->setEnabled(FALSE); mBtnBack->setClickedCallback(boost::bind(&LLNavigationBar::onBackButtonClicked, this)); mBtnBack->setHeldDownCallback(boost::bind(&LLNavigationBar::onBackOrForwardButtonHeldDown, this, _2)); mBtnForward->setEnabled(FALSE); mBtnForward->setClickedCallback(boost::bind(&LLNavigationBar::onForwardButtonClicked, this)); mBtnForward->setHeldDownCallback(boost::bind(&LLNavigationBar::onBackOrForwardButtonHeldDown, this, _2)); mBtnHome->setClickedCallback(boost::bind(&LLNavigationBar::onHomeButtonClicked, this)); mBtnHelp->setClickedCallback(boost::bind(&LLNavigationBar::onHelpButtonClicked, this)); mCmbLocation->setSelectionCallback(boost::bind(&LLNavigationBar::onLocationSelection, this)); mLeSearch->setCommitCallback(boost::bind(&LLNavigationBar::onSearchCommit, this)); search_btn->setClickedCallback(boost::bind(&LLNavigationBar::onSearchCommit, this)); // Load the location field context menu mLocationContextMenu = LLUICtrlFactory::getInstance()->createFromFile("menu_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); if (!mLocationContextMenu) { llwarns << "Error loading navigation bar context menu" << llendl; return FALSE; } // we'll be notified on teleport history changes LLTeleportHistory::getInstance()->setHistoryChangedCallback( boost::bind(&LLNavigationBar::onTeleportHistoryChanged, this)); return TRUE; } void LLNavigationBar::draw() { LLPanel::draw(); } BOOL LLNavigationBar::handleRightMouseDown(S32 x, S32 y, MASK mask) { // *HACK. We should use mCmbLocation's right click callback instead. // If the location field is clicked then show its context menu. if (mCmbLocation->getRect().pointInRect(x, y)) { // Pass the focus to the line editor when it is right-clicked mCmbLocation->setFocus(TRUE); if (mLocationContextMenu) { mLocationContextMenu->buildDrawLabels(); mLocationContextMenu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(this, mLocationContextMenu, x, y); } return TRUE; } return LLPanel:: handleRightMouseDown(x, y, mask); } void LLNavigationBar::onBackButtonClicked() { LLTeleportHistory::getInstance()->goBack(); } void LLNavigationBar::onBackOrForwardButtonHeldDown(const LLSD& param) { if (param["count"].asInteger() == 0) showTeleportHistoryMenu(); } void LLNavigationBar::onForwardButtonClicked() { LLTeleportHistory::getInstance()->goForward(); } void LLNavigationBar::onHomeButtonClicked() { gAgent.teleportHome(); } void LLNavigationBar::onHelpButtonClicked() { gViewerHtmlHelp.show(); } void LLNavigationBar::onSearchCommit() { invokeSearch(mLeSearch->getText()); } void LLNavigationBar::onTeleportHistoryMenuItemClicked(const LLSD& userdata) { int idx = userdata.asInteger(); LLTeleportHistory::getInstance()->goToItem(idx); } // This is called when user presses enter in the location input // or selects a location from the typed locations dropdown. void LLNavigationBar::onLocationSelection() { std::string typed_location = mCmbLocation->getSimple(); // Will not teleport to empty location. if (typed_location.empty()) return; std::string region_name; LLVector3 local_coords(128, 128, 0); // Is the typed location a SLURL? if (LLSLURL::isSLURL(typed_location)) { // Yes. Extract region name and local coordinates from it. S32 x = 0, y = 0, z = 0; if (LLURLSimString::parse(LLSLURL::stripProtocol(typed_location), ®ion_name, &x, &y, &z)) local_coords.set(x, y, z); else return; } else { // Treat it as region name. region_name = typed_location; } // Resolve the region name to its global coordinates. // If resolution succeeds we'll teleport. LLWorldMap::url_callback_t cb = boost::bind( &LLNavigationBar::onRegionNameResponse, this, typed_location, region_name, local_coords, _1, _2, _3, _4); LLWorldMap::getInstance()->sendNamedRegionRequest(region_name, cb, std::string("unused"), false); } void LLNavigationBar::onTeleportHistoryChanged() { // Update navigation controls. LLTeleportHistory* h = LLTeleportHistory::getInstance(); int cur_item = h->getCurrentItemIndex(); mBtnBack->setEnabled(cur_item > 0); mBtnForward->setEnabled(cur_item < ((int)h->getItems().size() - 1)); } void LLNavigationBar::rebuildTeleportHistoryMenu() { // Has the pop-up menu been built? if (mTeleportHistoryMenu) { // Clear it. mTeleportHistoryMenu->empty(); } else { // Create it. LLMenuGL::Params menu_p; menu_p.name("popup"); menu_p.can_tear_off(false); menu_p.visible(false); menu_p.bg_visible(true); menu_p.scrollable(true); mTeleportHistoryMenu = LLUICtrlFactory::create(menu_p); addChild(mTeleportHistoryMenu); } // Populate the menu with teleport history items. LLTeleportHistory* hist = LLTeleportHistory::getInstance(); const LLTeleportHistory::slurl_list_t& hist_items = hist->getItems(); int cur_item = hist->getCurrentItemIndex(); // Items will be shown in the reverse order, just like in Firefox. for (int i = (int)hist_items.size()-1; i >= 0; i--) { LLTeleportHistoryMenuItem::EType type; if (i < cur_item) type = LLTeleportHistoryMenuItem::TYPE_BACKWARD; else if (i > cur_item) type = LLTeleportHistoryMenuItem::TYPE_FORWARD; else type = LLTeleportHistoryMenuItem::TYPE_CURRENT; LLTeleportHistoryMenuItem::Params item_params(type, hist_items[i].mTitle); item_params.on_click.function(boost::bind(&LLNavigationBar::onTeleportHistoryMenuItemClicked, this, i)); mTeleportHistoryMenu->addChild(LLUICtrlFactory::create(item_params)); } } void LLNavigationBar::onRegionNameResponse( std::string typed_location, std::string region_name, LLVector3 local_coords, U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) { // Invalid location? if (!region_handle) { invokeSearch(typed_location); return; } // Location is valid. Add it to the typed locations history. S32 selected_item = mCmbLocation->getCurrentIndex(); if (selected_item == -1) // user has typed text { LLLocationHistory* lh = LLLocationHistory::getInstance(); mCmbLocation->add(typed_location); lh->addItem(typed_location); lh->save(); } // Teleport to the location. LLVector3d region_pos = from_region_handle(region_handle); LLVector3d global_pos = region_pos + (LLVector3d) local_coords; llinfos << "Teleporting to: " << global_pos << llendl; gAgent.teleportViaLocation(global_pos); } void LLNavigationBar::showTeleportHistoryMenu() { // Don't show the popup if teleport history is empty. if (LLTeleportHistory::getInstance()->isEmpty()) { lldebugs << "Teleport history is empty, will not show the menu." << llendl; return; } rebuildTeleportHistoryMenu(); if (mTeleportHistoryMenu == NULL) return; // *TODO: why to draw/update anything before showing the menu? mTeleportHistoryMenu->buildDrawLabels(); mTeleportHistoryMenu->updateParent(LLMenuGL::sMenuContainer); LLRect btnBackRect = mBtnBack->getRect(); LLMenuGL::showPopup(this, mTeleportHistoryMenu, btnBackRect.mLeft, btnBackRect.mBottom); // *HACK pass the mouse capturing to the drop-down menu gFocusMgr.setMouseCapture( NULL ); } void LLNavigationBar::onLocationContextMenuItemClicked(const LLSD& userdata) { std::string item = userdata.asString(); LLLineEditor* location_entry = mCmbLocation->getTextEntry(); if (item == std::string("copy_url")) { std::string sl_url = gAgent.getSLURL(); LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(sl_url)); LLSD args; args["SLURL"] = sl_url; LLNotifications::instance().add("CopySLURL", args); } else if (item == std::string("landmark")) { LLFloaterReg::showInstance("add_landmark"); } else if (item == std::string("cut")) { location_entry->cut(); } else if (item == std::string("copy")) { location_entry->copy(); } else if (item == std::string("paste")) { location_entry->paste(); } else if (item == std::string("delete")) { location_entry->deleteSelection(); } else if (item == std::string("select_all")) { location_entry->selectAll(); } } bool LLNavigationBar::onLocationContextMenuItemEnabled(const LLSD& userdata) { std::string item = userdata.asString(); const LLLineEditor* location_entry = mCmbLocation->getTextEntry(); if (item == std::string("can_cut")) { return location_entry->canCut(); } else if (item == std::string("can_copy")) { return location_entry->canCopy(); } else if (item == std::string("can_paste")) { return location_entry->canPaste(); } else if (item == std::string("can_delete")) { return location_entry->canDeselect(); } else if (item == std::string("can_select_all")) { return location_entry->canSelectAll(); } return false; } void LLNavigationBar::handleLoginComplete() { mCmbLocation->handleLoginComplete(); } void LLNavigationBar::invokeSearch(std::string search_text) { LLFloaterReg::showInstance("search", LLSD().insert("panel", "all").insert("id", LLSD(search_text))); }