/** * @file llpanelpick.cpp * @brief LLPanelPick class implementation * * $LicenseInfo:firstyear=2004&license=viewergpl$ * * Copyright (c) 2004-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$ */ // Display of a "Top Pick" used both for the global top picks in the // Find directory, and also for each individual user's picks in their // profile. #include "llviewerprecompiledheaders.h" #include "llpanel.h" #include "message.h" #include "llagent.h" #include "llagentpicksinfo.h" #include "llbutton.h" #include "lliconctrl.h" #include "lllineeditor.h" #include "llparcel.h" #include "llviewerparcelmgr.h" #include "lltexteditor.h" #include "lltexturectrl.h" #include "lluiconstants.h" #include "llviewerregion.h" #include "llworldmap.h" #include "llfloaterworldmap.h" #include "llfloaterreg.h" #include "llavatarpropertiesprocessor.h" #include "llpanelpick.h" #define XML_PANEL_EDIT_PICK "panel_edit_pick.xml" #define XML_PANEL_PICK_INFO "panel_pick_info.xml" #define XML_NAME "pick_name" #define XML_DESC "pick_desc" #define XML_SNAPSHOT "pick_snapshot" #define XML_LOCATION "pick_location" #define XML_BTN_ON_TXTR "edit_icon" #define XML_BTN_SAVE "save_changes_btn" #define SAVE_BTN_LABEL "[WHAT]" #define LABEL_PICK = "Pick" #define LABEL_CHANGES = "Changes" std::string SET_LOCATION_NOTICE("(will update after save)"); ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //static LLPanelPickInfo* LLPanelPickInfo::create() { LLPanelPickInfo* panel = new LLPanelPickInfo(); LLUICtrlFactory::getInstance()->buildPanel(panel, XML_PANEL_PICK_INFO); return panel; } LLPanelPickInfo::LLPanelPickInfo() : LLPanel() , LLAvatarPropertiesObserver() , LLRemoteParcelInfoObserver() , mAvatarId(LLUUID::null) , mSnapshotCtrl(NULL) , mPickId(LLUUID::null) , mParcelId(LLUUID::null) , mRequestedId(LLUUID::null) { } LLPanelPickInfo::~LLPanelPickInfo() { LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); if (mParcelId.notNull()) { LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelId, this); } } void LLPanelPickInfo::onOpen(const LLSD& key) { LLUUID avatar_id = key["avatar_id"]; if(avatar_id.isNull()) { return; } if(getAvatarId().notNull()) { LLAvatarPropertiesProcessor::getInstance()->removeObserver( getAvatarId(), this); } setAvatarId(avatar_id); resetData(); resetControls(); setPickId(key["pick_id"]); setPickName(key["pick_name"]); setPickDesc(key["pick_desc"]); setSnapshotId(key["snapshot_id"]); LLAvatarPropertiesProcessor::getInstance()->addObserver( getAvatarId(), this); LLAvatarPropertiesProcessor::getInstance()->sendPickInfoRequest( getAvatarId(), getPickId()); } BOOL LLPanelPickInfo::postBuild() { mSnapshotCtrl = getChild<LLTextureCtrl>(XML_SNAPSHOT); childSetLabelArg(XML_BTN_SAVE, SAVE_BTN_LABEL, std::string("Pick")); childSetAction("teleport_btn", boost::bind(&LLPanelPickInfo::onClickTeleport, this)); childSetAction("show_on_map_btn", boost::bind(&LLPanelPickInfo::onClickMap, this)); childSetAction("back_btn", boost::bind(&LLPanelPickInfo::onClickBack, this)); return TRUE; } void LLPanelPickInfo::processProperties(void* data, EAvatarProcessorType type) { if(APT_PICK_INFO != type) { return; } LLPickData* pick_info = static_cast<LLPickData*>(data); if(!pick_info || pick_info->creator_id != getAvatarId() || pick_info->pick_id != getPickId()) { return; } mParcelId = pick_info->parcel_id; setSnapshotId(pick_info->snapshot_id); setPickName(pick_info->name); setPickDesc(pick_info->desc); setPosGlobal(pick_info->pos_global); // Send remote parcel info request to get parcel name and sim (region) name. sendParcelInfoRequest(); // *NOTE dzaporozhan // We want to keep listening to APT_PICK_INFO because user may // edit the Pick and we have to update Pick info panel. // revomeObserver is called from onClickBack } void LLPanelPickInfo::sendParcelInfoRequest() { if (mParcelId != mRequestedId) { LLRemoteParcelInfoProcessor::getInstance()->addObserver(mParcelId, this); LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(mParcelId); mRequestedId = mParcelId; } } void LLPanelPickInfo::setExitCallback(const commit_callback_t& cb) { getChild<LLButton>("back_btn")->setClickedCallback(cb); } void LLPanelPickInfo::processParcelInfo(const LLParcelData& parcel_data) { setPickLocation(createLocationText(LLStringUtil::null, parcel_data.name, parcel_data.sim_name, getPosGlobal())); // We have received parcel info for the requested ID so clear it now. mRequestedId.setNull(); if (mParcelId.notNull()) { LLRemoteParcelInfoProcessor::getInstance()->removeObserver(mParcelId, this); } } void LLPanelPickInfo::setEditPickCallback(const commit_callback_t& cb) { getChild<LLButton>("edit_btn")->setClickedCallback(cb); } // PROTECTED AREA void LLPanelPickInfo::resetControls() { if(getAvatarId() == gAgent.getID()) { childSetEnabled("edit_btn", TRUE); childSetVisible("edit_btn", TRUE); } else { childSetEnabled("edit_btn", FALSE); childSetVisible("edit_btn", FALSE); } } void LLPanelPickInfo::resetData() { setPickName(LLStringUtil::null); setPickDesc(LLStringUtil::null); setPickLocation(LLStringUtil::null); setPickId(LLUUID::null); setSnapshotId(LLUUID::null); mPosGlobal.clearVec(); mParcelId.setNull(); mRequestedId.setNull(); } // static std::string LLPanelPickInfo::createLocationText(const std::string& owner_name, const std::string& original_name, const std::string& sim_name, const LLVector3d& pos_global) { std::string location_text; location_text.append(owner_name); if (!original_name.empty()) { if (!location_text.empty()) location_text.append(", "); location_text.append(original_name); } if (!sim_name.empty()) { if (!location_text.empty()) location_text.append(", "); location_text.append(sim_name); } if (!location_text.empty()) location_text.append(" "); if (!pos_global.isNull()) { S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS; S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS; S32 region_z = llround((F32)pos_global.mdV[VZ]); location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z)); } return location_text; } void LLPanelPickInfo::setSnapshotId(const LLUUID& id) { mSnapshotCtrl->setImageAssetID(id); mSnapshotCtrl->setValid(TRUE); } void LLPanelPickInfo::setPickName(const std::string& name) { childSetValue(XML_NAME, name); } void LLPanelPickInfo::setPickDesc(const std::string& desc) { childSetValue(XML_DESC, desc); updateContentPanelRect(); } void LLPanelPickInfo::setPickLocation(const std::string& location) { childSetValue(XML_LOCATION, location); } void LLPanelPickInfo::updateContentPanelRect() { LLTextBox* desc = getChild<LLTextBox>(XML_DESC); S32 text_height = desc->getTextPixelHeight(); LLRect text_rect = desc->getRect(); // let text-box height fit text height text_rect.set(text_rect.mLeft, text_rect.mTop, text_rect.mRight, text_rect.mTop - text_height); desc->setRect(text_rect); desc->reshape(text_rect.getWidth(), text_rect.getHeight()); // force reflow desc->setText(desc->getText()); // bottom of description text-box will be bottom of content panel desc->localRectToOtherView(desc->getLocalRect(), &text_rect, getChild<LLView>("profile_scroll")); LLPanel* content_panel = getChild<LLPanel>("scroll_content_panel"); LLRect content_rect = content_panel->getRect(); content_rect.set(content_rect.mLeft, content_rect.mTop, content_rect.mRight, text_rect.mBottom); // Somehow setRect moves all elements down. // Single reshape() updates rect and does not move anything. content_panel->reshape(content_rect.getWidth(), content_rect.getHeight()); } void LLPanelPickInfo::onClickMap() { LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); LLFloaterReg::showInstance("world_map", "center"); } void LLPanelPickInfo::onClickTeleport() { if (!getPosGlobal().isExactlyZero()) { gAgent.teleportViaLocation(getPosGlobal()); LLFloaterWorldMap::getInstance()->trackLocation(getPosGlobal()); } } void LLPanelPickInfo::onClickBack() { LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(), this); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //static LLPanelPickEdit* LLPanelPickEdit::create() { LLPanelPickEdit* panel = new LLPanelPickEdit(); LLUICtrlFactory::getInstance()->buildPanel(panel, XML_PANEL_EDIT_PICK); return panel; } LLPanelPickEdit::LLPanelPickEdit() : LLPanelPickInfo() , mLocationChanged(false) , mNeedData(true) , mNewPick(false) { } LLPanelPickEdit::~LLPanelPickEdit() { } void LLPanelPickEdit::onOpen(const LLSD& key) { LLUUID pick_id = key["pick_id"]; mNeedData = true; // creating new Pick if(pick_id.isNull()) { mNewPick = true; setAvatarId(gAgent.getID()); resetData(); resetControls(); setPosGlobal(gAgent.getPositionGlobal()); LLUUID parcel_id = LLUUID::null, snapshot_id = LLUUID::null; std::string pick_name, pick_desc, region_name; LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if(parcel) { parcel_id = parcel->getID(); pick_name = parcel->getName(); pick_desc = parcel->getDesc(); snapshot_id = parcel->getSnapshotID(); } LLViewerRegion* region = gAgent.getRegion(); if(region) { region_name = region->getName(); } setParcelID(parcel_id); childSetValue("pick_name", pick_name.empty() ? region_name : pick_name); childSetValue("pick_desc", pick_desc); setSnapshotId(snapshot_id); setPickLocation(createLocationText(SET_LOCATION_NOTICE, pick_name, region_name, getPosGlobal())); enableSaveButton(true); } // editing existing pick else { mNewPick = false; LLPanelPickInfo::onOpen(key); enableSaveButton(false); } resetDirty(); } void LLPanelPickEdit::setPickData(const LLPickData* pick_data) { if(!pick_data) { return; } mNeedData = false; setParcelID(pick_data->parcel_id); childSetValue("pick_name", pick_data->name); childSetValue("pick_desc", pick_data->desc); setSnapshotId(pick_data->snapshot_id); setPosGlobal(pick_data->pos_global); setPickLocation(createLocationText(LLStringUtil::null, pick_data->name, pick_data->sim_name, pick_data->pos_global)); } BOOL LLPanelPickEdit::postBuild() { LLPanelPickInfo::postBuild(); mSnapshotCtrl->setOnSelectCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1)); LLLineEditor* line_edit = getChild<LLLineEditor>("pick_name"); line_edit->setKeystrokeCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1), NULL); LLTextEditor* text_edit = getChild<LLTextEditor>("pick_desc"); text_edit->setKeystrokeCallback(boost::bind(&LLPanelPickEdit::onPickChanged, this, _1)); childSetAction(XML_BTN_SAVE, boost::bind(&LLPanelPickEdit::onClickSave, this)); childSetAction("set_to_curr_location_btn", boost::bind(&LLPanelPickEdit::onClickSetLocation, this)); initTexturePickerMouseEvents(); return TRUE; } void LLPanelPickEdit::setSaveCallback(const commit_callback_t& cb) { getChild<LLButton>("save_changes_btn")->setClickedCallback(cb); } void LLPanelPickEdit::setCancelCallback(const commit_callback_t& cb) { getChild<LLButton>("cancel_btn")->setClickedCallback(cb); } void LLPanelPickEdit::resetDirty() { LLPanelPickInfo::resetDirty(); getChild<LLLineEditor>("pick_name")->resetDirty(); getChild<LLTextEditor>("pick_desc")->resetDirty(); mSnapshotCtrl->resetDirty(); mLocationChanged = false; } BOOL LLPanelPickEdit::isDirty() const { if( mNewPick || LLPanelPickInfo::isDirty() || mLocationChanged || mSnapshotCtrl->isDirty() || getChild<LLLineEditor>("pick_name")->isDirty() || getChild<LLTextEditor>("pick_desc")->isDirty()) { return TRUE; } return FALSE; } // PROTECTED AREA void LLPanelPickEdit::sendUpdate() { LLPickData pick_data; // If we don't have a pick id yet, we'll need to generate one, // otherwise we'll keep overwriting pick_id 00000 in the database. if (getPickId().isNull()) { getPickId().generate(); } pick_data.agent_id = gAgent.getID(); pick_data.session_id = gAgent.getSessionID(); pick_data.pick_id = getPickId(); pick_data.creator_id = gAgent.getID();; //legacy var need to be deleted pick_data.top_pick = FALSE; pick_data.parcel_id = mParcelId; pick_data.name = childGetValue(XML_NAME).asString(); pick_data.desc = childGetValue(XML_DESC).asString(); pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); pick_data.pos_global = getPosGlobal(); pick_data.sort_order = 0; pick_data.enabled = TRUE; LLAvatarPropertiesProcessor::instance().sendPickInfoUpdate(&pick_data); if(mNewPick) { // Assume a successful create pick operation, make new number of picks // available immediately. Actual number of picks will be requested in // LLAvatarPropertiesProcessor::sendPickInfoUpdate and updated upon server respond. LLAgentPicksInfo::getInstance()->incrementNumberOfPicks(); } } void LLPanelPickEdit::onPickChanged(LLUICtrl* ctrl) { if(isDirty()) { enableSaveButton(true); } else { enableSaveButton(false); } } void LLPanelPickEdit::resetData() { LLPanelPickInfo::resetData(); mLocationChanged = false; } void LLPanelPickEdit::enableSaveButton(bool enable) { childSetEnabled(XML_BTN_SAVE, enable); } void LLPanelPickEdit::onClickSetLocation() { // Save location for later use. setPosGlobal(gAgent.getPositionGlobal()); std::string parcel_name, region_name; LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (parcel) { mParcelId = parcel->getID(); parcel_name = parcel->getName(); } LLViewerRegion* region = gAgent.getRegion(); if(region) { region_name = region->getName(); } setPickLocation(createLocationText(SET_LOCATION_NOTICE, parcel_name, region_name, getPosGlobal())); mLocationChanged = true; enableSaveButton(TRUE); } void LLPanelPickEdit::onClickSave() { sendUpdate(); mLocationChanged = false; LLSD params; params["action"] = "save_new_pick"; notifyParent(params); } void LLPanelPickEdit::processProperties(void* data, EAvatarProcessorType type) { if(mNeedData) { LLPanelPickInfo::processProperties(data, type); } } // PRIVATE AREA void LLPanelPickEdit::initTexturePickerMouseEvents() { text_icon = getChild<LLIconCtrl>(XML_BTN_ON_TXTR); mSnapshotCtrl->setMouseEnterCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseEnter, this, _1)); mSnapshotCtrl->setMouseLeaveCallback(boost::bind(&LLPanelPickEdit::onTexturePickerMouseLeave, this, _1)); // *WORKAROUND: Needed for EXT-1625: enabling save button each time when picker is opened, even if // texture wasn't changed (see Steve's comment). mSnapshotCtrl->setMouseDownCallback(boost::bind(&LLPanelPickEdit::enableSaveButton, this, true)); text_icon->setVisible(FALSE); } void LLPanelPickEdit::onTexturePickerMouseEnter(LLUICtrl* ctrl) { text_icon->setVisible(TRUE); } void LLPanelPickEdit::onTexturePickerMouseLeave(LLUICtrl* ctrl) { text_icon->setVisible(FALSE); }