/** * @file llpanelpicks.cpp * @brief LLPanelPicks and related class implementations * * $LicenseInfo:firstyear=2009&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 "llpanelpicks.h" #include "llagent.h" #include "llagentpicksinfo.h" #include "llavatarconstants.h" #include "llcommandhandler.h" #include "lldispatcher.h" #include "llflatlistview.h" #include "llfloaterreg.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterworldmap.h" #include "llnotificationsutil.h" #include "lltexturectrl.h" #include "lltoggleablemenu.h" #include "lltrans.h" #include "llviewergenericmessage.h" // send_generic_message #include "llmenugl.h" #include "llviewermenu.h" #include "llregistry.h" #include "llaccordionctrl.h" #include "llaccordionctrltab.h" #include "llavatarpropertiesprocessor.h" #include "llfloatersidepanelcontainer.h" #include "llpanelavatar.h" #include "llpanelprofile.h" #include "llpanelpick.h" #include "llpanelclassified.h" static const std::string XML_BTN_NEW = "new_btn"; static const std::string XML_BTN_DELETE = "trash_btn"; static const std::string XML_BTN_INFO = "info_btn"; static const std::string XML_BTN_TELEPORT = "teleport_btn"; static const std::string XML_BTN_SHOW_ON_MAP = "show_on_map_btn"; static const std::string PICK_ID("pick_id"); static const std::string PICK_CREATOR_ID("pick_creator_id"); static const std::string PICK_NAME("pick_name"); static const std::string CLASSIFIED_ID("classified_id"); static const std::string CLASSIFIED_NAME("classified_name"); static LLRegisterPanelClassWrapper<LLPanelPicks> t_panel_picks("panel_picks"); class LLPickHandler : public LLCommandHandler, public LLAvatarPropertiesObserver { public: std::set<LLUUID> mPickIds; // requires trusted browser to trigger LLPickHandler() : LLCommandHandler("pick", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { if (!LLUI::sSettingGroups["config"]->getBOOL("EnablePicks")) { LLNotificationsUtil::add("NoPicks", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); return true; } // handle app/classified/create urls first if (params.size() == 1 && params[0].asString() == "create") { createPick(); return true; } // then handle the general app/pick/{UUID}/{CMD} urls if (params.size() < 2) { return false; } // get the ID for the pick_id LLUUID pick_id; if (!pick_id.set(params[0], FALSE)) { return false; } // edit the pick in the side tray. // need to ask the server for more info first though... const std::string verb = params[1].asString(); if (verb == "edit") { mPickIds.insert(pick_id); LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest(gAgent.getID(),pick_id); return true; } else { llwarns << "unknown verb " << verb << llendl; return false; } } void createPick() { // open the new pick panel on the Picks floater LLFloater* picks_floater = LLFloaterReg::showInstance("picks"); LLPanelPicks* picks = picks_floater->findChild<LLPanelPicks>("panel_picks"); if (picks) { picks->createNewPick(); } } void editPick(LLPickData* pick_info) { LLSD params; params["open_tab_name"] = "panel_picks"; params["show_tab_panel"] = "edit_pick"; params["pick_id"] = pick_info->pick_id; params["avatar_id"] = pick_info->creator_id; params["snapshot_id"] = pick_info->snapshot_id; params["pick_name"] = pick_info->name; params["pick_desc"] = pick_info->desc; LLFloaterSidePanelContainer::showPanel("picks", params); } /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) { if (APT_PICK_INFO != type) { return; } // is this the pick that we asked for? LLPickData* pick_info = static_cast<LLPickData*>(data); if (!pick_info || mPickIds.find(pick_info->pick_id) == mPickIds.end()) { return; } // open the edit side tray for this pick if (pick_info->creator_id == gAgent.getID()) { editPick(pick_info); } else { llwarns << "Can't edit a pick you did not create" << llendl; } // remove our observer now that we're done mPickIds.erase(pick_info->pick_id); LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); } }; LLPickHandler gPickHandler; class LLClassifiedHandler : public LLCommandHandler, public LLAvatarPropertiesObserver { public: // throttle calls from untrusted browsers LLClassifiedHandler() : LLCommandHandler("classified", UNTRUSTED_THROTTLE) {} std::set<LLUUID> mClassifiedIds; std::string mRequestVerb; bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { if (!LLUI::sSettingGroups["config"]->getBOOL("EnableClassifieds")) { LLNotificationsUtil::add("NoClassifieds", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); return true; } // handle app/classified/create urls first if (params.size() == 1 && params[0].asString() == "create") { createClassified(); return true; } // then handle the general app/classified/{UUID}/{CMD} urls if (params.size() < 2) { return false; } // get the ID for the classified LLUUID classified_id; if (!classified_id.set(params[0], FALSE)) { return false; } // show the classified in the side tray. // need to ask the server for more info first though... const std::string verb = params[1].asString(); if (verb == "about") { mRequestVerb = verb; mClassifiedIds.insert(classified_id); LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); return true; } else if (verb == "edit") { mRequestVerb = verb; mClassifiedIds.insert(classified_id); LLAvatarPropertiesProcessor::getInstance()->addObserver(LLUUID(), this); LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(classified_id); return true; } return false; } void createClassified() { // open the new classified panel on the Picks floater LLFloater* picks_floater = LLFloaterReg::showInstance("picks"); LLPanelPicks* picks = picks_floater->findChild<LLPanelPicks>("panel_picks"); if (picks) { picks->createNewClassified(); } } void openClassified(LLAvatarClassifiedInfo* c_info) { if (mRequestVerb == "about") { // open the classified info panel on the Me > Picks sidetray LLSD params; params["id"] = c_info->creator_id; params["open_tab_name"] = "panel_picks"; params["show_tab_panel"] = "classified_details"; params["classified_id"] = c_info->classified_id; params["classified_creator_id"] = c_info->creator_id; params["classified_snapshot_id"] = c_info->snapshot_id; params["classified_name"] = c_info->name; params["classified_desc"] = c_info->description; params["from_search"] = true; LLFloaterSidePanelContainer::showPanel("picks", params); } else if (mRequestVerb == "edit") { if (c_info->creator_id == gAgent.getID()) { llwarns << "edit in progress" << llendl; // open the new classified panel on the Me > Picks sidetray LLSD params; params["id"] = gAgent.getID(); params["open_tab_name"] = "panel_picks"; params["show_tab_panel"] = "edit_classified"; params["classified_id"] = c_info->classified_id; LLFloaterSidePanelContainer::showPanel("my_profile", params); } else { llwarns << "Can't edit a classified you did not create" << llendl; } } } /*virtual*/ void processProperties(void* data, EAvatarProcessorType type) { if (APT_CLASSIFIED_INFO != type) { return; } // is this the classified that we asked for? LLAvatarClassifiedInfo* c_info = static_cast<LLAvatarClassifiedInfo*>(data); if (!c_info || mClassifiedIds.find(c_info->classified_id) == mClassifiedIds.end()) { return; } // open the detail side tray for this classified openClassified(c_info); // remove our observer now that we're done mClassifiedIds.erase(c_info->classified_id); LLAvatarPropertiesProcessor::getInstance()->removeObserver(LLUUID(), this); } }; LLClassifiedHandler gClassifiedHandler; ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- // LLPanelPicks //----------------------------------------------------------------------------- LLPanelPicks::LLPanelPicks() : LLPanelProfileTab(), mPopupMenu(NULL), mProfilePanel(NULL), mPickPanel(NULL), mPicksList(NULL), mClassifiedsList(NULL), mPanelPickInfo(NULL), mPanelPickEdit(NULL), mPlusMenu(NULL), mPicksAccTab(NULL), mClassifiedsAccTab(NULL), mPanelClassifiedInfo(NULL), mNoClassifieds(false), mNoPicks(false) { } LLPanelPicks::~LLPanelPicks() { if(getAvatarId().notNull()) { LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this); } } void* LLPanelPicks::create(void* data /* = NULL */) { return new LLPanelPicks(); } void LLPanelPicks::updateData() { // Send Picks request only when we need to, not on every onOpen(during tab switch). if(isDirty()) { mNoPicks = false; mNoClassifieds = false; mNoItemsLabel->setValue(LLTrans::getString("PicksClassifiedsLoadingText")); mNoItemsLabel->setVisible(TRUE); mPicksList->clear(); LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId()); mClassifiedsList->clear(); LLAvatarPropertiesProcessor::getInstance()->sendAvatarClassifiedsRequest(getAvatarId()); } } void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type) { if(APT_PICKS == type) { LLAvatarPicks* avatar_picks = static_cast<LLAvatarPicks*>(data); if(avatar_picks && getAvatarId() == avatar_picks->target_id) { std::string full_name; gCacheName->getFullName(getAvatarId(), full_name); getChild<LLUICtrl>("pick_title")->setTextArg("[NAME]", full_name); // Save selection, to be able to edit same item after saving changes. See EXT-3023. LLUUID selected_id = mPicksList->getSelectedValue()[PICK_ID]; mPicksList->clear(); LLAvatarPicks::picks_list_t::const_iterator it = avatar_picks->picks_list.begin(); for(; avatar_picks->picks_list.end() != it; ++it) { LLUUID pick_id = it->first; std::string pick_name = it->second; LLPickItem* picture = LLPickItem::create(); picture->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this)); picture->setPickName(pick_name); picture->setPickId(pick_id); picture->setCreatorId(getAvatarId()); LLAvatarPropertiesProcessor::instance().addObserver(getAvatarId(), picture); picture->update(); LLSD pick_value = LLSD(); pick_value.insert(PICK_ID, pick_id); pick_value.insert(PICK_NAME, pick_name); pick_value.insert(PICK_CREATOR_ID, getAvatarId()); mPicksList->addItem(picture, pick_value); // Restore selection by item id. if ( pick_id == selected_id ) mPicksList->selectItemByValue(pick_value); picture->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickPickItem, this, _1)); picture->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); picture->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this)); } showAccordion("tab_picks", mPicksList->size()); resetDirty(); updateButtons(); } mNoPicks = !mPicksList->size(); } else if(APT_CLASSIFIEDS == type) { LLAvatarClassifieds* c_info = static_cast<LLAvatarClassifieds*>(data); if(c_info && getAvatarId() == c_info->target_id) { // do not clear classified list in case we will receive two or more data packets. // list has been cleared in updateData(). (fix for EXT-6436) LLAvatarClassifieds::classifieds_list_t::const_iterator it = c_info->classifieds_list.begin(); for(; c_info->classifieds_list.end() != it; ++it) { LLAvatarClassifieds::classified_data c_data = *it; LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), c_data.classified_id); c_item->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this)); c_item->setClassifiedName(c_data.name); LLSD pick_value = LLSD(); pick_value.insert(CLASSIFIED_ID, c_data.classified_id); pick_value.insert(CLASSIFIED_NAME, c_data.name); if (!findClassifiedById(c_data.classified_id)) { mClassifiedsList->addItem(c_item, pick_value); } c_item->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickClassifiedItem, this, _1)); c_item->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); c_item->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this)); } showAccordion("tab_classifieds", mClassifiedsList->size()); resetDirty(); updateButtons(); } mNoClassifieds = !mClassifiedsList->size(); } bool no_data = mNoPicks && mNoClassifieds; mNoItemsLabel->setVisible(no_data); if (no_data) { if(getAvatarId() == gAgentID) { mNoItemsLabel->setValue(LLTrans::getString("NoPicksClassifiedsText")); } else { mNoItemsLabel->setValue(LLTrans::getString("NoAvatarPicksClassifiedsText")); } } } LLPickItem* LLPanelPicks::getSelectedPickItem() { LLPanel* selected_item = mPicksList->getSelectedItem(); if (!selected_item) return NULL; return dynamic_cast<LLPickItem*>(selected_item); } LLClassifiedItem* LLPanelPicks::getSelectedClassifiedItem() { LLPanel* selected_item = mClassifiedsList->getSelectedItem(); if (!selected_item) { return NULL; } return dynamic_cast<LLClassifiedItem*>(selected_item); } BOOL LLPanelPicks::postBuild() { mPicksList = getChild<LLFlatListView>("picks_list"); mClassifiedsList = getChild<LLFlatListView>("classifieds_list"); mPicksList->setCommitOnSelectionChange(true); mClassifiedsList->setCommitOnSelectionChange(true); mPicksList->setCommitCallback(boost::bind(&LLPanelPicks::onListCommit, this, mPicksList)); mClassifiedsList->setCommitCallback(boost::bind(&LLPanelPicks::onListCommit, this, mClassifiedsList)); mPicksList->setNoItemsCommentText(getString("no_picks")); mClassifiedsList->setNoItemsCommentText(getString("no_classifieds")); mNoItemsLabel = getChild<LLUICtrl>("picks_panel_text"); childSetAction(XML_BTN_NEW, boost::bind(&LLPanelPicks::onClickPlusBtn, this)); childSetAction(XML_BTN_DELETE, boost::bind(&LLPanelPicks::onClickDelete, this)); childSetAction(XML_BTN_TELEPORT, boost::bind(&LLPanelPicks::onClickTeleport, this)); childSetAction(XML_BTN_SHOW_ON_MAP, boost::bind(&LLPanelPicks::onClickMap, this)); childSetAction(XML_BTN_INFO, boost::bind(&LLPanelPicks::onClickInfo, this)); mPicksAccTab = getChild<LLAccordionCtrlTab>("tab_picks"); mPicksAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mPicksAccTab)); mPicksAccTab->setDisplayChildren(true); mClassifiedsAccTab = getChild<LLAccordionCtrlTab>("tab_classifieds"); mClassifiedsAccTab->setDropDownStateChangedCallback(boost::bind(&LLPanelPicks::onAccordionStateChanged, this, mClassifiedsAccTab)); mClassifiedsAccTab->setDisplayChildren(false); LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registar; registar.add("Pick.Info", boost::bind(&LLPanelPicks::onClickInfo, this)); registar.add("Pick.Edit", boost::bind(&LLPanelPicks::onClickMenuEdit, this)); registar.add("Pick.Teleport", boost::bind(&LLPanelPicks::onClickTeleport, this)); registar.add("Pick.Map", boost::bind(&LLPanelPicks::onClickMap, this)); registar.add("Pick.Delete", boost::bind(&LLPanelPicks::onClickDelete, this)); LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registar; enable_registar.add("Pick.Enable", boost::bind(&LLPanelPicks::onEnableMenuItem, this, _2)); mPopupMenu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_picks.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); LLUICtrl::CommitCallbackRegistry::ScopedRegistrar plus_registar; plus_registar.add("Picks.Plus.Action", boost::bind(&LLPanelPicks::onPlusMenuItemClicked, this, _2)); mEnableCallbackRegistrar.add("Picks.Plus.Enable", boost::bind(&LLPanelPicks::isActionEnabled, this, _2)); mPlusMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_picks_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); return TRUE; } void LLPanelPicks::onPlusMenuItemClicked(const LLSD& param) { std::string value = param.asString(); if("new_pick" == value) { createNewPick(); } else if("new_classified" == value) { createNewClassified(); } } bool LLPanelPicks::isActionEnabled(const LLSD& userdata) const { std::string command_name = userdata.asString(); if (command_name == "new_pick" && LLAgentPicksInfo::getInstance()->isPickLimitReached()) { return false; } return true; } bool LLPanelPicks::isClassifiedPublished(LLClassifiedItem* c_item) { if(c_item) { LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; if(panel) { return !panel->isNewWithErrors(); } // we've got this classified from server - it's published return true; } return false; } void LLPanelPicks::onAccordionStateChanged(const LLAccordionCtrlTab* acc_tab) { if(!mPicksAccTab->getDisplayChildren()) { mPicksList->resetSelection(true); } if(!mClassifiedsAccTab->getDisplayChildren()) { mClassifiedsList->resetSelection(true); } updateButtons(); } void LLPanelPicks::onOpen(const LLSD& key) { const LLUUID id(key.asUUID()); BOOL self = (gAgent.getID() == id); // only agent can edit her picks getChildView("edit_panel")->setEnabled(self); getChildView("edit_panel")->setVisible( self); // Disable buttons when viewing profile for first time if(getAvatarId() != id) { getChildView(XML_BTN_INFO)->setEnabled(FALSE); getChildView(XML_BTN_TELEPORT)->setEnabled(FALSE); getChildView(XML_BTN_SHOW_ON_MAP)->setEnabled(FALSE); } // and see a special title - set as invisible by default in xml file if (self) { getChildView("pick_title")->setVisible( !self); getChildView("pick_title_agent")->setVisible( self); mPopupMenu->setItemVisible("pick_delete", TRUE); mPopupMenu->setItemVisible("pick_edit", TRUE); mPopupMenu->setItemVisible("pick_separator", TRUE); } if(getAvatarId() != id) { showAccordion("tab_picks", false); showAccordion("tab_classifieds", false); mPicksList->goToTop(); // Set dummy value to make panel dirty and make it reload picks setValue(LLSD()); } LLPanelProfileTab::onOpen(key); } void LLPanelPicks::onClosePanel() { if (mPanelClassifiedInfo) { onPanelClassifiedClose(mPanelClassifiedInfo); } if (mPanelPickInfo) { onPanelPickClose(mPanelPickInfo); } } void LLPanelPicks::onListCommit(const LLFlatListView* f_list) { // Make sure only one of the lists has selection. if(f_list == mPicksList) { mClassifiedsList->resetSelection(true); } else if(f_list == mClassifiedsList) { mPicksList->resetSelection(true); } else { llwarns << "Unknown list" << llendl; } updateButtons(); } //static void LLPanelPicks::onClickDelete() { LLSD value = mPicksList->getSelectedValue(); if (value.isDefined()) { LLSD args; args["PICK"] = value[PICK_NAME]; LLNotificationsUtil::add("DeleteAvatarPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackDeletePick, this, _1, _2)); return; } value = mClassifiedsList->getSelectedValue(); if(value.isDefined()) { LLSD args; args["NAME"] = value[CLASSIFIED_NAME]; LLNotificationsUtil::add("DeleteClassified", args, LLSD(), boost::bind(&LLPanelPicks::callbackDeleteClassified, this, _1, _2)); return; } } bool LLPanelPicks::callbackDeletePick(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLSD pick_value = mPicksList->getSelectedValue(); if (0 == option) { LLAvatarPropertiesProcessor::instance().sendPickDelete(pick_value[PICK_ID]); mPicksList->removeItemByValue(pick_value); } updateButtons(); return false; } bool LLPanelPicks::callbackDeleteClassified(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLSD value = mClassifiedsList->getSelectedValue(); if (0 == option) { LLAvatarPropertiesProcessor::instance().sendClassifiedDelete(value[CLASSIFIED_ID]); mClassifiedsList->removeItemByValue(value); } updateButtons(); return false; } bool LLPanelPicks::callbackTeleport( const LLSD& notification, const LLSD& response ) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (0 == option) { onClickTeleport(); } return false; } //static void LLPanelPicks::onClickTeleport() { LLPickItem* pick_item = getSelectedPickItem(); LLClassifiedItem* c_item = getSelectedClassifiedItem(); LLVector3d pos; if(pick_item) pos = pick_item->getPosGlobal(); else if(c_item) { pos = c_item->getPosGlobal(); LLPanelClassifiedInfo::sendClickMessage("teleport", false, c_item->getClassifiedId(), LLUUID::null, pos, LLStringUtil::null); } if (!pos.isExactlyZero()) { gAgent.teleportViaLocation(pos); LLFloaterWorldMap::getInstance()->trackLocation(pos); } } //static void LLPanelPicks::onClickMap() { LLPickItem* pick_item = getSelectedPickItem(); LLClassifiedItem* c_item = getSelectedClassifiedItem(); LLVector3d pos; if (pick_item) pos = pick_item->getPosGlobal(); else if(c_item) { LLPanelClassifiedInfo::sendClickMessage("map", false, c_item->getClassifiedId(), LLUUID::null, pos, LLStringUtil::null); pos = c_item->getPosGlobal(); } LLFloaterWorldMap::getInstance()->trackLocation(pos); LLFloaterReg::showInstance("world_map", "center"); } void LLPanelPicks::onRightMouseUpItem(LLUICtrl* item, S32 x, S32 y, MASK mask) { updateButtons(); if (mPopupMenu) { mPopupMenu->buildDrawLabels(); mPopupMenu->updateParent(LLMenuGL::sMenuContainer); ((LLContextMenu*)mPopupMenu)->show(x, y); LLMenuGL::showPopup(item, mPopupMenu, x, y); } } void LLPanelPicks::onDoubleClickPickItem(LLUICtrl* item) { LLSD pick_value = mPicksList->getSelectedValue(); if (pick_value.isUndefined()) return; LLSD args; args["PICK"] = pick_value[PICK_NAME]; LLNotificationsUtil::add("TeleportToPick", args, LLSD(), boost::bind(&LLPanelPicks::callbackTeleport, this, _1, _2)); } void LLPanelPicks::onDoubleClickClassifiedItem(LLUICtrl* item) { LLSD value = mClassifiedsList->getSelectedValue(); if (value.isUndefined()) return; LLSD args; args["CLASSIFIED"] = value[CLASSIFIED_NAME]; LLNotificationsUtil::add("TeleportToClassified", args, LLSD(), boost::bind(&LLPanelPicks::callbackTeleport, this, _1, _2)); } void LLPanelPicks::updateButtons() { bool has_selected = mPicksList->numSelected() > 0 || mClassifiedsList->numSelected() > 0; if (getAvatarId() == gAgentID) { getChildView(XML_BTN_DELETE)->setEnabled(has_selected); } getChildView(XML_BTN_INFO)->setEnabled(has_selected); getChildView(XML_BTN_TELEPORT)->setEnabled(has_selected); getChildView(XML_BTN_SHOW_ON_MAP)->setEnabled(has_selected); LLClassifiedItem* c_item = dynamic_cast<LLClassifiedItem*>(mClassifiedsList->getSelectedItem()); if(c_item) { getChildView(XML_BTN_INFO)->setEnabled(isClassifiedPublished(c_item)); } } void LLPanelPicks::setProfilePanel(LLPanelProfile* profile_panel) { mProfilePanel = profile_panel; } void LLPanelPicks::buildPickPanel() { // if (mPickPanel == NULL) // { // mPickPanel = new LLPanelPick(); // mPickPanel->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, NULL)); // } } void LLPanelPicks::onClickPlusBtn() { LLRect rect(getChildView(XML_BTN_NEW)->getRect()); mPlusMenu->updateParent(LLMenuGL::sMenuContainer); mPlusMenu->setButtonRect(rect, this); LLMenuGL::showPopup(this, mPlusMenu, rect.mLeft, rect.mTop); } void LLPanelPicks::createNewPick() { createPickEditPanel(); getProfilePanel()->openPanel(mPanelPickEdit, LLSD()); } void LLPanelPicks::createNewClassified() { LLPanelClassifiedEdit* panel = NULL; createClassifiedEditPanel(&panel); getProfilePanel()->openPanel(panel, LLSD()); } void LLPanelPicks::onClickInfo() { if(mPicksList->numSelected() > 0) { openPickInfo(); } else if(mClassifiedsList->numSelected() > 0) { openClassifiedInfo(); } } void LLPanelPicks::openPickInfo() { LLSD selected_value = mPicksList->getSelectedValue(); if (selected_value.isUndefined()) return; LLPickItem* pick = (LLPickItem*)mPicksList->getSelectedItem(); createPickInfoPanel(); LLSD params; params["pick_id"] = pick->getPickId(); params["avatar_id"] = pick->getCreatorId(); params["snapshot_id"] = pick->getSnapshotId(); params["pick_name"] = pick->getPickName(); params["pick_desc"] = pick->getPickDesc(); getProfilePanel()->openPanel(mPanelPickInfo, params); } void LLPanelPicks::openClassifiedInfo() { LLSD selected_value = mClassifiedsList->getSelectedValue(); if (selected_value.isUndefined()) return; LLClassifiedItem* c_item = getSelectedClassifiedItem(); LLSD params; params["classified_id"] = c_item->getClassifiedId(); params["classified_creator_id"] = c_item->getAvatarId(); params["classified_snapshot_id"] = c_item->getSnapshotId(); params["classified_name"] = c_item->getClassifiedName(); params["classified_desc"] = c_item->getDescription(); params["from_search"] = false; openClassifiedInfo(params); } void LLPanelPicks::openClassifiedInfo(const LLSD ¶ms) { createClassifiedInfoPanel(); getProfilePanel()->openPanel(mPanelClassifiedInfo, params); } void LLPanelPicks::openClassifiedEdit(const LLSD& params) { LLUUID classified_id = params["classified_id"].asUUID();; llinfos << "opening classified " << classified_id << " for edit" << llendl; editClassified(classified_id); } void LLPanelPicks::showAccordion(const std::string& name, bool show) { LLAccordionCtrlTab* tab = getChild<LLAccordionCtrlTab>(name); tab->setVisible(show); LLAccordionCtrl* acc = getChild<LLAccordionCtrl>("accordion"); acc->arrange(); } void LLPanelPicks::onPanelPickClose(LLPanel* panel) { getProfilePanel()->closePanel(panel); } void LLPanelPicks::onPanelPickSave(LLPanel* panel) { onPanelPickClose(panel); updateButtons(); } void LLPanelPicks::onPanelClassifiedSave(LLPanelClassifiedEdit* panel) { if(!panel->canClose()) { return; } if(panel->isNew()) { mEditClassifiedPanels[panel->getClassifiedId()] = panel; LLClassifiedItem* c_item = new LLClassifiedItem(getAvatarId(), panel->getClassifiedId()); c_item->fillIn(panel); LLSD c_value; c_value.insert(CLASSIFIED_ID, c_item->getClassifiedId()); c_value.insert(CLASSIFIED_NAME, c_item->getClassifiedName()); mClassifiedsList->addItem(c_item, c_value, ADD_TOP); c_item->setDoubleClickCallback(boost::bind(&LLPanelPicks::onDoubleClickClassifiedItem, this, _1)); c_item->setRightMouseUpCallback(boost::bind(&LLPanelPicks::onRightMouseUpItem, this, _1, _2, _3, _4)); c_item->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this)); c_item->childSetAction("info_chevron", boost::bind(&LLPanelPicks::onClickInfo, this)); // order does matter, showAccordion will invoke arrange for accordions. mClassifiedsAccTab->changeOpenClose(false); showAccordion("tab_classifieds", true); } else if(panel->isNewWithErrors()) { LLClassifiedItem* c_item = dynamic_cast<LLClassifiedItem*>(mClassifiedsList->getSelectedItem()); llassert(c_item); if (c_item) { c_item->fillIn(panel); } } else { onPanelClassifiedClose(panel); return; } onPanelPickClose(panel); updateButtons(); } void LLPanelPicks::onPanelClassifiedClose(LLPanelClassifiedInfo* panel) { if(panel->getInfoLoaded() && !panel->isDirty()) { std::vector<LLSD> values; mClassifiedsList->getValues(values); for(size_t n = 0; n < values.size(); ++n) { LLUUID c_id = values[n][CLASSIFIED_ID].asUUID(); if(panel->getClassifiedId() == c_id) { LLClassifiedItem* c_item = dynamic_cast<LLClassifiedItem*>( mClassifiedsList->getItemByValue(values[n])); llassert(c_item); if (c_item) { c_item->setClassifiedName(panel->getClassifiedName()); c_item->setDescription(panel->getDescription()); c_item->setSnapshotId(panel->getSnapshotId()); } } } } onPanelPickClose(panel); updateButtons(); } void LLPanelPicks::createPickInfoPanel() { if(!mPanelPickInfo) { mPanelPickInfo = LLPanelPickInfo::create(); mPanelPickInfo->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickInfo)); mPanelPickInfo->setEditPickCallback(boost::bind(&LLPanelPicks::onPanelPickEdit, this)); mPanelPickInfo->setVisible(FALSE); } } void LLPanelPicks::createClassifiedInfoPanel() { mPanelClassifiedInfo = LLPanelClassifiedInfo::create(); mPanelClassifiedInfo->setExitCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, mPanelClassifiedInfo)); mPanelClassifiedInfo->setEditClassifiedCallback(boost::bind(&LLPanelPicks::onPanelClassifiedEdit, this)); mPanelClassifiedInfo->setVisible(FALSE); } void LLPanelPicks::createClassifiedEditPanel(LLPanelClassifiedEdit** panel) { if(panel) { LLPanelClassifiedEdit* new_panel = LLPanelClassifiedEdit::create(); new_panel->setExitCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, new_panel)); new_panel->setSaveCallback(boost::bind(&LLPanelPicks::onPanelClassifiedSave, this, new_panel)); new_panel->setCancelCallback(boost::bind(&LLPanelPicks::onPanelClassifiedClose, this, new_panel)); new_panel->setVisible(FALSE); *panel = new_panel; } } void LLPanelPicks::createPickEditPanel() { mPanelPickEdit = LLPanelPickEdit::create(); mPanelPickEdit->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickEdit)); mPanelPickEdit->setSaveCallback(boost::bind(&LLPanelPicks::onPanelPickSave, this, mPanelPickEdit)); mPanelPickEdit->setCancelCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickEdit)); mPanelPickEdit->setVisible(FALSE); } // void LLPanelPicks::openPickEditPanel(LLPickItem* pick) // { // if(!pick) // { // return; // } // } // void LLPanelPicks::openPickInfoPanel(LLPickItem* pick) // { // if(!mPanelPickInfo) // { // mPanelPickInfo = LLPanelPickInfo::create(); // mPanelPickInfo->setExitCallback(boost::bind(&LLPanelPicks::onPanelPickClose, this, mPanelPickInfo)); // mPanelPickInfo->setEditPickCallback(boost::bind(&LLPanelPicks::onPanelPickEdit, this)); // mPanelPickInfo->setVisible(FALSE); // } // // LLSD params; // params["pick_id"] = pick->getPickId(); // params["avatar_id"] = pick->getCreatorId(); // params["snapshot_id"] = pick->getSnapshotId(); // params["pick_name"] = pick->getPickName(); // params["pick_desc"] = pick->getPickDesc(); // // getProfilePanel()->openPanel(mPanelPickInfo, params); // } void LLPanelPicks::openPickEdit(const LLSD& params) { createPickEditPanel(); getProfilePanel()->openPanel(mPanelPickEdit, params); } void LLPanelPicks::onPanelPickEdit() { LLSD selected_value = mPicksList->getSelectedValue(); if (selected_value.isUndefined()) return; LLPickItem* pick = dynamic_cast<LLPickItem*>(mPicksList->getSelectedItem()); createPickEditPanel(); LLSD params; params["pick_id"] = pick->getPickId(); params["avatar_id"] = pick->getCreatorId(); params["snapshot_id"] = pick->getSnapshotId(); params["pick_name"] = pick->getPickName(); params["pick_desc"] = pick->getPickDesc(); getProfilePanel()->openPanel(mPanelPickEdit, params); } void LLPanelPicks::onPanelClassifiedEdit() { LLSD selected_value = mClassifiedsList->getSelectedValue(); if (selected_value.isUndefined()) { return; } LLClassifiedItem* c_item = dynamic_cast<LLClassifiedItem*>(mClassifiedsList->getSelectedItem()); llassert(c_item); if (!c_item) { return; } editClassified(c_item->getClassifiedId()); } LLClassifiedItem *LLPanelPicks::findClassifiedById(const LLUUID& classified_id) { // HACK - find item by classified id. Should be a better way. std::vector<LLPanel*> items; mClassifiedsList->getItems(items); LLClassifiedItem* c_item = NULL; for(std::vector<LLPanel*>::iterator it = items.begin(); it != items.end(); ++it) { LLClassifiedItem *test_item = dynamic_cast<LLClassifiedItem*>(*it); if (test_item && test_item->getClassifiedId() == classified_id) { c_item = test_item; break; } } return c_item; } void LLPanelPicks::editClassified(const LLUUID& classified_id) { LLClassifiedItem* c_item = findClassifiedById(classified_id); if (!c_item) { llwarns << "item not found for classified_id " << classified_id << llendl; return; } LLSD params; params["classified_id"] = c_item->getClassifiedId(); params["classified_creator_id"] = c_item->getAvatarId(); params["snapshot_id"] = c_item->getSnapshotId(); params["name"] = c_item->getClassifiedName(); params["desc"] = c_item->getDescription(); params["category"] = (S32)c_item->getCategory(); params["content_type"] = (S32)c_item->getContentType(); params["auto_renew"] = c_item->getAutoRenew(); params["price_for_listing"] = c_item->getPriceForListing(); params["location_text"] = c_item->getLocationText(); LLPanelClassifiedEdit* panel = mEditClassifiedPanels[c_item->getClassifiedId()]; if(!panel) { createClassifiedEditPanel(&panel); mEditClassifiedPanels[c_item->getClassifiedId()] = panel; } getProfilePanel()->openPanel(panel, params); panel->setPosGlobal(c_item->getPosGlobal()); } void LLPanelPicks::onClickMenuEdit() { if(getSelectedPickItem()) { onPanelPickEdit(); } else if(getSelectedClassifiedItem()) { onPanelClassifiedEdit(); } } bool LLPanelPicks::onEnableMenuItem(const LLSD& user_data) { std::string param = user_data.asString(); LLClassifiedItem* c_item = dynamic_cast<LLClassifiedItem*>(mClassifiedsList->getSelectedItem()); if(c_item && "info" == param) { // dont show Info panel if classified was not created return isClassifiedPublished(c_item); } return true; } inline LLPanelProfile* LLPanelPicks::getProfilePanel() { llassert_always(NULL != mProfilePanel); return mProfilePanel; } //----------------------------------------------------------------------------- // LLPanelPicks //----------------------------------------------------------------------------- LLPickItem::LLPickItem() : LLPanel() , mPickID(LLUUID::null) , mCreatorID(LLUUID::null) , mParcelID(LLUUID::null) , mSnapshotID(LLUUID::null) , mNeedData(true) { buildFromFile("panel_pick_list_item.xml"); } LLPickItem::~LLPickItem() { if (mCreatorID.notNull()) { LLAvatarPropertiesProcessor::instance().removeObserver(mCreatorID, this); } } LLPickItem* LLPickItem::create() { return new LLPickItem(); } void LLPickItem::init(LLPickData* pick_data) { setPickDesc(pick_data->desc); setSnapshotId(pick_data->snapshot_id); mPosGlobal = pick_data->pos_global; mSimName = pick_data->sim_name; mPickDescription = pick_data->desc; mUserName = pick_data->user_name; mOriginalName = pick_data->original_name; LLTextureCtrl* picture = getChild<LLTextureCtrl>("picture"); picture->setImageAssetID(pick_data->snapshot_id); } void LLPickItem::setPickName(const std::string& name) { mPickName = name; getChild<LLUICtrl>("picture_name")->setValue(name); } const std::string& LLPickItem::getPickName() { return mPickName; } const LLUUID& LLPickItem::getCreatorId() { return mCreatorID; } const LLUUID& LLPickItem::getSnapshotId() { return mSnapshotID; } void LLPickItem::setPickDesc(const std::string& descr) { getChild<LLUICtrl>("picture_descr")->setValue(descr); } void LLPickItem::setPickId(const LLUUID& id) { mPickID = id; } const LLUUID& LLPickItem::getPickId() { return mPickID; } const LLVector3d& LLPickItem::getPosGlobal() { return mPosGlobal; } const std::string LLPickItem::getDescription() { return getChild<LLUICtrl>("picture_descr")->getValue().asString(); } void LLPickItem::update() { setNeedData(true); LLAvatarPropertiesProcessor::instance().sendPickInfoRequest(mCreatorID, mPickID); } void LLPickItem::processProperties(void *data, EAvatarProcessorType type) { if (APT_PICK_INFO != type) { return; } LLPickData* pick_data = static_cast<LLPickData *>(data); if (!pick_data || mPickID != pick_data->pick_id) { return; } init(pick_data); setNeedData(false); LLAvatarPropertiesProcessor::instance().removeObserver(mCreatorID, this); } void set_child_visible(LLView* parent, const std::string& child_name, bool visible) { parent->getChildView(child_name)->setVisible(visible); } BOOL LLPickItem::postBuild() { setMouseEnterCallback(boost::bind(&set_child_visible, this, "hovered_icon", true)); setMouseLeaveCallback(boost::bind(&set_child_visible, this, "hovered_icon", false)); return TRUE; } void LLPickItem::setValue(const LLSD& value) { if (!value.isMap()) return;; if (!value.has("selected")) return; getChildView("selected_icon")->setVisible( value["selected"]); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// LLClassifiedItem::LLClassifiedItem(const LLUUID& avatar_id, const LLUUID& classified_id) : LLPanel() , mAvatarId(avatar_id) , mClassifiedId(classified_id) { buildFromFile("panel_classifieds_list_item.xml"); LLAvatarPropertiesProcessor::getInstance()->addObserver(getAvatarId(), this); LLAvatarPropertiesProcessor::getInstance()->sendClassifiedInfoRequest(getClassifiedId()); } LLClassifiedItem::~LLClassifiedItem() { LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); } void LLClassifiedItem::processProperties(void* data, EAvatarProcessorType type) { if(APT_CLASSIFIED_INFO != type) { return; } LLAvatarClassifiedInfo* c_info = static_cast<LLAvatarClassifiedInfo*>(data); if( !c_info || c_info->classified_id != getClassifiedId() ) { return; } setClassifiedName(c_info->name); setDescription(c_info->description); setSnapshotId(c_info->snapshot_id); setPosGlobal(c_info->pos_global); LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); } BOOL LLClassifiedItem::postBuild() { setMouseEnterCallback(boost::bind(&set_child_visible, this, "hovered_icon", true)); setMouseLeaveCallback(boost::bind(&set_child_visible, this, "hovered_icon", false)); return TRUE; } void LLClassifiedItem::setValue(const LLSD& value) { if (!value.isMap()) return;; if (!value.has("selected")) return; getChildView("selected_icon")->setVisible( value["selected"]); } void LLClassifiedItem::fillIn(LLPanelClassifiedEdit* panel) { if(!panel) { return; } setClassifiedName(panel->getClassifiedName()); setDescription(panel->getDescription()); setSnapshotId(panel->getSnapshotId()); setCategory(panel->getCategory()); setContentType(panel->getContentType()); setAutoRenew(panel->getAutoRenew()); setPriceForListing(panel->getPriceForListing()); setPosGlobal(panel->getPosGlobal()); setLocationText(panel->getClassifiedLocation()); } void LLClassifiedItem::setClassifiedName(const std::string& name) { getChild<LLUICtrl>("name")->setValue(name); } void LLClassifiedItem::setDescription(const std::string& desc) { getChild<LLUICtrl>("description")->setValue(desc); } void LLClassifiedItem::setSnapshotId(const LLUUID& snapshot_id) { getChild<LLUICtrl>("picture")->setValue(snapshot_id); } LLUUID LLClassifiedItem::getSnapshotId() { return getChild<LLUICtrl>("picture")->getValue(); } //EOF