diff --git a/etc/message.xml b/etc/message.xml index ea6fc8146..fd019aa70 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -718,7 +718,14 @@ UploadBakedTexture true - + + ObjectMedia + false + + ObjectMediaNavigate + false + + messageBans diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index 558c929e1..f07965877 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -126,7 +126,7 @@ bool LLAudioEngine::init(const S32 num_channels, void* userdata) // Initialize the decode manager gAudioDecodeMgrp = new LLAudioDecodeMgr; - llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; + LL_INFOS("AudioEngine") << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; return true; } @@ -228,6 +228,28 @@ std::string LLAudioEngine::getInternetStreamURL() else return std::string(); } +void LLAudioEngine::checkStates() +{ +#ifdef SHOW_ASSERT + for (S32 i = 0; i < MAX_BUFFERS; i++) + { + if (mBuffers[i]) + { + bool buf_has_ref = false; + for (S32 j = 0; j < MAX_CHANNELS; j++) + { + if (mChannels[j]) + { + if(mChannels[j]->mCurrentBufferp == mBuffers[i]) + buf_has_ref = true; + } + } + if(buf_has_ref) + llassert(mBuffers[i]->mInUse); + } + } +#endif //SHOW_ASSERT +} void LLAudioEngine::updateChannels() { @@ -239,8 +261,31 @@ void LLAudioEngine::updateChannels() mChannels[i]->updateBuffer(); mChannels[i]->update3DPosition(); mChannels[i]->updateLoop(); +#ifdef SHOW_ASSERT + if(mChannels[i]->getSource()) + llassert(mChannels[i]->mCurrentBufferp == mChannels[i]->getSource()->getCurrentBuffer()); + if(mChannels[i]->mCurrentBufferp) + { + bool found_buffer = false; + for (S32 j = 0; j < MAX_BUFFERS; j++) + { + if (mBuffers[j]) + { + if(mChannels[i]->mCurrentBufferp == mBuffers[j]) + found_buffer = true; + } + } + llassert(found_buffer); + if(!mChannels[i]->mCurrentBufferp->mInUse) + { + llassert(!mChannels[i]->isPlaying()); + llassert(!mChannels[i]->isWaiting()); + } + } +#endif //SHOW_ASSERT } } + checkStates(); } static const F32 default_max_decode_time = .002f; // 2 ms @@ -311,9 +356,15 @@ void LLAudioEngine::idle(F32 max_decode_time) LLAudioChannel *channelp = getFreeChannel(max_priority); if (channelp) { - //llinfos << "Replacing source in channel due to priority!" << llendl; - max_sourcep->setChannel(channelp); + LL_DEBUGS("AudioEngine") << "Replacing source in channel due to priority!" << llendl; + llassert(max_sourcep->getChannel() == NULL); + + llassert(channelp->mCurrentBufferp == NULL); channelp->setSource(max_sourcep); + + llassert(max_sourcep == channelp->getSource()); + llassert(channelp->mCurrentBufferp == max_sourcep->getCurrentBuffer()); + if (max_sourcep->isSyncSlave()) { // A sync slave, it doesn't start playing until it's synced up with the master. @@ -348,18 +399,22 @@ void LLAudioEngine::idle(F32 max_decode_time) } LLAudioChannel *channelp = sourcep->getChannel(); - if (!channelp) + bool is_stopped = channelp && channelp->isPlaying(); + if (is_stopped || (sourcep->isLoop() && channelp->mLoopedThisFrame)) { // This sound isn't playing, so we just process move the queue sourcep->mCurrentDatap = sourcep->mQueuedDatap; sourcep->mQueuedDatap = NULL; - // Reset the timer so the source doesn't die. - sourcep->mAgeTimer.reset(); - // Make sure we have the buffer set up if we just decoded the data - if (sourcep->mCurrentDatap) + if(is_stopped) { - updateBufferForData(sourcep->mCurrentDatap); + // Reset the timer so the source doesn't die. + sourcep->mAgeTimer.reset(); + // Make sure we have the buffer set up if we just decoded the data + if (sourcep->mCurrentDatap) + { + updateBufferForData(sourcep->mCurrentDatap); + } } // Actually play the associated data. @@ -372,52 +427,6 @@ void LLAudioEngine::idle(F32 max_decode_time) } continue; } - else - { - // Check to see if the current sound is done playing, or looped. - if (!channelp->isPlaying()) - { - sourcep->mCurrentDatap = sourcep->mQueuedDatap; - sourcep->mQueuedDatap = NULL; - - // Reset the timer so the source doesn't die. - sourcep->mAgeTimer.reset(); - - // Make sure we have the buffer set up if we just decoded the data - if (sourcep->mCurrentDatap) - { - updateBufferForData(sourcep->mCurrentDatap); - } - - // Actually play the associated data. - sourcep->setupChannel(); - channelp = sourcep->getChannel(); - if (channelp) - { - channelp->updateBuffer(); - channelp->play(); - } - } - else if (sourcep->isLoop()) - { - // It's a loop, we need to check and see if we're done with it. - if (channelp->mLoopedThisFrame) - { - sourcep->mCurrentDatap = sourcep->mQueuedDatap; - sourcep->mQueuedDatap = NULL; - - // Actually, should do a time sync so if we're a loop master/slave - // we don't drift away. - sourcep->setupChannel(); - channelp = sourcep->getChannel(); - if (channelp) - { - channelp->updateBuffer(); - channelp->play(); - } - } - } - } } // Lame, update the channels AGAIN. @@ -491,7 +500,7 @@ void LLAudioEngine::idle(F32 max_decode_time) { if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) { - //llinfos << "Flushing unused buffer!" << llendl; + LL_DEBUGS("AudioEngine") << "Flushing unused buffer!" << llendl; mBuffers[i]->mAudioDatap->mBufferp = NULL; delete mBuffers[i]; mBuffers[i] = NULL; @@ -572,6 +581,8 @@ void LLAudioEngine::enableWind(bool enable) LLAudioBuffer * LLAudioEngine::getFreeBuffer() { + //checkStates(); //Fails + S32 i; for (i = 0; i < MAX_BUFFERS; i++) { @@ -582,6 +593,7 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() } } + //checkStates(); // Fails // Grab the oldest unused buffer F32 max_age = -1.f; @@ -601,10 +613,11 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() } } + //checkStates(); //Fails + if (buffer_id >= 0) { - lldebugs << "Taking over unused buffer " << buffer_id << llendl; - //llinfos << "Flushing unused buffer!" << llendl; + LL_DEBUGS("AudioEngine") << "Taking over unused buffer! max_age=" << max_age << llendl; mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL; for (U32 i = 0; i < MAX_CHANNELS; i++) { @@ -612,12 +625,15 @@ LLAudioBuffer * LLAudioEngine::getFreeBuffer() if(channelp && channelp->mCurrentBufferp == mBuffers[buffer_id]) { channelp->cleanup(); + llassert(channelp->mCurrentBufferp == NULL); } } delete mBuffers[buffer_id]; mBuffers[buffer_id] = createBuffer(); return mBuffers[buffer_id]; } + + //checkStates(); //Fails return NULL; } @@ -638,6 +654,7 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) // Channel is allocated but not playing right now, use it. if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting()) { + LL_DEBUGS("AudioEngine") << "Replacing unused channel" << llendl; mChannels[i]->cleanup(); if (mChannels[i]->getSource()) { @@ -670,9 +687,9 @@ LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) return NULL; } + LL_DEBUGS("AudioEngine") << "Flushing min channel" << llendl; // Flush the minimum priority channel, and return it. min_channelp->cleanup(); - min_channelp->getSource()->setChannel(NULL); return min_channelp; } @@ -1006,10 +1023,13 @@ LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) if( sourcep && sourcep->getCurrentData() && sourcep->getCurrentData()->getID() == audio_uuid ) { LLAudioChannel* chan=sourcep->getChannel(); - delete sourcep; if(chan) + { + LL_DEBUGS("AudioEngine") << "removeAudioData" << llendl; chan->cleanup(); - mAllSources.erase(iter2++); + } + delete sourcep; + mAllSources.erase(iter2++); } else ++iter2; @@ -1043,7 +1063,7 @@ void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp) iter = mAllSources.find(asp->getID()); if (iter == mAllSources.end()) { - llwarns << "Cleaning up unknown audio source!" << llendl; + LL_WARNS("AudioEngine") << "Cleaning up unknown audio source!" << llendl; return; } delete asp; @@ -1264,7 +1284,7 @@ void LLAudioEngine::startNextTransfer() if (asset_id.notNull()) { - llinfos << "Getting asset data for: " << asset_id << llendl; + LL_DEBUGS("AudioEngine") << "Getting asset data for: " << asset_id << llendl; gAudiop->mCurrentTransfer = asset_id; gAudiop->mCurrentTransferTimer.reset(); gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND, @@ -1282,7 +1302,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E { if (result_code) { - llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; + LL_INFOS("AudioEngine") << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; // Need to mark data as bad to avoid constant rerequests. LLAudioData *adp = gAudiop->getAudioData(uuid); if (adp) @@ -1299,7 +1319,7 @@ void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::E if (!adp) { // Should never happen - llwarns << "Got asset callback without audio data for " << uuid << llendl; + LL_WARNS("AudioEngine") << "Got asset callback without audio data for " << uuid << llendl; } else { @@ -1360,8 +1380,7 @@ LLAudioSource::~LLAudioSource() if (mChannelp) { // Stop playback of this sound - mChannelp->setSource(NULL); - setChannel(NULL); + mChannelp->cleanup(); } } @@ -1404,7 +1423,7 @@ void LLAudioSource::update() } else if (adp->hasCompletedDecode()) // Only mark corrupted after decode is done { - llwarns << "Marking LLAudioSource corrupted for " << adp->getID() << llendl; + LL_WARNS("AudioEngine") << "Marking LLAudioSource corrupted for " << adp->getID() << llendl; mCorrupted = true ; } } @@ -1463,6 +1482,8 @@ bool LLAudioSource::setupChannel() } mChannelp->setSource(this); + llassert(this == mChannelp->getSource()); + llassert(mChannelp->mCurrentBufferp == getCurrentBuffer()); return true; } @@ -1474,8 +1495,8 @@ bool LLAudioSource::play(const LLUUID &audio_uuid) { if (getChannel()) { + llassert(this == getChannel()->getSource()); getChannel()->setSource(NULL); - setChannel(NULL); if (!isMuted()) { mCurrentDatap = NULL; @@ -1727,13 +1748,20 @@ LLAudioChannel::LLAudioChannel() : LLAudioChannel::~LLAudioChannel() { + llassert(mCurrentBufferp == NULL); + // Need to disconnect any sources which are using this channel. //llinfos << "Cleaning up audio channel" << llendl; - if (mCurrentSourcep) - { + cleanup(); +} + +void LLAudioChannel::cleanup() +{ + if(mCurrentSourcep) mCurrentSourcep->setChannel(NULL); - } mCurrentBufferp = NULL; + mCurrentSourcep = NULL; + mWaiting = false; } @@ -1744,22 +1772,21 @@ void LLAudioChannel::setSource(LLAudioSource *sourcep) if (!sourcep) { // Clearing the source for this channel, don't need to do anything. - //llinfos << "Clearing source for channel" << llendl; + LL_DEBUGS("AudioEngine") << "Clearing source for channel" << llendl; cleanup(); - mCurrentSourcep = NULL; - mWaiting = false; return; } if (sourcep == mCurrentSourcep) { // Don't reallocate the channel, this will make FMOD goofy. - //llinfos << "Calling setSource with same source!" << llendl; + LL_DEBUGS("AudioEngine") << "Calling setSource with same source!" << llendl; } + cleanup(); mCurrentSourcep = sourcep; - - + mCurrentSourcep->setChannel(this); + updateBuffer(); update3DPosition(); } @@ -1796,7 +1823,12 @@ bool LLAudioChannel::updateBuffer() // The source changed what buffer it's playing. We need to clean up // the existing channel // + LLAudioSource* source = mCurrentSourcep; cleanup(); + mCurrentSourcep = source; + mCurrentSourcep->setChannel(this); + + llassert(mCurrentBufferp == NULL); mCurrentBufferp = bufferp; if (bufferp) @@ -1856,7 +1888,7 @@ bool LLAudioData::load() if (mBufferp) { // We already have this sound in a buffer, don't do anything. - llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl; + LL_INFOS("AudioEngine") << "Already have a buffer for this sound, don't bother loading!" << llendl; return true; } @@ -1864,7 +1896,7 @@ bool LLAudioData::load() if (!mBufferp) { // No free buffers, abort. - lldebugs << "Not able to allocate a new audio buffer, aborting." << llendl; + LL_DEBUGS("AudioEngine") << "Not able to allocate a new audio buffer, aborting." << llendl; return false; } diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index 230168ee9..c739e37bf 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -189,6 +189,8 @@ public: static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status); friend class LLPipeline; // For debugging + + void checkStates(); public: F32 mMaxWindGain; // Hack. Public to set before fade in? @@ -447,7 +449,7 @@ public: protected: virtual void play() = 0; virtual void playSynced(LLAudioChannel *channelp) = 0; - virtual void cleanup() = 0; + virtual void cleanup(); void setWaiting(bool waiting) { mWaiting = waiting; } public: diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp index f47a6cac0..4c4f4f48d 100644 --- a/indra/llaudio/llaudioengine_fmod.cpp +++ b/indra/llaudio/llaudioengine_fmod.cpp @@ -576,6 +576,7 @@ void LLAudioChannelFMOD::updateLoop() void LLAudioChannelFMOD::cleanup() { + LLAudioChannel::cleanup(); if (!mChannelID) { //llinfos << "Aborting cleanup with no channelID." << llendl; @@ -588,7 +589,6 @@ void LLAudioChannelFMOD::cleanup() LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; } - mCurrentBufferp = NULL; mChannelID = 0; } diff --git a/indra/llaudio/llaudioengine_fmodex.cpp b/indra/llaudio/llaudioengine_fmodex.cpp index 56997a888..bfdda0957 100644 --- a/indra/llaudio/llaudioengine_fmodex.cpp +++ b/indra/llaudio/llaudioengine_fmodex.cpp @@ -70,12 +70,172 @@ bool attemptDelayLoad() } #endif +static bool sVerboseDebugging = false; + FMOD_RESULT F_CALLBACK windCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); FMOD::ChannelGroup *LLAudioEngine_FMODEX::mChannelGroups[LLAudioEngine::AUDIO_TYPE_COUNT] = {0}; -LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler) +//This class is designed to keep track of all sound<->channel assocations. +//Used to verify validity of sound and channel pointers, as well as catch cases were sounds +//are released with active channels still attached. +class CFMODSoundChecks { + typedef std::map > active_sounds_t; + typedef std::set dead_sounds_t; + typedef std::map active_channels_t; + typedef std::map dead_channels_t; + + active_sounds_t mActiveSounds; + dead_sounds_t mDeadSounds; + active_channels_t mActiveChannels; + dead_channels_t mDeadChannels; +public: + enum STATUS + { + ACTIVE, + DEAD, + UNKNOWN + }; + STATUS getPtrStatus(LLAudioChannelFMODEX* channel) + { + if(!channel) + return UNKNOWN; + return getPtrStatus(channel->mChannelp); + } + STATUS getPtrStatus(LLAudioBufferFMODEX* sound) + { + if(!sound) + return UNKNOWN; + return getPtrStatus(sound->mSoundp); + } + STATUS getPtrStatus(FMOD::Channel* channel) + { + if(!channel) + return UNKNOWN; + else if(mActiveChannels.find(channel) != mActiveChannels.end()) + return ACTIVE; + else if(mDeadChannels.find(channel) != mDeadChannels.end()) + return DEAD; + return UNKNOWN; + } + STATUS getPtrStatus(FMOD::Sound* sound) + { + if(!sound) + return UNKNOWN; + if(mActiveSounds.find(sound) != mActiveSounds.end()) + return ACTIVE; + else if(mDeadSounds.find(sound) != mDeadSounds.end()) + return DEAD; + return UNKNOWN; + } + void addNewSound(FMOD::Sound* sound) + { + llassert(getPtrStatus(sound) != ACTIVE); + + mDeadSounds.erase(sound); + mActiveSounds.insert(std::make_pair(sound,std::set())); + } + void removeSound(FMOD::Sound* sound) + { +#ifdef SHOW_ASSERT + STATUS status = getPtrStatus(sound); + llassert(status != DEAD); + llassert(status != UNKNOWN); +#endif + + active_sounds_t::const_iterator it = mActiveSounds.find(sound); + llassert(it != mActiveSounds.end()); + if(it != mActiveSounds.end()) + { + if(!it->second.empty()) + { +#ifdef LL_RELEASE_FOR_DOWNLOAD + LL_WARNS("AudioImpl") << "Removing sound " << sound << " with attached channels: \n"; +#else + LL_ERRS("AudioImpl") << "Removing sound " << sound << " with attached channels: \n"; +#endif + for(std::set::iterator it2 = it->second.begin(); it2 != it->second.end();++it2) + { + switch(getPtrStatus(*it2)) + { + case ACTIVE: + llcont << " Channel " << *it2 << " ACTIVE\n"; + break; + case DEAD: + llcont << " Channel " << *it2 << " DEAD\n"; + break; + default: + llcont << " Channel " << *it2 << " UNKNOWN\n"; + } + } + llcont << llendl; + } + mDeadSounds.insert(sound); + mActiveSounds.erase(sound); + } + } + void addNewChannelToSound(FMOD::Sound* sound,FMOD::Channel* channel) + { + STATUS status = getPtrStatus(sound); + llassert(status != DEAD); + llassert(status != UNKNOWN); + status = getPtrStatus(channel); + llassert(status != ACTIVE); + + mActiveSounds[sound].insert(channel); + mActiveChannels.insert(std::make_pair(channel,sound)); + } + void removeChannel(FMOD::Channel* channel) + { +#ifdef SHOW_ASSERT + STATUS status = getPtrStatus(channel); + llassert(status != DEAD); + llassert(status != UNKNOWN); +#endif + + active_channels_t::const_iterator it = mActiveChannels.find(channel); + llassert(it != mActiveChannels.end()); + if(it != mActiveChannels.end()) + { +#ifdef SHOW_ASSERT + STATUS status = getPtrStatus(it->second); + llassert(status != DEAD); + llassert(status != UNKNOWN); +#endif + + active_sounds_t::iterator it2 = mActiveSounds.find(it->second); + llassert(it2 != mActiveSounds.end()); + if(it2 != mActiveSounds.end()) + { + it2->second.erase(channel); + } + mDeadChannels.insert(*it); + mActiveChannels.erase(channel); + } + } +} gSoundCheck; + +bool isActive(LLAudioChannel* channel) +{ + return gSoundCheck.getPtrStatus(dynamic_cast(channel)) == CFMODSoundChecks::ACTIVE; +} +bool isActive(LLAudioBuffer* sound) +{ + return gSoundCheck.getPtrStatus(dynamic_cast(sound)) == CFMODSoundChecks::ACTIVE; +} + +#define CHECK_PTR(ptr) \ + if(sVerboseDebugging){\ + CFMODSoundChecks::STATUS chan = gSoundCheck.getPtrStatus(ptr); \ + if(chan == CFMODSoundChecks::DEAD) \ + LL_DEBUGS("AudioImpl") << __FUNCTION__ << ": Using dead " << typeid(ptr).name() << " " << ptr << llendl; \ + else if(chan == CFMODSoundChecks::UNKNOWN) \ + LL_DEBUGS("AudioImpl") << __FUNCTION__ << ": Using unknown " << typeid(ptr).name() << " " << ptr << llendl;} \ + +LLAudioEngine_FMODEX::LLAudioEngine_FMODEX(bool enable_profiler, bool verbose_debugging) +{ + sVerboseDebugging = verbose_debugging; mInited = false; mWindGen = NULL; mWindDSP = NULL; @@ -93,7 +253,7 @@ inline bool Check_FMOD_Error(FMOD_RESULT result, const char *string) { if(result == FMOD_OK) return false; - llwarns << string << " Error: " << FMOD_ErrorString(result) << llendl; + LL_WARNS("AudioImpl") << string << " Error: " << FMOD_ErrorString(result) << llendl; return true; } @@ -101,11 +261,11 @@ void* F_STDCALL decode_alloc(unsigned int size, FMOD_MEMORY_TYPE type, const cha { if(type & FMOD_MEMORY_STREAM_DECODE) { - llinfos << "Decode buffer size: " << size << llendl; + LL_INFOS("AudioImpl") << "Decode buffer size: " << size << llendl; } else if(type & FMOD_MEMORY_STREAM_FILE) { - llinfos << "Strean buffer size: " << size << llendl; + LL_INFOS("AudioImpl") << "Stream buffer size: " << size << llendl; } return new char[size]; } @@ -344,7 +504,7 @@ void LLAudioEngine_FMODEX::allocateListener(void) mListenerp = (LLListener *) new LLListener_FMODEX(mSystem); if (!mListenerp) { - llwarns << "Listener creation failed" << llendl; + LL_WARNS("AudioImpl") << "Listener creation failed" << llendl; } } @@ -353,13 +513,16 @@ void LLAudioEngine_FMODEX::shutdown() { stopInternetStream(); - llinfos << "About to LLAudioEngine::shutdown()" << llendl; + LL_INFOS("AudioImpl") << "About to LLAudioEngine::shutdown()" << llendl; LLAudioEngine::shutdown(); - llinfos << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() closing FMOD Ex" << llendl; + if ( mSystem ) // speculative fix for MAINT-2657 + { mSystem->close(); mSystem->release(); - llinfos << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; + } + LL_INFOS("AudioImpl") << "LLAudioEngine_FMODEX::shutdown() done closing FMOD Ex" << llendl; delete mListenerp; mListenerp = NULL; @@ -490,6 +653,34 @@ LLAudioChannelFMODEX::LLAudioChannelFMODEX(FMOD::System *system) : LLAudioChanne LLAudioChannelFMODEX::~LLAudioChannelFMODEX() { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Destructing Audio Channel. mChannelp = " << mChannelp << llendl; + + cleanup(); +} + +static FMOD_RESULT F_CALLBACK channel_callback(FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2) +{ + if(type == FMOD_CHANNEL_CALLBACKTYPE_END) + { + FMOD::Channel* chan = reinterpret_cast(channel); + LLAudioChannelFMODEX* audio_channel = NULL; + chan->getUserData((void**)&audio_channel); + if(audio_channel) + { + audio_channel->onRelease(); + } + } + return FMOD_OK; +} + +void LLAudioChannelFMODEX::onRelease() +{ + llassert(mChannelp); + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Fmod signaled channel release for channel " << mChannelp << llendl; + gSoundCheck.removeChannel(mChannelp); + mChannelp = NULL; //Null out channel here so cleanup doesn't try to redundantly stop it. cleanup(); } @@ -502,13 +693,16 @@ bool LLAudioChannelFMODEX::updateBuffer() LLAudioBufferFMODEX *bufferp = (LLAudioBufferFMODEX *)mCurrentSourcep->getCurrentBuffer(); + llassert(mCurrentBufferp != NULL); + llassert(mCurrentBufferp == bufferp); + // Grab the FMOD sample associated with the buffer FMOD::Sound *soundp = bufferp->getSound(); if (!soundp) { // This is bad, there should ALWAYS be a sound associated with a legit // buffer. - llerrs << "No FMOD sound!" << llendl; + LL_ERRS("AudioImpl") << "No FMOD sound!" << llendl; return false; } @@ -517,11 +711,21 @@ bool LLAudioChannelFMODEX::updateBuffer() // setup. if(!mChannelp) { + llassert(!isActive(this)); + FMOD_RESULT result = getSystem()->playSound(FMOD_CHANNEL_FREE, soundp, true, &mChannelp); Check_FMOD_Error(result, "FMOD::System::playSound"); - } + if(result == FMOD_OK) + { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Created channel " << mChannelp << " for sound " << soundp << llendl; - //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; + gSoundCheck.addNewChannelToSound(soundp,mChannelp); + mChannelp->setCallback(&channel_callback); + mChannelp->setUserData(this); + llassert(isActive(this)); + } + } } // If we have a source for the channel, we need to update its gain. @@ -530,18 +734,12 @@ bool LLAudioChannelFMODEX::updateBuffer() // SJB: warnings can spam and hurt framerate, disabling FMOD_RESULT result; + CHECK_PTR(mChannelp); result = mChannelp->setVolume(getSecondaryGain() * mCurrentSourcep->getGain()); Check_FMOD_Error(result, "FMOD::Channel::setVolume"); result = mChannelp->setMode(mCurrentSourcep->isLoop() ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); Check_FMOD_Error(result, "FMOD::Channel::setMode"); - /*if(Check_FMOD_Error(result, "FMOD::Channel::setMode")) - { - S32 index; - mChannelp->getIndex(&index); - llwarns << "Channel " << index << "Source ID: " << mCurrentSourcep->getID() - << " at " << mCurrentSourcep->getPositionGlobal() << llendl; - }*/ } return true; @@ -564,6 +762,8 @@ void LLAudioChannelFMODEX::update3DPosition() return; } + CHECK_PTR(mChannelp); + if (mCurrentSourcep->isAmbient()) { // Ambient sound, don't need to do any positional updates. @@ -590,6 +790,8 @@ void LLAudioChannelFMODEX::updateLoop() return; } + CHECK_PTR(mChannelp); + // // Hack: We keep track of whether we looped or not by seeing when the // sample position looks like it's going backwards. Not reliable; may @@ -608,17 +810,30 @@ void LLAudioChannelFMODEX::updateLoop() void LLAudioChannelFMODEX::cleanup() { + LLAudioChannel::cleanup(); + if (!mChannelp) { + llassert(!isActive(this)); + llassert(mCurrentBufferp == NULL); //llinfos << "Aborting cleanup with no channel handle." << llendl; return; } - //llinfos << "Cleaning up channel: " << mChannelID << llendl; - Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop"); + CHECK_PTR(mChannelp); + + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Stopping channel " << mChannelp << llendl; + + mChannelp->setCallback(NULL); + if(!Check_FMOD_Error(mChannelp->stop(),"FMOD::Channel::stop")) + { + gSoundCheck.removeChannel(mChannelp); + } - mCurrentBufferp = NULL; mChannelp = NULL; + llassert(!isActive(this)); + } @@ -626,12 +841,19 @@ void LLAudioChannelFMODEX::play() { if (!mChannelp) { - llwarns << "Playing without a channel handle, aborting" << llendl; + LL_WARNS("AudioImpl") << "Playing without a channel handle, aborting" << llendl; return; } + llassert(isActive(this)); + + CHECK_PTR(mChannelp); + Check_FMOD_Error(mChannelp->setPaused(false), "FMOD::Channel::setPaused"); + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Playing channel " << mChannelp << llendl; + getSource()->setPlayedOnce(true); if(LLAudioEngine_FMODEX::mChannelGroups[getSource()->getType()]) @@ -648,6 +870,8 @@ void LLAudioChannelFMODEX::playSynced(LLAudioChannel *channelp) return; } + CHECK_PTR(mChannelp); + U32 cur_pos; if(Check_FMOD_Error(mChannelp->getPosition(&cur_pos,FMOD_TIMEUNIT_PCMBYTES), "Unable to retrieve current position")) return; @@ -669,6 +893,8 @@ bool LLAudioChannelFMODEX::isPlaying() return false; } + CHECK_PTR(mChannelp); + bool paused, playing; Check_FMOD_Error(mChannelp->getPaused(&paused),"FMOD::Channel::getPaused"); Check_FMOD_Error(mChannelp->isPlaying(&playing),"FMOD::Channel::isPlaying"); @@ -690,7 +916,12 @@ LLAudioBufferFMODEX::~LLAudioBufferFMODEX() { if(mSoundp) { + if(sVerboseDebugging) + LL_DEBUGS("AudioImpl") << "Release sound " << mSoundp << llendl; + + CHECK_PTR(mSoundp); Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::Release"); + gSoundCheck.removeSound(mSoundp); mSoundp = NULL; } } @@ -714,8 +945,10 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) if (mSoundp) { + CHECK_PTR(mSoundp); // If there's already something loaded in this buffer, clean it up. Check_FMOD_Error(mSoundp->release(),"FMOD::Sound::release"); + gSoundCheck.removeSound(mSoundp); mSoundp = NULL; } @@ -734,7 +967,7 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) if (result != FMOD_OK) { // We failed to load the file for some reason. - llwarns << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; + LL_WARNS("AudioImpl") << "Could not load data '" << filename << "': " << FMOD_ErrorString(result) << llendl; // // If we EVER want to load wav files provided by end users, we need @@ -745,6 +978,8 @@ bool LLAudioBufferFMODEX::loadWAV(const std::string& filename) return false; } + gSoundCheck.addNewSound(mSoundp); + // Everything went well, return true return true; } @@ -757,6 +992,7 @@ U32 LLAudioBufferFMODEX::getLength() return 0; } + CHECK_PTR(mSoundp); U32 length; Check_FMOD_Error(mSoundp->getLength(&length, FMOD_TIMEUNIT_PCMBYTES),"FMOD::Sound::getLength"); return length; @@ -765,6 +1001,8 @@ U32 LLAudioBufferFMODEX::getLength() void LLAudioChannelFMODEX::set3DMode(bool use3d) { + CHECK_PTR(mChannelp); + FMOD_MODE current_mode; if(Check_FMOD_Error(mChannelp->getMode(¤t_mode),"FMOD::Channel::getMode")) return; diff --git a/indra/llaudio/llaudioengine_fmodex.h b/indra/llaudio/llaudioengine_fmodex.h index 6b780128f..18b4d61a8 100644 --- a/indra/llaudio/llaudioengine_fmodex.h +++ b/indra/llaudio/llaudioengine_fmodex.h @@ -53,7 +53,7 @@ namespace FMOD class LLAudioEngine_FMODEX : public LLAudioEngine { public: - LLAudioEngine_FMODEX(bool enable_profiler); + LLAudioEngine_FMODEX(bool enable_profiler, bool verbose_debugging); virtual ~LLAudioEngine_FMODEX(); // initialization/startup/shutdown @@ -95,7 +95,7 @@ class LLAudioChannelFMODEX : public LLAudioChannel public: LLAudioChannelFMODEX(FMOD::System *audioengine); virtual ~LLAudioChannelFMODEX(); - + void onRelease(); protected: /*virtual*/ void play(); /*virtual*/ void playSynced(LLAudioChannel *channelp); @@ -112,6 +112,8 @@ protected: FMOD::System *mSystemp; FMOD::Channel *mChannelp; S32 mLastSamplePos; + + friend class CFMODSoundChecks; }; @@ -129,6 +131,8 @@ protected: FMOD::System *mSystemp; FMOD::Sound *getSound() const{ return mSoundp; } FMOD::Sound *mSoundp; + + friend class CFMODSoundChecks; }; diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp index 1b2c8c6ee..10ce5e6ed 100644 --- a/indra/llaudio/llaudioengine_openal.cpp +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -186,10 +186,9 @@ LLAudioChannelOpenAL::~LLAudioChannelOpenAL() void LLAudioChannelOpenAL::cleanup() { + LLAudioChannel::cleanup(); alSourceStop(mALSource); alSourcei(mALSource, AL_BUFFER, AL_NONE); - - mCurrentBufferp = NULL; } void LLAudioChannelOpenAL::play() diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index ac958ef0e..6f93be79e 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -76,6 +76,7 @@ set(llcommon_SOURCE_FILES llrun.cpp llscopedvolatileaprpool.h llsd.cpp + llsdparam.cpp llsdserialize.cpp llsdserialize_xml.cpp llsdutil.cpp @@ -204,10 +205,11 @@ set(llcommon_HEADER_FILES llqueuedthread.h llrand.h llrefcount.h - llrefcount.h + llregistry.h llrun.h llsafehandle.h llsd.h + llsdparam.h llsdserialize.h llsdserialize_xml.h llsdutil.h diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h new file mode 100644 index 000000000..ca6d76a98 --- /dev/null +++ b/indra/llcommon/llregistry.h @@ -0,0 +1,358 @@ +/** + * @file llregistry.h + * @brief template classes for registering name, value pairs in nested scopes, statically, etc. + * + * $LicenseInfo:firstyear=2001&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$ + */ + +#ifndef LL_LLREGISTRY_H +#define LL_LLREGISTRY_H + +#include + +#include +#include "llsingleton.h" +#include "llstl.h" + +template +struct LLRegistryDefaultComparator +{ + bool operator()(const T& lhs, const T& rhs) const + { + using std::less; + return less()(lhs, rhs); + } +}; + +template > +class LLRegistry +{ +public: + typedef LLRegistry registry_t; + typedef typename boost::add_reference::type>::type ref_const_key_t; + typedef typename boost::add_reference::type>::type ref_const_value_t; + typedef typename boost::add_reference::type ref_value_t; + typedef typename boost::add_pointer::type>::type ptr_const_value_t; + typedef typename boost::add_pointer::type ptr_value_t; + + class Registrar + { + friend class LLRegistry; + public: + typedef std::map registry_map_t; + + bool add(ref_const_key_t key, ref_const_value_t value) + { + if (mMap.insert(std::make_pair(key, value)).second == false) + { + llwarns << "Tried to register " << key << " but it was already registered!" << llendl; + return false; + } + return true; + } + + void remove(ref_const_key_t key) + { + mMap.erase(key); + } + + void replace(ref_const_key_t key, ref_const_value_t value) + { + mMap[key] = value; + } + + typename registry_map_t::const_iterator beginItems() const + { + return mMap.begin(); + } + + typename registry_map_t::const_iterator endItems() const + { + return mMap.end(); + } + + protected: + ptr_value_t getValue(ref_const_key_t key) + { + typename registry_map_t::iterator found_it = mMap.find(key); + if (found_it != mMap.end()) + { + return &(found_it->second); + } + return NULL; + } + + ptr_const_value_t getValue(ref_const_key_t key) const + { + typename registry_map_t::const_iterator found_it = mMap.find(key); + if (found_it != mMap.end()) + { + return &(found_it->second); + } + return NULL; + } + + // if the registry is used to store pointers, and null values are valid entries + // then use this function to check the existence of an entry + bool exists(ref_const_key_t key) const + { + return mMap.find(key) != mMap.end(); + } + + bool empty() const + { + return mMap.empty(); + } + + protected: + // use currentRegistrar() or defaultRegistrar() + Registrar() {} + ~Registrar() {} + + private: + registry_map_t mMap; + }; + + typedef typename std::list scope_list_t; + typedef typename std::list::iterator scope_list_iterator_t; + typedef typename std::list::const_iterator scope_list_const_iterator_t; + + LLRegistry() + {} + + ~LLRegistry() {} + + ptr_value_t getValue(ref_const_key_t key) + { + for(scope_list_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + ptr_value_t valuep = (*it)->getValue(key); + if (valuep != NULL) return valuep; + } + return mDefaultRegistrar.getValue(key); + } + + ptr_const_value_t getValue(ref_const_key_t key) const + { + for(scope_list_const_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + ptr_value_t valuep = (*it)->getValue(key); + if (valuep != NULL) return valuep; + } + return mDefaultRegistrar.getValue(key); + } + + bool exists(ref_const_key_t key) const + { + for(scope_list_const_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + if ((*it)->exists(key)) return true; + } + + return mDefaultRegistrar.exists(key); + } + + bool empty() const + { + for(scope_list_const_iterator_t it = mActiveScopes.begin(); + it != mActiveScopes.end(); + ++it) + { + if (!(*it)->empty()) return false; + } + + return mDefaultRegistrar.empty(); + } + + + Registrar& defaultRegistrar() + { + return mDefaultRegistrar; + } + + const Registrar& defaultRegistrar() const + { + return mDefaultRegistrar; + } + + + Registrar& currentRegistrar() + { + if (!mActiveScopes.empty()) + { + return *mActiveScopes.front(); + } + + return mDefaultRegistrar; + } + + const Registrar& currentRegistrar() const + { + if (!mActiveScopes.empty()) + { + return *mActiveScopes.front(); + } + + return mDefaultRegistrar; + } + + +protected: + void addScope(Registrar* scope) + { + // newer scopes go up front + mActiveScopes.insert(mActiveScopes.begin(), scope); + } + + void removeScope(Registrar* scope) + { + // O(N) but should be near the beggining and N should be small and this is safer than storing iterators + scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope); + if (iter != mActiveScopes.end()) + { + mActiveScopes.erase(iter); + } + } + +private: + scope_list_t mActiveScopes; + Registrar mDefaultRegistrar; +}; + +template > +class LLRegistrySingleton + : public LLRegistry, + public LLSingleton +{ + friend class LLSingleton; +public: + typedef LLRegistry registry_t; + typedef const KEY& ref_const_key_t; + typedef const VALUE& ref_const_value_t; + typedef VALUE* ptr_value_t; + typedef const VALUE* ptr_const_value_t; + typedef LLSingleton singleton_t; + + class ScopedRegistrar : public registry_t::Registrar + { + public: + ScopedRegistrar(bool push_scope = true) + { + if (push_scope) + { + pushScope(); + } + } + + ~ScopedRegistrar() + { + if (!singleton_t::destroyed()) + { + popScope(); + } + } + + void pushScope() + { + singleton_t::instance().addScope(this); + } + + void popScope() + { + singleton_t::instance().removeScope(this); + } + + ptr_value_t getValueFromScope(ref_const_key_t key) + { + return getValue(key); + } + + ptr_const_value_t getValueFromScope(ref_const_key_t key) const + { + return getValue(key); + } + + private: + typename std::list::iterator mListIt; + }; + + class StaticRegistrar : public registry_t::Registrar + { + public: + virtual ~StaticRegistrar() {} + StaticRegistrar(ref_const_key_t key, ref_const_value_t value) + { + if(!singleton_t::instance().mStaticScope) + mStaticScope = new ScopedRegistrar(); + singleton_t::instance().mStaticScope->add(key, value); + } + }; + + // convenience functions + typedef typename LLRegistry::Registrar& ref_registrar_t; + static ref_registrar_t currentRegistrar() + { + return singleton_t::instance().registry_t::currentRegistrar(); + } + + static ref_registrar_t defaultRegistrar() + { + return singleton_t::instance().registry_t::defaultRegistrar(); + } + + static ptr_value_t getValue(ref_const_key_t key) + { + return singleton_t::instance().registry_t::getValue(key); + } + +protected: + // DERIVED_TYPE needs to derive from LLRegistrySingleton + LLRegistrySingleton() + : mStaticScope(NULL) + {} + + virtual void initSingleton() + { + //mStaticScope = new ScopedRegistrar(); + } + + virtual ~LLRegistrySingleton() + { + delete mStaticScope; + } + +private: + ScopedRegistrar* mStaticScope; +}; + +// helper macro for doing static registration +#define GLUED_TOKEN(x, y) x ## y +#define GLUE_TOKENS(x, y) GLUED_TOKEN(x, y) +#define LLREGISTER_STATIC(REGISTRY, KEY, VALUE) static REGISTRY::StaticRegistrar GLUE_TOKENS(reg, __LINE__)(KEY, VALUE); + +#endif diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp new file mode 100644 index 000000000..0e29873bb --- /dev/null +++ b/indra/llcommon/llsdparam.cpp @@ -0,0 +1,342 @@ +/** + * @file llsdparam.cpp + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&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 "linden_common.h" + +// Project includes +#include "llsdparam.h" +#include "llsdutil.h" + +static LLInitParam::Parser::parser_read_func_map_t sReadFuncs; +static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs; +static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs; +static const LLSD NO_VALUE_MARKER; + +LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR("LLSD to LLInitParam conversion"); + +// +// LLParamSDParser +// +LLParamSDParser::LLParamSDParser() +: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs) +{ + using boost::bind; + + if (sReadFuncs.empty()) + { + registerParserFuncs(readFlag, &LLParamSDParser::writeFlag); + registerParserFuncs(readS32, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readU32, &LLParamSDParser::writeU32Param); + registerParserFuncs(readF32, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readF64, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readBool, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readString, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readUUID, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readDate, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readURI, &LLParamSDParser::writeTypedValue); + registerParserFuncs(readSD, &LLParamSDParser::writeTypedValue); + } +} + +// special case handling of U32 due to ambiguous LLSD::assign overload +bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) +{ + LLParamSDParser& sdparser = static_cast(parser); + if (!sdparser.mWriteRootSD) return false; + + parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + sd_to_write.assign((S32)*((const U32*)val_ptr)); + + return true; +} + +bool LLParamSDParser::writeFlag(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) +{ + LLParamSDParser& sdparser = static_cast(parser); + if (!sdparser.mWriteRootSD) return false; + + parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + + return true; +} + +void LLParamSDParser::submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack) +{ + mCurReadSD = &sd; + block.submitValue(name_stack, *this); +} + +void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent) +{ + mCurReadSD = NULL; + mNameStack.clear(); + setParseSilently(silent); + + LLParamSDParserUtilities::readSDValues(boost::bind(&LLParamSDParser::submit, this, boost::ref(block), _1, _2), sd, mNameStack); + //readSDValues(sd, block); +} + +void LLParamSDParser::writeSD(LLSD& sd, const LLInitParam::BaseBlock& block) +{ + mNameStack.clear(); + mWriteRootSD = &sd; + + name_stack_t name_stack; + block.serializeBlock(*this, name_stack); +} + +/*virtual*/ std::string LLParamSDParser::getCurrentElementName() +{ + std::string full_name = "sd"; + for (name_stack_t::iterator it = mNameStack.begin(); + it != mNameStack.end(); + ++it) + { + full_name += llformat("[%s]", it->first.c_str()); + } + + return full_name; +} + + +bool LLParamSDParser::readFlag(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + return self.mCurReadSD == &NO_VALUE_MARKER; +} + + +bool LLParamSDParser::readS32(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((S32*)val_ptr) = self.mCurReadSD->asInteger(); + return true; +} + +bool LLParamSDParser::readU32(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((U32*)val_ptr) = self.mCurReadSD->asInteger(); + return true; +} + +bool LLParamSDParser::readF32(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((F32*)val_ptr) = self.mCurReadSD->asReal(); + return true; +} + +bool LLParamSDParser::readF64(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((F64*)val_ptr) = self.mCurReadSD->asReal(); + return true; +} + +bool LLParamSDParser::readBool(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((bool*)val_ptr) = self.mCurReadSD->asBoolean(); + return true; +} + +bool LLParamSDParser::readString(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((std::string*)val_ptr) = self.mCurReadSD->asString(); + return true; +} + +bool LLParamSDParser::readUUID(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLUUID*)val_ptr) = self.mCurReadSD->asUUID(); + return true; +} + +bool LLParamSDParser::readDate(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLDate*)val_ptr) = self.mCurReadSD->asDate(); + return true; +} + +bool LLParamSDParser::readURI(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLURI*)val_ptr) = self.mCurReadSD->asURI(); + return true; +} + +bool LLParamSDParser::readSD(Parser& parser, void* val_ptr) +{ + LLParamSDParser& self = static_cast(parser); + + *((LLSD*)val_ptr) = *self.mCurReadSD; + return true; +} + +// static +LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range) +{ + LLSD* sd_to_write = &input; + + for (LLInitParam::Parser::name_stack_t::iterator it = name_stack_range.first; + it != name_stack_range.second; + ++it) + { + bool new_traversal = it->second; + + LLSD* child_sd = it->first.empty() ? sd_to_write : &(*sd_to_write)[it->first]; + + if (child_sd->isArray()) + { + if (new_traversal) + { + // write to new element at end + sd_to_write = &(*child_sd)[child_sd->size()]; + } + else + { + // write to last of existing elements, or first element if empty + sd_to_write = &(*child_sd)[llmax(0, child_sd->size() - 1)]; + } + } + else + { + if (new_traversal + && child_sd->isDefined() + && !child_sd->isArray()) + { + // copy child contents into first element of an array + LLSD new_array = LLSD::emptyArray(); + new_array.append(*child_sd); + // assign array to slot that previously held the single value + *child_sd = new_array; + // return next element in that array + sd_to_write = &((*child_sd)[1]); + } + else + { + sd_to_write = child_sd; + } + } + it->second = false; + } + + return *sd_to_write; +} + +//static +void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack) +{ + if (sd.isMap()) + { + for (LLSD::map_const_iterator it = sd.beginMap(); + it != sd.endMap(); + ++it) + { + stack.push_back(make_pair(it->first, true)); + readSDValues(cb, it->second, stack); + stack.pop_back(); + } + } + else if (sd.isArray()) + { + for (LLSD::array_const_iterator it = sd.beginArray(); + it != sd.endArray(); + ++it) + { + stack.back().second = true; + readSDValues(cb, *it, stack); + } + } + else if (sd.isUndefined()) + { + if (!cb.empty()) + { + cb(NO_VALUE_MARKER, stack); + } + } + else + { + if (!cb.empty()) + { + cb(sd, stack); + } + } +} + +//static +void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd) +{ + LLInitParam::Parser::name_stack_t stack = LLInitParam::Parser::name_stack_t(); + readSDValues(cb, sd, stack); +} +namespace LLInitParam +{ + // LLSD specialization + // block param interface + bool ParamValue, false>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name) + { + LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack); + + LLSD::String string; + + if (p.readValue(string)) + { + sd = string; + return true; + } + return false; + } + + //static + void ParamValue, false>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) + { + p.writeValue(sd.asString(), name_stack); + } + + void ParamValue, false>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block) const + { + // read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) + Parser::name_stack_t stack; + LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, stack); + } +} diff --git a/indra/llcommon/llsdparam.h b/indra/llcommon/llsdparam.h new file mode 100644 index 000000000..6ef5debd7 --- /dev/null +++ b/indra/llcommon/llsdparam.h @@ -0,0 +1,126 @@ +/** + * @file llsdparam.h + * @brief parameter block abstraction for creating complex objects and + * parsing construction parameters from xml and LLSD + * + * $LicenseInfo:firstyear=2008&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$ + */ + +#ifndef LL_LLSDPARAM_H +#define LL_LLSDPARAM_H + +#include "llinitparam.h" +#include "boost/function.hpp" + +struct LL_COMMON_API LLParamSDParserUtilities +{ + static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range); + + typedef boost::function read_sd_cb_t; + static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack); + static void readSDValues(read_sd_cb_t cb, const LLSD& sd); +}; + +class LL_COMMON_API LLParamSDParser +: public LLInitParam::Parser +{ +LOG_CLASS(LLParamSDParser); + +typedef LLInitParam::Parser parser_t; + +public: + LLParamSDParser(); + void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false); + void writeSD(LLSD& sd, const LLInitParam::BaseBlock& block); + + /*virtual*/ std::string getCurrentElementName(); + +private: + void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack); + + template + static bool writeTypedValue(Parser& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) + { + LLParamSDParser& sdparser = static_cast(parser); + if (!sdparser.mWriteRootSD) return false; + + LLInitParam::Parser::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + + sd_to_write.assign(*((const T*)val_ptr)); + return true; + } + + static bool writeU32Param(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); + static bool writeFlag(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); + + static bool readFlag(Parser& parser, void* val_ptr); + static bool readS32(Parser& parser, void* val_ptr); + static bool readU32(Parser& parser, void* val_ptr); + static bool readF32(Parser& parser, void* val_ptr); + static bool readF64(Parser& parser, void* val_ptr); + static bool readBool(Parser& parser, void* val_ptr); + static bool readString(Parser& parser, void* val_ptr); + static bool readUUID(Parser& parser, void* val_ptr); + static bool readDate(Parser& parser, void* val_ptr); + static bool readURI(Parser& parser, void* val_ptr); + static bool readSD(Parser& parser, void* val_ptr); + + Parser::name_stack_t mNameStack; + const LLSD* mCurReadSD; + LLSD* mWriteRootSD; + LLSD* mCurWriteSD; +}; + + +extern LL_COMMON_API LLFastTimer::DeclareTimer FTM_SD_PARAM_ADAPTOR; +template +class LLSDParamAdapter : public T +{ +public: + LLSDParamAdapter() {} + LLSDParamAdapter(const LLSD& sd) + { + LLFastTimer _(FTM_SD_PARAM_ADAPTOR); + LLParamSDParser parser; + // don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it + bool parse_silently = true; + parser.readSD(sd, *this, parse_silently); + } + + operator LLSD() const + { + LLParamSDParser parser; + LLSD sd; + parser.writeSD(sd, *this); + return sd; + } + + LLSDParamAdapter(const T& val) + : T(val) + { + T::operator=(val); + } +}; + +#endif // LL_LLSDPARAM_H + diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp index 3aca80f6e..4f21bca5a 100644 --- a/indra/llcommon/llsecondlifeurls.cpp +++ b/indra/llcommon/llsecondlifeurls.cpp @@ -45,44 +45,49 @@ const std::string AUCTION_URL ( const std::string EVENTS_URL ( "http://secondlife.com/events/"); +/* const std::string TIER_UP_URL ( - "http://secondlife.com/app/landtier"); - -const std::string LAND_URL ( - "http://secondlife.com/app/landtier"); - -const std::string UPGRADE_TO_PREMIUM_URL ( - "http://secondlife.com/app/upgrade/"); + "http://secondlife.com/app/landtier"); // *TODO: NOT USED +*/ const std::string DIRECTX_9_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED +/* +const std::string LAND_URL ( + "http://secondlife.com/app/landtier"); // *TODO: NOT USED + +const std::string UPGRADE_TO_PREMIUM_URL ( + "http://secondlife.com/app/upgrade/"); // *TODO: NOT USED const std::string AMD_AGP_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string VIA_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string SUPPORT_URL ( "http://secondlife.com/support/"); const std::string INTEL_CHIPSET_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string SIS_CHIPSET_URL ( - "http://secondlife.com/support/"); + "http://secondlife.com/support/"); // *TODO: NOT USED const std::string BLOGS_URL ( - "http://blog.secondlife.com/"); + "http://blog.secondlife.com/"); // *TODO: NOT USED +*/ const std::string BUY_CURRENCY_URL ( "http://secondlife.com/app/currency/"); +/* const std::string LSL_DOC_URL ( - "http://secondlife.com/app/lsldoc/"); + "http://secondlife.com/app/lsldoc/"); // *TODO: NOT USED const std::string SL_KB_URL ( - "http://secondlife.com/knowledgebase/"); + "http://secondlife.com/knowledgebase/"); // *TODO: NOT USED const std::string RELEASE_NOTES_BASE_URL ( - "http://ascent.balseraph.org/?"); + "http://secondlife.com/app/releasenotes/"); +*/ diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h index 81d840833..4c741dce4 100644 --- a/indra/llcommon/llsecondlifeurls.h +++ b/indra/llcommon/llsecondlifeurls.h @@ -43,18 +43,22 @@ LL_COMMON_API extern const std::string AUCTION_URL; LL_COMMON_API extern const std::string EVENTS_URL; +/* // Tier up to a new land level. LL_COMMON_API extern const std::string TIER_UP_URL; // Tier up to a new land level. LL_COMMON_API extern const std::string LAND_URL; - -// Upgrade from basic membership to premium membership -LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL; +*/ // How to get DirectX 9 LL_COMMON_API extern const std::string DIRECTX_9_URL; +/* +// Upgrade from basic membership to premium membership +LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL; + + // Out of date VIA chipset LL_COMMON_API extern const std::string VIA_URL; @@ -63,10 +67,12 @@ LL_COMMON_API extern const std::string SUPPORT_URL; // Linden Blogs page LL_COMMON_API extern const std::string BLOGS_URL; +*/ // Currency page LL_COMMON_API extern const std::string BUY_CURRENCY_URL; +/* // LSL script wiki LL_COMMON_API extern const std::string LSL_DOC_URL; @@ -75,5 +81,5 @@ LL_COMMON_API extern const std::string SL_KB_URL; // Release Notes Redirect URL for Server and Viewer LL_COMMON_API extern const std::string RELEASE_NOTES_BASE_URL; - +*/ #endif diff --git a/indra/llmessage/aihttptimeoutpolicy.cpp b/indra/llmessage/aihttptimeoutpolicy.cpp index 93ba3b257..502cabf37 100644 --- a/indra/llmessage/aihttptimeoutpolicy.cpp +++ b/indra/llmessage/aihttptimeoutpolicy.cpp @@ -917,6 +917,7 @@ P(MPImportGetResponder); P(MPImportPostResponder); P(mapLayerResponder); P2(maturityPreferences, transfer_30s); +P(mediaDataClientResponder); P(mediaTypeResponder); P(meshDecompositionResponder); P(meshHeaderResponder); diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index dc452d1ef..c5ff97ef7 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -56,6 +56,7 @@ bool LLPluginClassMedia::init_impl(void) { // Queue up the media init message -- it will be sent after all the currently queued messages. LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "init"); + message.setValue("target", mTarget); sendMessage(message); return true; @@ -127,7 +128,7 @@ void LLPluginClassMedia::reset_impl(void) void LLPluginClassMedia::idle_impl(void) { - if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked())) + if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL) || (mPlugin->isBlocked()) || (mOwner == NULL)) { // Can't process a size change at this time } @@ -246,16 +247,18 @@ unsigned char* LLPluginClassMedia::getBitsData() void LLPluginClassMedia::setSize(int width, int height) { - if (width <= 0 || height <= 0) - { - width = height = -1; - } - if (mSetMediaWidth != width || mSetMediaHeight != height) + if((width > 0) && (height > 0)) { mSetMediaWidth = width; mSetMediaHeight = height; - setSizeInternal(); } + else + { + mSetMediaWidth = -1; + mSetMediaHeight = -1; + } + + setSizeInternal(); } void LLPluginClassMedia::setSizeInternal(void) diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h index fcaa52874..8e7a40a67 100644 --- a/indra/llplugin/llpluginclassmedia.h +++ b/indra/llplugin/llpluginclassmedia.h @@ -34,7 +34,9 @@ #include "llrect.h" #include "v4color.h" -class LLPluginClassMedia : public LLPluginClassBasic +#include + +class LLPluginClassMedia : public LLPluginClassBasic, public boost::signals2::trackable { LOG_CLASS(LLPluginClassMedia); diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp index 8b80180d4..34eff1751 100644 --- a/indra/llprimitive/lltextureentry.cpp +++ b/indra/llprimitive/lltextureentry.cpp @@ -26,13 +26,30 @@ #include "linden_common.h" +#include "lluuid.h" +#include "llmediaentry.h" #include "lltextureentry.h" #include "llsdutil_math.h" +#include "v4color.h" const U8 DEFAULT_BUMP_CODE = 0; // no bump or shininess const LLTextureEntry LLTextureEntry::null; +// Some LLSD keys. Do not change these! +#define OBJECT_ID_KEY_STR "object_id" +#define TEXTURE_INDEX_KEY_STR "texture_index" +#define OBJECT_MEDIA_VERSION_KEY_STR "object_media_version" +#define OBJECT_MEDIA_DATA_KEY_STR "object_media_data" +#define TEXTURE_MEDIA_DATA_KEY_STR "media_data" + +/*static*/ const char* LLTextureEntry::OBJECT_ID_KEY = OBJECT_ID_KEY_STR; +/*static*/ const char* LLTextureEntry::OBJECT_MEDIA_DATA_KEY = OBJECT_MEDIA_DATA_KEY_STR; +/*static*/ const char* LLTextureEntry::MEDIA_VERSION_KEY = OBJECT_MEDIA_VERSION_KEY_STR; +/*static*/ const char* LLTextureEntry::TEXTURE_INDEX_KEY = TEXTURE_INDEX_KEY_STR; +/*static*/ const char* LLTextureEntry::TEXTURE_MEDIA_DATA_KEY = TEXTURE_MEDIA_DATA_KEY_STR; + +static const std::string MEDIA_VERSION_STRING_PREFIX = "x-mv:"; // static LLTextureEntry* LLTextureEntry::newTextureEntry() @@ -42,16 +59,19 @@ LLTextureEntry* LLTextureEntry::newTextureEntry() //=============================================================== LLTextureEntry::LLTextureEntry() + : mMediaEntry(NULL) { init(LLUUID::null,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } LLTextureEntry::LLTextureEntry(const LLUUID& tex_id) + : mMediaEntry(NULL) { init(tex_id,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE); } LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) + : mMediaEntry(NULL) { mID = rhs.mID; mScaleS = rhs.mScaleS; @@ -63,6 +83,10 @@ LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs) mBump = rhs.mBump; mMediaFlags = rhs.mMediaFlags; mGlow = rhs.mGlow; + if (rhs.mMediaEntry != NULL) { + // Make a copy + mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry); + } } LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs) @@ -79,6 +103,16 @@ LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs) mBump = rhs.mBump; mMediaFlags = rhs.mMediaFlags; mGlow = rhs.mGlow; + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + if (rhs.mMediaEntry != NULL) { + // Make a copy + mMediaEntry = new LLMediaEntry(*rhs.mMediaEntry); + } + else { + mMediaEntry = NULL; + } } return *this; @@ -98,10 +132,19 @@ void LLTextureEntry::init(const LLUUID& tex_id, F32 scale_s, F32 scale_t, F32 of mGlow = 0; setColor(LLColor4(1.f, 1.f, 1.f, 1.f)); + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + mMediaEntry = NULL; } LLTextureEntry::~LLTextureEntry() { + if(mMediaEntry) + { + delete mMediaEntry; + mMediaEntry = NULL; + } } bool LLTextureEntry::operator!=(const LLTextureEntry &rhs) const @@ -153,6 +196,13 @@ void LLTextureEntry::asLLSD(LLSD& sd) const sd["bump"] = getBumpShiny(); sd["fullbright"] = getFullbright(); sd["media_flags"] = mMediaFlags; + if (hasMedia()) { + LLSD mediaData; + if (NULL != getMediaData()) { + getMediaData()->asLLSD(mediaData); + } + sd[TEXTURE_MEDIA_DATA_KEY] = mediaData; + } sd["glow"] = mGlow; } @@ -201,6 +251,17 @@ bool LLTextureEntry::fromLLSD(const LLSD& sd) { setMediaTexGen( sd[w].asInteger() ); } else goto fail; + // If the "has media" flag doesn't match the fact that + // media data exists, updateMediaData will "fix" it + // by either clearing or setting the flag + w = TEXTURE_MEDIA_DATA_KEY; + if (hasMedia() != sd.has(w)) + { + llwarns << "LLTextureEntry::fromLLSD: media_flags (" << hasMedia() << + ") does not match presence of media_data (" << sd.has(w) << "). Fixing." << llendl; + } + updateMediaData(sd[w]); + w = "glow"; if (sd.has(w)) { @@ -362,12 +423,10 @@ S32 LLTextureEntry::setBumpShinyFullbright(U8 bump) S32 LLTextureEntry::setMediaTexGen(U8 media) { - if (mMediaFlags != media) - { - mMediaFlags = media; - return TEM_CHANGE_MEDIA; - } - return TEM_CHANGE_NONE; + S32 result = TEM_CHANGE_NONE; + result |= setTexGen(media & TEM_TEX_GEN_MASK); + result |= setMediaFlags(media & TEM_MEDIA_MASK); + return result; } S32 LLTextureEntry::setBumpmap(U8 bump) @@ -425,6 +484,18 @@ S32 LLTextureEntry::setMediaFlags(U8 media_flags) { mMediaFlags &= ~TEM_MEDIA_MASK; mMediaFlags |= media_flags; + + // Special code for media handling + if( hasMedia() && mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + else if ( ! hasMedia() && mMediaEntry != NULL) + { + delete mMediaEntry; + mMediaEntry = NULL; + } + return TEM_CHANGE_MEDIA; } return TEM_CHANGE_NONE; @@ -452,4 +523,112 @@ S32 LLTextureEntry::setGlow(F32 glow) return TEM_CHANGE_NONE; } +void LLTextureEntry::setMediaData(const LLMediaEntry &media_entry) +{ + mMediaFlags |= MF_HAS_MEDIA; + if (NULL != mMediaEntry) + { + delete mMediaEntry; + } + mMediaEntry = new LLMediaEntry(media_entry); +} +bool LLTextureEntry::updateMediaData(const LLSD& media_data) +{ + if (media_data.isUndefined()) + { + // clear the media data + clearMediaData(); + return false; + } + else { + mMediaFlags |= MF_HAS_MEDIA; + if (mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + // *NOTE: this will *clobber* all of the fields in mMediaEntry + // with whatever fields are present (or not present) in media_data! + mMediaEntry->fromLLSD(media_data); + return true; + } +} + +void LLTextureEntry::clearMediaData() +{ + mMediaFlags &= ~MF_HAS_MEDIA; + if (mMediaEntry != NULL) { + delete mMediaEntry; + } + mMediaEntry = NULL; +} + +void LLTextureEntry::mergeIntoMediaData(const LLSD& media_fields) +{ + mMediaFlags |= MF_HAS_MEDIA; + if (mMediaEntry == NULL) + { + mMediaEntry = new LLMediaEntry; + } + // *NOTE: this will *merge* the data in media_fields + // with the data in our media entry + mMediaEntry->mergeFromLLSD(media_fields); +} + +//static +std::string LLTextureEntry::touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id) +{ + // XXX TODO: make media version string binary (base64-encoded?) + // Media "URL" is a representation of a version and the last-touched agent + // x-mv:nnnnn/agent-id + // where "nnnnn" is version number + // *NOTE: not the most efficient code in the world... + U32 current_version = getVersionFromMediaVersionString(in_version) + 1; + const size_t MAX_VERSION_LEN = 10; // 2^32 fits in 10 decimal digits + char buf[MAX_VERSION_LEN+1]; + snprintf(buf, (int)MAX_VERSION_LEN+1, "%0*u", (int)MAX_VERSION_LEN, current_version); // added int cast to fix warning/breakage on mac. + return MEDIA_VERSION_STRING_PREFIX + buf + "/" + agent_id.asString(); +} + +//static +U32 LLTextureEntry::getVersionFromMediaVersionString(const std::string &version_string) +{ + U32 version = 0; + if (!version_string.empty()) + { + size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX); + if (found != std::string::npos) + { + found = version_string.find_first_of("/", found); + std::string v = version_string.substr(MEDIA_VERSION_STRING_PREFIX.length(), found); + version = strtoul(v.c_str(),NULL,10); + } + } + return version; +} + +//static +LLUUID LLTextureEntry::getAgentIDFromMediaVersionString(const std::string &version_string) +{ + LLUUID id; + if (!version_string.empty()) + { + size_t found = version_string.find(MEDIA_VERSION_STRING_PREFIX); + if (found != std::string::npos) + { + found = version_string.find_first_of("/", found); + if (found != std::string::npos) + { + std::string v = version_string.substr(found + 1); + id.set(v); + } + } + } + return id; +} + +//static +bool LLTextureEntry::isMediaVersionString(const std::string &version_string) +{ + return std::string::npos != version_string.find(MEDIA_VERSION_STRING_PREFIX); +} diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h index ba0c92dc7..d0181421f 100644 --- a/indra/llprimitive/lltextureentry.h +++ b/indra/llprimitive/lltextureentry.h @@ -62,6 +62,8 @@ const S32 TEM_MEDIA_MASK = 0x01; const S32 TEM_TEX_GEN_MASK = 0x06; const S32 TEM_TEX_GEN_SHIFT = 1; +// forward declarations +class LLMediaEntry; class LLTextureEntry { @@ -137,6 +139,34 @@ public: U8 getTexGen() const { return mMediaFlags & TEM_TEX_GEN_MASK; } U8 getMediaTexGen() const { return mMediaFlags; } F32 getGlow() const { return mGlow; } + + // *NOTE: it is possible for hasMedia() to return true, but getMediaData() to return NULL. + // CONVERSELY, it is also possible for hasMedia() to return false, but getMediaData() + // to NOT return NULL. + bool hasMedia() const { return (bool)(mMediaFlags & MF_HAS_MEDIA); } + LLMediaEntry* getMediaData() const { return mMediaEntry; } + + // Completely change the media data on this texture entry. + void setMediaData(const LLMediaEntry &media_entry); + // Returns true if media data was updated, false if it was cleared + bool updateMediaData(const LLSD& media_data); + // Clears media data, and sets the media flags bit to 0 + void clearMediaData(); + // Merges the given LLSD of media fields with this media entry. + // Only those fields that are set that match the keys in + // LLMediaEntry will be affected. If no fields are set or if + // the LLSD is undefined, this is a no-op. + void mergeIntoMediaData(const LLSD& media_fields); + + // Takes a media version string (an empty string or a previously-returned string) + // and returns a "touched" string, touched by agent_id + static std::string touchMediaVersionString(const std::string &in_version, const LLUUID &agent_id); + // Given a media version string, return the version + static U32 getVersionFromMediaVersionString(const std::string &version_string); + // Given a media version string, return the UUID of the agent + static LLUUID getAgentIDFromMediaVersionString(const std::string &version_string); + // Return whether or not the given string is actually a media version + static bool isMediaVersionString(const std::string &version_string); // Media flags enum { MF_NONE = 0x0, MF_HAS_MEDIA = 0x1 }; @@ -149,6 +179,14 @@ public: F32 mRotation; // anti-clockwise rotation in rad about the bottom left corner static const LLTextureEntry null; + + // LLSD key defines + static const char* OBJECT_ID_KEY; + static const char* OBJECT_MEDIA_DATA_KEY; + static const char* MEDIA_VERSION_KEY; + static const char* TEXTURE_INDEX_KEY; + static const char* TEXTURE_MEDIA_DATA_KEY; + protected: LLUUID mID; // Texture GUID LLColor4 mColor; @@ -156,6 +194,9 @@ protected: U8 mMediaFlags; // replace with web page, movie, etc. F32 mGlow; + // Note the media data is not sent via the same message structure as the rest of the TE + LLMediaEntry* mMediaEntry; // The media data for the face + // NOTE: when adding new data to this class, in addition to adding it to the serializers asLLSD/fromLLSD and the // message packers (e.g. LLPrimitive::packTEMessage) you must also implement its copy in LLPrimitive::copyTEs() diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index 54ebcb504..4b6c6156d 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -39,6 +39,7 @@ set(llui_SOURCE_FILES llfunctorregistry.cpp lliconctrl.cpp llkeywords.cpp + lllayoutstack.cpp lllineeditor.cpp lllocalcliprect.cpp llmenugl.cpp @@ -78,6 +79,10 @@ set(llui_SOURCE_FILES lluictrlfactory.cpp lluistring.cpp llundo.cpp + llurlaction.cpp + llurlentry.cpp + llurlmatch.cpp + llurlregistry.cpp llview.cpp llviewborder.cpp llviewmodel.cpp @@ -105,6 +110,7 @@ set(llui_HEADER_FILES llhtmlhelp.h lliconctrl.h llkeywords.h + lllayoutstack.h lllineeditor.h lllocalcliprect.h llmemberlistener.h @@ -150,6 +156,10 @@ set(llui_HEADER_FILES lluistring.h lluixmltags.h llundo.h + llurlaction.h + llurlentry.h + llurlmatch.h + llurlregistry.h llview.h llviewborder.h llviewmodel.h diff --git a/indra/llui/llalertdialog.cpp b/indra/llui/llalertdialog.cpp index 7015e9c6a..739eb61d4 100644 --- a/indra/llui/llalertdialog.cpp +++ b/indra/llui/llalertdialog.cpp @@ -520,7 +520,7 @@ void LLAlertDialog::onButtonPressed( LLUICtrl* ctrl, const std::string url ) // If we declared a URL and chose the URL option, go to the url if (!url.empty() && sURLLoader != NULL) { - sURLLoader->load(url); + sURLLoader->load(url, false); } mNote->respond(response); // new notification reponse diff --git a/indra/llui/llalertdialog.h b/indra/llui/llalertdialog.h index 799ab8a80..5463eb54b 100644 --- a/indra/llui/llalertdialog.h +++ b/indra/llui/llalertdialog.h @@ -57,7 +57,7 @@ public: class URLLoader { public: - virtual void load(const std::string& url) = 0; + virtual void load(const std::string& url, bool force_open_externally) = 0; virtual ~URLLoader() {} }; diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 888dc2e6e..daa7de213 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -544,7 +544,7 @@ void LLButton::getOverlayImageSize(S32& overlay_width, S32& overlay_height) // virtual void LLButton::draw() { - F32 alpha = mAlpha; + F32 alpha = mAlpha * getDrawContext().mAlpha; bool flash = FALSE; if( mFlashing ) { diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp index ad0fac5df..ada81b639 100644 --- a/indra/llui/llcheckboxctrl.cpp +++ b/indra/llui/llcheckboxctrl.cpp @@ -307,9 +307,6 @@ LLXMLNodePtr LLCheckBoxCtrl::getXML(bool save_children) const // static LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("checkbox"); - node->getAttributeString("name", name); - std::string label(""); node->getAttributeString("label", label); @@ -326,7 +323,7 @@ LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto LLRect rect; createRect(node, rect, parent, LLRect()); - LLCheckBoxCtrl* checkbox = new LLCheckboxCtrl(name, + LLCheckBoxCtrl* checkbox = new LLCheckboxCtrl("checkbox", rect, label, font, diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index bdd0fe4a8..30a10c39a 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -149,9 +149,6 @@ LLXMLNodePtr LLComboBox::getXML(bool save_children) const // static LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("combo_box"); - node->getAttributeString("name", name); - std::string label(""); node->getAttributeString("label", label); @@ -164,7 +161,7 @@ LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * S32 max_chars = 20; node->getAttributeS32("max_chars", max_chars); - LLComboBox* combo_box = new LLComboBox(name, + LLComboBox* combo_box = new LLComboBox("combo_box", rect, label); combo_box->setAllowTextEntry(allow_text_entry, max_chars); @@ -1245,16 +1242,13 @@ LLXMLNodePtr LLFlyoutButton::getXML(bool save_children) const //static LLView* LLFlyoutButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name = "flyout_button"; - node->getAttributeString("name", name); - std::string label(""); node->getAttributeString("label", label); LLRect rect; createRect(node, rect, parent, LLRect()); - LLFlyoutButton* flyout_button = new LLFlyoutButton(name, + LLFlyoutButton* flyout_button = new LLFlyoutButton("flyout_button", rect, label); diff --git a/indra/llui/llfiltereditor.cpp b/indra/llui/llfiltereditor.cpp index fa05c03d7..12e1c2d52 100644 --- a/indra/llui/llfiltereditor.cpp +++ b/indra/llui/llfiltereditor.cpp @@ -52,9 +52,6 @@ void LLFilterEditor::handleKeystroke() // static LLView* LLFilterEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("filter_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -63,7 +60,7 @@ LLView* LLFilterEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto std::string text = node->getValue().substr(0, max_text_length - 1); - LLFilterEditor* search_editor = new LLFilterEditor(name, + LLFilterEditor* search_editor = new LLFilterEditor("filter_editor", rect, max_text_length); diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index cee6f7500..16fb57c25 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -58,6 +58,7 @@ #include "v2math.h" #include "llfasttimer.h" #include "airecursive.h" +#include "llnotifications.h" const S32 MINIMIZED_WIDTH = 160; const S32 CLOSE_BOX_FROM_TOP = 1; @@ -1411,6 +1412,10 @@ void LLFloater::closeFocusedFloater() } } +LLNotificationPtr LLFloater::addContextualNotification(const std::string& name, const LLSD& substitutions) +{ + return LLNotifications::instance().add(LLNotification::Params(name).context(mNotificationContext).substitutions(substitutions)); +} void LLFloater::onClickClose() { @@ -2488,8 +2493,13 @@ LLView* LLFloater::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *f if (filename.empty()) { + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + floaterp->getCommitCallbackRegistrar().pushScope(); + floaterp->getEnableCallbackRegistrar().pushScope(); // Load from node floaterp->initFloaterXML(node, parent, factory); + floaterp->getCommitCallbackRegistrar().popScope(); + floaterp->getEnableCallbackRegistrar().popScope(); } else { diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 9a9fa9d19..bd826a936 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -40,7 +40,8 @@ #include "llpanel.h" #include "lluuid.h" #include "lltabcontainer.h" -#include "llnotifications.h" +#include "llnotificationcontext.h" +#include "llnotificationptr.h" #include class LLDragHandle; @@ -232,10 +233,7 @@ public: // handle refocusing. static void closeFocusedFloater(); - LLNotification::Params contextualNotification(const std::string& name) - { - return LLNotification::Params(name).context(mNotificationContext); - } + LLNotificationPtr addContextualNotification(const std::string& name, const LLSD& substitutions = LLSD()); void onClickClose(); void onClickMinimize(); diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 1e4a5dcec..90b313d45 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -106,7 +106,8 @@ void LLIconCtrl::draw() { if( mImagep.notNull() ) { - mImagep->draw(getLocalRect(), mColor ); + const F32 alpha = getDrawContext().mAlpha; + mImagep->draw(getLocalRect(), mColor % alpha ); } LLUICtrl::draw(); @@ -157,9 +158,6 @@ LLXMLNodePtr LLIconCtrl::getXML(bool save_children) const LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("icon"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -172,7 +170,7 @@ LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * LLColor4 color(LLColor4::white); LLUICtrlFactory::getAttributeColor(node,"color", color); - LLIconCtrl* icon = new LLIconCtrl(name, rect, image_name); + LLIconCtrl* icon = new LLIconCtrl("icon", rect, image_name); icon->setColor(color); diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp new file mode 100644 index 000000000..83e532a65 --- /dev/null +++ b/indra/llui/lllayoutstack.cpp @@ -0,0 +1,1038 @@ +/** + * @file lllayoutstack.cpp + * @brief LLLayout class - dynamic stacking of UI elements + * + * $LicenseInfo:firstyear=2001&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$ + */ + +// Opaque view with a background and a border. Can contain LLUICtrls. + +#include "linden_common.h" + +#include "lllayoutstack.h" + +#include "lllocalcliprect.h" +#include "llpanel.h" +#include "lluictrlfactory.h" +#include "llcriticaldamp.h" +#include "boost/foreach.hpp" + +static const F32 MIN_FRACTIONAL_SIZE = 0.00001f; +static const F32 MAX_FRACTIONAL_SIZE = 1.f; +static const S32 RESIZE_BAR_OVERLAP = 1; +static const S32 RESIZE_BAR_HEIGHT = 3; + +static LLRegisterWidget r1("layout_panel"); + + +// +// LLLayoutPanel +// +LLLayoutPanel::LLLayoutPanel(S32 min_dim, BOOL auto_resize, BOOL user_resize, LLRect rect) +: LLPanel("",rect,false), + mMinDim(min_dim), + mAutoResize(auto_resize), + mUserResize(user_resize), + mCollapsed(FALSE), + mCollapseAmt(0.f), + mVisibleAmt(1.f), // default to fully visible + mResizeBar(NULL), + mFractionalSize(0.f), + mTargetDim(0), + mIgnoreReshape(false), + mOrientation(LLLayoutStack::HORIZONTAL), + mExpandedMinDim(-1) +{ + +} + +BOOL LLLayoutPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + std::string orientation_string; + node->getAttributeString("orientation", orientation_string); + if (orientation_string == "horizontal") + { + mOrientation = LLLayoutStack::HORIZONTAL; + } + else if (orientation_string == "vertical") + { + mOrientation = LLLayoutStack::VERTICAL; + } + + if(node->hasAttribute("min_dim")) + node->getAttributeS32("min_dim", mMinDim); + else if(mOrientation == LLLayoutStack::HORIZONTAL) + node->getAttributeS32("min_width", mMinDim); + else if(mOrientation == LLLayoutStack::VERTICAL) + node->getAttributeS32("min_height", mMinDim); + node->getAttributeS32("expanded_min_dim", mExpandedMinDim); + + BOOL auto_resize = mAutoResize; + BOOL user_resize = mUserResize; + node->getAttributeBOOL("auto_resize", auto_resize); + node->getAttributeBOOL("user_resize", user_resize); + mAutoResize = auto_resize; + mUserResize = user_resize; + + bool ret = LLPanel::initPanelXML(node,parent,factory); + // panels initialized as hidden should not start out partially visible + if (!getVisible()) + { + mVisibleAmt = 0.f; + } + setFollowsNone(); + return ret; +} + +LLLayoutPanel::~LLLayoutPanel() +{ + // probably not necessary, but... + delete mResizeBar; + mResizeBar = NULL; +} + +F32 LLLayoutPanel::getAutoResizeFactor() const +{ + return mVisibleAmt * (1.f - mCollapseAmt); +} + +F32 LLLayoutPanel::getVisibleAmount() const +{ + return mVisibleAmt; +} + +S32 LLLayoutPanel::getLayoutDim() const +{ + return llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight())); +} + +S32 LLLayoutPanel::getTargetDim() const +{ + return mTargetDim; +} + +void LLLayoutPanel::setTargetDim(S32 value) +{ + LLRect new_rect(getRect()); + if (mOrientation == LLLayoutStack::HORIZONTAL) + { + new_rect.mRight = new_rect.mLeft + value; + } + else + { + new_rect.mTop = new_rect.mBottom + value; + } + setShape(new_rect, true); +} + +S32 LLLayoutPanel::getVisibleDim() const +{ + F32 min_dim = getRelevantMinDim(); + return llround(mVisibleAmt + * (min_dim + + (((F32)mTargetDim - min_dim) * (1.f - mCollapseAmt)))); +} + +void LLLayoutPanel::setOrientation( LLLayoutStack::ELayoutOrientation orientation ) +{ + mOrientation = orientation; + S32 layout_dim = llround((F32)((mOrientation == LLLayoutStack::HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight())); + + if (mAutoResize == FALSE + && mUserResize == TRUE + && mMinDim == -1 ) + { + setMinDim(layout_dim); + } + mTargetDim = llmax(layout_dim, getMinDim()); +} + +void LLLayoutPanel::setVisible( BOOL visible ) +{ + if (visible != getVisible()) + { + LLLayoutStack* stackp = dynamic_cast(getParent()); + if (stackp) + { + stackp->mNeedsLayout = true; + } + } + LLPanel::setVisible(visible); +} + +void LLLayoutPanel::reshape( S32 width, S32 height, BOOL called_from_parent /*= TRUE*/ ) +{ + if (width == getRect().getWidth() && height == getRect().getHeight()) return; + + if (!mIgnoreReshape && mAutoResize == false) + { + mTargetDim = (mOrientation == LLLayoutStack::HORIZONTAL) ? width : height; + LLLayoutStack* stackp = dynamic_cast(getParent()); + if (stackp) + { + stackp->mNeedsLayout = true; + } + } + LLPanel::reshape(width, height, called_from_parent); +} + +void LLLayoutPanel::handleReshape(const LLRect& new_rect, bool by_user) +{ + LLLayoutStack* stackp = dynamic_cast(getParent()); + if (stackp) + { + if (by_user) + { // tell layout stack to account for new shape + + // make sure that panels have already been auto resized + stackp->updateLayout(); + // now apply requested size to panel + stackp->updatePanelRect(this, new_rect); + } + stackp->mNeedsLayout = true; + } + LLPanel::handleReshape(new_rect, by_user); +} + +static LLRegisterWidget r2("layout_stack"); + +LLLayoutStack::LLLayoutStack(ELayoutOrientation orientation, S32 border_size, bool animate, bool clip, F32 open_time_constant, F32 close_time_constant, F32 resize_bar_overlap) +: LLView(), + mPanelSpacing(border_size), + mOrientation(orientation), + mAnimate(animate), + mAnimatedThisFrame(false), + mNeedsLayout(true), + mClip(clip), + mOpenTimeConstant(open_time_constant), + mCloseTimeConstant(close_time_constant), + mResizeBarOverlap(resize_bar_overlap) +{} + +LLLayoutStack::~LLLayoutStack() +{ + e_panel_list_t panels = mPanels; // copy list of panel pointers + mPanels.clear(); // clear so that removeChild() calls don't cause trouble + std::for_each(panels.begin(), panels.end(), DeletePointer()); +} + +void LLLayoutStack::draw() +{ + updateLayout(); + + // always clip to stack itself + LLLocalClipRect clip(getLocalRect()); + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + // clip to layout rectangle, not bounding rectangle + LLRect clip_rect = panelp->getRect(); + // scale clipping rectangle by visible amount + if (mOrientation == HORIZONTAL) + { + clip_rect.mRight = clip_rect.mLeft + panelp->getVisibleDim(); + } + else + { + clip_rect.mBottom = clip_rect.mTop - panelp->getVisibleDim(); + } + + {LLLocalClipRect clip(clip_rect, mClip); + // only force drawing invisible children if visible amount is non-zero + drawChild(panelp, 0, 0, !clip_rect.isEmpty()); + + if (sDebugRects) + { + LLUI::pushMatrix(); + { + // drawing solids requires texturing be disabled + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + + LLUI::translate((F32)panelp->getRect().mLeft, (F32)panelp->getRect().mBottom, 0.f); + panelp->drawDebugRect(); + + // Check for bogus rectangle + if (!panelp->getRect().isValid()) + { + llwarns << "Bogus rectangle for " << panelp->getName() << " with " << panelp->getRect() << llendl; + } + } + LLUI::popMatrix(); + } + } + } + if (sDebugRects) + { + drawDebugRect(); + + // Check for bogus rectangle + if (!getRect().isValid()) + { + llwarns << "Bogus rectangle for " << getName() << " with " << getRect() << llendl; + } + } +} + +void LLLayoutStack::removeChild(LLView* view) +{ + LLLayoutPanel* embedded_panelp = findEmbeddedPanel(dynamic_cast(view)); + + if (embedded_panelp) + { + mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); + delete embedded_panelp; + updateFractionalSizes(); + mNeedsLayout = true; + } + + LLView::removeChild(view); +} + +BOOL LLLayoutStack::postBuild() +{ + updateLayout(); + return TRUE; +} + +bool LLLayoutStack::addChild(LLView* child, S32 tab_group) +{ + LLLayoutPanel* panelp = dynamic_cast(child); + if (panelp) + { + panelp->setOrientation(mOrientation); + mPanels.push_back(panelp); + createResizeBar(panelp); + mNeedsLayout = true; + } + BOOL result = LLView::addChild(child, tab_group); + + updateFractionalSizes(); + return result; +} + +void LLLayoutStack::addPanel(LLLayoutPanel* panel, EAnimate animate) +{ + addChild(panel); + + // panel starts off invisible (collapsed) + if (animate == ANIMATE) + { + panel->mVisibleAmt = 0.f; + panel->setVisible(TRUE); + } +} + +void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) +{ + LLLayoutPanel* panel_container = findEmbeddedPanel(panel); + if (!panel_container) return; + panel_container->mCollapsed = collapsed; + mNeedsLayout = true; +} + +static LLFastTimer::DeclareTimer FTM_UPDATE_LAYOUT("Update LayoutStacks"); + +void LLLayoutStack::updateLayout() +{ + LLFastTimer ft(FTM_UPDATE_LAYOUT); + + if (!mNeedsLayout) return; + + bool continue_animating = animatePanels(); + F32 total_visible_fraction = 0.f; + S32 space_to_distribute = (mOrientation == HORIZONTAL) + ? getRect().getWidth() + : getRect().getHeight(); + + // first, assign minimum dimensions + LLLayoutPanel* panelp = NULL; + BOOST_FOREACH(panelp, mPanels) + { + if (panelp->mAutoResize) + { + panelp->mTargetDim = panelp->getRelevantMinDim(); + } + space_to_distribute -= panelp->getVisibleDim() + llround((F32)mPanelSpacing * panelp->getVisibleAmount()); + total_visible_fraction += panelp->mFractionalSize * panelp->getAutoResizeFactor(); + } + + llassert(total_visible_fraction < 1.05f); + + // don't need spacing after last panel + space_to_distribute += panelp ? llround((F32)mPanelSpacing * panelp->getVisibleAmount()) : 0; + + S32 remaining_space = space_to_distribute; + F32 fraction_distributed = 0.f; + if (space_to_distribute > 0 && total_visible_fraction > 0.f) + { // give space proportionally to visible auto resize panels + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + F32 fraction_to_distribute = (panelp->mFractionalSize * panelp->getAutoResizeFactor()) / (total_visible_fraction); + S32 delta = llround((F32)space_to_distribute * fraction_to_distribute); + fraction_distributed += fraction_to_distribute; + panelp->mTargetDim += delta; + remaining_space -= delta; + } + } + } + + // distribute any left over pixels to non-collapsed, visible panels + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (remaining_space == 0) break; + + if (panelp->mAutoResize + && !panelp->mCollapsed + && panelp->getVisible()) + { + S32 space_for_panel = remaining_space > 0 ? 1 : -1; + panelp->mTargetDim += space_for_panel; + remaining_space -= space_for_panel; + } + } + + F32 cur_pos = (mOrientation == HORIZONTAL) ? 0.f : (F32)getRect().getHeight(); + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + F32 panel_dim = llmax(panelp->getExpandedMinDim(), panelp->mTargetDim); + F32 panel_visible_dim = panelp->getVisibleDim(); + + LLRect panel_rect; + if (mOrientation == HORIZONTAL) + { + panel_rect.setLeftTopAndSize(llround(cur_pos), + getRect().getHeight(), + llround(panel_dim), + getRect().getHeight()); + } + else + { + panel_rect.setLeftTopAndSize(0, + llround(cur_pos), + getRect().getWidth(), + llround(panel_dim)); + } + panelp->setIgnoreReshape(true); + panelp->setShape(panel_rect); + panelp->setIgnoreReshape(false); + + LLRect resize_bar_rect(panel_rect); + + F32 panel_spacing = (F32)mPanelSpacing * panelp->getVisibleAmount(); + if (mOrientation == HORIZONTAL) + { + resize_bar_rect.mLeft = panel_rect.mRight - mResizeBarOverlap; + resize_bar_rect.mRight = panel_rect.mRight + (S32)(llround(panel_spacing)) + mResizeBarOverlap; + + cur_pos += panel_visible_dim + panel_spacing; + } + else //VERTICAL + { + resize_bar_rect.mTop = panel_rect.mBottom + mResizeBarOverlap; + resize_bar_rect.mBottom = panel_rect.mBottom - (S32)(llround(panel_spacing)) - mResizeBarOverlap; + + cur_pos -= panel_visible_dim + panel_spacing; + } + panelp->mResizeBar->setShape(resize_bar_rect); + } + + updateResizeBarLimits(); + + // clear animation flag at end, since panel resizes will set it + // and leave it set if there is any animation in progress + mNeedsLayout = continue_animating; +} // end LLLayoutStack::updateLayout + +LLLayoutPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const +{ + if (!panelp) return NULL; + + e_panel_list_t::const_iterator panel_it; + BOOST_FOREACH(LLLayoutPanel* p, mPanels) + { + if (p == panelp) + { + return p; + } + } + return NULL; +} + +LLLayoutPanel* LLLayoutStack::findEmbeddedPanelByName(const std::string& name) const +{ + LLLayoutPanel* result = NULL; + + BOOST_FOREACH(LLLayoutPanel* p, mPanels) + { + if (p->getName() == name) + { + result = p; + break; + } + } + + return result; +} + +void LLLayoutStack::createResizeBar(LLLayoutPanel* panelp) +{ + BOOST_FOREACH(LLLayoutPanel* lp, mPanels) + { + if (lp->mResizeBar == NULL) + { + LLResizeBar::Side side = (mOrientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM; + + LLResizeBar::Params resize_params; + resize_params.name("resize"); + resize_params.resizing_view(lp); + resize_params.min_size(lp->getRelevantMinDim()); + resize_params.side(side); + resize_params.snapping_enabled(false); + LLResizeBar* resize_bar = LLUICtrlFactory::create(resize_params); + lp->mResizeBar = resize_bar; + LLView::addChild(resize_bar, 0); + } + } + // bring all resize bars to the front so that they are clickable even over the panels + // with a bit of overlap + for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) + { + LLResizeBar* resize_barp = (*panel_it)->mResizeBar; + sendChildToFront(resize_barp); + } +} + +// update layout stack animations, etc. once per frame +// NOTE: we use this to size world view based on animating UI, *before* we draw the UI +// we might still need to call updateLayout during UI draw phase, in case UI elements +// are resizing themselves dynamically +//static +void LLLayoutStack::updateClass() +{ + for (instance_iter it = beginInstances(); it != endInstances(); ++it) + { + it->updateLayout(); + it->mAnimatedThisFrame = false; + } +} + +void LLLayoutStack::updateFractionalSizes() +{ + F32 total_resizable_dim = 0.f; + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + total_resizable_dim += llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); + } + } + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + F32 panel_resizable_dim = llmax(MIN_FRACTIONAL_SIZE, (F32)(panelp->getLayoutDim() - panelp->getRelevantMinDim())); + panelp->mFractionalSize = panel_resizable_dim > 0.f + ? llclamp(panel_resizable_dim / total_resizable_dim, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE) + : MIN_FRACTIONAL_SIZE; + llassert(!llisnan(panelp->mFractionalSize)); + } + } + + normalizeFractionalSizes(); +} + + +void LLLayoutStack::normalizeFractionalSizes() +{ + S32 num_auto_resize_panels = 0; + F32 total_fractional_size = 0.f; + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + total_fractional_size += panelp->mFractionalSize; + num_auto_resize_panels++; + } + } + + if (total_fractional_size == 0.f) + { // equal distribution + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + panelp->mFractionalSize = MAX_FRACTIONAL_SIZE / (F32)num_auto_resize_panels; + } + } + } + else + { // renormalize + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + panelp->mFractionalSize /= total_fractional_size; + } + } + } +} + +bool LLLayoutStack::animatePanels() +{ + bool continue_animating = false; + + // + // animate visibility + // + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->getVisible()) + { + if (mAnimate && panelp->mVisibleAmt < 1.f) + { + if (!mAnimatedThisFrame) + { + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(mOpenTimeConstant)); + if (panelp->mVisibleAmt > 0.99f) + { + panelp->mVisibleAmt = 1.f; + } + } + + mAnimatedThisFrame = true; + continue_animating = true; + } + else + { + if (panelp->mVisibleAmt != 1.f) + { + panelp->mVisibleAmt = 1.f; + mAnimatedThisFrame = true; + } + } + } + else // not visible + { + if (mAnimate && panelp->mVisibleAmt > 0.f) + { + if (!mAnimatedThisFrame) + { + panelp->mVisibleAmt = lerp(panelp->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + if (panelp->mVisibleAmt < 0.001f) + { + panelp->mVisibleAmt = 0.f; + } + } + + continue_animating = true; + mAnimatedThisFrame = true; + } + else + { + if (panelp->mVisibleAmt != 0.f) + { + panelp->mVisibleAmt = 0.f; + mAnimatedThisFrame = true; + } + } + } + + F32 collapse_state = panelp->mCollapsed ? 1.f : 0.f; + if (panelp->mCollapseAmt != collapse_state) + { + if (mAnimate) + { + if (!mAnimatedThisFrame) + { + panelp->mCollapseAmt = lerp(panelp->mCollapseAmt, collapse_state, LLCriticalDamp::getInterpolant(mCloseTimeConstant)); + } + + if (llabs(panelp->mCollapseAmt - collapse_state) < 0.001f) + { + panelp->mCollapseAmt = collapse_state; + } + + mAnimatedThisFrame = true; + continue_animating = true; + } + else + { + panelp->mCollapseAmt = collapse_state; + mAnimatedThisFrame = true; + } + } + } + + if (mAnimatedThisFrame) mNeedsLayout = true; + return continue_animating; +} + +void LLLayoutStack::updatePanelRect( LLLayoutPanel* resized_panel, const LLRect& new_rect ) +{ + S32 new_dim = (mOrientation == HORIZONTAL) + ? new_rect.getWidth() + : new_rect.getHeight(); + S32 delta_panel_dim = new_dim - resized_panel->getVisibleDim(); + if (delta_panel_dim == 0) return; + + F32 total_visible_fraction = 0.f; + F32 delta_auto_resize_headroom = 0.f; + F32 old_auto_resize_headroom = 0.f; + + LLLayoutPanel* other_resize_panel = NULL; + LLLayoutPanel* following_panel = NULL; + + BOOST_REVERSE_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (panelp->mAutoResize) + { + old_auto_resize_headroom += (F32)(panelp->mTargetDim - panelp->getRelevantMinDim()); + if (panelp->getVisible() && !panelp->mCollapsed) + { + total_visible_fraction += panelp->mFractionalSize; + } + } + + if (panelp == resized_panel) + { + other_resize_panel = following_panel; + } + + if (panelp->getVisible() && !panelp->mCollapsed) + { + following_panel = panelp; + } + } + + if (resized_panel->mAutoResize) + { + if (!other_resize_panel || !other_resize_panel->mAutoResize) + { + delta_auto_resize_headroom += delta_panel_dim; + } + } + else + { + if (!other_resize_panel || other_resize_panel->mAutoResize) + { + delta_auto_resize_headroom -= delta_panel_dim; + } + } + + F32 fraction_given_up = 0.f; + F32 fraction_remaining = 1.f; + F32 new_auto_resize_headroom = old_auto_resize_headroom + delta_auto_resize_headroom; + + enum + { + BEFORE_RESIZED_PANEL, + RESIZED_PANEL, + NEXT_PANEL, + AFTER_RESIZED_PANEL + } which_panel = BEFORE_RESIZED_PANEL; + + BOOST_FOREACH(LLLayoutPanel* panelp, mPanels) + { + if (!panelp->getVisible() || panelp->mCollapsed) + { + if (panelp->mAutoResize) + { + fraction_remaining -= panelp->mFractionalSize; + } + continue; + } + + if (panelp == resized_panel) + { + which_panel = RESIZED_PANEL; + } + + switch(which_panel) + { + case BEFORE_RESIZED_PANEL: + if (panelp->mAutoResize) + { // freeze current size as fraction of overall auto_resize space + F32 fractional_adjustment_factor = new_auto_resize_headroom == 0.f + ? 1.f + : old_auto_resize_headroom / new_auto_resize_headroom; + F32 new_fractional_size = llclamp(panelp->mFractionalSize * fractional_adjustment_factor, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + fraction_remaining -= panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + llassert(!llisnan(panelp->mFractionalSize)); + } + else + { + // leave non auto-resize panels alone + } + break; + case RESIZED_PANEL: + if (panelp->mAutoResize) + { // freeze new size as fraction + F32 new_fractional_size = (new_auto_resize_headroom == 0.f) + ? MAX_FRACTIONAL_SIZE + : llclamp(total_visible_fraction * (F32)(new_dim - panelp->getRelevantMinDim()) / new_auto_resize_headroom, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + fraction_remaining -= panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + llassert(!llisnan(panelp->mFractionalSize)); + } + else + { // freeze new size as original size + panelp->mTargetDim = new_dim; + } + which_panel = NEXT_PANEL; + break; + case NEXT_PANEL: + if (panelp->mAutoResize) + { + fraction_remaining -= panelp->mFractionalSize; + if (resized_panel->mAutoResize) + { + panelp->mFractionalSize = llclamp(panelp->mFractionalSize + fraction_given_up, MIN_FRACTIONAL_SIZE, MAX_FRACTIONAL_SIZE); + fraction_given_up = 0.f; + } + else + { + if (new_auto_resize_headroom < 1.f) + { + new_auto_resize_headroom = 1.f; + } + + F32 new_fractional_size = llclamp(total_visible_fraction * (F32)(panelp->mTargetDim - panelp->getRelevantMinDim() + delta_auto_resize_headroom) + / new_auto_resize_headroom, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + fraction_given_up -= new_fractional_size - panelp->mFractionalSize; + panelp->mFractionalSize = new_fractional_size; + } + } + else + { + panelp->mTargetDim -= delta_panel_dim; + } + which_panel = AFTER_RESIZED_PANEL; + break; + case AFTER_RESIZED_PANEL: + if (panelp->mAutoResize && fraction_given_up != 0.f) + { + panelp->mFractionalSize = llclamp(panelp->mFractionalSize + (panelp->mFractionalSize / fraction_remaining) * fraction_given_up, + MIN_FRACTIONAL_SIZE, + MAX_FRACTIONAL_SIZE); + } + default: + break; + } + } + updateLayout(); + //normalizeFractionalSizes(); +} + +void LLLayoutStack::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + mNeedsLayout = true; + LLView::reshape(width, height, called_from_parent); +} + +void LLLayoutStack::updateResizeBarLimits() +{ + LLLayoutPanel* previous_visible_panelp = NULL; + BOOST_REVERSE_FOREACH(LLLayoutPanel* visible_panelp, mPanels) + { + if (!visible_panelp->getVisible() || visible_panelp->mCollapsed) + { + visible_panelp->mResizeBar->setVisible(FALSE); + continue; + } + + // toggle resize bars based on panel visibility, resizability, etc + if (previous_visible_panelp + && (visible_panelp->mUserResize || previous_visible_panelp->mUserResize) // one of the pair is user resizable + && (visible_panelp->mAutoResize || visible_panelp->mUserResize) // current panel is resizable + && (previous_visible_panelp->mAutoResize || previous_visible_panelp->mUserResize)) // previous panel is resizable + { + visible_panelp->mResizeBar->setVisible(TRUE); + S32 previous_panel_headroom = previous_visible_panelp->getVisibleDim() - previous_visible_panelp->getRelevantMinDim(); + visible_panelp->mResizeBar->setResizeLimits(visible_panelp->getRelevantMinDim(), + visible_panelp->getVisibleDim() + previous_panel_headroom); + } + else + { + visible_panelp->mResizeBar->setVisible(FALSE); + } + + previous_visible_panelp = visible_panelp; + } +} + +LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const +{ + LLXMLNodePtr node = LLView::getXML(); + node->setName(LL_LAYOUT_STACK_TAG); + + if (mOrientation == HORIZONTAL) + { + node->createChild("orientation", TRUE)->setStringValue("horizontal"); + } + else + { + node->createChild("orientation", TRUE)->setStringValue("vertical"); + } + + if (save_children) + { + LLView::child_list_const_reverse_iter_t rit; + for (rit = getChildList()->rbegin(); rit != getChildList()->rend(); ++rit) + { + LLView* childp = *rit; + + if (childp->getSaveToXML()) + { + LLXMLNodePtr xml_node = childp->getXML(); + + if (xml_node->hasName(LL_PANEL_TAG)) + { + xml_node->setName(LL_LAYOUT_PANEL_TAG); + } + + node->addChild(xml_node); + } + } + } + + return node; +} + +//static +LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + std::string orientation_string("vertical"); + node->getAttributeString("orientation", orientation_string); + + ELayoutOrientation orientation = VERTICAL; + + if (orientation_string == "horizontal") + { + orientation = HORIZONTAL; + } + else if (orientation_string == "vertical") + { + orientation = VERTICAL; + } + else + { + llwarns << "Unknown orientation " << orientation_string << ", using vertical" << llendl; + } + + BOOL clip = false; + BOOL animate = true; + S32 border_size = RESIZE_BAR_HEIGHT; + F32 open_time_constant = 0.02f; + F32 close_time_constant = 0.02f; + + node->getAttributeBOOL("animate", animate); + node->getAttributeBOOL("clip", clip); + node->getAttributeS32("border_size", border_size); + node->getAttributeF32("over_time_constant", open_time_constant); + node->getAttributeF32("close_time_constant", close_time_constant); + + border_size = llmax(border_size,0); + + LLLayoutStack* layout_stackp = new LLLayoutStack(orientation,border_size,animate,clip,open_time_constant,close_time_constant,RESIZE_BAR_OVERLAP); + + std::string name("stack"); + node->getAttributeString("name", name); + + layout_stackp->setName(name); + layout_stackp->initFromXML(node, parent); + + LLXMLNodePtr child; + for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) + { + std::string ctrl_type = child->getName()->mString; + LLStringUtil::toLower(ctrl_type); + child->createChild("orientation", TRUE)->setStringValue(orientation == HORIZONTAL ? "horizontal" : "vertical"); + LLWidgetClassRegistry::factory_func_t func = LLWidgetClassRegistry::getInstance()->getCreatorFunc(ctrl_type); + + if(func) + { + LLView* widget = func(child,layout_stackp,factory); + if(widget) + layout_stackp->addChild(widget); + } + } + layout_stackp->updateLayout(); + + return layout_stackp; +} + +LLView* LLLayoutPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *factory) +{ + llassert_always(dynamic_cast(parent)!=NULL); + + std::string name("layout panel"); + node->getAttributeString("name", name); + + LLLayoutPanel* panelp = NULL; + LLPanel* factory_panelp = factory->createFactoryPanel(name); + if(factory_panelp) + llassert_always((panelp = dynamic_cast(factory_panelp)) != NULL); + // Fall back on a default panel, if there was no special factory. + if (!panelp) + { + // create a new panel without a border, by default + panelp = new LLLayoutPanel(-1,TRUE,TRUE,LLRect()); + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); + panelp->initPanelXML(node, parent, factory); + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); + + } + else + { + if(!factory->builtPanel(panelp)) + { + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); + panelp->initPanelXML(node, parent, factory); + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); + } + } + panelp->setOrigin(0, 0); + + return panelp; +} \ No newline at end of file diff --git a/indra/llui/lllayoutstack.h b/indra/llui/lllayoutstack.h new file mode 100644 index 000000000..2fd8a6407 --- /dev/null +++ b/indra/llui/lllayoutstack.h @@ -0,0 +1,179 @@ +/** + * @file lllayoutstack.h + * @author Richard Nelson + * @brief LLLayout class - dynamic stacking of UI elements + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Reshasearch, 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$ + */ + +#ifndef LL_LLLAYOUTSTACK_H +#define LL_LLLAYOUTSTACK_H + +#include "llpanel.h" +#include "llresizebar.h" + + +class LLLayoutPanel; + + +class LLLayoutStack : public LLView, public LLInstanceTracker +{ +public: + typedef enum e_layout_orientation + { + HORIZONTAL, + VERTICAL + } ELayoutOrientation; + + LLLayoutStack(ELayoutOrientation orientation, S32 border_size, bool animate, bool clip, F32 open_time_constant, F32 close_time_constant, F32 resize_bar_overlap); + virtual ~LLLayoutStack(); + + /*virtual*/ void draw(); + /*virtual*/ void removeChild(LLView*); + /*virtual*/ BOOL postBuild(); + /*virtual*/ bool addChild(LLView* child, S32 tab_groupdatefractuiona = 0); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + + /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + + typedef enum e_animate + { + NO_ANIMATE, + ANIMATE + } EAnimate; + + void addPanel(LLLayoutPanel* panel, EAnimate animate = NO_ANIMATE); + void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE); + S32 getNumPanels() { return mPanels.size(); } + + void updateLayout(); + + S32 getPanelSpacing() const { return mPanelSpacing; } + + static void updateClass(); + +protected: + LLLayoutStack(const Params&); + friend class LLUICtrlFactory; + friend class LLLayoutPanel; + +private: + void updateResizeBarLimits(); + bool animatePanels(); + void createResizeBar(LLLayoutPanel* panel); + + const ELayoutOrientation mOrientation; + + typedef std::vector e_panel_list_t; + e_panel_list_t mPanels; + + LLLayoutPanel* findEmbeddedPanel(LLPanel* panelp) const; + LLLayoutPanel* findEmbeddedPanelByName(const std::string& name) const; + void updateFractionalSizes(); + void normalizeFractionalSizes(); + void updatePanelRect( LLLayoutPanel* param1, const LLRect& new_rect ); + + S32 mPanelSpacing; + + // true if we already applied animation this frame + bool mAnimatedThisFrame; + bool mAnimate; + bool mClip; + F32 mOpenTimeConstant; + F32 mCloseTimeConstant; + bool mNeedsLayout; + S32 mResizeBarOverlap; +}; // end class LLLayoutStack + + +class LLLayoutPanel : public LLPanel +{ +friend class LLLayoutStack; +friend class LLUICtrlFactory; +public: + ~LLLayoutPanel(); + + void initFromParams(const Params& p); + static LLView* fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *factory); + + void handleReshape(const LLRect& new_rect, bool by_user); + + void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + + + void setVisible(BOOL visible); + + S32 getLayoutDim() const; + S32 getTargetDim() const; + void setTargetDim(S32 value); + S32 getMinDim() const { return llmax(0, mMinDim); } + void setMinDim(S32 value) { mMinDim = value; } + + S32 getExpandedMinDim() const { return mExpandedMinDim >= 0 ? mExpandedMinDim : getMinDim(); } + void setExpandedMinDim(S32 value) { mExpandedMinDim = value; } + + S32 getRelevantMinDim() const + { + S32 min_dim = mMinDim; + + if (!mCollapsed) + { + min_dim = getExpandedMinDim(); + } + + return min_dim; + } + + F32 getAutoResizeFactor() const; + F32 getVisibleAmount() const; + S32 getVisibleDim() const; + LLResizeBar* getResizeBar() { return mResizeBar; } + + bool isCollapsed() const { return mCollapsed;} + + void setOrientation(LLLayoutStack::ELayoutOrientation orientation); + void storeOriginalDim(); + + void setIgnoreReshape(bool ignore) { mIgnoreReshape = ignore; } + +protected: + LLLayoutPanel(S32 min_dim=-1, BOOL auto_resize=TRUE, BOOL user_resize=TRUE, LLRect rect = LLRect()); + /*virtual*/ BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + + bool mAutoResize; + bool mUserResize; + + S32 mExpandedMinDim; + S32 mMinDim; + bool mCollapsed; + F32 mVisibleAmt; + F32 mCollapseAmt; + F32 mFractionalSize; + S32 mTargetDim; + bool mIgnoreReshape; + LLLayoutStack::ELayoutOrientation mOrientation; + class LLResizeBar* mResizeBar; +}; + + +#endif diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 811de99a9..15b387cbc 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -1702,6 +1702,7 @@ void LLLineEditor::drawMisspelled(LLRect background) if (glggHunSpell->getSpellCheckHighlight()) { + F32 alpha = getDrawContext().mAlpha; for (int i =0; i<(int)misspellLocations.size(); i++) { S32 wstart =findPixelNearestPos( misspellLocations[i]-getCursor()); @@ -1716,7 +1717,7 @@ void LLLineEditor::drawMisspelled(LLRect background) { wstart = maxw; } - gGL.color4ub(255,0,0,200); + gGL.color4ub(255,0,0,200*alpha); //3 line zig zags.. while (wstart < wend) { @@ -1733,6 +1734,7 @@ void LLLineEditor::drawMisspelled(LLRect background) void LLLineEditor::draw() { + F32 alpha = getDrawContext().mAlpha; S32 text_len = mText.length(); std::string saved_text; @@ -1776,7 +1778,7 @@ void LLLineEditor::draw() bg_color = mWriteableBgColor; } } - gl_rect_2d(background, bg_color); + gl_rect_2d(background, bg_color % alpha); } #endif @@ -1801,7 +1803,9 @@ void LLLineEditor::draw() { text_color = mReadOnlyFgColor; } - LLColor4 label_color = mTentativeFgColor; + + text_color %= alpha; + LLColor4 label_color = mTentativeFgColor % alpha; if (hasPreeditString()) { @@ -1824,7 +1828,7 @@ void LLLineEditor::draw() background.mBottom + PREEDIT_STANDOUT_POSITION, preedit_pixels_right - PREEDIT_STANDOUT_GAP - 1, background.mBottom + PREEDIT_STANDOUT_POSITION - PREEDIT_STANDOUT_THICKNESS, - (text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f)); + (text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 - PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(alpha)); } else { @@ -1832,7 +1836,7 @@ void LLLineEditor::draw() background.mBottom + PREEDIT_MARKER_POSITION, preedit_pixels_right - PREEDIT_MARKER_GAP - 1, background.mBottom + PREEDIT_MARKER_POSITION - PREEDIT_MARKER_THICKNESS, - (text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f)); + (text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 - PREEDIT_MARKER_BRIGHTNESS)).setAlpha(alpha)); } } } @@ -1874,7 +1878,7 @@ void LLLineEditor::draw() if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) ) { - LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], 1.f); + LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], alpha ); // selected middle S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text); width = llmin(width, mMaxHPixels - llround(rendered_pixels_right)); @@ -1883,7 +1887,7 @@ void LLLineEditor::draw() rendered_text += mGLFont->render( mText, mScrollHPos + rendered_text, rendered_pixels_right, text_bottom, - LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ), + LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, @@ -1953,7 +1957,7 @@ void LLLineEditor::draw() if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) { mGLFont->render(mText, getCursor(), (F32)(cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS / 2), text_bottom, - LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ), + LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], alpha ), LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, @@ -2551,9 +2555,6 @@ LLXMLNodePtr LLLineEditor::getXML(bool save_children) const // static LLView* LLLineEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("line_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -2580,7 +2581,7 @@ LLView* LLLineEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory S32 border_thickness = 1; node->getAttributeS32("border_thickness", border_thickness); - LLLineEditor* line_editor = new LLLineEditor(name, + LLLineEditor* line_editor = new LLLineEditor("line_editor", rect, text, font, diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index f1f61a045..c501d865c 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -1928,6 +1928,7 @@ LLXMLNodePtr LLMenuGL::getXML(bool save_children) const void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory *factory) { + std::string name(child->getName()->mString); if (child->hasName(LL_MENU_GL_TAG)) { // SUBMENU @@ -2025,7 +2026,7 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) { - if (call_child->hasName("on_check")) + if (call_child->hasName("on_check") || call_child->hasName(name+".on_check")) { std::string callback_name; std::string control_name; @@ -2083,7 +2084,7 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling()) { - if (call_child->hasName("on_click")) + if (call_child->hasName("on_click") || call_child->hasName(name+".on_click")) { std::string callback_name; call_child->getAttributeString("function", callback_name); @@ -2104,7 +2105,7 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory new_item->addListener(callback, "on_click", callback_data); } - if (call_child->hasName("on_enable")) + if (call_child->hasName("on_enable") || call_child->hasName(name+".on_enable")) { std::string callback_name; std::string control_name; @@ -2148,7 +2149,7 @@ void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory } new_item->setEnabledControl(control_name, parent); } - if (call_child->hasName("on_visible")) + if (call_child->hasName("on_visible") || call_child->hasName(name+".on_visible")) { std::string callback_name; std::string control_name; @@ -2322,7 +2323,7 @@ LLView* LLMenuGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *fa ++token_count; } - BOOL opaque = FALSE; + BOOL opaque = TRUE; node->getAttributeBOOL("opaque", opaque); LLMenuGL *menu = new LLMenuGL(name, new_menu_label); @@ -4150,13 +4151,10 @@ LLXMLNodePtr LLMenuBarGL::getXML(bool save_children) const LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("menu"); - node->getAttributeString("name", name); - BOOL opaque = FALSE; node->getAttributeBOOL("opaque", opaque); - LLMenuBarGL *menubar = new LLMenuBarGL(name); + LLMenuBarGL *menubar = new LLMenuBarGL("menu"); LLHandle parent_handle; LLFloater* parent_floater = dynamic_cast(parent); diff --git a/indra/llui/llmultislider.cpp b/indra/llui/llmultislider.cpp index f0b140b40..183a1245f 100644 --- a/indra/llui/llmultislider.cpp +++ b/indra/llui/llmultislider.cpp @@ -624,9 +624,6 @@ LLXMLNodePtr LLMultiSlider::getXML(bool save_children) const //static LLView* LLMultiSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("multi_slider_bar"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -654,7 +651,7 @@ LLView* LLMultiSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor BOOL use_triangle = FALSE; node->getAttributeBOOL("use_triangle", use_triangle); - LLMultiSlider* multiSlider = new LLMultiSlider(name, + LLMultiSlider* multiSlider = new LLMultiSlider("multi_slider_bar", rect, NULL, initial_value, diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 29fe889d3..50da07787 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -470,9 +470,6 @@ LLXMLNodePtr LLMultiSliderCtrl::getXML(bool save_children) const LLView* LLMultiSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("multi_slider"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -556,7 +553,7 @@ LLView* LLMultiSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFa label.assign(node->getTextContents()); } - LLMultiSliderCtrl* slider = new LLMultiSliderCtrl(name, + LLMultiSliderCtrl* slider = new LLMultiSliderCtrl("multi_slider", rect, label, font, diff --git a/indra/llui/llnotificationcontext.h b/indra/llui/llnotificationcontext.h new file mode 100644 index 000000000..fe83017ee --- /dev/null +++ b/indra/llui/llnotificationcontext.h @@ -0,0 +1,59 @@ +/** +* @file llnotifications.h +* @brief Notification context class, pulled out of llnotifications.h to avoid including said file in llfloater.h. (Needed, as llnotifications.h includes llevents.h, which causes issues) +* @author Q (with assistance from Richard and Coco) +* +* $LicenseInfo:firstyear=2008&license=viewergpl$ +* +* Copyright (c) 2008-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$ +*/ + + +#ifndef LL_LLNOTIFICATION_CONTEX +#define LL_LLNOTIFICATION_CONTEX + +// context data that can be looked up via a notification's payload by the display logic +// derive from this class to implement specific contexts +class LLNotificationContext : public LLInstanceTracker +{ +public: + LLNotificationContext() : LLInstanceTracker(LLUUID::generateNewID()) + { + } + + virtual ~LLNotificationContext() {} + + LLSD asLLSD() const + { + return getKey(); + } + +private: + +}; + +#endif //LL_LLNOTIFICATION_CONTEX + diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index b950d5274..41254b0bb 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1562,4 +1562,3 @@ std::ostream& operator<<(std::ostream& s, const LLNotification& notification) s << notification.summarize(); return s; } - diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 1585f35f4..fe9049ee2 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -100,11 +100,13 @@ #include "llinstancetracker.h" // and we need this to manage the notification callbacks +#include "llavatarname.h" #include "llevents.h" #include "llfunctorregistry.h" #include "llui.h" #include "llxmlnode.h" #include "llnotificationptr.h" +#include "llnotificationcontext.h" typedef enum e_notification_priority { @@ -120,26 +122,6 @@ typedef boost::function LLNotificationResponder typedef LLFunctorRegistry LLNotificationFunctorRegistry; typedef LLFunctorRegistration LLNotificationFunctorRegistration; -// context data that can be looked up via a notification's payload by the display logic -// derive from this class to implement specific contexts -class LLNotificationContext : public LLInstanceTracker -{ -public: - LLNotificationContext() : LLInstanceTracker(LLUUID::generateNewID()) - { - } - - virtual ~LLNotificationContext() {} - - LLSD asLLSD() const - { - return getKey(); - } - -private: - -}; - // Contains notification form data, such as buttons and text fields along with // manipulator functions class LLNotificationForm @@ -791,7 +773,5 @@ private: LLNotificationMap mUniqueNotifications; }; - - #endif//LL_LLNOTIFICATIONS_H diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index dd388e719..9435f65bb 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -58,13 +58,12 @@ #include "llnotificationsutil.h" #include "llfasttimer.h" +#include "llnotifications.h" + // LLLayoutStack #include "llresizebar.h" #include "llcriticaldamp.h" -const S32 RESIZE_BAR_OVERLAP = 1; -const S32 RESIZE_BAR_HEIGHT = 3; - static LLRegisterWidget r1("panel"); void LLPanel::init() @@ -84,7 +83,9 @@ void LLPanel::init() } LLPanel::LLPanel() -: mRectControl() +: mRectControl(), + mCommitCallbackRegistrar(false), + mEnableCallbackRegistrar(false) { init(); setName(std::string("panel")); @@ -92,7 +93,9 @@ LLPanel::LLPanel() LLPanel::LLPanel(const std::string& name) : LLUICtrl(name), - mRectControl() + mRectControl(), + mCommitCallbackRegistrar(false), + mEnableCallbackRegistrar(false) { init(); } @@ -100,7 +103,9 @@ LLPanel::LLPanel(const std::string& name) LLPanel::LLPanel(const std::string& name, const LLRect& rect, BOOL bordered) : LLUICtrl(name,rect), - mRectControl() + mRectControl(), + mCommitCallbackRegistrar(false), + mEnableCallbackRegistrar(false) { init(); if (bordered) @@ -112,7 +117,9 @@ LLPanel::LLPanel(const std::string& name, const LLRect& rect, BOOL bordered) LLPanel::LLPanel(const std::string& name, const std::string& rect_control, BOOL bordered) : LLUICtrl(name, LLUI::sConfigGroup->getRect(rect_control)), - mRectControl( rect_control ) + mRectControl( rect_control ), + mCommitCallbackRegistrar(false), + mEnableCallbackRegistrar(false) { init(); if (bordered) @@ -459,7 +466,12 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac createRect(node, rect, parent, LLRect()); // create a new panel without a border, by default panelp = new LLPanel(name, rect, FALSE); + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); panelp->initPanelXML(node, parent, factory); + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); // preserve panel's width and height, but override the location const LLRect& panelrect = panelp->getRect(); S32 w = panelrect.getWidth(); @@ -470,7 +482,14 @@ LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parent, LLUICtrlFactory *fac else { if(!factory->builtPanel(panelp)) + { + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->mCommitCallbackRegistrar.pushScope(); + panelp->mEnableCallbackRegistrar.pushScope(); panelp->initPanelXML(node, parent, factory); + panelp->mCommitCallbackRegistrar.popScope(); + panelp->mEnableCallbackRegistrar.popScope(); + } else { LLRect new_rect = panelp->getRect(); @@ -1020,651 +1039,3 @@ void LLPanel::storeRectControl() } -// -// LLLayoutStack -// -struct LLLayoutStack::LLEmbeddedPanel -{ - LLEmbeddedPanel(LLPanel* panelp, eLayoutOrientation orientation, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize) : - mPanel(panelp), - mMinWidth(min_width), - mMinHeight(min_height), - mAutoResize(auto_resize), - mUserResize(user_resize), - mOrientation(orientation), - mCollapsed(FALSE), - mCollapseAmt(0.f), - mVisibleAmt(1.f) // default to fully visible - { - LLResizeBar::Side side = (orientation == HORIZONTAL) ? LLResizeBar::RIGHT : LLResizeBar::BOTTOM; - - S32 min_dim; - if (orientation == HORIZONTAL) - { - min_dim = mMinHeight; - } - else - { - min_dim = mMinWidth; - } - LLResizeBar::Params p; - p.name = "resizer"; - p.resizing_view = mPanel; - p.min_size = min_dim; - p.max_size = S32_MAX; - p.side = side; - mResizeBar = LLUICtrlFactory::create(p); - mResizeBar->setEnableSnapping(FALSE); - // panels initialized as hidden should not start out partially visible - if (!mPanel->getVisible()) - { - mVisibleAmt = 0.f; - } - } - - ~LLEmbeddedPanel() - { - // probably not necessary, but... - delete mResizeBar; - mResizeBar = NULL; - } - - F32 getCollapseFactor() - { - if (mOrientation == HORIZONTAL) - { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, (F32)mMinWidth / (F32)llmax(1, mPanel->getRect().getWidth())); - return mVisibleAmt * collapse_amt; - } - else - { - F32 collapse_amt = - clamp_rescale(mCollapseAmt, 0.f, 1.f, 1.f, llmin(1.f, (F32)mMinHeight / (F32)llmax(1, mPanel->getRect().getHeight()))); - return mVisibleAmt * collapse_amt; - } - } - - LLPanel* mPanel; - S32 mMinWidth; - S32 mMinHeight; - BOOL mAutoResize; - BOOL mUserResize; - BOOL mCollapsed; - LLResizeBar* mResizeBar; - eLayoutOrientation mOrientation; - F32 mVisibleAmt; - F32 mCollapseAmt; -}; - -static LLRegisterWidget r2("layout_stack"); - -LLLayoutStack::LLLayoutStack(eLayoutOrientation orientation) : - mOrientation(orientation), - mMinWidth(0), - mMinHeight(0), - mPanelSpacing(RESIZE_BAR_HEIGHT) -{ -} - -LLLayoutStack::~LLLayoutStack() -{ - std::for_each(mPanels.begin(), mPanels.end(), DeletePointer()); -} - -void LLLayoutStack::draw() -{ - updateLayout(); - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - // clip to layout rectangle, not bounding rectangle - LLRect clip_rect = (*panel_it)->mPanel->getRect(); - // scale clipping rectangle by visible amount - if (mOrientation == HORIZONTAL) - { - clip_rect.mRight = clip_rect.mLeft + llround((F32)clip_rect.getWidth() * (*panel_it)->getCollapseFactor()); - } - else - { - clip_rect.mBottom = clip_rect.mTop - llround((F32)clip_rect.getHeight() * (*panel_it)->getCollapseFactor()); - } - - LLPanel* panelp = (*panel_it)->mPanel; - - LLLocalClipRect clip(clip_rect); - // only force drawing invisible children if visible amount is non-zero - drawChild(panelp, 0, 0, clip_rect.notEmpty()); - } -} - -void LLLayoutStack::removeChild(LLView* ctrl) -{ - LLView::removeChild(ctrl); - LLPanel* panel = dynamic_cast(ctrl); - if(!panel) - return; - LLEmbeddedPanel* embedded_panelp = findEmbeddedPanel(panel); - - if (embedded_panelp) - { - mPanels.erase(std::find(mPanels.begin(), mPanels.end(), embedded_panelp)); - delete embedded_panelp; - } - - // need to update resizebars - - calcMinExtents(); -} - -LLXMLNodePtr LLLayoutStack::getXML(bool save_children) const -{ - LLXMLNodePtr node = LLView::getXML(); - node->setName(LL_LAYOUT_STACK_TAG); - - if (mOrientation == HORIZONTAL) - { - node->createChild("orientation", TRUE)->setStringValue("horizontal"); - } - else - { - node->createChild("orientation", TRUE)->setStringValue("vertical"); - } - - if (save_children) - { - LLView::child_list_const_reverse_iter_t rit; - for (rit = getChildList()->rbegin(); rit != getChildList()->rend(); ++rit) - { - LLView* childp = *rit; - - if (childp->getSaveToXML()) - { - LLXMLNodePtr xml_node = childp->getXML(); - - if (xml_node->hasName(LL_PANEL_TAG)) - { - xml_node->setName(LL_LAYOUT_PANEL_TAG); - } - - node->addChild(xml_node); - } - } - } - - return node; -} - -//static -LLView* LLLayoutStack::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) -{ - std::string orientation_string("vertical"); - node->getAttributeString("orientation", orientation_string); - - eLayoutOrientation orientation = VERTICAL; - - if (orientation_string == "horizontal") - { - orientation = HORIZONTAL; - } - else if (orientation_string == "vertical") - { - orientation = VERTICAL; - } - else - { - llwarns << "Unknown orientation " << orientation_string << ", using vertical" << llendl; - } - - LLLayoutStack* layout_stackp = new LLLayoutStack(orientation); - - node->getAttributeS32("border_size", layout_stackp->mPanelSpacing); - // don't allow negative spacing values - layout_stackp->mPanelSpacing = llmax(layout_stackp->mPanelSpacing, 0); - - std::string name("stack"); - node->getAttributeString("name", name); - - layout_stackp->setName(name); - layout_stackp->initFromXML(node, parent); - - LLXMLNodePtr child; - for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling()) - { - S32 min_width = 0; - S32 min_height = 0; - BOOL auto_resize = TRUE; - - child->getAttributeS32("min_width", min_width); - child->getAttributeS32("min_height", min_height); - child->getAttributeBOOL("auto_resize", auto_resize); - - if (child->hasName("layout_panel")) - { - BOOL user_resize = TRUE; - child->getAttributeBOOL("user_resize", user_resize); - LLPanel* panelp = (LLPanel*)LLPanel::fromXML(child, layout_stackp, factory); - if (panelp) - { - panelp->setFollowsNone(); - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); - } - } - else - { - BOOL user_resize = FALSE; - child->getAttributeBOOL("user_resize", user_resize); - - LLPanel* panelp = new LLPanel(std::string("auto_panel")); - LLView* new_child = factory->createWidget(panelp, child); - if (new_child) - { - // put child in new embedded panel - layout_stackp->addPanel(panelp, min_width, min_height, auto_resize, user_resize); - // resize panel to contain widget and move widget to be contained in panel - panelp->setRect(new_child->getRect()); - new_child->setOrigin(0, 0); - } - else - { - panelp->die(); - } - } - } - layout_stackp->updateLayout(); - - return layout_stackp; -} - -S32 LLLayoutStack::getDefaultHeight(S32 cur_height) -{ - // if we are spanning our children (crude upward propagation of size) - // then don't enforce our size on our children - if (mOrientation == HORIZONTAL) - { - cur_height = llmax(mMinHeight, getRect().getHeight()); - } - - return cur_height; -} - -S32 LLLayoutStack::getDefaultWidth(S32 cur_width) -{ - // if we are spanning our children (crude upward propagation of size) - // then don't enforce our size on our children - if (mOrientation == VERTICAL) - { - cur_width = llmax(mMinWidth, getRect().getWidth()); - } - - return cur_width; -} - -void LLLayoutStack::addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate, S32 index) -{ - // panel starts off invisible (collapsed) - if (animate == ANIMATE) - { - panel->setVisible(FALSE); - } - LLEmbeddedPanel* embedded_panel = new LLEmbeddedPanel(panel, mOrientation, min_width, min_height, auto_resize, user_resize); - - mPanels.insert(mPanels.begin() + llclamp(index, 0, (S32)mPanels.size()), embedded_panel); - - addChild(panel); - addChild(embedded_panel->mResizeBar); - - // bring all resize bars to the front so that they are clickable even over the panels - // with a bit of overlap - for (e_panel_list_t::iterator panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLResizeBar* resize_barp = (*panel_it)->mResizeBar; - sendChildToFront(resize_barp); - } - - // start expanding panel animation - if (animate == ANIMATE) - { - panel->setVisible(TRUE); - } -} - -void LLLayoutStack::removePanel(LLPanel* panel) -{ - removeChild(panel); -} - -void LLLayoutStack::collapsePanel(LLPanel* panel, BOOL collapsed) -{ - LLEmbeddedPanel* panel_container = findEmbeddedPanel(panel); - if (!panel_container) return; - - panel_container->mCollapsed = collapsed; -} - -void LLLayoutStack::updateLayout(BOOL force_resize) -{ - calcMinExtents(); - - // calculate current extents - S32 total_width = 0; - S32 total_height = 0; - - const F32 ANIM_OPEN_TIME = 0.02f; - const F32 ANIM_CLOSE_TIME = 0.03f; - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - if (panelp->getVisible()) - { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_OPEN_TIME)); - if ((*panel_it)->mVisibleAmt > 0.99f) - { - (*panel_it)->mVisibleAmt = 1.f; - } - } - else // not visible - { - (*panel_it)->mVisibleAmt = lerp((*panel_it)->mVisibleAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - if ((*panel_it)->mVisibleAmt < 0.001f) - { - (*panel_it)->mVisibleAmt = 0.f; - } - } - - if ((*panel_it)->mCollapsed) - { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 1.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - } - else - { - (*panel_it)->mCollapseAmt = lerp((*panel_it)->mCollapseAmt, 0.f, LLCriticalDamp::getInterpolant(ANIM_CLOSE_TIME)); - } - - if (mOrientation == HORIZONTAL) - { - // enforce minimize size constraint by default - if (panelp->getRect().getWidth() < (*panel_it)->mMinWidth) - { - panelp->reshape((*panel_it)->mMinWidth, panelp->getRect().getHeight()); - } - total_width += llround(panelp->getRect().getWidth() * (*panel_it)->getCollapseFactor()); - // want n-1 panel gaps for n panels - if (panel_it != mPanels.begin()) - { - total_width += mPanelSpacing; - } - } - else //VERTICAL - { - // enforce minimize size constraint by default - if (panelp->getRect().getHeight() < (*panel_it)->mMinHeight) - { - panelp->reshape(panelp->getRect().getWidth(), (*panel_it)->mMinHeight); - } - total_height += llround(panelp->getRect().getHeight() * (*panel_it)->getCollapseFactor()); - if (panel_it != mPanels.begin()) - { - total_height += mPanelSpacing; - } - } - } - - S32 num_resizable_panels = 0; - S32 shrink_headroom_available = 0; - S32 shrink_headroom_total = 0; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - // panels that are not fully visible do not count towards shrink headroom - if ((*panel_it)->getCollapseFactor() < 1.f) - { - continue; - } - - // if currently resizing a panel or the panel is flagged as not automatically resizing - // only track total available headroom, but don't use it for automatic resize logic - if ((*panel_it)->mResizeBar->hasMouseCapture() - || (!(*panel_it)->mAutoResize - && !force_resize)) - { - if (mOrientation == HORIZONTAL) - { - shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - } - else //VERTICAL - { - shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - } - } - else - { - num_resizable_panels++; - if (mOrientation == HORIZONTAL) - { - shrink_headroom_available += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - shrink_headroom_total += (*panel_it)->mPanel->getRect().getWidth() - (*panel_it)->mMinWidth; - } - else //VERTICAL - { - shrink_headroom_available += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - shrink_headroom_total += (*panel_it)->mPanel->getRect().getHeight() - (*panel_it)->mMinHeight; - } - } - } - - // calculate how many pixels need to be distributed among layout panels - // positive means panels need to grow, negative means shrink - S32 pixels_to_distribute; - if (mOrientation == HORIZONTAL) - { - pixels_to_distribute = getRect().getWidth() - total_width; - } - else //VERTICAL - { - pixels_to_distribute = getRect().getHeight() - total_height; - } - - // now we distribute the pixels... - S32 cur_x = 0; - S32 cur_y = getRect().getHeight(); - - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - - S32 cur_width = panelp->getRect().getWidth(); - S32 cur_height = panelp->getRect().getHeight(); - S32 new_width = llmax((*panel_it)->mMinWidth, cur_width); - S32 new_height = llmax((*panel_it)->mMinHeight, cur_height); - - S32 delta_size = 0; - - // if panel can automatically resize (not animating, and resize flag set)... - if ((*panel_it)->getCollapseFactor() == 1.f - && (force_resize || (*panel_it)->mAutoResize) - && !(*panel_it)->mResizeBar->hasMouseCapture()) - { - if (mOrientation == HORIZONTAL) - { - // if we're shrinking - if (pixels_to_distribute < 0) - { - // shrink proportionally to amount over minimum - // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_width - (*panel_it)->mMinWidth) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_width - (*panel_it)->mMinWidth); - } - else - { - // grow all elements equally - delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); - num_resizable_panels--; - } - pixels_to_distribute -= delta_size; - new_width = llmax((*panel_it)->mMinWidth, cur_width + delta_size); - } - else - { - new_width = getDefaultWidth(new_width); - } - - if (mOrientation == VERTICAL) - { - if (pixels_to_distribute < 0) - { - // shrink proportionally to amount over minimum - // so we can do this in one pass - delta_size = (shrink_headroom_available > 0) ? llround((F32)pixels_to_distribute * ((F32)(cur_height - (*panel_it)->mMinHeight) / (F32)shrink_headroom_available)) : 0; - shrink_headroom_available -= (cur_height - (*panel_it)->mMinHeight); - } - else - { - delta_size = llround((F32)pixels_to_distribute / (F32)num_resizable_panels); - num_resizable_panels--; - } - pixels_to_distribute -= delta_size; - new_height = llmax((*panel_it)->mMinHeight, cur_height + delta_size); - } - else - { - new_height = getDefaultHeight(new_height); - } - } - else - { - if (mOrientation == HORIZONTAL) - { - new_height = getDefaultHeight(new_height); - } - else // VERTICAL - { - new_width = getDefaultWidth(new_width); - } - } - - // adjust running headroom count based on new sizes - shrink_headroom_total += delta_size; - - panelp->reshape(new_width, new_height); - panelp->setOrigin(cur_x, cur_y - new_height); - - LLRect panel_rect = panelp->getRect(); - LLRect resize_bar_rect = panel_rect; - if (mOrientation == HORIZONTAL) - { - resize_bar_rect.mLeft = panel_rect.mRight - RESIZE_BAR_OVERLAP; - resize_bar_rect.mRight = panel_rect.mRight + mPanelSpacing + RESIZE_BAR_OVERLAP; - } - else - { - resize_bar_rect.mTop = panel_rect.mBottom + RESIZE_BAR_OVERLAP; - resize_bar_rect.mBottom = panel_rect.mBottom - mPanelSpacing - RESIZE_BAR_OVERLAP; - } - (*panel_it)->mResizeBar->setRect(resize_bar_rect); - - if (mOrientation == HORIZONTAL) - { - cur_x += llround(new_width * (*panel_it)->getCollapseFactor()) + mPanelSpacing; - } - else //VERTICAL - { - cur_y -= llround(new_height * (*panel_it)->getCollapseFactor()) + mPanelSpacing; - } - } - - // update resize bars with new limits - LLResizeBar* last_resize_bar = NULL; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - LLPanel* panelp = (*panel_it)->mPanel; - - if (mOrientation == HORIZONTAL) - { - (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinWidth, - (*panel_it)->mMinWidth + shrink_headroom_total); - } - else //VERTICAL - { - (*panel_it)->mResizeBar->setResizeLimits( - (*panel_it)->mMinHeight, - (*panel_it)->mMinHeight + shrink_headroom_total); - } - - // toggle resize bars based on panel visibility, resizability, etc - BOOL resize_bar_enabled = panelp->getVisible() && (*panel_it)->mUserResize; - (*panel_it)->mResizeBar->setVisible(resize_bar_enabled); - - if (resize_bar_enabled) - { - last_resize_bar = (*panel_it)->mResizeBar; - } - } - - // hide last resize bar as there is nothing past it - // resize bars need to be in between two resizable panels - if (last_resize_bar) - { - last_resize_bar->setVisible(FALSE); - } - - // not enough room to fit existing contents - if (force_resize == FALSE - // layout did not complete by reaching target position - && ((mOrientation == VERTICAL && cur_y != -mPanelSpacing) - || (mOrientation == HORIZONTAL && cur_x != getRect().getWidth() + mPanelSpacing))) - { - // do another layout pass with all stacked elements contributing - // even those that don't usually resize - llassert_always(force_resize == FALSE); - updateLayout(TRUE); - } -} // end LLLayoutStack::updateLayout - - -LLLayoutStack::LLEmbeddedPanel* LLLayoutStack::findEmbeddedPanel(LLPanel* panelp) const -{ - e_panel_list_t::const_iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - if ((*panel_it)->mPanel == panelp) - { - return *panel_it; - } - } - return NULL; -} - -void LLLayoutStack::calcMinExtents() -{ - mMinWidth = 0; - mMinHeight = 0; - - e_panel_list_t::iterator panel_it; - for (panel_it = mPanels.begin(); panel_it != mPanels.end(); ++panel_it) - { - if (mOrientation == HORIZONTAL) - { - mMinHeight = llmax( mMinHeight, - (*panel_it)->mMinHeight); - mMinWidth += (*panel_it)->mMinWidth; - if (panel_it != mPanels.begin()) - { - mMinWidth += mPanelSpacing; - } - } - else //VERTICAL - { - mMinWidth = llmax( mMinWidth, - (*panel_it)->mMinWidth); - mMinHeight += (*panel_it)->mMinHeight; - if (panel_it != mPanels.begin()) - { - mMinHeight += mPanelSpacing; - } - } - } -} diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 5700d1606..d2ca151e5 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -140,7 +140,9 @@ public: const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; } - BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + CommitCallbackRegistry::ScopedRegistrar& getCommitCallbackRegistrar() { return mCommitCallbackRegistrar; } + EnableCallbackRegistry::ScopedRegistrar& getEnableCallbackRegistrar() { return mEnableCallbackRegistrar; } + virtual BOOL initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); void initChildrenXML(LLXMLNodePtr node, LLUICtrlFactory* factory); void setPanelParameters(LLXMLNodePtr node, LLView *parentp); @@ -230,6 +232,8 @@ protected: // Override to set not found list LLButton* getDefaultButton() { return mDefaultBtn; } LLCallbackMap::map_t mFactoryMap; + CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar; + EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar; commit_signal_t* mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD() private: @@ -258,56 +262,4 @@ private: }; // end class LLPanel - -class LLLayoutStack : public LLView -{ -public: - typedef enum e_layout_orientation - { - HORIZONTAL, - VERTICAL - } eLayoutOrientation; - - LLLayoutStack(eLayoutOrientation orientation); - virtual ~LLLayoutStack(); - - /*virtual*/ void draw(); - /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; - /*virtual*/ void removeChild(LLView* ctrl); - - static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); - - S32 getMinWidth() const { return mMinWidth; } - S32 getMinHeight() const { return mMinHeight; } - - typedef enum e_animate - { - NO_ANIMATE, - ANIMATE - } EAnimate; - - void addPanel(LLPanel* panel, S32 min_width, S32 min_height, BOOL auto_resize, BOOL user_resize, EAnimate animate = NO_ANIMATE, S32 index = S32_MAX); - void removePanel(LLPanel* panel); - void collapsePanel(LLPanel* panel, BOOL collapsed = TRUE); - S32 getNumPanels() { return mPanels.size(); } - -private: - struct LLEmbeddedPanel; - - void updateLayout(BOOL force_resize = FALSE); - void calcMinExtents(); - S32 getDefaultHeight(S32 cur_height); - S32 getDefaultWidth(S32 cur_width); - - const eLayoutOrientation mOrientation; - - typedef std::vector e_panel_list_t; - e_panel_list_t mPanels; - LLEmbeddedPanel* findEmbeddedPanel(LLPanel* panelp) const; - - S32 mMinWidth; - S32 mMinHeight; - S32 mPanelSpacing; -}; // end class LLLayoutStack - #endif diff --git a/indra/llui/llprogressbar.cpp b/indra/llui/llprogressbar.cpp index 9dbdb8a8e..0443c480a 100644 --- a/indra/llui/llprogressbar.cpp +++ b/indra/llui/llprogressbar.cpp @@ -73,19 +73,19 @@ LLProgressBar::~LLProgressBar() void LLProgressBar::draw() { static LLTimer timer; - - LLUIImagePtr shadow_imagep = LLUI::getUIImage("rounded_square_soft.tga"); + F32 alpha = getDrawContext().mAlpha; + + //LLUIImagePtr shadow_imagep = LLUI::getUIImage("rounded_square_soft.tga"); LLUIImagePtr bar_fg_imagep = LLUI::getUIImage("progressbar_fill.tga"); LLUIImagePtr bar_bg_imagep = LLUI::getUIImage("progressbar_track.tga"); - LLUIImagePtr bar_imagep = LLUI::getUIImage("rounded_square.tga"); + //LLUIImagePtr bar_imagep = LLUI::getUIImage("rounded_square.tga"); LLColor4 background_color = LLUI::sColorsGroup->getColor("LoginProgressBarBgColor"); - bar_bg_imagep->draw(getLocalRect(), - background_color); + bar_bg_imagep->draw(getLocalRect(), background_color % alpha); LLRect progress_rect = getLocalRect(); progress_rect.mRight = llround(getRect().getWidth() * (mPercentDone / 100.f)); - bar_fg_imagep->draw(progress_rect); + bar_fg_imagep->draw(progress_rect, LLColor4::white % alpha); } void LLProgressBar::setPercent(const F32 percent) diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index ce01f75bf..c592fccfb 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -352,9 +352,6 @@ LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const // static LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("radio_group"); - node->getAttributeString("name", name); - U32 initial_value = 0; node->getAttributeU32("initial_value", initial_value); @@ -364,7 +361,7 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory LLRect rect; createRect(node, rect, parent, LLRect()); - LLRadioGroup* radio_group = new LLRadioGroup(name, + LLRadioGroup* radio_group = new LLRadioGroup("radio_group", rect, initial_value, NULL, @@ -408,10 +405,8 @@ LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory LLRect item_rect; createRect(child, item_rect, radio_group, rect); - std::string radioname("radio"); - child->getAttributeString("name", radioname); std::string item_label = child->getTextContents(); - LLRadioCtrl* radio = radio_group->addRadioButton(radioname, item_label, item_rect, font); + LLRadioCtrl* radio = radio_group->addRadioButton("radio", item_label, item_rect, font); radio->initFromXML(child, radio_group); } diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index 8aa8b9fe3..c5d1ad6ed 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -152,13 +152,10 @@ LLXMLNodePtr LLScrollingPanelList::getXML(bool save_children) const // static LLView* LLScrollingPanelList::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("scrolling_panel_list"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); - LLScrollingPanelList* scrolling_panel_list = new LLScrollingPanelList(name, rect); + LLScrollingPanelList* scrolling_panel_list = new LLScrollingPanelList("scrolling_panel_list", rect); scrolling_panel_list->initFromXML(node, parent); return scrolling_panel_list; } diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 3f198bbaf..ee9b62a9a 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -3104,9 +3104,6 @@ void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node) // static LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("scroll_list"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -3132,7 +3129,7 @@ LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFac node->getAttributeBOOL("mouse_wheel_opaque", mouse_wheel_opaque); LLScrollListCtrl* scroll_list = new LLScrollListCtrl( - name, + "scroll_list", rect, NULL, multi_select, diff --git a/indra/llui/llsearcheditor.cpp b/indra/llui/llsearcheditor.cpp index a326803e4..6ccfed88a 100644 --- a/indra/llui/llsearcheditor.cpp +++ b/indra/llui/llsearcheditor.cpp @@ -147,9 +147,6 @@ void LLSearchEditor::handleKeystroke() // static LLView* LLSearchEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("search_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -158,7 +155,7 @@ LLView* LLSearchEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto std::string text = node->getValue().substr(0, max_text_length - 1); - LLSearchEditor* search_editor = new LLSearchEditor(name, + LLSearchEditor* search_editor = new LLSearchEditor("search_editor", rect, max_text_length); diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp index becc33424..893354252 100644 --- a/indra/llui/llslider.cpp +++ b/indra/llui/llslider.cpp @@ -250,6 +250,8 @@ BOOL LLSlider::handleKeyHere(KEY key, MASK mask) void LLSlider::draw() { + F32 alpha = getDrawContext().mAlpha; + // since thumb image might still be decoding, need thumb to accomodate image size updateThumbRect(); @@ -258,31 +260,47 @@ void LLSlider::draw() // drawing solids requires texturing be disabled gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - F32 opacity = getEnabled() ? 1.f : 0.3f; - LLColor4 center_color = (mThumbCenterColor % opacity); - // Track LLRect track_rect(mThumbImage->getWidth() / 2, getLocalRect().getCenterY() + (mTrackImage->getHeight() / 2), getRect().getWidth() - mThumbImage->getWidth() / 2, getLocalRect().getCenterY() - (mTrackImage->getHeight() / 2) ); LLRect highlight_rect(track_rect.mLeft, track_rect.mTop, mThumbRect.getCenterX(), track_rect.mBottom); - mTrackImage->draw(track_rect); - mTrackHighlightImage->draw(highlight_rect); + mTrackImage->draw(track_rect, LLColor4::white % alpha); + mTrackHighlightImage->draw(highlight_rect, LLColor4::white % alpha); // Thumb - if( hasMouseCapture() ) - { - // Show ghost where thumb was before dragging began. - mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor % 0.3f); - } if (hasFocus()) { // Draw focus highlighting. - mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor(), gFocusMgr.getFocusFlashWidth()); + mThumbImage->drawBorder(mThumbRect, gFocusMgr.getFocusColor() % alpha, gFocusMgr.getFocusFlashWidth()); + } + + if( hasMouseCapture() ) + { + // Show ghost where thumb was before dragging began. + if (mThumbImage.notNull()) + { + mThumbImage->draw(mDragStartThumbRect, mThumbCenterColor % (0.3f * alpha)); + mThumbImage->draw(mThumbRect, mThumbOutlineColor % alpha); + } + } + else if(!getEnabled()) + { + if (mThumbImage.notNull()) + { + mThumbImage->draw(mThumbRect, mThumbCenterColor % (0.3f * alpha)); + } + } + else + { + if (mThumbImage.notNull()) + { + mThumbImage->draw(mThumbRect, mThumbCenterColor % alpha); + } } // Fill in the thumb. - mThumbImage->draw(mThumbRect, hasMouseCapture() ? mThumbOutlineColor : center_color); + LLUICtrl::draw(); } @@ -325,9 +343,6 @@ boost::signals2::connection LLSlider::setMouseUpCallback( const commit_signal_t: //static LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory) { - std::string name("slider_bar"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -346,7 +361,7 @@ LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFacto BOOL volume = node->hasName("volume_slider") ? TRUE : FALSE; node->getAttributeBOOL("volume", volume); - LLSlider* slider = new LLSlider(name, + LLSlider* slider = new LLSlider("slider_bar", rect, NULL, initial_value, diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h index 8d16911f6..59bda673f 100644 --- a/indra/llui/llslider.h +++ b/indra/llui/llslider.h @@ -96,9 +96,9 @@ private: S32 mMouseOffset; LLRect mDragStartThumbRect; - LLUIImage* mThumbImage; - LLUIImage* mTrackImage; - LLUIImage* mTrackHighlightImage; + LLPointer mThumbImage; + LLPointer mTrackImage; + LLPointer mTrackHighlightImage; LLRect mThumbRect; LLColor4 mTrackColor; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index df1acf62a..594a2d890 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -401,9 +401,6 @@ LLXMLNodePtr LLSliderCtrl::getXML(bool save_children) const LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("slider"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -478,7 +475,7 @@ LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory label.assign(node->getTextContents()); } - LLSliderCtrl* slider = new LLSliderCtrl(name, + LLSliderCtrl* slider = new LLSliderCtrl("slider", rect, label, font, diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 44f6a607e..489f71282 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -515,9 +515,6 @@ LLXMLNodePtr LLSpinCtrl::getXML(bool save_children) const LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("spinner"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -552,7 +549,7 @@ LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory * label.assign( node->getValue() ); } - LLSpinCtrl* spinner = new LLSpinCtrl(name, + LLSpinCtrl* spinner = new LLSpinCtrl("spinner", rect, label, font, diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index 4248ecead..239ac5b74 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -197,12 +197,16 @@ void LLTextBox::setWrappedText(const LLStringExplicit& in_text, F32 max_width) { max_width = (F32)getRect().getWidth(); } + if(max_width <= 0.0f) + { + return; //It makes no sense to try to wrap text to fit zero-width columns. (In fact, it causes infinite recursion in the following while loop!) + } LLWString wtext = utf8str_to_wstring(in_text); LLWString final_wtext; LLWString::size_type cur = 0;; - LLWString::size_type len = wtext.size(); + LLWString::size_type len = wtext.length(); while (cur < len) { @@ -282,6 +286,7 @@ BOOL LLTextBox::setTextArg( const std::string& key, const LLStringExplicit& text void LLTextBox::draw() { + F32 alpha = getDrawContext().mAlpha; if (mBorderVisible) { gl_rect_2d_offset_local(getLocalRect(), 2, FALSE); @@ -292,12 +297,12 @@ void LLTextBox::draw() static LLColor4 color_drop_shadow = LLUI::sColorsGroup->getColor("ColorDropShadow"); static S32 drop_shadow_tooltip = LLUI::sConfigGroup->getS32("DropShadowTooltip"); gl_drop_shadow(0, getRect().getHeight(), getRect().getWidth(), 0, - color_drop_shadow, drop_shadow_tooltip); + color_drop_shadow % alpha, drop_shadow_tooltip); } if (mBackgroundVisible) { - gl_rect_2d( getLocalRect(), mBackgroundColor ); + gl_rect_2d( getLocalRect(), mBackgroundColor % alpha ); } S32 text_x = 0; @@ -320,16 +325,16 @@ void LLTextBox::draw() { if(mHasHover) { - drawText( text_x, text_y, mHoverColor ); + drawText( text_x, text_y, mHoverColor % alpha ); } else { - drawText( text_x, text_y, mTextColor ); + drawText( text_x, text_y, mTextColor % alpha ); } } else { - drawText( text_x, text_y, mDisabledColor ); + drawText( text_x, text_y, mDisabledColor % alpha ); } if (sDebugRects) @@ -382,6 +387,69 @@ void LLTextBox::reshapeToFitText() reshape( width + 2 * mHPad, height + 2 * mVPad ); } +// virtual +void LLTextBox::initFromXML(LLXMLNodePtr node, LLView* parent) +{ + LLUICtrl::initFromXML(node, parent); + + LLFontGL* font = LLView::selectFont(node); + if(font) + mFontGL = font; + + setText(node->getTextContents()); + + LLFontGL::HAlign halign = LLView::selectFontHAlign(node); + setHAlign(halign); + + node->getAttributeS32("line_spacing", mLineSpacing); + + std::string font_style; + if (node->getAttributeString("font-style", font_style)) + { + mFontStyle = LLFontGL::getStyleFromString(font_style); + } + + if (node->getAttributeString("font-shadow", font_style)) + { + if(font_style == "soft") + mFontShadow = LLFontGL::DROP_SHADOW_SOFT; + else if(font_style == "hard") + mFontShadow = LLFontGL::DROP_SHADOW; + else + mFontShadow = LLFontGL::NO_SHADOW; + } + + BOOL mouse_opaque = getMouseOpaque(); + if (node->getAttributeBOOL("mouse_opaque", mouse_opaque)) + { + setMouseOpaque(mouse_opaque); + } + + if(node->hasAttribute("text_color")) + { + LLColor4 color; + LLUICtrlFactory::getAttributeColor(node, "text_color", color); + setColor(color); + } + + if(node->hasAttribute("hover_color")) + { + LLColor4 color; + LLUICtrlFactory::getAttributeColor(node, "hover_color", color); + setHoverColor(color); + setHoverActive(true); + } + + BOOL hover_active = FALSE; + if(node->getAttributeBOOL("hover", hover_active)) + { + setHoverActive(hover_active); + } + + node->getAttributeBOOL("use_ellipses", mUseEllipses); + +} + // virtual LLXMLNodePtr LLTextBox::getXML(bool save_children) const { @@ -411,68 +479,8 @@ LLXMLNodePtr LLTextBox::getXML(bool save_children) const // static LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("text_box"); - node->getAttributeString("name", name); - LLFontGL* font = LLView::selectFont(node); - - std::string text = node->getTextContents(); - - LLTextBox* text_box = new LLTextBox(name, - LLRect(), - text, - font, - FALSE); - - - LLFontGL::HAlign halign = LLView::selectFontHAlign(node); - text_box->setHAlign(halign); - + LLTextBox* text_box = new LLTextBox("text_box", LLRect(), "" , NULL, FALSE); text_box->initFromXML(node, parent); - node->getAttributeS32("line_spacing", text_box->mLineSpacing); - - std::string font_style; - if (node->getAttributeString("font-style", font_style)) - { - text_box->mFontStyle = LLFontGL::getStyleFromString(font_style); - } - - if (node->getAttributeString("font-shadow", font_style)) - { - if(font_style == "soft") - text_box->mFontShadow = LLFontGL::DROP_SHADOW_SOFT; - else if(font_style == "hard") - text_box->mFontShadow = LLFontGL::DROP_SHADOW; - else - text_box->mFontShadow = LLFontGL::NO_SHADOW; - } - - BOOL mouse_opaque = text_box->getMouseOpaque(); - if (node->getAttributeBOOL("mouse_opaque", mouse_opaque)) - { - text_box->setMouseOpaque(mouse_opaque); - } - - if(node->hasAttribute("text_color")) - { - LLColor4 color; - LLUICtrlFactory::getAttributeColor(node, "text_color", color); - text_box->setColor(color); - } - - if(node->hasAttribute("hover_color")) - { - LLColor4 color; - LLUICtrlFactory::getAttributeColor(node, "hover_color", color); - text_box->setHoverColor(color); - text_box->setHoverActive(true); - } - - BOOL hover_active = FALSE; - if(node->getAttributeBOOL("hover", hover_active)) - { - text_box->setHoverActive(hover_active); - } - return text_box; } diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h index 17abe2742..738ec3b06 100644 --- a/indra/llui/lltextbox.h +++ b/indra/llui/lltextbox.h @@ -63,6 +63,7 @@ public: virtual ~LLTextBox() {} + virtual void initFromXML(LLXMLNodePtr node, LLView* parent); virtual LLXMLNodePtr getXML(bool save_children = true) const; static LLView* fromXML(LLXMLNodePtr node, LLView *parent, class LLUICtrlFactory *factory); diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index ed749aabb..e6049f843 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -42,6 +42,7 @@ #include "llui.h" #include "lluictrlfactory.h" #include "lluiimage.h" +#include "llurlaction.h" #include "llrect.h" #include "llfocusmgr.h" #include "lltimer.h" @@ -96,10 +97,6 @@ const S32 PREEDIT_STANDOUT_THICKNESS = 2; LLColor4 LLTextEditor::mLinkColor = LLColor4::blue; -void (* LLTextEditor::mURLcallback)(const std::string&) = NULL; -bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL; -bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&) = NULL; - /////////////////////////////////////////////////////////////////// @@ -4350,11 +4347,15 @@ void LLTextEditor::loadKeywords(const std::string& filename, std::string name = utf8str_trim(funcs[i]); mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] ); } + segment_list_t segment_list; + mKeywords.findSegments(&segment_list, getWText(), mDefaultColor); - mKeywords.findSegments( &mSegments, mWText, mDefaultColor ); - - llassert( mSegments.front()->getStart() == 0 ); - llassert( mSegments.back()->getEnd() == getLength() ); + mSegments.clear(); + segment_list_t::iterator insert_it = mSegments.begin(); + for (segment_list_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + { + insert_it = mSegments.insert(insert_it, *list_it); + } } } @@ -4493,11 +4494,7 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) // and launch it if we did. if (mParseHTML && mHTML.length() > 0) { - //Special handling for slurls - if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) ) - { - if (mURLcallback!=NULL) (*mURLcallback)(mHTML); - } + LLUrlAction::clickAction(mHTML); mHTML.clear(); } } @@ -4751,9 +4748,6 @@ LLXMLNodePtr LLTextEditor::getXML(bool save_children) const // static LLView* LLTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("text_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -4767,7 +4761,7 @@ LLView* LLTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory std::string text = node->getTextContents().substr(0, max_text_length - 1); - LLTextEditor* text_editor = new LLTextEditor(name, + LLTextEditor* text_editor = new LLTextEditor("text_editor", rect, max_text_length, text, diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 91be64e65..51300450d 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -258,10 +258,6 @@ public: // Callbacks static void setLinkColor(LLColor4 color) { mLinkColor = color; } - static void setURLCallbacks(void (*callback1) (const std::string& url), - bool (*callback2) (const std::string& url), - bool (*callback3) (const std::string& url) ) - { mURLcallback = callback1; mSecondlifeURLcallback = callback2; mSecondlifeURLcallbackRightClick = callback3;} void setOnScrollEndCallback(void (*callback)(void*), void* userdata); @@ -509,9 +505,6 @@ private: // LLKeywords mKeywords; static LLColor4 mLinkColor; - static void (*mURLcallback) (const std::string& url); - static bool (*mSecondlifeURLcallback) (const std::string& url); - static bool (*mSecondlifeURLcallbackRightClick) (const std::string& url); // Concrete LLTextCmd sub-classes used by the LLTextEditor base class class LLTextCmdInsert; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 40f43bf54..5a68c9efe 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -161,19 +161,15 @@ void LLUI::setMousePositionScreen(S32 x, S32 y) screen_x = llround((F32)x * getScaleFactor().mV[VX]); screen_y = llround((F32)y * getScaleFactor().mV[VY]); - LLCoordWindow window_point; - LLView::getWindow()->convertCoords(LLCoordGL(screen_x, screen_y), &window_point); - - LLView::getWindow()->setCursorPosition(window_point); + LLView::getWindow()->setCursorPosition(LLCoordGL(screen_x, screen_y).convert()); } //static void LLUI::getMousePositionScreen(S32 *x, S32 *y) { LLCoordWindow cursor_pos_window; - LLView::getWindow()->getCursorPosition(&cursor_pos_window); - LLCoordGL cursor_pos_gl; - LLView::getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl); + getWindow()->getCursorPosition(&cursor_pos_window); + LLCoordGL cursor_pos_gl(cursor_pos_window.convert()); *x = llround((F32)cursor_pos_gl.mX / getScaleFactor().mV[VX]); *y = llround((F32)cursor_pos_gl.mY / getScaleFactor().mV[VX]); } diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 44cb0d820..aede0f859 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -32,6 +32,7 @@ #include "llcontrol.h" #include "llcoord.h" #include "v2math.h" +#include "llregistry.h" #include "llrender2dutils.h" #include "llpointer.h" #include "lluiimage.h" @@ -102,6 +103,7 @@ public: static void glRectToScreen(const LLRect& gl, LLRect *screen); // Returns the control group containing the control name, or the default group static LLControlGroup& getControlControlGroup (const std::string& controlname); + static LLWindow* getWindow() { return sWindow; } static void setHtmlHelp(LLHtmlHelp* html_help); // diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 9f7ccc12a..7dbca795d 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -45,6 +45,8 @@ LLUICtrl::LLUICtrl() : mViewModel(LLViewModelPtr(new LLViewModel)), mCommitSignal(NULL), mValidateSignal(NULL), + mMouseEnterSignal(NULL), + mMouseLeaveSignal(NULL), mTentative(FALSE), mTabStop(TRUE), mIsChrome(FALSE) @@ -60,6 +62,8 @@ LLUICtrl::LLUICtrl(const std::string& name, const LLRect rect, BOOL mouse_opaque mCommitSignal(NULL), mValidateSignal(NULL), mViewModel(LLViewModelPtr(new LLViewModel)), + mMouseEnterSignal(NULL), + mMouseLeaveSignal(NULL), mTentative( FALSE ), mTabStop( TRUE ), mIsChrome(FALSE) @@ -82,6 +86,25 @@ LLUICtrl::~LLUICtrl() delete mValidateSignal; } + +// virtual +void LLUICtrl::onMouseEnter(S32 x, S32 y, MASK mask) +{ + if (mMouseEnterSignal) + { + (*mMouseEnterSignal)(this, getValue()); + } +} + +// virtual +void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if(mMouseLeaveSignal) + { + (*mMouseLeaveSignal)(this, getValue()); + } +} + void LLUICtrl::onCommit() { if (mCommitSignal) @@ -452,11 +475,62 @@ BOOL LLUICtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect void LLUICtrl::initFromXML(LLXMLNodePtr node, LLView* parent) { + std::string name; + if(node->getAttributeString("name", name)) + setName(name); + BOOL has_tab_stop = hasTabStop(); node->getAttributeBOOL("tab_stop", has_tab_stop); setTabStop(has_tab_stop); + std::string str = node->getName()->mString; + std::string attrib_str; + LLXMLNodePtr child_node; + + //Since so many other callback 'types' piggyback off of the commitcallback registrar as well as use the same callback signature + //we can assemble a nice little static list to iterate down instead of copy-pasting mostly similar code for each instance. + //Validate/enable callbacks differ, as it uses its own registry/callback signature. This could be worked around with a template, but keeping + //all the code local to this scope is more beneficial. + typedef boost::signals2::connection (LLUICtrl::*commit_fn)( const commit_signal_t::slot_type& cb ); + static std::pair sCallbackRegistryMap[3] = + { + std::pair(".commit_callback",&LLUICtrl::setCommitCallback), + std::pair(".mouseenter_callback",&LLUICtrl::setMouseEnterCallback), + std::pair(".mouseleave_callback",&LLUICtrl::setMouseLeaveCallback) + }; + for(S32 i= 0; i < sizeof(sCallbackRegistryMap)/sizeof(sCallbackRegistryMap[0]);++i) + { + if(node->getChild((str+sCallbackRegistryMap[i].first).c_str(),child_node,false)) + { + if(child_node->getAttributeString("function",attrib_str)) + { + commit_callback_t* func = (CommitCallbackRegistry::getValue(attrib_str)); + if (func) + { + if(child_node->getAttributeString("parameter",attrib_str)) + (this->*sCallbackRegistryMap[i].second)(boost::bind((*func), this, LLSD(attrib_str))); + else + (this->*sCallbackRegistryMap[i].second)(commit_signal_t::slot_type(*func)); + } + } + } + } + + if(node->getChild((str+".validate_callback").c_str(),child_node,false)) + { + if(child_node->getAttributeString("function",attrib_str)) + { + enable_callback_t* func = (EnableCallbackRegistry::getValue(attrib_str)); + if (func) + { + if(child_node->getAttributeString("parameter",attrib_str)) + setValidateCallback(boost::bind((*func), this, LLSD(attrib_str))); + else + setValidateCallback(enable_signal_t::slot_type(*func)); + } + } + } LLView::initFromXML(node, parent); } @@ -546,3 +620,15 @@ boost::signals2::connection LLUICtrl::setValidateCallback( const enable_signal_t if (!mValidateSignal) mValidateSignal = new enable_signal_t(); return mValidateSignal->connect(cb); } + +boost::signals2::connection LLUICtrl::setMouseEnterCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseEnterSignal) mMouseEnterSignal = new commit_signal_t(); + return mMouseEnterSignal->connect(cb); +} + +boost::signals2::connection LLUICtrl::setMouseLeaveCallback( const commit_signal_t::slot_type& cb ) +{ + if (!mMouseLeaveSignal) mMouseLeaveSignal = new commit_signal_t(); + return mMouseLeaveSignal->connect(cb); +} diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 954f1a688..79d8fd8fc 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -40,6 +40,7 @@ #include #include +#include "llinitparam.h" #include "llviewmodel.h" // *TODO move dependency to .cpp file class LLUICtrl @@ -48,6 +49,7 @@ class LLUICtrl public: typedef boost::function commit_callback_t; typedef boost::signals2::signal commit_signal_t; + typedef boost::function enable_callback_t; typedef boost::signals2::signal enable_signal_t; LLUICtrl(); @@ -65,6 +67,8 @@ public: /*virtual*/ LLXMLNodePtr getXML(bool save_children = true) const; /*virtual*/ BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); /*virtual*/ BOOL isCtrl() const; + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); // From LLFocusableElement /*virtual*/ void setFocus( BOOL b ); @@ -122,7 +126,9 @@ public: //Start using these! boost::signals2::connection setCommitCallback( const commit_signal_t::slot_type& cb ); boost::signals2::connection setValidateCallback( const enable_signal_t::slot_type& cb ); - + + boost::signals2::connection setMouseEnterCallback( const commit_signal_t::slot_type& cb ); + boost::signals2::connection setMouseLeaveCallback( const commit_signal_t::slot_type& cb ); // *TODO: Deprecate; for backwards compatability only: boost::signals2::connection setCommitCallback( boost::function cb, void* data); boost::signals2::connection setValidateBeforeCommit( boost::function cb ); @@ -139,10 +145,20 @@ public: } }; + template class CallbackRegistry : public LLRegistrySingleton + {}; + + class CommitCallbackRegistry : public CallbackRegistry{}; + // the enable callback registry is also used for visiblity callbacks + class EnableCallbackRegistry : public CallbackRegistry{}; + protected: commit_signal_t* mCommitSignal; enable_signal_t* mValidateSignal; + + commit_signal_t* mMouseEnterSignal; + commit_signal_t* mMouseLeaveSignal; LLViewModelPtr mViewModel; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index d1d3ffd46..d5bb963d4 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -102,11 +102,8 @@ public: static LLView *fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("pad"); - node->getAttributeString("name", name); - LLUICtrlLocate *new_ctrl = new LLUICtrlLocate(); - new_ctrl->setName(name); + new_ctrl->setName("pad"); new_ctrl->initFromXML(node, parent); return new_ctrl; } @@ -285,8 +282,12 @@ void LLUICtrlFactory::buildFloaterInternal(LLFloater *floaterp, LLXMLNodePtr &ro mFactoryStack.push_front(factory_map); } + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + floaterp->getCommitCallbackRegistrar().pushScope(); + floaterp->getEnableCallbackRegistrar().pushScope(); floaterp->initFloaterXML(root, NULL, this, open); /* Flawfinder: ignore */ - + floaterp->getCommitCallbackRegistrar().popScope(); + floaterp->getEnableCallbackRegistrar().popScope(); if (LLUI::sShowXUINames) { floaterp->setToolTip(filename); @@ -381,8 +382,13 @@ BOOL LLUICtrlFactory::buildPanelInternal(LLPanel* panelp, LLXMLNodePtr &root, co mFactoryStack.push_front(factory_map); } + // for local registry callbacks; define in constructor, referenced in XUI or postBuild + panelp->getCommitCallbackRegistrar().pushScope(); + panelp->getEnableCallbackRegistrar().pushScope(); didPost = panelp->initPanelXML(root, NULL, this); - + panelp->getCommitCallbackRegistrar().popScope(); + panelp->getEnableCallbackRegistrar().popScope(); + if (LLUI::sShowXUINames) { panelp->setToolTip(filename); diff --git a/indra/llui/llurlaction.cpp b/indra/llui/llurlaction.cpp new file mode 100644 index 000000000..fd9b3d9a6 --- /dev/null +++ b/indra/llui/llurlaction.cpp @@ -0,0 +1,159 @@ +/** + * @file llurlaction.cpp + * @author Martin Reddy + * @brief A set of actions that can performed on Urls + * + * $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 "linden_common.h" + +#include "llurlaction.h" +#include "llview.h" +#include "llwindow.h" +#include "llurlregistry.h" + +// global state for the callback functions +LLUrlAction::url_callback_t LLUrlAction::sOpenURLCallback; +LLUrlAction::url_callback_t LLUrlAction::sOpenURLInternalCallback; +LLUrlAction::url_callback_t LLUrlAction::sOpenURLExternalCallback; +LLUrlAction::execute_url_callback_t LLUrlAction::sExecuteSLURLCallback; + + +void LLUrlAction::setOpenURLCallback(url_callback_t cb) +{ + sOpenURLCallback = cb; +} + +void LLUrlAction::setOpenURLInternalCallback(url_callback_t cb) +{ + sOpenURLInternalCallback = cb; +} + +void LLUrlAction::setOpenURLExternalCallback(url_callback_t cb) +{ + sOpenURLExternalCallback = cb; +} + +void LLUrlAction::setExecuteSLURLCallback(execute_url_callback_t cb) +{ + sExecuteSLURLCallback = cb; +} + +void LLUrlAction::openURL(std::string url) +{ + if (sOpenURLCallback) + { + sOpenURLCallback(url); + } +} + +void LLUrlAction::openURLInternal(std::string url) +{ + if (sOpenURLInternalCallback) + { + sOpenURLInternalCallback(url); + } +} + +void LLUrlAction::openURLExternal(std::string url) +{ + if (sOpenURLExternalCallback) + { + sOpenURLExternalCallback(url); + } +} + +void LLUrlAction::executeSLURL(std::string url) +{ + if (sExecuteSLURLCallback) + { + sExecuteSLURLCallback(url); + } +} + +void LLUrlAction::clickAction(std::string url) +{ + // Try to handle as SLURL first, then http Url + if ( (sExecuteSLURLCallback) && !sExecuteSLURLCallback(url) ) + { + if (sOpenURLCallback) + { + sOpenURLCallback(url); + } + } +} + +void LLUrlAction::teleportToLocation(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + if (! match.getLocation().empty()) + { + executeSLURL("secondlife:///app/teleport/" + match.getLocation()); + } + } +} + +void LLUrlAction::showLocationOnMap(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + if (! match.getLocation().empty()) + { + executeSLURL("secondlife:///app/worldmap/" + match.getLocation()); + } + } +} + +void LLUrlAction::copyURLToClipboard(std::string url) +{ + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(url)); +} + +void LLUrlAction::copyLabelToClipboard(std::string url) +{ + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(match.getLabel())); + } +} + +void LLUrlAction::showProfile(std::string url) +{ + // Get id from 'secondlife:///app/{cmd}/{id}/{action}' + // and show its profile + LLURI uri(url); + LLSD path_array = uri.pathArray(); + if (path_array.size() == 4) + { + std::string id_str = path_array.get(2).asString(); + if (LLUUID::validate(id_str)) + { + std::string cmd_str = path_array.get(1).asString(); + executeSLURL("secondlife:///app/" + cmd_str + "/" + id_str + "/about"); + } + } +} diff --git a/indra/llui/llurlaction.h b/indra/llui/llurlaction.h new file mode 100644 index 000000000..c34960b82 --- /dev/null +++ b/indra/llui/llurlaction.h @@ -0,0 +1,98 @@ +/** + * @file llurlaction.h + * @author Martin Reddy + * @brief A set of actions that can performed on Urls + * + * $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$ + */ + +#ifndef LL_LLURLACTION_H +#define LL_LLURLACTION_H + +#include +#include + +/// +/// The LLUrlAction class provides a number of static functions that +/// let you open Urls in web browsers, execute SLURLs, and copy Urls +/// to the clipboard. Many of these functions are not available at +/// the llui level, and must be supplied via a set of callbacks. +/// +/// N.B. The action functions specifically do not use const ref +/// strings so that a url parameter can be used into a boost::bind() +/// call under situations when that input string is deallocated before +/// the callback is executed. +/// +class LLUrlAction +{ +public: + LLUrlAction(); + + /// load a Url in the user's preferred web browser + static void openURL(std::string url); + + /// load a Url in the internal Second Life web browser + static void openURLInternal(std::string url); + + /// load a Url in the operating system's default web browser + static void openURLExternal(std::string url); + + /// execute the given secondlife: SLURL + static void executeSLURL(std::string url); + + /// if the Url specifies an SL location, teleport there + static void teleportToLocation(std::string url); + + /// if the Url specifies an SL location, show it on a map + static void showLocationOnMap(std::string url); + + /// perform the appropriate action for left-clicking on a Url + static void clickAction(std::string url); + + /// copy the label for a Url to the clipboard + static void copyLabelToClipboard(std::string url); + + /// copy a Url to the clipboard + static void copyURLToClipboard(std::string url); + + /// if the Url specifies an SL command in the form like 'app/{cmd}/{id}/*', show its profile + static void showProfile(std::string url); + + /// specify the callbacks to enable this class's functionality + typedef boost::function url_callback_t; + typedef boost::function execute_url_callback_t; + + static void setOpenURLCallback(url_callback_t cb); + static void setOpenURLInternalCallback(url_callback_t cb); + static void setOpenURLExternalCallback(url_callback_t cb); + static void setExecuteSLURLCallback(execute_url_callback_t cb); + +private: + // callbacks for operations we can perform on Urls + static url_callback_t sOpenURLCallback; + static url_callback_t sOpenURLInternalCallback; + static url_callback_t sOpenURLExternalCallback; + + static execute_url_callback_t sExecuteSLURLCallback; +}; + +#endif diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp new file mode 100644 index 000000000..d4c83d674 --- /dev/null +++ b/indra/llui/llurlentry.cpp @@ -0,0 +1,1188 @@ +/** + * @file llurlentry.cpp + * @author Martin Reddy + * @brief Describes the Url types that can be registered in LLUrlRegistry + * + * $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 "linden_common.h" +#include "llurlentry.h" +#include "lluictrl.h" +#include "lluri.h" +#include "llurlmatch.h" +#include "llurlregistry.h" + +#include "llavatarnamecache.h" +#include "llcachename.h" +#include "lltrans.h" +//#include "lluicolortable.h" +#include "message.h" + +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + +// Utility functions +std::string localize_slapp_label(const std::string& url, const std::string& full_name); + + +LLUrlEntryBase::LLUrlEntryBase() +{} + +LLUrlEntryBase::~LLUrlEntryBase() +{ +} + +std::string LLUrlEntryBase::getUrl(const std::string &string) const +{ + return escapeUrl(string); +} + +//virtual +std::string LLUrlEntryBase::getIcon(const std::string &url) +{ + return mIcon; +} + +/*LLStyle::Params LLUrlEntryBase::getStyle() const +{ + LLStyle::Params style_params; + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.font.style = "UNDERLINE"; + return style_params; +}*/ + + +std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const +{ + // return the id from a SLURL in the format /app/{cmd}/{id}/about + LLURI uri(url); + LLSD path_array = uri.pathArray(); + if (path_array.size() == 4) + { + return path_array.get(2).asString(); + } + return ""; +} + +std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const +{ + return LLURI::unescape(url); +} + +std::string LLUrlEntryBase::escapeUrl(const std::string &url) const +{ + static std::string no_escape_chars; + static bool initialized = false; + if (!initialized) + { + no_escape_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~!$?&()*+,@:;=/%#"; + + std::sort(no_escape_chars.begin(), no_escape_chars.end()); + initialized = true; + } + return LLURI::escape(url, no_escape_chars, true); +} + +std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const +{ + // return the label part from [http://www.example.org Label] + const char *text = url.c_str(); + S32 start = 0; + while (! isspace(text[start])) + { + start++; + } + while (text[start] == ' ' || text[start] == '\t') + { + start++; + } + return unescapeUrl(url.substr(start, url.size()-start-1)); +} + +std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const +{ + // return the url part from [http://www.example.org Label] + const char *text = string.c_str(); + S32 end = 0; + while (! isspace(text[end])) + { + end++; + } + return escapeUrl(string.substr(1, end-1)); +} + +void LLUrlEntryBase::addObserver(const std::string &id, + const std::string &url, + const LLUrlLabelCallback &cb) +{ + // add a callback to be notified when we have a label for the uuid + LLUrlEntryObserver observer; + observer.url = url; + observer.signal = new LLUrlLabelSignal(); + if (observer.signal) + { + observer.signal->connect(cb); + mObservers.insert(std::pair(id, observer)); + } +} + +// *NOTE: See also LLUrlEntryAgent::callObservers() +void LLUrlEntryBase::callObservers(const std::string &id, + const std::string &label, + const std::string &icon) +{ + // notify all callbacks waiting on the given uuid + typedef std::multimap::iterator observer_it; + std::pair matching_range = mObservers.equal_range(id); + for (observer_it it = matching_range.first; it != matching_range.second;) + { + // call the callback - give it the new label + LLUrlEntryObserver &observer = it->second; + (*observer.signal)(it->second.url, label, icon); + // then remove the signal - we only need to call it once + delete observer.signal; + mObservers.erase(it++); + } +} + +/// is this a match for a URL that should not be hyperlinked? +bool LLUrlEntryBase::isLinkDisabled() const +{ + // this allows us to have a global setting to turn off text hyperlink highlighting/action + bool globally_disabled = LLUI::sConfigGroup->getBOOL("DisableTextHyperlinkActions"); + + return globally_disabled; +} + +static std::string getStringAfterToken(const std::string str, const std::string token) +{ + size_t pos = str.find(token); + if (pos == std::string::npos) + { + return ""; + } + + pos += token.size(); + return str.substr(pos, str.size() - pos); +} + +// +// LLUrlEntryHTTP Describes generic http: and https: Urls +// +LLUrlEntryHTTP::LLUrlEntryHTTP() +{ + mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +// +// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label +// We use the wikipedia syntax of [http://www.example.org Text] +// +LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel() +{ + mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getLabelFromWikiLink(url); + return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url); +} + +std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const +{ + return getUrl(string); +} + +std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const +{ + return getUrlFromWikiLink(string); +} + +// +// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com +// +LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol() +{ + mPattern = boost::regex("(" + "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR + "|" // or + "(?]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net + ")", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_http.xml"; + mTooltip = LLTrans::getString("TooltipHttpUrl"); +} + +std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const +{ + if (string.find("://") == std::string::npos) + { + return "http://" + escapeUrl(string); + } + return escapeUrl(string); +} + +// +// LLUrlEntrySLURL Describes generic http: and https: Urls +// +LLUrlEntrySLURL::LLUrlEntrySLURL() +{ + // see http://slurl.com/about.php for details on the SLURL format + mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - http://slurl.com/secondlife/Place/X/Y/Z + // - http://slurl.com/secondlife/Place/X/Y + // - http://slurl.com/secondlife/Place/X + // - http://slurl.com/secondlife/Place + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts == 5) + { + // handle slurl with (X,Y,Z) coordinates + std::string location = unescapeUrl(path_array[path_parts-4]); + std::string x = path_array[path_parts-3]; + std::string y = path_array[path_parts-2]; + std::string z = path_array[path_parts-1]; + return location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 4) + { + // handle slurl with (X,Y) coordinates + std::string location = unescapeUrl(path_array[path_parts-3]); + std::string x = path_array[path_parts-2]; + std::string y = path_array[path_parts-1]; + return location + " (" + x + "," + y + ")"; + } + else if (path_parts == 3) + { + // handle slurl with (X) coordinate + std::string location = unescapeUrl(path_array[path_parts-2]); + std::string x = path_array[path_parts-1]; + return location + " (" + x + ")"; + } + else if (path_parts == 2) + { + // handle slurl with no coordinates + std::string location = unescapeUrl(path_array[path_parts-1]); + return location; + } + + return url; +} + +std::string LLUrlEntrySLURL::getLocation(const std::string &url) const +{ + // return the part of the Url after slurl.com/secondlife/ + const std::string search_string = "/secondlife"; + size_t pos = url.find(search_string); + if (pos == std::string::npos) + { + return ""; + } + + pos += search_string.size() + 1; + return url.substr(pos, url.size() - pos); +} + +// +// LLUrlEntryAgent Describes a Second Life agent Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// +LLUrlEntryAgent::LLUrlEntryAgent() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_agent.xml"; + mIcon = "Generic_Person"; +} + +// virtual +void LLUrlEntryAgent::callObservers(const std::string &id, + const std::string &label, + const std::string &icon) +{ + // notify all callbacks waiting on the given uuid + typedef std::multimap::iterator observer_it; + std::pair matching_range = mObservers.equal_range(id); + for (observer_it it = matching_range.first; it != matching_range.second;) + { + // call the callback - give it the new label + LLUrlEntryObserver &observer = it->second; + std::string final_label = localize_slapp_label(observer.url, label); + (*observer.signal)(observer.url, final_label, icon); + // then remove the signal - we only need to call it once + delete observer.signal; + mObservers.erase(it++); + } +} + +void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id, + const LLAvatarName& av_name) +{ + std::string label = av_name.getCompleteName(); + + // received the agent name from the server - tell our observers + callObservers(id.asString(), label, mIcon); +} + +LLUUID LLUrlEntryAgent::getID(const std::string &string) const +{ + return LLUUID(getIDStringFromUrl(string)); +} + +std::string LLUrlEntryAgent::getTooltip(const std::string &string) const +{ + // return a tooltip corresponding to the URL type instead of the generic one + std::string url = getUrl(string); + + if (LLStringUtil::endsWith(url, "/inspect")) + { + return LLTrans::getString("TooltipAgentInspect"); + } + if (LLStringUtil::endsWith(url, "/mute")) + { + return LLTrans::getString("TooltipAgentMute"); + } + if (LLStringUtil::endsWith(url, "/unmute")) + { + return LLTrans::getString("TooltipAgentUnmute"); + } + if (LLStringUtil::endsWith(url, "/im")) + { + return LLTrans::getString("TooltipAgentIM"); + } + if (LLStringUtil::endsWith(url, "/pay")) + { + return LLTrans::getString("TooltipAgentPay"); + } + if (LLStringUtil::endsWith(url, "/offerteleport")) + { + return LLTrans::getString("TooltipAgentOfferTeleport"); + } + if (LLStringUtil::endsWith(url, "/requestfriend")) + { + return LLTrans::getString("TooltipAgentRequestFriend"); + } + return LLTrans::getString("TooltipAgentUrl"); +} + +bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const +{ + std::string url = getUrl(string); + return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect"); +} + +std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string agent_id_string = getIDStringFromUrl(url); + if (agent_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID agent_id(agent_id_string); + if (agent_id.isNull()) + { + return LLTrans::getString("AvatarNameNobody"); + } + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + std::string label = av_name.getCompleteName(); + + // handle suffixes like /mute or /offerteleport + label = localize_slapp_label(url, label); + return label; + } + else + { + LLAvatarNameCache::get(agent_id, + boost::bind(&LLUrlEntryAgent::onAvatarNameCache, + this, _1, _2)); + addObserver(agent_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +/*LLStyle::Params LLUrlEntryAgent::getStyle() const +{ + LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; +}*/ + +std::string localize_slapp_label(const std::string& url, const std::string& full_name) +{ + // customize label string based on agent SLapp suffix + if (LLStringUtil::endsWith(url, "/mute")) + { + return LLTrans::getString("SLappAgentMute") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/unmute")) + { + return LLTrans::getString("SLappAgentUnmute") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/im")) + { + return LLTrans::getString("SLappAgentIM") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/pay")) + { + return LLTrans::getString("SLappAgentPay") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/offerteleport")) + { + return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name; + } + if (LLStringUtil::endsWith(url, "/requestfriend")) + { + return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name; + } + return full_name; +} + + +std::string LLUrlEntryAgent::getIcon(const std::string &url) +{ + // *NOTE: Could look up a badge here by calling getIDStringFromUrl() + // and looking up the badge for the agent. + return mIcon; +} + +// +// LLUrlEntryAgentName describes a Second Life agent name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +// +LLUrlEntryAgentName::LLUrlEntryAgentName() +{} + +void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id, + const LLAvatarName& av_name) +{ + std::string label = getName(av_name); + // received the agent name from the server - tell our observers + callObservers(id.asString(), label, mIcon); +} + +std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at the login screen, use short string for layout + return LLTrans::getString("LoadingData"); + } + + std::string agent_id_string = getIDStringFromUrl(url); + if (agent_id_string.empty()) + { + // something went wrong, just give raw url + return unescapeUrl(url); + } + + LLUUID agent_id(agent_id_string); + if (agent_id.isNull()) + { + return LLTrans::getString("AvatarNameNobody"); + } + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + return getName(av_name); + } + else + { + LLAvatarNameCache::get(agent_id, + boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache, + this, _1, _2)); + addObserver(agent_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +/*LLStyle::Params LLUrlEntryAgentName::getStyle() const +{ + // don't override default colors + return LLStyle::Params().is_link(false); +}*/ + +// +// LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +// +LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.getCompleteName(); +} + +// +// LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +// +LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.mDisplayName; +} + +// +// LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g., +// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +// +LLUrlEntryAgentUserName::LLUrlEntryAgentUserName() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name) +{ + return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername; +} + +// +// LLUrlEntryGroup Describes a Second Life group Url, e.g., +// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about +// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// +LLUrlEntryGroup::LLUrlEntryGroup() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_group.xml"; + mIcon = "Generic_Group"; + mTooltip = LLTrans::getString("TooltipGroupUrl"); +} + + + +void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id, + const std::string& name, + bool is_group) +{ + // received the group name from the server - tell our observers + callObservers(id.asString(), name, mIcon); +} + +LLUUID LLUrlEntryGroup::getID(const std::string &string) const +{ + return LLUUID(getIDStringFromUrl(string)); +} + + +std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + if (!gCacheName) + { + // probably at login screen, give something short for layout + return LLTrans::getString("LoadingData"); + } + + std::string group_id_string = getIDStringFromUrl(url); + if (group_id_string.empty()) + { + // something went wrong, give raw url + return unescapeUrl(url); + } + + LLUUID group_id(group_id_string); + std::string group_name; + if (group_id.isNull()) + { + return LLTrans::getString("GroupNameNone"); + } + else if (gCacheName->getGroupName(group_id, group_name)) + { + return group_name; + } + else + { + gCacheName->getGroup(group_id, + boost::bind(&LLUrlEntryGroup::onGroupNameReceived, + this, _1, _2, _3)); + addObserver(group_id_string, url, cb); + return LLTrans::getString("LoadingData"); + } +} + +/*LLStyle::Params LLUrlEntryGroup::getStyle() const +{ + LLStyle::Params style_params = LLUrlEntryBase::getStyle(); + style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + return style_params; +}*/ + + +// +// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., +// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select +// +LLUrlEntryInventory::LLUrlEntryInventory() +{ + //*TODO: add supporting of inventory item names with whitespaces + //this pattern cann't parse for example + //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_inventory.xml"; +} + +std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + std::string label = getStringAfterToken(url, "name="); + return LLURI::unescape(label.empty() ? url : label); +} + +// +// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., +// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 +// +LLUrlEntryObjectIM::LLUrlEntryObjectIM() +{ + mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_objectim.xml"; +} + +std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + if (query_map.has("name")) + return query_map["name"]; + return unescapeUrl(url); +} + +std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const +{ + LLURI uri(url); + LLSD query_map = uri.queryMap(); + if (query_map.has("slurl")) + return query_map["slurl"]; + return LLUrlEntryBase::getLocation(url); +} + +// LLUrlEntryParcel statics. +LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null); +LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null); +LLHost LLUrlEntryParcel::sRegionHost(LLHost::invalid); +bool LLUrlEntryParcel::sDisconnected(false); +std::set LLUrlEntryParcel::sParcelInfoObservers; + +/// +/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., +/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// +LLUrlEntryParcel::LLUrlEntryParcel() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_parcel.xml"; + mTooltip = LLTrans::getString("TooltipParcelUrl"); + + sParcelInfoObservers.insert(this); +} + +LLUrlEntryParcel::~LLUrlEntryParcel() +{ + sParcelInfoObservers.erase(this); +} + +std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no parcel id + { + llwarns << "Failed to parse url [" << url << "]" << llendl; + return url; + } + + std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id + + // Add an observer to call LLUrlLabelCallback when we have parcel name. + addObserver(parcel_id_string, url, cb); + + LLUUID parcel_id(parcel_id_string); + + sendParcelInfoRequest(parcel_id); + + return unescapeUrl(url); +} + +void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id) +{ + if (sRegionHost == LLHost::invalid || sDisconnected) return; + + LLMessageSystem *msg = gMessageSystem; + msg->newMessage("ParcelInfoRequest"); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, sAgentID ); + msg->addUUID("SessionID", sSessionID); + msg->nextBlock("Data"); + msg->addUUID("ParcelID", parcel_id); + msg->sendReliable(sRegionHost); +} + +void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label) +{ + callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon); +} + +// static +void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data) +{ + std::string label(LLStringUtil::null); + if (!parcel_data.name.empty()) + { + label = parcel_data.name; + } + // If parcel name is empty use Sim_name (x, y, z) for parcel label. + else if (!parcel_data.sim_name.empty()) + { + S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS; + S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS; + S32 region_z = llround(parcel_data.global_z); + + label = llformat("%s (%d, %d, %d)", + parcel_data.sim_name.c_str(), region_x, region_y, region_z); + } + + for (std::set::iterator iter = sParcelInfoObservers.begin(); + iter != sParcelInfoObservers.end(); + ++iter) + { + LLUrlEntryParcel* url_entry = *iter; + if (url_entry) + { + url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label); + } + } +} + +// +// LLUrlEntryPlace Describes secondlife:// URLs +// +LLUrlEntryPlace::LLUrlEntryPlace() +{ + mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife://Place/X/Y/Z + // - secondlife://Place/X/Y + // + LLURI uri(url); + std::string location = unescapeUrl(uri.hostName()); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts == 3) + { + // handle slurl with (X,Y,Z) coordinates + std::string x = path_array[0]; + std::string y = path_array[1]; + std::string z = path_array[2]; + return location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 2) + { + // handle slurl with (X,Y) coordinates + std::string x = path_array[0]; + std::string y = path_array[1]; + return location + " (" + x + "," + y + ")"; + } + + return url; +} + +std::string LLUrlEntryPlace::getLocation(const std::string &url) const +{ + // return the part of the Url after secondlife:// part + return ::getStringAfterToken(url, "://"); +} + +// +// LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g. +// secondlife:///app/region/Ahern/128/128/0 +// +LLUrlEntryRegion::LLUrlEntryRegion() +{ + mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slurl.xml"; + mTooltip = LLTrans::getString("TooltipSLURL"); +} + +std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/region/Place/X/Y/Z + // - secondlife:///app/region/Place/X/Y + // - secondlife:///app/region/Place/X + // - secondlife:///app/region/Place + // + + LLSD path_array = LLURI(url).pathArray(); + S32 path_parts = path_array.size(); + + if (path_parts < 3) // no region name + { + llwarns << "Failed to parse url [" << url << "]" << llendl; + return url; + } + + std::string label = unescapeUrl(path_array[2]); // region name + + if (path_parts > 3) // secondlife:///app/region/Place/X + { + std::string x = path_array[3]; + label += " (" + x; + + if (path_parts > 4) // secondlife:///app/region/Place/X/Y + { + std::string y = path_array[4]; + label += "," + y; + + if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z + { + std::string z = path_array[5]; + label = label + "," + z; + } + } + + label += ")"; + } + + return label; +} + +std::string LLUrlEntryRegion::getLocation(const std::string &url) const +{ + LLSD path_array = LLURI(url).pathArray(); + std::string region_name = unescapeUrl(path_array[2]); + return region_name; +} + +// +// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., +// secondlife:///app/teleport/Ahern/50/50/50/ +// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ +// +LLUrlEntryTeleport::LLUrlEntryTeleport() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_teleport.xml"; + mTooltip = LLTrans::getString("TooltipTeleportUrl"); +} + +std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle teleport SLURLs in the following formats: + // - secondlife:///app/teleport/Place/X/Y/Z + // - secondlife:///app/teleport/Place/X/Y + // - secondlife:///app/teleport/Place/X + // - secondlife:///app/teleport/Place + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + std::string host = uri.hostName(); + std::string label = LLTrans::getString("SLurlLabelTeleport"); + if (!host.empty()) + { + label += " " + host; + } + if (path_parts == 6) + { + // handle teleport url with (X,Y,Z) coordinates + std::string location = unescapeUrl(path_array[path_parts-4]); + std::string x = path_array[path_parts-3]; + std::string y = path_array[path_parts-2]; + std::string z = path_array[path_parts-1]; + return label + " " + location + " (" + x + "," + y + "," + z + ")"; + } + else if (path_parts == 5) + { + // handle teleport url with (X,Y) coordinates + std::string location = unescapeUrl(path_array[path_parts-3]); + std::string x = path_array[path_parts-2]; + std::string y = path_array[path_parts-1]; + return label + " " + location + " (" + x + "," + y + ")"; + } + else if (path_parts == 4) + { + // handle teleport url with (X) coordinate only + std::string location = unescapeUrl(path_array[path_parts-2]); + std::string x = path_array[path_parts-1]; + return label + " " + location + " (" + x + ")"; + } + else if (path_parts == 3) + { + // handle teleport url with no coordinates + std::string location = unescapeUrl(path_array[path_parts-1]); + return label + " " + location; + } + + return url; +} + +std::string LLUrlEntryTeleport::getLocation(const std::string &url) const +{ + // return the part of the Url after ///app/teleport + return ::getStringAfterToken(url, "app/teleport/"); +} + +// +// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts +// with secondlife:// (used as a catch-all for cases not matched above) +// +LLUrlEntrySL::LLUrlEntrySL() +{ + mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return unescapeUrl(url); +} + +// +// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// with the ability to specify a custom label. +// +LLUrlEntrySLLabel::LLUrlEntrySLLabel() +{ + mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_slapp.xml"; + mTooltip = LLTrans::getString("TooltipSLAPP"); +} + +std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return getLabelFromWikiLink(url); +} + +std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const +{ + return getUrlFromWikiLink(string); +} + +std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const +{ + // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574) + std::string url = getUrl(string); + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.getTooltip(); + } + + // unrecognized URL? should not happen + return LLUrlEntryBase::getTooltip(string); +} + +bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const +{ + std::string url = getUrl(string); + LLUrlMatch match; + if (LLUrlRegistry::instance().findUrl(url, match)) + { + return match.underlineOnHoverOnly(); + } + + // unrecognized URL? should not happen + return LLUrlEntryBase::underlineOnHoverOnly(string); +} + +// +// LLUrlEntryWorldMap Describes secondlife:/// URLs +// +LLUrlEntryWorldMap::LLUrlEntryWorldMap() +{ + mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", + boost::regex::perl|boost::regex::icase); + mMenuName = "menu_url_map.xml"; + mTooltip = LLTrans::getString("TooltipMapUrl"); +} + +std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + // + // we handle SLURLs in the following formats: + // - secondlife:///app/worldmap/PLACE/X/Y/Z + // - secondlife:///app/worldmap/PLACE/X/Y + // - secondlife:///app/worldmap/PLACE/X + // + LLURI uri(url); + LLSD path_array = uri.pathArray(); + S32 path_parts = path_array.size(); + if (path_parts < 3) + { + return url; + } + + const std::string label = LLTrans::getString("SLurlLabelShowOnMap"); + std::string location = unescapeUrl(path_array[2]); + std::string x = (path_parts > 3) ? path_array[3] : "128"; + std::string y = (path_parts > 4) ? path_array[4] : "128"; + std::string z = (path_parts > 5) ? path_array[5] : "0"; + return label + " " + location + " (" + x + "," + y + "," + z + ")"; +} + +std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const +{ + // return the part of the Url after secondlife:///app/worldmap/ part + return ::getStringAfterToken(url, "app/worldmap/"); +} + +// +// LLUrlEntryNoLink lets us turn of URL detection with ... tags +// +LLUrlEntryNoLink::LLUrlEntryNoLink() +{ + mPattern = boost::regex(".*?", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryNoLink::getUrl(const std::string &url) const +{ + // return the text between the and tags + return url.substr(8, url.size()-8-9); +} + +std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return getUrl(url); +} + +/*LLStyle::Params LLUrlEntryNoLink::getStyle() const +{ + // Don't render as URL (i.e. no context menu or hand cursor). + return LLStyle::Params().is_link(false); +}*/ + + +// +// LLUrlEntryIcon describes an icon with ... tags +// +LLUrlEntryIcon::LLUrlEntryIcon() +{ + mPattern = boost::regex("\\s*([^<]*)?\\s*", + boost::regex::perl|boost::regex::icase); +} + +std::string LLUrlEntryIcon::getUrl(const std::string &url) const +{ + return LLStringUtil::null; +} + +std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb) +{ + return LLStringUtil::null; +} + +std::string LLUrlEntryIcon::getIcon(const std::string &url) +{ + // Grep icon info between ... tags + // matches[1] contains the icon name/path + boost::match_results matches; + mIcon = (boost::regex_match(url, matches, mPattern) && matches[1].matched) + ? matches[1] + : LLStringUtil::null; + LLStringUtil::trim(mIcon); + return mIcon; +} diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h new file mode 100644 index 000000000..0e2f4038f --- /dev/null +++ b/indra/llui/llurlentry.h @@ -0,0 +1,429 @@ +/** + * @file llurlentry.h + * @author Martin Reddy + * @brief Describes the Url types that can be registered in LLUrlRegistry + * + * $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$ + */ + +#ifndef LL_LLURLENTRY_H +#define LL_LLURLENTRY_H + +#include "lluuid.h" +#include "lluicolor.h" +#include "llstyle.h" + +#include "llhost.h" // for resolving parcel name by parcel id + +#include +#include +#include +#include + +class LLAvatarName; + +typedef boost::signals2::signal LLUrlLabelSignal; +typedef LLUrlLabelSignal::slot_type LLUrlLabelCallback; + +/// +/// LLUrlEntryBase is the base class of all Url types registered in the +/// LLUrlRegistry. Each derived classes provides a regular expression +/// to match the Url type (e.g., http://... or secondlife://...) along +/// with an optional icon to display next to instances of the Url in +/// a text display and a XUI file to use for any context menu popup. +/// Functions are also provided to compute an appropriate label and +/// tooltip/status bar text for the Url. +/// +/// Some derived classes of LLUrlEntryBase may wish to compute an +/// appropriate label for a Url by asking the server for information. +/// You must therefore provide a callback method, so that you can be +/// notified when an updated label has been received from the server. +/// This label should then be used to replace any previous label +/// that you received from getLabel() for the Url in question. +/// +class LLUrlEntryBase +{ +public: + LLUrlEntryBase(); + virtual ~LLUrlEntryBase(); + + /// Return the regex pattern that matches this Url + boost::regex getPattern() const { return mPattern; } + + /// Return the url from a string that matched the regex + virtual std::string getUrl(const std::string &string) const; + + /// Given a matched Url, return a label for the Url + virtual std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb) { return url; } + + /// Return an icon that can be displayed next to Urls of this type + virtual std::string getIcon(const std::string &url); + + /// Return the style to render the displayed text + //virtual LLStyle::Params getStyle() const; + + /// Given a matched Url, return a tooltip string for the hyperlink + virtual std::string getTooltip(const std::string &string) const { return mTooltip; } + + /// Return the name of a XUI file containing the context menu items + std::string getMenuName() const { return mMenuName; } + + /// Return the name of a SL location described by this Url, if any + virtual std::string getLocation(const std::string &url) const { return ""; } + + /// Should this link text be underlined only when mouse is hovered over it? + virtual bool underlineOnHoverOnly(const std::string &string) const { return false; } + + virtual LLUUID getID(const std::string &string) const { return LLUUID::null; } + + bool isLinkDisabled() const; + +protected: + std::string getIDStringFromUrl(const std::string &url) const; + std::string escapeUrl(const std::string &url) const; + std::string unescapeUrl(const std::string &url) const; + std::string getLabelFromWikiLink(const std::string &url) const; + std::string getUrlFromWikiLink(const std::string &string) const; + void addObserver(const std::string &id, const std::string &url, const LLUrlLabelCallback &cb); + virtual void callObservers(const std::string &id, const std::string &label, const std::string& icon); + + typedef struct { + std::string url; + LLUrlLabelSignal *signal; + } LLUrlEntryObserver; + + boost::regex mPattern; + std::string mIcon; + std::string mMenuName; + std::string mTooltip; + std::multimap mObservers; +}; + +/// +/// LLUrlEntryHTTP Describes generic http: and https: Urls +/// +class LLUrlEntryHTTP : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTP(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +}; + +/// +/// LLUrlEntryHTTPLabel Describes generic http: and https: Urls with custom labels +/// +class LLUrlEntryHTTPLabel : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTPLabel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + +/// +/// LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com +/// +class LLUrlEntryHTTPNoProtocol : public LLUrlEntryBase +{ +public: + LLUrlEntryHTTPNoProtocol(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; +}; + +/// +/// LLUrlEntrySLURL Describes http://slurl.com/... Urls +/// +class LLUrlEntrySLURL : public LLUrlEntryBase +{ +public: + LLUrlEntrySLURL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryAgent Describes a Second Life agent Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +class LLUrlEntryAgent : public LLUrlEntryBase +{ +public: + LLUrlEntryAgent(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getIcon(const std::string &url); + /*virtual*/ std::string getTooltip(const std::string &string) const; + //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLUUID getID(const std::string &string) const; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +protected: + /*virtual*/ void callObservers(const std::string &id, const std::string &label, const std::string& icon); +private: + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +}; + +/// +/// LLUrlEntryAgentName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username) +/// that displays various forms of user name +/// This is a base class for the various implementations of name display +class LLUrlEntryAgentName : public LLUrlEntryBase, public boost::signals2::trackable +{ +public: + LLUrlEntryAgentName(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + //*virtual*/ LLStyle::Params getStyle() const; +protected: + // override this to pull out relevant name fields + virtual std::string getName(const LLAvatarName& avatar_name) = 0; +private: + void onAvatarNameCache(const LLUUID& id, const LLAvatarName& av_name); +}; + + +/// +/// LLUrlEntryAgentCompleteName Describes a Second Life agent name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename +/// that displays the full display name + user name for an avatar +/// such as "James Linden (james.linden)" +class LLUrlEntryAgentCompleteName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentCompleteName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentDisplayName Describes a Second Life agent display name Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname +/// that displays the just the display name for an avatar +/// such as "James Linden" +class LLUrlEntryAgentDisplayName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentDisplayName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryAgentUserName Describes a Second Life agent username Url, e.g., +/// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username +/// that displays the just the display name for an avatar +/// such as "james.linden" +class LLUrlEntryAgentUserName : public LLUrlEntryAgentName +{ +public: + LLUrlEntryAgentUserName(); +private: + /*virtual*/ std::string getName(const LLAvatarName& avatar_name); +}; + +/// +/// LLUrlEntryGroup Describes a Second Life group Url, e.g., +/// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about +/// +class LLUrlEntryGroup : public LLUrlEntryBase +{ +public: + LLUrlEntryGroup(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + //*virtual*/ LLStyle::Params getStyle() const; + /*virtual*/ LLUUID getID(const std::string &string) const; +private: + void onGroupNameReceived(const LLUUID& id, const std::string& name, bool is_group); +}; + +/// +/// LLUrlEntryInventory Describes a Second Life inventory Url, e.g., +/// secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select +/// +class LLUrlEntryInventory : public LLUrlEntryBase +{ +public: + LLUrlEntryInventory(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +private: +}; + +/// +/// LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g., +/// secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1 +/// +class LLUrlEntryObjectIM : public LLUrlEntryBase +{ +public: + LLUrlEntryObjectIM(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +private: +}; + +/// +/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., +/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// +class LLUrlEntryParcel : public LLUrlEntryBase +{ +public: + struct LLParcelData + { + LLUUID parcel_id; + std::string name; + std::string sim_name; + F32 global_x; + F32 global_y; + F32 global_z; + }; + + LLUrlEntryParcel(); + ~LLUrlEntryParcel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + + // Sends a parcel info request to sim. + void sendParcelInfoRequest(const LLUUID& parcel_id); + + // Calls observers of certain parcel id providing them with parcel label. + void onParcelInfoReceived(const std::string &id, const std::string &label); + + // Processes parcel label and triggers notifying observers. + static void processParcelInfo(const LLParcelData& parcel_data); + + // Next 4 setters are used to update agent and viewer connection information + // upon events like user login, viewer disconnect and user changing region host. + // These setters are made public to be accessible from newview and should not be + // used in other cases. + static void setAgentID(const LLUUID& id) { sAgentID = id; } + static void setSessionID(const LLUUID& id) { sSessionID = id; } + static void setRegionHost(const LLHost& host) { sRegionHost = host; } + static void setDisconnected(bool disconnected) { sDisconnected = disconnected; } + +private: + static LLUUID sAgentID; + static LLUUID sSessionID; + static LLHost sRegionHost; + static bool sDisconnected; + static std::set sParcelInfoObservers; +}; + +/// +/// LLUrlEntryPlace Describes a Second Life location Url, e.g., +/// secondlife://Ahern/50/50/50 +/// +class LLUrlEntryPlace : public LLUrlEntryBase +{ +public: + LLUrlEntryPlace(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryRegion Describes a Second Life location Url, e.g., +/// secondlife:///app/region/Ahern/128/128/0 +/// +class LLUrlEntryRegion : public LLUrlEntryBase +{ +public: + LLUrlEntryRegion(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., +/// secondlife:///app/teleport/Ahern/50/50/50/ +/// +class LLUrlEntryTeleport : public LLUrlEntryBase +{ +public: + LLUrlEntryTeleport(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// (used as a catch-all for cases not matched above) +/// +class LLUrlEntrySL : public LLUrlEntryBase +{ +public: + LLUrlEntrySL(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); +}; + +/// +/// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts +/// with secondlife:// with the ability to specify a custom label. +/// +class LLUrlEntrySLLabel : public LLUrlEntryBase +{ +public: + LLUrlEntrySLLabel(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getTooltip(const std::string &string) const; + /*virtual*/ bool underlineOnHoverOnly(const std::string &string) const; +}; + +/// +/// LLUrlEntryWorldMap Describes a Second Life worldmap Url, e.g., +/// secondlife:///app/worldmap/Ahern/50/50/50 +/// +class LLUrlEntryWorldMap : public LLUrlEntryBase +{ +public: + LLUrlEntryWorldMap(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getLocation(const std::string &url) const; +}; + +/// +/// LLUrlEntryNoLink lets us turn of URL detection with ... tags +/// +class LLUrlEntryNoLink : public LLUrlEntryBase +{ +public: + LLUrlEntryNoLink(); + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getUrl(const std::string &string) const; + //*virtual*/ LLStyle::Params getStyle() const; +}; + +/// +/// LLUrlEntryIcon describes an icon with ... tags +/// +class LLUrlEntryIcon : public LLUrlEntryBase +{ +public: + LLUrlEntryIcon(); + /*virtual*/ std::string getUrl(const std::string &string) const; + /*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb); + /*virtual*/ std::string getIcon(const std::string &url); +}; + + +#endif diff --git a/indra/llui/llurlmatch.cpp b/indra/llui/llurlmatch.cpp new file mode 100644 index 000000000..af4955ac4 --- /dev/null +++ b/indra/llui/llurlmatch.cpp @@ -0,0 +1,62 @@ +/** + * @file llurlmatch.cpp + * @author Martin Reddy + * @brief Specifies a matched Url in a string, as returned by LLUrlRegistry + * + * $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 "linden_common.h" +#include "llurlmatch.h" + +LLUrlMatch::LLUrlMatch() : + mStart(0), + mEnd(0), + mUrl(""), + mLabel(""), + mTooltip(""), + mIcon(""), + mMenuName(""), + mLocation(""), + mUnderlineOnHoverOnly(false) +{ +} + +void LLUrlMatch::setValues(U32 start, U32 end, const std::string &url, + const std::string &label, const std::string &tooltip, + const std::string &icon, /*const LLStyle::Params& style,*/ + const std::string &menu, const std::string &location, + const LLUUID& id, bool underline_on_hover_only) +{ + mStart = start; + mEnd = end; + mUrl = url; + mLabel = label; + mTooltip = tooltip; + mIcon = icon; + //mStyle = style; + //mStyle.link_href = url; + mMenuName = menu; + mLocation = location; + mID = id; + mUnderlineOnHoverOnly = underline_on_hover_only; +} diff --git a/indra/llui/llurlmatch.h b/indra/llui/llurlmatch.h new file mode 100644 index 000000000..af36e6925 --- /dev/null +++ b/indra/llui/llurlmatch.h @@ -0,0 +1,105 @@ +/** + * @file llurlmatch.h + * @author Martin Reddy + * @brief Specifies a matched Url in a string, as returned by LLUrlRegistry + * + * $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$ + */ + +#ifndef LL_LLURLMATCH_H +#define LL_LLURLMATCH_H + +//#include "linden_common.h" + +#include +#include +#include "llstyle.h" + +/// +/// LLUrlMatch describes a single Url that was matched within a string by +/// the LLUrlRegistry::findUrl() method. It includes the actual Url that +/// was matched along with its first/last character offset in the string. +/// An alternate label is also provided for creating a hyperlink, as well +/// as tooltip/status text, an icon, and a XUI file for a context menu +/// that can be used in a popup for a Url (e.g., Open, Copy URL, etc.) +/// +class LLUrlMatch +{ +public: + LLUrlMatch(); + + /// return true if this object does not contain a valid Url match yet + bool empty() const { return mUrl.empty(); } + + /// return the offset in the string for the first character of the Url + U32 getStart() const { return mStart; } + + /// return the offset in the string for the last character of the Url + U32 getEnd() const { return mEnd; } + + /// return the Url that has been matched in the input string + std::string getUrl() const { return mUrl; } + + /// return a label that can be used for the display of this Url + std::string getLabel() const { return mLabel; } + + /// return a message that could be displayed in a tooltip or status bar + std::string getTooltip() const { return mTooltip; } + + /// return the filename for an icon that can be displayed next to this Url + std::string getIcon() const { return mIcon; } + + /// Return the color to render the displayed text + //LLStyle::Params getStyle() const { return mStyle; } + + /// Return the name of a XUI file containing the context menu items + std::string getMenuName() const { return mMenuName; } + + /// return the SL location that this Url describes, or "" if none. + std::string getLocation() const { return mLocation; } + + /// Should this link text be underlined only when mouse is hovered over it? + bool underlineOnHoverOnly() const { return mUnderlineOnHoverOnly; } + + /// Change the contents of this match object (used by LLUrlRegistry) + void setValues(U32 start, U32 end, const std::string &url, const std::string &label, + const std::string &tooltip, const std::string &icon, + /*const LLStyle::Params& style, */const std::string &menu, + const std::string &location, const LLUUID& id, + bool underline_on_hover_only = false ); + + const LLUUID& getID() const { return mID; } +private: + U32 mStart; + U32 mEnd; + std::string mUrl; + std::string mLabel; + std::string mTooltip; + std::string mIcon; + std::string mMenuName; + std::string mLocation; + LLUUID mID; + //LLStyle::Params mStyle; + bool mUnderlineOnHoverOnly; +}; + +#endif diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp new file mode 100644 index 000000000..235b002d0 --- /dev/null +++ b/indra/llui/llurlregistry.cpp @@ -0,0 +1,263 @@ +/** + * @file llurlregistry.cpp + * @author Martin Reddy + * @brief Contains a set of Url types that can be matched in a string + * + * $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 "linden_common.h" +#include "llurlregistry.h" + +#include + +// default dummy callback that ignores any label updates from the server +void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, const std::string& icon) +{ +} + +LLUrlRegistry::LLUrlRegistry() +{ + mUrlEntry.reserve(20); + + // Urls are matched in the order that they were registered + registerUrl(new LLUrlEntryNoLink()); + registerUrl(new LLUrlEntryIcon()); + registerUrl(new LLUrlEntrySLURL()); + registerUrl(new LLUrlEntryHTTP()); + registerUrl(new LLUrlEntryHTTPLabel()); + registerUrl(new LLUrlEntryAgentCompleteName()); + registerUrl(new LLUrlEntryAgentDisplayName()); + registerUrl(new LLUrlEntryAgentUserName()); + // LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since + // LLUrlEntryAgent is a less specific (catchall for agent urls) + registerUrl(new LLUrlEntryAgent()); + registerUrl(new LLUrlEntryGroup()); + registerUrl(new LLUrlEntryParcel()); + registerUrl(new LLUrlEntryTeleport()); + registerUrl(new LLUrlEntryRegion()); + registerUrl(new LLUrlEntryWorldMap()); + registerUrl(new LLUrlEntryObjectIM()); + registerUrl(new LLUrlEntryPlace()); + registerUrl(new LLUrlEntryInventory()); + registerUrl(new LLUrlEntryObjectIM()); + //LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern, + //so it should be registered in the end of list + registerUrl(new LLUrlEntrySL()); + registerUrl(new LLUrlEntrySLLabel()); + // most common pattern is a URL without any protocol, + // e.g., "secondlife.com" + registerUrl(new LLUrlEntryHTTPNoProtocol()); +} + +LLUrlRegistry::~LLUrlRegistry() +{ + // free all of the LLUrlEntryBase objects we are holding + std::vector::iterator it; + for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it) + { + delete *it; + } +} + +void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front) +{ + if (url) + { + if (force_front) // IDEVO + mUrlEntry.insert(mUrlEntry.begin(), url); + else + mUrlEntry.push_back(url); + } +} + +static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end) +{ + boost::cmatch result; + bool found; + + // regex_search can potentially throw an exception, so check for it + try + { + found = boost::regex_search(text, result, regex); + } + catch (std::runtime_error &) + { + return false; + } + + if (! found) + { + return false; + } + + // return the first/last character offset for the matched substring + start = static_cast(result[0].first - text); + end = static_cast(result[0].second - text) - 1; + + // we allow certain punctuation to terminate a Url but not match it, + // e.g., "http://foo.com/." should just match "http://foo.com/" + if (text[end] == '.' || text[end] == ',') + { + end--; + } + // ignore a terminating ')' when Url contains no matching '(' + // see DEV-19842 for details + else if (text[end] == ')' && std::string(text+start, end-start).find('(') == std::string::npos) + { + end--; + } + + return true; +} + +static bool stringHasUrl(const std::string &text) +{ + // fast heuristic test for a URL in a string. This is used + // to avoid lots of costly regex calls, BUT it needs to be + // kept in sync with the LLUrlEntry regexes we support. + return (text.find("://") != std::string::npos || + text.find("www.") != std::string::npos || + text.find(".com") != std::string::npos || + text.find(".net") != std::string::npos || + text.find(".edu") != std::string::npos || + text.find(".org") != std::string::npos || + text.find("") != std::string::npos || + text.find("::iterator it; + for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it) + { + LLUrlEntryBase *url_entry = *it; + + U32 start = 0, end = 0; + if (matchRegex(text.c_str(), url_entry->getPattern(), start, end)) + { + // does this match occur in the string before any other match + if (start < match_start || match_entry == NULL) + { + match_start = start; + match_end = end; + match_entry = url_entry; + } + } + } + + // did we find a match? if so, return its details in the match object + if (match_entry) + { + // fill in the LLUrlMatch object and return it + std::string url = text.substr(match_start, match_end - match_start + 1); + match.setValues(match_start, match_end, + match_entry->getUrl(url), + match_entry->getLabel(url, cb), + match_entry->getTooltip(url), + match_entry->getIcon(url), + //match_entry->getStyle(), + match_entry->getMenuName(), + match_entry->getLocation(url), + match_entry->getID(url), + match_entry->underlineOnHoverOnly(url)); + return true; + } + + return false; +} + +bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUrlLabelCallback &cb) +{ + // boost::regex_search() only works on char or wchar_t + // types, but wchar_t is only 2-bytes on Win32 (not 4). + // So we use UTF-8 to make this work the same everywhere. + std::string utf8_text = wstring_to_utf8str(text); + if (findUrl(utf8_text, match, cb)) + { + // we cannot blindly return the start/end offsets from + // the UTF-8 string because it is a variable-length + // character encoding, so we need to update the start + // and end values to be correct for the wide string. + LLWString wurl = utf8str_to_wstring(match.getUrl()); + S32 start = text.find(wurl); + if (start == std::string::npos) + { + return false; + } + S32 end = start + wurl.size() - 1; + + match.setValues(start, end, match.getUrl(), + match.getLabel(), + match.getTooltip(), + match.getIcon(), + //match.getStyle(), + match.getMenuName(), + match.getLocation(), + match.getID(), + match.underlineOnHoverOnly()); + return true; + } + return false; +} + +bool LLUrlRegistry::hasUrl(const std::string &text) +{ + LLUrlMatch match; + return findUrl(text, match); +} + +bool LLUrlRegistry::hasUrl(const LLWString &text) +{ + LLUrlMatch match; + return findUrl(text, match); +} + +bool LLUrlRegistry::isUrl(const std::string &text) +{ + LLUrlMatch match; + if (findUrl(text, match)) + { + return (match.getStart() == 0 && match.getEnd() >= text.size()-1); + } + return false; +} + +bool LLUrlRegistry::isUrl(const LLWString &text) +{ + LLUrlMatch match; + if (findUrl(text, match)) + { + return (match.getStart() == 0 && match.getEnd() >= text.size()-1); + } + return false; +} diff --git a/indra/llui/llurlregistry.h b/indra/llui/llurlregistry.h new file mode 100644 index 000000000..da16171a9 --- /dev/null +++ b/indra/llui/llurlregistry.h @@ -0,0 +1,97 @@ +/** + * @file llurlregistry.h + * @author Martin Reddy + * @brief Contains a set of Url types that can be matched in a string + * + * $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$ + */ + +#ifndef LL_LLURLREGISTRY_H +#define LL_LLURLREGISTRY_H + +#include "llurlentry.h" +#include "llurlmatch.h" +#include "llsingleton.h" +#include "llstring.h" + +#include +#include + +/// This default callback for findUrl() simply ignores any label updates +void LLUrlRegistryNullCallback(const std::string &url, + const std::string &label, + const std::string &icon); + +/// +/// LLUrlRegistry is a singleton that contains a set of Url types that +/// can be matched in string. E.g., http:// or secondlife:// Urls. +/// +/// Clients call the findUrl() method on a string to locate the first +/// occurence of a supported Urls in that string. If findUrl() returns +/// true, the LLUrlMatch object will be updated to describe the Url +/// that was matched, including a label that can be used to hyperlink +/// the Url, an icon to display next to the Url, and a XUI menu that +/// can be used as a popup context menu for that Url. +/// +/// New Url types can be added to the registry with the registerUrl +/// method. E.g., to add support for a new secondlife:///app/ Url. +/// +/// Computing the label for a Url could involve a roundtrip request +/// to the server (e.g., to find the actual agent or group name). +/// As such, you can provide a callback method that will get invoked +/// when a new label is available for one of your matched Urls. +/// +class LLUrlRegistry : public LLSingleton +{ +public: + ~LLUrlRegistry(); + + /// add a new Url handler to the registry (will be freed on destruction) + /// optionally force it to the front of the list, making it take + /// priority over other regular expression matches for URLs + void registerUrl(LLUrlEntryBase *url, bool force_front = false); + + /// get the next Url in an input string, starting at a given character offset + /// your callback is invoked if the matched Url's label changes in the future + bool findUrl(const std::string &text, LLUrlMatch &match, + const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback); + + /// a slightly less efficient version of findUrl for wide strings + bool findUrl(const LLWString &text, LLUrlMatch &match, + const LLUrlLabelCallback &cb = &LLUrlRegistryNullCallback); + + // return true if the given string contains a URL that findUrl would match + bool hasUrl(const std::string &text); + bool hasUrl(const LLWString &text); + + // return true if the given string is a URL that findUrl would match + bool isUrl(const std::string &text); + bool isUrl(const LLWString &text); + +private: + LLUrlRegistry(); + friend class LLSingleton; + + std::vector mUrlEntry; +}; + +#endif diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index 7b6202a79..6d2ae643a 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -74,6 +74,7 @@ BOOL LLView::sForceReshape = FALSE; LLView* LLView::sEditingUIView = NULL; S32 LLView::sLastLeftXML = S32_MIN; S32 LLView::sLastBottomXML = S32_MIN; +std::vector LLViewDrawContext::sDrawContextStack; LLView::DrilldownFunc LLView::sDrilldown = boost::bind(&LLView::pointInView, _1, _2, _3, HIT_TEST_USE_BOUNDING_RECT); @@ -677,6 +678,16 @@ BOOL LLView::handleHover(S32 x, S32 y, MASK mask) } +void LLView::onMouseEnter(S32 x, S32 y, MASK mask) +{ + //llinfos << "Mouse entered " << getName() << llendl; +} + +void LLView::onMouseLeave(S32 x, S32 y, MASK mask) +{ + //llinfos << "Mouse left " << getName() << llendl; +} + std::string LLView::getShowNamesToolTip() { LLView* view = getParent(); @@ -2481,6 +2492,56 @@ void LLView::parseFollowsFlags(const LLView::Params& params) } } +LLView::tree_iterator_t LLView::beginTreeDFS() +{ + return tree_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); +} + +LLView::tree_iterator_t LLView::endTreeDFS() +{ + // an empty iterator is an "end" iterator + return tree_iterator_t(); +} + +LLView::tree_post_iterator_t LLView::beginTreeDFSPost() +{ + return tree_post_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); +} + +LLView::tree_post_iterator_t LLView::endTreeDFSPost() +{ + // an empty iterator is an "end" iterator + return tree_post_iterator_t(); +} + +LLView::bfs_tree_iterator_t LLView::beginTreeBFS() +{ + return bfs_tree_iterator_t(this, + boost::bind(boost::mem_fn(&LLView::beginChild), _1), + boost::bind(boost::mem_fn(&LLView::endChild), _1)); +} + +LLView::bfs_tree_iterator_t LLView::endTreeBFS() +{ + // an empty iterator is an "end" iterator + return bfs_tree_iterator_t(); +} + + +LLView::root_to_view_iterator_t LLView::beginRootToView() +{ + return root_to_view_iterator_t(this, boost::bind(&LLView::getParent, _1)); +} + +LLView::root_to_view_iterator_t LLView::endRootToView() +{ + return root_to_view_iterator_t(); +} + // static U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect) { @@ -2944,6 +3005,32 @@ S32 LLView::notifyParent(const LLSD& info) return parent->notifyParent(info); return 0; } +bool LLView::notifyChildren(const LLSD& info) +{ + bool ret = false; + BOOST_FOREACH(LLView* childp, mChildList) + { + ret = ret || childp->notifyChildren(info); + } + return ret; +} + +// convenient accessor for draw context +const LLViewDrawContext& LLView::getDrawContext() +{ + return LLViewDrawContext::getCurrentContext(); +} + +const LLViewDrawContext& LLViewDrawContext::getCurrentContext() +{ + static LLViewDrawContext default_context; + + if (sDrawContextStack.empty()) + return default_context; + + return *sDrawContextStack.back(); +} + LLView* LLView::createWidget(LLXMLNodePtr xml_node) const { // forward requests to ui ctrl factory diff --git a/indra/llui/llview.h b/indra/llui/llview.h index 54c298824..b009bab41 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -55,6 +55,7 @@ #include "lluistring.h" #include "llcursortypes.h" #include "llinitparam.h" +#include "lltreeiterators.h" #include "llfocusmgr.h" #include #include "ailist.h" @@ -130,6 +131,35 @@ public: } }; +// maintains render state during traversal of UI tree +class LLViewDrawContext +{ +public: + F32 mAlpha; + + LLViewDrawContext(F32 alpha = 1.f) + : mAlpha(alpha) + { + if (!sDrawContextStack.empty()) + { + LLViewDrawContext* context_top = sDrawContextStack.back(); + // merge with top of stack + mAlpha *= context_top->mAlpha; + } + sDrawContextStack.push_back(this); + } + + ~LLViewDrawContext() + { + sDrawContextStack.pop_back(); + } + + static const LLViewDrawContext& getCurrentContext(); + +private: + static std::vector sDrawContextStack; +}; + class LLView : public LLMouseHandler, // handles mouse events public LLFocusableElement, // handles keyboard events @@ -378,7 +408,24 @@ public: BOOL hasAncestor(const LLView* parentp) const; BOOL hasChild(const std::string& childname, BOOL recurse = FALSE) const; BOOL childHasKeyboardFocus( const std::string& childname ) const; + + // these iterators are used for collapsing various tree traversals into for loops + typedef LLTreeDFSIter tree_iterator_t; + tree_iterator_t beginTreeDFS(); + tree_iterator_t endTreeDFS(); + typedef LLTreeDFSPostIter tree_post_iterator_t; + tree_post_iterator_t beginTreeDFSPost(); + tree_post_iterator_t endTreeDFSPost(); + + typedef LLTreeBFSIter bfs_tree_iterator_t; + bfs_tree_iterator_t beginTreeBFS(); + bfs_tree_iterator_t endTreeBFS(); + + + typedef LLTreeDownIter root_to_view_iterator_t; + root_to_view_iterator_t beginRootToView(); + root_to_view_iterator_t endRootToView(); // // UTILITIES @@ -489,6 +536,9 @@ public: virtual LLView* childFromPoint(S32 x, S32 y, bool recur=false); + // view-specific handlers + virtual void onMouseEnter(S32 x, S32 y, MASK mask); + virtual void onMouseLeave(S32 x, S32 y, MASK mask); template T* findChild(const std::string& name) { return getChild(name,true,false); @@ -607,6 +657,16 @@ public: //send custom notification to LLView parent virtual S32 notifyParent(const LLSD& info); + + //send custom notification to all view childrend + // return true if _any_ children return true. otherwise false. + virtual bool notifyChildren(const LLSD& info); + + //send custom notification to current view + virtual S32 notify(const LLSD& info) { return 0;}; + + static const LLViewDrawContext& getDrawContext(); + protected: void drawDebugRect(); void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0, BOOL force_draw = FALSE); diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index cd2987fe8..2fdd7dac0 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -104,6 +104,8 @@ void LLViewBorder::draw() void LLViewBorder::drawOnePixelLines() { + F32 alpha = getDrawContext().mAlpha; + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLColor4 top_color = mHighlightLight; @@ -138,6 +140,9 @@ void LLViewBorder::drawOnePixelLines() S32 right = getRect().getWidth(); S32 bottom = 0; + top_color %= alpha; + bottom_color %= alpha; + gGL.color4fv( top_color.mV ); gl_line_2d(left, bottom, left, top); gl_line_2d(left, top, right, top); diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 78e856d0d..d2d5fa585 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -41,6 +41,17 @@ #include "lluuid.h" #include "lldiriterator.h" +#include "stringize.h" +#include +#include +#include +#include +#include +#include +#include + +using boost::assign::list_of; +using boost::assign::map_list_of; #if LL_WINDOWS #include "lldir_win32.h" @@ -86,12 +97,17 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask) std::string fullpath; S32 result; + // File masks starting with "/" will match nothing, so we consider them invalid. + if (LLStringUtil::startsWith(mask, getDirDelimiter())) + { + llwarns << "Invalid file mask: " << mask << llendl; + llassert(!"Invalid file mask"); + } + LLDirIterator iter(dirname, mask); while (iter.next(filename)) { - fullpath = dirname; - fullpath += getDirDelimiter(); - fullpath += filename; + fullpath = add(dirname, filename); if(LLFile::isdir(fullpath)) { @@ -259,12 +275,12 @@ std::string LLDir::buildSLOSCacheDir() const } else { - res = getOSUserAppDir() + mDirDelimiter + "cache_sg1"; + res = add(getOSUserAppDir(), "cache_sg1"); } } else { - res = getOSCacheDir() + mDirDelimiter + "SingularityViewer"; + res = add(getOSCacheDir(), "SingularityViewer"); } return res; } @@ -312,6 +328,39 @@ const std::string &LLDir::getLLPluginDir() const return mLLPluginDir; } +static std::string ELLPathToString(ELLPath location) +{ + typedef std::map ELLPathMap; +#define ENT(symbol) (symbol, #symbol) + static const ELLPathMap sMap = map_list_of + ENT(LL_PATH_NONE) + ENT(LL_PATH_USER_SETTINGS) + ENT(LL_PATH_APP_SETTINGS) + ENT(LL_PATH_PER_SL_ACCOUNT) // returns/expands to blank string if we don't know the account name yet + ENT(LL_PATH_CACHE) + ENT(LL_PATH_CHARACTER) + ENT(LL_PATH_HELP) + ENT(LL_PATH_LOGS) + ENT(LL_PATH_TEMP) + ENT(LL_PATH_SKINS) + ENT(LL_PATH_TOP_SKIN) + ENT(LL_PATH_CHAT_LOGS) + ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS) + ENT(LL_PATH_USER_SKIN) + ENT(LL_PATH_LOCAL_ASSETS) + ENT(LL_PATH_EXECUTABLE) + ENT(LL_PATH_DEFAULT_SKIN) + ENT(LL_PATH_FONTS) + ENT(LL_PATH_LAST) + ; +#undef ENT + + ELLPathMap::const_iterator found = sMap.find(location); + if (found != sMap.end()) + return found->second; + return STRINGIZE("Invalid ELLPath value " << location); +} + std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const { return getExpandedFilename(location, "", filename); @@ -332,15 +381,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_APP_SETTINGS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "app_settings"; + prefix = add(getAppRODataDir(), "app_settings"); break; case LL_PATH_CHARACTER: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "character"; + prefix = add(getAppRODataDir(), "character"); break; case LL_PATH_HELP: @@ -352,9 +397,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_USER_SETTINGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "user_settings"; + prefix = add(getOSUserAppDir(), "user_settings"); break; case LL_PATH_PER_SL_ACCOUNT: @@ -370,9 +413,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOGS: - prefix = getOSUserAppDir(); - prefix += mDirDelimiter; - prefix += "logs"; + prefix = add(getOSUserAppDir(), "logs"); break; case LL_PATH_TEMP: @@ -396,9 +437,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_LOCAL_ASSETS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "local_assets"; + prefix = add(getAppRODataDir(), "local_assets"); break; case LL_PATH_EXECUTABLE: @@ -406,56 +445,36 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd break; case LL_PATH_FONTS: - prefix = getAppRODataDir(); - prefix += mDirDelimiter; - prefix += "fonts"; + prefix = add(getAppRODataDir(), "fonts"); break; default: llassert(0); } - std::string filename = in_filename; - if (!subdir2.empty()) - { - filename = subdir2 + mDirDelimiter + filename; - } - - if (!subdir1.empty()) - { - filename = subdir1 + mDirDelimiter + filename; - } - if (prefix.empty()) { - llwarns << "prefix is empty, possible bad filename" << llendl; - } - - std::string expanded_filename; - if (!filename.empty()) - { - if (!prefix.empty()) - { - expanded_filename += prefix; - expanded_filename += mDirDelimiter; - expanded_filename += filename; - } - else - { - expanded_filename = filename; - } - } - else if (!prefix.empty()) - { - // Directory only, no file name. - expanded_filename = prefix; - } - else - { - expanded_filename.assign(""); + llwarns << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "': prefix is empty, possible bad filename" << llendl; } - //llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; + std::string expanded_filename = add(add(prefix, subdir1), subdir2); + if (expanded_filename.empty() && in_filename.empty()) + { + return ""; + } + // Use explicit concatenation here instead of another add() call. Callers + // passing in_filename as "" expect to obtain a pathname ending with + // mDirSeparator so they can later directly concatenate with a specific + // filename. A caller using add() doesn't care, but there's still code + // loose in the system that uses std::string::operator+(). + expanded_filename += mDirDelimiter; + expanded_filename += in_filename; + + LL_DEBUGS("LLDir") << ELLPathToString(location) + << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename + << "' => '" << expanded_filename << "'" << LL_ENDL; return expanded_filename; } @@ -530,12 +549,7 @@ std::string LLDir::getTempFilename() const random_uuid.generate(); random_uuid.toString(uuid_str); - std::string temp_filename = getTempDir(); - temp_filename += mDirDelimiter; - temp_filename += uuid_str; - temp_filename += ".tmp"; - - return temp_filename; + return add(getTempDir(), uuid_str + ".tmp"); } // static @@ -572,15 +586,9 @@ void LLDir::setLindenUserDir(const std::string &grid, const std::string &first, { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); - mLindenUserDir = getOSUserAppDir(); - mLindenUserDir += mDirDelimiter; - mLindenUserDir += firstlower; - mLindenUserDir += "_"; - mLindenUserDir += lastlower; + std::string userlower(first+"_"+last); + LLStringUtil::toLower(userlower); + mLindenUserDir = add(getOSUserAppDir(), userlower); if (!grid.empty()) { @@ -617,16 +625,9 @@ void LLDir::setPerAccountChatLogsDir(const std::string &grid, const std::string { // some platforms have case-sensitive filesystems, so be // utterly consistent with our firstname/lastname case. - std::string firstlower(first); - LLStringUtil::toLower(firstlower); - std::string lastlower(last); - LLStringUtil::toLower(lastlower); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += firstlower; - mPerAccountChatLogsDir += "_"; - mPerAccountChatLogsDir += lastlower; - + std::string userlower(first+"_"+last); + LLStringUtil::toLower(userlower); + mPerAccountChatLogsDir = add(getChatLogsDir(), userlower); if (!grid.empty()) { std::string gridlower(grid); @@ -644,22 +645,18 @@ void LLDir::setPerAccountChatLogsDir(const std::string &grid, const std::string void LLDir::setSkinFolder(const std::string &skin_folder) { mSkinDir = getSkinBaseDir(); - mSkinDir += mDirDelimiter; - mSkinDir += skin_folder; + append(mSkinDir, skin_folder); // user modifications to current skin // e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle mUserSkinDir = getOSUserAppDir(); - mUserSkinDir += mDirDelimiter; - mUserSkinDir += "skins_sg1"; - mUserSkinDir += mDirDelimiter; - mUserSkinDir += skin_folder; + append(mUserSkinDir, "skins_sg1"); + append(mUserSkinDir, skin_folder); // base skin which is used as fallback for all skinned files // e.g. c:\program files\secondlife\skins\default mDefaultSkinDir = getSkinBaseDir(); - mDefaultSkinDir += mDirDelimiter; - mDefaultSkinDir += "default"; + append(mDefaultSkinDir, "default"); } bool LLDir::setCacheDir(const std::string &path) @@ -673,7 +670,7 @@ bool LLDir::setCacheDir(const std::string &path) else { LLFile::mkdir(path); - std::string tempname = path + mDirDelimiter + "temp"; + std::string tempname = add(path, "temp"); LLFILE* file = LLFile::fopen(tempname,"wt"); if (file) { @@ -706,6 +703,57 @@ void LLDir::dumpCurrentDirectories() LL_DEBUGS2("AppInit","Directories") << " SkinDir: " << getSkinDir() << LL_ENDL; } +std::string LLDir::add(const std::string& path, const std::string& name) const +{ + std::string destpath(path); + append(destpath, name); + return destpath; +} + +void LLDir::append(std::string& destpath, const std::string& name) const +{ + // Delegate question of whether we need a separator to helper method. + SepOff sepoff(needSep(destpath, name)); + if (sepoff.first) // do we need a separator? + { + destpath += mDirDelimiter; + } + // If destpath ends with a separator, AND name starts with one, skip + // name's leading separator. + destpath += name.substr(sepoff.second); +} + +LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const +{ + if (path.empty() || name.empty()) + { + // If either path or name are empty, we do not need a separator + // between them. + return SepOff(false, 0); + } + // Here we know path and name are both non-empty. But if path already ends + // with a separator, or if name already starts with a separator, we need + // not add one. + std::string::size_type seplen(mDirDelimiter.length()); + bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter); + bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter); + if ((! path_ends_sep) && (! name_starts_sep)) + { + // If neither path nor name brings a separator to the junction, then + // we need one. + return SepOff(true, 0); + } + if (path_ends_sep && name_starts_sep) + { + // But if BOTH path and name bring a separator, we need not add one. + // Moreover, we should actually skip the leading separator of 'name'. + return SepOff(false, seplen); + } + // Here we know that either path_ends_sep or name_starts_sep is true -- + // but not both. So don't add a separator, and don't skip any characters: + // simple concatenation will do the trick. + return SepOff(false, 0); +} void dir_exists_or_crash(const std::string &dir_name) { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index d87249fd6..23f84bb41 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -140,7 +140,17 @@ class LLDir // Utility routine std::string buildSLOSCacheDir() const; + /// Append specified @a name to @a destpath, separated by getDirDelimiter() + /// if both are non-empty. + void append(std::string& destpath, const std::string& name) const; + /// Append specified @a name to @a path, separated by getDirDelimiter() + /// if both are non-empty. Return result, leaving @a path unmodified. + std::string add(const std::string& path, const std::string& name) const; + protected: + // Does an add() or append() call need a directory delimiter? + typedef std::pair SepOff; + SepOff needSep(const std::string& path, const std::string& name) const; std::string mAppName; // install directory under progams/ ie "SecondLife" std::string mExecutablePathAndName; // full path + Filename of .exe std::string mExecutableFilename; // Filename of .exe diff --git a/indra/llwindow/lldragdropwin32.cpp b/indra/llwindow/lldragdropwin32.cpp index d4d444eb2..15acddd98 100644 --- a/indra/llwindow/lldragdropwin32.cpp +++ b/indra/llwindow/lldragdropwin32.cpp @@ -124,10 +124,9 @@ class LLDragDropWin32Target: ScreenToClient( mAppWindowHandle, &pt2 ); LLCoordWindow cursor_coord_window( pt2.x, pt2.y ); - window_imp->convertCoords(cursor_coord_window, &gl_coord); MASK mask = gKeyboard->currentMask(TRUE); - LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, LLWindowCallbacks::DNDA_START_TRACKING, mDropUrl ); switch (result) @@ -180,10 +179,9 @@ class LLDragDropWin32Target: ScreenToClient( mAppWindowHandle, &pt2 ); LLCoordWindow cursor_coord_window( pt2.x, pt2.y ); - window_imp->convertCoords(cursor_coord_window, &gl_coord); MASK mask = gKeyboard->currentMask(TRUE); - LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( gl_coord, mask, + LLWindowCallbacks::DragNDropResult result = window_imp->completeDragNDropRequest( cursor_coord_window.convert(), mask, LLWindowCallbacks::DNDA_TRACK, mDropUrl ); switch (result) @@ -237,15 +235,13 @@ class LLDragDropWin32Target: LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong( mAppWindowHandle, GWL_USERDATA ); if ( NULL != window_imp ) { - LLCoordGL gl_coord( 0, 0 ); - POINT pt_client; pt_client.x = pt.x; pt_client.y = pt.y; ScreenToClient( mAppWindowHandle, &pt_client ); LLCoordWindow cursor_coord_window( pt_client.x, pt_client.y ); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + LLCoordGL gl_coord(cursor_coord_window.convert()); llinfos << "### (Drop) URL is: " << mDropUrl << llendl; llinfos << "### raw coords are: " << pt.x << " x " << pt.y << llendl; llinfos << "### client coords are: " << pt_client.x << " x " << pt_client.y << llendl; diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 9d0d73b81..8df242331 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -50,14 +50,15 @@ LLSplashScreen *gSplashScreenp = NULL; BOOL gDebugClicks = FALSE; BOOL gDebugWindowProc = FALSE; -const S32 gURLProtocolWhitelistCount = 3; -const std::string gURLProtocolWhitelist[] = { "file:", "http:", "https:" }; +const S32 gURLProtocolWhitelistCount = 4; +const std::string gURLProtocolWhitelist[] = { "secondlife:", "http:", "https:", "data:"/*, "file:"*/ }; // CP: added a handler list - this is what's used to open the protocol and is based on registry entry // only meaningful difference currently is that file: protocols are opened using http: // since no protocol handler exists in registry for file: // Important - these lists should match - protocol to handler -const std::string gURLProtocolWhitelistHandler[] = { "http", "http", "https" }; +// Maestro: This list isn't referenced anywhere that I could find +//const std::string gURLProtocolWhitelistHandler[] = { "http", "http", "https" }; S32 OSMessageBox(const std::string& text, const std::string& caption, U32 type) @@ -112,6 +113,8 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags) mCursorHidden(FALSE), mBusyCount(0), mIsMouseClipping(FALSE), + mMinWindowWidth(0), + mMinWindowHeight(0), mSwapMethod(SWAP_METHOD_UNDEFINED), mHideCursorPermanent(FALSE), mFlags(flags), @@ -180,6 +183,51 @@ void *LLWindow::getMediaWindow() return getPlatformWindow(); } +BOOL LLWindow::setSize(LLCoordScreen size) +{ + if (!getMaximized()) + { + size.mX = llmax(size.mX, mMinWindowWidth); + size.mY = llmax(size.mY, mMinWindowHeight); + } + return setSizeImpl(size); +} + +BOOL LLWindow::setSize(LLCoordWindow size) +{ + //HACK: we are inconsistently using minimum window dimensions + // in this case, we are constraining the inner "client" rect and other times + // we constrain the outer "window" rect + // There doesn't seem to be a good way to do this consistently without a bunch of platform + // specific code + if (!getMaximized()) + { + size.mX = llmax(size.mX, mMinWindowWidth); + size.mY = llmax(size.mY, mMinWindowHeight); + } + return setSizeImpl(size); +} + + +// virtual +void LLWindow::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately) +{ + mMinWindowWidth = min_width; + mMinWindowHeight = min_height; + + if (enforce_immediately) + { + LLCoordScreen cur_size; + if (!getMaximized() && getSize(&cur_size)) + { + if (cur_size.mX < mMinWindowWidth || cur_size.mY < mMinWindowHeight) + { + setSizeImpl(LLCoordScreen(llmin(cur_size.mX, mMinWindowWidth), llmin(cur_size.mY, mMinWindowHeight))); + } + } + } +} + //virtual void LLWindow::processMiscNativeEvents() { @@ -209,6 +257,8 @@ std::vector LLWindow::getDynamicFallbackFontList() return LLWindowWin32::getDynamicFallbackFontList(); #elif LL_DARWIN return LLWindowMacOSX::getDynamicFallbackFontList(); +#elif LL_MESA_HEADLESS + return std::vector(); #elif LL_SDL return LLWindowSDL::getDynamicFallbackFontList(); #else @@ -350,26 +400,26 @@ LLWindow* LLWindowManager::createWindow( #if LL_MESA_HEADLESS new_window = new LLWindowMesaHeadless(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth); #elif LL_SDL new_window = new LLWindowSDL(callbacks, title, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); #elif LL_WINDOWS new_window = new LLWindowWin32(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); #elif LL_DARWIN new_window = new LLWindowMacOSX(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth, fsaa_samples); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth, fsaa_samples); #endif } else { new_window = new LLWindowHeadless(callbacks, title, name, x, y, width, height, flags, - fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth); + fullscreen, clearBg, disable_vsync, ignore_pixel_depth); } if (FALSE == new_window->isValid()) @@ -404,3 +454,42 @@ BOOL LLWindowManager::isWindowValid(LLWindow *window) { return sWindowList.find(window) != sWindowList.end(); } + +//coordinate conversion utility funcs that forward to llwindow +LLCoordCommon LL_COORD_TYPE_WINDOW::convertToCommon() const +{ + const LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL out; + windowp->convertCoords(self, &out); + return out.convert(); +} + +void LL_COORD_TYPE_WINDOW::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordWindow& self = LLCoordWindow::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL from_gl(from); + windowp->convertCoords(from_gl, &self); +} + +LLCoordCommon LL_COORD_TYPE_SCREEN::convertToCommon() const +{ + const LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL out; + windowp->convertCoords(self, &out); + return out.convert(); +} + +void LL_COORD_TYPE_SCREEN::convertFromCommon(const LLCoordCommon& from) +{ + LLCoordScreen& self = LLCoordScreen::getTypedCoords(*this); + + LLWindow* windowp = &(*LLWindow::beginInstances()); + LLCoordGL from_gl(from); + windowp->convertCoords(from_gl, &self); +} diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 73723c6f0..575c57816 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -46,7 +46,7 @@ const S32 MIN_WINDOW_HEIGHT = 256; // Refer to llwindow_test in test/common/llwindow for usage example -class LLWindow +class LLWindow : public LLInstanceTracker { public: struct LLWindowResolution @@ -79,7 +79,9 @@ public: virtual BOOL getSize(LLCoordScreen *size) = 0; virtual BOOL getSize(LLCoordWindow *size) = 0; virtual BOOL setPosition(LLCoordScreen position) = 0; - virtual BOOL setSize(LLCoordScreen size) = 0; + BOOL setSize(LLCoordScreen size); + BOOL setSize(LLCoordWindow size); + virtual void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); virtual BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) = 0; virtual BOOL setCursorPosition(LLCoordWindow position) = 0; virtual BOOL getCursorPosition(LLCoordWindow *position) = 0; @@ -180,6 +182,9 @@ protected: // Defaults to true virtual BOOL canDelete(); + virtual BOOL setSizeImpl(LLCoordScreen size) = 0; + virtual BOOL setSizeImpl(LLCoordWindow size) = 0; + protected: LLWindowCallbacks* mCallbacks; @@ -200,6 +205,8 @@ protected: BOOL mHideCursorPermanent; U32 mFlags; U16 mHighSurrogate; + S32 mMinWindowWidth; + S32 mMinWindowHeight; // Handle a UTF-16 encoding unit received from keyboard. // Converting the series of UTF-16 encoding units to UTF-32 data, @@ -283,7 +290,7 @@ extern BOOL gDebugWindowProc; // Protocols, like "http" and "https" we support in URLs extern const S32 gURLProtocolWhitelistCount; extern const std::string gURLProtocolWhitelist[]; -extern const std::string gURLProtocolWhitelistHandler[]; +//extern const std::string gURLProtocolWhitelistHandler[]; void simpleEscapeString ( std::string& stringIn ); diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp index e6e6bc67f..dbdb40f5b 100644 --- a/indra/llwindow/llwindowheadless.cpp +++ b/indra/llwindow/llwindowheadless.cpp @@ -35,7 +35,7 @@ // LLWindowHeadless::LLWindowHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clear_background, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) + BOOL disable_vsync, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { // Initialize a headless keyboard. diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h index 1e911d754..72f9684ca 100644 --- a/indra/llwindow/llwindowheadless.h +++ b/indra/llwindow/llwindowheadless.h @@ -46,7 +46,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;}; /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;}; /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;}; - /*virtual*/ BOOL setSize(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;}; /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;}; /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;}; /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;}; @@ -95,7 +96,7 @@ public: S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clear_background, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); + BOOL disable_vsync, BOOL ignore_pixel_depth); virtual ~LLWindowHeadless(); private: diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h index ed5d7b1e7..7893dedda 100644 --- a/indra/llwindow/llwindowmacosx-objc.h +++ b/indra/llwindow/llwindowmacosx-objc.h @@ -3,31 +3,25 @@ * @brief Prototypes for functions shared between llwindowmacosx.cpp * and llwindowmacosx-objc.mm. * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm index 4ce225ca3..bebb537cd 100644 --- a/indra/llwindow/llwindowmacosx-objc.mm +++ b/indra/llwindow/llwindowmacosx-objc.mm @@ -3,31 +3,25 @@ * @brief Definition of functions shared between llwindowmacosx.cpp * and llwindowmacosx-objc.mm. * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 0f4883665..303a23959 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -210,7 +210,7 @@ LLWindowMacOSX::LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, + BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(NULL, fullscreen, flags) @@ -1256,7 +1256,7 @@ BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position) return TRUE; } -BOOL LLWindowMacOSX::setSize(const LLCoordScreen size) +BOOL LLWindowMacOSX::setSizeImpl(const LLCoordScreen size) { if(mWindow) { @@ -1266,6 +1266,31 @@ BOOL LLWindowMacOSX::setSize(const LLCoordScreen size) return TRUE; } +BOOL LLWindowMacOSX::setSizeImpl(const LLCoordWindow size) +{ + Rect client_rect; + if (mWindow) + { + OSStatus err = GetWindowBounds(mWindow, kWindowContentRgn, &client_rect); + if (err == noErr) + { + client_rect.right = client_rect.left + size.mX; + client_rect.bottom = client_rect.top + size.mY; + err = SetWindowBounds(mWindow, kWindowContentRgn, &client_rect); + } + if (err == noErr) + { + return TRUE; + } + else + { + llinfos << "Error setting size" << err << llendl; + return FALSE; + } + } + return FALSE; +} + void LLWindowMacOSX::swapBuffers() { aglSwapBuffers(mContext); @@ -2546,6 +2571,9 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e { // This is where we would constrain move/resize to a particular screen + const S32 MIN_WIDTH = mMinWindowWidth; + const S32 MIN_HEIGHT = mMinWindowHeight; + Rect currentBounds; Rect previousBounds; @@ -2570,14 +2598,14 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e mPreviousWindowRect = previousBounds; } - if ((currentBounds.right - currentBounds.left) < MIN_WINDOW_WIDTH) + if ((currentBounds.right - currentBounds.left) < MIN_WIDTH) { - currentBounds.right = currentBounds.left + MIN_WINDOW_WIDTH; + currentBounds.right = currentBounds.left + MIN_WIDTH; } - if ((currentBounds.bottom - currentBounds.top) < MIN_WINDOW_HEIGHT) + if ((currentBounds.bottom - currentBounds.top) < MIN_HEIGHT) { - currentBounds.bottom = currentBounds.top + MIN_WINDOW_HEIGHT; + currentBounds.bottom = currentBounds.top + MIN_HEIGHT; } SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), ¤tBounds); diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 89eca5750..3f75dc9ab 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -58,7 +58,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size); /*virtual*/ BOOL getSize(LLCoordWindow *size); /*virtual*/ BOOL setPosition(LLCoordScreen position); - /*virtual*/ BOOL setSize(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size); /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL); /*virtual*/ BOOL setCursorPosition(LLCoordWindow position); /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position); @@ -123,7 +124,7 @@ public: protected: LLWindowMacOSX(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowMacOSX(); diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp index 11c22ac94..2b668d3fc 100644 --- a/indra/llwindow/llwindowmesaheadless.cpp +++ b/indra/llwindow/llwindowmesaheadless.cpp @@ -41,28 +41,25 @@ U16 *gMesaBuffer = NULL; LLWindowMesaHeadless::LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth) + BOOL disable_vsync, BOOL ignore_pixel_depth) : LLWindow(callbacks, fullscreen, flags) { - if (use_gl) + llinfos << "MESA Init" << llendl; + mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL ); + + /* Allocate the image buffer */ + mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE]; + llassert(mMesaBuffer); + + gMesaBuffer = (U16*)mMesaBuffer; + + /* Bind the buffer to the context and make it current */ + if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height )) { - llinfos << "MESA Init" << llendl; - mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL ); - - /* Allocate the image buffer */ - mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE]; - llassert(mMesaBuffer); - - gMesaBuffer = (U16*)mMesaBuffer; - - /* Bind the buffer to the context and make it current */ - if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height )) - { - llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl; - } - - llverify(gGLManager.initGL()); + llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl; } + + llverify(gGLManager.initGL()); } diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h index db7cb4375..c8d2bf282 100644 --- a/indra/llwindow/llwindowmesaheadless.h +++ b/indra/llwindow/llwindowmesaheadless.h @@ -50,7 +50,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;}; /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;}; /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;}; - /*virtual*/ BOOL setSize(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size) {return FALSE;}; + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size) {return FALSE;}; /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL) {return FALSE;}; /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;}; /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;}; @@ -97,7 +98,7 @@ public: LLWindowMesaHeadless(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth); + BOOL disable_vsync, BOOL ignore_pixel_depth); ~LLWindowMesaHeadless(); private: diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 3c5601337..e1fa498fa 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -189,7 +189,7 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, + BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags), Lock_Display(NULL), @@ -200,7 +200,6 @@ LLWindowSDL::LLWindowSDL(LLWindowCallbacks* callbacks, gKeyboard->setCallbacks(callbacks); // Note that we can't set up key-repeat until after SDL has init'd video - // Ignore use_gl for now, only used for drones on PC mWindow = NULL; mNeedsResize = FALSE; mOverrideAspectRatio = 0.f; @@ -975,7 +974,7 @@ BOOL LLWindowSDL::setPosition(const LLCoordScreen position) return TRUE; } -BOOL LLWindowSDL::setSize(const LLCoordScreen size) +BOOL LLWindowSDL::setSizeImpl(const LLCoordScreen size) { if(mWindow) { @@ -993,6 +992,25 @@ BOOL LLWindowSDL::setSize(const LLCoordScreen size) return FALSE; } +BOOL LLWindowSDL::setSizeImpl(const LLCoordWindow size) +{ + if(mWindow) + { + // Push a resize event onto SDL's queue - we'll handle it + // when it comes out again. + SDL_Event event; + event.type = SDL_VIDEORESIZE; + event.resize.w = size.mX; + event.resize.h = size.mY; + SDL_PushEvent(&event); // copied into queue + + return TRUE; + } + + return FALSE; +} + + void LLWindowSDL::swapBuffers() { if (mWindow) @@ -1045,6 +1063,25 @@ void LLWindowSDL::setMouseClipping( BOOL b ) //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF); } +// virtual +void LLWindowSDL::setMinSize(U32 min_width, U32 min_height, bool enforce_immediately) +{ + LLWindow::setMinSize(min_width, min_height, enforce_immediately); + +#if LL_X11 + // Set the minimum size limits for X11 window + // so the window manager doesn't allow resizing below those limits. + XSizeHints* hints = XAllocSizeHints(); + hints->flags |= PMinSize; + hints->min_width = mMinWindowWidth; + hints->min_height = mMinWindowHeight; + + XSetWMNormalHints(mSDL_Display, mSDL_XWindowID, hints); + + XFree(hints); +#endif +} + BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position) { BOOL result = TRUE; @@ -1859,8 +1896,8 @@ void LLWindowSDL::gatherInput() llinfos << "Handling a resize event: " << event.resize.w << "x" << event.resize.h << llendl; - S32 width = llmax(event.resize.w, MIN_WINDOW_WIDTH); - S32 height = llmax(event.resize.h, MIN_WINDOW_HEIGHT); + S32 width = llmax(event.resize.w, (S32)mMinWindowWidth); + S32 height = llmax(event.resize.h, (S32)mMinWindowHeight); if (width != mWindow->w || height != mWindow->h) { @@ -2488,6 +2525,23 @@ void exec_cmd(const std::string& cmd, const std::string& arg) // Must begin with protocol identifier. void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url, bool async) { + bool found = false; + S32 i; + for (i = 0; i < gURLProtocolWhitelistCount; i++) + { + if (escaped_url.find(gURLProtocolWhitelist[i]) != std::string::npos) + { + found = true; + break; + } + } + + if (!found) + { + llwarns << "spawn_web_browser called for url with protocol not on whitelist: " << escaped_url << llendl; + return; + } + llinfos << "spawn_web_browser: " << escaped_url << llendl; #if LL_LINUX || LL_SOLARIS @@ -2623,9 +2677,9 @@ std::vector LLWindowSDL::getDynamicFallbackFontList() if (sortpat) { // Sort the list of system fonts from most-to-least-desirable. - FcResult fresult; + FcResult result; fs = FcFontSort(NULL, sortpat, elide_unicode_coverage, - NULL, &fresult); + NULL, &result); FcPatternDestroy(sortpat); } diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h index 4ec4236af..134554e6e 100644 --- a/indra/llwindow/llwindowsdl.h +++ b/indra/llwindow/llwindowsdl.h @@ -63,7 +63,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size); /*virtual*/ BOOL getSize(LLCoordWindow *size); /*virtual*/ BOOL setPosition(LLCoordScreen position); - /*virtual*/ BOOL setSize(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size); /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL); /*virtual*/ BOOL setCursorPosition(LLCoordWindow position); /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position); @@ -76,6 +77,7 @@ public: /*virtual*/ void captureMouse(); /*virtual*/ void releaseMouse(); /*virtual*/ void setMouseClipping( BOOL b ); + /*virtual*/ void setMinSize(U32 min_width, U32 min_height, bool enforce_immediately = true); /*virtual*/ BOOL isClipboardTextAvailable(); /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst); @@ -147,7 +149,7 @@ public: protected: LLWindowSDL(LLWindowCallbacks* callbacks, const std::string& title, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowSDL(); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 01d198783..78f54a7a4 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -363,11 +363,15 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, S32 x, S32 y, S32 width, S32 height, U32 flags, BOOL fullscreen, BOOL clearBg, - BOOL disable_vsync, BOOL use_gl, + BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples) : LLWindow(callbacks, fullscreen, flags) { + + //MAINT-516 -- force a load of opengl32.dll just in case windows went sideways + LoadLibrary(L"opengl32.dll"); + mFSAASamples = fsaa_samples; mIconResource = gIconResource; mOverrideAspectRatio = 0.f; @@ -860,13 +864,11 @@ BOOL LLWindowWin32::setPosition(const LLCoordScreen position) return FALSE; } getSize(&size); - moveWindow(position, size); - return TRUE; } -BOOL LLWindowWin32::setSize(const LLCoordScreen size) +BOOL LLWindowWin32::setSizeImpl(const LLCoordScreen size) { LLCoordScreen position; @@ -876,11 +878,30 @@ BOOL LLWindowWin32::setSize(const LLCoordScreen size) return FALSE; } - moveWindow(position, size); + WINDOWPLACEMENT placement; + placement.length = sizeof(WINDOWPLACEMENT); + if (!GetWindowPlacement(mWindowHandle, &placement)) return FALSE; + + placement.showCmd = SW_RESTORE; + + if (!SetWindowPlacement(mWindowHandle, &placement)) return FALSE; + + moveWindow(position, size); return TRUE; } +BOOL LLWindowWin32::setSizeImpl(const LLCoordWindow size) +{ + RECT window_rect = {0, 0, size.mX, size.mY }; + DWORD dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dw_style = WS_OVERLAPPEDWINDOW; + + AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style); + + return setSizeImpl(LLCoordScreen(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top)); +} + // changing fullscreen resolution BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp) { @@ -891,12 +912,12 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO DWORD current_refresh; DWORD dw_ex_style; DWORD dw_style; - RECT window_rect; + RECT window_rect = {0, 0, 0, 0}; S32 width = size.mX; S32 height = size.mY; BOOL auto_show = FALSE; - if (mhRC) + if (mhRC) { auto_show = TRUE; resetDisplayResolution(); @@ -1093,6 +1114,37 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO return FALSE; } + // (EXP-1765) dump pixel data to see if there is a pattern that leads to unreproducible crash + LL_INFOS("Window") << "--- begin pixel format dump ---" << llendl ; + LL_INFOS("Window") << "pixel_format is " << pixel_format << llendl ; + LL_INFOS("Window") << "pfd.nSize: " << pfd.nSize << llendl ; + LL_INFOS("Window") << "pfd.nVersion: " << pfd.nVersion << llendl ; + LL_INFOS("Window") << "pfd.dwFlags: 0x" << std::hex << pfd.dwFlags << std::dec << llendl ; + LL_INFOS("Window") << "pfd.iPixelType: " << (int)pfd.iPixelType << llendl ; + LL_INFOS("Window") << "pfd.cColorBits: " << (int)pfd.cColorBits << llendl ; + LL_INFOS("Window") << "pfd.cRedBits: " << (int)pfd.cRedBits << llendl ; + LL_INFOS("Window") << "pfd.cRedShift: " << (int)pfd.cRedShift << llendl ; + LL_INFOS("Window") << "pfd.cGreenBits: " << (int)pfd.cGreenBits << llendl ; + LL_INFOS("Window") << "pfd.cGreenShift: " << (int)pfd.cGreenShift << llendl ; + LL_INFOS("Window") << "pfd.cBlueBits: " << (int)pfd.cBlueBits << llendl ; + LL_INFOS("Window") << "pfd.cBlueShift: " << (int)pfd.cBlueShift << llendl ; + LL_INFOS("Window") << "pfd.cAlphaBits: " << (int)pfd.cAlphaBits << llendl ; + LL_INFOS("Window") << "pfd.cAlphaShift: " << (int)pfd.cAlphaShift << llendl ; + LL_INFOS("Window") << "pfd.cAccumBits: " << (int)pfd.cAccumBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumRedBits: " << (int)pfd.cAccumRedBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumGreenBits: " << (int)pfd.cAccumGreenBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumBlueBits: " << (int)pfd.cAccumBlueBits << llendl ; + LL_INFOS("Window") << "pfd.cAccumAlphaBits: " << (int)pfd.cAccumAlphaBits << llendl ; + LL_INFOS("Window") << "pfd.cDepthBits: " << (int)pfd.cDepthBits << llendl ; + LL_INFOS("Window") << "pfd.cStencilBits: " << (int)pfd.cStencilBits << llendl ; + LL_INFOS("Window") << "pfd.cAuxBuffers: " << (int)pfd.cAuxBuffers << llendl ; + LL_INFOS("Window") << "pfd.iLayerType: " << (int)pfd.iLayerType << llendl ; + LL_INFOS("Window") << "pfd.bReserved: " << (int)pfd.bReserved << llendl ; + LL_INFOS("Window") << "pfd.dwLayerMask: " << pfd.dwLayerMask << llendl ; + LL_INFOS("Window") << "pfd.dwVisibleMask: " << pfd.dwVisibleMask << llendl ; + LL_INFOS("Window") << "pfd.dwDamageMask: " << pfd.dwDamageMask << llendl ; + LL_INFOS("Window") << "--- end pixel format dump ---" << llendl ; + if (pfd.cColorBits < 32) { close(); @@ -1564,7 +1616,8 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen &size, BO } else { - llinfos << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << " context." << llendl; + llinfos << "Created OpenGL " << llformat("%d.%d", attribs[1], attribs[3]) << + (LLRender::sGLCoreProfile ? " core" : " compatibility") << " context." << llendl; done = true; if (LLRender::sGLCoreProfile) @@ -1675,24 +1728,15 @@ void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScre BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position) { - LLCoordScreen screen_pos; - mMousePositionModified = TRUE; if (!mWindowHandle) { return FALSE; } - if (!convertCoords(position, &screen_pos)) - { - return FALSE; - } - // Inform the application of the new mouse position (needed for per-frame // hover/picking to function). - LLCoordGL gl_pos; - convertCoords(position, &gl_pos); - mCallbacks->handleMouseMove(this, gl_pos, (MASK)0); + mCallbacks->handleMouseMove(this, position.convert(), (MASK)0); // DEV-18951 VWR-8524 Camera moves wildly when alt-clicking. // Because we have preemptively notified the application of the new @@ -1702,24 +1746,23 @@ BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position) while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) { } - return SetCursorPos(screen_pos.mX, screen_pos.mY); + LLCoordScreen screen_pos(position.convert()); + return ::SetCursorPos(screen_pos.mX, screen_pos.mY); } BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position) { POINT cursor_point; - LLCoordScreen screen_pos; - if (!mWindowHandle || - !GetCursorPos(&cursor_point)) + if (!mWindowHandle + || !GetCursorPos(&cursor_point) + || !position) { return FALSE; } - screen_pos.mX = cursor_point.x; - screen_pos.mY = cursor_point.y; - - return convertCoords(screen_pos, position); + *position = LLCoordScreen(cursor_point.x, cursor_point.y).convert(); + return TRUE; } void LLWindowWin32::hideCursor() @@ -1880,7 +1923,7 @@ void LLWindowWin32::gatherInput() MSG msg; int msg_count = 0; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE) + while ((msg_count < MAX_MESSAGE_PER_UPDATE) && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { mCallbacks->handlePingWatchdog(this, "Main:TranslateGatherInput"); TranslateMessage(&msg); @@ -1936,6 +1979,10 @@ static LLFastTimer::DeclareTimer FTM_MOUSEHANDLER("Handle Mouse"); LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param) { + // Ignore clicks not originated in the client area, i.e. mouse-up events not preceded with a WM_LBUTTONDOWN. + // This helps prevent avatar walking after maximizing the window by double-clicking the title bar. + static bool sHandleLeftMouseUp = true; + LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA); @@ -2282,10 +2329,20 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(FALSE)); return 0; + case WM_NCLBUTTONDOWN: + { + window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_NCLBUTTONDOWN"); + // A click in a non-client area, e.g. title bar or window border. + sHandleLeftMouseUp = false; + } + break; + case WM_LBUTTONDOWN: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDOWN"); LLFastTimer t2(FTM_MOUSEHANDLER); + sHandleLeftMouseUp = true; + if (LLWinImm::isAvailable() && window_imp->mPreeditor) { window_imp->interruptLanguageTextInput(); @@ -2296,15 +2353,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2326,15 +2383,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2350,6 +2407,13 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONUP"); LLFastTimer t2(FTM_MOUSEHANDLER); + + if (!sHandleLeftMouseUp) + { + sHandleLeftMouseUp = true; + break; + } + //if (gDebugClicks) //{ // LL_INFOS("Window") << "WndProc left button up" << LL_ENDL; @@ -2359,15 +2423,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2394,15 +2458,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2423,15 +2487,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2458,15 +2522,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2487,15 +2551,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // If we don't do this, many clicks could get buffered up, and if the // first click changes the cursor position, all subsequent clicks // will occur at the wrong location. JC - LLCoordWindow cursor_coord_window; if (window_imp->mMousePositionModified) { + LLCoordWindow cursor_coord_window; window_imp->getCursorPosition(&cursor_coord_window); - window_imp->convertCoords(cursor_coord_window, &gl_coord); + gl_coord = cursor_coord_window.convert(); } else { - window_imp->convertCoords(window_coord, &gl_coord); + gl_coord = window_coord.convert(); } MASK mask = gKeyboard->currentMask(TRUE); // generate move event to update mouse coordinates @@ -2567,17 +2631,16 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_MOUSEMOVE: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MOUSEMOVE"); - window_imp->convertCoords(window_coord, &gl_coord); MASK mask = gKeyboard->currentMask(TRUE); - window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask); + window_imp->mCallbacks->handleMouseMove(window_imp, window_coord.convert(), mask); return 0; } case WM_GETMINMAXINFO: { LPMINMAXINFO min_max = (LPMINMAXINFO)l_param; - min_max->ptMinTrackSize.x = MIN_WINDOW_WIDTH; - min_max->ptMinTrackSize.y = MIN_WINDOW_HEIGHT; + min_max->ptMinTrackSize.x = window_imp->mMinWindowWidth; + min_max->ptMinTrackSize.y = window_imp->mMinWindowHeight; return 0; } @@ -3483,7 +3546,7 @@ void LLWindowWin32::setLanguageTextInput( const LLCoordGL & position ) LLWinImm::setCompositionWindow( himc, &ime_form ); - sWinIMEWindowPosition.set( win_pos.mX, win_pos.mY ); + sWinIMEWindowPosition = win_pos; } LLWinImm::releaseContext(mWindowHandle, himc); diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index c24fbdca7..9f678b96c 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -57,7 +57,8 @@ public: /*virtual*/ BOOL getSize(LLCoordScreen *size); /*virtual*/ BOOL getSize(LLCoordWindow *size); /*virtual*/ BOOL setPosition(LLCoordScreen position); - /*virtual*/ BOOL setSize(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordScreen size); + /*virtual*/ BOOL setSizeImpl(LLCoordWindow size); /*virtual*/ BOOL switchContext(BOOL fullscreen, const LLCoordScreen &size, BOOL disable_vsync, const LLCoordScreen * const posp = NULL); /*virtual*/ BOOL setCursorPosition(LLCoordWindow position); /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position); @@ -120,7 +121,7 @@ public: protected: LLWindowWin32(LLWindowCallbacks* callbacks, const std::string& title, const std::string& name, int x, int y, int width, int height, U32 flags, - BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl, + BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL ignore_pixel_depth, U32 fsaa_samples); ~LLWindowWin32(); diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp index 3d7818338..0fd4615b4 100644 --- a/indra/mac_updater/mac_updater.cpp +++ b/indra/mac_updater/mac_updater.cpp @@ -1133,9 +1133,7 @@ void *updatethreadproc(void*) llinfos << "Clearing cache..." << llendl; - char mask[LL_MAX_PATH]; /* Flawfinder: ignore */ - snprintf(mask, LL_MAX_PATH, "%s*.*", gDirUtilp->getDirDelimiter().c_str()); - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),"*.*"); llinfos << "Clear complete." << llendl; diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index fa0bb64c2..5f1f6311b 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -220,7 +220,7 @@ set(viewer_SOURCE_FILES llfloaterlandholdings.cpp llfloaterlandmark.cpp llfloatermap.cpp - llfloatermediabrowser.cpp + llfloatermediasettings.cpp llfloatermemleak.cpp llfloatermessagelog.cpp llfloatermodelpreview.cpp @@ -259,7 +259,10 @@ set(viewer_SOURCE_FILES llfloatertos.cpp llfloaterurldisplay.cpp llfloaterurlentry.cpp + llfloatervoiceeffect.cpp llfloaterwater.cpp + llfloaterwebcontent.cpp + llfloaterwhitelistentry.cpp llfloaterwindlight.cpp llfloaterworldmap.cpp llfolderview.cpp @@ -314,6 +317,7 @@ set(viewer_SOURCE_FILES llmarketplacefunctions.cpp llmarketplacenotifications.cpp llmediactrl.cpp + llmediadataclient.cpp llmediaremotectrl.cpp llmenucommands.cpp llmenuoptionpathfindingrebakenavmesh.cpp @@ -364,7 +368,10 @@ set(viewer_SOURCE_FILES llpanellogin.cpp llpanelmaininventory.cpp llpanelmarketplaceoutboxinventory.cpp - llpanelmediahud.cpp + llpanelmediasettingsgeneral.cpp + llpanelmediasettingspermissions.cpp + llpanelmediasettingssecurity.cpp + llpanelnearbymedia.cpp llpanelmorph.cpp llpanelmsgs.cpp llpanelnetwork.cpp @@ -373,9 +380,11 @@ set(viewer_SOURCE_FILES llpanelpermissions.cpp llpanelpick.cpp llpanelplace.cpp + llpanelprimmediacontrols.cpp llpanelprofile.cpp llpanelskins.cpp llpanelvoicedevicesettings.cpp + llpanelvoiceeffect.cpp llpanelvolume.cpp llpanelweb.cpp llparcelselection.cpp @@ -414,6 +423,7 @@ set(viewer_SOURCE_FILES llscrollingpanelparambase.cpp llselectmgr.cpp llsky.cpp + llslurl.cpp llspatialpartition.cpp llspeakers.cpp llsprite.cpp @@ -456,7 +466,6 @@ set(viewer_SOURCE_FILES llurl.cpp llurldispatcher.cpp llurlhistory.cpp - llurlsimstring.cpp llurlwhitelist.cpp lluserauth.cpp llvectorperfoptions.cpp @@ -482,9 +491,7 @@ set(viewer_SOURCE_FILES llviewerlayer.cpp llviewermedia.cpp llviewermedia_streamingaudio.cpp - llviewermediaeventemitter.cpp llviewermediafocus.cpp - llviewermediaobserver.cpp llviewermenu.cpp llviewermenufile.cpp llviewermessage.cpp @@ -519,6 +526,7 @@ set(viewer_SOURCE_FILES llvoclouds.cpp llvograss.cpp llvoground.cpp + llvoicecallhandler.cpp llvoicechannel.cpp llvoiceclient.cpp llvoiceremotectrl.cpp @@ -722,7 +730,7 @@ set(viewer_HEADER_FILES llfloaterlandholdings.h llfloaterlandmark.h llfloatermap.h - llfloatermediabrowser.h + llfloatermediasettings.h llfloatermemleak.h llfloatermessagelog.h llfloatermodelpreview.h @@ -761,7 +769,10 @@ set(viewer_HEADER_FILES llfloatertos.h llfloaterurldisplay.h llfloaterurlentry.h + llfloatervoiceeffect.h llfloaterwater.h + llfloaterwebcontent.h + llfloaterwhitelistentry.h llfloaterwindlight.h llfloaterworldmap.h llfolderview.h @@ -816,6 +827,7 @@ set(viewer_HEADER_FILES llmarketplacefunctions.h llmarketplacenotifications.h llmediactrl.h + llmediadataclient.h llmediaremotectrl.h llmenucommands.h llmenuoptionpathfindingrebakenavmesh.h @@ -866,7 +878,10 @@ set(viewer_HEADER_FILES llpanellogin.h llpanelmaininventory.h llpanelmarketplaceoutboxinventory.h - llpanelmediahud.h + llpanelmediasettingsgeneral.h + llpanelmediasettingspermissions.h + llpanelmediasettingssecurity.h + llpanelnearbymedia.h llpanelmorph.h llpanelmsgs.h llpanelnetwork.h @@ -875,9 +890,11 @@ set(viewer_HEADER_FILES llpanelpermissions.h llpanelpick.h llpanelplace.h + llpanelprimmediacontrols.h llpanelprofile.h llpanelskins.h llpanelvoicedevicesettings.h + llpanelvoiceeffect.h llpanelvolume.h llpanelweb.h llparcelselection.h @@ -918,6 +935,7 @@ set(viewer_HEADER_FILES llselectmgr.h llsimplestat.h llsky.h + llslurl.h llspatialpartition.h llspeakers.h llsprite.h @@ -963,7 +981,6 @@ set(viewer_HEADER_FILES llurl.h llurldispatcher.h llurlhistory.h - llurlsimstring.h llurlwhitelist.h lluserauth.h llvectorperfoptions.h @@ -988,9 +1005,7 @@ set(viewer_HEADER_FILES llviewerkeyboard.h llviewerlayer.h llviewermedia.h - llviewermediaeventemitter.h llviewermediafocus.h - llviewermediaobserver.h llviewermenu.h llviewermenufile.h llviewermessage.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index d85f7f895..ed43f7ffe 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -425,7 +425,19 @@ - + ShowNearbyMediaFloater + + Comment + Show nearby media floter + Persist + 1 + Type + Boolean + Value + 0 + + + ShowAOSitPopup @@ -1158,7 +1170,7 @@ This should be as low as possible, but too low may break functionality Comment Show Windlight popup Persist - 0 + 1 Type Boolean Value @@ -2257,6 +2269,17 @@ This should be as low as possible, but too low may break functionality F32 Value 0.075 + + AudioStreamingMedia + + Comment + Enable streaming + Persist + 1 + Type + Boolean + Value + 1 AudioStreamingMusic @@ -2269,17 +2292,6 @@ This should be as low as possible, but too low may break functionality Value 1 - AudioStreamingVideo - - Comment - Enable streaming video - Persist - 1 - Type - Boolean - Value - 0 - AuditTexture Comment @@ -2724,23 +2736,23 @@ This should be as low as possible, but too low may break functionality Value 0 - PluginAttachDebuggerToPlugins + BrowserEnableJSObject Comment - Opens a terminal window with a debugger and attach it, every time a new SLPlugin process is started. + (WARNING: Advanced feature. Use if you are aware of the implications). Enable or disable the viewer to Javascript bridge object. Persist - 1 + 0 Type Boolean Value - 0 + 0 BlockAvatarAppearanceMessages - - Comment - Ignores appearance messages (for simulating Ruth) - Persist - 1 + + Comment + Ignores appearance messages (for simulating Ruth) + Persist + 1 Type Boolean Value @@ -3802,17 +3814,6 @@ This should be as low as possible, but too low may break functionality - CmdLineRegionURI - - Comment - URL of region to connect to through Agent Domain. - Persist - 0 - Type - String - Value - - ColorPaletteEntry01 Comment @@ -5452,6 +5453,17 @@ This should be as low as possible, but too low may break functionality Value 0 + DisableExternalBrowser + + Comment + Disable opening an external browser. + Persist + 1 + Type + Boolean + Value + 0 + DisableRendering Comment @@ -5463,6 +5475,17 @@ This should be as low as possible, but too low may break functionality Value 0 + DisableTextHyperlinkActions + + Comment + Disable highlighting and linking of URLs in XUI text boxes + Persist + 1 + Type + Boolean + Value + 0 + DisableVerticalSync Comment @@ -6036,17 +6059,6 @@ This should be as low as possible, but too low may break functionality Value 1 - FirstLoginThisInstall - - Comment - Specifies that you have not successfully logged in since you installed the latest update - Persist - 1 - Type - Boolean - Value - 1 - FirstName Comment @@ -6091,6 +6103,17 @@ This should be as low as possible, but too low may break functionality Value 1 + FirstLoginThisInstall + + Comment + Specifies that you have not successfully logged in since you installed the latest update + Persist + 1 + Type + Boolean + Value + 1 + FixedWeather Comment @@ -7438,6 +7461,22 @@ This should be as low as possible, but too low may break functionality Value -1 + FloaterVoiceEffectRect + + Comment + Rectangle for voice morpher floater + Persist + 1 + Type + Rect + Value + + 0 + 300 + 360 + 0 + + FloaterWorldMapRect2 Comment @@ -8812,6 +8851,17 @@ This should be as low as possible, but too low may break functionality Value 0 + LastMediaSettingsTab + + Comment + Last selected tab in media settings window + Persist + 1 + Type + S32 + Value + 0 + LastRunVersion Comment @@ -9131,16 +9181,16 @@ This should be as low as possible, but too low may break functionality Value 0 - LoginLastLocation + LoginLocation Comment - Login at same location you last logged out + Default Login location ('last', 'home') preference Persist 1 Type - Boolean + String Value - 1 + last LoginPage @@ -9457,13 +9507,68 @@ This should be as low as possible, but too low may break functionality Persist 1 Type - Boolean - Value - 0 - - MemoryFailurePreventionEnabled - - Comment + Boolean + Value + 1 + + MediaPerformanceManagerDebug + + Comment + Whether to show debug data for the media performance manager in the nearby media list. + Persist + 1 + Type + Boolean + Value + 0 + + MediaShowOnOthers + + Comment + Whether or not to show media on other avatars + Persist + 1 + Type + Boolean + Value + 0 + + MediaShowOutsideParcel + + Comment + Whether or not to show media from outside the current parcel + Persist + 1 + Type + Boolean + Value + 1 + + MediaShowWithinParcel + + Comment + Whether or not to show media within the current parcel + Persist + 1 + Type + Boolean + Value + 1 + + MediaTentativeAutoPlay + + Comment + This is a tentative flag that may be temporarily set off by the user, until she teleports + Persist + 0 + Type + Boolean + Value + 1 + + MemoryFailurePreventionEnabled + + Comment If set, the viewer will quit to avoid crash when memory failure happens Persist 1 @@ -10448,6 +10553,86 @@ This should be as low as possible, but too low may break functionality Value 1 + PluginAttachDebuggerToPlugins + + Comment + If true, attach a debugger session to each plugin process as it's launched. + Persist + 1 + Type + Boolean + Value + 0 + + PluginInstancesCPULimit + + Comment + Amount of total plugin CPU usage before inworld plugins start getting turned down to "slideshow" priority. Set to 0 to disable this check. + Persist + 1 + Type + F32 + Value + 0.9 + + + PlainTextChatHistory + + Comment + Enable/Disable plain text chat history style + Persist + 1 + Type + Boolean + Value + 0 + + + PluginInstancesLow + + Comment + Limit on the number of inworld media plugins that will run at "low" priority + Persist + 1 + Type + U32 + Value + 4 + + PluginInstancesNormal + + Comment + Limit on the number of inworld media plugins that will run at "normal" or higher priority + Persist + 1 + Type + U32 + Value + 2 + + PluginInstancesTotal + + Comment + Hard limit on the number of plugins that will be instantiated at once for inworld media + Persist + 1 + Type + U32 + Value + 8 + + + PluginUseReadThread + + Comment + Use a separate thread to read incoming messages from plugins + Persist + 1 + Type + Boolean + Value + 0 + PlayTypingSound Comment @@ -10651,6 +10836,94 @@ This should be as low as possible, but too low may break functionality + PrimMediaMasterEnabled + + Comment + Whether or not Media on a Prim is enabled. + Persist + 1 + Type + Boolean + Value + 1 + + PrimMediaControlsUseHoverControlSet + + Comment + Whether or not hovering over prim media uses minimal "hover" controls or the authored control set. + Persist + 1 + Type + Boolean + Value + 0 + + PrimMediaDragNDrop + + Comment + Enable drag and drop of URLs onto prim faces + Persist + 1 + Type + Boolean + Value + 1 + + PrimMediaMaxRetries + + Comment + Maximum number of retries for media queries. + Persist + 1 + Type + U32 + Value + 4 + + PrimMediaRequestQueueDelay + + Comment + Timer delay for fetching media from the queue (in seconds). + Persist + 1 + Type + F32 + Value + 1.0 + + PrimMediaRetryTimerDelay + + Comment + Timer delay for retrying on media queries (in seconds). + Persist + 1 + Type + F32 + Value + 5.0 + + PrimMediaMaxSortedQueueSize + + Comment + Maximum number of objects the viewer will load media for initially + Persist + 1 + Type + U32 + Value + 100000 + + PrimMediaMaxRoundRobinQueueSize + + Comment + Maximum number of objects the viewer will continuously update media for + Persist + 1 + Type + U32 + Value + 100000 + PreviewAnimRect Comment @@ -10959,6 +11232,50 @@ This should be as low as possible, but too low may break functionality Value 1.0 + WebContentWindowLimit + + Comment + Maximum number of web browser windows that can be open at once in the Web content floater (0 for no limit) + Persist + 1 + Type + S32 + Value + 5 + + MediaRollOffRate + + Comment + Multiplier to change rate of media attenuation + Persist + 1 + Type + F32 + Value + 0.125 + + MediaRollOffMin + + Comment + Adjusts the distance at which media attentuation starts + Persist + 1 + Type + F32 + Value + 5.0 + + MediaRollOffMax + + Comment + Distance at which media volume is set to 0 + Persist + 1 + Type + F32 + Value + 30.0 + RecentItemsSortOrder Comment @@ -16475,6 +16792,17 @@ This should be as low as possible, but too low may break functionality Value 0.40000000596 + moapbeacon + + Comment + Beacon / Highlight media on a prim sources + Persist + 1 + Type + Boolean + Value + 0 + particlesbeacon Comment @@ -16573,6 +16901,28 @@ This should be as low as possible, but too low may break functionality Boolean Value 1 + + SLURLDragNDrop + + Comment + Enable drag and drop of SLURLs onto the viewer + Persist + 1 + Type + Boolean + Value + 1 + + SLURLPassToOtherInstance + + Comment + Pass execution to prevoius viewer instances if there is a given slurl + Persist + 1 + Type + Boolean + Value + 1 soundsbeacon @@ -16585,17 +16935,6 @@ This should be as low as possible, but too low may break functionality Value 0 - keepbeacons - - Comment - Keep beacons when closing floater - Persist - 1 - Type - Boolean - Value - 0 - ClearBeaconAfterTeleport Comment @@ -16651,6 +16990,19 @@ This should be as low as possible, but too low may break functionality Value 180 + + SLURLTeleportDirectly + + Comment + Clicking on a slurl will teleport you directly instead of opening places panel + Persist + 1 + Type + Boolean + Value + 0 + + UseHTTPInventory Comment @@ -16770,6 +17122,22 @@ This should be as low as possible, but too low may break functionality 473 + FloaterNearbyMediaRect + + Comment + Rectangle for nearby media floater. + Persist + 1 + Type + Rect + Value + + 0 + 0 + 0 + 0 + + WindEnabled Comment diff --git a/indra/newview/app_settings/settings_sh.xml b/indra/newview/app_settings/settings_sh.xml index d0f7516a2..732da64bf 100644 --- a/indra/newview/app_settings/settings_sh.xml +++ b/indra/newview/app_settings/settings_sh.xml @@ -41,6 +41,17 @@ Value 0 + SHEnableFMODEXVerboseDebugging + + Comment + Enable profiler tool if using FMOD Ex + Persist + 1 + Type + Boolean + Value + 0 + SHFMODExStreamBufferSize Comment diff --git a/indra/newview/chatbar_as_cmdline.cpp b/indra/newview/chatbar_as_cmdline.cpp index 895f72b04..5b9d7817a 100644 --- a/indra/newview/chatbar_as_cmdline.cpp +++ b/indra/newview/chatbar_as_cmdline.cpp @@ -60,6 +60,8 @@ #include "lltooldraganddrop.h" #include "llinventorymodel.h" #include "llselectmgr.h" +#include "llslurl.h" +#include "llurlaction.h" #include @@ -374,7 +376,6 @@ bool cmd_line_chat(std::string revised_text, EChatType type) S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); S32 agent_z = llround( (F32)agentPos.mdV[VZ] ); std::string region_name = LLWeb::escapeURL(revised_text.substr(command.length()+1)); - std::string url; if(!sAscentCmdLineMapToKeepPos) { @@ -383,8 +384,8 @@ bool cmd_line_chat(std::string revised_text, EChatType type) agent_z = 0; } - url = llformat("secondlife:///app/teleport/%s/%d/%d/%d",region_name.c_str(),agent_x,agent_y,agent_z); - LLURLDispatcher::dispatch(url, NULL, true); + LLSLURL slurl(region_name,LLVector3(agent_x,agent_y,agent_z)); + LLUrlAction::teleportToLocation(std::string("secondlife:///app/teleport/")+slurl.getLocationString()); } return false; } diff --git a/indra/newview/hippogridmanager.cpp b/indra/newview/hippogridmanager.cpp index 93168da88..d7c02be7f 100644 --- a/indra/newview/hippogridmanager.cpp +++ b/indra/newview/hippogridmanager.cpp @@ -506,7 +506,7 @@ void HippoGridInfo::formatFee(std::string &fee, int cost, bool showFree) const } //static -std::string HippoGridInfo::sanitizeGridNick(std::string &gridnick) +std::string HippoGridInfo::sanitizeGridNick(const std::string &gridnick) { std::string tmp; int size = gridnick.size(); @@ -529,7 +529,7 @@ std::string HippoGridInfo::sanitizeGridNick(std::string &gridnick) } -std::string HippoGridInfo::getGridNick() +std::string HippoGridInfo::getGridNick() const { if(!mGridNick.empty()) { @@ -684,42 +684,57 @@ void HippoGridManager::discardAndReload() HippoGridInfo* HippoGridManager::getGrid(const std::string& grid) const { + if(grid.empty()) + return NULL; + std::map::const_iterator it; it = mGridInfo.find(grid); + + //The grids are keyed by 'name' which equates to something like "Second Life" + //Try to match such first. if (it != mGridInfo.end()) { return it->second; } - else + else //Fall back to nick short names. (so something like "secondlife" will work) { - return 0; + for(it = mGridInfo.begin(); it != mGridInfo.end(); ++it) + { + if(it->second && LLStringUtil::compareInsensitive(it->second->getGridNick(), grid)==0) + return it->second; + } } + return NULL; } HippoGridInfo* HippoGridManager::getCurrentGrid() const { HippoGridInfo* grid = getGrid(mCurrentGrid); - if (grid) + if(!grid) { - return grid; - } - else - { - return &HippoGridInfo::FALLBACK_GRIDINFO; + grid = getGrid(mDefaultGrid); } + return grid ? grid : &HippoGridInfo::FALLBACK_GRIDINFO; } -const std::string& HippoGridManager::getDefaultGridNick() const +std::string HippoGridManager::getDefaultGridNick() const +{ + HippoGridInfo* grid = getGrid(mDefaultGrid); + return grid ? grid->getGridNick() : HippoGridInfo::FALLBACK_GRIDINFO.getGridNick(); +} + +std::string HippoGridManager::getCurrentGridNick() const +{ + return getCurrentGrid()->getGridNick(); +} + +const std::string& HippoGridManager::getDefaultGridName() const { return mDefaultGrid; } -const std::string& HippoGridManager::getCurrentGridNick() const +const std::string& HippoGridManager::getCurrentGridName() const { - if (mCurrentGrid.empty()) - { - return mDefaultGrid; - } return mCurrentGrid; } diff --git a/indra/newview/hippogridmanager.h b/indra/newview/hippogridmanager.h index 9562d563f..e5600dfa9 100644 --- a/indra/newview/hippogridmanager.h +++ b/indra/newview/hippogridmanager.h @@ -57,7 +57,7 @@ public: const std::string& getVoiceConnector() const { return mVoiceConnector; } std::string getSearchUrl(SearchType ty, bool is_web) const; bool isRenderCompat() const { return mRenderCompat; } - std::string getGridNick(); + std::string getGridNick() const; int getMaxAgentGroups() const { return mMaxAgentGroups; } const std::string& getCurrencySymbol() const { return mCurrencySymbol; } @@ -99,7 +99,7 @@ public: bool retrieveGridInfo(); static const char* getPlatformString(Platform platform); - static std::string sanitizeGridNick(std::string &gridnick); + static std::string sanitizeGridNick(const std::string &gridnick); static HippoGridInfo FALLBACK_GRIDINFO; static void initFallback(); @@ -163,8 +163,10 @@ public: HippoGridInfo* getConnectedGrid() const { return mConnectedGrid ? mConnectedGrid : getCurrentGrid(); } HippoGridInfo* getCurrentGrid() const; - const std::string& getDefaultGridNick() const; - const std::string& getCurrentGridNick() const; + std::string getDefaultGridNick() const; + std::string getCurrentGridNick() const; + const std::string& getDefaultGridName() const; + const std::string& getCurrentGridName() const; void setDefaultGrid(const std::string& grid); void setCurrentGrid(const std::string& grid); diff --git a/indra/newview/hippopanelgrids.cpp b/indra/newview/hippopanelgrids.cpp index 6af943c5b..c5fe98de2 100644 --- a/indra/newview/hippopanelgrids.cpp +++ b/indra/newview/hippopanelgrids.cpp @@ -164,7 +164,7 @@ BOOL HippoPanelGridsImpl::postBuild() // called internally too void HippoPanelGridsImpl::refresh() { - const std::string &defaultGrid = gHippoGridManager->getDefaultGridNick(); + const std::string &defaultGrid = gHippoGridManager->getDefaultGridName(); LLComboBox *grids = getChild("grid_selector"); S32 selectIndex = -1, i = 0; @@ -365,7 +365,7 @@ bool HippoPanelGridsImpl::saveCurGrid() void HippoPanelGridsImpl::reset() { mState = NORMAL; - mCurGrid = gHippoGridManager->getCurrentGridNick(); + mCurGrid = gHippoGridManager->getCurrentGridName(); loadCurGrid(); } diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 6021f3676..25e984503 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -57,6 +57,7 @@ #include "llsdmessage.h" #include "llsdutil.h" #include "llsky.h" +#include "llslurl.h" #include "llsmoothstep.h" #include "llspeakers.h" #include "llstartup.h" @@ -65,6 +66,8 @@ #include "lltoolpie.h" #include "lltoolmgr.h" #include "lltrans.h" +#include "lluictrl.h" +#include "llurlentry.h" #include "llviewercontrol.h" #include "llviewerdisplay.h" #include "llviewerjoystick.h" @@ -345,6 +348,7 @@ LLAgent::LLAgent() : mAgentAccess(new LLAgentAccess(gSavedSettings)), mGodLevelChangeSignal(), mCanEditParcel(false), + mTeleportSourceSLURL(new LLSLURL), mTeleportRequest(), mTeleportFinishedSlot(), mTeleportFailedSlot(), @@ -485,6 +489,8 @@ LLAgent::~LLAgent() mAgentAccess = NULL; delete mEffectColor; mEffectColor = NULL; + delete mTeleportSourceSLURL; + mTeleportSourceSLURL = NULL; } // Handle any actions that need to be performed when the main app gains focus @@ -949,24 +955,6 @@ const LLHost& LLAgent::getRegionHost() const } } -//----------------------------------------------------------------------------- -// getSLURL() -// returns empty() if getRegion() == NULL -//----------------------------------------------------------------------------- -std::string LLAgent::getSLURL() const -{ - std::string slurl; - LLViewerRegion *regionp = getRegion(); - if (regionp) - { - LLVector3d agentPos = getPositionGlobal(); - S32 x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) ); - S32 y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); - S32 z = llround( (F32)agentPos.mdV[VZ] ); - slurl = LLURLDispatcher::buildSLURL(regionp->getName(), x, y, z); - } - return slurl; -} //----------------------------------------------------------------------------- // inPrelude() @@ -3916,7 +3904,7 @@ bool LLAgent::teleportCore(bool is_local) LLFloaterLand::hideInstance(); LLViewerParcelMgr::getInstance()->deselectLand(); - LLViewerMediaFocus::getInstance()->setFocusFace(false, NULL, 0, NULL); + LLViewerMediaFocus::getInstance()->clearFocus(); // Close all pie menus, deselect land, etc. // Don't change the camera until we know teleport succeeded. JC @@ -4308,7 +4296,7 @@ void LLAgent::setTeleportState(ETeleportState state) case TELEPORT_MOVING: // We're outa here. Save "back" slurl. - mTeleportSourceSLURL = getSLURL(); + LLAgentUI::buildSLURL(*mTeleportSourceSLURL); break; case TELEPORT_ARRIVING: @@ -4734,6 +4722,10 @@ void LLAgent::parseTeleportMessages(const std::string& xml_filename) }//end for (all message sets in xml file) } +const void LLAgent::getTeleportSourceSLURL(LLSLURL& slurl) const +{ + slurl = *mTeleportSourceSLURL; +} void LLAgent::sendAgentUpdateUserInfo(bool im_via_email, const std::string& directory_visibility ) { @@ -4787,14 +4779,13 @@ void LLAgent::renderAutoPilotTarget() } } -void LLAgent::showLureDestination(const std::string fromname, const int global_x, const int global_y, const int x, const int y, const int z, const std::string maturity) +void LLAgent::showLureDestination(const std::string fromname, U64& handle, U32 x, U32 y, U32 z) { - const LLVector3d posglobal = LLVector3d(F64(global_x), F64(global_y), F64(0)); - LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(posglobal); + LLSimInfo* siminfo = LLWorldMap::getInstance()->simInfoFromHandle(handle); if(mPendingLure) delete mPendingLure; - mPendingLure = new SHLureRequest(fromname,posglobal,x,y,z); + mPendingLure = new SHLureRequest(fromname,handle,x,y,z); if(siminfo) //We already have an entry? Go right on to displaying it. { @@ -4802,8 +4793,8 @@ void LLAgent::showLureDestination(const std::string fromname, const int global_x } else { - U16 grid_x = (U16)(global_x / REGION_WIDTH_UNITS); - U16 grid_y = (U16)(global_y / REGION_WIDTH_UNITS); + U32 grid_x, grid_y; + grid_from_region_handle(handle,&grid_x,&grid_y); LLWorldMapMessage::getInstance()->sendMapBlockRequest(grid_x, grid_y, grid_x, grid_y, true); //Will call onFoundLureDestination on response } } @@ -4814,7 +4805,7 @@ void LLAgent::onFoundLureDestination(LLSimInfo *siminfo) return; if(!siminfo) - siminfo = LLWorldMap::getInstance()->simInfoFromPosGlobal(mPendingLure->mPosGlobal); + siminfo = LLWorldMap::getInstance()->simInfoFromHandle(mPendingLure->mRegionHandle); if(siminfo) { const std::string sim_name = siminfo->getName(); @@ -4823,7 +4814,7 @@ void LLAgent::onFoundLureDestination(LLSimInfo *siminfo) llinfos << mPendingLure->mAvatarName << "'s teleport lure is to " << sim_name << " (" << maturity << ")" << llendl; LLStringUtil::format_map_t args; args["[NAME]"] = mPendingLure->mAvatarName; - args["[DESTINATION]"] = LLURLDispatcher::buildSLURL(sim_name, (S32)mPendingLure->mPosLocal[0], (S32)mPendingLure->mPosLocal[1], (S32)mPendingLure->mPosLocal[2] ); + args["[DESTINATION]"] = LLSLURL(sim_name,mPendingLure->mPosLocal).getSLURLString(); std::string msg = LLTrans::getString("TeleportOfferMaturity", args); if (!maturity.empty()) { diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index eb039800e..8a01093bf 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -68,6 +68,7 @@ class LLPickInfo; class LLViewerObject; class LLAgentDropGroupViewerNode; class LLAgentAccess; +class LLSLURL; class LLSimInfo; class LLTeleportRequest; @@ -245,20 +246,18 @@ public: LLViewerRegion *getRegion() const { return mRegionp; } const LLHost& getRegionHost() const; BOOL inPrelude(); - std::string getSLURL() const; //Return uri for current region - + // struct SHLureRequest { - SHLureRequest(const std::string& avatar_name, const LLVector3d& pos_global, const int x, const int y, const int z) : - mAvatarName(avatar_name), mPosGlobal(pos_global) - { mPosLocal[0] = x; mPosLocal[1] = y; mPosLocal[2] = z;} + SHLureRequest(const std::string& avatar_name, U64& handle, const U32 x, const U32 y, const U32 z) : + mAvatarName(avatar_name), mRegionHandle(handle), mPosLocal(x,y,z) {} const std::string mAvatarName; - const LLVector3d mPosGlobal; - int mPosLocal[3]; + const U64 mRegionHandle; + LLVector3 mPosLocal; }; SHLureRequest *mPendingLure; - void showLureDestination(const std::string fromname, const int global_x, const int global_y, const int x, const int y, const int z, const std::string maturity); + void showLureDestination(const std::string fromname, U64& handle, U32 x, U32 y, U32 z); void onFoundLureDestination(LLSimInfo *siminfo = NULL); // @@ -584,13 +583,14 @@ public: public: static void parseTeleportMessages(const std::string& xml_filename); - const std::string& getTeleportSourceSLURL() const { return mTeleportSourceSLURL; } + const void getTeleportSourceSLURL(LLSLURL& slurl) const; public: // ! TODO ! Define ERROR and PROGRESS enums here instead of exposing the mappings. static std::map sTeleportErrorMessages; static std::map sTeleportProgressMessages; -public: - std::string mTeleportSourceSLURL; // SLURL where last TP began. +private: + LLSLURL * mTeleportSourceSLURL; // SLURL where last TP began + //-------------------------------------------------------------------- // Teleport Actions //-------------------------------------------------------------------- diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index eb6b1835b..0fcc5fb2b 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -37,6 +37,7 @@ #include "llviewerregion.h" #include "llviewerparcelmgr.h" #include "llvoavatarself.h" +#include "llslurl.h" // [RLVa:KB] - Checked: 2010-04-04 (RLVa-1.2.0d) #include "rlvhandler.h" // [/RLVa:KB] @@ -48,9 +49,8 @@ void LLAgentUI::buildFullname(std::string& name) name = gAgentAvatarp->getFullname(); } -/* //static -void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /= true/ ) +void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /*= true*/) { LLSLURL return_slurl; LLViewerRegion *regionp = gAgent.getRegion(); @@ -59,7 +59,7 @@ void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /= true/ ) return_slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal()); } slurl = return_slurl; -}*/ +} //static BOOL LLAgentUI::checkAgentDistance(const LLVector3& pole, F32 radius) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 06f84e9cf..5cb16e6d9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -56,6 +56,7 @@ #include "llmodaldialog.h" #include "llpumpio.h" #include "llmimetypes.h" +#include "llslurl.h" #include "llstartup.h" #include "llfocusmgr.h" #include "llviewerjoystick.h" @@ -80,6 +81,7 @@ #include "llvector4a.h" #include "llvoicechannel.h" #include "llvoavatarself.h" +#include "llurlmatch.h" #include "llprogressview.h" #include "llvocache.h" #include "llvopartgroup.h" @@ -94,6 +96,8 @@ #include "llimagej2c.h" #include "llmemory.h" #include "llprimitive.h" +#include "llurlaction.h" +#include "llurlentry.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include @@ -149,7 +153,6 @@ #include "llworld.h" #include "llhudeffecttrail.h" #include "llvectorperfoptions.h" -#include "llurlsimstring.h" #include "llwatchdog.h" // Included so that constants/settings might be initialized @@ -749,9 +752,11 @@ bool LLAppViewer::init() LLWeb::initClass(); // do this after LLUI - LLTextEditor::setURLCallbacks(&LLWeb::loadURL, - &LLURLDispatcher::dispatchFromTextEditor, - &LLURLDispatcher::dispatchFromTextEditor); + // Provide the text fields with callbacks for opening Urls + LLUrlAction::setOpenURLCallback(boost::bind(&LLWeb::loadURL, _1, LLStringUtil::null, LLStringUtil::null)); + LLUrlAction::setOpenURLInternalCallback(boost::bind(&LLWeb::loadURLInternal, _1, LLStringUtil::null, LLStringUtil::null)); + LLUrlAction::setOpenURLExternalCallback(boost::bind(&LLWeb::loadURLExternal, _1, true, LLStringUtil::null)); + LLUrlAction::setExecuteSLURLCallback(&LLURLDispatcher::dispatchFromTextEditor); LLToolMgr::getInstance(); // Initialize tool manager if not already instantiated @@ -969,6 +974,8 @@ bool LLAppViewer::init() LLEnvManagerNew::instance().usePrefs(); gGLActive = FALSE; + LLViewerMedia::initClass(); + LL_INFOS("InitInfo") << "Viewer media initialized." << LL_ENDL ; return true; } @@ -1695,8 +1702,7 @@ bool LLAppViewer::cleanup() if (mPurgeOnExit) { llinfos << "Purging all cache files on exit" << llendflush; - std::string mask = gDirUtilp->getDirDelimiter() + "*.*"; - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),"*.*"); } removeMarkerFile(); // Any crashes from here on we'll just have to ignore @@ -1773,7 +1779,6 @@ bool LLAppViewer::cleanup() //Note: //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() //because some new image might be generated during cleaning up media. --bao - LLViewerMediaFocus::cleanupClass(); LLViewerMedia::cleanupClass(); LLViewerParcelMedia::cleanupClass(); gTextureList.shutdown(); // shutdown again in case a callback added something @@ -2323,30 +2328,17 @@ bool LLAppViewer::initConfiguration() // injection and steal passwords. Phoenix. SL-55321 if(clp.hasOption("url")) { - std::string slurl = clp.getOption("url")[0]; - if (LLURLDispatcher::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } + LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); + if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) + { + gHippoGridManager->setCurrentGrid(LLStartUp::getStartSLURL().getGrid()); + + } } else if(clp.hasOption("slurl")) { - std::string slurl = clp.getOption("slurl")[0]; - if(LLURLDispatcher::isSLURL(slurl)) - { - if (LLURLDispatcher::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } - } + LLSLURL start_slurl(clp.getOption("slurl")[0]); + LLStartUp::setStartSLURL(start_slurl); } const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); @@ -2425,18 +2417,11 @@ bool LLAppViewer::initConfiguration() // don't call anotherInstanceRunning() when doing URL handoff, as // it relies on checking a marker file which will not work when running // out of different directories - std::string slurl; - if (!LLStartUp::sSLURLCommand.empty()) + + if (LLStartUp::getStartSLURL().isValid() && + (gSavedSettings.getBOOL("SLURLPassToOtherInstance"))) { - slurl = LLStartUp::sSLURLCommand; - } - else if (LLURLSimString::parse()) - { - slurl = LLURLSimString::getURL(); - } - if (!slurl.empty()) - { - if (sendURLToOtherInstance(slurl)) + if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) { // successfully handed off URL to existing instance, exit return false; @@ -2492,9 +2477,10 @@ bool LLAppViewer::initConfiguration() // need to do this here - need to have initialized global settings first std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); - if ( nextLoginLocation.length() ) + if ( !nextLoginLocation.empty() ) { - LLURLSimString::setString( nextLoginLocation ); + LL_DEBUGS("AppInit")<<"set start from NextLoginLocation: "<getDirDelimiter() + file_mask; - gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), mask); + gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""), file_mask); } void LLAppViewer::writeSystemInfo() diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index 29f8cfe8e..ce64710fa 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -459,7 +459,7 @@ gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **succ std::string url = slurl; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + if (LLURLDispatcher::dispatch(url, "", web, trusted_browser)) { // bring window to foreground, as it has just been "launched" from a URL // todo: hmm, how to get there from here? diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index da03fc76e..029b0953e 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -43,7 +43,6 @@ #include "llviewernetwork.h" #include "llviewercontrol.h" #include "llmd5.h" -#include "llurlsimstring.h" #include "llfloaterworldmap.h" #include "llurldispatcher.h" #include @@ -476,7 +475,7 @@ OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn) LLMediaCtrl* web = NULL; const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, web, trusted_browser); + LLURLDispatcher::dispatch(url, "", web, trusted_browser); } return(result); diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index 0d71bc3f4..5ccb1f7e6 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -488,7 +488,7 @@ bool LLAppViewerWin32::initHardwareTest() if (OSBTN_NO== button) { LL_INFOS("AppInit") << "User quitting after failed DirectX 9 detection" << LL_ENDL; - LLWeb::loadURLExternal(DIRECTX_9_URL); + LLWeb::loadURLExternal(DIRECTX_9_URL, false); return false; } gSavedSettings.setWarning("AboutDirectX9", FALSE); diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index 7f278fae9..a242a029b 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -30,6 +30,7 @@ #include "llavataractions.h" #include "llavatarnamecache.h" // IDEVO +#include "llnotifications.h" #include "llnotificationsutil.h" // for LLNotificationsUtil #include "roles_constants.h" // for GP_MEMBER_INVITE @@ -46,6 +47,7 @@ #include "lltrans.h" #include "llvoiceclient.h" #include "llweb.h" +#include "llslurl.h" // IDEVO // [RLVa:KB] - Checked: 2011-04-11 (RLVa-1.3.0h) | Added: RLVa-1.3.0h #include "rlvhandler.h" // [/RLVa:KB] @@ -177,7 +179,7 @@ void LLAvatarActions::startIM(const LLUUID& id) if ( (idSession.notNull()) && (!gIMMgr->hasSession(idSession)) ) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", id/*LLSLURL("agent", id, "completename").getSLURLString()*/)); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); return; } } @@ -203,11 +205,10 @@ void LLAvatarActions::endIM(const LLUUID& id) } } -/* Singu TODO: Voice refactor static void on_avatar_name_cache_start_call(const LLUUID& agent_id, const LLAvatarName& av_name) { - LLUUID session_id = gIMMgr->addSession(LLCacheName::cleanFullName(av_name.getLegacyName()), IM_NOTHING_SPECIAL, agent_id, true); + LLUUID session_id = gIMMgr->addSession(LLCacheName::cleanFullName(av_name.getLegacyName()), IM_NOTHING_SPECIAL, agent_id); if (session_id.notNull()) { gIMMgr->startCall(session_id); @@ -231,7 +232,7 @@ void LLAvatarActions::startCall(const LLUUID& id) if ( (idSession.notNull()) && (!gIMMgr->hasSession(idSession)) ) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", id));//LLSLURL("agent", id, "completename").getSLURLString())); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("agent", id, "completename").getSLURLString())); return; } } @@ -258,7 +259,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(idAgent)) ) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", idAgent));//LLSLURL("agent", idAgent, "completename").getSLURLString())); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", LLSLURL("agent", idAgent, "completename").getSLURLString())); return; } id_array.push_back(idAgent); @@ -269,7 +270,7 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) // create the new ad hoc voice session const std::string title = LLTrans::getString("conference-title"); LLUUID session_id = gIMMgr->addSession(title, IM_SESSION_CONFERENCE_START, - ids[0], id_array, true); + ids[0], id_array); if (session_id.isNull()) { return; @@ -279,7 +280,6 @@ void LLAvatarActions::startAdhocCall(const uuid_vec_t& ids) make_ui_sound("UISndStartIM"); } -*/ /* AD *TODO: Is this function needed any more? I fixed it a bit(added check for canCall), but it appears that it is not used @@ -312,7 +312,7 @@ void LLAvatarActions::startConference(const uuid_vec_t& ids) if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(idAgent)) ) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", idAgent/*LLSLURL("agent", idAgent, "completename").getSLURLString()*/)); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTCONF, LLSD().with("RECIPIENT", LLSLURL("agent", idAgent, "completename").getSLURLString())); return; } // [/RLVa:KB] diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index b8fa0a1bf..95e39ee89 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -111,7 +111,7 @@ private: // LLChatBar::LLChatBar() -: LLPanel(LLStringUtil::null, LLRect(), BORDER_NO), +: LLPanel(), mInputEditor(NULL), mGestureLabelTimer(), mLastSpecialChatChannel(0), @@ -509,7 +509,7 @@ void LLChatBar::sendChat( EChatType type ) // static void LLChatBar::startChat(const char* line) { - gChatBar->setVisible(TRUE); + gChatBar->getParent()->setVisible(TRUE); gChatBar->setKeyboardFocus(TRUE); gSavedSettings.setBOOL("ChatVisible", TRUE); @@ -540,7 +540,7 @@ void LLChatBar::stopChat() gAgent.stopTyping(); // hide chat bar so it doesn't grab focus back - gChatBar->setVisible(FALSE); + gChatBar->getParent()->setVisible(FALSE); gSavedSettings.setBOOL("ChatVisible", FALSE); } @@ -896,7 +896,7 @@ class LLChatHandler : public LLCommandHandler { public: // not allowed from outside the app - LLChatHandler() : LLCommandHandler("chat", true) { } + LLChatHandler() : LLCommandHandler("chat", UNTRUSTED_BLOCK) { } // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h index 4ab866f8c..b031e312a 100644 --- a/indra/newview/llchatbar.h +++ b/indra/newview/llchatbar.h @@ -36,6 +36,7 @@ #include "llpanel.h" #include "llframetimer.h" #include "llchat.h" +#include "lllayoutstack.h" class LLLineEditor; class LLMessageSystem; @@ -45,8 +46,7 @@ class LLFrameTimer; class LLChatBarGestureObserver; class LLComboBox; -class LLChatBar -: public LLPanel +class LLChatBar : public LLPanel { public: // constructor for inline chat-bars (e.g. hosted in chat history window) diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp index d42ea03e6..61fcd5d7b 100644 --- a/indra/newview/llcolorswatch.cpp +++ b/indra/newview/llcolorswatch.cpp @@ -351,9 +351,6 @@ LLXMLNodePtr LLColorSwatchCtrl::getXML(bool save_children) const LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("colorswatch"); - node->getAttributeString("name", name); - std::string label; node->getAttributeString("label", label); @@ -372,7 +369,7 @@ LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFa } LLColorSwatchCtrl* color_swatch = new LLColorSwatchCtrl( - name, + "colorswatch", rect, label, color ); diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index a04182a91..679a30891 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -4,46 +4,47 @@ * which manipulate user interface. For example, the command * "agent (uuid) about" will open the UI for an avatar's profile. * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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 "llcommandhandler.h" +#include "llnotificationsutil.h" +//#include "llcommanddispatcherlistener.h" +#include "stringize.h" // system includes #include +#define THROTTLE_PERIOD 5 // required seconds between throttled commands + +//static LLCommandDispatcherListener sCommandDispatcherListener; + //--------------------------------------------------------------------------- // Underlying registry for command handlers, not directly accessible. //--------------------------------------------------------------------------- struct LLCommandHandlerInfo { - bool mRequireTrustedBrowser; + LLCommandHandler::EUntrustedAccess mUntrustedBrowserAccess; LLCommandHandler* mHandler; // safe, all of these are static objects }; @@ -51,14 +52,18 @@ class LLCommandHandlerRegistry { public: static LLCommandHandlerRegistry& instance(); - void add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler); + void add(const char* cmd, + LLCommandHandler::EUntrustedAccess untrusted_access, + LLCommandHandler* handler); bool dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser); private: + friend LLSD LLCommandDispatcher::enumerate(); std::map mMap; }; @@ -72,10 +77,12 @@ LLCommandHandlerRegistry& LLCommandHandlerRegistry::instance() return instance; } -void LLCommandHandlerRegistry::add(const char* cmd, bool require_trusted_browser, LLCommandHandler* handler) +void LLCommandHandlerRegistry::add(const char* cmd, + LLCommandHandler::EUntrustedAccess untrusted_access, + LLCommandHandler* handler) { LLCommandHandlerInfo info; - info.mRequireTrustedBrowser = require_trusted_browser; + info.mUntrustedBrowserAccess = untrusted_access; info.mHandler = handler; mMap[cmd] = info; @@ -85,17 +92,60 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser) { + static bool slurl_blocked = false; + static bool slurl_throttled = false; + static F64 last_throttle_time = 0.0; + F64 cur_time = 0.0; std::map::iterator it = mMap.find(cmd); if (it == mMap.end()) return false; const LLCommandHandlerInfo& info = it->second; - if (!trusted_browser && info.mRequireTrustedBrowser) + if (!trusted_browser) { - // block request from external browser, but report as - // "handled" because it was well formatted. - LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; - return true; + switch (info.mUntrustedBrowserAccess) + { + case LLCommandHandler::UNTRUSTED_ALLOW: + // fall through and let the command be handled + break; + + case LLCommandHandler::UNTRUSTED_BLOCK: + // block request from external browser, but report as + // "handled" because it was well formatted. + LL_WARNS_ONCE("SLURL") << "Blocked SLURL command from untrusted browser" << LL_ENDL; + if (! slurl_blocked) + { + LLNotificationsUtil::add("BlockedSLURL"); + slurl_blocked = true; + } + return true; + + case LLCommandHandler::UNTRUSTED_THROTTLE: + // if users actually click on a link, we don't need to throttle it + // (throttling mechanism is used to prevent an avalanche of clicks via + // javascript + if ( nav_type == "clicked" ) + { + break; + } + + cur_time = LLTimer::getElapsedSeconds(); + if (cur_time < last_throttle_time + THROTTLE_PERIOD) + { + // block request from external browser if it happened + // within THROTTLE_PERIOD seconds of the last command + LL_WARNS_ONCE("SLURL") << "Throttled SLURL command from untrusted browser" << LL_ENDL; + if (! slurl_throttled) + { + LLNotificationsUtil::add("ThrottledSLURL"); + slurl_throttled = true; + } + return true; + } + last_throttle_time = cur_time; + break; + } } if (!info.mHandler) return false; return info.mHandler->handle(params, query_map, web); @@ -106,10 +156,9 @@ bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, //--------------------------------------------------------------------------- LLCommandHandler::LLCommandHandler(const char* cmd, - bool require_trusted_browser) + EUntrustedAccess untrusted_access) { - LLCommandHandlerRegistry::instance().add( - cmd, require_trusted_browser, this); + LLCommandHandlerRegistry::instance().add(cmd, untrusted_access, this); } LLCommandHandler::~LLCommandHandler() @@ -127,8 +176,62 @@ bool LLCommandDispatcher::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser) { return LLCommandHandlerRegistry::instance().dispatch( - cmd, params, query_map, web, trusted_browser); + cmd, params, query_map, web, nav_type, trusted_browser); +} + +static std::string lookup(LLCommandHandler::EUntrustedAccess value); + +LLSD LLCommandDispatcher::enumerate() +{ + LLSD response; + LLCommandHandlerRegistry& registry(LLCommandHandlerRegistry::instance()); + for (std::map::const_iterator chi(registry.mMap.begin()), + chend(registry.mMap.end()); + chi != chend; ++chi) + { + LLSD info; + info["untrusted"] = chi->second.mUntrustedBrowserAccess; + info["untrusted_str"] = lookup(chi->second.mUntrustedBrowserAccess); + response[chi->first] = info; + } + return response; +} + +/*------------------------------ lookup stuff ------------------------------*/ +struct symbol_info +{ + const char* name; + LLCommandHandler::EUntrustedAccess value; +}; + +#define ent(SYMBOL) \ + { \ + #SYMBOL + 28, /* skip "LLCommandHandler::UNTRUSTED_" prefix */ \ + SYMBOL \ + } + +symbol_info symbols[] = +{ + ent(LLCommandHandler::UNTRUSTED_ALLOW), // allow commands from untrusted browsers + ent(LLCommandHandler::UNTRUSTED_BLOCK), // ignore commands from untrusted browsers + ent(LLCommandHandler::UNTRUSTED_THROTTLE) // allow untrusted, but only a few per min. +}; + +#undef ent + +static std::string lookup(LLCommandHandler::EUntrustedAccess value) +{ + for (symbol_info *sii(symbols), *siend(symbols + (sizeof(symbols)/sizeof(symbols[0]))); + sii != siend; ++sii) + { + if (sii->value == value) + { + return sii->name; + } + } + return STRINGIZE("UNTRUSTED_" << value); } diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h index 5cb3ee73d..1e0895565 100644 --- a/indra/newview/llcommandhandler.h +++ b/indra/newview/llcommandhandler.h @@ -4,36 +4,32 @@ * which manipulate user interface. For example, the command * "agent (uuid) about" will open the UI for an avatar's profile. * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ #ifndef LLCOMMANDHANDLER_H #define LLCOMMANDHANDLER_H +#include "llsd.h" + /* Example: secondlife:///app/foo/ Command "foo" that takes one parameter, a UUID. @@ -43,7 +39,7 @@ public: // Inform the system you handle commands starting // with "foo" and they are only allowed from // "trusted" (pointed at Linden content) browsers - LLFooHandler() : LLCommandHandler("foo", true) { } + LLFooHandler() : LLCommandHandler("foo", UNTRUSTED_BLOCK) { } // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, @@ -65,7 +61,14 @@ class LLMediaCtrl; class LLCommandHandler { public: - LLCommandHandler(const char* command, bool allow_from_untrusted_browser); + enum EUntrustedAccess + { + UNTRUSTED_ALLOW, // allow commands from untrusted browsers + UNTRUSTED_BLOCK, // ignore commands from untrusted browsers + UNTRUSTED_THROTTLE // allow untrusted, but only a few per min. + }; + + LLCommandHandler(const char* command, EUntrustedAccess untrusted_access); // Automatically registers object to get called when // command is executed. All commands can be processed // in links from LLMediaCtrl, but some (like teleport) @@ -92,10 +95,14 @@ public: const LLSD& params, const LLSD& query_map, LLMediaCtrl* web, + const std::string& nav_type, bool trusted_browser); // Execute a command registered via the above mechanism, // passing string parameters. // Returns true if command was found and executed correctly. + /// Return an LLSD::Map of registered LLCommandHandlers and associated + /// info (e.g. EUntrustedAccess). + static LLSD enumerate(); }; #endif diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 95245b8f0..5d0371601 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -190,8 +190,9 @@ void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp) mImportanceToCamera = 0.f ; mBoundingSphereRadius = 0.0f ; -} + mHasMedia = FALSE ; +} void LLFace::destroy() { @@ -2065,6 +2066,20 @@ BOOL LLFace::getGeometryVolume(const LLVolume& volume, return TRUE; } +//check if the face has a media +BOOL LLFace::hasMedia() const +{ + if(mHasMedia) + { + return TRUE ; + } + if(mTexture.notNull()) + { + return mTexture->hasParcelMedia() ; //if has a parcel media + } + + return FALSE ; //no media. +} const F32 LEAST_IMPORTANCE = 0.05f ; const F32 LEAST_IMPORTANCE_FOR_LARGE_IMAGE = 0.3f ; @@ -2134,7 +2149,7 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) t.load3(camera->getOrigin().mV); lookAt.setSub(center, t); F32 dist = lookAt.getLength3().getF32(); - dist = llmax(dist-size.getLength3().getF32(), 0.f); + dist = llmax(dist-size.getLength3().getF32(), 0.001f); lookAt.normalize3fast() ; //get area of circle around node @@ -2145,9 +2160,33 @@ BOOL LLFace::calcPixelArea(F32& cos_angle_to_view_dir, F32& radius) x_axis.load3(camera->getXAxis().mV); cos_angle_to_view_dir = lookAt.dot3(x_axis).getF32(); + //if has media, check if the face is out of the view frustum. + if(hasMedia()) + { + if(!camera->AABBInFrustum(center, size)) + { + mImportanceToCamera = 0.f ; + return false ; + } + if(cos_angle_to_view_dir > camera->getCosHalfFov()) //the center is within the view frustum + { + cos_angle_to_view_dir = 1.0f ; + } + else + { + LLVector4a d; + d.setSub(lookAt, x_axis); + + if(dist * dist * d.dot3(d) < size_squared) + { + cos_angle_to_view_dir = 1.0f ; + } + } + } + if(dist < mBoundingSphereRadius) //camera is very close { - cos_angle_to_view_dir = 1.0f; + cos_angle_to_view_dir = 1.0f ; mImportanceToCamera = 1.0f; } else diff --git a/indra/newview/llface.h b/indra/newview/llface.h index b59dff8f0..68cdcadde 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -224,6 +224,9 @@ public: F32 getTextureVirtualSize() ; F32 getImportanceToCamera()const {return mImportanceToCamera ;} + + void setHasMedia(bool has_media) { mHasMedia = has_media ;} + BOOL hasMedia() const ; //vertex buffer tracking void setVertexBuffer(LLVertexBuffer* buffer); void clearVertexBuffer(); //sets mVertexBuffer and mLastVertexBuffer to NULL @@ -289,6 +292,7 @@ private: //based on the distance from the face to the view point and the angle from the face center to the view direction. F32 mImportanceToCamera ; F32 mBoundingSphereRadius ; + bool mHasMedia ; protected: static BOOL sSafeRenderSelect; diff --git a/indra/newview/llfloateractivespeakers.cpp b/indra/newview/llfloateractivespeakers.cpp index 7d9706ad4..077951e3e 100644 --- a/indra/newview/llfloateractivespeakers.cpp +++ b/indra/newview/llfloateractivespeakers.cpp @@ -34,12 +34,22 @@ #include "llfloateractivespeakers.h" #include "llparticipantlist.h" +#include "llpanelvoiceeffect.h" #include "llspeakers.h" #include "lluictrlfactory.h" +namespace +{ + void* createEffectPanel(void*) + { + return new LLPanelVoiceEffect; + } +} + LLFloaterActiveSpeakers::LLFloaterActiveSpeakers(const LLSD& seed) : mPanel(NULL) { mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL); + mFactoryMap["panel_voice_effect"] = LLCallbackMap(createEffectPanel, NULL); // do not automatically open singleton floaters (as result of getInstance()) BOOL no_open = FALSE; LLUICtrlFactory::getInstance()->buildFloater(this, "floater_active_speakers.xml", &getFactoryMap(), no_open); diff --git a/indra/newview/llfloaterbeacons.cpp b/indra/newview/llfloaterbeacons.cpp index b8d457fa9..696708a50 100644 --- a/indra/newview/llfloaterbeacons.cpp +++ b/indra/newview/llfloaterbeacons.cpp @@ -36,7 +36,6 @@ #include "llviewercontrol.h" #include "lluictrlfactory.h" #include "llcheckboxctrl.h" -#include "llsliderctrl.h" #include "pipeline.h" // [RLVa:KB] @@ -45,8 +44,6 @@ LLFloaterBeacons::LLFloaterBeacons(const LLSD& seed) { - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_beacons.xml"); - // Initialize pipeline states from saved settings. // OK to do at floater constructor time because beacons do not display unless the floater is open // therefore it is OK to not initialize the pipeline state before needed. @@ -59,20 +56,14 @@ LLFloaterBeacons::LLFloaterBeacons(const LLSD& seed) LLPipeline::setRenderParticleBeacons( gSavedSettings.getBOOL("particlesbeacon")); LLPipeline::setRenderHighlights( gSavedSettings.getBOOL("renderhighlights")); LLPipeline::setRenderBeacons( gSavedSettings.getBOOL("renderbeacons")); - getChild("beacon_width_label")->setEnabled(gSavedSettings.getBOOL("renderbeacons")); - getChild("beacon_width")->setEnabled(gSavedSettings.getBOOL("renderbeacons")); + LLPipeline::setRenderMOAPBeacons( gSavedSettings.getBOOL("moapbeacon")); + mCommitCallbackRegistrar.add("Beacons.UICheck", boost::bind(&LLFloaterBeacons::onClickUICheck, this,_1)); + + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_beacons.xml"); } BOOL LLFloaterBeacons::postBuild() { - childSetCommitCallback("always_on", onClickUICheck, this); - childSetCommitCallback("touch_only", onClickUICheck, this); - childSetCommitCallback("scripted", onClickUICheck, this); - childSetCommitCallback("physical", onClickUICheck, this); - childSetCommitCallback("sounds", onClickUICheck, this); - childSetCommitCallback("particles", onClickUICheck, this); - childSetCommitCallback("highlights", onClickUICheck, this); - childSetCommitCallback("beacons", onClickUICheck, this); return TRUE; } @@ -105,64 +96,59 @@ void LLFloaterBeacons::close(bool app_quitting) // Callback attached to each check box control to both affect their main purpose // and to implement the couple screwy interdependency rules that some have. -//static -void LLFloaterBeacons::onClickUICheck(LLUICtrl *ctrl, void* data) + +void LLFloaterBeacons::onClickUICheck(LLUICtrl *ctrl) { LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl; std::string name = check->getName(); - LLFloaterBeacons* view = (LLFloaterBeacons*)data; - if (name == "always_on") gSavedSettings.setBOOL("BeaconsKeepVisible", check->get()); - else if (name == "touch_only") LLPipeline::setRenderScriptedTouchBeacons(check->get()); - else if (name == "scripted") LLPipeline::setRenderScriptedBeacons(check->get()); - else if (name == "physical") LLPipeline::setRenderPhysicalBeacons(check->get()); - else if (name == "sounds") LLPipeline::setRenderSoundBeacons(check->get()); - else if (name == "particles") LLPipeline::setRenderParticleBeacons(check->get()); - else if (name == "highlights") LLPipeline::setRenderHighlights(check->get()); - else if (name == "beacons") - { - bool enabled = check->get(); - LLPipeline::setRenderBeacons(enabled); - view->getChild("beacon_width_label")->setEnabled(enabled); - view->getChild("beacon_width")->setEnabled(enabled); - } - - if (check->get()) + if (name == "touch_only") { + LLPipeline::toggleRenderScriptedTouchBeacons(NULL); // Don't allow both to be ON at the same time. Toggle the other one off if both now on. if (LLPipeline::getRenderScriptedTouchBeacons(NULL) && LLPipeline::getRenderScriptedBeacons(NULL) ) { - if (name == "touch_only") - { - LLPipeline::setRenderScriptedBeacons(FALSE); - view->getChild("scripted")->setControlValue(LLSD(FALSE)); - } - else - { - LLPipeline::setRenderScriptedTouchBeacons(FALSE); - view->getChild("touch_only")->setControlValue(LLSD(FALSE)); - } + LLPipeline::setRenderScriptedBeacons(FALSE); + getChild("scripted")->setControlValue(LLSD(FALSE)); } } - else + + else if (name == "scripted") + { + LLPipeline::toggleRenderScriptedBeacons(NULL); + // Don't allow both to be ON at the same time. Toggle the other one off if both now on. + if (LLPipeline::getRenderScriptedTouchBeacons(NULL) && + LLPipeline::getRenderScriptedBeacons(NULL) ) + { + LLPipeline::setRenderScriptedTouchBeacons(FALSE); + getChild("touch_only")->setControlValue(LLSD(FALSE)); + } + } + else if (name == "physical") LLPipeline::setRenderPhysicalBeacons(check->get()); + else if (name == "sounds") LLPipeline::setRenderSoundBeacons(check->get()); + else if (name == "particles") LLPipeline::setRenderParticleBeacons(check->get()); + else if (name == "moapbeacon") LLPipeline::setRenderMOAPBeacons(check->get()); + else if (name == "highlights") { + LLPipeline::toggleRenderHighlights(NULL); // Don't allow both to be OFF at the same time. Toggle the other one on if both now off. if (!LLPipeline::getRenderBeacons(NULL) && !LLPipeline::getRenderHighlights(NULL)) { - if (name == "highlights") - { - LLPipeline::setRenderBeacons(TRUE); - view->getChild("beacons")->setControlValue(LLSD(TRUE)); - view->getChild("beacon_width_label")->setEnabled(TRUE); - view->getChild("beacon_width")->setEnabled(TRUE); - } - else - { - LLPipeline::setRenderHighlights(TRUE); - view->getChild("highlights")->setControlValue(LLSD(TRUE)); - } + LLPipeline::setRenderBeacons(TRUE); + getChild("beacons")->setControlValue(LLSD(TRUE)); + } + } + else if (name == "beacons") + { + LLPipeline::toggleRenderBeacons(NULL); + // Don't allow both to be OFF at the same time. Toggle the other one on if both now off. + if (!LLPipeline::getRenderBeacons(NULL) && + !LLPipeline::getRenderHighlights(NULL)) + { + LLPipeline::setRenderHighlights(TRUE); + getChild("highlights")->setControlValue(LLSD(TRUE)); } } } diff --git a/indra/newview/llfloaterbeacons.h b/indra/newview/llfloaterbeacons.h index c7a7b8803..03ec20d41 100644 --- a/indra/newview/llfloaterbeacons.h +++ b/indra/newview/llfloaterbeacons.h @@ -46,11 +46,10 @@ public: // Needed to make the floater visibility toggle the beacons. /*virtual*/ void open(); /*virtual*/ void close(bool app_quitting); + void onClickUICheck(LLUICtrl *ctrl); private: LLFloaterBeacons(const LLSD& seed); - - static void onClickUICheck(LLUICtrl *ctrl, void* data); }; #endif diff --git a/indra/newview/llfloaterclassified.cpp b/indra/newview/llfloaterclassified.cpp index 07603039b..f695da399 100644 --- a/indra/newview/llfloaterclassified.cpp +++ b/indra/newview/llfloaterclassified.cpp @@ -55,7 +55,7 @@ class LLClassifiedHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLClassifiedHandler() : LLCommandHandler("classified", true) { } + LLClassifiedHandler() : LLCommandHandler("classified", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloaterdaycycle.cpp b/indra/newview/llfloaterdaycycle.cpp index 0873c0351..3cf67821f 100644 --- a/indra/newview/llfloaterdaycycle.cpp +++ b/indra/newview/llfloaterdaycycle.cpp @@ -123,7 +123,7 @@ void LLFloaterDayCycle::onClickHelp(void* data) LLFloaterDayCycle* self = LLFloaterDayCycle::instance(); std::string xml_alert = *(std::string *) data; - LLNotifications::instance().add(self->contextualNotification(xml_alert)); + self->addContextualNotification(xml_alert); } void LLFloaterDayCycle::initHelpBtn(const std::string& name, const std::string& xml_alert) diff --git a/indra/newview/llfloaterdirectory.cpp b/indra/newview/llfloaterdirectory.cpp index cd3c85b5f..a696e61ff 100644 --- a/indra/newview/llfloaterdirectory.cpp +++ b/indra/newview/llfloaterdirectory.cpp @@ -71,6 +71,8 @@ #include "llviewerregion.h" #include "llwindow.h" +#include "llnotifications.h" + const char* market_panel = "market_panel"; class LLPanelDirMarket : public LLPanelDirFind diff --git a/indra/newview/llfloaterenvsettings.cpp b/indra/newview/llfloaterenvsettings.cpp index 9e59cf046..57a952244 100644 --- a/indra/newview/llfloaterenvsettings.cpp +++ b/indra/newview/llfloaterenvsettings.cpp @@ -70,7 +70,7 @@ LLFloaterEnvSettings::~LLFloaterEnvSettings() void LLFloaterEnvSettings::onClickHelp(void* data) { LLFloaterEnvSettings* self = (LLFloaterEnvSettings*)data; - LLNotifications::instance().add(self->contextualNotification("EnvSettingsHelpButton")); + self->addContextualNotification("EnvSettingsHelpButton"); } void LLFloaterEnvSettings::initCallbacks(void) diff --git a/indra/newview/llfloaterevent.cpp b/indra/newview/llfloaterevent.cpp index 0ec2a7637..b526767f8 100644 --- a/indra/newview/llfloaterevent.cpp +++ b/indra/newview/llfloaterevent.cpp @@ -56,7 +56,7 @@ class LLEventHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLEventHandler() : LLCommandHandler("event", true) { } + LLEventHandler() : LLCommandHandler("event", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloaterhandler.h b/indra/newview/llfloaterhandler.h index 31ea80c12..cd9d8b537 100644 --- a/indra/newview/llfloaterhandler.h +++ b/indra/newview/llfloaterhandler.h @@ -38,7 +38,7 @@ class LLFloaterHandler : public LLCommandHandler { public: - LLFloaterHandler() : LLCommandHandler("floater", true) { } + LLFloaterHandler() : LLCommandHandler("floater", UNTRUSTED_BLOCK) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web); }; diff --git a/indra/newview/llfloaterhtmlsimple.cpp b/indra/newview/llfloaterhtmlsimple.cpp index 8091c1e97..67650be48 100644 --- a/indra/newview/llfloaterhtmlsimple.cpp +++ b/indra/newview/llfloaterhtmlsimple.cpp @@ -63,5 +63,5 @@ void LLFloaterHtmlSimple::navigateTo(const std::string &url) void LLFloaterHtmlSimple::setTrusted(bool trusted) { LLMediaCtrl* web = getChild("browser"); - web->setTrusted(trusted); + web->setTrustedContent(trusted); } diff --git a/indra/newview/llfloaterhud.cpp b/indra/newview/llfloaterhud.cpp index cb8071588..f2743e0cf 100644 --- a/indra/newview/llfloaterhud.cpp +++ b/indra/newview/llfloaterhud.cpp @@ -78,7 +78,7 @@ LLFloaterHUD::LLFloaterHUD() if (mWebBrowser) { // Open links in internal browser - mWebBrowser->setOpenInExternalBrowser(false); + //mWebBrowser->setOpenInExternalBrowser(false); // This is a "chrome" floater, so we don't want anything to // take focus (as the user needs to be able to walk with diff --git a/indra/newview/llfloatermediabrowser.cpp b/indra/newview/llfloatermediabrowser.cpp deleted file mode 100644 index 62fa849dd..000000000 --- a/indra/newview/llfloatermediabrowser.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/** - * @file llfloaterhtmlhelp.cpp - * @brief HTML Help floater - uses embedded web browser control - * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-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 "llfloatermediabrowser.h" -#include "llfloaterhtml.h" - -#include "llparcel.h" -#include "llpluginclassmedia.h" -#include "lluictrlfactory.h" -#include "llmediactrl.h" -#include "llviewerwindow.h" -#include "llviewercontrol.h" -#include "llviewerparcelmgr.h" -#include "llweb.h" -#include "llui.h" -#include "roles_constants.h" -#include "llwindow.h" - -#include "llurlhistory.h" -#include "llmediactrl.h" -#include "llviewermedia.h" -#include "llviewerparcelmedia.h" -#include "llcombobox.h" -#include "llnotificationsutil.h" - -// TEMP -#include "llsdutil.h" - -LLFloaterMediaBrowser::LLFloaterMediaBrowser(const LLSD& media_data) -{ - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_media_browser.xml"); - -} - - -void LLFloaterMediaBrowser::geometryChanged(S32 x, S32 y, S32 width, S32 height) -{ - // Make sure the layout of the browser control is updated, so this calculation is correct. - LLLayoutStack::updateClass(); - - // TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc. - LLCoordWindow window_size; - getWindow()->getSize(&window_size); - - // Adjust width and height for the size of the chrome on the Media Browser window. - width += getRect().getWidth() - mBrowser->getRect().getWidth(); - height += getRect().getHeight() - mBrowser->getRect().getHeight(); - - LLRect geom; - geom.setOriginAndSize(x, window_size.mY - (y + height), width, height); - - lldebugs << "geometry change: " << geom << llendl; - - handleReshape(geom,false); -} - -void LLFloaterMediaBrowser::draw() -{ - childSetEnabled("go", !mAddressCombo->getValue().asString().empty()); - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(parcel) - { - childSetVisible("parcel_owner_controls", LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_MEDIA)); - childSetEnabled("assign", !mAddressCombo->getValue().asString().empty()); - } - bool show_time_controls = false; - bool media_playing = false; - if(mBrowser) - { - LLPluginClassMedia* media_plugin = mBrowser->getMediaPlugin(); - if(media_plugin) - { - show_time_controls = media_plugin->pluginSupportsMediaTime(); - media_playing = media_plugin->getStatus() == LLPluginClassMediaOwner::MEDIA_PLAYING; - } - } - childSetVisible("time_controls", show_time_controls); - childSetVisible("rewind", show_time_controls); - childSetVisible("play", show_time_controls && ! media_playing); - childSetVisible("pause", show_time_controls && media_playing); - childSetVisible("stop", show_time_controls); - childSetVisible("seek", show_time_controls); - - childSetEnabled("play", ! media_playing); - childSetEnabled("stop", media_playing); - - childSetEnabled("back", mBrowser->canNavigateBack()); - childSetEnabled("forward", mBrowser->canNavigateForward()); - - LLFloater::draw(); -} - -BOOL LLFloaterMediaBrowser::postBuild() -{ - mBrowser = getChild("browser"); - mBrowser->addObserver(this); - - mAddressCombo = getChild("address"); - mAddressCombo->setCommitCallback(onEnterAddress, this); - mAddressCombo->sortByName(); - - childSetAction("back", onClickBack, this); - childSetAction("forward", onClickForward, this); - childSetAction("reload", onClickRefresh, this); - childSetAction("rewind", onClickRewind, this); - childSetAction("play", onClickPlay, this); - childSetAction("stop", onClickStop, this); - childSetAction("pause", onClickPlay, this); - childSetAction("seek", onClickSeek, this); - childSetAction("go", onClickGo, this); - childSetAction("open_browser", onClickOpenWebBrowser, this); - childSetAction("assign", onClickAssign, this); - - buildURLHistory(); - return TRUE; -} - -void LLFloaterMediaBrowser::buildURLHistory() -{ - LLCtrlListInterface* url_list = childGetListInterface("address"); - if (url_list) - { - url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); - } - - // Get all of the entries in the "browser" collection - LLSD browser_history = LLURLHistory::getURLHistory("browser"); - - LLSD::array_iterator iter_history = - browser_history.beginArray(); - LLSD::array_iterator end_history = - browser_history.endArray(); - for(; iter_history != end_history; ++iter_history) - { - std::string url = (*iter_history).asString(); - if(! url.empty()) - url_list->addSimpleElement(url); - } - - // initialize URL history in the plugin - if(mBrowser && mBrowser->getMediaPlugin()) - { - mBrowser->getMediaPlugin()->initializeUrlHistory(browser_history); - } -} - -std::string LLFloaterMediaBrowser::getSupportURL() -{ - return getString("support_page_url"); -} -void LLFloaterMediaBrowser::onClose(bool app_quitting) -{ - //setVisible(FALSE); - destroy(); -} - -void LLFloaterMediaBrowser::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) -{ - if(event == MEDIA_EVENT_LOCATION_CHANGED) - { - setCurrentURL(self->getLocation()); - } - else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) - { - // This is the event these flags are sent with. - childSetEnabled("back", self->getHistoryBackAvailable()); - childSetEnabled("forward", self->getHistoryForwardAvailable()); - } - else if(event == MEDIA_EVENT_CLOSE_REQUEST) - { - // The browser instance wants its window closed. - close(); - } - else if(event == MEDIA_EVENT_GEOMETRY_CHANGE) - { - geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); - } -} -void LLFloaterMediaBrowser::setCurrentURL(const std::string& url) -{ - mCurrentURL = url; - - // redirects will navigate momentarily to about:blank, don't add to history - if (mCurrentURL != "about:blank") - { - mAddressCombo->remove(mCurrentURL); - mAddressCombo->add(mCurrentURL, ADD_SORTED); - mAddressCombo->selectByValue(mCurrentURL); - - // Serialize url history - LLURLHistory::removeURL("browser", mCurrentURL); - LLURLHistory::addURL("browser", mCurrentURL); - } - childSetEnabled("back", mBrowser->canNavigateBack()); - childSetEnabled("forward", mBrowser->canNavigateForward()); - childSetEnabled("reload", TRUE); -} - -LLFloaterMediaBrowser* LLFloaterMediaBrowser::showInstance(const LLSD& media_url) -{ - LLFloaterMediaBrowser* floaterp = LLUISingleton >::showInstance(media_url); - - floaterp->openMedia(media_url.asString()); - return floaterp; -} - -//static -void LLFloaterMediaBrowser::onEnterAddress(LLUICtrl* ctrl, void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - self->mBrowser->navigateTo(self->mAddressCombo->getValue().asString()); -} - -//static -void LLFloaterMediaBrowser::onClickRefresh(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - self->mAddressCombo->remove(0); - if( self->mBrowser->getMediaPlugin() && self->mBrowser->getMediaPlugin()->pluginSupportsMediaBrowser()) - { - bool ignore_cache = true; - self->mBrowser->getMediaPlugin()->browse_reload( ignore_cache ); - } - else - { - self->mBrowser->navigateTo(self->mCurrentURL); - } -} - -//static -void LLFloaterMediaBrowser::onClickForward(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - self->mBrowser->navigateForward(); -} - -//static -void LLFloaterMediaBrowser::onClickBack(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - self->mBrowser->navigateBack(); -} - -//static -void LLFloaterMediaBrowser::onClickGo(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - self->mBrowser->navigateTo(self->mAddressCombo->getValue().asString()); -} - -//static -void LLFloaterMediaBrowser::onClickOpenWebBrowser(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - std::string url = self->mCurrentURL.empty() ? - self->mBrowser->getHomePageUrl() : - self->mCurrentURL; - LLWeb::loadURLExternal(url); -} - -void LLFloaterMediaBrowser::onClickAssign(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (!parcel) - { - return; - } - std::string media_url = self->mAddressCombo->getValue().asString(); - LLStringUtil::trim(media_url); - - if(parcel->getMediaType() != "text/html") - { - parcel->setMediaURL(media_url); - parcel->setMediaCurrentURL(media_url); - parcel->setMediaType(std::string("text/html")); - LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel, true ); - LLViewerParcelMedia::sendMediaNavigateMessage(media_url); - LLViewerParcelMedia::stop(); - // LLViewerParcelMedia::update( parcel ); - } - LLViewerParcelMedia::sendMediaNavigateMessage(media_url); -} -//static -void LLFloaterMediaBrowser::onClickRewind(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - if(self->mBrowser->getMediaPlugin()) - self->mBrowser->getMediaPlugin()->start(-2.0f); -} -//static -void LLFloaterMediaBrowser::onClickPlay(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - LLPluginClassMedia* plugin = self->mBrowser->getMediaPlugin(); - if(plugin) - { - if(plugin->getStatus() == LLPluginClassMediaOwner::MEDIA_PLAYING) - { - plugin->pause(); - } - else - { - plugin->start(); - } - } -} -//static -void LLFloaterMediaBrowser::onClickStop(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - if(self->mBrowser->getMediaPlugin()) - self->mBrowser->getMediaPlugin()->stop(); -} -//static -void LLFloaterMediaBrowser::onClickSeek(void* user_data) -{ - LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; - - if(self->mBrowser->getMediaPlugin()) - self->mBrowser->getMediaPlugin()->start(2.0f); -} -void LLFloaterMediaBrowser::openMedia(const std::string& media_url) -{ - mBrowser->setHomePageUrl(media_url); - mBrowser->navigateTo(media_url); - setCurrentURL(media_url); -} -//////////////////////////////////////////////////////////////////////////////// -// - -LLViewerHtmlHelp gViewerHtmlHelp; - - -//////////////////////////////////////////////////////////////////////////////// -// -LLViewerHtmlHelp::LLViewerHtmlHelp() -{ - - LLUI::setHtmlHelp(this); -} - -LLViewerHtmlHelp::~LLViewerHtmlHelp() -{ - - LLUI::setHtmlHelp(NULL); -} - -void LLViewerHtmlHelp::show() -{ - show(""); -} - -void LLViewerHtmlHelp::show(std::string url) -{ - LLFloaterMediaBrowser* floater_html = LLFloaterMediaBrowser::getInstance(); - floater_html->setVisible(FALSE); - - if (url.empty()) - { - url = floater_html->getSupportURL(); - } - - if (gSavedSettings.getBOOL("UseExternalBrowser")) - { - LLSD notificationData; - notificationData["url"] = url; - - LLNotificationsUtil::add("ClickOpenF1Help", notificationData, LLSD(), onClickF1HelpLoadURL); - floater_html->close(); - } - else - { - // don't wait, just do it - floater_html->setVisible(TRUE); - floater_html->openMedia(url); - } -} -// static -bool LLViewerHtmlHelp::onClickF1HelpLoadURL(const LLSD& notification, const LLSD& response) -{ - LLFloaterMediaBrowser* floater_html = LLFloaterMediaBrowser::getInstance(); - floater_html->setVisible(FALSE); - std::string url = floater_html->getSupportURL(); - S32 option = LLNotification::getSelectedOption(notification, response); - if (option == 0) - { - LLWeb::loadURL(url); - } - floater_html->close(); - return false; -} - diff --git a/indra/newview/llfloatermediabrowser.h b/indra/newview/llfloatermediabrowser.h deleted file mode 100644 index c2203670a..000000000 --- a/indra/newview/llfloatermediabrowser.h +++ /dev/null @@ -1,104 +0,0 @@ -/** - * @file llfloaterhtmlhelp.h - * @brief HTML Help floater - uses embedded web browser control - * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-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$ - */ - -#ifndef LL_LLFLOATERHTMLHELP_H -#define LL_LLFLOATERHTMLHELP_H - -#include "llhtmlhelp.h" -#include "llfloater.h" -#include "llmediactrl.h" - -class LLViewerHtmlHelp : public LLHtmlHelp -{ -public: - LLViewerHtmlHelp(); - virtual ~LLViewerHtmlHelp(); - - /*virtual*/ void show(); - /*virtual*/ void show(std::string start_url); - void show(std::string start_url, std::string title); - - static bool onClickF1HelpLoadURL(const LLSD& notification, const LLSD& response); - -}; - -class LLComboBox; -class LLMediaCtrl; - -class LLFloaterMediaBrowser : - public LLFloater, - public LLUISingleton >, - public LLViewerMediaObserver -{ - friend class LLUISingleton >; -public: - LLFloaterMediaBrowser(const LLSD& media_data); - - - void geometryChanged(S32 x, S32 y, S32 width, S32 height); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onClose(bool app_quitting); - /*virtual*/ void draw(); - - // inherited from LLViewerMediaObserver - /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); - - void openMedia(const std::string& media_url); - void buildURLHistory(); - std::string getSupportURL(); - void setCurrentURL(const std::string& url); - - static LLFloaterMediaBrowser* showInstance(const LLSD& id); - static void onEnterAddress(LLUICtrl* ctrl, void* user_data); - static void onClickRefresh(void* user_data); - static void onClickBack(void* user_data); - static void onClickForward(void* user_data); - static void onClickGo(void* user_data); - static void onClickOpenWebBrowser(void* user_data); - static void onClickAssign(void* user_data); - static void onClickRewind(void* user_data); - static void onClickPlay(void* user_data); - static void onClickStop(void* user_data); - static void onClickSeek(void* user_data); - -private: - LLMediaCtrl* mBrowser; - LLComboBox* mAddressCombo; - std::string mCurrentURL; -}; - -extern LLViewerHtmlHelp gViewerHtmlHelp; - -#endif // LL_LLFLOATERHTMLHELP_H - diff --git a/indra/newview/llfloatermediasettings.cpp b/indra/newview/llfloatermediasettings.cpp new file mode 100644 index 000000000..151ed53b6 --- /dev/null +++ b/indra/newview/llfloatermediasettings.cpp @@ -0,0 +1,319 @@ +/** + * @file llfloatermediasettings.cpp + * @brief Tabbed dialog for media settings - 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 "llfloatermediasettings.h" +#include "llfloaterwhitelistentry.h" +#include "llpanelmediasettingsgeneral.h" +#include "llpanelmediasettingssecurity.h" +#include "llpanelmediasettingspermissions.h" +#include "llviewercontrol.h" +#include "lluictrlfactory.h" +#include "llbutton.h" +#include "llselectmgr.h" +#include "llsdutil.h" + +LLFloaterMediaSettings* LLFloaterMediaSettings::sInstance = NULL; + +//////////////////////////////////////////////////////////////////////////////// +// +LLFloaterMediaSettings::LLFloaterMediaSettings(const LLSD& key) + : LLFloater(key), + mTabContainer(NULL), + mPanelMediaSettingsGeneral(NULL), + mPanelMediaSettingsSecurity(NULL), + mPanelMediaSettingsPermissions(NULL), + mWaitingToClose( false ), + mIdenticalHasMediaInfo( true ), + mMultipleMedia(false), + mMultipleValidMedia(false) +{ + LLUICtrlFactory::getInstance()->buildFloater(this,"floater_media_settings.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLFloaterMediaSettings::~LLFloaterMediaSettings() +{ + if ( mPanelMediaSettingsGeneral ) + { + delete mPanelMediaSettingsGeneral; + mPanelMediaSettingsGeneral = NULL; + } + + if ( mPanelMediaSettingsSecurity ) + { + delete mPanelMediaSettingsSecurity; + mPanelMediaSettingsSecurity = NULL; + } + + if ( mPanelMediaSettingsPermissions ) + { + delete mPanelMediaSettingsPermissions; + mPanelMediaSettingsPermissions = NULL; + } + + sInstance = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLFloaterMediaSettings::postBuild() +{ + mApplyBtn = getChild("Apply"); + mApplyBtn->setClickedCallback(onBtnApply, this); + + mCancelBtn = getChild("Cancel"); + mCancelBtn->setClickedCallback(onBtnCancel, this); + + mOKBtn = getChild("OK"); + mOKBtn->setClickedCallback(onBtnOK, this); + + mTabContainer = getChild( "tab_container" ); + + mPanelMediaSettingsGeneral = new LLPanelMediaSettingsGeneral(); + mTabContainer->addTabPanel( mPanelMediaSettingsGeneral, mPanelMediaSettingsGeneral->getLabel() ); + mPanelMediaSettingsGeneral->setParent( this ); + + // note that "permissions" tab is really "Controls" tab - refs to 'perms' and + // 'permissions' not changed to 'controls' since we don't want to change + // shared files in server code and keeping everything the same seemed best. + mPanelMediaSettingsPermissions = new LLPanelMediaSettingsPermissions(); + mTabContainer->addTabPanel( mPanelMediaSettingsPermissions, mPanelMediaSettingsPermissions->getLabel() ); + + mPanelMediaSettingsSecurity = new LLPanelMediaSettingsSecurity(); + mTabContainer->addTabPanel( mPanelMediaSettingsSecurity, mPanelMediaSettingsSecurity->getLabel() ); + mPanelMediaSettingsSecurity->setParent( this ); + + // restore the last tab viewed from persistance variable storage + if (!mTabContainer->selectTab(gSavedSettings.getS32("LastMediaSettingsTab"))) + { + mTabContainer->selectFirstTab(); + }; + + sInstance = this; + + return TRUE; +} + +//static +LLFloaterMediaSettings* LLFloaterMediaSettings::getInstance() +{ + if ( !sInstance ) + { + sInstance = new LLFloaterMediaSettings(LLSD()); + sInstance->setVisible(FALSE); + } + + return sInstance; +} + +//static +void LLFloaterMediaSettings::apply() +{ + if (sInstance->haveValuesChanged()) + { + LLSD settings; + sInstance->mPanelMediaSettingsGeneral->preApply(); + sInstance->mPanelMediaSettingsGeneral->getValues( settings, false ); + sInstance->mPanelMediaSettingsSecurity->preApply(); + sInstance->mPanelMediaSettingsSecurity->getValues( settings, false ); + sInstance->mPanelMediaSettingsPermissions->preApply(); + sInstance->mPanelMediaSettingsPermissions->getValues( settings, false ); + + LLSelectMgr::getInstance()->selectionSetMedia( LLTextureEntry::MF_HAS_MEDIA, settings ); + + sInstance->mPanelMediaSettingsGeneral->postApply(); + sInstance->mPanelMediaSettingsSecurity->postApply(); + sInstance->mPanelMediaSettingsPermissions->postApply(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void LLFloaterMediaSettings::onClose(bool app_quitting) +{ + if(mPanelMediaSettingsGeneral) + { + mPanelMediaSettingsGeneral->onClose(app_quitting); + } + if(LLFloaterWhiteListEntry::instanceExists()) + LLFloaterWhiteListEntry::getInstance()->close(app_quitting); + + LLFloater::onClose(app_quitting); +} + +//////////////////////////////////////////////////////////////////////////////// +//static +void LLFloaterMediaSettings::initValues( const LLSD& media_settings, bool editable ) +{ + if (sInstance->hasFocus()) return; + + sInstance->clearValues(editable); + // update all panels with values from simulator + sInstance->mPanelMediaSettingsGeneral-> + initValues( sInstance->mPanelMediaSettingsGeneral, media_settings, editable ); + + sInstance->mPanelMediaSettingsSecurity-> + initValues( sInstance->mPanelMediaSettingsSecurity, media_settings, editable ); + + sInstance->mPanelMediaSettingsPermissions-> + initValues( sInstance->mPanelMediaSettingsPermissions, media_settings, editable ); + + // Squirrel away initial values + sInstance->mInitialValues.clear(); + sInstance->mPanelMediaSettingsGeneral->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsSecurity->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsPermissions->getValues( sInstance->mInitialValues ); + + sInstance->mApplyBtn->setEnabled(editable); + sInstance->mOKBtn->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterMediaSettings::commitFields() +{ + if (hasFocus()) + { + LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (cur_focus && cur_focus->acceptsTextInput()) + { + cur_focus->onCommit(); + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +//static +void LLFloaterMediaSettings::clearValues( bool editable) +{ + if (sInstance) + { + // clean up all panels before updating + sInstance->mPanelMediaSettingsGeneral ->clearValues(sInstance->mPanelMediaSettingsGeneral, editable); + sInstance->mPanelMediaSettingsSecurity ->clearValues(sInstance->mPanelMediaSettingsSecurity, editable); + sInstance->mPanelMediaSettingsPermissions->clearValues(sInstance->mPanelMediaSettingsPermissions, editable); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onBtnOK( void* userdata ) +{ + sInstance->commitFields(); + + sInstance->apply(); + + sInstance->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onBtnApply( void* userdata ) +{ + sInstance->commitFields(); + + sInstance->apply(); + + sInstance->mInitialValues.clear(); + sInstance->mPanelMediaSettingsGeneral->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsSecurity->getValues( sInstance->mInitialValues ); + sInstance->mPanelMediaSettingsPermissions->getValues( sInstance->mInitialValues ); + +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onBtnCancel( void* userdata ) +{ + sInstance->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterMediaSettings::onTabChanged(void* user_data, bool from_click) +{ + LLTabContainer* self = (LLTabContainer*)user_data; + gSavedSettings.setS32("LastMediaSettingsTab", self->getCurrentPanelIndex()); +} +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLFloaterMediaSettings::getHomeUrl() +{ + if ( mPanelMediaSettingsGeneral ) + return mPanelMediaSettingsGeneral->getHomeUrl(); + else + return std::string( "" ); +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +void LLFloaterMediaSettings::draw() +{ + if (NULL != mApplyBtn) + { + // Set the enabled state of the "Apply" button if values changed + mApplyBtn->setEnabled( haveValuesChanged() ); + } + + LLFloater::draw(); +} + + +//private +bool LLFloaterMediaSettings::haveValuesChanged() const +{ + bool values_changed = false; + // *NOTE: The code below is very inefficient. Better to do this + // only when data change. + // Every frame, check to see what the values are. If they are not + // the same as the initial media data, enable the OK/Apply buttons + LLSD settings; + sInstance->mPanelMediaSettingsGeneral->getValues( settings ); + sInstance->mPanelMediaSettingsSecurity->getValues( settings ); + sInstance->mPanelMediaSettingsPermissions->getValues( settings ); + LLSD::map_const_iterator iter = settings.beginMap(); + LLSD::map_const_iterator end = settings.endMap(); + for ( ; iter != end; ++iter ) + { + const std::string ¤t_key = iter->first; + const LLSD ¤t_value = iter->second; + if ( ! llsd_equals(current_value, mInitialValues[current_key])) + { + values_changed = true; + break; + } + } + return values_changed; +} + +bool LLFloaterMediaSettings::instanceExists() +{ + return sInstance; +} + + diff --git a/indra/newview/llfloatermediasettings.h b/indra/newview/llfloatermediasettings.h new file mode 100644 index 000000000..1d2553098 --- /dev/null +++ b/indra/newview/llfloatermediasettings.h @@ -0,0 +1,89 @@ +/** + * @file llfloatermediasettings.cpp + * @brief Tabbed dialog for media settings - class definition + * + * $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$ + */ + +#ifndef LL_LLFLOATERMEDIASETTINGS_H +#define LL_LLFLOATERMEDIASETTINGS_H + +#include "llfloater.h" +#include "lltabcontainer.h" + +class LLPanelMediaSettingsGeneral; +class LLPanelMediaSettingsSecurity; +class LLPanelMediaSettingsPermissions; + +class LLFloaterMediaSettings : + public LLFloater +{ +public: + LLFloaterMediaSettings(const LLSD& key); + ~LLFloaterMediaSettings(); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onClose(bool app_quitting); + + static LLFloaterMediaSettings* getInstance(); + static bool instanceExists(); + static void apply(); + static void initValues( const LLSD& media_settings , bool editable); + static void clearValues( bool editable); + + LLPanelMediaSettingsSecurity* getPanelSecurity(){return mPanelMediaSettingsSecurity;}; + const std::string getHomeUrl(); + //bool passesWhiteList( const std::string& test_url ); + + virtual void draw(); + + bool mIdenticalHasMediaInfo; + bool mMultipleMedia; + bool mMultipleValidMedia; + +protected: + LLButton *mOKBtn; + LLButton *mCancelBtn; + LLButton *mApplyBtn; + + LLTabContainer *mTabContainer; + LLPanelMediaSettingsGeneral* mPanelMediaSettingsGeneral; + LLPanelMediaSettingsSecurity* mPanelMediaSettingsSecurity; + LLPanelMediaSettingsPermissions* mPanelMediaSettingsPermissions; + + static void onBtnOK(void*); + static void onBtnCancel(void*); + static void onBtnApply(void*); + static void onTabChanged(void* user_data, bool from_click); + void commitFields(); + + static LLFloaterMediaSettings* sInstance; + +private: + + bool haveValuesChanged() const; + + LLSD mInitialValues; + bool mWaitingToClose; +}; + +#endif // LL_LLFLOATERMEDIASETTINGS_H diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index fc358d79a..d4133befe 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -43,7 +43,7 @@ const S32 NOTIFICATION_PANEL_HEADER_HEIGHT = 20; const S32 HEADER_PADDING = 38; -class LLNotificationChannelPanel : public LLPanel +class LLNotificationChannelPanel : public LLLayoutPanel { public: LLNotificationChannelPanel(const std::string& channel_name); @@ -59,8 +59,9 @@ private: }; LLNotificationChannelPanel::LLNotificationChannelPanel(const std::string& channel_name) - : LLPanel(channel_name) + : LLLayoutPanel(NOTIFICATION_PANEL_HEADER_HEIGHT,true,true) { + setName(channel_name); mChannelPtr = LLNotifications::instance().getChannel(channel_name); mChannelRejectsPtr = LLNotificationChannelPtr( LLNotificationChannel::buildChannel(channel_name + "rejects", mChannelPtr->getParentChannelName(), !boost::bind(mChannelPtr->getFilter(), _1))); @@ -203,7 +204,7 @@ void LLFloaterNotificationConsole::addChannel(const std::string& name, bool open { LLLayoutStack& stack = getChildRef("notification_channels"); LLNotificationChannelPanel* panelp = new LLNotificationChannelPanel(name); - stack.addPanel(panelp, 0, NOTIFICATION_PANEL_HEADER_HEIGHT, TRUE, TRUE, LLLayoutStack::ANIMATE); + stack.addPanel(panelp, LLLayoutStack::ANIMATE); LLButton& header_button = panelp->getChildRef("header"); header_button.setToggleState(!open); @@ -217,7 +218,7 @@ void LLFloaterNotificationConsole::removeChannel(const std::string& name) LLPanel* panelp = getChild(name, TRUE, FALSE); if (panelp) { - getChildRef("notification_channels").removePanel(panelp); + getChildRef("notification_channels").removeChild(panelp); delete panelp; } diff --git a/indra/newview/llfloaternotificationsconsole.h b/indra/newview/llfloaternotificationsconsole.h index 302428a3f..c4a361538 100644 --- a/indra/newview/llfloaternotificationsconsole.h +++ b/indra/newview/llfloaternotificationsconsole.h @@ -34,6 +34,7 @@ #define LL_LLFLOATER_NOTIFICATIONS_CONSOLE_H #include "llfloater.h" +#include "lllayoutstack.h" #include "llnotifications.h" class LLFloaterNotificationConsole : diff --git a/indra/newview/llfloaterobjectiminfo.cpp b/indra/newview/llfloaterobjectiminfo.cpp index af4650d0c..5d433bca6 100644 --- a/indra/newview/llfloaterobjectiminfo.cpp +++ b/indra/newview/llfloaterobjectiminfo.cpp @@ -42,9 +42,13 @@ #include "llfloatermute.h" #include "llgroupactions.h" #include "llmutelist.h" -#include "llsdutil.h" +#include "llslurl.h" +#include "lltrans.h" +#include "llui.h" +#include "lluictrl.h" #include "lluictrlfactory.h" -#include "llurldispatcher.h" +#include "llurlaction.h" +#include "llweb.h" // [RLVa:KB] - Version: 1.23.4 #include "rlvhandler.h" @@ -52,34 +56,9 @@ //////////////////////////////////////////////////////////////////////////// // LLFloaterObjectIMInfo -class LLFloaterObjectIMInfo : public LLFloater, public LLFloaterSingleton -{ -public: - LLFloaterObjectIMInfo(const LLSD& sd); - virtual ~LLFloaterObjectIMInfo() { }; - - BOOL postBuild(void); - - void update(const LLUUID& id, const std::string& name, const std::string& slurl, const LLUUID& owner, bool owner_is_group); - - // UI Handlers - static void onClickMap(void* data); - static void onClickOwner(void* data); - static void onClickMute(void* data); - - void nameCallback(const LLUUID& id, const std::string& full_name, bool is_group); - -private: - LLUUID mObjectID; - std::string mObjectName; - std::string mSlurl; - LLUUID mOwnerID; - std::string mOwnerName; - bool mOwnerIsGroup; -}; LLFloaterObjectIMInfo::LLFloaterObjectIMInfo(const LLSD& seed) -: mObjectID(), mObjectName(), mSlurl(), mOwnerID(), mOwnerName(), mOwnerIsGroup(false) +: mObjectID(), mName(), mSLurl(), mOwnerID(), mGroupOwned(false) { LLUICtrlFactory::getInstance()->buildFloater(this, "floater_object_im_info.xml"); @@ -99,34 +78,37 @@ BOOL LLFloaterObjectIMInfo::postBuild(void) return true; } -void LLFloaterObjectIMInfo::update(const LLUUID& object_id, const std::string& name, const std::string& slurl, const LLUUID& owner_id, bool owner_is_group) +void LLFloaterObjectIMInfo::update(LLSD& data) { + // Extract appropriate object information from input LLSD + // (Eventually, it might be nice to query server for details + // rather than require caller to pass in the information.) + mObjectID = data["object_id"].asUUID(); + mName = data["name"].asString(); + mOwnerID = data["owner_id"].asUUID(); + mGroupOwned = data["group_owned"].asBoolean(); + mSLurl = data["slurl"].asString(); + // When talking to an old region we won't have a slurl. // The object id isn't really the object id either but we don't use it so who cares. //bool have_slurl = !slurl.empty(); // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-04 (RLVa-1.0.0a) | Added: RLVa-0.2.0g - bool have_slurl = (!slurl.empty()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); + bool have_slurl = (!mSLurl.empty()) && (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)); // [/RLVa:KB] childSetVisible("Unknown_Slurl",!have_slurl); childSetVisible("Slurl",have_slurl); - childSetText("ObjectName",name); - childSetText("Slurl",slurl); + childSetText("ObjectName",mName); + childSetText("Slurl",mSLurl); childSetText("OwnerName",std::string("")); // bool my_object = (owner_id == gAgentID); // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g - bool my_object = (owner_id == gAgentID) || ((gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(owner_id))); + bool my_object = (mOwnerID == gAgentID) || ((gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(mOwnerID))); // [/RLVa:KB] childSetEnabled("Mute",!my_object); - mObjectID = object_id; - mObjectName = name; - mSlurl = slurl; - mOwnerID = owner_id; - mOwnerIsGroup = owner_is_group; - - if (gCacheName) gCacheName->get(owner_id,owner_is_group,boost::bind(&LLFloaterObjectIMInfo::nameCallback,this,_1,_2,_3)); + if (gCacheName) gCacheName->get(mOwnerID,mGroupOwned,boost::bind(&LLFloaterObjectIMInfo::nameCallback,this,_1,_2,_3)); } //static @@ -134,17 +116,15 @@ void LLFloaterObjectIMInfo::onClickMap(void* data) { LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; - std::ostringstream link; - link << "secondlife://" << self->mSlurl; - class LLMediaCtrl* web = NULL; - LLURLDispatcher::dispatch(link.str(), web, true); + std::string url = "secondlife://" + self->mSLurl; + LLUrlAction::showLocationOnMap(url); } //static void LLFloaterObjectIMInfo::onClickOwner(void* data) { LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; - if (self->mOwnerIsGroup) + if (self->mGroupOwned) { LLGroupActions::show(self->mOwnerID); } @@ -162,7 +142,7 @@ void LLFloaterObjectIMInfo::onClickMute(void* data) { LLFloaterObjectIMInfo* self = (LLFloaterObjectIMInfo*)data; - LLMute::EType mute_type = (self->mOwnerIsGroup) ? LLMute::GROUP : LLMute::AGENT; + LLMute::EType mute_type = (self->mGroupOwned) ? LLMute::GROUP : LLMute::AGENT; // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g if ( (LLMute::GROUP != mute_type) && (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(self->mOwnerID)) ) { @@ -170,7 +150,7 @@ void LLFloaterObjectIMInfo::onClickMute(void* data) } // [/RLVa:KB] - LLMute mute(self->mOwnerID, self->mOwnerName, mute_type); + LLMute mute(self->mOwnerID, self->mName, mute_type); LLMuteList::getInstance()->add(mute); LLFloaterMute::showInstance(); self->close(); @@ -179,49 +159,50 @@ void LLFloaterObjectIMInfo::onClickMute(void* data) //static void LLFloaterObjectIMInfo::nameCallback(const LLUUID& id, const std::string& full_name, bool is_group) { - mOwnerName = full_name; + mName = full_name; // [RLVa:KB] - Version: 1.23.4 | Checked: 2009-07-08 (RLVa-1.0.0e) | Added: RLVa-0.2.0g if ( (!is_group) && (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) && (RlvUtil::isNearbyAgent(id)) ) { - mOwnerName = RlvStrings::getAnonym(mOwnerName); + mName = RlvStrings::getAnonym(mName); } // [/RLVa:KB] - childSetText("OwnerName", mOwnerName); -} - -//////////////////////////////////////////////////////////////////////////// -// LLObjectIMInfo -void LLObjectIMInfo::show(const LLUUID &object_id, const std::string &name, const std::string &location, const LLUUID &owner_id, bool owner_is_group) -{ - LLFloaterObjectIMInfo* im_info_floater = LLFloaterObjectIMInfo::showInstance(); - im_info_floater->update(object_id,name,location,owner_id,owner_is_group); + childSetText("OwnerName", mName); } //////////////////////////////////////////////////////////////////////////// // LLObjectIMInfoHandler +//moved to llchathistory.cpp in v2 class LLObjectIMInfoHandler : public LLCommandHandler { public: - LLObjectIMInfoHandler() : LLCommandHandler("objectim", true) { } + LLObjectIMInfoHandler() : LLCommandHandler("objectim", UNTRUSTED_THROTTLE) { } - bool handle(const LLSD& tokens, const LLSD& query_map, - LLMediaCtrl* web); + bool handle(const LLSD& params, const LLSD& query_map,LLMediaCtrl* web) + { + if (params.size() < 1) + { + return false; + } + + LLUUID object_id; + if (!object_id.set(params[0], FALSE)) + { + return false; + } + + LLSD payload; + payload["object_id"] = object_id; + payload["owner_id"] = query_map["owner"]; + payload["name"] = query_map["name"]; + payload["slurl"] = LLWeb::escapeURL(query_map["slurl"]); + payload["group_owned"] = query_map["groupowned"]; + + LLFloaterObjectIMInfo::showInstance()->update(payload); + + return true; + } }; // Creating the object registers with the dispatcher. LLObjectIMInfoHandler gObjectIMHandler; - -// ex. secondlife:///app/objectim/9426adfc-9c17-8765-5f09-fdf19957d003?owner=a112d245-9095-4e9c-ace4-ffa31717f934&groupowned=true&slurl=ahern/123/123/123&name=Object -bool LLObjectIMInfoHandler::handle(const LLSD &tokens, const LLSD &query_map, LLMediaCtrl* web) -{ - LLUUID task_id = tokens[0].asUUID(); - std::string name = query_map["name"].asString(); - std::string slurl = query_map["slurl"].asString(); - LLUUID owner = query_map["owner"].asUUID(); - bool group_owned = query_map.has("groupowned"); - - LLObjectIMInfo::show(task_id,name,slurl,owner,group_owned); - - return true; -} diff --git a/indra/newview/llfloaterobjectiminfo.h b/indra/newview/llfloaterobjectiminfo.h index 55f8bba6f..58377c009 100644 --- a/indra/newview/llfloaterobjectiminfo.h +++ b/indra/newview/llfloaterobjectiminfo.h @@ -33,14 +33,31 @@ #ifndef LL_LLFLOATEROBJECTIMINFO_H #define LL_LLFLOATEROBJECTIMINFO_H -namespace LLObjectIMInfo +#include "llfloater.h" + +class LLFloaterObjectIMInfo : public LLFloater, public LLFloaterSingleton { - // Show an LLFloaterObjectIMInfo for this object. - static void show(const LLUUID& object_id, - const std::string& name, - const std::string& location, - const LLUUID& owner_id, - bool owner_is_group); +public: + LLFloaterObjectIMInfo(const LLSD& sd); + virtual ~LLFloaterObjectIMInfo() { }; + + /*virtual*/ BOOL postBuild(void); + + void update(LLSD& payload); + + // UI Handlers + static void onClickMap(void* data); + static void onClickOwner(void* data); + static void onClickMute(void* data); + + void nameCallback(const LLUUID& id, const std::string& full_name, bool is_group); + +private: + LLUUID mObjectID; + LLUUID mOwnerID; + std::string mSLurl; + std::string mName; + bool mGroupOwned; }; #endif // LL_LLFLOATERURLDISPLAY_H diff --git a/indra/newview/llfloaterparcel.cpp b/indra/newview/llfloaterparcel.cpp index a61f3b912..93afb820f 100644 --- a/indra/newview/llfloaterparcel.cpp +++ b/indra/newview/llfloaterparcel.cpp @@ -49,11 +49,12 @@ LLMap< const LLUUID, LLFloaterParcelInfo* > gPlaceInfoInstances; +//moved to llpanelplaces.cpp in v2 class LLParcelHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLParcelHandler() : LLCommandHandler("parcel", true) { } + LLParcelHandler() : LLCommandHandler("parcel", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index ed0b07be4..dbbd06f76 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -93,7 +93,7 @@ class LLPreferencesHandler : public LLCommandHandler { public: // requires trusted browser - LLPreferencesHandler() : LLCommandHandler("preferences", true) { } + LLPreferencesHandler() : LLCommandHandler("preferences", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { @@ -469,7 +469,7 @@ void LLFloaterPreference::onBtnOK( void* userdata ) llinfos << "Can't close preferences!" << llendl; } - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationSelectorsVisibility(); } @@ -480,14 +480,14 @@ void LLFloaterPreference::onBtnApply( void* userdata ) if (fp->hasFocus()) { LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (cur_focus->acceptsTextInput()) + if (cur_focus && cur_focus->acceptsTextInput()) { cur_focus->onCommit(); } } fp->apply(); - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationSelectorsVisibility(); } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 387c20ccc..ea6a0e0fd 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -124,6 +124,7 @@ public: static void closeWithoutSaving(); protected: + friend class LLPanelNearByMedia; LLPreferenceCore *mPreferenceCore; /*virtual*/ void onClose(bool app_quitting); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 83ab8b0e8..d41cbf17a 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -51,9 +51,11 @@ // viewer project includes #include "llagent.h" +#include "llagentui.h" #include "llbutton.h" #include "lltexturectrl.h" #include "llscrolllistctrl.h" +#include "llslurl.h" #include "lldispatcher.h" #include "llviewerobject.h" #include "llviewerregion.h" @@ -122,7 +124,9 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) // virtual BOOL LLFloaterReporter::postBuild() { - getChild("abuse_location_edit")->setValue(gAgent.getSLURL()); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + getChild("abuse_location_edit")->setValue(slurl.getSLURLString()); enableControls(TRUE); diff --git a/indra/newview/llfloaterteleporthistory.cpp b/indra/newview/llfloaterteleporthistory.cpp index bdb089e3c..a4ffe91fa 100644 --- a/indra/newview/llfloaterteleporthistory.cpp +++ b/indra/newview/llfloaterteleporthistory.cpp @@ -44,15 +44,17 @@ #include "llappviewer.h" #include "llfloaterteleporthistory.h" #include "llfloaterworldmap.h" +#include "llslurl.h" #include "lltimer.h" #include "lluictrlfactory.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" #include "llviewercontrol.h" #include "llviewerwindow.h" #include "llwindow.h" #include "llweb.h" #include "llsdserialize.h" +#include "llurlaction.h" + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -137,12 +139,12 @@ void LLFloaterTeleportHistory::addPendingEntry(std::string regionName, S16 x, S1 // Set pending position mPendingPosition = llformat("%d, %d, %d", x, y, z); + LLSLURL slurl(regionName, LLVector3(x, y, z)); // prepare simstring for later parsing - mPendingSimString = regionName + llformat("/%d/%d/%d", x, y, z); - mPendingSimString = LLWeb::escapeURL(mPendingSimString); + mPendingSimString = LLWeb::escapeURL(slurl.getLocationString()); // Prepare the SLURL - mPendingSLURL = LLURLDispatcher::buildSLURL(regionName, x, y, z); + mPendingSLURL = slurl.getSLURLString(); } void LLFloaterTeleportHistory::addEntry(std::string parcelName) @@ -332,9 +334,8 @@ void LLFloaterTeleportHistory::onTeleport(void* data) LLFloaterTeleportHistory* self = (LLFloaterTeleportHistory*) data; // build secondlife::/app link from simstring for instant teleport to destination - std::string slapp = "secondlife:///app/teleport/" + self->mPlacesList->getFirstSelected()->getColumn(LIST_SIMSTRING)->getValue().asString(); - LLMediaCtrl* web = NULL; - LLURLDispatcher::dispatch(slapp, web, TRUE); + std::string slapp = "secondlife:///app/teleport/" + self->mPlacesList->getFirstSelected()->getColumn(LIST_SLURL)->getValue().asString(); + LLUrlAction::teleportToLocation(slapp); } // static @@ -344,15 +345,11 @@ void LLFloaterTeleportHistory::onShowOnMap(void* data) // get simstring from selected entry and parse it for its components std::string simString = self->mPlacesList->getFirstSelected()->getColumn(LIST_SIMSTRING)->getValue().asString(); - std::string region = ""; - S32 x = 128; - S32 y = 128; - S32 z = 20; - LLURLSimString::parse(simString, ®ion, &x, &y, &z); + LLSLURL slurl(simString); // point world map at position - gFloaterWorldMap->trackURL(region, x, y, z); + gFloaterWorldMap->trackURL(slurl.getRegion(), slurl.getPosition().mV[VX], slurl.getPosition().mV[VY], slurl.getPosition().mV[VZ]); LLFloaterWorldMap::show(true); } diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp index 1cbaac1a7..d63fde073 100644 --- a/indra/newview/llfloatertools.cpp +++ b/indra/newview/llfloatertools.cpp @@ -45,9 +45,13 @@ #include "llcombobox.h" #include "lldraghandle.h" #include "llfloaterbuildoptions.h" +#include "llfloatermediasettings.h" #include "llfloateropenobject.h" #include "llfocusmgr.h" +#include "llmediaentry.h" +#include "llmediactrl.h" #include "llmenugl.h" +#include "llnotificationsutil.h" #include "llpanelcontents.h" #include "llpanelface.h" #include "llpanelland.h" @@ -55,6 +59,7 @@ #include "llpanelobject.h" #include "llpanelvolume.h" #include "llpanelpermissions.h" +#include "llparcel.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llslider.h" @@ -74,12 +79,15 @@ #include "lltoolpipette.h" #include "lltoolplacer.h" #include "lltoolselectland.h" +#include "lltrans.h" #include "llui.h" #include "llviewercontrol.h" #include "llviewerjoystick.h" +#include "llviewerregion.h" #include "llviewermenu.h" #include "llviewerparcelmgr.h" #include "llviewerwindow.h" +#include "llvovolume.h" #include "lluictrlfactory.h" #include "llmeshrepository.h" @@ -103,23 +111,21 @@ const std::string PANEL_NAMES[LLFloaterTools::PANEL_COUNT] = // Local prototypes -void commit_grid_mode(LLUICtrl *ctrl, void*); -void commit_select_component(LLUICtrl *ctrl, void *data); +void commit_grid_mode(LLUICtrl *ctrl); +void commit_select_component(void *data); void click_show_more(void*); void click_popup_info(void*); void click_popup_done(void*); void click_popup_minimize(void*); -void click_popup_grab_drag(LLUICtrl *, void*); -void click_popup_grab_lift(LLUICtrl *, void*); -void click_popup_grab_spin(LLUICtrl *, void*); -void click_popup_dozer_mode(LLUICtrl *, void *user); -void commit_slider_dozer_size(LLUICtrl *, void*); -void commit_slider_dozer_force(LLUICtrl *, void*); +void commit_slider_dozer_size(LLUICtrl *); +void commit_slider_dozer_force(LLUICtrl *); void click_apply_to_selection(void*); -void commit_radio_zoom(LLUICtrl *, void*); -void commit_radio_orbit(LLUICtrl *, void*); -void commit_radio_pan(LLUICtrl *, void*); -void commit_slider_zoom(LLUICtrl *ctrl, void*); +void commit_radio_group_focus(LLUICtrl* ctrl); +void commit_radio_group_move(LLUICtrl* ctrl); +void commit_radio_group_edit(LLUICtrl* ctrl); +void commit_radio_group_land(LLUICtrl* ctrl); + +void commit_slider_zoom(LLUICtrl *ctrl); void commit_select_tool(LLUICtrl *ctrl, void *data); @@ -224,58 +230,40 @@ BOOL LLFloaterTools::postBuild() LLRect rect; mBtnFocus = getChild("button focus");//btn; - childSetAction("button focus",LLFloaterTools::setEditTool, (void*)LLToolCamera::getInstance()); mBtnMove = getChild("button move"); - childSetAction("button move",LLFloaterTools::setEditTool, (void*)LLToolGrab::getInstance()); mBtnEdit = getChild("button edit"); - childSetAction("button edit",LLFloaterTools::setEditTool, (void*)LLToolCompTranslate::getInstance()); mBtnCreate = getChild("button create"); - childSetAction("button create",LLFloaterTools::setEditTool, (void*)LLToolCompCreate::getInstance()); mBtnLand = getChild("button land" ); - childSetAction("button land",LLFloaterTools::setEditTool, (void*)LLToolSelectLand::getInstance()); mTextStatus = getChild("text status"); - childSetCommitCallback("slider zoom",commit_slider_zoom,this); mRadioZoom = getChild("radio zoom"); - childSetCommitCallback("radio zoom",commit_radio_zoom,this); mRadioOrbit = getChild("radio orbit"); - childSetCommitCallback("radio orbit",commit_radio_orbit,this); mRadioPan = getChild("radio pan"); - childSetCommitCallback("radio pan",commit_radio_pan,this); mRadioMove = getChild("radio move"); - childSetCommitCallback("radio move",click_popup_grab_drag,this); mRadioLift = getChild("radio lift"); - childSetCommitCallback("radio lift",click_popup_grab_lift,this); mRadioSpin = getChild("radio spin"); - childSetCommitCallback("radio spin",click_popup_grab_spin,NULL); mRadioPosition = getChild("radio position"); - childSetCommitCallback("radio position",commit_select_tool,NULL); mRadioRotate = getChild("radio rotate"); - childSetCommitCallback("radio rotate",commit_select_tool,NULL); mRadioStretch = getChild("radio stretch"); - childSetCommitCallback("radio stretch",commit_select_tool,NULL); mRadioSelectFace = getChild("radio select face"); - childSetCommitCallback("radio select face",commit_select_tool,NULL); mRadioAlign = getChild("radio align"); - childSetCommitCallback("radio align",commit_select_tool,NULL); + mTitleMedia = getChild("title_media"); + mCheckSelectIndividual = getChild("checkbox edit linked parts"); - childSetValue("checkbox edit linked parts",(BOOL)gSavedSettings.getBOOL("EditLinkedParts")); - childSetCommitCallback("checkbox edit linked parts",commit_select_component,this); + getChild("checkbox edit linked parts")->setValue((BOOL)gSavedSettings.getBOOL("EditLinkedParts")); mCheckSnapToGrid = getChild("checkbox snap to grid"); - childSetValue("checkbox snap to grid",(BOOL)gSavedSettings.getBOOL("SnapEnabled")); + getChild("checkbox snap to grid")->setValue((BOOL)gSavedSettings.getBOOL("SnapEnabled")); mBtnGridOptions = getChild("Options..."); - childSetAction("Options...",onClickGridOptions, this); mCheckStretchUniform = getChild("checkbox uniform"); - childSetValue("checkbox uniform",(BOOL)gSavedSettings.getBOOL("ScaleUniform")); + getChild("checkbox uniform")->setValue((BOOL)gSavedSettings.getBOOL("ScaleUniform")); mCheckStretchTexture = getChild("checkbox stretch textures"); - childSetValue("checkbox stretch textures",(BOOL)gSavedSettings.getBOOL("ScaleStretchTextures")); + getChild("checkbox stretch textures")->setValue((BOOL)gSavedSettings.getBOOL("ScaleStretchTextures")); mCheckLimitDrag = getChild("checkbox limit drag distance"); childSetValue("checkbox limit drag distance",(BOOL)gSavedSettings.getBOOL("LimitDragDistance")); mTextGridMode = getChild("text ruler mode"); mComboGridMode = getChild("combobox grid mode"); - childSetCommitCallback("combobox grid mode",commit_grid_mode, this); mCheckShowHighlight = getChild("checkbox show highlight"); mCheckActualRoot = getChild("checkbox actual root"); @@ -297,38 +285,28 @@ BOOL LLFloaterTools::postBuild() if ((mComboTreesGrass = findChild("trees_grass"))) childSetCommitCallback("trees_grass", onSelectTreesGrass, (void*)0); mCheckCopySelection = getChild("checkbox copy selection"); - childSetValue("checkbox copy selection",(BOOL)gSavedSettings.getBOOL("CreateToolCopySelection")); + getChild("checkbox copy selection")->setValue((BOOL)gSavedSettings.getBOOL("CreateToolCopySelection")); mCheckSticky = getChild("checkbox sticky"); - childSetValue("checkbox sticky",(BOOL)gSavedSettings.getBOOL("CreateToolKeepSelected")); + getChild("checkbox sticky")->setValue((BOOL)gSavedSettings.getBOOL("CreateToolKeepSelected")); mCheckCopyCenters = getChild("checkbox copy centers"); - childSetValue("checkbox copy centers",(BOOL)gSavedSettings.getBOOL("CreateToolCopyCenters")); + getChild("checkbox copy centers")->setValue((BOOL)gSavedSettings.getBOOL("CreateToolCopyCenters")); mCheckCopyRotates = getChild("checkbox copy rotates"); - childSetValue("checkbox copy rotates",(BOOL)gSavedSettings.getBOOL("CreateToolCopyRotates")); + getChild("checkbox copy rotates")->setValue((BOOL)gSavedSettings.getBOOL("CreateToolCopyRotates")); mRadioSelectLand = getChild("radio select land"); - childSetCommitCallback("radio select land",commit_select_tool, NULL); mRadioDozerFlatten = getChild("radio flatten"); - childSetCommitCallback("radio flatten",click_popup_dozer_mode, (void*)0); mRadioDozerRaise = getChild("radio raise"); - childSetCommitCallback("radio raise",click_popup_dozer_mode, (void*)1); mRadioDozerLower = getChild("radio lower"); - childSetCommitCallback("radio lower",click_popup_dozer_mode, (void*)2); mRadioDozerSmooth = getChild("radio smooth"); - childSetCommitCallback("radio smooth",click_popup_dozer_mode, (void*)3); mRadioDozerNoise = getChild("radio noise"); - childSetCommitCallback("radio noise",click_popup_dozer_mode, (void*)4); mRadioDozerRevert = getChild("radio revert"); - childSetCommitCallback("radio revert",click_popup_dozer_mode, (void*)5); mBtnApplyToSelection = getChild("button apply to selection"); - childSetAction("button apply to selection",click_apply_to_selection, (void*)0); mSliderDozerSize = getChild("slider brush size"); - childSetCommitCallback("slider brush size", commit_slider_dozer_size, (void*)0); - childSetValue( "slider brush size", gSavedSettings.getF32("LandBrushSize")); + getChild("slider brush size")->setValue(gSavedSettings.getF32("LandBrushSize")); mSliderDozerForce = getChild("slider force"); - childSetCommitCallback("slider force",commit_slider_dozer_force, (void*)0); // the setting stores the actual force multiplier, but the slider is logarithmic, so we convert here - childSetValue("slider force", log10(gSavedSettings.getF32("LandBrushForce"))); + getChild("slider force")->setValue(log10(gSavedSettings.getF32("LandBrushForce"))); mTab = getChild("Object Info Tabs"); if(mTab) @@ -362,14 +340,17 @@ LLFloaterTools::LLFloaterTools() mBtnLand(NULL), mTextStatus(NULL), + //Camera Focus mRadioOrbit(NULL), mRadioZoom(NULL), mRadioPan(NULL), + //Move via physics mRadioMove(NULL), mRadioLift(NULL), mRadioSpin(NULL), + //Edit prim mRadioPosition(NULL), mRadioRotate(NULL), mRadioStretch(NULL), @@ -380,6 +361,7 @@ LLFloaterTools::LLFloaterTools() mCheckSnapToGrid(NULL), mBtnGridOptions(NULL), mTextGridMode(NULL), + mTitleMedia(NULL), mComboGridMode(NULL), mCheckStretchUniform(NULL), mCheckStretchTexture(NULL), @@ -400,6 +382,8 @@ LLFloaterTools::LLFloaterTools() mCheckCopySelection(NULL), mCheckCopyCenters(NULL), mCheckCopyRotates(NULL), + + //Edit land mRadioSelectLand(NULL), mRadioDozerFlatten(NULL), mRadioDozerRaise(NULL), @@ -407,6 +391,7 @@ LLFloaterTools::LLFloaterTools() mRadioDozerSmooth(NULL), mRadioDozerNoise(NULL), mRadioDozerRevert(NULL), + mSliderDozerSize(NULL), mSliderDozerForce(NULL), mBtnApplyToSelection(NULL), @@ -419,7 +404,8 @@ LLFloaterTools::LLFloaterTools() mPanelFace(NULL), mPanelLandInfo(NULL), - mDirty(TRUE) + mDirty(TRUE), + mNeedMediaTitle(TRUE) { setAutoFocus(FALSE); LLCallbackMap::map_t factory_map; @@ -431,6 +417,23 @@ LLFloaterTools::LLFloaterTools() factory_map["ContentsInventory"] = LLCallbackMap(createPanelContentsInventory, this);//LLPanelContents factory_map["land info panel"] = LLCallbackMap(createPanelLandInfo, this);//LLPanelLandInfo + mCommitCallbackRegistrar.add("BuildTool.setTool", boost::bind(&LLFloaterTools::setTool,this, _2)); + mCommitCallbackRegistrar.add("BuildTool.commitDozerSize", boost::bind(&commit_slider_dozer_size, _1)); + mCommitCallbackRegistrar.add("BuildTool.commitZoom", boost::bind(&commit_slider_zoom, _1)); + mCommitCallbackRegistrar.add("BuildTool.commitRadioFocus", boost::bind(&commit_radio_group_focus, _1)); + mCommitCallbackRegistrar.add("BuildTool.commitRadioMove", boost::bind(&commit_radio_group_move,_1)); + mCommitCallbackRegistrar.add("BuildTool.commitRadioEdit", boost::bind(&commit_radio_group_edit,_1)); + + mCommitCallbackRegistrar.add("BuildTool.gridMode", boost::bind(&commit_grid_mode,_1)); + mCommitCallbackRegistrar.add("BuildTool.selectComponent", boost::bind(&commit_select_component, this)); + mCommitCallbackRegistrar.add("BuildTool.gridOptions", boost::bind(&LLFloaterTools::onClickGridOptions,this)); + mCommitCallbackRegistrar.add("BuildTool.applyToSelection", boost::bind(&click_apply_to_selection, this)); + mCommitCallbackRegistrar.add("BuildTool.commitRadioLand", boost::bind(&commit_radio_group_land,_1)); + mCommitCallbackRegistrar.add("BuildTool.LandBrushForce", boost::bind(&commit_slider_dozer_force,_1)); + mCommitCallbackRegistrar.add("BuildTool.AddMedia", boost::bind(&LLFloaterTools::onClickBtnAddMedia,this)); + mCommitCallbackRegistrar.add("BuildTool.DeleteMedia", boost::bind(&LLFloaterTools::onClickBtnDeleteMedia,this)); + mCommitCallbackRegistrar.add("BuildTool.EditMedia", boost::bind(&LLFloaterTools::onClickBtnEditMedia,this)); + LLUICtrlFactory::getInstance()->buildFloater(this,"floater_tools.xml",&factory_map,FALSE); } @@ -539,6 +542,8 @@ void LLFloaterTools::refresh() mPanelObject->refresh(); mPanelVolume->refresh(); mPanelFace->refresh(); + if(mTitleMedia) + refreshMedia(); mPanelContents->refresh(); mPanelLandInfo->refresh(); } @@ -551,6 +556,10 @@ void LLFloaterTools::draw() mDirty = FALSE; } + // grab media name/title and update the UI widget + if(mTitleMedia) + updateMediaTitle(); + // mCheckSelectIndividual->set(gSavedSettings.getBOOL("EditLinkedParts")); LLFloater::draw(); } @@ -755,8 +764,8 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) if (mCheckCopyCenters) mCheckCopyCenters ->setVisible( create_visible ); if (mCheckCopyRotates) mCheckCopyRotates ->setVisible( create_visible ); - if (mCheckCopyCenters) mCheckCopyCenters->setEnabled( mCheckCopySelection->get() ); - if (mCheckCopyRotates) mCheckCopyRotates->setEnabled( mCheckCopySelection->get() ); + if (mCheckCopyCenters && mCheckCopySelection) mCheckCopyCenters->setEnabled( mCheckCopySelection->get() ); + if (mCheckCopyRotates && mCheckCopySelection) mCheckCopyRotates->setEnabled( mCheckCopySelection->get() ); // Land buttons BOOL land_visible = (tool == LLToolBrushLand::getInstance() || tool == LLToolSelectLand::getInstance() ); @@ -809,13 +818,13 @@ void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask) if (mSliderDozerSize) { mSliderDozerSize ->setVisible( land_visible ); - childSetVisible("Bulldozer:", land_visible); - childSetVisible("Dozer Size:", land_visible); + getChildView("Bulldozer:")->setVisible( land_visible); + getChildView("Dozer Size:")->setVisible( land_visible); } if (mSliderDozerForce) { mSliderDozerForce ->setVisible( land_visible ); - childSetVisible("Strength:", land_visible); + getChildView("Strength:")->setVisible( land_visible); } childSetVisible("link_num_obj_count", !land_visible); @@ -851,6 +860,10 @@ void LLFloaterTools::onClose(bool app_quitting) LLViewerJoystick::getInstance()->moveAvatar(false); + // destroy media source used to grab media title + if( mTitleMedia ) + mTitleMedia->unloadMediaSource(); + // Different from handle_reset_view in that it doesn't actually // move the camera if EditCameraMovement is not set. gAgentCamera.resetView(gSavedSettings.getBOOL("EditCameraMovement")); @@ -885,6 +898,9 @@ void LLFloaterTools::onClose(bool app_quitting) // gMenuBarView->setItemVisible(std::string("Tools"), FALSE); // gMenuBarView->arrange(); + + if(LLFloaterMediaSettings::instanceExists()) + LLFloaterMediaSettings::getInstance()->close(); } void LLFloaterTools::showPanel(EInfoPanel panel) @@ -903,66 +919,63 @@ void click_popup_done(void*) handle_reset_view(); } -void click_popup_grab_drag(LLUICtrl*, void*) +void commit_radio_group_move(LLUICtrl* ctrl) { - gGrabBtnVertical = FALSE; - gGrabBtnSpin = FALSE; + std::string selected = ctrl->getName(); + if (selected == "radio move") + { + gGrabBtnVertical = FALSE; + gGrabBtnSpin = FALSE; + } + else if (selected == "radio lift") + { + gGrabBtnVertical = TRUE; + gGrabBtnSpin = FALSE; + } + else if (selected == "radio spin") + { + gGrabBtnVertical = FALSE; + gGrabBtnSpin = TRUE; + } } -void click_popup_grab_lift(LLUICtrl*, void*) +void commit_radio_group_focus(LLUICtrl* ctrl) { - gGrabBtnVertical = TRUE; - gGrabBtnSpin = FALSE; + std::string selected = ctrl->getName(); + if (selected == "radio zoom") + { + gCameraBtnZoom = TRUE; + gCameraBtnOrbit = FALSE; + gCameraBtnPan = FALSE; + } + else if (selected == "radio orbit") + { + gCameraBtnZoom = FALSE; + gCameraBtnOrbit = TRUE; + gCameraBtnPan = FALSE; + } + else if (selected == "radio pan") + { + gCameraBtnZoom = FALSE; + gCameraBtnOrbit = FALSE; + gCameraBtnPan = TRUE; + } } -void click_popup_grab_spin(LLUICtrl*, void*) -{ - gGrabBtnVertical = FALSE; - gGrabBtnSpin = TRUE; -} - -void commit_radio_zoom(LLUICtrl *, void*) -{ - gCameraBtnZoom = TRUE; - gCameraBtnOrbit = FALSE; - gCameraBtnPan = FALSE; -} - -void commit_radio_orbit(LLUICtrl *, void*) -{ - gCameraBtnZoom = FALSE; - gCameraBtnOrbit = TRUE; - gCameraBtnPan = FALSE; -} - -void commit_radio_pan(LLUICtrl *, void*) -{ - gCameraBtnZoom = FALSE; - gCameraBtnOrbit = FALSE; - gCameraBtnPan = TRUE; -} - -void commit_slider_zoom(LLUICtrl *ctrl, void*) +void commit_slider_zoom(LLUICtrl *ctrl) { // renormalize value, since max "volume" level is 0.5 for some reason F32 zoom_level = (F32)ctrl->getValue().asReal() * 2.f; // / 0.5f; gAgentCamera.setCameraZoomFraction(zoom_level); } -void click_popup_dozer_mode(LLUICtrl *, void *user) -{ - S32 mode = (S32)(intptr_t) user; - gFloaterTools->setEditTool( LLToolBrushLand::getInstance() ); - gSavedSettings.setS32("RadioLandBrushAction", mode); -} - -void commit_slider_dozer_size(LLUICtrl *ctrl, void*) +void commit_slider_dozer_size(LLUICtrl *ctrl) { F32 size = (F32)ctrl->getValue().asReal(); gSavedSettings.setF32("LandBrushSize", size); } -void commit_slider_dozer_force(LLUICtrl *ctrl, void*) +void commit_slider_dozer_force(LLUICtrl *ctrl) { // the slider is logarithmic, so we exponentiate to get the actual force multiplier F32 dozer_force = pow(10.f, (F32)ctrl->getValue().asReal()); @@ -974,12 +987,11 @@ void click_apply_to_selection(void*) LLToolBrushLand::getInstance()->modifyLandInSelectionGlobal(); } -void commit_select_tool(LLUICtrl *ctrl, void *data) +void commit_radio_group_edit(LLUICtrl *ctrl) { S32 show_owners = gSavedSettings.getBOOL("ShowParcelOwners"); - LLCheckBoxCtrl* group = (LLCheckBoxCtrl*)ctrl; - std::string selected = group->getName(); + std::string selected = ctrl->getName(); if (selected == "radio position") { LLFloaterTools::setEditTool( LLToolCompTranslate::getInstance() ); @@ -1000,14 +1012,37 @@ void commit_select_tool(LLUICtrl *ctrl, void *data) { LLFloaterTools::setEditTool( QToolAlign::getInstance() ); } - else if (selected == "radio select land") - { - LLFloaterTools::setEditTool( LLToolSelectLand::getInstance()); - } gSavedSettings.setBOOL("ShowParcelOwners", show_owners); } -void commit_select_component(LLUICtrl *ctrl, void *data) +void commit_radio_group_land(LLUICtrl* ctrl) +{ + std::string selected = ctrl->getName(); + if (selected == "radio select land") + { + LLFloaterTools::setEditTool( LLToolSelectLand::getInstance() ); + } + else + { + LLFloaterTools::setEditTool( LLToolBrushLand::getInstance() ); + S32 dozer_mode = gSavedSettings.getS32("RadioLandBrushAction"); + if (selected == "radio flatten") + dozer_mode = 0; + else if (selected == "radio raise") + dozer_mode = 1; + else if (selected == "radio lower") + dozer_mode = 2; + else if (selected == "radio smooth") + dozer_mode = 3; + else if (selected == "radio noise") + dozer_mode = 4; + else if (selected == "radio revert") + dozer_mode = 5; + gSavedSettings.setS32("RadioLandBrushAction", dozer_mode); + } +} + +void commit_select_component(void *data) { LLFloaterTools* floaterp = (LLFloaterTools*)data; @@ -1039,15 +1074,14 @@ void LLFloaterTools::setObjectType( LLPCode pcode ) gFocusMgr.setMouseCapture(NULL); } -void commit_grid_mode(LLUICtrl *ctrl, void *data) +void commit_grid_mode(LLUICtrl *ctrl) { LLComboBox* combo = (LLComboBox*)ctrl; LLSelectMgr::getInstance()->setGridMode((EGridMode)combo->getCurrentIndex()); } -// static -void LLFloaterTools::onClickGridOptions(void* data) +void LLFloaterTools::onClickGridOptions() { //LLFloaterTools* floaterp = (LLFloaterTools*)data; LLFloaterBuildOptions::show(NULL); @@ -1055,18 +1089,866 @@ void LLFloaterTools::onClickGridOptions(void* data) //floaterp->addDependentFloater(LLFloaterBuildOptions::getInstance(), FALSE); } +// static void LLFloaterTools::setEditTool(void* tool_pointer) { LLTool *tool = (LLTool *)tool_pointer; LLToolMgr::getInstance()->getCurrentToolset()->selectTool( tool ); } +void LLFloaterTools::setTool(const LLSD& user_data) +{ + std::string control_name = user_data.asString(); + if(control_name == "Focus") + LLToolMgr::getInstance()->getCurrentToolset()->selectTool((LLTool *) LLToolCamera::getInstance() ); + else if (control_name == "Move" ) + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( (LLTool *)LLToolGrab::getInstance() ); + else if (control_name == "Edit" ) + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( (LLTool *) LLToolCompTranslate::getInstance()); + else if (control_name == "Create" ) + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( (LLTool *) LLToolCompCreate::getInstance()); + else if (control_name == "Land" ) + LLToolMgr::getInstance()->getCurrentToolset()->selectTool( (LLTool *) LLToolSelectLand::getInstance()); + else + llwarns<<" no parameter name "<setCurrentToolset(gBasicToolset); LLFloater::onFocusReceived(); } +// Media stuff +void LLFloaterTools::refreshMedia() +{ + if(!mTitleMedia) + return; + getMediaState(); +} + +bool LLFloaterTools::selectedMediaEditable() +{ + U32 owner_mask_on; + U32 owner_mask_off; + U32 valid_owner_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_OWNER, + &owner_mask_on, &owner_mask_off ); + U32 group_mask_on; + U32 group_mask_off; + U32 valid_group_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_GROUP, + &group_mask_on, &group_mask_off ); + U32 everyone_mask_on; + U32 everyone_mask_off; + S32 valid_everyone_perms = LLSelectMgr::getInstance()->selectGetPerm( PERM_EVERYONE, + &everyone_mask_on, &everyone_mask_off ); + + bool selected_Media_editable = false; + + // if perms we got back are valid + if ( valid_owner_perms && + valid_group_perms && + valid_everyone_perms ) + { + + if ( ( owner_mask_on & PERM_MODIFY ) || + ( group_mask_on & PERM_MODIFY ) || + ( group_mask_on & PERM_MODIFY ) ) + { + selected_Media_editable = true; + } + else + // user is NOT allowed to press the RESET button + { + selected_Media_editable = false; + }; + }; + + return selected_Media_editable; +} +void LLFloaterTools::getMediaState() +{ + LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); + LLViewerObject* first_object = selected_objects->getFirstObject(); + LLTextBox* media_info = getChild("media_info"); + + if( !(first_object + && first_object->getPCode() == LL_PCODE_VOLUME + &&first_object->permModify() + )) + { + getChildView("Add_Media")->setEnabled(FALSE); + media_info->setValue(""); + clearMediaSettings(); + return; + } + + std::string url = first_object->getRegion()->getCapability("ObjectMedia"); + bool has_media_capability = (!url.empty()); + + if(!has_media_capability) + { + getChildView("Add_Media")->setEnabled(FALSE); + LL_WARNS("LLFloaterTools: media") << "Media not enabled (no capability) in this region!" << LL_ENDL; + clearMediaSettings(); + return; + } + + BOOL is_nonpermanent_enforced = (LLSelectMgr::getInstance()->getSelection()->getFirstRootNode() + && LLSelectMgr::getInstance()->selectGetRootsNonPermanentEnforced()) + || LLSelectMgr::getInstance()->selectGetNonPermanentEnforced(); + bool editable = is_nonpermanent_enforced && (first_object->permModify() || selectedMediaEditable()); + + // Check modify permissions and whether any selected objects are in + // the process of being fetched. If they are, then we're not editable + if (editable) + { + LLObjectSelection::iterator iter = selected_objects->begin(); + LLObjectSelection::iterator end = selected_objects->end(); + for ( ; iter != end; ++iter) + { + LLSelectNode* node = *iter; + LLVOVolume* object = dynamic_cast(node->getObject()); + if (NULL != object) + { + if (!object->permModify()) + { + LL_INFOS("LLFloaterTools: media") + << "Selection not editable due to lack of modify permissions on object id " + << object->getID() << LL_ENDL; + + editable = false; + break; + } + // XXX DISABLE this for now, because when the fetch finally + // does come in, the state of this floater doesn't properly + // update. Re-selecting fixes the problem, but there is + // contention as to whether this is a sufficient solution. +// if (object->isMediaDataBeingFetched()) +// { +// LL_INFOS("LLFloaterTools: media") +// << "Selection not editable due to media data being fetched for object id " +// << object->getID() << LL_ENDL; +// +// editable = false; +// break; +// } + } + } + } + + // Media settings + bool bool_has_media = false; + struct media_functor : public LLSelectedTEGetFunctor + { + bool get(LLViewerObject* object, S32 face) + { + LLTextureEntry *te = object->getTE(face); + if (te) + { + return te->hasMedia(); + } + return false; + } + } func; + + + // check if all faces have media(or, all dont have media) + LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo = selected_objects->getSelectedTEValue( &func, bool_has_media ); + + const LLMediaEntry default_media_data; + + struct functor_getter_media_data : public LLSelectedTEGetFunctor< LLMediaEntry> + { + functor_getter_media_data(const LLMediaEntry& entry): mMediaEntry(entry) {} + + LLMediaEntry get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return *(object->getTE(face)->getMediaData()); + return mMediaEntry; + }; + + const LLMediaEntry& mMediaEntry; + + } func_media_data(default_media_data); + + LLMediaEntry media_data_get; + LLFloaterMediaSettings::getInstance()->mMultipleMedia = !(selected_objects->getSelectedTEValue( &func_media_data, media_data_get )); + + std::string multi_media_info_str = LLTrans::getString("Multiple Media"); + std::string media_title = ""; + mNeedMediaTitle = false; + // update UI depending on whether "object" (prim or face) has media + // and whether or not you are allowed to edit it. + + getChildView("Add_Media")->setEnabled(editable); + // IF all the faces have media (or all dont have media) + if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) + { + // TODO: get media title and set it. + media_info->setValue(""); + // if identical is set, all faces are same (whether all empty or has the same media) + if(!(LLFloaterMediaSettings::getInstance()->mMultipleMedia) ) + { + // Media data is valid + if(media_data_get!=default_media_data) + { + // initial media title is the media URL (until we get the name) + media_title = media_data_get.getHomeURL(); + + // kick off a navigate and flag that we need to update the title + navigateToTitleMedia( media_data_get.getHomeURL() ); + mNeedMediaTitle = true; + } + // else all faces might be empty. + } + else // there' re Different Medias' been set on on the faces. + { + media_title = multi_media_info_str; + mNeedMediaTitle = false; + } + + getChildView("media_tex")->setEnabled(bool_has_media && editable); + getChildView("edit_media")->setEnabled(bool_has_media && LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo && editable ); + getChildView("delete_media")->setEnabled(bool_has_media && editable ); + getChildView("add_media")->setEnabled(( ! bool_has_media ) && editable ); + // TODO: display a list of all media on the face - use 'identical' flag + } + else // not all face has media but at least one does. + { + // seleted faces have not identical value + LLFloaterMediaSettings::getInstance()->mMultipleValidMedia = selected_objects->isMultipleTEValue(&func_media_data, default_media_data ); + + if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) + { + media_title = multi_media_info_str; + mNeedMediaTitle = false; + } + else + { + // Media data is valid + if(media_data_get!=default_media_data) + { + // initial media title is the media URL (until we get the name) + media_title = media_data_get.getHomeURL(); + + // kick off a navigate and flag that we need to update the title + navigateToTitleMedia( media_data_get.getHomeURL() ); + mNeedMediaTitle = true; + } + } + + getChildView("media_tex")->setEnabled(TRUE); + getChildView("edit_media")->setEnabled(LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo); + getChildView("delete_media")->setEnabled(TRUE); + getChildView("add_media")->setEnabled(FALSE ); + } + media_info->setText(media_title); + + // load values for media settings + updateMediaSettings(); + + if(mTitleMedia) + LLFloaterMediaSettings::initValues(mMediaSettings, editable ); +} +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to add media to a prim or prim face +void LLFloaterTools::onClickBtnAddMedia() +{ + // check if multiple faces are selected + if(LLSelectMgr::getInstance()->getSelection()->isMultipleTESelected()) + { + LLNotificationsUtil::add("MultipleFacesSelected", LLSD(), LLSD(), multipleFacesSelectedConfirm); + } + else + { + onClickBtnEditMedia(); + } +} + +// static +bool LLFloaterTools::multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch( option ) + { + case 0: // "Yes" + gFloaterTools->onClickBtnEditMedia(); + break; + case 1: // "No" + default: + break; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to edit existing media settings on a prim or prim face +// TODO: test if there is media on the item and only allow editing if present +void LLFloaterTools::onClickBtnEditMedia() +{ + refreshMedia(); + LLFloaterMediaSettings::getInstance()->open(); + LLFloaterMediaSettings::getInstance()->setVisible(TRUE); + const LLRect& rect = getRect(); + U32 height_offset = rect.getHeight() - LLFloaterMediaSettings::getInstance()->getRect().getHeight(); + LLFloaterMediaSettings::getInstance()->setOrigin(rect.mRight, rect.mBottom + height_offset); +} + +////////////////////////////////////////////////////////////////////////////// +// called when a user wants to delete media from a prim or prim face +void LLFloaterTools::onClickBtnDeleteMedia() +{ + LLNotificationsUtil::add("DeleteMedia", LLSD(), LLSD(), deleteMediaConfirm); +} + + +// static +bool LLFloaterTools::deleteMediaConfirm(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + switch( option ) + { + case 0: // "Yes" + LLSelectMgr::getInstance()->selectionSetMedia( 0, LLSD() ); + if(LLFloaterMediaSettings::instanceExists()) + { + LLFloaterMediaSettings::getInstance()->close(); + } + break; + + case 1: // "No" + default: + break; + } + return false; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::clearMediaSettings() +{ + LLFloaterMediaSettings::clearValues(false); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::navigateToTitleMedia( const std::string url ) +{ + if ( mTitleMedia ) + { + LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); + if ( media_plugin ) + { + // if it's a movie, we don't want to hear it + media_plugin->setVolume( 0 ); + }; + mTitleMedia->navigateTo( url ); + }; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::updateMediaTitle() +{ + // only get the media name if we need it + if ( ! mNeedMediaTitle || !mTitleMedia ) + return; + + // get plugin impl + LLPluginClassMedia* media_plugin = mTitleMedia->getMediaPlugin(); + if ( media_plugin ) + { + // get the media name (asynchronous - must call repeatedly) + std::string media_title = media_plugin->getMediaName(); + + // only replace the title if what we get contains something + if ( ! media_title.empty() ) + { + // update the UI widget + LLTextBox* media_title_field = getChild("media_info"); + if ( media_title_field ) + { + media_title_field->setText( media_title ); + + // stop looking for a title when we get one + // FIXME: check this is the right approach + mNeedMediaTitle = false; + }; + }; + }; +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLFloaterTools::updateMediaSettings() +{ + bool identical( false ); + std::string base_key( "" ); + std::string value_str( "" ); + int value_int = 0; + bool value_bool = false; + LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); + // TODO: (CP) refactor this using something clever or boost or both !! + + const LLMediaEntry default_media_data; + + // controls + U8 value_u8 = default_media_data.getControls(); + struct functor_getter_controls : public LLSelectedTEGetFunctor< U8 > + { + functor_getter_controls(const LLMediaEntry &entry) : mMediaEntry(entry) {} + + U8 get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getControls(); + return mMediaEntry.getControls(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_controls(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_controls, value_u8 ); + base_key = std::string( LLMediaEntry::CONTROLS_KEY ); + mMediaSettings[ base_key ] = value_u8; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // First click (formerly left click) + value_bool = default_media_data.getFirstClickInteract(); + struct functor_getter_first_click : public LLSelectedTEGetFunctor< bool > + { + functor_getter_first_click(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getFirstClickInteract(); + return mMediaEntry.getFirstClickInteract(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_first_click(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_first_click, value_bool ); + base_key = std::string( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Home URL + value_str = default_media_data.getHomeURL(); + struct functor_getter_home_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_home_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getHomeURL(); + return mMediaEntry.getHomeURL(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_home_url(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_home_url, value_str ); + base_key = std::string( LLMediaEntry::HOME_URL_KEY ); + mMediaSettings[ base_key ] = value_str; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Current URL + value_str = default_media_data.getCurrentURL(); + struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getCurrentURL(); + return mMediaEntry.getCurrentURL(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_current_url(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_current_url, value_str ); + base_key = std::string( LLMediaEntry::CURRENT_URL_KEY ); + mMediaSettings[ base_key ] = value_str; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Auto zoom + value_bool = default_media_data.getAutoZoom(); + struct functor_getter_auto_zoom : public LLSelectedTEGetFunctor< bool > + { + + functor_getter_auto_zoom(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoZoom(); + return mMediaEntry.getAutoZoom(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_zoom(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_zoom, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_ZOOM_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Auto play + //value_bool = default_media_data.getAutoPlay(); + // set default to auto play TRUE -- angela EXT-5172 + value_bool = true; + struct functor_getter_auto_play : public LLSelectedTEGetFunctor< bool > + { + functor_getter_auto_play(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoPlay(); + //return mMediaEntry.getAutoPlay(); set default to auto play TRUE -- angela EXT-5172 + return true; + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_play(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_play, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_PLAY_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + + // Auto scale + // set default to auto scale TRUE -- angela EXT-5172 + //value_bool = default_media_data.getAutoScale(); + value_bool = true; + struct functor_getter_auto_scale : public LLSelectedTEGetFunctor< bool > + { + functor_getter_auto_scale(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoScale(); + // return mMediaEntry.getAutoScale(); set default to auto scale TRUE -- angela EXT-5172 + return true; + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_scale(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_scale, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_SCALE_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Auto loop + value_bool = default_media_data.getAutoLoop(); + struct functor_getter_auto_loop : public LLSelectedTEGetFunctor< bool > + { + functor_getter_auto_loop(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAutoLoop(); + return mMediaEntry.getAutoLoop(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_auto_loop(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_auto_loop, value_bool ); + base_key = std::string( LLMediaEntry::AUTO_LOOP_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // width pixels (if not auto scaled) + value_int = default_media_data.getWidthPixels(); + struct functor_getter_width_pixels : public LLSelectedTEGetFunctor< int > + { + functor_getter_width_pixels(const LLMediaEntry& entry): mMediaEntry(entry) {} + + int get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getWidthPixels(); + return mMediaEntry.getWidthPixels(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_width_pixels(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_width_pixels, value_int ); + base_key = std::string( LLMediaEntry::WIDTH_PIXELS_KEY ); + mMediaSettings[ base_key ] = value_int; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // height pixels (if not auto scaled) + value_int = default_media_data.getHeightPixels(); + struct functor_getter_height_pixels : public LLSelectedTEGetFunctor< int > + { + functor_getter_height_pixels(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + int get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getHeightPixels(); + return mMediaEntry.getHeightPixels(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_height_pixels(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_height_pixels, value_int ); + base_key = std::string( LLMediaEntry::HEIGHT_PIXELS_KEY ); + mMediaSettings[ base_key ] = value_int; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Enable Alt image + value_bool = default_media_data.getAltImageEnable(); + struct functor_getter_enable_alt_image : public LLSelectedTEGetFunctor< bool > + { + functor_getter_enable_alt_image(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getAltImageEnable(); + return mMediaEntry.getAltImageEnable(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_enable_alt_image(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_enable_alt_image, value_bool ); + base_key = std::string( LLMediaEntry::ALT_IMAGE_ENABLE_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - owner interact + value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_OWNER ); + struct functor_getter_perms_owner_interact : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_owner_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_OWNER)); + return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_OWNER ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_owner_interact(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_owner_interact, value_bool ); + base_key = std::string( LLPanelContents::PERMS_OWNER_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - owner control + value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_OWNER ); + struct functor_getter_perms_owner_control : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_owner_control(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_OWNER)); + return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_OWNER ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_owner_control(default_media_data); + identical = selected_objects ->getSelectedTEValue( &func_perms_owner_control, value_bool ); + base_key = std::string( LLPanelContents::PERMS_OWNER_CONTROL_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - group interact + value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_GROUP ); + struct functor_getter_perms_group_interact : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_group_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_GROUP)); + return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_GROUP ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_group_interact(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_group_interact, value_bool ); + base_key = std::string( LLPanelContents::PERMS_GROUP_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - group control + value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_GROUP ); + struct functor_getter_perms_group_control : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_group_control(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_GROUP)); + return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_GROUP ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_group_control(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_group_control, value_bool ); + base_key = std::string( LLPanelContents::PERMS_GROUP_CONTROL_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - anyone interact + value_bool = 0 != ( default_media_data.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); + struct functor_getter_perms_anyone_interact : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_anyone_interact(const LLMediaEntry& entry): mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsInteract() & LLMediaEntry::PERM_ANYONE)); + return 0 != ( mMediaEntry.getPermsInteract() & LLMediaEntry::PERM_ANYONE ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_anyone_interact(default_media_data); + identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_perms_anyone_interact, value_bool ); + base_key = std::string( LLPanelContents::PERMS_ANYONE_INTERACT_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // Perms - anyone control + value_bool = 0 != ( default_media_data.getPermsControl() & LLMediaEntry::PERM_ANYONE ); + struct functor_getter_perms_anyone_control : public LLSelectedTEGetFunctor< bool > + { + functor_getter_perms_anyone_control(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return (0 != (object->getTE(face)->getMediaData()->getPermsControl() & LLMediaEntry::PERM_ANYONE)); + return 0 != ( mMediaEntry.getPermsControl() & LLMediaEntry::PERM_ANYONE ); + }; + + const LLMediaEntry &mMediaEntry; + + } func_perms_anyone_control(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_perms_anyone_control, value_bool ); + base_key = std::string( LLPanelContents::PERMS_ANYONE_CONTROL_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // security - whitelist enable + value_bool = default_media_data.getWhiteListEnable(); + struct functor_getter_whitelist_enable : public LLSelectedTEGetFunctor< bool > + { + functor_getter_whitelist_enable(const LLMediaEntry& entry) : mMediaEntry(entry) {} + + bool get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getWhiteListEnable(); + return mMediaEntry.getWhiteListEnable(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_whitelist_enable(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_whitelist_enable, value_bool ); + base_key = std::string( LLMediaEntry::WHITELIST_ENABLE_KEY ); + mMediaSettings[ base_key ] = value_bool; + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; + + // security - whitelist URLs + std::vector value_vector_str = default_media_data.getWhiteList(); + struct functor_getter_whitelist_urls : public LLSelectedTEGetFunctor< std::vector > + { + functor_getter_whitelist_urls(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::vector get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getWhiteList(); + return mMediaEntry.getWhiteList(); + }; + + const LLMediaEntry &mMediaEntry; + + } func_whitelist_urls(default_media_data); + identical = selected_objects->getSelectedTEValue( &func_whitelist_urls, value_vector_str ); + base_key = std::string( LLMediaEntry::WHITELIST_KEY ); + mMediaSettings[ base_key ].clear(); + std::vector< std::string >::iterator iter = value_vector_str.begin(); + while( iter != value_vector_str.end() ) + { + std::string white_list_url = *iter; + mMediaSettings[ base_key ].append( white_list_url ); + ++iter; + }; + + mMediaSettings[ base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ) ] = ! identical; +} + // static void LLFloaterTools::onSelectTreesGrass(LLUICtrl*, void*) { @@ -1140,3 +2022,4 @@ void LLFloaterTools::updateTreeGrassCombo(bool visible) mComboTreesGrass->setVisible(visible); tree_grass_label->setVisible(visible); } + diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h index 90cdf83eb..fec736819 100644 --- a/indra/newview/llfloatertools.h +++ b/indra/newview/llfloatertools.h @@ -49,6 +49,7 @@ class LLPanelLandInfo; class LLSlider; class LLTabContainer; class LLTextBox; +class LLMediaCtrl; class LLTool; class LLParcelSelection; class LLObjectSelection; @@ -71,11 +72,11 @@ public: LLFloaterTools(); virtual ~LLFloaterTools(); - virtual void onOpen(); - virtual BOOL canClose(); - virtual void onClose(bool app_quitting); + /*virtual*/ void onOpen(); + /*virtual*/ BOOL canClose(); + /*virtual*/ void onClose(bool app_quitting); /*virtual*/ void draw(); - virtual void onFocusReceived(); + /*virtual*/ void onFocusReceived(); // call this once per frame to handle visibility, rect location, // button highlights, etc. @@ -100,13 +101,25 @@ public: void setStatusText(const std::string& text); static void setEditTool(void* data); + void setTool(const LLSD& user_data); void saveLastTool(); + void onClickBtnDeleteMedia(); + void onClickBtnAddMedia(); + void onClickBtnEditMedia(); + void clearMediaSettings(); + void updateMediaTitle(); + void navigateToTitleMedia( const std::string url ); + bool selectedMediaEditable(); private: void refresh(); - + void refreshMedia(); + void getMediaState(); + void updateMediaSettings(); + static bool deleteMediaConfirm(const LLSD& notification, const LLSD& response); + static bool multipleFacesSelectedConfirm(const LLSD& notification, const LLSD& response); static void setObjectType( LLPCode pcode ); - static void onClickGridOptions(void* data); + void onClickGridOptions(); public: LLButton *mBtnFocus; @@ -190,6 +203,8 @@ public: LLParcelSelectionHandle mParcelSelection; LLObjectSelectionHandle mObjectSelection; + LLMediaCtrl *mTitleMedia; + bool mNeedMediaTitle; private: BOOL mDirty; @@ -197,6 +212,8 @@ private: void updateTreeGrassCombo(bool visible); static void onSelectTreesGrass(LLUICtrl*, void*); +protected: + LLSD mMediaSettings; }; extern LLFloaterTools *gFloaterTools; diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index dd6ea2864..65171b007 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -32,12 +32,15 @@ #include "llviewerprecompiledheaders.h" +#include "llhttpclient.h" + #include "llfloaterurlentry.h" #include "llpanellandmedia.h" +#include "llpanelface.h" -// project includes #include "llcombobox.h" +#include "llmimetypes.h" #include "llnotificationsutil.h" #include "llurlhistory.h" #include "lluictrlfactory.h" @@ -83,7 +86,7 @@ public: { // Set empty type to none/none. Empty string is reserved for legacy parcels // which have no mime type set. - std::string resolved_mime_type = ! mime_type.empty() ? mime_type : "none/none"; + std::string resolved_mime_type = ! mime_type.empty() ? mime_type : LLMIMETypes::getDefaultMimeType(); LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mParent.get(); if ( floater_url_entry ) floater_url_entry->headerFetchComplete( status, resolved_mime_type ); @@ -165,6 +168,16 @@ void LLFloaterURLEntry::headerFetchComplete(U32 status, const std::string& mime_ panel_media->setMediaType(mime_type); panel_media->setMediaURL(mMediaURLEdit->getValue().asString()); } + else + { + LLPanelFace* panel_face = dynamic_cast(mPanelLandMediaHandle.get()); + if(panel_face) + { + panel_face->setMediaType(mime_type); + panel_face->setMediaURL(mMediaURLEdit->getValue().asString()); + } + + } // Decrement the cursor getWindow()->decBusyCount(); getChildView("loading_label")->setVisible( false); @@ -172,26 +185,18 @@ void LLFloaterURLEntry::headerFetchComplete(U32 status, const std::string& mime_ } // static -LLHandle LLFloaterURLEntry::show(LLHandle parent) +LLHandle LLFloaterURLEntry::show(LLHandle parent, const std::string media_url) { if (!sInstance) { sInstance = new LLFloaterURLEntry(parent); } sInstance->open(); - sInstance->updateFromLandMediaPanel(); + sInstance->addURLToCombobox(media_url); return sInstance->getHandle(); } -void LLFloaterURLEntry::updateFromLandMediaPanel() -{ - LLPanelLandMedia* panel_media = (LLPanelLandMedia*)mPanelLandMediaHandle.get(); - if (panel_media) - { - std::string media_url = panel_media->getMediaURL(); - addURLToCombobox(media_url); - } -} + bool LLFloaterURLEntry::addURLToCombobox(const std::string& media_url) { diff --git a/indra/newview/llfloaterurlentry.h b/indra/newview/llfloaterurlentry.h index 0aeca823b..6dd9c8453 100644 --- a/indra/newview/llfloaterurlentry.h +++ b/indra/newview/llfloaterurlentry.h @@ -44,10 +44,8 @@ class LLFloaterURLEntry : public LLFloater public: // Can only be shown by LLPanelLandMedia, and pushes data back into // that panel via the handle. - static LLHandle show(LLHandle panel_land_media_handle); + static LLHandle show(LLHandle panel_land_media_handle, const std::string media_url); /*virtual*/ BOOL postBuild(); - void updateFromLandMediaPanel(); - void headerFetchComplete(U32 status, const std::string& mime_type); bool addURLToCombobox(const std::string& media_url); diff --git a/indra/newview/llfloatervoiceeffect.cpp b/indra/newview/llfloatervoiceeffect.cpp new file mode 100644 index 000000000..415798086 --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.cpp @@ -0,0 +1,318 @@ +/** + * @file llfloatervoiceeffect.cpp + * @author Aimee + * @brief Selection and preview of voice effect. + * + * $LicenseInfo:firstyear=2010&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 "llfloatervoiceeffect.h" + +#include "llscrolllistctrl.h" +#include "lltexteditor.h" // For linked text hack +#include "lltrans.h" +#include "lluictrlfactory.h" +#include "llweb.h" + +LLFloaterVoiceEffect::LLFloaterVoiceEffect(const LLSD& key) + : LLFloater(/*key*/) +{ + mCommitCallbackRegistrar.add("VoiceEffect.Record", boost::bind(&LLFloaterVoiceEffect::onClickRecord, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Play", boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Stop", boost::bind(&LLFloaterVoiceEffect::onClickStop, this)); + mCommitCallbackRegistrar.add("VoiceEffect.Activate", boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_voice_effect.xml"); +} + +// virtual +LLFloaterVoiceEffect::~LLFloaterVoiceEffect() +{ + if(LLVoiceClient::instanceExists()) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->removeObserver(this); + } + } +} + +// virtual +BOOL LLFloaterVoiceEffect::postBuild() +{ + setDefaultBtn("record_btn"); + getChild("record_btn")->setFocus(true); + + // Singu Note: Here we must hack together a linked piece of text + if (LLTextEditor* editor = getChild("voice_morphing_link")) + { + editor->setParseHTML(true); // For some reason, adding style doesn't work unless this is true. + const std::string text = editor->getValue(); + editor->clear(); + LLStyleSP link(new LLStyle); + link->setLinkHREF(LLTrans::getString("voice_morphing_url")); + link->setColor(gSavedSettings.getColor4("HTMLLinkColor")); + editor->appendStyledText(text, false, false, link); + } + + mVoiceEffectList = getChild("voice_effect_list"); + if (mVoiceEffectList) + { + mVoiceEffectList->setCommitCallback(boost::bind(&LLFloaterVoiceEffect::onClickPlay, this)); +// mVoiceEffectList->setDoubleClickCallback(boost::bind(&LLFloaterVoiceEffect::onClickActivate, this)); + } + + return TRUE; +} + +// virtual +void LLFloaterVoiceEffect::onOpen() +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->addObserver(this); + + // Disconnect from the current voice channel ready to record a voice sample for previewing + effect_interface->enablePreviewBuffer(true); + } + + refreshEffectList(); + updateControls(); +} + +// virtual +void LLFloaterVoiceEffect::onClose(bool app_quitting) +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->enablePreviewBuffer(false); + } + setVisible(false); +} + +void LLFloaterVoiceEffect::refreshEffectList() +{ + if (!mVoiceEffectList) + { + return; + } + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) + { + mVoiceEffectList->setEnabled(false); + return; + } + + LL_DEBUGS("Voice")<< "Rebuilding Voice Morph list."<< LL_ENDL; + + // Preserve selected items and scroll position + S32 scroll_pos = mVoiceEffectList->getScrollPos(); + uuid_vec_t selected_items; + std::vector items = mVoiceEffectList->getAllSelected(); + for(std::vector::const_iterator it = items.begin(); it != items.end(); it++) + { + selected_items.push_back((*it)->getUUID()); + } + + mVoiceEffectList->deleteAllItems(); + + { + // Add the "No Voice Morph" entry + LLSD element; + + element["id"] = LLUUID::null; + element["columns"][NAME_COLUMN]["column"] = "name"; + element["columns"][NAME_COLUMN]["value"] = getString("no_voice_effect"); + element["columns"][NAME_COLUMN]["font"] = "SANSSERIF"; + element["columns"][NAME_COLUMN]["font-style"] = "BOLD"; + + /*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM); + /* Singu Note: Ours works + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + ((LLScrollListText*)sl_item->getColumn(0))->setFontStyle(LLFontGL::BOLD); + } + */ + } + + // Add each Voice Morph template, if there are any (template list includes all usable effects) + const voice_effect_list_t& template_list = effect_interface->getVoiceEffectTemplateList(); + if (!template_list.empty()) + { + for (voice_effect_list_t::const_iterator it = template_list.begin(); it != template_list.end(); ++it) + { + const LLUUID& effect_id = it->second; + + std::string localized_effect = "effect_" + it->first; + std::string effect_name = hasString(localized_effect) ? getString(localized_effect) : it->first; // XML contains localized effects names + + LLSD effect_properties = effect_interface->getVoiceEffectProperties(effect_id); + + // Tag the active effect. + if (effect_id == LLVoiceClient::instance().getVoiceEffectDefault()) + { + effect_name += " " + getString("active_voice_effect"); + } + + // Tag available effects that are new this session + if (effect_properties["is_new"].asBoolean()) + { + effect_name += " " + getString("new_voice_effect"); + } + + LLDate expiry_date = effect_properties["expiry_date"].asDate(); + bool is_template_only = effect_properties["template_only"].asBoolean(); + + std::string font_style = "NORMAL"; + if (!is_template_only) + { + font_style = "BOLD"; + } + + LLSD element; + element["id"] = effect_id; + + element["columns"][NAME_COLUMN]["column"] = "name"; + element["columns"][NAME_COLUMN]["value"] = effect_name; + element["columns"][NAME_COLUMN]["font"] = "SANSSERIF"; + element["columns"][NAME_COLUMN]["font-style"] = font_style; + + element["columns"][1]["column"] = "expires"; + if (!is_template_only) + { + element["columns"][DATE_COLUMN]["value"] = expiry_date; + element["columns"][DATE_COLUMN]["type"] = "date"; + } + else { + element["columns"][DATE_COLUMN]["value"] = getString("unsubscribed_voice_effect"); + } + element["columns"][DATE_COLUMN]["font"] = "SANSSERIF"; + element["columns"][DATE_COLUMN]["font-style"] = "NORMAL"; + + /*LLScrollListItem* sl_item =*/ mVoiceEffectList->addElement(element, ADD_BOTTOM); + /* Singu Note: Ours works + // *HACK: Copied from llfloatergesture.cpp : ["font"]["style"] does not affect font style :( + if(sl_item) + { + LLFontGL::StyleFlags style = is_template_only ? LLFontGL::NORMAL : LLFontGL::BOLD; + LLScrollListText* slt = dynamic_cast(sl_item->getColumn(0)); + llassert(slt); + if (slt) + { + slt->setFontStyle(style); + } + } + */ + } + } + + // Re-select items that were selected before, and restore the scroll position + for(uuid_vec_t::iterator it = selected_items.begin(); it != selected_items.end(); it++) + { + mVoiceEffectList->selectByID(*it); + } + mVoiceEffectList->setScrollPos(scroll_pos); + mVoiceEffectList->setEnabled(true); +} + +void LLFloaterVoiceEffect::updateControls() +{ + bool recording = false; + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + recording = effect_interface->isPreviewRecording(); + } + + getChild("record_btn")->setVisible(!recording); + getChild("record_stop_btn")->setVisible(recording); +} + +// virtual +void LLFloaterVoiceEffect::onVoiceEffectChanged(bool effect_list_updated) +{ + if (effect_list_updated) + { + refreshEffectList(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickRecord() +{ + LL_DEBUGS("Voice") << "Record clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->recordPreviewBuffer(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickPlay() +{ + LL_DEBUGS("Voice") << "Play clicked" << LL_ENDL; + if (!mVoiceEffectList) + { + return; + } + + const LLUUID& effect_id = mVoiceEffectList->getCurrentID(); + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->playPreviewBuffer(effect_id); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickStop() +{ + LL_DEBUGS("Voice") << "Stop clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->stopPreviewBuffer(); + } + updateControls(); +} + +void LLFloaterVoiceEffect::onClickActivate() +{ + LL_DEBUGS("Voice") << "Activate clicked" << LL_ENDL; + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface && mVoiceEffectList) + { + //effect_interface->setVoiceEffect(mVoiceEffectList->getCurrentID()); // Singu Note: This would return early, since we're disconnected from voice, just set the string and refresh list + gSavedPerAccountSettings.setString("VoiceEffectDefault", mVoiceEffectList->getCurrentID().asString()); + refreshEffectList(); + } +} + diff --git a/indra/newview/llfloatervoiceeffect.h b/indra/newview/llfloatervoiceeffect.h new file mode 100644 index 000000000..9bee8937c --- /dev/null +++ b/indra/newview/llfloatervoiceeffect.h @@ -0,0 +1,74 @@ +/** + * @file llfloatervoiceeffect.h + * @author Aimee + * @brief Selection and preview of voice effects. + * + * $LicenseInfo:firstyear=2010&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$ + */ + +#ifndef LL_LLFLOATERVOICEEFFECT_H +#define LL_LLFLOATERVOICEEFFECT_H + +#include "llfloater.h" +#include "llvoiceclient.h" + +class LLButton; +class LLScrollListCtrl; + +class LLFloaterVoiceEffect + : public LLFloater + , public LLVoiceEffectObserver + , public LLFloaterSingleton +{ +public: + LOG_CLASS(LLFloaterVoiceEffect); + + LLFloaterVoiceEffect(const LLSD& key); + virtual ~LLFloaterVoiceEffect(); + + virtual BOOL postBuild(); + virtual void onOpen(); + virtual void onClose(bool app_quitting); + +private: + enum ColumnIndex + { + NAME_COLUMN = 0, + DATE_COLUMN = 1, + }; + + void refreshEffectList(); + void updateControls(); + + /// Called by voice effect provider when voice effect list is changed. + virtual void onVoiceEffectChanged(bool effect_list_updated); + + void onClickRecord(); + void onClickPlay(); + void onClickStop(); + void onClickActivate(); + + LLUUID mSelectedID; + LLScrollListCtrl* mVoiceEffectList; +}; + +#endif diff --git a/indra/newview/llfloaterwater.cpp b/indra/newview/llfloaterwater.cpp index 7f4173056..f444a7cbc 100644 --- a/indra/newview/llfloaterwater.cpp +++ b/indra/newview/llfloaterwater.cpp @@ -159,7 +159,7 @@ void LLFloaterWater::onClickHelp(void* data) LLFloaterWater* self = LLFloaterWater::instance(); const std::string* xml_alert = (std::string*)data; - LLNotifications::instance().add(self->contextualNotification(*xml_alert)); + self->addContextualNotification(*xml_alert); } void LLFloaterWater::initHelpBtn(const std::string& name, const std::string& xml_alert) @@ -175,7 +175,7 @@ bool LLFloaterWater::newPromptCallback(const LLSD& notification, const LLSD& res return false; } - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(option == 0) { LLWaterParamManager * param_mgr = LLWaterParamManager::getInstance(); diff --git a/indra/newview/llfloaterwebcontent.cpp b/indra/newview/llfloaterwebcontent.cpp new file mode 100644 index 000000000..c47a893ff --- /dev/null +++ b/indra/newview/llfloaterwebcontent.cpp @@ -0,0 +1,517 @@ +/** + * @file llfloaterwebcontent.cpp + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&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 "llcombobox.h" +#include "lliconctrl.h" +#include "lllayoutstack.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "lltextbox.h" +#include "llurlhistory.h" +#include "llviewercontrol.h" +#include "llweb.h" +#include "llwindow.h" +#include "lluictrlfactory.h" + +#include "llfloaterwebcontent.h" + +LLFloaterWebContent::_Params::_Params() +: url("url"), + target("target"), + id("id"), + window_class("window_class", "web_content"), + show_chrome("show_chrome", true), + allow_address_entry("allow_address_entry", true), + preferred_media_size("preferred_media_size"), + trusted_content("trusted_content", false), + show_page_title("show_page_title", true) +{} + +LLFloaterWebContent::LLFloaterWebContent( const Params& params ) +: LLFloater( params.id ), + LLInstanceTracker(params.id()), + mWebBrowser(NULL), + mAddressCombo(NULL), + mSecureLockIcon(NULL), + mStatusBarText(NULL), + mStatusBarProgress(NULL), + mBtnBack(NULL), + mBtnForward(NULL), + mBtnReload(NULL), + mBtnStop(NULL), + mUUID(params.id()), + mShowPageTitle(params.show_page_title), + mKey(params) +{ + mCommitCallbackRegistrar.add( "WebContent.Back", boost::bind( &LLFloaterWebContent::onClickBack, this )); + mCommitCallbackRegistrar.add( "WebContent.Forward", boost::bind( &LLFloaterWebContent::onClickForward, this )); + mCommitCallbackRegistrar.add( "WebContent.Reload", boost::bind( &LLFloaterWebContent::onClickReload, this )); + mCommitCallbackRegistrar.add( "WebContent.Stop", boost::bind( &LLFloaterWebContent::onClickStop, this )); + mCommitCallbackRegistrar.add( "WebContent.EnterAddress", boost::bind( &LLFloaterWebContent::onEnterAddress, this )); + mCommitCallbackRegistrar.add( "WebContent.PopExternal", boost::bind( &LLFloaterWebContent::onPopExternal, this )); + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_web_content.xml"); + mAgeTimer.reset(); +} + +BOOL LLFloaterWebContent::postBuild() +{ + // these are used in a bunch of places so cache them + mWebBrowser = getChild< LLMediaCtrl >( "webbrowser" ); + mAddressCombo = getChild< LLComboBox >( "address" ); + mStatusBarText = getChild< LLTextBox >( "statusbartext" ); + mStatusBarProgress = getChild("statusbarprogress" ); + + mBtnBack = getChildView( "back" ); + mBtnForward = getChildView( "forward" ); + mBtnReload = getChildView( "reload" ); + mBtnStop = getChildView( "stop" ); + + // observe browser events + mWebBrowser->addObserver( this ); + + // these buttons are always enabled + mBtnReload->setEnabled( true ); + getChildView("popexternal")->setEnabled( true ); + + // cache image for secure browsing + mSecureLockIcon = getChild< LLIconCtrl >("media_secure_lock_flag"); + + // initialize the URL history using the system URL History manager + initializeURLHistory(); + + return TRUE; +} + +void LLFloaterWebContent::initializeURLHistory() +{ + // start with an empty list + LLCtrlListInterface* url_list = childGetListInterface("address"); + if (url_list) + { + url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); + } + + // Get all of the entries in the "browser" collection + LLSD browser_history = LLURLHistory::getURLHistory("browser"); + LLSD::array_iterator iter_history = browser_history.beginArray(); + LLSD::array_iterator end_history = browser_history.endArray(); + for(; iter_history != end_history; ++iter_history) + { + std::string url = (*iter_history).asString(); + if(! url.empty()) + url_list->addSimpleElement(url); + } +} + +bool LLFloaterWebContent::matchesKey(const LLSD& key) +{ + Params p(mKey); + Params other_p(key); + if (!other_p.target().empty() && other_p.target() != "_blank") + { + return other_p.target() == p.target(); + } + else + { + return other_p.id() == p.id(); + } +} + +//static +void LLFloaterWebContent::showInstance(const std::string& window_class, Params& p) +{ + p.window_class(window_class); + + LLSD key = p; + + instance_iter it = beginInstances(); + for(;it!=endInstances();++it) + { + if(it->mKey["window_class"].asString() == window_class) + { + if(it->matchesKey(key)) + { + it->mKey = key; + it->setKey(p.id()); + it->mAgeTimer.reset(); + it->open(); + return; + } + } + } + LLFloaterWebContent* old_inst = getInstance(p.id()); + if(old_inst) + { + llwarns << "Replacing unexpected duplicate floater: " << p.id() << llendl; + old_inst->mKey = key; + old_inst->mAgeTimer.reset(); + old_inst->open(); + } + assert(!old_inst); + + if(!old_inst) + LLFloaterWebContent::create(p); +} + +//static +LLFloater* LLFloaterWebContent::create( Params p) +{ + preCreate(p); + return new LLFloaterWebContent(p); +} + +//static +void LLFloaterWebContent::closeRequest(const std::string &uuid) +{ + LLFloaterWebContent* floaterp = instance_tracker_t::getInstance(uuid); + if (floaterp) + { + floaterp->close(); + } +} + +//static +void LLFloaterWebContent::geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height) +{ + LLFloaterWebContent* floaterp = instance_tracker_t::getInstance(uuid); + if (floaterp) + { + floaterp->geometryChanged(x, y, width, height); + } +} + +void LLFloaterWebContent::geometryChanged(S32 x, S32 y, S32 width, S32 height) +{ + // Make sure the layout of the browser control is updated, so this calculation is correct. + getChild("stack1")->updateLayout(); + + // TODO: need to adjust size and constrain position to make sure floaters aren't moved outside the window view, etc. + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + + // Adjust width and height for the size of the chrome on the web Browser window. + LLRect browser_rect; + mWebBrowser->localRectToOtherView(mWebBrowser->getLocalRect(), &browser_rect, this); + + S32 requested_browser_bottom = window_size.mY - (y + height); + LLRect geom; + geom.setOriginAndSize(x - browser_rect.mLeft, + requested_browser_bottom - browser_rect.mBottom, + width + getRect().getWidth() - browser_rect.getWidth(), + height + getRect().getHeight() - browser_rect.getHeight()); + + lldebugs << "geometry change: " << geom << llendl; + + LLRect new_rect; + getParent()->screenRectToLocal(geom, &new_rect); + setShape(new_rect); +} + +// static +void LLFloaterWebContent::preCreate(LLFloaterWebContent::Params& p) +{ + lldebugs << "url = " << p.url() << ", target = " << p.target() << ", uuid = " << p.id() << llendl; + + if (!p.id.isProvided()) + { + p.id = LLUUID::generateNewID().asString(); + } + + if(p.target().empty() || p.target() == "_blank") + { + p.target = p.id(); + } + + S32 browser_window_limit = gSavedSettings.getS32("WebContentWindowLimit"); + if(browser_window_limit != 0) + { + // showInstance will open a new window. Figure out how many web browsers are already open, + // and close the least recently opened one if this will put us over the limit. + + std::vector instances; + instances.reserve(instanceCount()); + instance_iter it = beginInstances(); + for(;it!=endInstances();++it) + { + if(it->mKey["window_class"].asString() == p.window_class.getValue()) + instances.push_back(&*it); + } + + std::sort(instances.begin(), instances.end(), CompareAgeDescending()); + + //LLFloaterReg::const_instance_list_t &instances = LLFloaterReg::getFloaterList(p.window_class); + lldebugs << "total instance count is " << instances.size() << llendl; + + for(std::vector::const_iterator iter = instances.begin(); iter != instances.end(); iter++) + { + lldebugs << " " << (*iter)->mKey["target"] << llendl; + } + + if(instances.size() >= (size_t)browser_window_limit) + { + // Destroy the least recently opened instance + (*instances.begin())->close(); + } + } +} + +void LLFloaterWebContent::open_media(const Params& p) +{ + // Specifying a mime type of text/html here causes the plugin system to skip the MIME type probe and just open a browser plugin. + LLViewerMedia::proxyWindowOpened(p.target(), p.id()); + mWebBrowser->setHomePageUrl(p.url, "text/html"); + mWebBrowser->setTarget(p.target); + mWebBrowser->navigateTo(p.url, "text/html"); + + set_current_url(p.url); + + getChild("status_bar")->setVisible(p.show_chrome); + getChild("nav_controls")->setVisible(p.show_chrome); + bool address_entry_enabled = p.allow_address_entry && !p.trusted_content; + getChildView("address")->setEnabled(address_entry_enabled); + getChildView("popexternal")->setEnabled(address_entry_enabled); + + if (!address_entry_enabled) + { + mWebBrowser->setFocus(TRUE); + } + + if (!p.show_chrome) + { + setResizeLimits(100, 100); + } + + if (!p.preferred_media_size().isEmpty()) + { + getChild("stack1")->updateLayout(); + LLRect browser_rect = mWebBrowser->calcScreenRect(); + LLCoordWindow window_size; + getWindow()->getSize(&window_size); + + geometryChanged(browser_rect.mLeft, window_size.mY - browser_rect.mTop, p.preferred_media_size().getWidth(), p.preferred_media_size().getHeight()); + } + +} + +void LLFloaterWebContent::onOpen() +{ + Params params(mKey); + + if (!params.validateBlock()) + { + close(); + return; + } + + mWebBrowser->setTrustedContent(params.trusted_content); + + // tell the browser instance to load the specified URL + open_media(params); +} + +//virtual +void LLFloaterWebContent::onClose(bool app_quitting) +{ + LLViewerMedia::proxyWindowClosed(mUUID); + destroy(); +} + +// virtual +void LLFloaterWebContent::draw() +{ + // this is asynchronous so we need to keep checking + mBtnBack->setEnabled( mWebBrowser->canNavigateBack() ); + mBtnForward->setEnabled( mWebBrowser->canNavigateForward() ); + + LLFloater::draw(); +} + +// virtual +void LLFloaterWebContent::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + if(event == MEDIA_EVENT_LOCATION_CHANGED) + { + const std::string url = self->getLocation(); + + if ( url.length() ) + mStatusBarText->setText( url ); + + set_current_url( url ); + } + else if(event == MEDIA_EVENT_NAVIGATE_BEGIN) + { + // flags are sent with this event + mBtnBack->setEnabled( self->getHistoryBackAvailable() ); + mBtnForward->setEnabled( self->getHistoryForwardAvailable() ); + + // toggle visibility of these buttons based on browser state + mBtnReload->setVisible( false ); + mBtnStop->setVisible( true ); + + // turn "on" progress bar now we're about to start loading + mStatusBarProgress->setVisible( true ); + } + else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) + { + // flags are sent with this event + mBtnBack->setEnabled( self->getHistoryBackAvailable() ); + mBtnForward->setEnabled( self->getHistoryForwardAvailable() ); + + // toggle visibility of these buttons based on browser state + mBtnReload->setVisible( true ); + mBtnStop->setVisible( false ); + + // turn "off" progress bar now we're loaded + mStatusBarProgress->setVisible( false ); + + // we populate the status bar with URLs as they change so clear it now we're done + const std::string end_str = ""; + mStatusBarText->setText( end_str ); + + // decide if secure browsing icon should be displayed + std::string prefix = std::string("https://"); + std::string test_prefix = mCurrentURL.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if(test_prefix == prefix) + { + mSecureLockIcon->setVisible(true); + } + else + { + mSecureLockIcon->setVisible(false); + } + } + else if(event == MEDIA_EVENT_CLOSE_REQUEST) + { + // The browser instance wants its window closed. + close(); + } + else if(event == MEDIA_EVENT_GEOMETRY_CHANGE) + { + geometryChanged(self->getGeometryX(), self->getGeometryY(), self->getGeometryWidth(), self->getGeometryHeight()); + } + else if(event == MEDIA_EVENT_STATUS_TEXT_CHANGED ) + { + const std::string text = self->getStatusText(); + if ( text.length() ) + mStatusBarText->setText( text ); + } + else if(event == MEDIA_EVENT_PROGRESS_UPDATED ) + { + int percent = (int)self->getProgressPercent(); + mStatusBarProgress->setPercent( percent ); + } + else if(event == MEDIA_EVENT_NAME_CHANGED ) + { + std::string page_title = self->getMediaName(); + // simulate browser behavior - title is empty, use the current URL + if (mShowPageTitle) + { + if ( page_title.length() > 0 ) + setTitle( page_title ); + else + setTitle( mCurrentURL ); + } + } + else if(event == MEDIA_EVENT_LINK_HOVERED ) + { + const std::string link = self->getHoverLink(); + mStatusBarText->setText( link ); + } +} + +void LLFloaterWebContent::set_current_url(const std::string& url) +{ + mCurrentURL = url; + + // serialize url history into the system URL History manager + LLURLHistory::removeURL("browser", mCurrentURL); + LLURLHistory::addURL("browser", mCurrentURL); + + mAddressCombo->remove( mCurrentURL ); + mAddressCombo->add( mCurrentURL ); + mAddressCombo->selectByValue( mCurrentURL ); +} + +void LLFloaterWebContent::onClickForward() +{ + mWebBrowser->navigateForward(); +} + +void LLFloaterWebContent::onClickBack() +{ + mWebBrowser->navigateBack(); +} + +void LLFloaterWebContent::onClickReload() +{ + + if( mWebBrowser->getMediaPlugin() ) + { + bool ignore_cache = true; + mWebBrowser->getMediaPlugin()->browse_reload( ignore_cache ); + } + else + { + mWebBrowser->navigateTo(mCurrentURL); + } +} + +void LLFloaterWebContent::onClickStop() +{ + if( mWebBrowser->getMediaPlugin() ) + mWebBrowser->getMediaPlugin()->browse_stop(); + + // still should happen when we catch the navigate complete event + // but sometimes (don't know why) that event isn't sent from Qt + // and we ghetto a point where the stop button stays active. + mBtnReload->setVisible( true ); + mBtnStop->setVisible( false ); +} + +void LLFloaterWebContent::onEnterAddress() +{ + // make sure there is at least something there. + // (perhaps this test should be for minimum length of a URL) + std::string url = mAddressCombo->getValue().asString(); + if ( url.length() > 0 ) + { + mWebBrowser->navigateTo( url, "text/html"); + }; +} + +void LLFloaterWebContent::onPopExternal() +{ + // make sure there is at least something there. + // (perhaps this test should be for minimum length of a URL) + std::string url = mAddressCombo->getValue().asString(); + if ( url.length() > 0 ) + { + LLWeb::loadURLExternal( url ); + }; +} diff --git a/indra/newview/llfloaterwebcontent.h b/indra/newview/llfloaterwebcontent.h new file mode 100644 index 000000000..00d8c9532 --- /dev/null +++ b/indra/newview/llfloaterwebcontent.h @@ -0,0 +1,125 @@ +/** + * @file llfloaterwebcontent.h + * @brief floater for displaying web content - e.g. profiles and search (eventually) + * + * $LicenseInfo:firstyear=2006&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$ + */ + +#ifndef LL_LLFLOATERWEBCONTENT_H +#define LL_LLFLOATERWEBCONTENT_H + +#include "llfloater.h" +#include "llmediactrl.h" +#include "llsdparam.h" + +class LLMediaCtrl; +class LLComboBox; +class LLTextBox; +class LLProgressBar; +class LLIconCtrl; + +class LLFloaterWebContent : + public LLFloater, + public LLViewerMediaObserver, + public LLInstanceTracker +{ +public: + typedef LLInstanceTracker instance_tracker_t; + LOG_CLASS(LLFloaterWebContent); + + struct _Params : public LLInitParam::Block<_Params> + { + Optional url, + target, + window_class, + id; + Optional show_chrome, + allow_address_entry, + trusted_content, + show_page_title; + Optional preferred_media_size; + + _Params(); + }; + + typedef LLSDParamAdapter<_Params> Params; + + LLFloaterWebContent(const Params& params); + + void initializeURLHistory(); + + static LLFloater* create(Params); + + static void showInstance(const std::string& window_class, Params& p); + static void closeRequest(const std::string &uuid); + static void geometryChanged(const std::string &uuid, S32 x, S32 y, S32 width, S32 height); + void geometryChanged(S32 x, S32 y, S32 width, S32 height); + + /* virtual */ BOOL postBuild(); + /* virtual */ void onOpen(); + /* virtual */ bool matchesKey(const LLSD& key); + /* virtual */ void onClose(bool app_quitting); + /* virtual */ void draw(); + +protected: + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + + void onClickBack(); + void onClickForward(); + void onClickReload(); + void onClickStop(); + void onEnterAddress(); + void onPopExternal(); + + static void preCreate(Params& p); + void open_media(const Params& ); + void set_current_url(const std::string& url); + + LLMediaCtrl* mWebBrowser; + LLComboBox* mAddressCombo; + LLIconCtrl* mSecureLockIcon; + LLTextBox* mStatusBarText; + LLProgressBar* mStatusBarProgress; + + LLView* mBtnBack; + LLView* mBtnForward; + LLView* mBtnReload; + LLView* mBtnStop; + + std::string mCurrentURL; + std::string mUUID; + bool mShowPageTitle; + + LLSD mKey; + LLTimer mAgeTimer; + + struct CompareAgeDescending + { + bool operator()(const LLFloaterWebContent* const& lhs, const LLFloaterWebContent* const& rhs) + { + return lhs->mAgeTimer.getElapsedTimeF64() > lhs->mAgeTimer.getElapsedTimeF64(); + } + }; +}; + +#endif // LL_LLFLOATERWEBCONTENT_H diff --git a/indra/newview/llfloaterwhitelistentry.cpp b/indra/newview/llfloaterwhitelistentry.cpp new file mode 100644 index 000000000..f930e0940 --- /dev/null +++ b/indra/newview/llfloaterwhitelistentry.cpp @@ -0,0 +1,91 @@ +/** + * @file llfloaterwhitelistentry.cpp + * @brief LLFloaterWhistListEntry class implementation + * + * $LicenseInfo:firstyear=2007&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 "llfloatermediasettings.h" +#include "llfloaterwhitelistentry.h" +#include "llpanelmediasettingssecurity.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "lllineeditor.h" + + +/////////////////////////////////////////////////////////////////////////////// +// +LLFloaterWhiteListEntry::LLFloaterWhiteListEntry() : + LLFloater() +{ + LLUICtrlFactory::getInstance()->buildFloater(this, "floater_whitelist_entry.xml"); +} + +/////////////////////////////////////////////////////////////////////////////// +// +LLFloaterWhiteListEntry::~LLFloaterWhiteListEntry() +{ +} + +/////////////////////////////////////////////////////////////////////////////// +// +BOOL LLFloaterWhiteListEntry::postBuild() +{ + mWhiteListEdit = getChild("whitelist_entry"); + + childSetAction("cancel_btn", onBtnCancel, this); + childSetAction("ok_btn", onBtnOK, this); + + setDefaultBtn("ok_btn"); + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterWhiteListEntry::onBtnOK( void* userdata ) +{ + LLFloaterWhiteListEntry *self =(LLFloaterWhiteListEntry *)userdata; + + LLPanelMediaSettingsSecurity* panel = LLFloaterMediaSettings::instanceExists() ? LLFloaterMediaSettings::getInstance()->getPanelSecurity() : NULL; + if ( panel ) + { + std::string white_list_item = self->mWhiteListEdit->getText(); + + panel->addWhiteListEntry( white_list_item ); + panel->updateWhitelistEnableStatus(); + }; + + self->close(); +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLFloaterWhiteListEntry::onBtnCancel( void* userdata ) +{ + LLFloaterWhiteListEntry *self =(LLFloaterWhiteListEntry *)userdata; + + self->close(); +} diff --git a/indra/newview/llfloaterwhitelistentry.h b/indra/newview/llfloaterwhitelistentry.h new file mode 100644 index 000000000..da23b1ebf --- /dev/null +++ b/indra/newview/llfloaterwhitelistentry.h @@ -0,0 +1,50 @@ +/** + * @file llfloaterwhitelistentry.h + * @brief LLFloaterWhiteListEntry class definition + * + * $LicenseInfo:firstyear=2007&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$ + */ + +#ifndef LL_LLFLOATERWHITELISTENTRY_H +#define LL_LLFLOATERWHITELISTENTRY_H + +#include "llfloater.h" + +class LLLineEditor; + +class LLFloaterWhiteListEntry : + public LLFloater, public LLSingleton +{ + public: + LLFloaterWhiteListEntry(); + ~LLFloaterWhiteListEntry(); + + BOOL postBuild(); + + private: + LLLineEditor* mWhiteListEdit; + + static void onBtnOK(void*); + static void onBtnCancel(void*); +}; + +#endif // LL_LLFLOATERWHITELISTENTRY_H diff --git a/indra/newview/llfloaterwindlight.cpp b/indra/newview/llfloaterwindlight.cpp index b5a23367c..7e9eb6a1f 100644 --- a/indra/newview/llfloaterwindlight.cpp +++ b/indra/newview/llfloaterwindlight.cpp @@ -213,7 +213,7 @@ void LLFloaterWindLight::onClickHelp(void* data) LLFloaterWindLight* self = LLFloaterWindLight::instance(); const std::string xml_alert = *(std::string*)data; - LLNotifications::instance().add(self->contextualNotification(xml_alert)); + self->addContextualNotification(xml_alert); } void LLFloaterWindLight::initHelpBtn(const std::string& name, const std::string& xml_alert) diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index e3532cfe8..0f6eb2cdf 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -75,6 +75,7 @@ #include "llmapimagetype.h" #include "llweb.h" #include "llwindow.h" // copyTextToClipboard() +#include "llslurl.h" // [RLVa:KB] @@ -728,7 +729,7 @@ void LLFloaterWorldMap::updateLocation() // Figure out where user is // Set the current SLURL - mSLURL = LLURLDispatcher::buildSLURL(agent_sim_name, x, y, z); + mSLURL = LLSLURL(agent_sim_name, LLVector3(x, y, z)).getSLURLString(); // [RLVa:KB] - Checked: 2009-07-04 (RLVa-1.0.0a) if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWLOC)) @@ -774,7 +775,7 @@ void LLFloaterWorldMap::updateLocation() S32 x = llround( (F32)fmod( (F32)coord_pos[VX], (F32)REGION_WIDTH_METERS ) ); S32 y = llround( (F32)fmod( (F32)coord_pos[VY], (F32)REGION_WIDTH_METERS ) ); S32 z = llround( (F32)coord_pos[VZ] ); - mSLURL = LLURLDispatcher::buildSLURL(sim_name, x, y, z); + mSLURL = LLSLURL(sim_name, LLVector3(x, y, z)).getSLURLString(); } else { // Empty SLURL will disable the "Copy SLURL to clipboard" button diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index 5efe05ccc..ade7e1d1f 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -42,7 +42,7 @@ #include "groupchatlistener.h" #include "hippolimits.h" // for getMaxAgentGroups // [RLVa:KB] - Checked: 2011-03-28 (RLVa-1.3.0f) -//#include "llslurl.h" +#include "llslurl.h" #include "rlvhandler.h" // [/RLVa:KB] @@ -55,7 +55,7 @@ class LLGroupHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLGroupHandler() : LLCommandHandler("group", true/*UNTRUSTED_THROTTLE*/) { } + LLGroupHandler() : LLCommandHandler("group", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { @@ -128,7 +128,6 @@ void LLGroupActions::search() LLFloaterDirectory::showGroups(); } -/* Singu TODO: Voice refactor // static void LLGroupActions::startCall(const LLUUID& group_id) { @@ -162,7 +161,6 @@ void LLGroupActions::startCall(const LLUUID& group_id) make_ui_sound("UISndStartIM"); } -*/ // static void LLGroupActions::join(const LLUUID& group_id) @@ -402,7 +400,7 @@ LLUUID LLGroupActions::startIM(const LLUUID& group_id) if ( (rlv_handler_t::isEnabled()) && (!gRlvHandler.canStartIM(group_id)) && (!gIMMgr->hasSession(group_id)) ) { make_ui_sound("UISndInvalidOp"); - RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", group_id/*LLSLURL("group", group_id, "about").getSLURLString()*/)); + RlvUtil::notifyBlocked(RLV_STRING_BLOCKED_STARTIM, LLSD().with("RECIPIENT", LLSLURL("group", group_id, "about").getSLURLString())); return LLUUID::null; } // [/RLVa:KB] diff --git a/indra/newview/llhoverview.cpp b/indra/newview/llhoverview.cpp index 2427a89cb..dd53f11d6 100644 --- a/indra/newview/llhoverview.cpp +++ b/indra/newview/llhoverview.cpp @@ -70,6 +70,9 @@ #include "llviewertexturelist.h" //#include "lltoolobjpicker.h" #include "llhudmanager.h" // HACK for creating flex obj's +#include "llviewermenu.h" +#include "llviewermedia.h" +#include "llmediaentry.h" #include "llhudmanager.h" // For testing effects #include "llhudeffect.h" @@ -294,201 +297,257 @@ void LLHoverView::updateText() // BOOL suppressObjectHoverDisplay = !gSavedSettings.getBOOL("ShowAllObjectHoverTip"); - LLSelectNode *nodep = LLSelectMgr::getInstance()->getHoverNode();; + LLSelectNode *nodep = LLSelectMgr::getInstance()->getHoverNode(); if (nodep) { line.clear(); - if (nodep->mName.empty()) - { - line.append(LLTrans::getString("TooltipNoName")); - } - else - { - line.append( nodep->mName ); - } - mText.push_back(line); - if (!nodep->mDescription.empty() - && nodep->mDescription != DEFAULT_DESC) + bool for_copy = nodep->mValid && nodep->mPermissions->getMaskEveryone() & PERM_COPY && hit_object && hit_object->permCopy(); + bool for_sale = nodep->mValid && for_sale_selection(nodep); + + bool has_media = false; + bool is_time_based_media = false; + bool is_web_based_media = false; + bool is_media_playing = false; + bool is_media_displaying = false; + + // Does this face have media? + const LLTextureEntry* tep = hit_object ? hit_object->getTE(mLastPickInfo.mObjectFace) : NULL; + + if(tep) { - mText.push_back( nodep->mDescription ); - } - - // Line: "Owner: James Linden" - line.clear(); - line.append(LLTrans::getString("TooltipOwner") + " "); - - if (nodep->mValid) - { - LLUUID owner; - std::string name; - if (!nodep->mPermissions->isGroupOwned()) + has_media = tep->hasMedia(); + const LLMediaEntry* mep = has_media ? tep->getMediaData() : NULL; + if (mep) { - owner = nodep->mPermissions->getOwner(); - if (LLUUID::null == owner) + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); + LLPluginClassMedia* media_plugin = NULL; + + if (media_impl.notNull() && (media_impl->hasMedia())) { - line.append(LLTrans::getString("TooltipPublic")); - } - else if(LLAvatarNameCache::getPNSName(owner, name)) - { -// [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) - if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) - { - name = RlvStrings::getAnonym(name); + is_media_displaying = true; + //LLStringUtil::format_map_t args; + + media_plugin = media_impl->getMediaPlugin(); + if(media_plugin) + { + if(media_plugin->pluginSupportsMediaTime()) + { + is_time_based_media = true; + is_web_based_media = false; + //args["[CurrentURL]"] = media_impl->getMediaURL(); + is_media_playing = media_impl->isMediaPlaying(); + } + else + { + is_time_based_media = false; + is_web_based_media = true; + //args["[CurrentURL]"] = media_plugin->getLocation(); + } + //tooltip_msg.append(LLTrans::getString("CurrentURL", args)); } -// [/RLVa:KB] + } + } + } - line.append(name); + + // Avoid showing tip over media that's displaying unless it's for sale + // also check the primary node since sometimes it can have an action even though + // the root node doesn't + + if(!suppressObjectHoverDisplay || !is_media_displaying || for_sale) + { + if (nodep->mName.empty()) + { + line.append(LLTrans::getString("TooltipNoName")); + } + else + { + line.append( nodep->mName ); + } + + mText.push_back(line); + + if (!nodep->mDescription.empty() + && nodep->mDescription != DEFAULT_DESC) + { + mText.push_back( nodep->mDescription ); + } + + // Line: "Owner: James Linden" + line.clear(); + line.append(LLTrans::getString("TooltipOwner") + " "); + + if (nodep->mValid) + { + LLUUID owner; + std::string name; + if (!nodep->mPermissions->isGroupOwned()) + { + owner = nodep->mPermissions->getOwner(); + if (LLUUID::null == owner) + { + line.append(LLTrans::getString("TooltipPublic")); + } + else if(LLAvatarNameCache::getPNSName(owner, name)) + { + // [RLVa:KB] - Checked: 2009-07-08 (RLVa-1.0.0e) + if (gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) + { + name = RlvStrings::getAnonym(name); + } + // [/RLVa:KB] + + line.append(name); + } + else + { + line.append(LLTrans::getString("RetrievingData")); + } } else { - line.append(LLTrans::getString("RetrievingData")); + std::string name; + owner = nodep->mPermissions->getGroup(); + if (gCacheName->getGroupName(owner, name)) + { + line.append(name); + line.append(LLTrans::getString("TooltipIsGroup")); + } + else + { + line.append(LLTrans::getString("RetrievingData")); + } } } else { - std::string name; - owner = nodep->mPermissions->getGroup(); - if (gCacheName->getGroupName(owner, name)) - { - line.append(name); - line.append(LLTrans::getString("TooltipIsGroup")); - } - else - { - line.append(LLTrans::getString("RetrievingData")); - } - } - } - else - { - line.append(LLTrans::getString("RetrievingData")); - } - mText.push_back(line); - - // Build a line describing any special properties of this object. - LLViewerObject *object = hit_object; - LLViewerObject *parent = (LLViewerObject *)object->getParent(); - - if (object && - (object->flagUsePhysics() || - object->flagScripted() || - object->flagHandleTouch() || (parent && parent->flagHandleTouch()) || - object->flagTakesMoney() || (parent && parent->flagTakesMoney()) || - object->flagAllowInventoryAdd() || - object->flagTemporary() || - object->flagPhantom()) ) - { - line.clear(); - if (object->flagScripted()) - { - - line.append(LLTrans::getString("TooltipFlagScript") + " "); - } - - if (object->flagUsePhysics()) - { - line.append(LLTrans::getString("TooltipFlagPhysics") + " "); - } - - if (object->flagHandleTouch() || (parent && parent->flagHandleTouch()) ) - { - line.append(LLTrans::getString("TooltipFlagTouch") + " "); - suppressObjectHoverDisplay = FALSE; // Show tip - } - - if (object->flagTakesMoney() || (parent && parent->flagTakesMoney()) ) - { - line.append(gHippoGridManager->getConnectedGrid()->getCurrencySymbol() + " "); - suppressObjectHoverDisplay = FALSE; // Show tip - } - - if (object->flagAllowInventoryAdd()) - { - line.append(LLTrans::getString("TooltipFlagDropInventory") + " "); - suppressObjectHoverDisplay = FALSE; // Show tip - } - - if (object->flagPhantom()) - { - line.append(LLTrans::getString("TooltipFlagPhantom") + " "); - } - - if (object->flagTemporary()) - { - line.append(LLTrans::getString("TooltipFlagTemporary") + " "); - } - - if (object->flagUsePhysics() || - object->flagHandleTouch() || - (parent && parent->flagHandleTouch()) ) - { - line.append(LLTrans::getString("TooltipFlagRightClickMenu") + " "); + line.append(LLTrans::getString("RetrievingData")); } mText.push_back(line); - } - // Free to copy / For Sale: L$ - line.clear(); - if (nodep->mValid) - { - BOOL for_copy = nodep->mPermissions->getMaskEveryone() & PERM_COPY && object->permCopy(); - BOOL for_sale = nodep->mSaleInfo.isForSale() && - nodep->mPermissions->getMaskOwner() & PERM_TRANSFER && - (nodep->mPermissions->getMaskOwner() & PERM_COPY || - nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY); - if (for_copy) + // Build a line describing any special properties of this object. + LLViewerObject *object = hit_object; + LLViewerObject *parent = (LLViewerObject *)object->getParent(); + + if (object && + (object->flagUsePhysics() || + object->flagScripted() || + object->flagHandleTouch() || (parent && parent->flagHandleTouch()) || + object->flagTakesMoney() || (parent && parent->flagTakesMoney()) || + object->flagAllowInventoryAdd() || + object->flagTemporary() || + object->flagPhantom()) ) { - line.append(LLTrans::getString("TooltipFreeToCopy")); - suppressObjectHoverDisplay = FALSE; // Show tip + line.clear(); + if (object->flagScripted()) + { + + line.append(LLTrans::getString("TooltipFlagScript") + " "); + } + + if (object->flagUsePhysics()) + { + line.append(LLTrans::getString("TooltipFlagPhysics") + " "); + } + + if (object->flagHandleTouch() || (parent && parent->flagHandleTouch()) ) + { + line.append(LLTrans::getString("TooltipFlagTouch") + " "); + suppressObjectHoverDisplay = FALSE; // Show tip + } + + if (object->flagTakesMoney() || (parent && parent->flagTakesMoney()) ) + { + line.append(gHippoGridManager->getConnectedGrid()->getCurrencySymbol() + " "); + suppressObjectHoverDisplay = FALSE; // Show tip + } + + if (object->flagAllowInventoryAdd()) + { + line.append(LLTrans::getString("TooltipFlagDropInventory") + " "); + suppressObjectHoverDisplay = FALSE; // Show tip + } + + if (object->flagPhantom()) + { + line.append(LLTrans::getString("TooltipFlagPhantom") + " "); + } + + if (object->flagTemporary()) + { + line.append(LLTrans::getString("TooltipFlagTemporary") + " "); + } + + if (object->flagUsePhysics() || + object->flagHandleTouch() || + (parent && parent->flagHandleTouch()) ) + { + line.append(LLTrans::getString("TooltipFlagRightClickMenu") + " "); + } + mText.push_back(line); } - else if (for_sale) + + // Free to copy / For Sale: L$ + line.clear(); + if (nodep->mValid) { - LLStringUtil::format_map_t args; - args["[AMOUNT]"] = llformat("%d", nodep->mSaleInfo.getSalePrice()); - line.append(LLTrans::getString("TooltipForSaleL$", args)); - suppressObjectHoverDisplay = FALSE; // Show tip + if (for_copy) + { + line.append(LLTrans::getString("TooltipFreeToCopy")); + suppressObjectHoverDisplay = FALSE; // Show tip + } + else if (for_sale) + { + LLStringUtil::format_map_t args; + args["[AMOUNT]"] = llformat("%d", nodep->mSaleInfo.getSalePrice()); + line.append(LLTrans::getString("TooltipForSaleL$", args)); + suppressObjectHoverDisplay = FALSE; // Show tip + } + else + { + // Nothing if not for sale + // line.append("Not for sale"); + } } else { - // Nothing if not for sale - // line.append("Not for sale"); + LLStringUtil::format_map_t args; + args["[MESSAGE]"] = LLTrans::getString("RetrievingData"); + line.append(LLTrans::getString("TooltipForSaleMsg", args)); } + mText.push_back(line); + line.clear(); + S32 prim_count = LLSelectMgr::getInstance()->getHoverObjects()->getObjectCount(); + line.append(llformat("Prims: %d", prim_count)); + mText.push_back(line); + + line.clear(); + line.append("Position: "); + + LLViewerRegion *region = gAgent.getRegion(); + LLVector3 position = region->getPosRegionFromGlobal(hit_object->getPositionGlobal());//regionp->getOriginAgent(); + LLVector3 mypos = region->getPosRegionFromGlobal(gAgent.getPositionGlobal()); + + + LLVector3 delta = position - mypos; + F32 distance = (F32)delta.magVec(); + + line.append(llformat("<%.02f,%.02f,%.02f>",position.mV[0],position.mV[1],position.mV[2])); + mText.push_back(line); + line.clear(); + line.append(llformat("Distance: %.02fm",distance)); + mText.push_back(line); } else { - LLStringUtil::format_map_t args; - args["[MESSAGE]"] = LLTrans::getString("RetrievingData"); - line.append(LLTrans::getString("TooltipForSaleMsg", args)); + suppressObjectHoverDisplay = TRUE; + } + // If the hover tip shouldn't be shown, delete all the object text + if (suppressObjectHoverDisplay) + { + mText.clear(); } - mText.push_back(line); - } - line.clear(); - S32 prim_count = LLSelectMgr::getInstance()->getHoverObjects()->getObjectCount(); - line.append(llformat("Prims: %d", prim_count)); - mText.push_back(line); - - line.clear(); - line.append("Position: "); - - LLViewerRegion *region = gAgent.getRegion(); - LLVector3 position = region->getPosRegionFromGlobal(hit_object->getPositionGlobal());//regionp->getOriginAgent(); - LLVector3 mypos = region->getPosRegionFromGlobal(gAgent.getPositionGlobal()); - - - LLVector3 delta = position - mypos; - F32 distance = (F32)delta.magVec(); - - line.append(llformat("<%.02f,%.02f,%.02f>",position.mV[0],position.mV[1],position.mV[2])); - mText.push_back(line); - line.clear(); - line.append(llformat("Distance: %.02fm",distance)); - mText.push_back(line); - - // If the hover tip shouldn't be shown, delete all the object text - if (suppressObjectHoverDisplay) - { - mText.clear(); } } } diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index edd499610..88d8467df 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -315,7 +315,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mSentTypingState(TRUE), mNumUnreadMessages(0), mShowSpeakersOnConnect(TRUE), - mAutoConnect(FALSE), + mStartCallOnInitialize(false), mTextIMPossible(TRUE), mProfileButtonEnabled(TRUE), mCallBackEnabled(TRUE), @@ -352,7 +352,7 @@ LLFloaterIMPanel::LLFloaterIMPanel( mTypingLineStartIndex(0), mSentTypingState(TRUE), mShowSpeakersOnConnect(TRUE), - mAutoConnect(FALSE), + mStartCallOnInitialize(false), mTextIMPossible(TRUE), mProfileButtonEnabled(TRUE), mCallBackEnabled(TRUE), @@ -574,8 +574,8 @@ BOOL LLFloaterIMPanel::postBuild() if (LLUICtrl* ctrl = findChild("rp_mode")) ctrl->setCommitCallback(boost::bind(&LLFloaterIMPanel::onRPMode, this, _2)); - childSetAction("start_call_btn", onClickStartCall, this); - childSetAction("end_call_btn", onClickEndCall, this); + getChild("start_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::startCall, gIMMgr, mSessionUUID, LLVoiceChannel::OUTGOING_CALL)); + getChild("end_call_btn")->setCommitCallback(boost::bind(&LLIMMgr::endCall, gIMMgr, mSessionUUID)); getChild("send_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onSendMsg,this)); if (LLButton* btn = findChild("toggle_active_speakers_btn")) btn->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickToggleActiveSpeakers, this, _2)); @@ -600,8 +600,8 @@ BOOL LLFloaterIMPanel::postBuild() if (mDialog == IM_NOTHING_SPECIAL) { - childSetAction("mute_btn", onClickMuteVoice, this); - childSetCommitCallback("speaker_volume", onVolumeChange, this); + getChild("mute_btn")->setCommitCallback(boost::bind(&LLFloaterIMPanel::onClickMuteVoice, this)); + getChild("speaker_volume")->setCommitCallback(boost::bind(&LLVoiceClient::setUserVolume, LLVoiceClient::getInstance(), mOtherParticipantUUID, _2)); } setDefaultBtn("send_btn"); @@ -625,33 +625,16 @@ void* LLFloaterIMPanel::createSpeakersPanel(void* data) return floaterp->mSpeakerPanel; } -//static -void LLFloaterIMPanel::onClickMuteVoice(void* user_data) +void LLFloaterIMPanel::onClickMuteVoice() { - LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; - if (floaterp) + LLMute mute(mOtherParticipantUUID, getTitle(), LLMute::AGENT); + if (!LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)) { - BOOL is_muted = LLMuteList::getInstance()->isMuted(floaterp->mOtherParticipantUUID, LLMute::flagVoiceChat); - - LLMute mute(floaterp->mOtherParticipantUUID, floaterp->getTitle(), LLMute::AGENT); - if (!is_muted) - { - LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); - } - else - { - LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); - } + LLMuteList::getInstance()->add(mute, LLMute::flagVoiceChat); } -} - -//static -void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data) -{ - LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data; - if (floaterp) + else { - LLVoiceClient::getInstance()->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); + LLMuteList::getInstance()->remove(mute, LLMute::flagVoiceChat); } } @@ -670,7 +653,7 @@ void LLFloaterIMPanel::draw() mEndCallBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); mStartCallBtn->setVisible(LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel->getState() < LLVoiceChannel::STATE_CALL_STARTED); mStartCallBtn->setEnabled(enable_connect); - mSendBtn->setEnabled(!childGetValue("chat_editor").asString().empty()); + mSendBtn->setEnabled(!mInputEditor->getValue().asString().empty()); LLPointer self_speaker = mSpeakers->findSpeaker(gAgent.getID()); if(!mTextIMPossible) @@ -689,12 +672,6 @@ void LLFloaterIMPanel::draw() mInputEditor->setLabel(getString("default_text_label")); } - if (mAutoConnect && enable_connect) - { - onClickStartCall(this); - mAutoConnect = FALSE; - } - // show speakers window when voice first connects if (mShowSpeakersOnConnect && mVoiceChannel->isActive()) { @@ -1111,14 +1088,6 @@ void LLFloaterIMPanel::onClickStartCall(void* userdata) self->mVoiceChannel->activate(); } -// static -void LLFloaterIMPanel::onClickEndCall(void* userdata) -{ - LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata; - - self->getVoiceChannel()->deactivate(); -} - void LLFloaterIMPanel::onClickToggleActiveSpeakers(const LLSD& value) { childSetVisible("active_speakers_panel", !value); @@ -1454,11 +1423,6 @@ LL_WARNS("Splitting") << "Pos: " << pos << " next_split: " << next_split << LL_E mSentTypingState = TRUE; } -void LLFloaterIMPanel::updateSpeakersList(const LLSD& speaker_updates) -{ - mSpeakers->updateSpeakers(speaker_updates); -} - void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) { if ( @@ -1482,11 +1446,6 @@ void LLFloaterIMPanel::processSessionUpdate(const LLSD& session_update) } } -void LLFloaterIMPanel::setSpeakers(const LLSD& speaker_list) -{ - mSpeakers->setSpeakers(speaker_list); -} - void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) { mSessionUUID = session_id; @@ -1512,11 +1471,12 @@ void LLFloaterIMPanel::sessionInitReplyReceived(const LLUUID& session_id) mOtherParticipantUUID, mDialog); } -} -void LLFloaterIMPanel::requestAutoConnect() -{ - mAutoConnect = TRUE; + // auto-start the call on session initialization? + if (mStartCallOnInitialize) + { + gIMMgr->startCall(mSessionUUID); + } } void LLFloaterIMPanel::setTyping(BOOL typing) diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index 7d4d1fe71..0f659a820 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -121,20 +121,15 @@ public: static void* createSpeakersPanel(void* data); //callbacks for P2P muting and volume control - static void onClickMuteVoice(void* user_data); - static void onVolumeChange(LLUICtrl* source, void* user_data); + void onClickMuteVoice(); const LLUUID& getSessionID() const { return mSessionUUID; } const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; } - void updateSpeakersList(const LLSD& speaker_updates); void processSessionUpdate(const LLSD& update); - void setSpeakers(const LLSD& speaker_list); LLVoiceChannel* getVoiceChannel() { return mVoiceChannel; } LLIMSpeakerMgr* getSpeakerManager() const { return mSpeakers; } // Singu TODO: LLIMModel::getSpeakerManager EInstantMessage getDialogType() const { return mDialog; } - void requestAutoConnect(); - void sessionInitReplyReceived(const LLUUID& im_session_id); // Handle other participant in the session typing. @@ -161,6 +156,10 @@ public: bool isGroupSessionType() const { return mSessionType == GROUP_SESSION;} SType mSessionType; + // LLIMModel Functionality + bool getSessionInitialized() const { return mSessionInitialized; } + bool mStartCallOnInitialize; + private: // called by constructors void init(const std::string& session_label); @@ -236,8 +235,6 @@ private: BOOL mShowSpeakersOnConnect; - BOOL mAutoConnect; - BOOL mTextIMPossible; BOOL mProfileButtonEnabled; BOOL mCallBackEnabled; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index 06ac47b5b..837b76ff8 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -53,6 +53,7 @@ #include "llfloaterchatterbox.h" #include "llhttpnode.h" #include "llimpanel.h" +#include "llnotificationsutil.h" #include "llsdserialize.h" #include "llspeakers.h" #include "lltabcontainer.h" @@ -60,7 +61,6 @@ #include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerwindow.h" -#include "llvoicechannel.h" #include "llnotify.h" #include "llviewerregion.h" @@ -115,6 +115,7 @@ LLColor4 agent_chat_color(const LLUUID& id, const std::string& name, bool local_ //{ // return (LLStringUtil::compareDict( a->mName, b->mName ) < 0); //} + class LLViewerChatterBoxInvitationAcceptResponder : public LLHTTPClient::ResponderWithResult { public: @@ -130,10 +131,9 @@ public: { if ( gIMMgr) { - LLFloaterIMPanel* floaterp = - gIMMgr->findFloaterBySession(mSessionID); - - if (floaterp) + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(mSessionID); + LLIMSpeakerMgr* speaker_mgr = floater ? floater->getSpeakerManager() : NULL; + if (speaker_mgr) { //we've accepted our invitation //and received a list of agents that were @@ -147,26 +147,26 @@ public: //but unfortunately, our base that we are receiving here //may not be the most up to date. It was accurate at //some point in time though. - floaterp->setSpeakers(content); + speaker_mgr->setSpeakers(content); //we now have our base of users in the session //that was accurate at some point, but maybe not now //so now we apply all of the udpates we've received //in case of race conditions - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(mSessionID)); + speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(mSessionID)); + } - if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE ) - { - floaterp->requestAutoConnect(); - LLFloaterIMPanel::onClickStartCall(floaterp); - // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(TRUE); - } - else if ( mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE ) - { - LLFloaterChatterBox::showInstance(TRUE); - } + if (LLIMMgr::INVITATION_TYPE_VOICE == mInvitiationType) + { + gIMMgr->startCall(mSessionID, LLVoiceChannel::INCOMING_CALL); + } + + if ((mInvitiationType == LLIMMgr::INVITATION_TYPE_VOICE + || mInvitiationType == LLIMMgr::INVITATION_TYPE_IMMEDIATE) + && gIMMgr->hasSession(mSessionID)) + { + // always open IM window when connecting to voice + LLFloaterChatterBox::showInstance(TRUE); } gIMMgr->clearPendingAgentListUpdates(mSessionID); @@ -176,6 +176,8 @@ public: /*virtual*/ void error(U32 statusNum, const std::string& reason) { + llwarns << "LLViewerChatterBoxInvitationAcceptResponder error [status:" + << statusNum << "]: " << reason << llendl; //throw something back to the viewer here? if ( gIMMgr ) { @@ -286,11 +288,14 @@ protected: bool inviteUserResponse(const LLSD& notification, const LLSD& response) { + if (!gIMMgr) + return false; + const LLSD& payload = notification["payload"]; LLUUID session_id = payload["session_id"].asUUID(); EInstantMessage type = (EInstantMessage)payload["type"].asInteger(); LLIMMgr::EInvitationType inv_type = (LLIMMgr::EInvitationType)payload["inv_type"].asInteger(); - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch(option) { case 0: // accept @@ -304,16 +309,10 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) payload["session_handle"].asString(), payload["session_uri"].asString()); - LLFloaterIMPanel* im_floater = - gIMMgr->findFloaterBySession( - session_id); - if (im_floater) - { - im_floater->requestAutoConnect(); - LLFloaterIMPanel::onClickStartCall(im_floater); - // always open IM window when connecting to voice - LLFloaterChatterBox::showInstance(session_id); - } + gIMMgr->startCall(session_id); + + // always open IM window when connecting to voice + LLFloaterChatterBox::showInstance(session_id); gIMMgr->clearPendingAgentListUpdates(session_id); gIMMgr->clearPendingInvitation(session_id); @@ -355,11 +354,8 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response) { if (type == IM_SESSION_P2P_INVITE) { - if(LLVoiceClient::instanceExists()) - { - std::string s = payload["session_handle"].asString(); - LLVoiceClient::getInstance()->declineInvite(s); - } + std::string s = payload["session_handle"].asString(); + LLVoiceClient::getInstance()->declineInvite(s); } else { @@ -688,6 +684,22 @@ BOOL LLIMMgr::isIMSessionOpen(const LLUUID& uuid) return FALSE; } +void LLIMMgr::autoStartCallOnStartup(const LLUUID& session_id) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = findFloaterBySession(session_id); + if (!floater) return; + + if (floater->getSessionInitialized()) + { + startCall(session_id); + } + else + { + floater->mStartCallOnInitialize = true; + } +} + LLUUID LLIMMgr::addP2PSession(const std::string& name, const LLUUID& other_participant_id, const std::string& voice_session_handle, @@ -1025,6 +1037,31 @@ void LLIMMgr::clearPendingInvitation(const LLUUID& session_id) } } +void LLIMMgr::processAgentListUpdates(const LLUUID& session_id, const LLSD& body) +{ + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + if (!im_floater) return; + LLIMSpeakerMgr* speaker_mgr = im_floater->getSpeakerManager(); + if (speaker_mgr) + { + speaker_mgr->updateSpeakers(body); + + // also the same call is added into LLVoiceClient::participantUpdatedEvent because + // sometimes it is called AFTER LLViewerChatterBoxSessionAgentListUpdates::post() + // when moderation state changed too late. See EXT-3544. + speaker_mgr->update(true); + } + else + { + //we don't have a speaker manager yet..something went wrong + //we are probably receiving an update here before + //a start or an acceptance of an invitation. Race condition. + gIMMgr->addPendingAgentListUpdates( + session_id, + body); + } +} + LLSD LLIMMgr::getPendingAgentListUpdates(const LLUUID& session_id) { if ( mPendingAgentListUpdates.has(session_id.asString()) ) @@ -1105,6 +1142,39 @@ void LLIMMgr::clearPendingAgentListUpdates(const LLUUID& session_id) } } +bool LLIMMgr::startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (!floater) return false; + + LLVoiceChannel* voice_channel = floater->getVoiceChannel(); + if (!voice_channel) return false; + + voice_channel->setCallDirection(direction); + voice_channel->activate(); + return true; +} + +bool LLIMMgr::endCall(const LLUUID& session_id) +{ + // Singu TODO: LLIMModel + LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(session_id); + if (!floater) return false; + + LLVoiceChannel* voice_channel = floater->getVoiceChannel(); + if (!voice_channel) return false; + + voice_channel->deactivate(); + /*LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id); + if (im_session)*/ + { + // need to update speakers' state + floater->getSpeakerManager()->update(FALSE); + } + return true; +} + // create a floater and update internal representation for // consistency. Returns the pointer, caller (the class instance since // it is a private method) is not responsible for deleting the @@ -1418,35 +1488,30 @@ public: if ( success ) { session_id = body["session_id"].asUUID(); - gIMMgr->updateFloaterSessionID( - temp_session_id, - session_id); - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) + // Singu TODO: LLIMModel + gIMMgr->updateFloaterSessionID(temp_session_id, session_id); + + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + LLIMSpeakerMgr* speaker_mgr = im_floater ? im_floater->getSpeakerManager() : NULL; + if (speaker_mgr) { - floaterp->setSpeakers(body); - - //apply updates we've possibly received previously - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(session_id)); + speaker_mgr->setSpeakers(body); + speaker_mgr->updateSpeakers(gIMMgr->getPendingAgentListUpdates(session_id)); + } + if (im_floater) + { if ( body.has("session_info") ) { - floaterp->processSessionUpdate(body["session_info"]); + im_floater->processSessionUpdate(body["session_info"]); } - - //apply updates we've possibly received previously - floaterp->updateSpeakersList( - gIMMgr->getPendingAgentListUpdates(session_id)); } + gIMMgr->clearPendingAgentListUpdates(session_id); } else { - //throw an error dialog and close the temp session's floater - LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id); - - if ( floater ) + if (LLFloaterIMPanel* floater = gIMMgr->findFloaterBySession(temp_session_id)) { floater->showSessionStartError(body["error"].asString()); } @@ -1526,21 +1591,8 @@ public: const LLSD& context, const LLSD& input) const { - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(input["body"]["session_id"].asUUID()); - if (floaterp) - { - floaterp->updateSpeakersList( - input["body"]); - } - else - { - //we don't have a floater yet..something went wrong - //we are probably receiving an update here before - //a start or an acceptance of an invitation. Race condition. - gIMMgr->addPendingAgentListUpdates( - input["body"]["session_id"].asUUID(), - input["body"]); - } + const LLUUID& session_id = input["body"]["session_id"].asUUID(); + gIMMgr->processAgentListUpdates(session_id, input["body"]); } }; @@ -1553,12 +1605,12 @@ public: const LLSD& input) const { LLUUID session_id = input["body"]["session_id"].asUUID(); - LLFloaterIMPanel* floaterp = gIMMgr->findFloaterBySession(session_id); - if (floaterp) + LLFloaterIMPanel* im_floater = gIMMgr->findFloaterBySession(session_id); + if ( im_floater ) { - floaterp->processSessionUpdate(input["body"]["info"]); + im_floater->processSessionUpdate(input["body"]["info"]); } - LLIMSpeakerMgr* im_mgr = floaterp ? floaterp->getSpeakerManager() : NULL; //LLIMModel::getInstance()->getSpeakerManager(session_id); + LLIMSpeakerMgr* im_mgr = im_floater ? im_floater->getSpeakerManager() : NULL; //LLIMModel::getInstance()->getSpeakerManager(session_id); if (im_mgr) { im_mgr->processSessionUpdate(input["body"]["info"]); @@ -1651,7 +1703,9 @@ public: std::string saved; if(offline == IM_OFFLINE) { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } std::string buffer = separator_string + saved + message.substr(message_offset); diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index 4db4b75f9..3e7bc6fff 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -36,12 +36,12 @@ #include "llmultifloater.h" #include "llinstantmessage.h" #include "lluuid.h" +#include "llvoicechannel.h" + class LLFloaterChatterBox; -class LLUUID; class LLFloaterIMPanel; class LLFriendObserver; -class LLFloaterIM; class LLIMMgr : public LLSingleton { @@ -131,6 +131,9 @@ public: void notifyNewIM(); void clearNewIMNotification(); + // automatically start a call once the session has initialized + void autoStartCallOnStartup(const LLUUID& session_id); + // IM received that you haven't seen yet BOOL getIMReceived() const; int getIMUnreadCount(); @@ -163,6 +166,7 @@ public: void clearPendingInvitation(const LLUUID& session_id); + void processAgentListUpdates(const LLUUID& session_id, const LLSD& body); LLSD getPendingAgentListUpdates(const LLUUID& session_id); void addPendingAgentListUpdates( const LLUUID& sessioN_id, @@ -178,6 +182,18 @@ public: // Returns true if group chat is ignored for the UUID, false if not bool getIgnoreGroup(const LLUUID& group_id) const; + /** + * Start call in a session + * @return false if voice channel doesn't exist + **/ + bool startCall(const LLUUID& session_id, LLVoiceChannel::EDirection direction = LLVoiceChannel::OUTGOING_CALL); + + /** + * End call in a session + * @return false if voice channel doesn't exist + **/ + bool endCall(const LLUUID& session_id); + private: // create a panel and update internal representation for // consistency. Returns the pointer, caller (the class instance diff --git a/indra/newview/llinventoryactions.cpp b/indra/newview/llinventoryactions.cpp index fe07063bb..a02de1998 100644 --- a/indra/newview/llinventoryactions.cpp +++ b/indra/newview/llinventoryactions.cpp @@ -48,6 +48,7 @@ #include "llpreview.h" // For LLMultiPreview #include "lltrans.h" #include "llvoavatarself.h" +#include "llnotifications.h" extern LLUUID gAgentID; diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp index 5e6f3e1b0..c33667061 100644 --- a/indra/newview/lljoystickbutton.cpp +++ b/indra/newview/lljoystickbutton.cpp @@ -331,9 +331,6 @@ LLXMLNodePtr LLJoystickAgentTurn::getXML(bool save_children) const LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("button"); - node->getAttributeString("name", name); - std::string image_unselected; if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected); @@ -343,7 +340,7 @@ LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrl EJoystickQuadrant quad = JQ_ORIGIN; if (node->hasAttribute("quadrant")) quad = selectQuadrant(node); - LLJoystickAgentTurn *button = new LLJoystickAgentTurn(name, + LLJoystickAgentTurn *button = new LLJoystickAgentTurn("button", LLRect(), image_unselected, image_selected, @@ -449,9 +446,6 @@ LLXMLNodePtr LLJoystickAgentSlide::getXML(bool save_children) const // static LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("button"); - node->getAttributeString("name", name); - std::string image_unselected; if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected); @@ -462,7 +456,7 @@ LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtr EJoystickQuadrant quad = JQ_ORIGIN; if (node->hasAttribute("quadrant")) quad = selectQuadrant(node); - LLJoystickAgentSlide *button = new LLJoystickAgentSlide(name, + LLJoystickAgentSlide *button = new LLJoystickAgentSlide("button", LLRect(), image_unselected, image_selected, diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 447087276..40f25011d 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -36,8 +36,8 @@ // viewer includes #include "llpanellogin.h" // save_password_to_disk() +#include "llslurl.h" #include "llstartup.h" // getStartupState() -#include "llurlsimstring.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewernetwork.h" // EGridInfo @@ -79,17 +79,15 @@ void LLLoginHandler::parse(const LLSD& queryMap) if (startLocation == "specify") { - LLURLSimString::setString(queryMap["region"].asString()); + LLStartUp::setStartSLURL(queryMap["region"].asString()); } else if (startLocation == "home") { - gSavedSettings.setBOOL("LoginLastLocation", FALSE); - LLURLSimString::setString(LLStringUtil::null); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); } else if (startLocation == "last") { - gSavedSettings.setBOOL("LoginLastLocation", TRUE); - LLURLSimString::setString(LLStringUtil::null); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); } } diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index 0844b80c7..d36ceaf3c 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -39,7 +39,7 @@ class LLLoginHandler : public LLCommandHandler { public: // allow from external browsers - LLLoginHandler() : LLCommandHandler("login", false) { } + LLLoginHandler() : LLCommandHandler("login", UNTRUSTED_ALLOW) { } /*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web); // Fill in our internal fields from a SLURL like diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 17cf01b2f..b6a7fdb07 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -40,97 +40,124 @@ #include "llfloaterworldmap.h" #include "lluictrlfactory.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" #include "llviewborder.h" #include "llviewercontrol.h" #include "llviewermedia.h" +#include "llviewertexture.h" #include "llviewerwindow.h" -#include "llnotificationsutil.h" #include "llweb.h" #include "llrender.h" #include "llpluginclassmedia.h" +#include "llslurl.h" +#include "lluictrlfactory.h" // LLRegisterWidget +#include "llkeyboard.h" +#include "llviewermenu.h" // linden library includes #include "llfocusmgr.h" +#include "llsdutil.h" +#include "lltextbox.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llnotifications.h" +#include "lllineeditor.h" extern BOOL gRestoreGL; -// Setting the mozilla buffer width to 2048 exactly doesn't work, since it pads its rowbytes a bit, pushing the texture width over 2048. -// 2000 should give enough headroom for any amount of padding it cares to add. -const S32 MAX_DIMENSION = 2000; -const S32 MAX_TEXTURE_DIMENSION = 2048; - static LLRegisterWidget r("web_browser"); -LLMediaCtrl::LLMediaCtrl( const std::string& name, const LLRect& rect ) : - LLUICtrl( name, rect, FALSE), +LLMediaCtrl::Params::Params() +: start_url("start_url"), + border_visible("border_visible", false), + decouple_texture_size("decouple_texture_size", false), + texture_width("texture_width", 1024), + texture_height("texture_height", 1024), + caret_color("caret_color"), + initial_mime_type("initial_mime_type"), + error_page_url("error_page_url"), + media_id("media_id"), + trusted_content("trusted_content", false), + focus_on_click("focus_on_click", true) +{ +} + +LLMediaCtrl::LLMediaCtrl( const Params& p) : + LLPanel( p.name, p.rect, FALSE), LLInstanceTracker(LLUUID::generateNewID()), mTextureDepthBytes( 4 ), - mWebBrowserImage( 0 ), mBorder(NULL), mFrequentUpdates( true ), mForceUpdate( false ), - mOpenLinksInExternalBrowser( false ), - mOpenLinksInInternalBrowser( false ), - mTrusted( false ), mHomePageUrl( "" ), - mIgnoreUIScale( true ), mAlwaysRefresh( false ), - mExternalUrl( "" ), mMediaSource( 0 ), - mTakeFocusOnClick( true ), + mTakeFocusOnClick( p.focus_on_click ), mCurrentNavUrl( "about:blank" ), - mLastSetCursor( UI_CURSOR_ARROW ), mStretchToFill( true ), mMaintainAspectRatio ( true ), mDecoupleTextureSize ( false ), mTextureWidth ( 1024 ), mTextureHeight ( 1024 ), - mHideLoading (false) + mClearCache(false), + mHomePageMimeType(p.initial_mime_type), + mErrorPageURL(p.error_page_url), + mTrusted(p.trusted_content), + mHoverTextChanged(false), + mContextMenu() { + { + LLColor4 color = p.caret_color().get(); + setCaretColor( (unsigned int)color.mV[0], (unsigned int)color.mV[1], (unsigned int)color.mV[2] ); + } + + setHomePageUrl(p.start_url, p.initial_mime_type); + + setBorderVisible(p.border_visible); + + setDecoupleTextureSize(p.decouple_texture_size); + + setTextureSize(p.texture_width, p.texture_height); + if(!getDecoupleTextureSize()) { - S32 screen_width = mIgnoreUIScale ? - llround((F32)getRect().getWidth() * LLUI::getScaleFactor().mV[VX]) : getRect().getWidth(); - S32 screen_height = mIgnoreUIScale ? - llround((F32)getRect().getHeight() * LLUI::getScaleFactor().mV[VY]) : getRect().getHeight(); - + S32 screen_width = llround((F32)getRect().getWidth() * LLUI::getScaleFactor().mV[VX]); + S32 screen_height = llround((F32)getRect().getHeight() * LLUI::getScaleFactor().mV[VY]); + setTextureSize(screen_width, screen_height); } + + mMediaTextureID = getKey(); + // We don't need to create the media source up front anymore unless we have a non-empty home URL to navigate to. - if(!mHomePageUrl.empty()) + /*if(!mHomePageUrl.empty()) { navigateHome(); - } + }*/ - LLRect border_rect( 0, getRect().getHeight() + 2, getRect().getWidth() + 2, 0 ); - mBorder = new LLViewBorder( std::string("web control border"), border_rect, LLViewBorder::BEVEL_IN ); - addChild( mBorder ); + //LLRect border_rect( 0, getRect().getHeight() + 2, getRect().getWidth() + 2, 0 ); } -//////////////////////////////////////////////////////////////////////////////// -// note: this is now a singleton and destruction happens via initClass() now LLMediaCtrl::~LLMediaCtrl() { - if (mMediaSource) { mMediaSource->remObserver( this ); mMediaSource = NULL; } - - mWebBrowserImage = NULL; } //////////////////////////////////////////////////////////////////////////////// // void LLMediaCtrl::setBorderVisible( BOOL border_visible ) { - if ( mBorder ) + if(border_visible && !mBorder) { - mBorder->setVisible( border_visible ); - }; + mBorder = new LLViewBorder( std::string("web control border"), getLocalRect(), LLViewBorder::BEVEL_IN ); + addChild( mBorder ); + } + if(mBorder) + mBorder->setVisible(border_visible); }; //////////////////////////////////////////////////////////////////////////////// @@ -140,39 +167,24 @@ void LLMediaCtrl::setTakeFocusOnClick( bool take_focus ) mTakeFocusOnClick = take_focus; } - -//////////////////////////////////////////////////////////////////////////////// -// set flag that forces the embedded browser to open links in the external system browser -void LLMediaCtrl::setOpenInExternalBrowser( bool valIn ) -{ - mOpenLinksInExternalBrowser = valIn; -}; - -//////////////////////////////////////////////////////////////////////////////// -// set flag that forces the embedded browser to open links in the internal browser floater -void LLMediaCtrl::setOpenInInternalBrowser( bool valIn ) -{ - mOpenLinksInInternalBrowser = valIn; -}; - -//////////////////////////////////////////////////////////////////////////////// -void LLMediaCtrl::setTrusted( bool valIn ) -{ - mTrusted = valIn; -} - //////////////////////////////////////////////////////////////////////////////// // BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleHover(x, y, mask)) return TRUE; + if (LLPanel::handleHover(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) { mMediaSource->mouseMove(x, y, mask); - - gViewerWindow->setCursor(mLastSetCursor); + gViewerWindow->setCursor(mMediaSource->getLastSetCursor()); + } + + // TODO: Is this the right way to handle hover text changes driven by the plugin? + if(mHoverTextChanged) + { + mHoverTextChanged = false; + //handleToolTip(x, y, mask); } return TRUE; @@ -182,9 +194,36 @@ BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) { - if (LLUICtrl::handleScrollWheel(x, y, clicks)) return TRUE; + if (LLPanel::handleScrollWheel(x, y, clicks)) return TRUE; if (mMediaSource && mMediaSource->hasMedia()) - mMediaSource->getMediaPlugin()->scrollEvent(0, clicks, MASK_NONE); + mMediaSource->getMediaPlugin()->scrollEvent(0, clicks, gKeyboard->currentMask(TRUE)); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +BOOL LLMediaCtrl::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) +{ + std::string hover_text; + + if (mMediaSource && mMediaSource->hasMedia()) + hover_text = mMediaSource->getMediaPlugin()->getHoverText(); + + if(hover_text.empty()) + { + return FALSE; + } + else + { + msg = hover_text; + + S32 screen_x, screen_y; + + localPointToScreen(x, y, &screen_x, &screen_y); + LLRect sticky_rect_screen; + sticky_rect_screen.setCenterAndSize(screen_x, screen_y, 20, 20); + } return TRUE; } @@ -193,20 +232,12 @@ BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) // BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleMouseUp(x, y, mask)) return TRUE; + if (LLPanel::handleMouseUp(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) { mMediaSource->mouseUp(x, y, mask); - - /*// *HACK: LLMediaImplLLMozLib automatically takes focus on mouseup, - // in addition to the onFocusReceived() call below. Undo this. JC - if (!mTakeFocusOnClick) - { - mMediaSource->focus(false); - gViewerWindow->focusClient(); - }*/ } gFocusMgr.setMouseCapture( NULL ); @@ -218,7 +249,7 @@ BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleMouseDown(x, y, mask)) return TRUE; + if (LLPanel::handleMouseDown(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) @@ -238,7 +269,7 @@ BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) { - /*if (LLPanel::handleRightMouseUp(x, y, mask)) return TRUE; + if (LLPanel::handleRightMouseUp(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) @@ -255,7 +286,7 @@ BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) } gFocusMgr.setMouseCapture( NULL ); - */ + return TRUE; } @@ -263,7 +294,7 @@ BOOL LLMediaCtrl::handleRightMouseUp( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleRightMouseDown(x, y, mask)) return TRUE; + if (LLPanel::handleRightMouseDown(x, y, mask)) return TRUE; S32 media_x = x, media_y = y; convertInputCoords(media_x, media_y); @@ -278,6 +309,14 @@ BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) setFocus( TRUE ); } + LLMenuGL* menu = (LLMenuGL*)mContextMenu.get(); + if (menu) + { + menu->buildDrawLabels(); + menu->updateParent(LLMenuGL::sMenuContainer); + LLMenuGL::showPopup(this,menu, x, y); + } + return TRUE; } @@ -285,7 +324,7 @@ BOOL LLMediaCtrl::handleRightMouseDown( S32 x, S32 y, MASK mask ) // BOOL LLMediaCtrl::handleDoubleClick( S32 x, S32 y, MASK mask ) { - if (LLUICtrl::handleDoubleClick(x, y, mask)) return TRUE; + if (LLPanel::handleDoubleClick(x, y, mask)) return TRUE; convertInputCoords(x, y); if (mMediaSource) @@ -313,7 +352,7 @@ void LLMediaCtrl::onFocusReceived() LLEditMenuHandler::gEditMenuHandler = mMediaSource; } - LLUICtrl::onFocusReceived(); + LLPanel::onFocusReceived(); } //////////////////////////////////////////////////////////////////////////////// @@ -333,15 +372,20 @@ void LLMediaCtrl::onFocusLost() gViewerWindow->focusClient(); - LLUICtrl::onFocusLost(); + LLPanel::onFocusLost(); } //////////////////////////////////////////////////////////////////////////////// // - BOOL LLMediaCtrl::postBuild () { - //setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); + LLMenuGL* menu = LLUICtrlFactory::getInstance()->buildMenu("menu_media_ctrl.xml",LLMenuGL::sMenuContainer); + if(menu) + { + mContextMenu = menu->getHandle(); + } + + setVisibleCallback(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); return true; } @@ -350,43 +394,20 @@ void LLMediaCtrl::onOpenWebInspector() if (mMediaSource && mMediaSource->hasMedia()) mMediaSource->getMediaPlugin()->showWebInspector( true ); } + //////////////////////////////////////////////////////////////////////////////// // BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask ) { BOOL result = FALSE; - // FIXME: THIS IS SO WRONG. - // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it... - if (mMediaSource) { - if( MASK_CONTROL & mask ) - { - if( 'C' == key ) - { - mMediaSource->copy(); - result = TRUE; - } - else - if( 'V' == key ) - { - mMediaSource->paste(); - result = TRUE; - } - else - if( 'X' == key ) - { - mMediaSource->cut(); - result = TRUE; - } - } - - if(!result) - { - result = mMediaSource->handleKeyHere(key, mask); - } + result = mMediaSource->handleKeyHere(key, mask); } + + if ( ! result ) + result = LLPanel::handleKeyHere(key, mask); return result; } @@ -400,14 +421,10 @@ void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility ) { mMediaSource->setVisible( new_visibility ); } - LLUICtrl::handleVisibilityChange( new_visibility ); - //Hack due to not being derived from LLPanel yet - LLMediaCtrl::onVisibilityChange(LLSD(new_visibility)); } //////////////////////////////////////////////////////////////////////////////// // - BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) { BOOL result = FALSE; @@ -420,6 +437,9 @@ BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) result = mMediaSource->handleUnicodeCharHere(uni_char); } + if ( ! result ) + result = LLPanel::handleUnicodeCharHere(uni_char); + return result; } @@ -444,8 +464,8 @@ void LLMediaCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) { if(!getDecoupleTextureSize()) { - S32 screen_width = mIgnoreUIScale ? llround((F32)width * LLUI::getScaleFactor().mV[VX]) : width; - S32 screen_height = mIgnoreUIScale ? llround((F32)height * LLUI::getScaleFactor().mV[VY]) : height; + S32 screen_width = llround((F32)width * LLUI::getScaleFactor().mV[VX]); + S32 screen_height = llround((F32)height * LLUI::getScaleFactor().mV[VY]); // when floater is minimized, these sizes are negative if ( screen_height > 0 && screen_width > 0 ) @@ -497,6 +517,21 @@ bool LLMediaCtrl::canNavigateForward() return false; } +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::clearCache() +{ + if(mMediaSource) + { + mMediaSource->clearCache(); + } + else + { + mClearCache = true; + } + +} + //////////////////////////////////////////////////////////////////////////////// // void LLMediaCtrl::set404RedirectUrl( std::string redirect_url ) @@ -541,31 +576,18 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type) void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in ) { std::string language = LLUI::getLanguage(); - std::string delim = gDirUtilp->getDirDelimiter(); - std::string filename; - - filename += subdir; - filename += delim; - filename += filename_in; + std::string filename(gDirUtilp->add(subdir, filename_in)); std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); - if (! gDirUtilp->fileExists(expanded_filename)) + if (expanded_filename.empty() && language != "en-us") { - if (language != "en-us") - { - expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename); - if (! gDirUtilp->fileExists(expanded_filename)) - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } - } - else - { - llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; - return; - } + expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename); + } + if(expanded_filename.empty()) + { + llwarns << "File " << filename << "not found" << llendl; + return; } if (ensureMediaSourceExists()) { @@ -589,15 +611,34 @@ void LLMediaCtrl::navigateHome() //////////////////////////////////////////////////////////////////////////////// // -void LLMediaCtrl::setHomePageUrl( const std::string urlIn ) +void LLMediaCtrl::setHomePageUrl( const std::string& urlIn, const std::string& mime_type ) { mHomePageUrl = urlIn; if (mMediaSource) { - mMediaSource->setHomeURL(mHomePageUrl); + mMediaSource->setHomeURL(mHomePageUrl, mime_type); } } +void LLMediaCtrl::setTarget(const std::string& target) +{ + mTarget = target; + if (mMediaSource) + { + mMediaSource->setTarget(mTarget); + } +} + +void LLMediaCtrl::setErrorPageURL(const std::string& url) +{ + mErrorPageURL = url; +} + +const std::string& LLMediaCtrl::getErrorPageURL() +{ + return mErrorPageURL; +} + //////////////////////////////////////////////////////////////////////////////// // bool LLMediaCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue) @@ -616,7 +657,6 @@ void LLMediaCtrl::setTextureSize(S32 width, S32 height) if(mMediaSource) { mMediaSource->setSize(mTextureWidth, mTextureHeight); - mWebBrowserImage->resize( mTextureWidth, mTextureHeight ); mForceUpdate = true; } } @@ -634,14 +674,24 @@ bool LLMediaCtrl::ensureMediaSourceExists() { if(mMediaSource.isNull()) { - mMediaSource = LLViewerMedia::newMediaImpl(mHomePageUrl, LLUUID::null, mTextureWidth, mTextureWidth, false, false, "text/html"); + // If we don't already have a media source, try to create one. + mMediaSource = LLViewerMedia::newMediaImpl(mMediaTextureID, mTextureWidth, mTextureHeight); if ( mMediaSource ) { - // create a new texture (based on LLDynamic texture) that will be used to display the output - mWebBrowserImage = new LLWebBrowserTexture( mTextureWidth, mTextureWidth, this, mMediaSource ); - mMediaSource->setHomeURL(mHomePageUrl); + mMediaSource->setUsedInUI(true); + mMediaSource->setHomeURL(mHomePageUrl, mHomePageMimeType); + mMediaSource->setTarget(mTarget); mMediaSource->setVisible( getVisible() ); mMediaSource->addObserver( this ); + mMediaSource->setBackgroundColor( getBackgroundColor() ); + mMediaSource->setTrustedBrowser(mTrusted); + mMediaSource->setPageZoomFactor( LLUI::getScaleFactor().mV[ VX ] ); + + if(mClearCache) + { + mMediaSource->clearCache(); + mClearCache = false; + } } else { @@ -658,7 +708,6 @@ bool LLMediaCtrl::ensureMediaSourceExists() void LLMediaCtrl::unloadMediaSource() { mMediaSource = NULL; - mWebBrowserImage = NULL; //release the dynamic texture too. } //////////////////////////////////////////////////////////////////////////////// @@ -672,15 +721,12 @@ LLPluginClassMedia* LLMediaCtrl::getMediaPlugin() // void LLMediaCtrl::draw() { - if ( ! mWebBrowserImage || mWebBrowserImage->getNeedsUpdate()) - return; - if ( gRestoreGL == 1 ) { LLRect r = getRect(); reshape( r.getWidth(), r.getHeight(), FALSE ); return; - }; + } // NOTE: optimization needed here - probably only need to do this once // unless tearoffs change the parent which they probably do. @@ -701,7 +747,7 @@ void LLMediaCtrl::draw() bool draw_media = false; LLPluginClassMedia* media_plugin = NULL; - LLWebBrowserTexture* media_texture = mWebBrowserImage; + LLViewerMediaTexture* media_texture = NULL; if(mMediaSource && mMediaSource->hasMedia()) { @@ -709,29 +755,22 @@ void LLMediaCtrl::draw() if(media_plugin && (media_plugin->textureValid())) { - media_texture = mWebBrowserImage; + media_texture = LLViewerTextureManager::findMediaTexture(mMediaTextureID); if(media_texture) { draw_media = true; } } } + + bool background_visible = isBackgroundVisible(); + bool background_opaque = isBackgroundOpaque(); + if(draw_media) { gGL.pushUIMatrix(); { - /*if (mIgnoreUIScale) - { - gGL.pushUIMatrix(); - gGL.loadUIIdentity(); - gGL.pushMatrix(); - gGL.loadIdentity(); - // font system stores true screen origin, need to scale this by UI scale factor - // to get render origin for this view (with unit scale) - gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::getScaleFactor().mV[VX]), - floorf(LLFontGL::sCurOrigin.mY * LLUI::getScaleFactor().mV[VY]), - LLFontGL::sCurDepth); - }*/ + mMediaSource->setPageZoomFactor( LLUI::getScaleFactor().mV[ VX ] ); // scale texture to fit the space using texture coords gGL.getTexUnit(0)->bind(media_texture); @@ -822,20 +861,26 @@ void LLMediaCtrl::draw() } gGL.end(); gGL.setSceneBlendType(LLRender::BT_ALPHA); - /*if (mIgnoreUIScale) - { - gGL.popUIMatrix(); - gGL.popMatrix(); - }*/ } gGL.popUIMatrix(); + } + else + { + // Setting these will make LLPanel::draw draw the opaque background color. + setBackgroundVisible(true); + setBackgroundOpaque(true); + } + // highlight if keyboard focus here. (TODO: this needs some work) if ( mBorder && mBorder->getVisible() ) mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) ); - - LLUICtrl::draw(); + LLPanel::draw(); + + // Restore the previous values + setBackgroundVisible(background_visible); + setBackgroundOpaque(background_opaque); } //////////////////////////////////////////////////////////////////////////////// @@ -849,31 +894,17 @@ void LLMediaCtrl::convertInputCoords(S32& x, S32& y) coords_opengl = mMediaSource->getMediaPlugin()->getTextureCoordsOpenGL(); } - x = mIgnoreUIScale ? llround((F32)x * LLUI::getScaleFactor().mV[VX]) : x; + x = llround((F32)x * LLUI::getScaleFactor().mV[VX]); if ( ! coords_opengl ) { - y = mIgnoreUIScale ? llround((F32)(y) * LLUI::getScaleFactor().mV[VY]) : y; + y = llround((F32)(y) * LLUI::getScaleFactor().mV[VY]); } else { - y = mIgnoreUIScale ? llround((F32)(getRect().getHeight() - y) * LLUI::getScaleFactor().mV[VY]) : getRect().getHeight() - y; + y = llround((F32)(getRect().getHeight() - y) * LLUI::getScaleFactor().mV[VY]); }; } -//////////////////////////////////////////////////////////////////////////////// -// static -bool LLMediaCtrl::onClickLinkExternalTarget(const LLSD& notification, const LLSD& response ) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - if ( 0 == option ) - { - // open in external browser because we don't support - // creation of our own secondary browser windows - LLWeb::loadURLExternal( notification["payload"]["external_url"].asString() ); - } - return false; -} - //////////////////////////////////////////////////////////////////////////////// // inherited from LLViewerMediaObserver //virtual @@ -903,28 +934,14 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_CURSOR_CHANGED: { - LL_INFOS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL; - - std::string cursor = self->getCursorName(); - - if(cursor == "arrow") - mLastSetCursor = UI_CURSOR_ARROW; - else if(cursor == "ibeam") - mLastSetCursor = UI_CURSOR_IBEAM; - else if(cursor == "splith") - mLastSetCursor = UI_CURSOR_SIZEWE; - else if(cursor == "splitv") - mLastSetCursor = UI_CURSOR_SIZENS; - else if(cursor == "hand") - mLastSetCursor = UI_CURSOR_HAND; - else // for anything else, default to the arrow - mLastSetCursor = UI_CURSOR_ARROW; - }; + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL; + } break; case MEDIA_EVENT_NAVIGATE_BEGIN: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN, url is " << self->getNavigateURI() << LL_ENDL; + hideNotification(); }; break; @@ -959,20 +976,39 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_NAVIGATE_ERROR_PAGE: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_ERROR_PAGE" << LL_ENDL; + if ( mErrorPageURL.length() > 0 ) + { + navigateTo(mErrorPageURL, "text/html"); + }; }; break; case MEDIA_EVENT_CLICK_LINK_HREF: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; - onClickLinkHref(self); + // retrieve the event parameters + std::string url = self->getClickURL(); + std::string target = self->getClickTarget(); + std::string uuid = self->getClickUUID(); + + LLNotification::Params notify_params("PopupAttempt"); + notify_params.payload = LLSD().with("target", target).with("url", url).with("uuid", uuid).with("media_id", mMediaTextureID); + notify_params.functor(boost::bind(&LLMediaCtrl::onPopup, this, _1, _2)); + + if (mTrusted) + { + LLNotifications::instance().forceResponse(notify_params, 0); + } + else + { + LLNotifications::instance().add(notify_params); + } + break; }; - break; case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL; - onClickLinkNoFollow(self); }; break; @@ -997,30 +1033,42 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) case MEDIA_EVENT_CLOSE_REQUEST: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLOSE_REQUEST" << LL_ENDL; - }; + } break; case MEDIA_EVENT_PICK_FILE_REQUEST: { LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PICK_FILE_REQUEST" << LL_ENDL; - }; + } break; case MEDIA_EVENT_GEOMETRY_CHANGE: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE" << LL_ENDL; - }; + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_GEOMETRY_CHANGE, uuid is " << self->getClickUUID() << LL_ENDL; + } break; case MEDIA_EVENT_AUTH_REQUEST: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_AUTH_REQUEST" << LL_ENDL; + LLNotification::Params auth_request_params("AuthRequest"); + + // pass in host name and realm for site (may be zero length but will always exist) + LLSD args; + LLURL raw_url( self->getAuthURL().c_str() ); + args["HOST_NAME"] = raw_url.getAuthority(); + args["REALM"] = self->getAuthRealm(); + auth_request_params.substitutions = args; + + auth_request_params.payload = LLSD().with("media_id", mMediaTextureID); + auth_request_params.functor(boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2)); + LLNotifications::instance().add(auth_request_params); }; break; - + case MEDIA_EVENT_LINK_HOVERED: { - LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LINK_HOVERED" << LL_ENDL; + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LINK_HOVERED, hover text is: " << self->getHoverText() << LL_ENDL; + mHoverTextChanged = true; }; break; @@ -1042,65 +1090,28 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) //////////////////////////////////////////////////////////////////////////////// // -void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self ) +std::string LLMediaCtrl::getCurrentNavUrl() { - // retrieve the event parameters - std::string target = self->getClickTarget(); - std::string url = self->getClickURL(); - - // if there is a value for the target - if ( !target.empty() ) - { - if ( target == "_external" ) - { - mExternalUrl = url; - LLSD payload; - payload["external_url"] = mExternalUrl; - LLNotifications::instance().add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget); - return; - } - } - - const std::string protocol1( "http://" ); - const std::string protocol2( "https://" ); - if( mOpenLinksInExternalBrowser ) - { - if ( !url.empty() ) - { - if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 || - LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 ) - { - LLWeb::loadURLExternal( url ); - } - } - } - else - if( mOpenLinksInInternalBrowser ) - { - if ( !url.empty() ) - { - if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 || - LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 ) - { - // If we spawn a new LLFloaterHTML, assume we want it to - // follow this LLMediaCtrl's trust for whether or - // not to open secondlife:///app/ links. JC. -// const bool open_links_externally = false; -// LLFloaterHtml::getInstance()->show( -// event_in.mStringPayload, -// "Second Life Browser", -// open_links_externally, -// mTrusted); - } - } - } + return mCurrentNavUrl; } -//////////////////////////////////////////////////////////////////////////////// -// -void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self ) +bool LLMediaCtrl::onPopup(const LLSD& notification, const LLSD& response) { - std::string url = self->getClickURL(); + if (response["open"]) + { + LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"], notification["payload"]["uuid"]); + } + else + { + // Make sure the opening instance knows its window open request was denied, so it can clean things up. + LLViewerMedia::proxyWindowClosed(notification["payload"]["uuid"]); + } + return FALSE; +} + +void LLMediaCtrl::showNotification(LLNotificationPtr notify) +{ +/* std::string url = self->getClickURL(); if (LLURLDispatcher::isSLURLCommand(url) && !mTrusted) { @@ -1109,241 +1120,24 @@ void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self ) return; } - LLURLDispatcher::dispatch(url, this, mTrusted); + LLURLDispatcher::dispatch(url, this, mTrusted);*/ + LLNotifications::instance().add(notify); } -//////////////////////////////////////////////////////////////////////////////// -// -LLWebBrowserTexture::LLWebBrowserTexture( S32 width, S32 height, LLMediaCtrl* browserCtrl, viewer_media_t media_source ) : - LLViewerDynamicTexture( 512, 512, 4, ORDER_FIRST, TRUE ), - mNeedsUpdate( true ), - mNeedsResize( false ), - mTextureCoordsOpenGL( true ), - mWebBrowserCtrl( browserCtrl ), - mMediaSource(media_source) -{ - mElapsedTime.start(); - resize( width, height ); +void LLMediaCtrl::hideNotification() +{ } -//////////////////////////////////////////////////////////////////////////////// -// -LLWebBrowserTexture::~LLWebBrowserTexture() +void LLMediaCtrl::setTrustedContent(bool trusted) { - mElapsedTime.stop(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLWebBrowserTexture::needsRender() -{ - bool texture_dirty = false; - - if ( mWebBrowserCtrl->getFrequentUpdates() || - mWebBrowserCtrl->getAlwaysRefresh() || - mWebBrowserCtrl->getForceUpdate() ) + mTrusted = trusted; + if (mMediaSource) { - // All of these force an update - return TRUE; + mMediaSource->setTrustedBrowser(trusted); } - - // If the texture needs updating, render needs to be called. - if (mMediaSource && mMediaSource->hasMedia()) - { - LLPluginClassMedia* media = mMediaSource->getMediaPlugin(); - - if(media->textureValid() && media->getDirty()) - { - texture_dirty = true; - } - } - - - return texture_dirty; } -//////////////////////////////////////////////////////////////////////////////// -// -BOOL LLWebBrowserTexture::render() -{ - if(updateBrowserTexture()) - { - // updateBrowserTexture already verified that the media plugin is there and the texture is valid. - LLPluginClassMedia* media_plugin = mMediaSource->getMediaPlugin(); - LLRect dirty_rect; - - if(mNeedsUpdate) - { - // If we need an update, use the whole rect instead of the dirty rect. - dirty_rect.mLeft = 0; - dirty_rect.mBottom = 0; - dirty_rect.mRight = media_plugin->getWidth(); - dirty_rect.mTop = media_plugin->getHeight(); - } - else - { - mNeedsUpdate = media_plugin->getDirty(&dirty_rect); - } - - if ( mNeedsUpdate ) - { - mNeedsUpdate = false; - mWebBrowserCtrl->setForceUpdate(false); - - // Constrain the dirty rect to be inside the texture - S32 x_pos = llmax(dirty_rect.mLeft, 0); - S32 y_pos = llmax(dirty_rect.mBottom, 0); - S32 width = llmin(dirty_rect.mRight, getWidth()) - x_pos; - S32 height = llmin(dirty_rect.mTop, getHeight()) - y_pos; - - if(width > 0 && height > 0) - { - U8* data = media_plugin->getBitsData(); - - // Offset the pixels pointer to match x_pos and y_pos - data += ( x_pos * media_plugin->getTextureDepth() * media_plugin->getBitsWidth() ); - data += ( y_pos * media_plugin->getTextureDepth() ); - - setSubImage( - data, - media_plugin->getBitsWidth(), - media_plugin->getBitsHeight(), - x_pos, - y_pos, - width, - height, - TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.) - } - - media_plugin->resetDirty(); - - return TRUE; - }; - }; - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////////// -// -S32 LLWebBrowserTexture::getMediaWidth() -{ - return mMediaWidth; -} - -//////////////////////////////////////////////////////////////////////////////// -// -S32 LLWebBrowserTexture::getMediaHeight() -{ - return mMediaHeight; -} - -//////////////////////////////////////////////////////////////////////////////// -// -void LLWebBrowserTexture::setNeedsUpdate() -{ - mNeedsUpdate = true; -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool LLWebBrowserTexture::getNeedsUpdate() -{ - return mNeedsUpdate; -} - -//////////////////////////////////////////////////////////////////////////////// -// -bool LLWebBrowserTexture::getTextureCoordsOpenGL() -{ - return mTextureCoordsOpenGL; -} - - -//////////////////////////////////////////////////////////////////////////////// -// -void LLWebBrowserTexture::resize( S32 new_width, S32 new_height ) -{ - F32 scale_ratio = 1.f; - if (new_width > MAX_DIMENSION) - { - scale_ratio = (F32)MAX_DIMENSION / (F32)new_width; - } - if (new_height > MAX_DIMENSION) - { - scale_ratio = llmin(scale_ratio, (F32)MAX_DIMENSION / (F32)new_height); - } - - mMediaWidth = llround(scale_ratio * (F32)new_width); - mMediaHeight = llround(scale_ratio * (F32)new_height); - - adjustSize(); -} - -bool LLWebBrowserTexture::adjustSize() -{ - if (mMediaSource && mMediaSource->hasMedia()) - { - int natural_width = mMediaSource->getMediaPlugin()->getNaturalWidth(); - int natural_height = mMediaSource->getMediaPlugin()->getNaturalHeight(); - - if(natural_width != 0) - { - // If the media has a "natural size", use it. - mMediaWidth = natural_width; - mMediaHeight = natural_height; - } - - mMediaSource->setSize(mMediaWidth, mMediaHeight); - mNeedsResize = false; - - return true; - } - else - { - // The media isn't fully initialized yet, delay the resize until later. - mNeedsResize = true; - } - - return false; -} - -bool LLWebBrowserTexture::updateBrowserTexture() -{ - if (!adjustSize()) - return false; - - LLPluginClassMedia* media = mMediaSource->getMediaPlugin(); - - if(!media->textureValid()) - return false; - - if(mMediaSource->mNeedsNewTexture - || media->getTextureWidth() != getFullWidth() - || media->getTextureHeight() != getFullHeight() ) - { - //releaseGLTexture(); - - mFullWidth = media->getTextureWidth(); - mFullHeight = media->getTextureHeight(); - mTextureCoordsOpenGL = media->getTextureCoordsOpenGL(); - - const LLColor4U fill_color(0,0,0,255); - // will create mWidth * mHeight sized texture, using the texture params specified by the media. - generateGLTexture( - media->getTextureFormatInternal(), - media->getTextureFormatPrimary(), - media->getTextureFormatType(), - media->getTextureFormatSwapBytes(), - &fill_color); //Initialize the texture to black. - - - mMediaSource->mNeedsNewTexture = false; - } - - return true; -} // virtual LLXMLNodePtr LLMediaCtrl::getXML(bool save_children) const { @@ -1356,48 +1150,50 @@ LLXMLNodePtr LLMediaCtrl::getXML(bool save_children) const LLView* LLMediaCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("web_browser"); - node->getAttributeString("name", name); - - std::string start_url(""); - node->getAttributeString("start_url", start_url ); - - BOOL border_visible = true; - node->getAttributeBOOL("border_visible", border_visible); - + LLMediaCtrl::Params p; + + BOOL bval; + LLColor4 color; + S32 ival; LLRect rect; + + std::string sval("web_browser"); + node->getAttributeString("name", sval); + p.name = sval; createRect(node, rect, parent, LLRect()); + p.rect = rect; - LLMediaCtrl* web_browser = new LLMediaCtrl( name, rect ); + if(node->getAttributeString("start_url", sval )) + p.start_url = sval; + if(node->getAttributeString("error_page_url", sval )) + p.error_page_url = sval; + if(node->getAttributeString("media_id", sval )) + p.media_id = sval; + if(node->getAttributeString("initial_mime_type", sval )) + p.initial_mime_type = sval; + if(node->getAttributeBOOL("border_visible", bval)) + p.border_visible = bval; + if(node->getAttributeBOOL("focus_on_click", bval)) + p.focus_on_click = bval; + if(node->getAttributeBOOL("decouple_texture_size", bval)) + p.decouple_texture_size = bval; + if(node->getAttributeBOOL("trusted_content", bval)) + p.trusted_content = bval; + if(node->getAttributeS32("texture_width", ival)) + p.texture_width = ival; + if(node->getAttributeBOOL("texture_height", ival)) + p.texture_height = ival; + if(LLUICtrlFactory::getAttributeColor(node, "caret_color", color)) + p.caret_color = color; - if(node->hasAttribute("caret_color")) - { - LLColor4 color; - LLUICtrlFactory::getAttributeColor(node, "caret_color", color); - LLColor4U colorU = LLColor4U(color); - web_browser->setCaretColor( colorU.mV[0], colorU.mV[1], colorU.mV[2] ); - } - - //BOOL ignore_ui_scale = web_browser->getIgnoreUIScale(); - //node->getAttributeBOOL("ignore_ui_scale", ignore_ui_scale); - //web_browser->setIgnoreUIScale((bool)ignore_ui_scale); + LLMediaCtrl* web_browser = LLUICtrlFactory::create(p,parent); web_browser->initFromXML(node, parent); - web_browser->setHomePageUrl( start_url ); - - web_browser->setBorderVisible( border_visible ); - - if(! start_url.empty()) + if(!p.start_url.getValue().empty()) { web_browser->navigateHome(); } return web_browser; } - -std::string LLMediaCtrl::getCurrentNavUrl() -{ - return mCurrentNavUrl; -} - diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h index ef25ae0e3..306d68650 100644 --- a/indra/newview/llmediactrl.h +++ b/indra/newview/llmediactrl.h @@ -34,25 +34,50 @@ #define LL_LLMediaCtrl_H #include "llviewermedia.h" -#include "llviewermediaobserver.h" + #include "lluictrl.h" #include "llframetimer.h" -#include "lldynamictexture.h" class LLViewBorder; -class LLWebBrowserTexture; class LLUICtrlFactory; //////////////////////////////////////////////////////////////////////////////// // class LLMediaCtrl : - public LLUICtrl, + public LLPanel, public LLViewerMediaObserver, public LLViewerMediaEventEmitter, public LLInstanceTracker { - public: - LLMediaCtrl( const std::string& name, const LLRect& rect ); + LOG_CLASS(LLMediaCtrl); +public: + struct Params : public LLInitParam::Block + { + Optional start_url; + + Optional border_visible, + hide_loading, + decouple_texture_size, + trusted_content, + focus_on_click; + + Optional texture_width, + texture_height; + + Optional caret_color; + + Optional initial_mime_type; + Optional media_id; + Optional error_page_url; + + Params(); + }; + +protected: + LLMediaCtrl(const Params&); + friend class LLUICtrlFactory; + +public: virtual ~LLMediaCtrl(); void setBorderVisible( BOOL border_visible ); @@ -74,6 +99,7 @@ class LLMediaCtrl : virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + virtual BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen); // navigation void navigateTo( std::string url_in, std::string mime_type = ""); @@ -83,8 +109,6 @@ class LLMediaCtrl : void navigateToLocalPage( const std::string& subdir, const std::string& filename_in ); bool canNavigateBack(); bool canNavigateForward(); - void setOpenInExternalBrowser( bool valIn ); - void setOpenInInternalBrowser( bool valIn ); std::string getCurrentNavUrl(); // By default, we do not handle "secondlife:///app/" SLURLs, because @@ -93,12 +117,17 @@ class LLMediaCtrl : // Javascript or some other mechanism. However, we need the search // floater and login page to handle these URLs. Those are safe // because we control the page content. See DEV-9530. JC. - void setTrusted( bool valIn ); - - void setHomePageUrl( const std::string urlIn ); + void setHomePageUrl( const std::string& urlIn, const std::string& mime_type = LLStringUtil::null ); std::string getHomePageUrl(); - // set/clear URL to visit when a 404 page is reached + void setTarget(const std::string& target); + + void setErrorPageURL(const std::string& url); + const std::string& getErrorPageURL(); + + // Clear the browser cache when the instance gets loaded + void clearCache(); + void set404RedirectUrl( std::string redirect_url ); void clr404RedirectUrl(); @@ -106,9 +135,6 @@ class LLMediaCtrl : bool getFrequentUpdates() { return mFrequentUpdates; }; void setFrequentUpdates( bool frequentUpdatesIn ) { mFrequentUpdates = frequentUpdatesIn; }; - void setIgnoreUIScale(bool ignore) { mIgnoreUIScale = ignore; } - bool getIgnoreUIScale() { return mIgnoreUIScale; } - void setAlwaysRefresh(bool refresh) { mAlwaysRefresh = refresh; } bool getAlwaysRefresh() { return mAlwaysRefresh; } @@ -127,6 +153,11 @@ class LLMediaCtrl : void setTextureSize(S32 width, S32 height); + void showNotification(boost::shared_ptr notify); + void hideNotification(); + + void setTrustedContent(bool trusted); + // over-rides virtual BOOL handleKeyHere( KEY key, MASK mask); virtual void handleVisibilityChange ( BOOL new_visibility ); @@ -142,80 +173,43 @@ class LLMediaCtrl : // Incoming media event dispatcher virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); - // handlers for individual events (could be done inside the switch in handleMediaEvent, they're just individual functions for clarity) - void onClickLinkHref( LLPluginClassMedia* self ); - - void onClickLinkNoFollow( LLPluginClassMedia* self ); - // right click debugging item void onOpenWebInspector(); + LLUUID getTextureID() {return mMediaTextureID;} + protected: void convertInputCoords(S32& x, S32& y); private: void onVisibilityChange ( const LLSD& new_visibility ); - static bool onClickLinkExternalTarget( const LLSD&, const LLSD& ); + bool onPopup(const LLSD& notification, const LLSD& response); const S32 mTextureDepthBytes; LLUUID mMediaTextureID; - LLPointer mWebBrowserImage; LLViewBorder* mBorder; - bool mFrequentUpdates; - bool mForceUpdate; - bool mOpenLinksInExternalBrowser; - bool mOpenLinksInInternalBrowser; - bool mTrusted; - std::string mHomePageUrl; - std::string mExternalUrl; - std::string mCurrentNavUrl; - bool mIgnoreUIScale; - bool mAlwaysRefresh; - viewer_media_t mMediaSource; - bool mTakeFocusOnClick; - ECursorType mLastSetCursor; - bool mStretchToFill; - bool mMaintainAspectRatio; - bool mHideLoading; - bool mHidingInitialLoad; - bool mDecoupleTextureSize; - S32 mTextureWidth; - S32 mTextureHeight; -}; - -//////////////////////////////////////////////////////////////////////////////// -// -class LLWebBrowserTexture : public LLViewerDynamicTexture -{ -LOG_CLASS(LLWebBrowserTexture); - public: - LLWebBrowserTexture( S32 width, S32 height, LLMediaCtrl* browserCtrl, viewer_media_t media_source ); - virtual ~LLWebBrowserTexture(); - - virtual BOOL needsRender(); - virtual void preRender( BOOL clear_depth = TRUE ) {}; - virtual void postRender( BOOL success ) {}; - virtual BOOL render(); - - bool adjustSize(); - S32 getMediaWidth(); - S32 getMediaHeight(); - bool getNeedsUpdate(); - void setNeedsUpdate(); - bool getTextureCoordsOpenGL(); - - void resize( S32 new_width, S32 new_height ); - bool updateBrowserTexture(); - - protected: - S32 mMediaWidth; - S32 mMediaHeight; - bool mNeedsUpdate; - bool mNeedsResize; - bool mTextureCoordsOpenGL; - LLFrameTimer mElapsedTime; - LLMediaCtrl* mWebBrowserCtrl; + bool mFrequentUpdates, + mForceUpdate, + mTrusted, + mAlwaysRefresh, + mTakeFocusOnClick, + mStretchToFill, + mMaintainAspectRatio, + mHideLoading, + mHidingInitialLoad, + mClearCache, + mHoverTextChanged, + mDecoupleTextureSize; + + std::string mHomePageUrl, + mHomePageMimeType, + mCurrentNavUrl, + mErrorPageURL, + mTarget; viewer_media_t mMediaSource; + S32 mTextureWidth, + mTextureHeight; + LLHandle mContextMenu; }; #endif // LL_LLMediaCtrl_H diff --git a/indra/newview/llmediadataclient.cpp b/indra/newview/llmediadataclient.cpp new file mode 100644 index 000000000..31038b4aa --- /dev/null +++ b/indra/newview/llmediadataclient.cpp @@ -0,0 +1,1070 @@ +/** + * @file llmediadataclient.cpp + * @brief class for queueing up requests for media data + * + * $LicenseInfo:firstyear=2001&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 "llmediadataclient.h" + +#if LL_MSVC +// disable boost::lexical_cast warning +#pragma warning (disable:4702) +#endif + +#include + +#include "llhttpstatuscodes.h" +#include "llsdutil.h" +#include "llmediaentry.h" +#include "lltextureentry.h" +#include "llviewerregion.h" + +// +// When making a request +// - obtain the "overall interest score" of the object. +// This would be the sum of the impls' interest scores. +// - put the request onto a queue sorted by this score +// (highest score at the front of the queue) +// - On a timer, once a second, pull off the head of the queue and send +// the request. +// - Any request that gets a 503 still goes through the retry logic +// + +/*************************************************************************************************************** + What's up with this queueing code? + + First, a bit of background: + + Media on a prim was added into the system in the Viewer 2.0 timeframe. In order to avoid changing the + network format of objects, an unused field in the object (the "MediaURL" string) was repurposed to + indicate that the object had media data, and also hold a sequence number and the UUID of the agent + who last updated the data. The actual media data for objects is accessed via the "ObjectMedia" capability. + Due to concerns about sim performance, requests to this capability are rate-limited to 5 requests every + 5 seconds per agent. + + The initial implementation of LLMediaDataClient used a single queue to manage requests to the "ObjectMedia" cap. + Requests to the cap were queued so that objects closer to the avatar were loaded in first, since they were most + likely to be the ones the media performance manager would load. + + This worked in some cases, but we found that it was possible for a scripted object that constantly updated its + media data to starve other objects, since the same queue contained both requests to load previously unseen media + data and requests to fetch media data in response to object updates. + + The solution for this we came up with was to have two queues. The sorted queue contains requests to fetch media + data for objects that don't have it yet, and the round-robin queue contains requests to update media data for + objects that have already completed their initial load. When both queues are non-empty, the code ping-pongs + between them so that updates can't completely block initial load-in. +**************************************************************************************************************/ + +// +// Forward decls +// +const F32 LLMediaDataClient::QUEUE_TIMER_DELAY = 1.0; // seconds(s) +const F32 LLMediaDataClient::UNAVAILABLE_RETRY_TIMER_DELAY = 5.0; // secs +const U32 LLMediaDataClient::MAX_RETRIES = 4; +const U32 LLMediaDataClient::MAX_SORTED_QUEUE_SIZE = 10000; +const U32 LLMediaDataClient::MAX_ROUND_ROBIN_QUEUE_SIZE = 10000; + +// << operators +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q); +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &q); + +template +static typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type) +{ + for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter) + { + if(request->isMatch(*iter, match_type)) + { + return iter; + } + } + + return c.end(); +} + +template +static typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type) +{ + for(typename T::iterator iter = c.begin(); iter != c.end(); ++iter) + { + if(((*iter)->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == (*iter)->getType()))) + { + return iter; + } + } + + return c.end(); +} + +// NOTE: remove_matching_requests will not work correctly for containers where deleting an element may invalidate iterators +// to other elements in the container (such as std::vector). +// If the implementation is changed to use a container with this property, this will need to be revisited. +template +static void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type) +{ + for(typename T::iterator iter = c.begin(); iter != c.end();) + { + typename T::value_type i = *iter; + typename T::iterator next = iter; + next++; + if((i->getID() == id) && ((match_type == LLMediaDataClient::Request::ANY) || (match_type == i->getType()))) + { + i->markDead(); + c.erase(iter); + } + iter = next; + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::LLMediaDataClient(F32 queue_timer_delay, + F32 retry_timer_delay, + U32 max_retries, + U32 max_sorted_queue_size, + U32 max_round_robin_queue_size) + : mQueueTimerDelay(queue_timer_delay), + mRetryTimerDelay(retry_timer_delay), + mMaxNumRetries(max_retries), + mMaxSortedQueueSize(max_sorted_queue_size), + mMaxRoundRobinQueueSize(max_round_robin_queue_size), + mQueueTimerIsRunning(false) +{ +} + +LLMediaDataClient::~LLMediaDataClient() +{ + stopQueueTimer(); +} + +bool LLMediaDataClient::isEmpty() const +{ + return mQueue.empty(); +} + +bool LLMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) +{ + if(find_matching_request(mQueue, object->getID()) != mQueue.end()) + return true; + + if(find_matching_request(mUnQueuedRequests, object->getID()) != mUnQueuedRequests.end()) + return true; + + return false; +} + +void LLMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) +{ + LL_DEBUGS("LLMediaDataClient") << "removing requests matching ID " << object->getID() << LL_ENDL; + remove_matching_requests(mQueue, object->getID()); + remove_matching_requests(mUnQueuedRequests, object->getID()); +} + +void LLMediaDataClient::startQueueTimer() +{ + if (! mQueueTimerIsRunning) + { + LL_DEBUGS("LLMediaDataClient") << "starting queue timer (delay=" << mQueueTimerDelay << " seconds)" << LL_ENDL; + // LLEventTimer automagically takes care of the lifetime of this object + new QueueTimer(mQueueTimerDelay, this); + } + else { + LL_DEBUGS("LLMediaDataClient") << "queue timer is already running" << LL_ENDL; + } +} + +void LLMediaDataClient::stopQueueTimer() +{ + mQueueTimerIsRunning = false; +} + +bool LLMediaDataClient::processQueueTimer() +{ + if(isEmpty()) + return true; + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() started, queue size is: " << mQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() started, SORTED queue is: " << mQueue << LL_ENDL; + + serviceQueue(); + + LL_DEBUGS("LLMediaDataClient") << "QueueTimer::tick() finished, queue size is: " << mQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << "QueueTimer::tick() finished, SORTED queue is: " << mQueue << LL_ENDL; + + return isEmpty(); +} + +LLMediaDataClient::request_ptr_t LLMediaDataClient::dequeue() +{ + request_ptr_t request; + request_queue_t *queue_p = getQueue(); + + if (queue_p->empty()) + { + LL_DEBUGS("LLMediaDataClient") << "queue empty: " << (*queue_p) << LL_ENDL; + } + else + { + request = queue_p->front(); + + if(canServiceRequest(request)) + { + // We will be returning this request, so remove it from the queue. + queue_p->pop_front(); + } + else + { + // Don't return this request -- it's not ready to be serviced. + request = NULL; + } + } + + return request; +} + +void LLMediaDataClient::pushBack(request_ptr_t request) +{ + request_queue_t *queue_p = getQueue(); + queue_p->push_front(request); +} + +void LLMediaDataClient::trackRequest(request_ptr_t request) +{ + request_set_t::iterator iter = mUnQueuedRequests.find(request); + + if(iter != mUnQueuedRequests.end()) + { + LL_WARNS("LLMediaDataClient") << "Tracking already tracked request: " << *request << LL_ENDL; + } + else + { + mUnQueuedRequests.insert(request); + } +} + +void LLMediaDataClient::stopTrackingRequest(request_ptr_t request) +{ + request_set_t::iterator iter = mUnQueuedRequests.find(request); + + if (iter != mUnQueuedRequests.end()) + { + mUnQueuedRequests.erase(iter); + } + else + { + LL_WARNS("LLMediaDataClient") << "Removing an untracked request: " << *request << LL_ENDL; + } +} + +void LLMediaDataClient::serviceQueue() +{ + // Peel one off of the items from the queue and execute it + request_ptr_t request; + + do + { + request = dequeue(); + + if(request.isNull()) + { + // Queue is empty. + return; + } + + if(request->isDead()) + { + LL_INFOS("LLMediaDataClient") << "Skipping dead request " << *request << LL_ENDL; + continue; + } + + } while(false); + + // try to send the HTTP message to the cap url + std::string url = request->getCapability(); + if (!url.empty()) + { + const LLSD &sd_payload = request->getPayload(); + LL_INFOS("LLMediaDataClient") << "Sending request for " << *request << LL_ENDL; + + // Add this request to the non-queued tracking list + trackRequest(request); + + // and make the post + LLHTTPClient::post(url, sd_payload, request->createResponder()); + } + else + { + // Cap url doesn't exist. + + if(request->getRetryCount() < mMaxNumRetries) + { + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " (empty cap url), will retry." << LL_ENDL; + // Put this request back at the head of its queue, and retry next time the queue timer fires. + request->incRetryCount(); + pushBack(request); + } + else + { + // This request has exceeded its maxumim retry count. It will be dropped. + LL_WARNS("LLMediaDataClient") << "Could not send request " << *request << " for " << mMaxNumRetries << " tries, dropping request." << LL_ENDL; + } + + } +} + + +// dump the queue +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::request_queue_t &q) +{ + int i = 0; + LLMediaDataClient::request_queue_t::const_iterator iter = q.begin(); + LLMediaDataClient::request_queue_t::const_iterator end = q.end(); + while (iter != end) + { + s << "\t" << i << "]: " << (*iter)->getID().asString() << "(" << (*iter)->getObject()->getMediaInterest() << ")"; + iter++; + i++; + } + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::QueueTimer +// Queue of LLMediaDataClientObject smart pointers to request media for. +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::QueueTimer::QueueTimer(F32 time, LLMediaDataClient *mdc) +: LLEventTimer(time), mMDC(mdc) +{ + mMDC->setIsRunning(true); +} + +// virtual +BOOL LLMediaDataClient::QueueTimer::tick() +{ + BOOL result = TRUE; + + if (!mMDC.isNull()) + { + result = mMDC->processQueueTimer(); + + if(result) + { + // This timer won't fire again. + mMDC->setIsRunning(false); + mMDC = NULL; + } + } + + return result; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder::RetryTimer +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::RetryTimer::RetryTimer(F32 time, request_ptr_t request) +: LLEventTimer(time), mRequest(request) +{ + mRequest->startTracking(); +} + +// virtual +BOOL LLMediaDataClient::RetryTimer::tick() +{ + mRequest->stopTracking(); + + if(mRequest->isDead()) + { + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for dead request: " << *mRequest << ", aborting." << LL_ENDL; + } + else + { + LL_INFOS("LLMediaDataClient") << "RetryTimer fired for: " << *mRequest << ", retrying." << LL_ENDL; + mRequest->reEnqueue(); + } + + // Release the ref to the request. + mRequest = NULL; + + // Don't fire again + return TRUE; +} + + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Request +// +////////////////////////////////////////////////////////////////////////////////////// +/*static*/U32 LLMediaDataClient::Request::sNum = 0; + +LLMediaDataClient::Request::Request(Type in_type, + LLMediaDataClientObject *obj, + LLMediaDataClient *mdc, + S32 face) +: mType(in_type), + mObject(obj), + mNum(++sNum), + mRetryCount(0), + mMDC(mdc), + mScore((F64)0.0), + mFace(face) +{ + mObjectID = mObject->getID(); +} + +const char *LLMediaDataClient::Request::getCapName() const +{ + if(mMDC) + return mMDC->getCapabilityName(); + + return ""; +} + +std::string LLMediaDataClient::Request::getCapability() const +{ + if(mMDC) + { + return getObject()->getCapabilityUrl(getCapName()); + } + + return ""; +} + +const char *LLMediaDataClient::Request::getTypeAsString() const +{ + Type t = getType(); + switch (t) + { + case GET: + return "GET"; + break; + case UPDATE: + return "UPDATE"; + break; + case NAVIGATE: + return "NAVIGATE"; + break; + case ANY: + return "ANY"; + break; + } + return ""; +} + + +void LLMediaDataClient::Request::reEnqueue() +{ + if(mMDC) + { + mMDC->enqueue(this); + } +} + +F32 LLMediaDataClient::Request::getRetryTimerDelay() const +{ + if(mMDC) + return mMDC->mRetryTimerDelay; + + return 0.0f; +} + +U32 LLMediaDataClient::Request::getMaxNumRetries() const +{ + if(mMDC) + return mMDC->mMaxNumRetries; + + return 0; +} + +void LLMediaDataClient::Request::updateScore() +{ + F64 tmp = mObject->getMediaInterest(); + if (tmp != mScore) + { + LL_DEBUGS("LLMediaDataClient") << "Score for " << mObject->getID() << " changed from " << mScore << " to " << tmp << LL_ENDL; + mScore = tmp; + } +} + +void LLMediaDataClient::Request::markDead() +{ + mMDC = NULL; +} + +bool LLMediaDataClient::Request::isDead() +{ + return ((mMDC == NULL) || mObject->isDead()); +} + +void LLMediaDataClient::Request::startTracking() +{ + if(mMDC) + mMDC->trackRequest(this); +} + +void LLMediaDataClient::Request::stopTracking() +{ + if(mMDC) + mMDC->stopTrackingRequest(this); +} + +std::ostream& operator<<(std::ostream &s, const LLMediaDataClient::Request &r) +{ + s << "request: num=" << r.getNum() + << " type=" << r.getTypeAsString() + << " ID=" << r.getID() + << " face=" << r.getFace() + << " #retries=" << r.getRetryCount(); + return s; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLMediaDataClient::Responder +// +////////////////////////////////////////////////////////////////////////////////////// + +LLMediaDataClient::Responder::Responder(const request_ptr_t &request) +: mRequest(request) +{ +} + +/*virtual*/ +void LLMediaDataClient::Responder::error(U32 status, const std::string& reason) +{ + mRequest->stopTracking(); + + if(mRequest->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; + return; + } + + if (status == HTTP_SERVICE_UNAVAILABLE) + { + F32 retry_timeout = mRequest->getRetryTimerDelay(); + + mRequest->incRetryCount(); + + if (mRequest->getRetryCount() < mRequest->getMaxNumRetries()) + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retrying in " << retry_timeout << " seconds" << LL_ENDL; + + // Start timer (instances are automagically tracked by + // InstanceTracker<> and LLEventTimer) + new RetryTimer(F32(retry_timeout/*secs*/), mRequest); + } + else + { + LL_INFOS("LLMediaDataClient") << *mRequest << " got SERVICE_UNAVAILABLE...retry count " + << mRequest->getRetryCount() << " exceeds " << mRequest->getMaxNumRetries() << ", not retrying" << LL_ENDL; + } + } + else + { + std::string msg = boost::lexical_cast(status) + ": " + reason; + LL_WARNS("LLMediaDataClient") << *mRequest << " http error(" << msg << ")" << LL_ENDL; + } +} + +/*virtual*/ +void LLMediaDataClient::Responder::result(const LLSD& content) +{ + mRequest->stopTracking(); + + if(mRequest->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *mRequest << LL_ENDL; + return; + } + + LL_DEBUGS("LLMediaDataClientResponse") << *mRequest << " result : " << ll_print_sd(content) << LL_ENDL; +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaDataClient +// Subclass of LLMediaDataClient for the ObjectMedia cap +// +////////////////////////////////////////////////////////////////////////////////////// + +void LLObjectMediaDataClient::fetchMedia(LLMediaDataClientObject *object) +{ + // Create a get request and put it in the queue. + enqueue(new RequestGet(object, this)); +} + +const char *LLObjectMediaDataClient::getCapabilityName() const +{ + return "ObjectMedia"; +} + +LLObjectMediaDataClient::request_queue_t *LLObjectMediaDataClient::getQueue() +{ + return (mCurrentQueueIsTheSortedQueue) ? &mQueue : &mRoundRobinQueue; +} + +void LLObjectMediaDataClient::sortQueue() +{ + if(!mQueue.empty()) + { + // score all elements in the sorted queue. + for(request_queue_t::iterator iter = mQueue.begin(); iter != mQueue.end(); iter++) + { + (*iter)->updateScore(); + } + + // Re-sort the list... + mQueue.sort(compareRequestScores); + + // ...then cull items over the max + U32 size = mQueue.size(); + if (size > mMaxSortedQueueSize) + { + U32 num_to_cull = (size - mMaxSortedQueueSize); + LL_INFOS_ONCE("LLMediaDataClient") << "sorted queue MAXED OUT! Culling " + << num_to_cull << " items" << LL_ENDL; + while (num_to_cull-- > 0) + { + mQueue.back()->markDead(); + mQueue.pop_back(); + } + } + } + +} + +// static +bool LLObjectMediaDataClient::compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2) +{ + if (o2.isNull()) return true; + if (o1.isNull()) return false; + return ( o1->getScore() > o2->getScore() ); +} + +void LLObjectMediaDataClient::enqueue(Request *request) +{ + if(request->isDead()) + { + LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL; + return; + } + + // Invariants: + // new requests always go into the sorted queue. + // + + bool is_new = request->isNew(); + + if(!is_new && (request->getType() == Request::GET)) + { + // For GET requests that are not new, if a matching request is already in the round robin queue, + // in flight, or being retried, leave it at its current position. + request_queue_t::iterator iter = find_matching_request(mRoundRobinQueue, request->getID(), Request::GET); + request_set_t::iterator iter2 = find_matching_request(mUnQueuedRequests, request->getID(), Request::GET); + + if( (iter != mRoundRobinQueue.end()) || (iter2 != mUnQueuedRequests.end()) ) + { + LL_DEBUGS("LLMediaDataClient") << "ALREADY THERE: NOT Queuing request for " << *request << LL_ENDL; + + return; + } + } + + // TODO: should an UPDATE cause pending GET requests for the same object to be removed from the queue? + // IF the update will cause an object update message to be sent out at some point in the future, it probably should. + + // Remove any existing requests of this type for this object + remove_matching_requests(mQueue, request->getID(), request->getType()); + remove_matching_requests(mRoundRobinQueue, request->getID(), request->getType()); + remove_matching_requests(mUnQueuedRequests, request->getID(), request->getType()); + + if (is_new) + { + LL_DEBUGS("LLMediaDataClient") << "Queuing SORTED request for " << *request << LL_ENDL; + + mQueue.push_back(request); + + LL_DEBUGS("LLMediaDataClientQueue") << "SORTED queue:" << mQueue << LL_ENDL; + } + else + { + if (mRoundRobinQueue.size() > mMaxRoundRobinQueueSize) + { + LL_INFOS_ONCE("LLMediaDataClient") << "RR QUEUE MAXED OUT!!!" << LL_ENDL; + LL_DEBUGS("LLMediaDataClient") << "Not queuing " << *request << LL_ENDL; + return; + } + + LL_DEBUGS("LLMediaDataClient") << "Queuing RR request for " << *request << LL_ENDL; + // Push the request on the pending queue + mRoundRobinQueue.push_back(request); + + LL_DEBUGS("LLMediaDataClientQueue") << "RR queue:" << mRoundRobinQueue << LL_ENDL; + } + // Start the timer if not already running + startQueueTimer(); +} + +bool LLObjectMediaDataClient::canServiceRequest(request_ptr_t request) +{ + if(mCurrentQueueIsTheSortedQueue) + { + if(!request->getObject()->isInterestingEnough()) + { + LL_DEBUGS("LLMediaDataClient") << "Not fetching " << *request << ": not interesting enough" << LL_ENDL; + return false; + } + } + + return true; +}; + +void LLObjectMediaDataClient::swapCurrentQueue() +{ + // Swap + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + // If its empty, swap back + if (getQueue()->empty()) + { + mCurrentQueueIsTheSortedQueue = !mCurrentQueueIsTheSortedQueue; + } +} + +bool LLObjectMediaDataClient::isEmpty() const +{ + return mQueue.empty() && mRoundRobinQueue.empty(); +} + +bool LLObjectMediaDataClient::isInQueue(const LLMediaDataClientObject::ptr_t &object) +{ + // First, call parent impl. + if(LLMediaDataClient::isInQueue(object)) + return true; + + if(find_matching_request(mRoundRobinQueue, object->getID()) != mRoundRobinQueue.end()) + return true; + + return false; +} + +void LLObjectMediaDataClient::removeFromQueue(const LLMediaDataClientObject::ptr_t &object) +{ + // First, call parent impl. + LLMediaDataClient::removeFromQueue(object); + + remove_matching_requests(mRoundRobinQueue, object->getID()); +} + +bool LLObjectMediaDataClient::processQueueTimer() +{ + if(isEmpty()) + return true; + + LL_DEBUGS("LLMediaDataClient") << "started, SORTED queue size is: " << mQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL; + +// purgeDeadRequests(); + + sortQueue(); + + LL_DEBUGS("LLMediaDataClientQueue") << "after sort, SORTED queue is: " << mQueue << LL_ENDL; + + serviceQueue(); + + swapCurrentQueue(); + + LL_DEBUGS("LLMediaDataClient") << "finished, SORTED queue size is: " << mQueue.size() + << ", RR queue size is: " << mRoundRobinQueue.size() << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " SORTED queue is: " << mQueue << LL_ENDL; + LL_DEBUGS("LLMediaDataClientQueue") << " RR queue is: " << mRoundRobinQueue << LL_ENDL; + + return isEmpty(); +} + +LLObjectMediaDataClient::RequestGet::RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc): + LLMediaDataClient::Request(LLMediaDataClient::Request::GET, obj, mdc) +{ +} + +LLSD LLObjectMediaDataClient::RequestGet::getPayload() const +{ + LLSD result; + result["verb"] = "GET"; + result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID(); + + return result; +} + +LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestGet::createResponder() +{ + return new LLObjectMediaDataClient::Responder(this); +} + + +void LLObjectMediaDataClient::updateMedia(LLMediaDataClientObject *object) +{ + // Create an update request and put it in the queue. + enqueue(new RequestUpdate(object, this)); +} + +LLObjectMediaDataClient::RequestUpdate::RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc): + LLMediaDataClient::Request(LLMediaDataClient::Request::UPDATE, obj, mdc) +{ +} + +LLSD LLObjectMediaDataClient::RequestUpdate::getPayload() const +{ + LLSD result; + result["verb"] = "UPDATE"; + result[LLTextureEntry::OBJECT_ID_KEY] = mObject->getID(); + + LLSD object_media_data; + int i = 0; + int end = mObject->getMediaDataCount(); + for ( ; i < end ; ++i) + { + object_media_data.append(mObject->getMediaDataLLSD(i)); + } + + result[LLTextureEntry::OBJECT_MEDIA_DATA_KEY] = object_media_data; + + return result; +} + +LLMediaDataClient::Responder *LLObjectMediaDataClient::RequestUpdate::createResponder() +{ + // This just uses the base class's responder. + return new LLMediaDataClient::Responder(this); +} + + +/*virtual*/ +void LLObjectMediaDataClient::Responder::result(const LLSD& content) +{ + getRequest()->stopTracking(); + + if(getRequest()->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; + return; + } + + // This responder is only used for GET requests, not UPDATE. + + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " GET returned: " << ll_print_sd(content) << LL_ENDL; + + // Look for an error + if (content.has("error")) + { + const LLSD &error = content["error"]; + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error getting media data for object: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + + // XXX Warn user? + } + else + { + // Check the data + const LLUUID &object_id = content[LLTextureEntry::OBJECT_ID_KEY]; + if (object_id != getRequest()->getObject()->getID()) + { + // NOT good, wrong object id!! + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " DROPPING response with wrong object id (" << object_id << ")" << LL_ENDL; + return; + } + + // Otherwise, update with object media data + getRequest()->getObject()->updateObjectMediaData(content[LLTextureEntry::OBJECT_MEDIA_DATA_KEY], + content[LLTextureEntry::MEDIA_VERSION_KEY]); + } +} + +////////////////////////////////////////////////////////////////////////////////////// +// +// LLObjectMediaNavigateClient +// Subclass of LLMediaDataClient for the ObjectMediaNavigate cap +// +////////////////////////////////////////////////////////////////////////////////////// + +const char *LLObjectMediaNavigateClient::getCapabilityName() const +{ + return "ObjectMediaNavigate"; +} + +void LLObjectMediaNavigateClient::enqueue(Request *request) +{ + if(request->isDead()) + { + LL_DEBUGS("LLMediaDataClient") << "not queueing dead request " << *request << LL_ENDL; + return; + } + + // If there's already a matching request in the queue, remove it. + request_queue_t::iterator iter = find_matching_request(mQueue, request); + if(iter != mQueue.end()) + { + LL_DEBUGS("LLMediaDataClient") << "removing matching queued request " << (**iter) << LL_ENDL; + mQueue.erase(iter); + } + else + { + request_set_t::iterator set_iter = find_matching_request(mUnQueuedRequests, request); + if(set_iter != mUnQueuedRequests.end()) + { + LL_DEBUGS("LLMediaDataClient") << "removing matching unqueued request " << (**set_iter) << LL_ENDL; + mUnQueuedRequests.erase(set_iter); + } + } + +#if 0 + // Sadly, this doesn't work. It ends up creating a race condition when the user navigates and then hits the "back" button + // where the navigate-back appears to be spurious and doesn't get broadcast. + if(request->getObject()->isCurrentMediaUrl(request->getFace(), request->getURL())) + { + // This navigate request is trying to send the face to the current URL. Drop it. + LL_DEBUGS("LLMediaDataClient") << "dropping spurious request " << (*request) << LL_ENDL; + } + else +#endif + { + LL_DEBUGS("LLMediaDataClient") << "queueing new request " << (*request) << LL_ENDL; + mQueue.push_back(request); + + // Start the timer if not already running + startQueueTimer(); + } +} + +void LLObjectMediaNavigateClient::navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url) +{ + +// LL_INFOS("LLMediaDataClient") << "navigate() initiated: " << ll_print_sd(sd_payload) << LL_ENDL; + + // Create a get request and put it in the queue. + enqueue(new RequestNavigate(object, this, texture_index, url)); +} + +LLObjectMediaNavigateClient::RequestNavigate::RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url): + LLMediaDataClient::Request(LLMediaDataClient::Request::NAVIGATE, obj, mdc, (S32)texture_index), + mURL(url) +{ +} + +LLSD LLObjectMediaNavigateClient::RequestNavigate::getPayload() const +{ + LLSD result; + result[LLTextureEntry::OBJECT_ID_KEY] = getID(); + result[LLMediaEntry::CURRENT_URL_KEY] = mURL; + result[LLTextureEntry::TEXTURE_INDEX_KEY] = (LLSD::Integer)getFace(); + + return result; +} + +LLMediaDataClient::Responder *LLObjectMediaNavigateClient::RequestNavigate::createResponder() +{ + return new LLObjectMediaNavigateClient::Responder(this); +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::error(U32 status, const std::string& reason) +{ + getRequest()->stopTracking(); + + if(getRequest()->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; + return; + } + + // Bounce back (unless HTTP_SERVICE_UNAVAILABLE, in which case call base + // class + if (status == HTTP_SERVICE_UNAVAILABLE) + { + LLMediaDataClient::Responder::error(status, reason); + } + else + { + // bounce the face back + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: http code=" << status << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); + } +} + +/*virtual*/ +void LLObjectMediaNavigateClient::Responder::result(const LLSD& content) +{ + getRequest()->stopTracking(); + + if(getRequest()->isDead()) + { + LL_WARNS("LLMediaDataClient") << "dead request " << *(getRequest()) << LL_ENDL; + return; + } + + LL_INFOS("LLMediaDataClient") << *(getRequest()) << " NAVIGATE returned " << ll_print_sd(content) << LL_ENDL; + + if (content.has("error")) + { + const LLSD &error = content["error"]; + int error_code = error["code"]; + + if (ERROR_PERMISSION_DENIED_CODE == error_code) + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Navigation denied: bounce back" << LL_ENDL; + const LLSD &payload = getRequest()->getPayload(); + // bounce the face back + getRequest()->getObject()->mediaNavigateBounceBack((LLSD::Integer)payload[LLTextureEntry::TEXTURE_INDEX_KEY]); + } + else + { + LL_WARNS("LLMediaDataClient") << *(getRequest()) << " Error navigating: code=" << + error["code"].asString() << ": " << error["message"].asString() << LL_ENDL; + } + + // XXX Warn user? + } + else + { + // No action required. + LL_DEBUGS("LLMediaDataClientResponse") << *(getRequest()) << " result : " << ll_print_sd(content) << LL_ENDL; + } +} diff --git a/indra/newview/llmediadataclient.h b/indra/newview/llmediadataclient.h new file mode 100644 index 000000000..19ea5c6ce --- /dev/null +++ b/indra/newview/llmediadataclient.h @@ -0,0 +1,416 @@ +/** + * @file llmediadataclient.h + * @brief class for queueing up requests to the media service + * + * $LicenseInfo:firstyear=2007&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$ + */ + +#ifndef LL_LLMEDIADATACLIENT_H +#define LL_LLMEDIADATACLIENT_H + +#include "llhttpclient.h" +#include +#include "llrefcount.h" +#include "llpointer.h" +#include "lleventtimer.h" + +extern AIHTTPTimeoutPolicy mediaDataClientResponder_timeout; + +// Link seam for LLVOVolume +class LLMediaDataClientObject : public LLRefCount +{ +public: + // Get the number of media data items + virtual U8 getMediaDataCount() const = 0; + // Get the media data at index, as an LLSD + virtual LLSD getMediaDataLLSD(U8 index) const = 0; + // Return true if the current URL for the face in the media data matches the specified URL. + virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const = 0; + // Get this object's UUID + virtual LLUUID getID() const = 0; + // Navigate back to previous URL + virtual void mediaNavigateBounceBack(U8 index) = 0; + // Does this object have media? + virtual bool hasMedia() const = 0; + // Update the object's media data to the given array + virtual void updateObjectMediaData(LLSD const &media_data_array, const std::string &version_string) = 0; + // Return the total "interest" of the media (on-screen area) + virtual F64 getMediaInterest() const = 0; + // Return the given cap url + virtual std::string getCapabilityUrl(const std::string &name) const = 0; + // Return whether the object has been marked dead + virtual bool isDead() const = 0; + // Returns a media version number for the object + virtual U32 getMediaVersion() const = 0; + // Returns whether the object is "interesting enough" to fetch + virtual bool isInterestingEnough() const = 0; + // Returns whether we've seen this object yet or not + virtual bool isNew() const = 0; + + // smart pointer + typedef LLPointer ptr_t; +}; + + +// This object creates a priority queue for requests. +// Abstracts the Cap URL, the request, and the responder +class LLMediaDataClient : public LLRefCount +{ +public: + LOG_CLASS(LLMediaDataClient); + + const static F32 QUEUE_TIMER_DELAY;// = 1.0; // seconds(s) + const static F32 UNAVAILABLE_RETRY_TIMER_DELAY;// = 5.0; // secs + const static U32 MAX_RETRIES;// = 4; + const static U32 MAX_SORTED_QUEUE_SIZE;// = 10000; + const static U32 MAX_ROUND_ROBIN_QUEUE_SIZE;// = 10000; + + // Constructor + LLMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE); + + F32 getRetryTimerDelay() const { return mRetryTimerDelay; } + + // Returns true iff the queue is empty + virtual bool isEmpty() const; + + // Returns true iff the given object is in the queue + virtual bool isInQueue(const LLMediaDataClientObject::ptr_t &object); + + // Remove the given object from the queue. Returns true iff the given object is removed. + virtual void removeFromQueue(const LLMediaDataClientObject::ptr_t &object); + + // Called only by the Queue timer and tests (potentially) + virtual bool processQueueTimer(); + +protected: + // Destructor + virtual ~LLMediaDataClient(); // use unref + + class Responder; + + // Request (pure virtual base class for requests in the queue) + class Request : public LLRefCount + { + public: + // Subclasses must implement this to build a payload for their request type. + virtual LLSD getPayload() const = 0; + // and must create the correct type of responder. + virtual Responder *createResponder() = 0; + + virtual std::string getURL() { return ""; } + + enum Type { + GET, + UPDATE, + NAVIGATE, + ANY + }; + + protected: + // The only way to create one of these is through a subclass. + Request(Type in_type, LLMediaDataClientObject *obj, LLMediaDataClient *mdc, S32 face = -1); + public: + LLMediaDataClientObject *getObject() const { return mObject; } + + U32 getNum() const { return mNum; } + U32 getRetryCount() const { return mRetryCount; } + void incRetryCount() { mRetryCount++; } + Type getType() const { return mType; } + F64 getScore() const { return mScore; } + + // Note: may return empty string! + std::string getCapability() const; + const char *getCapName() const; + const char *getTypeAsString() const; + + // Re-enqueue thyself + void reEnqueue(); + + F32 getRetryTimerDelay() const; + U32 getMaxNumRetries() const; + + bool isObjectValid() const { return mObject.notNull() && (!mObject->isDead()); } + bool isNew() const { return isObjectValid() && mObject->isNew(); } + void updateScore(); + + void markDead(); + bool isDead(); + void startTracking(); + void stopTracking(); + + friend std::ostream& operator<<(std::ostream &s, const Request &q); + + const LLUUID &getID() const { return mObjectID; } + S32 getFace() const { return mFace; } + + bool isMatch (const Request* other, Type match_type = ANY) const + { + return ((match_type == ANY) || (mType == other->mType)) && + (mFace == other->mFace) && + (mObjectID == other->mObjectID); + } + protected: + LLMediaDataClientObject::ptr_t mObject; + private: + Type mType; + // Simple tracking + U32 mNum; + static U32 sNum; + U32 mRetryCount; + F64 mScore; + + LLUUID mObjectID; + S32 mFace; + + // Back pointer to the MDC...not a ref! + LLMediaDataClient *mMDC; + }; + typedef LLPointer request_ptr_t; + + // Responder + class Responder : public LLHTTPClient::ResponderWithResult + { + public: + + Responder(const request_ptr_t &request); + //If we get back an error (not found, etc...), handle it here + virtual void error(U32 status, const std::string& reason); + //If we get back a normal response, handle it here. Default just logs it. + virtual void result(const LLSD& content); + /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mediaDataClientResponder_timeout; } + /*virtual*/ char const* getName(void) const { return "LLMediaDataClientResponder"; } + + request_ptr_t &getRequest() { return mRequest; } + + private: + request_ptr_t mRequest; + }; + + class RetryTimer : public LLEventTimer + { + public: + RetryTimer(F32 time, request_ptr_t); + virtual BOOL tick(); + private: + // back-pointer + request_ptr_t mRequest; + }; + + +protected: + typedef std::list request_queue_t; + typedef std::set request_set_t; + + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const = 0; + + // Puts the request into a queue, appropriately handling duplicates, etc. + virtual void enqueue(Request*) = 0; + + virtual void serviceQueue(); + + virtual request_queue_t *getQueue() { return &mQueue; }; + + // Gets the next request, removing it from the queue + virtual request_ptr_t dequeue(); + + virtual bool canServiceRequest(request_ptr_t request) { return true; }; + + // Returns a request to the head of the queue (should only be used for requests that came from dequeue + virtual void pushBack(request_ptr_t request); + + void trackRequest(request_ptr_t request); + void stopTrackingRequest(request_ptr_t request); + + request_queue_t mQueue; + + const F32 mQueueTimerDelay; + const F32 mRetryTimerDelay; + const U32 mMaxNumRetries; + const U32 mMaxSortedQueueSize; + const U32 mMaxRoundRobinQueueSize; + + // Set for keeping track of requests that aren't in either queue. This includes: + // Requests that have been sent and are awaiting a response (pointer held by the Responder) + // Requests that are waiting for their retry timers to fire (pointer held by the retry timer) + request_set_t mUnQueuedRequests; + + void startQueueTimer(); + void stopQueueTimer(); + +private: + + static F64 getObjectScore(const LLMediaDataClientObject::ptr_t &obj); + + friend std::ostream& operator<<(std::ostream &s, const Request &q); + friend std::ostream& operator<<(std::ostream &s, const request_queue_t &q); + + class QueueTimer : public LLEventTimer + { + public: + QueueTimer(F32 time, LLMediaDataClient *mdc); + virtual BOOL tick(); + private: + // back-pointer + LLPointer mMDC; + }; + + void setIsRunning(bool val) { mQueueTimerIsRunning = val; } + + bool mQueueTimerIsRunning; + + template friend typename T::iterator find_matching_request(T &c, const LLMediaDataClient::Request *request, LLMediaDataClient::Request::Type match_type = LLMediaDataClient::Request::ANY); + template friend typename T::iterator find_matching_request(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type = LLMediaDataClient::Request::ANY); + template friend void remove_matching_requests(T &c, const LLUUID &id, LLMediaDataClient::Request::Type match_type = LLMediaDataClient::Request::ANY); + +}; + +// MediaDataClient specific for the ObjectMedia cap +class LLObjectMediaDataClient : public LLMediaDataClient +{ +public: + LOG_CLASS(LLObjectMediaDataClient); + LLObjectMediaDataClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries), + mCurrentQueueIsTheSortedQueue(true) + {} + + void fetchMedia(LLMediaDataClientObject *object); + void updateMedia(LLMediaDataClientObject *object); + + class RequestGet: public Request + { + public: + RequestGet(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); + /*virtual*/ LLSD getPayload() const; + /*virtual*/ Responder *createResponder(); + }; + + class RequestUpdate: public Request + { + public: + RequestUpdate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc); + /*virtual*/ LLSD getPayload() const; + /*virtual*/ Responder *createResponder(); + }; + + // Returns true iff the queue is empty + virtual bool isEmpty() const; + + // Returns true iff the given object is in the queue + virtual bool isInQueue(const LLMediaDataClientObject::ptr_t &object); + + // Remove the given object from the queue. Returns true iff the given object is removed. + virtual void removeFromQueue(const LLMediaDataClientObject::ptr_t &object); + + virtual bool processQueueTimer(); + + virtual bool canServiceRequest(request_ptr_t request); + +protected: + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + virtual request_queue_t *getQueue(); + + // Puts the request into the appropriate queue + virtual void enqueue(Request*); + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void result(const LLSD &content); + }; +private: + // The Get/Update data client needs a second queue to avoid object updates starving load-ins. + void swapCurrentQueue(); + + request_queue_t mRoundRobinQueue; + bool mCurrentQueueIsTheSortedQueue; + + // Comparator for sorting + static bool compareRequestScores(const request_ptr_t &o1, const request_ptr_t &o2); + void sortQueue(); +}; + + +// MediaDataClient specific for the ObjectMediaNavigate cap +class LLObjectMediaNavigateClient : public LLMediaDataClient +{ +public: + LOG_CLASS(LLObjectMediaNavigateClient); + // NOTE: from llmediaservice.h + static const int ERROR_PERMISSION_DENIED_CODE = 8002; + + LLObjectMediaNavigateClient(F32 queue_timer_delay = QUEUE_TIMER_DELAY, + F32 retry_timer_delay = UNAVAILABLE_RETRY_TIMER_DELAY, + U32 max_retries = MAX_RETRIES, + U32 max_sorted_queue_size = MAX_SORTED_QUEUE_SIZE, + U32 max_round_robin_queue_size = MAX_ROUND_ROBIN_QUEUE_SIZE) + : LLMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries) + {} + + void navigate(LLMediaDataClientObject *object, U8 texture_index, const std::string &url); + + // Puts the request into the appropriate queue + virtual void enqueue(Request*); + + class RequestNavigate: public Request + { + public: + RequestNavigate(LLMediaDataClientObject *obj, LLMediaDataClient *mdc, U8 texture_index, const std::string &url); + /*virtual*/ LLSD getPayload() const; + /*virtual*/ Responder *createResponder(); + /*virtual*/ std::string getURL() { return mURL; } + private: + std::string mURL; + }; + +protected: + // Subclasses must override to return a cap name + virtual const char *getCapabilityName() const; + + class Responder : public LLMediaDataClient::Responder + { + public: + Responder(const request_ptr_t &request) + : LLMediaDataClient::Responder(request) {} + virtual void error(U32 status, const std::string& reason); + virtual void result(const LLSD &content); + private: + void mediaNavigateBounceBack(); + }; + +}; + + +#endif // LL_LLMEDIADATACLIENT_H diff --git a/indra/newview/llmediaremotectrl.cpp b/indra/newview/llmediaremotectrl.cpp index e5fa89e16..38ce628da 100644 --- a/indra/newview/llmediaremotectrl.cpp +++ b/indra/newview/llmediaremotectrl.cpp @@ -181,7 +181,7 @@ void LLMediaRemoteCtrl::enableMediaButtons() std::string media_url = mControls->getString("default_tooltip_label"); LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (gSavedSettings.getBOOL("AudioStreamingVideo")) + if (gSavedSettings.getBOOL("AudioStreamingMedia")) { if ( parcel && !parcel->getMediaURL().empty()) { diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp index 449bf9329..28381301d 100644 --- a/indra/newview/llnamebox.cpp +++ b/indra/newview/llnamebox.cpp @@ -46,20 +46,18 @@ // statics std::set LLNameBox::sInstances; +static LLRegisterWidget r("name_box"); -LLNameBox::LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id, BOOL is_group, const LLFontGL* font, BOOL mouse_opaque) -: LLTextBox(name, rect, std::string("(retrieving)"), font, mouse_opaque), - mNameID(name_id) + +LLNameBox::LLNameBox(const std::string& name) +: LLTextBox(name, LLRect(), "" , NULL, TRUE) { + mNameID = LLUUID::null; + mLink = false; + //mParseHTML = mLink; // STORM-215 + mInitialValue = "(retrieving)"; LLNameBox::sInstances.insert(this); - if(!name_id.isNull()) - { - setNameID(name_id, is_group); - } - else - { - setText(LLStringUtil::null); - } + setText(LLStringUtil::null); } LLNameBox::~LLNameBox() @@ -72,25 +70,30 @@ void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group) mNameID = name_id; std::string name; + BOOL got_name = FALSE; if (!is_group) { - gCacheName->getFullName(name_id, name); + got_name = gCacheName->getFullName(name_id, name); } else { - gCacheName->getGroupName(name_id, name); + got_name = gCacheName->getGroupName(name_id, name); } - setText(name); + // Got the name already? Set it. + // Otherwise it will be set later in refresh(). + if (got_name) + setName(name, is_group); + else + setText(mInitialValue); } void LLNameBox::refresh(const LLUUID& id, const std::string& full_name, bool is_group) - { if (id == mNameID) { - setText(full_name); + setName(full_name, is_group); } } @@ -105,3 +108,39 @@ void LLNameBox::refreshAll(const LLUUID& id, const std::string& full_name, bool box->refresh(id, full_name, is_group); } } + +void LLNameBox::setName(const std::string& name, BOOL is_group) +{ + if (mLink) + { + std::string url; + + if (is_group) + url = "[secondlife:///app/group/" + mNameID.asString() + "/about " + name + "]"; + else + url = "[secondlife:///app/agent/" + mNameID.asString() + "/about " + name + "]"; + + setText(url); + } + else + { + setText(name); + } +} + +// virtual +void LLNameBox::initFromXML(LLXMLNodePtr node, LLView* parent) +{ + LLTextBox::initFromXML(node, parent); + node->getAttributeBOOL("link", mLink); + node->getAttributeString("initial_value", mInitialValue); +} + +// static +LLView* LLNameBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) +{ + LLNameBox* name_box = new LLNameBox("name_box"); + name_box->initFromXML(node,parent); + return name_box; +} + diff --git a/indra/newview/llnamebox.h b/indra/newview/llnamebox.h index cb968e78d..0d5ec6a83 100644 --- a/indra/newview/llnamebox.h +++ b/indra/newview/llnamebox.h @@ -44,10 +44,9 @@ class LLNameBox : public LLTextBox { public: - LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id = LLUUID::null, BOOL is_group = FALSE, const LLFontGL* font = NULL, BOOL mouse_opaque = TRUE ); - // By default, follows top and left and is mouse-opaque. - // If no text, text = name. - // If no font, uses default system font. + virtual void initFromXML(LLXMLNodePtr node, LLView* parent); + static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory); + virtual ~LLNameBox(); void setNameID(const LLUUID& name_id, BOOL is_group); @@ -56,11 +55,19 @@ public: static void refreshAll(const LLUUID& id, const std::string& full_name, bool is_group); +protected: + LLNameBox (const std::string& name); + + friend class LLUICtrlFactory; private: + void setName(const std::string& name, BOOL is_group); + static std::set sInstances; private: LLUUID mNameID; + BOOL mLink; + std::string mInitialValue; }; diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp index 1fac553c0..d7c948d36 100644 --- a/indra/newview/llnameeditor.cpp +++ b/indra/newview/llnameeditor.cpp @@ -132,9 +132,6 @@ LLXMLNodePtr LLNameEditor::getXML(bool save_children) const LLView* LLNameEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("name_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -143,7 +140,7 @@ LLView* LLNameEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory LLFontGL* font = LLView::selectFont(node); - LLNameEditor* line_editor = new LLNameEditor(name, + LLNameEditor* line_editor = new LLNameEditor("name_editor", rect, LLUUID::null, FALSE, font, diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 7c2b128d6..49f26ad4e 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -319,9 +319,6 @@ LLXMLNodePtr LLNameListCtrl::getXML(bool save_children) const LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("name_list"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -337,7 +334,7 @@ LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFacto S32 name_column_index = 0; node->getAttributeS32("name_column_index", name_column_index); - LLNameListCtrl* name_list = new LLNameListCtrl(name, + LLNameListCtrl* name_list = new LLNameListCtrl("name_list", rect, multi_select, draw_border, diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 7997c8df0..114bc56f1 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -69,7 +69,7 @@ #include "llmediactrl.h" #include "llselectmgr.h" #include "wlfPanel_AdvSettings.h" - +#include "llpanelnearbymedia.h" @@ -87,12 +87,6 @@ LLOverlayBar *gOverlayBar = NULL; -extern S32 MENU_BAR_HEIGHT; -extern ImportTracker gImportTracker; - -BOOL LLOverlayBar::sAdvSettingsPopup; -BOOL LLOverlayBar::sChatVisible; - // // Functions // @@ -115,9 +109,7 @@ void* LLOverlayBar::createVoiceRemote(void* userdata) void* LLOverlayBar::createAdvSettings(void* userdata) { - LLOverlayBar *self = (LLOverlayBar*)userdata; - self->mAdvSettings = new wlfPanel_AdvSettings(); - return self->mAdvSettings; + return wlfPanel_AdvSettings::getInstance(); } void* LLOverlayBar::createAORemote(void* userdata) @@ -134,7 +126,7 @@ void* LLOverlayBar::createChatBar(void* userdata) } LLOverlayBar::LLOverlayBar() - : LLPanel(), + : LLLayoutPanel(), mMediaRemote(NULL), mVoiceRemote(NULL), mAORemote(NULL), @@ -156,23 +148,36 @@ LLOverlayBar::LLOverlayBar() LLUICtrlFactory::getInstance()->buildPanel(this, "panel_overlaybar.xml", &factory_map); } -bool updateAdvSettingsPopup(const LLSD &data) +bool LLOverlayBar::updateAdvSettingsPopup(const LLSD &data) { - LLOverlayBar::sAdvSettingsPopup = gSavedSettings.getBOOL("wlfAdvSettingsPopup"); - gOverlayBar->childSetVisible("AdvSettings_container", !LLOverlayBar::sAdvSettingsPopup); - gOverlayBar->childSetVisible("AdvSettings_container_exp", LLOverlayBar::sAdvSettingsPopup); + bool wfl_adv_settings_popup = data.asBoolean(); + wlfPanel_AdvSettings::updateClass(); + LLLayoutPanel* layout_panel = dynamic_cast((LLPanel*)mAdvSettingsContainer); + if(layout_panel) + { + ((LLLayoutStack*)layout_panel->getParent())->collapsePanel(layout_panel,!wfl_adv_settings_popup); + if(wfl_adv_settings_popup) + layout_panel->setTargetDim(layout_panel->getChild("Adv_Settings")->getBoundingRect().getWidth()); + } + return true; } -bool updateChatVisible(const LLSD &data) +bool LLOverlayBar::updateChatVisible(const LLSD &data) { - LLOverlayBar::sChatVisible = data.asBoolean(); + mChatBar->getParent()->setVisible(data.asBoolean()); return true; } -bool updateAORemote(const LLSD &data) +bool LLOverlayBar::updateAORemoteVisible(const LLSD &data) { - gOverlayBar->childSetVisible("ao_remote_container", gSavedSettings.getBOOL("EnableAORemote")); + mAORemoteContainer->setVisible(data.asBoolean()); + return true; +} + +bool updateNearbyMediaFloater(const LLSD &data) +{ + LLFloaterNearbyMedia::updateClass(); return true; } @@ -185,12 +190,13 @@ BOOL LLOverlayBar::postBuild() childSetAction("Stand Up",onClickStandUp,this); childSetAction("Cancel TP",onClickCancelTP,this); childSetAction("Flycam",onClickFlycam,this); - childSetVisible("chat_bar", gSavedSettings.getBOOL("ChatVisible")); mCancelBtn = getChild("Cancel TP"); setFocusRoot(TRUE); mBuilt = true; + mUnreadCountStringPlural = getString("unread_count_string_plural"); + mChatbarAndButtons.connect(this,"chatbar_and_buttons"); mNewIM.connect(this,"New IM"); mNotBusy.connect(this,"Set Not Busy"); @@ -199,20 +205,26 @@ BOOL LLOverlayBar::postBuild() mFlyCam.connect(this,"Flycam"); mChatBar.connect(this,"chat_bar"); mVoiceRemoteContainer.connect(this,"voice_remote_container"); + mStateManagementContainer.connect(this,"state_management_buttons_container"); + mAORemoteContainer.connect(this,"ao_remote_container"); + mAdvSettingsContainer.connect(this,"AdvSettings_container"); + mMediaRemoteContainer.connect(this,"media_remote_container"); + + updateAdvSettingsPopup(gSavedSettings.getBOOL("wlfAdvSettingsPopup")); + updateChatVisible(gSavedSettings.getBOOL("ChatVisible")); + updateAORemoteVisible(gSavedSettings.getBOOL("EnableAORemote")); mOriginalIMLabel = mNewIM->getLabelSelected(); layoutButtons(); - sAdvSettingsPopup = gSavedSettings.getBOOL("wlfAdvSettingsPopup"); - sChatVisible = gSavedSettings.getBOOL("ChatVisible"); + gSavedSettings.getControl("wlfAdvSettingsPopup")->getSignal()->connect(boost::bind(&LLOverlayBar::updateAdvSettingsPopup,this,_2)); + gSavedSettings.getControl("ChatVisible")->getSignal()->connect(boost::bind(&LLOverlayBar::updateChatVisible,this,_2)); + gSavedSettings.getControl("EnableAORemote")->getSignal()->connect(boost::bind(&LLOverlayBar::updateAORemoteVisible,this,_2)); + gSavedSettings.getControl("ShowNearbyMediaFloater")->getSignal()->connect(boost::bind(&updateNearbyMediaFloater,_2)); + + mAORemoteContainer->setVisible(gSavedSettings.getBOOL("EnableAORemote")); - gSavedSettings.getControl("wlfAdvSettingsPopup")->getSignal()->connect(boost::bind(&updateAdvSettingsPopup,_2)); - gSavedSettings.getControl("ChatVisible")->getSignal()->connect(boost::bind(&updateChatVisible,_2)); - gSavedSettings.getControl("EnableAORemote")->getSignal()->connect(boost::bind(&updateAORemote,_2)); - childSetVisible("AdvSettings_container", !sAdvSettingsPopup); - childSetVisible("AdvSettings_container_exp", sAdvSettingsPopup); - childSetVisible("ao_remote_container", gSavedSettings.getBOOL("EnableAORemote")); return TRUE; @@ -242,12 +254,10 @@ void LLOverlayBar::reshape(S32 width, S32 height, BOOL called_from_parent) void LLOverlayBar::layoutButtons() { - LLView* state_buttons_panel = getChildView("state_management_buttons_container"); - - if (state_buttons_panel->getVisible()) + if (mStateManagementContainer->getVisible()) { U32 button_count = 0; - const child_list_t& view_list = *(state_buttons_panel->getChildList()); + const child_list_t& view_list = *(mStateManagementContainer->getChildList()); BOOST_FOREACH(LLView* viewp, view_list) { if(!viewp->getEnabled()) @@ -255,7 +265,7 @@ void LLOverlayBar::layoutButtons() ++button_count; } const S32 MAX_BAR_WIDTH = 600; - S32 bar_width = llclamp(state_buttons_panel->getRect().getWidth(), 0, MAX_BAR_WIDTH); + S32 bar_width = llclamp(mStateManagementContainer->getRect().getWidth(), 0, MAX_BAR_WIDTH); // calculate button widths const S32 MAX_BUTTON_WIDTH = 150; @@ -306,7 +316,7 @@ void LLOverlayBar::refresh() if (unread_count > 1) { std::stringstream ss; - ss << unread_count << " " << getString("unread_count_string_plural"); + ss << unread_count << " " << mUnreadCountStringPlural; button->setLabel(ss.str()); } else @@ -337,33 +347,17 @@ void LLOverlayBar::refresh() if(last_mouselook != in_mouselook) { last_mouselook = in_mouselook; - if (in_mouselook) - { - childSetVisible("media_remote_container", FALSE); - childSetVisible("voice_remote_container", FALSE); - childSetVisible("AdvSettings_container", FALSE); - childSetVisible("AdvSettings_container_exp", FALSE); - childSetVisible("ao_remote_container", FALSE); - childSetVisible("state_management_buttons_container", FALSE); - } - else - { - // update "remotes" - childSetVisible("media_remote_container", TRUE); - childSetVisible("voice_remote_container", LLVoiceClient::getInstance()->voiceEnabled()); - childSetVisible("AdvSettings_container", !sAdvSettingsPopup);//!gSavedSettings.getBOOL("wlfAdvSettingsPopup")); - childSetVisible("AdvSettings_container_exp", sAdvSettingsPopup);//gSavedSettings.getBOOL("wlfAdvSettingsPopup")); - childSetVisible("ao_remote_container", gSavedSettings.getBOOL("EnableAORemote")); - childSetVisible("state_management_buttons_container", TRUE); - } + + static const LLCachedControl enable_ao_remote("EnableAORemote", true); + mMediaRemoteContainer->setVisible(!in_mouselook); + mVoiceRemoteContainer->setVisible(!in_mouselook && LLVoiceClient::getInstance()->voiceEnabled()); + mAdvSettingsContainer->setVisible(!in_mouselook); + mAORemoteContainer->setVisible(!in_mouselook && enable_ao_remote); + mStateManagementContainer->setVisible(!in_mouselook); } if(!in_mouselook) mVoiceRemoteContainer->setVisible(LLVoiceClient::getInstance()->voiceEnabled()); - // always let user toggle into and out of chatbar - static const LLCachedControl chat_visible("ChatVisible",true); - mChatBar->setVisible(chat_visible); - if (buttons_changed) { layoutButtons(); diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h index 365b2ac41..a02977538 100644 --- a/indra/newview/lloverlaybar.h +++ b/indra/newview/lloverlaybar.h @@ -34,6 +34,7 @@ #define LL_LLOVERLAYBAR_H #include "llpanel.h" +#include "lllayoutstack.h" // "Constants" loaded from settings.xml at start time extern S32 STATUS_BAR_HEIGHT; @@ -55,7 +56,7 @@ class AORemoteCtrl; class LLChatBar; class LLOverlayBar -: public LLPanel +: public LLLayoutPanel { public: LLOverlayBar(); @@ -97,8 +98,6 @@ public: void setCancelTPButtonVisible(BOOL b, const std::string& label); - static BOOL sChatVisible; - static BOOL sAdvSettingsPopup; protected: static void* createMediaRemote(void* userdata); static void* createVoiceRemote(void* userdata); @@ -109,15 +108,16 @@ protected: void enableMediaButtons(); protected: + friend class LLFloaterNearbyMedia; //Crappy workaround to access mMediaRemote LLMediaRemoteCtrl* mMediaRemote; LLVoiceRemoteCtrl* mVoiceRemote; LLButton* mCancelBtn; - wlfPanel_AdvSettings* mAdvSettings; AORemoteCtrl* mAORemote; bool mBuilt; // dialog constructed yet? enum { STOPPED=0, PLAYING=1, PAUSED=2 }; S32 mMusicState; std::string mOriginalIMLabel; + std::string mUnreadCountStringPlural; CachedUICtrl mChatbarAndButtons; CachedUICtrl mNewIM; @@ -127,12 +127,14 @@ protected: CachedUICtrl mFlyCam; CachedUICtrl mChatBar; CachedUICtrl mVoiceRemoteContainer; + CachedUICtrl mStateManagementContainer; + CachedUICtrl mAORemoteContainer; + CachedUICtrl mAdvSettingsContainer; + CachedUICtrl mMediaRemoteContainer; private: - - - /*static void updateAdvSettingsPopup(const LLSD &data); - static void updateChatVisible(const LLSD &data);*/ - + bool updateChatVisible(const LLSD &data); + bool updateAORemoteVisible(const LLSD &data); + bool updateAdvSettingsPopup(const LLSD &data); }; extern LLOverlayBar* gOverlayBar; diff --git a/indra/newview/llpanelaudioprefs.cpp b/indra/newview/llpanelaudioprefs.cpp index d58635c85..e61028fa8 100644 --- a/indra/newview/llpanelaudioprefs.cpp +++ b/indra/newview/llpanelaudioprefs.cpp @@ -111,7 +111,7 @@ void LLPanelAudioPrefs::refreshValues() mPreviousHealthThreshold = gSavedSettings.getF32("UISndHealthReductionThreshold"); mPreviousStreamingMusic = gSavedSettings.getBOOL("AudioStreamingMusic"); - mPreviousStreamingVideo = gSavedSettings.getBOOL("AudioStreamingVideo"); + mPreviousStreamingVideo = gSavedSettings.getBOOL("AudioStreamingMedia"); mPreviousMuteAudio = gSavedSettings.getBOOL("MuteAudio"); mPreviousMuteWhenMinimized = gSavedSettings.getBOOL("MuteWhenMinimized"); @@ -133,7 +133,7 @@ void LLPanelAudioPrefs::cancel() gSavedSettings.setF32("UISndHealthReductionThreshold", mPreviousHealthThreshold ); gSavedSettings.setBOOL("AudioStreamingMusic", mPreviousStreamingMusic ); - gSavedSettings.setBOOL("AudioStreamingVideo", mPreviousStreamingVideo ); + gSavedSettings.setBOOL("AudioStreamingMedia", mPreviousStreamingVideo ); gSavedSettings.setBOOL("MuteAudio", mPreviousMuteAudio ); diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index d375b1dc1..bbf57a1b5 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -567,7 +567,7 @@ BOOL LLPanelAvatarWeb::postBuild(void) mWebBrowser->addObserver(this); // links open in internally - mWebBrowser->setOpenInExternalBrowser( false ); + //mWebBrowser->setOpenInExternalBrowser( false ); return TRUE; } diff --git a/indra/newview/llpanelcontents.cpp b/indra/newview/llpanelcontents.cpp index 5eda11310..c44a855b7 100644 --- a/indra/newview/llpanelcontents.cpp +++ b/indra/newview/llpanelcontents.cpp @@ -82,6 +82,13 @@ // // Globals // +const char* LLPanelContents::TENTATIVE_SUFFIX = "_tentative"; +const char* LLPanelContents::PERMS_OWNER_INTERACT_KEY = "perms_owner_interact"; +const char* LLPanelContents::PERMS_OWNER_CONTROL_KEY = "perms_owner_control"; +const char* LLPanelContents::PERMS_GROUP_INTERACT_KEY = "perms_group_interact"; +const char* LLPanelContents::PERMS_GROUP_CONTROL_KEY = "perms_group_control"; +const char* LLPanelContents::PERMS_ANYONE_INTERACT_KEY = "perms_anyone_interact"; +const char* LLPanelContents::PERMS_ANYONE_CONTROL_KEY = "perms_anyone_control"; BOOL LLPanelContents::postBuild() { diff --git a/indra/newview/llpanelcontents.h b/indra/newview/llpanelcontents.h index b7b103d5b..8e4aa7c4e 100644 --- a/indra/newview/llpanelcontents.h +++ b/indra/newview/llpanelcontents.h @@ -54,6 +54,17 @@ public: static void onClickNewScript( void* userdata); static void onClickPermissions( void* userdata); + // Key suffix for "tentative" fields + static const char* TENTATIVE_SUFFIX; + + // These aren't fields in LLMediaEntry, so we have to define them ourselves for checkbox control + static const char* PERMS_OWNER_INTERACT_KEY; + static const char* PERMS_OWNER_CONTROL_KEY; + static const char* PERMS_GROUP_INTERACT_KEY; + static const char* PERMS_GROUP_CONTROL_KEY; + static const char* PERMS_ANYONE_INTERACT_KEY; + static const char* PERMS_ANYONE_CONTROL_KEY; + protected: void getState(LLViewerObject *object); diff --git a/indra/newview/llpaneldirfind.cpp b/indra/newview/llpaneldirfind.cpp index b380942f7..c181497cc 100644 --- a/indra/newview/llpaneldirfind.cpp +++ b/indra/newview/llpaneldirfind.cpp @@ -151,13 +151,9 @@ BOOL LLPanelDirFind::postBuild() if (mWebBrowser) { mWebBrowser->addObserver(this); - - // new pages appear in same window as the results page now - mWebBrowser->setOpenInInternalBrowser( false ); - mWebBrowser->setOpenInExternalBrowser( false ); // need to handle secondlife:///app/ URLs for direct teleports - mWebBrowser->setTrusted( true ); + mWebBrowser->setTrustedContent( true ); // redirect 404 pages from S3 somewhere else mWebBrowser->set404RedirectUrl( getString("redirect_404_url") ); diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index f3b7f5af1..78ef25eed 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -45,6 +45,7 @@ #include "llscrollingpanelparam.h" #include "llradiogroup.h" #include "llnotificationsutil.h" +#include "llnotifications.h" #include "llcolorswatch.h" #include "lltexturectrl.h" diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index be0ebbad9..c6ed7163f 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -53,6 +53,8 @@ #include "llface.h" #include "llinventorymodel.h" //Perms check for texture params #include "lllineeditor.h" +#include "llmediaentry.h" +#include "llnotificationsutil.h" #include "llresmgr.h" #include "llselectmgr.h" #include "llspinctrl.h" @@ -66,8 +68,10 @@ #include "llviewerobject.h" #include "llviewerregion.h" #include "llviewerstats.h" +#include "llvovolume.h" #include "lluictrlfactory.h" #include "llpluginclassmedia.h" +#include "llviewertexturelist.h" // // Methods @@ -75,6 +79,22 @@ BOOL LLPanelFace::postBuild() { + childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this); + childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this); + + childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("checkbox flip t",&LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("TexRot",&LLPanelFace::onCommitTextureInfo, this); + childSetAction("button apply",&LLPanelFace::onClickApply,this); + childSetCommitCallback("checkbox planar align",&LLPanelFace::onCommitPlanarAlign, this); + childSetCommitCallback("TexOffsetU",LLPanelFace::onCommitTextureInfo, this); + childSetCommitCallback("TexOffsetV",LLPanelFace::onCommitTextureInfo, this); + childSetAction("button align",&LLPanelFace::onClickAutoFix,this); + childSetAction("copytextures",&LLPanelFace::onClickCopy,this); + childSetAction("pastetextures",&LLPanelFace::onClickPaste,this); + LLTextureCtrl* mTextureCtrl; LLColorSwatchCtrl* mColorSwatch; @@ -165,20 +185,6 @@ BOOL LLPanelFace::postBuild() mCtrlGlow->setCommitCallback(LLPanelFace::onCommitGlow, this); } - childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this); - childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this); - childSetCommitCallback("checkbox planar align",&LLPanelFace::onCommitPlanarAlign, this); - childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("checkbox flip t",&LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("TexRot",&LLPanelFace::onCommitTextureInfo, this); - childSetAction("button apply",&onClickApply,this); - childSetCommitCallback("TexOffsetU",LLPanelFace::onCommitTextureInfo, this); - childSetCommitCallback("TexOffsetV",LLPanelFace::onCommitTextureInfo, this); - childSetAction("button align",onClickAutoFix,this); - childSetAction("copytextures",onClickCopy,this); - childSetAction("pastetextures",onClickPaste,this); clearCtrls(); @@ -511,12 +517,9 @@ void LLPanelFace::getState() { BOOL editable = objectp->permModify() && !objectp->isPermanentEnforced(); - // only turn on auto-adjust button if there is a media renderer and the media is loaded - childSetEnabled("textbox autofix",FALSE); - //mLabelTexAutoFix->setEnabled ( FALSE ); - childSetEnabled("button align",FALSE); - //mBtnAutoFix->setEnabled ( FALSE ); + getChildView("textbox autofix")->setEnabled(editable); + getChildView("button align")->setEnabled(editable); //if ( LLMediaEngine::getInstance()->getMediaRenderer () ) // if ( LLMediaEngine::getInstance()->getMediaRenderer ()->isLoaded () ) @@ -533,7 +536,7 @@ void LLPanelFace::getState() childSetEnabled("copytextures", single_volume && editable); childSetEnabled("pastetextures", single_volume && editable); childSetEnabled("textbox params", single_volume && editable); - childSetEnabled("button apply",editable); + getChildView("button apply")->setEnabled(editable); bool identical; LLTextureCtrl* texture_ctrl = getChild("texture control"); @@ -543,20 +546,43 @@ void LLPanelFace::getState() LLUUID id; struct f1 : public LLSelectedTEGetFunctor { - LLUUID get(LLViewerObject* object, S32 te) + LLUUID get(LLViewerObject* object, S32 te_index) { + LLUUID id; //LLViewerTexture* image = object->getTEImage(te); - LLTextureEntry* image = object->getTE(te); //Singu Note: Use this instead of the above. - //The above actually returns LLViewerFetchedTexture::sDefaultImagep when - //the texture id is null, which gives us IMG_DEFAULT, not LLUUID::null - //Such behavior prevents the 'None' button from ever greying out in the face panel. - return image ? image->getID() : LLUUID::null; - + LLTextureEntry* image = object->getTE(te_index); //Singu Note: Use this instead of the above. + //The above actually returns LLViewerFetchedTexture::sDefaultImagep when + //the texture id is null, which gives us IMG_DEFAULT, not LLUUID::null + //Such behavior prevents the 'None' button from ever greying out in the face panel. + if (image) id = image->getID(); + if (!id.isNull() && LLViewerMedia::textureHasMedia(id)) + { + LLTextureEntry *te = object->getTE(te_index); + if (te) + { + LLViewerTexture* tex = te->getID().notNull() ? gTextureList.findImage(te->getID()) : NULL ; + if(!tex) + { + tex = LLViewerFetchedTexture::sDefaultImagep; + } + if (tex) + { + id = tex->getID(); + } + } + } + return id; } } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, id ); + if(LLViewerMedia::textureHasMedia(id)) + { + getChildView("textbox autofix")->setEnabled(editable); + getChildView("button align")->setEnabled(editable); + } + if (identical) { // All selected have the same texture @@ -587,13 +613,6 @@ void LLPanelFace::getState() } } } - - if(LLViewerMedia::textureHasMedia(id)) - { - childSetEnabled("textbox autofix",editable); - childSetEnabled("button align",editable); - } - } @@ -658,7 +677,7 @@ void LLPanelFace::getState() // Texture scale { - childSetEnabled("tex scale",editable); + getChildView("tex scale")->setEnabled(editable); //mLabelTexScale->setEnabled( editable ); F32 scale_s = 1.f; struct f2 : public LLSelectedTEGetFunctor @@ -670,12 +689,12 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, scale_s ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexScaleU",editable ? llabs(scale_s) : 0); - childSetTentative("TexScaleU",LLSD((BOOL)(!identical))); - childSetEnabled("TexScaleU",editable); - childSetValue("checkbox flip s",LLSD((BOOL)(scale_s < 0 ? TRUE : FALSE ))); - childSetTentative("checkbox flip s",LLSD((BOOL)((!identical) ? TRUE : FALSE ))); - childSetEnabled("checkbox flip s",editable); + getChild("TexScaleU")->setValue(editable ? llabs(scale_s) : 0); + getChild("TexScaleU")->setTentative(LLSD((BOOL)(!identical))); + getChildView("TexScaleU")->setEnabled(editable); + getChild("checkbox flip s")->setValue(LLSD((BOOL)(scale_s < 0 ? TRUE : FALSE ))); + getChild("checkbox flip s")->setTentative(LLSD((BOOL)((!identical) ? TRUE : FALSE ))); + getChildView("checkbox flip s")->setEnabled(editable); } { @@ -690,17 +709,17 @@ void LLPanelFace::getState() identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, scale_t ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexScaleV",llabs(editable ? llabs(scale_t) : 0)); - childSetTentative("TexScaleV",LLSD((BOOL)(!identical))); - childSetEnabled("TexScaleV",editable); - childSetValue("checkbox flip t",LLSD((BOOL)(scale_t< 0 ? TRUE : FALSE ))); - childSetTentative("checkbox flip t",LLSD((BOOL)((!identical) ? TRUE : FALSE ))); - childSetEnabled("checkbox flip t",editable); + getChild("TexScaleV")->setValue(llabs(editable ? llabs(scale_t) : 0)); + getChild("TexScaleV")->setTentative(LLSD((BOOL)(!identical))); + getChildView("TexScaleV")->setEnabled(editable); + getChild("checkbox flip t")->setValue(LLSD((BOOL)(scale_t< 0 ? TRUE : FALSE ))); + getChild("checkbox flip t")->setTentative(LLSD((BOOL)((!identical) ? TRUE : FALSE ))); + getChildView("checkbox flip t")->setEnabled(editable); } // Texture offset { - childSetEnabled("tex offset",editable); + getChildView("tex offset")->setEnabled(editable); F32 offset_s = 0.f; struct f4 : public LLSelectedTEGetFunctor { @@ -711,9 +730,9 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, offset_s ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexOffsetU", editable ? offset_s : 0); - childSetTentative("TexOffsetU",!identical); - childSetEnabled("TexOffsetU",editable); + getChild("TexOffsetU")->setValue(editable ? offset_s : 0); + getChild("TexOffsetU")->setTentative(!identical); + getChildView("TexOffsetU")->setEnabled(editable); } { @@ -727,14 +746,14 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, offset_t ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexOffsetV", editable ? offset_t : 0); - childSetTentative("TexOffsetV",!identical); - childSetEnabled("TexOffsetV",editable); + getChild("TexOffsetV")->setValue(editable ? offset_t : 0); + getChild("TexOffsetV")->setTentative(!identical); + getChildView("TexOffsetV")->setEnabled(editable); } // Texture rotation { - childSetEnabled("tex rotate",editable); + getChildView("tex rotate")->setEnabled(editable); F32 rotation = 0.f; struct f6 : public LLSelectedTEGetFunctor { @@ -745,9 +764,9 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, rotation ); identical = align_planar ? identical_planar_aligned : identical; - childSetValue("TexRot", editable ? rotation * RAD_TO_DEG : 0); - childSetTentative("TexRot",!identical); - childSetEnabled("TexRot",editable); + getChild("TexRot")->setValue(editable ? rotation * RAD_TO_DEG : 0); + getChild("TexRot")->setTentative(!identical); + getChildView("TexRot")->setEnabled(editable); } // Color swatch @@ -773,13 +792,13 @@ void LLPanelFace::getState() } // Color transparency { - childSetEnabled("color trans",editable); + getChildView("color trans")->setEnabled(editable); } F32 transparency = (1.f - color.mV[VALPHA]) * 100.f; { - childSetValue("ColorTrans", editable ? transparency : 0); - childSetEnabled("ColorTrans",editable); + getChild("ColorTrans")->setValue(editable ? transparency : 0); + getChildView("ColorTrans")->setEnabled(editable); } { @@ -793,10 +812,10 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, glow ); - childSetValue("glow",glow); - childSetEnabled("glow",editable); - childSetTentative("glow",!identical); - childSetEnabled("glow label",editable); + getChild("glow")->setValue(glow); + getChildView("glow")->setEnabled(editable); + getChild("glow")->setTentative(!identical); + getChildView("glow label")->setEnabled(editable); } @@ -821,9 +840,9 @@ void LLPanelFace::getState() { llwarns << "failed childGetSelectionInterface for 'combobox shininess'" << llendl; } - childSetEnabled("combobox shininess",editable); - childSetTentative("combobox shininess",!identical); - childSetEnabled("label shininess",editable); + getChildView("combobox shininess")->setEnabled(editable); + getChild("combobox shininess")->setTentative(!identical); + getChildView("label shininess")->setEnabled(editable); } { @@ -846,9 +865,9 @@ void LLPanelFace::getState() { llwarns << "failed childGetSelectionInterface for 'combobox bumpiness'" << llendl; } - childSetEnabled("combobox bumpiness",editable); - childSetTentative("combobox bumpiness",!identical); - childSetEnabled("label bumpiness",editable); + getChildView("combobox bumpiness")->setEnabled(editable); + getChild("combobox bumpiness")->setTentative(!identical); + getChildView("label bumpiness")->setEnabled(editable); } { @@ -872,19 +891,21 @@ void LLPanelFace::getState() { llwarns << "failed childGetSelectionInterface for 'combobox texgen'" << llendl; } - childSetEnabled("combobox texgen",editable); - childSetTentative("combobox texgen",!identical); - childSetEnabled("tex gen",editable); + getChildView("combobox texgen")->setEnabled(editable); + getChild("combobox texgen")->setTentative(!identical); + getChildView("tex gen")->setEnabled(editable); if (selected_texgen == 1) { - childSetText("tex scale",getString("string repeats per meter")); - childSetValue("TexScaleU", 2.0f * childGetValue("TexScaleU").asReal() ); - childSetValue("TexScaleV", 2.0f * childGetValue("TexScaleV").asReal() ); + getChild("TexScaleU")->setValue(2.0f * getChild("TexScaleU")->getValue().asReal() ); + getChild("TexScaleV")->setValue(2.0f * getChild("TexScaleV")->getValue().asReal() ); + + // EXP-1507 (change label based on the mapping mode) + getChild("tex scale")->setValue(getString("string repeats per meter")); } else { - childSetText("tex scale",getString("string repeats per face")); + getChild("tex scale")->setValue(getString("string repeats per face")); } } @@ -900,14 +921,14 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, fullbrightf ); - childSetValue("checkbox fullbright",(S32)fullbrightf); - childSetEnabled("checkbox fullbright",editable); - childSetTentative("checkbox fullbright",!identical); + getChild("checkbox fullbright")->setValue((S32)fullbrightf); + getChildView("checkbox fullbright")->setEnabled(editable); + getChild("checkbox fullbright")->setTentative(!identical); } // Repeats per meter label { - childSetEnabled("rpt",editable); + getChildView("rpt")->setEnabled(editable); } // Repeats per meter @@ -927,14 +948,14 @@ void LLPanelFace::getState() } func; identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func, repeats ); - childSetValue("rptctrl", editable ? repeats : 0); - childSetTentative("rptctrl",!identical); + getChild("rptctrl")->setValue(editable ? repeats : 0); + getChild("rptctrl")->setTentative(!identical); LLComboBox* mComboTexGen = getChild("combobox texgen"); if (mComboTexGen) { BOOL enabled = editable && (!mComboTexGen || mComboTexGen->getCurrentIndex() != 1); - childSetEnabled("rptctrl",enabled); - childSetEnabled("button apply",enabled); + getChildView("rptctrl")->setEnabled(enabled); + getChildView("button apply")->setEnabled(enabled); } } @@ -968,19 +989,21 @@ void LLPanelFace::getState() mColorSwatch->setFallbackImageName("locked_image.j2c" ); mColorSwatch->setValid(FALSE); } - childSetEnabled("color trans",FALSE); - childSetEnabled("rpt",FALSE); - childSetEnabled("tex scale",FALSE); - childSetEnabled("tex offset",FALSE); - childSetEnabled("tex rotate",FALSE); - childSetEnabled("tex gen",FALSE); - childSetEnabled("label shininess",FALSE); - childSetEnabled("label bumpiness",FALSE); + getChildView("color trans")->setEnabled(FALSE); + getChildView("rpt")->setEnabled(FALSE); + getChildView("tex scale")->setEnabled(FALSE); + getChildView("tex offset")->setEnabled(FALSE); + getChildView("tex rotate")->setEnabled(FALSE); + getChildView("tex gen")->setEnabled(FALSE); + getChildView("label shininess")->setEnabled(FALSE); + getChildView("label bumpiness")->setEnabled(FALSE); - childSetEnabled("textbox autofix",FALSE); + getChildView("textbox autofix")->setEnabled(FALSE); - childSetEnabled("button align",FALSE); - childSetEnabled("button apply",FALSE); + getChildView("button align")->setEnabled(FALSE); + getChildView("button apply")->setEnabled(FALSE); + //getChildView("has media")->setEnabled(FALSE); + //getChildView("media info set")->setEnabled(FALSE); // Set variable values for numeric expressions @@ -1119,19 +1142,29 @@ void LLPanelFace::onClickApply(void* userdata) gFocusMgr.setKeyboardFocus( NULL ); //F32 repeats_per_meter = self->mCtrlRepeatsPerMeter->get(); - F32 repeats_per_meter = (F32)self->childGetValue( "rptctrl" ).asReal();//self->mCtrlRepeatsPerMeter->get(); + F32 repeats_per_meter = (F32)self->getChild("rptctrl")->getValue().asReal();//self->mCtrlRepeatsPerMeter->get(); LLSelectMgr::getInstance()->selectionTexScaleAutofit( repeats_per_meter ); } -// commit the fit media texture to prim button - struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor { virtual bool apply(LLViewerObject* object, S32 te) { - // TODO: the media impl pointer should actually be stored by the texture - viewer_media_t pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(object->getTE ( te )->getID()); - // only do this if it's a media texture + viewer_media_t pMediaImpl; + + const LLTextureEntry* tep = object->getTE(te); + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; + if ( mep ) + { + pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); + } + + if ( pMediaImpl.isNull()) + { + // If we didn't find face media for this face, check whether this face is showing parcel media. + pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); + } + if ( pMediaImpl.notNull()) { LLPluginClassMedia *media = pMediaImpl->getMediaPlugin(); @@ -1163,6 +1196,17 @@ void LLPanelFace::onClickAutoFix(void* userdata) LLPanelFaceSendFunctor sendfunc; LLSelectMgr::getInstance()->getSelection()->applyToObjects(&sendfunc); } + + + +// TODO: I don't know who put these in or what these are for??? +void LLPanelFace::setMediaURL(const std::string& url) +{ +} +void LLPanelFace::setMediaType(const std::string& mime_type) +{ +} + // static void LLPanelFace::onCommitPlanarAlign(LLUICtrl* ctrl, void* userdata) { diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h index fac5e7fb0..52ccbaf10 100644 --- a/indra/newview/llpanelface.h +++ b/indra/newview/llpanelface.h @@ -56,6 +56,8 @@ public: virtual ~LLPanelFace(); void refresh(); + void setMediaURL(const std::string& url); + void setMediaType(const std::string& mime_type); protected: void getState(); @@ -69,6 +71,7 @@ protected: void sendShiny(); // applies and sends shininess void sendFullbright(); // applies and sends full bright void sendGlow(); + void sendMedia(); // this function is to return TRUE if the drag should succeed. static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item); diff --git a/indra/newview/llpanelgeneral.cpp b/indra/newview/llpanelgeneral.cpp index bd67c5284..559dba021 100644 --- a/indra/newview/llpanelgeneral.cpp +++ b/indra/newview/llpanelgeneral.cpp @@ -39,7 +39,6 @@ #include "llcolorswatch.h" #include "llcombobox.h" #include "lluictrlfactory.h" -#include "llurlsimstring.h" #include "llviewercontrol.h" #include "llagent.h" @@ -48,6 +47,7 @@ #include "llavatarnamecache.h" #include "llvoavatar.h" #include "llcallingcard.h" +#include "llnotifications.h" LLPanelGeneral::LLPanelGeneral() { @@ -62,7 +62,11 @@ BOOL LLPanelGeneral::postBuild() LLComboBox* namesystem_combobox = getChild("namesystem_combobox"); namesystem_combobox->setCurrentByIndex(gSavedSettings.getS32("PhoenixNameSystem")); - childSetValue("default_start_location", gSavedSettings.getBOOL("LoginLastLocation") ? "MyLastLocation" : "MyHome"); + std::string login_location = gSavedSettings.getString("LoginLocation"); + if(login_location != "last" && login_location != "home") + login_location = "last"; + + childSetValue("default_start_location", login_location); childSetValue("show_location_checkbox", gSavedSettings.getBOOL("ShowStartLocation")); childSetValue("show_all_title_checkbox", gSavedSettings.getBOOL("RenderHideGroupTitleAll")); childSetValue("language_is_public", gSavedSettings.getBOOL("LanguageIsPublic")); @@ -148,7 +152,7 @@ void LLPanelGeneral::apply() } } - gSavedSettings.setBOOL("LoginLastLocation", childGetValue("default_start_location").asString() == "MyLastLocation"); + gSavedSettings.setString("LoginLocation", childGetValue("default_start_location").asString()); gSavedSettings.setBOOL("ShowStartLocation", childGetValue("show_location_checkbox")); gSavedSettings.setBOOL("RenderHideGroupTitleAll", childGetValue("show_all_title_checkbox")); gSavedSettings.setBOOL("LanguageIsPublic", childGetValue("language_is_public")); @@ -165,8 +169,6 @@ void LLPanelGeneral::apply() gSavedSettings.setBOOL("UIAutoScale", childGetValue("ui_auto_scale")); gSavedSettings.setString("Language", childGetValue("language_combobox")); - LLURLSimString::setString(childGetValue("location_combobox")); - LLComboBox* crash_behavior_combobox = getChild("crash_behavior_combobox"); gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior_combobox->getCurrentIndex()); } diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index ad8b2cd40..816ba6fba 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -119,9 +119,7 @@ void LLPanelGroupTab::handleClickHelp() LLSD args; args["MESSAGE"] = help_text; LLFloater* parent_floater = gFloaterView->getParentFloater(this); - LLNotification::Params params(parent_floater->contextualNotification("GenericAlert")); - params.substitutions(args); - LLNotifications::instance().add(params); + parent_floater->addContextualNotification("GenericAlert",args); } } diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h index cc063c8bb..f9ea38136 100644 --- a/indra/newview/llpanelgroup.h +++ b/indra/newview/llpanelgroup.h @@ -38,7 +38,7 @@ class LLOfferInfo; -const F32 UPDATE_MEMBERS_SECONDS_PER_FRAME = 0.005; // 5ms +const F32 UPDATE_MEMBERS_SECONDS_PER_FRAME = 0.005f; // 5ms // Forward declares class LLPanelGroupTab; diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index e524a280b..31482d15e 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -143,14 +143,7 @@ BOOL LLPanelGroupGeneral::postBuild() mBtnInfo->setClickedCallback(boost::bind(LLGroupActions::show, mGroupID)); } - LLTextBox* founder = getChild("founder_name"); - if (founder) - { - mFounderName = new LLNameBox(founder->getName(),founder->getRect(),LLUUID::null,FALSE,founder->getFont(),founder->getMouseOpaque()); - removeChild(founder); - delete founder; - addChild(mFounderName); - } + mFounderName = getChild("founder_name"); mListVisibleMembers = getChild("visible_members", recurse); if (mListVisibleMembers) diff --git a/indra/newview/llpanellandmedia.cpp b/indra/newview/llpanellandmedia.cpp index 152d1786f..a515eadc7 100644 --- a/indra/newview/llpanellandmedia.cpp +++ b/indra/newview/llpanellandmedia.cpp @@ -91,6 +91,7 @@ BOOL LLPanelLandMedia::postBuild() mMediaTextureCtrl->setCommitCallback( onCommitAny, this ); mMediaTextureCtrl->setAllowNoTexture ( TRUE ); mMediaTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); + mMediaTextureCtrl->setDnDFilterPermMask(PERM_COPY | PERM_TRANSFER); mMediaTextureCtrl->setNonImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER); mMediaAutoScaleCheck = getChild("media_auto_scale"); @@ -156,7 +157,7 @@ void LLPanelLandMedia::refresh() std::string mime_type = parcel->getMediaType(); if (mime_type.empty()) { - mime_type = "none/none"; + mime_type = LLMIMETypes::getDefaultMimeType(); } setMediaType(mime_type); mMediaTypeCombo->setEnabled( can_change_media ); @@ -202,17 +203,17 @@ void LLPanelLandMedia::refresh() mSetURLButton->setEnabled( can_change_media ); mResetURLButton->setEnabled( can_change_media ); - LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mURLEntryFloater.get(); + /*LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mURLEntryFloater.get(); if (floater_url_entry) { floater_url_entry->updateFromLandMediaPanel(); - } + }*/ } } void LLPanelLandMedia::populateMIMECombo() { - std::string default_mime_type = "none/none"; + std::string default_mime_type = LLMIMETypes::getDefaultMimeType(); std::string default_label; LLMIMETypes::mime_widget_set_map_t::const_iterator it; for (it = LLMIMETypes::sWidgetMap.begin(); it != LLMIMETypes::sWidgetMap.end(); ++it) @@ -326,7 +327,7 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl*, void *userdata) void LLPanelLandMedia::onSetBtn(void *userdata) { LLPanelLandMedia *self = (LLPanelLandMedia *)userdata; - self->mURLEntryFloater = LLFloaterURLEntry::show( self->getHandle() ); + self->mURLEntryFloater = LLFloaterURLEntry::show( self->getHandle(), self->getMediaURL() ); LLFloater* parent_floater = gFloaterView->getParentFloater(self); if (parent_floater) { diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index c18a52362..0986ac024 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -45,6 +45,7 @@ #include "sgversion.h" #include "v4color.h" +#include "llappviewer.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcommandhandler.h" // for secondlife:///app/login/ @@ -61,20 +62,17 @@ #include "llui.h" #include "lluiconstants.h" #include "llurlhistory.h" // OGPX : regionuri text box has a history of region uris (if FN/LN are loaded at startup) -#include "llurlsimstring.h" #include "llviewerbuild.h" #include "llviewertexturelist.h" #include "llviewermenu.h" // for handle_preferences() #include "llviewernetwork.h" #include "llviewerwindow.h" // to link into child list #include "llnotify.h" -#include "llurlsimstring.h" #include "lluictrlfactory.h" #include "llhttpclient.h" #include "llweb.h" #include "llmediactrl.h" -#include "llfloatermediabrowser.h" #include "llfloatertos.h" #include "llglheaders.h" @@ -84,7 +82,6 @@ // [/RLVa:KB] // -#include "llappviewer.h" #include "llspinctrl.h" #include "llviewermessage.h" #include @@ -93,8 +90,6 @@ #include "llstring.h" #include -#define USE_VIEWER_AUTH 0 - class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy iamHereLogin_timeout; @@ -113,7 +108,7 @@ static bool nameSplit(const std::string& full, std::string& first, std::string& first = fragments[0]; if (fragments.size() == 1) { - if (gHippoGridManager->getConnectedGrid()->isAurora()) + if (gHippoGridManager->getCurrentGrid()->isAurora()) last = ""; else last = "Resident"; @@ -135,7 +130,8 @@ static std::string nameJoin(const std::string& first,const std::string& last, bo } static std::string getDisplayString(const std::string& first, const std::string& last, const std::string& grid, bool is_secondlife) { - if(grid == gHippoGridManager->getDefaultGridNick()) + //grid comes via LLSavedLoginEntry, which uses full grid names, not nicks + if(grid == gHippoGridManager->getDefaultGridName()) return nameJoin(first, last, is_secondlife); else return nameJoin(first, last, is_secondlife) + " (" + grid + ")"; @@ -149,7 +145,7 @@ class LLLoginRefreshHandler : public LLCommandHandler { public: // don't allow from external browsers - LLLoginRefreshHandler() : LLCommandHandler("login_refresh", true) { } + LLLoginRefreshHandler() : LLCommandHandler("login_refresh", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) @@ -188,6 +184,8 @@ class LLIamHereLogin : public LLHTTPClient::ResponderHeadersOnly { if (mParent) { + if(200 <= status && status < 300) + llinfos << "Found site" << llendl; mParent->setSiteIsAlive(200 <= status && status < 300); } } @@ -202,11 +200,6 @@ namespace { boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0; }; -void set_start_location(const LLSD& value) -{ - LLURLSimString::setString(value.asString()); -} - //--------------------------------------------------------------------------- // Public methods //--------------------------------------------------------------------------- @@ -216,8 +209,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, : LLPanel(std::string("panel_login"), LLRect(0,600,800,0), FALSE), // not bordered mLogoImage(), mCallback(callback), - mCallbackData(cb_data), - mHtmlAvailable( TRUE ) + mCallbackData(cb_data) { setFocusRoot(TRUE); @@ -244,13 +236,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLUICtrlFactory::getInstance()->buildPanel(this, "panel_login.xml"); -#if USE_VIEWER_AUTH - //leave room for the login menu bar - setRect(LLRect(0, rect.getHeight()-18, rect.getWidth(), 0)); -#endif reshape(rect.getWidth(), rect.getHeight()); -#if !USE_VIEWER_AUTH LLComboBox* name_combo = sInstance->getChild("name_combo"); name_combo->setCommitCallback(boost::bind(LLPanelLogin::onSelectLoginEntry, _1, this)); name_combo->setFocusLostCallback(boost::bind(&LLPanelLogin::onLoginComboLostFocus, this, name_combo)); @@ -270,72 +257,48 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, sendChildToBack(getChildView("channel_text")); sendChildToBack(getChildView("forgot_password_text")); - //OGPX : This keeps the uris in a history file - //OGPX TODO: should this be inside an OGP only check? - LLComboBox* regioncombo = getChild("regionuri_edit"); - regioncombo->setAllowTextEntry(TRUE, 256, FALSE); - std::string current_regionuri = gSavedSettings.getString("CmdLineRegionURI"); - - // iterate on uri list adding to combobox (couldn't figure out how to add them all in one call) - // ... and also append the command line value we might have gotten to the URLHistory - LLSD regionuri_history = LLURLHistory::getURLHistory("regionuri"); - LLSD::array_iterator iter_history = regionuri_history.beginArray(); - LLSD::array_iterator iter_end = regionuri_history.endArray(); - for (; iter_history != iter_end; ++iter_history) - { - regioncombo->addSimpleElement((*iter_history).asString()); - } - - if ( LLURLHistory::appendToURLCollection("regionuri",current_regionuri)) - { - // since we are in login, another read of urlhistory file is going to happen - // so we need to persist the new value we just added (or maybe we should do it in startup.cpp?) - - // since URL history only populated on create of sInstance, add to combo list directly - regioncombo->addSimpleElement(current_regionuri); - } - - // select which is displayed if we have a current URL. - regioncombo->setSelectedByValue(LLSD(current_regionuri),TRUE); - //llinfos << " url history: " << LLSDOStreamer(LLURLHistory::getURLHistory("regionuri")) << llendl; - LLComboBox* combo = getChild("start_location_combo"); - combo->setAllowTextEntry(TRUE, 128, FALSE); + LLComboBox* location_combo = getChild("start_location_combo"); + updateLocationSelectorsVisibility(); // separate so that it can be called from preferences + location_combo->setAllowTextEntry(TRUE, 128, FALSE); + location_combo->setFocusLostCallback( boost::bind(&LLPanelLogin::onLocationSLURL, this) ); + + LLComboBox *server_choice_combo = getChild("grids_combo"); + server_choice_combo->setCommitCallback(boost::bind(&LLPanelLogin::onSelectGrid, _1)); + + // Load all of the grids, sorted, and then add a bar and the current grid at the top + updateGridCombo(); - // The XML file loads the combo with the following labels: - // 0 - "My Home" - // 1 - "My Last Location" - // 2 - "" - - BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation"); - std::string sim_string = LLURLSimString::sInstance.mSimString; - if (!sim_string.empty()) + LLSLURL start_slurl(LLStartUp::getStartSLURL()); + if ( !start_slurl.isSpatial() ) // has a start been established by the command line or NextLoginLocation ? { - // Replace "" with this region name - combo->remove(2); - combo->add( sim_string ); - combo->setTextEntry(sim_string); - combo->setCurrentByIndex( 2 ); - } - else if (login_last) - { - combo->setCurrentByIndex( 1 ); + // no, so get the preference setting + std::string defaultStartLocation = gSavedSettings.getString("LoginLocation"); + LL_INFOS("AppInit")<<"default LoginLocation '"<setCurrentByIndex( 0 ); + LLPanelLogin::onUpdateStartSLURL(start_slurl); // updates grid if needed } - combo->setCommitCallback(boost::bind(set_start_location, _2)); - childSetAction("connect_btn", onClickConnect, this); setDefaultBtn("connect_btn"); - // childSetAction("quit_btn", onClickQuit, this); childSetAction("grids_btn", onClickGrids, this); - childSetCommitCallback("grids_combo", onSelectGrid, this); std::string channel = gVersionChannel; @@ -355,38 +318,17 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLTextBox* create_new_account_text = getChild("create_new_account_text"); create_new_account_text->setClickedCallback(boost::bind(&onClickNewAccount)); -#endif - + // get the web browser control LLMediaCtrl* web_browser = getChild("login_html"); web_browser->addObserver(this); - - // Need to handle login secondlife:///app/ URLs - web_browser->setTrusted( true ); - - // don't make it a tab stop until SL-27594 is fixed - web_browser->setTabStop(FALSE); - // web_browser->navigateToLocalPage( "loading", "loading.html" ); - - // make links open in external browser - web_browser->setOpenInExternalBrowser( true ); + web_browser->setBackgroundColor(LLColor4::black); reshapeBrowser(); - updateGridCombo(); - - childSetVisible("create_new_account_text", - !gHippoGridManager->getConnectedGrid()->getRegisterUrl().empty()); - childSetVisible("forgot_password_text", - !gHippoGridManager->getConnectedGrid()->getPasswordUrl().empty()); - loadLoginPage(); -#if !USE_VIEWER_AUTH - // Initialize visibility (and don't force visibility - use prefs) - refreshLocation( false ); -#endif - + refreshLoginPage(); } void LLPanelLogin::setSiteIsAlive( bool alive ) @@ -400,33 +342,17 @@ void LLPanelLogin::setSiteIsAlive( bool alive ) loadLoginPage(); web_browser->setVisible(true); - - // mark as available - mHtmlAvailable = TRUE; } } else // the site is not available (missing page, server down, other badness) { -#if !USE_VIEWER_AUTH if ( web_browser ) { // hide browser control (revealing default one) web_browser->setVisible( FALSE ); - - // mark as unavailable - mHtmlAvailable = FALSE; + web_browser->navigateTo( "data:text/html,%3Chtml%3E%3Cbody%20bgcolor=%22#000000%22%3E%3C/body%3E%3C/html%3E", "text/html" ); } -#else - - if ( web_browser ) - { - web_browser->navigateToLocalPage( "loading-error" , "index.html" ); - - // mark as available - mHtmlAvailable = TRUE; - } -#endif } } @@ -454,13 +380,8 @@ void LLPanelLogin::reshapeBrowser() LLRect rect = gViewerWindow->getWindowRectScaled(); LLRect html_rect; html_rect.setCenterAndSize( -#if USE_VIEWER_AUTH - rect.getCenterX() - 2, rect.getCenterY(), - rect.getWidth() + 6, rect.getHeight()); -#else - rect.getCenterX() - 2, rect.getCenterY() + 40, - rect.getWidth() + 6, rect.getHeight() - 78 ); -#endif + rect.getCenterX() /*- 2*/, rect.getCenterY() + 40, + rect.getWidth() /*+ 6*/, rect.getHeight() - 78 ); web_browser->setRect( html_rect ); web_browser->reshape( html_rect.getWidth(), html_rect.getHeight(), TRUE ); reshape( rect.getWidth(), rect.getHeight(), 1 ); @@ -474,9 +395,6 @@ LLPanelLogin::~LLPanelLogin() if ( gResponsePtr ) gResponsePtr->setParent( 0 ); - //// We know we're done with the image, so be rid of it. - //gTextureList.deleteImage( mLogoImage ); - if ( gFocusMgr.getDefaultKeyboardFocus() == this ) { gFocusMgr.setDefaultKeyboardFocus(NULL); @@ -518,14 +436,13 @@ void LLPanelLogin::draw() S32 width = getRect().getWidth(); S32 height = getRect().getHeight(); - if ( mHtmlAvailable ) + if ( getChild("login_html")->getVisible()) { -#if !USE_VIEWER_AUTH // draw a background box in black - gl_rect_2d( 0, height - 264, width, 264, LLColor4( 0.0f, 0.0f, 0.0f, 1.f ) ); - // draw the bottom part of the background image - just the blue background to the native client UI + gl_rect_2d( 0, height - 264, width, 264, LLColor4::black ); + // draw the bottom part of the background image + // just the blue background to the native client UI mLogoImage->draw(0, -264, width + 8, mLogoImage->getHeight()); -#endif } else { @@ -560,12 +477,13 @@ BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask) return TRUE; } - if ( KEY_F1 == key ) + //Singu TODO: Re-implement f1 help. + /*if ( KEY_F1 == key ) { llinfos << "Spawning HTML help window" << llendl; gViewerHtmlHelp.show(); return TRUE; - } + }*/ # if !LL_RELEASE_FOR_DOWNLOAD if ( KEY_F2 == key ) @@ -605,12 +523,6 @@ void LLPanelLogin::setFocus(BOOL b) // static void LLPanelLogin::giveFocus() { -#if USE_VIEWER_AUTH - if (sInstance) - { - sInstance->setFocus(TRUE); - } -#else if( sInstance ) { // Grab focus and move cursor to first blank input field @@ -621,12 +533,9 @@ void LLPanelLogin::giveFocus() BOOL have_pass = !pass.empty(); LLLineEditor* edit = NULL; - LLUICtrl* combo = NULL; - if (have_username) + LLComboBox* combo = NULL; + if (have_username && !have_pass) { - if(have_pass) - combo = sInstance->getChild("connect_btn"); - else // User saved his name but not his password. Move // focus to password field. edit = sInstance->getChild("password_edit"); @@ -647,7 +556,6 @@ void LLPanelLogin::giveFocus() combo->setFocus(TRUE); } } -#endif } @@ -726,7 +634,9 @@ void LLPanelLogin::setFields(const LLSavedLoginEntry& entry, bool takeFocus) //sInstance->childSetText("name_combo", fullname); std::string grid = entry.getGrid(); - if(!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridNick()) { + //grid comes via LLSavedLoginEntry, which uses full grid names, not nicks + if(!grid.empty() && gHippoGridManager->getGrid(grid) && grid != gHippoGridManager->getCurrentGridName()) + { gHippoGridManager->setCurrentGrid(grid); LLPanelLogin::refreshLoginPage(); } @@ -770,7 +680,7 @@ void LLPanelLogin::getFields(std::string *firstname, } // static -void LLPanelLogin::getLocation(std::string &location) +/*void LLPanelLogin::getLocation(std::string &location) { if (!sInstance) { @@ -780,33 +690,14 @@ void LLPanelLogin::getLocation(std::string &location) LLComboBox* combo = sInstance->getChild("start_location_combo"); location = combo->getValue().asString(); -} +}*/ // static -void LLPanelLogin::refreshLocation( bool force_visible ) +void LLPanelLogin::updateLocationSelectorsVisibility() { - if (!sInstance) return; - -#if USE_VIEWER_AUTH - loadLoginPage(); -#else - LLComboBox* combo = sInstance->getChild("start_location_combo"); - - if (LLURLSimString::parse()) + if (sInstance) { - combo->setCurrentByIndex( 3 ); // BUG? Maybe 2? - combo->setTextEntry(LLURLSimString::sInstance.mSimString); - } - else - { - BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation"); - combo->setCurrentByIndex( login_last ? 1 : 0 ); - } - - BOOL show_start = TRUE; - - if ( ! force_visible ) - show_start = gSavedSettings.getBOOL("ShowStartLocation"); + BOOL show_start = gSavedSettings.getBOOL("ShowStartLocation"); // [RLVa:KB] - Alternate: Snowglobe-1.2.4 | Checked: 2009-07-08 (RLVa-1.0.0e) // TODO-RLVa: figure out some way to make this work with RLV_EXTENSION_STARTLOCATION @@ -818,11 +709,61 @@ void LLPanelLogin::refreshLocation( bool force_visible ) #endif // RLV_EXTENSION_STARTLOCATION // [/RLVa:KB] - sInstance->childSetVisible("start_location_combo", show_start); // maintain ShowStartLocation if legacy - sInstance->childSetVisible("start_location_text", show_start); - sInstance->childSetVisible("regionuri_edit",FALSE); // Do Not show regionuri box if legacy + sInstance->getChild("start_location_combo")->setVisible(show_start); // maintain ShowStartLocation if legacy + sInstance->getChild("start_location_text")->setVisible(show_start); + + bool show_server = true; + sInstance->getChild("grids_combo")->setVisible(show_server); + sInstance->getChild("grids_text")->setVisible(show_server); + sInstance->getChild("grids_btn")->setVisible(show_server); + } + +} -#endif +// static +void LLPanelLogin::onUpdateStartSLURL(const LLSLURL& new_start_slurl) +{ + if (!sInstance) return; + + LL_DEBUGS("AppInit")<getChild("start_location_combo"); + /* + * Determine whether or not the new_start_slurl modifies the grid. + * + * Note that some forms that could be in the slurl are grid-agnostic., + * such as "home". Other forms, such as + * https://grid.example.com/region/Party%20Town/20/30/5 + * specify a particular grid; in those cases we want to change the grid + * and the grid selector to match the new value. + */ + enum LLSLURL::SLURL_TYPE new_slurl_type = new_start_slurl.getType(); + switch ( new_slurl_type ) + { + case LLSLURL::LOCATION: + { + location_combo->setCurrentByIndex( 2 ); + location_combo->setTextEntry(new_start_slurl.getLocationString()); + } + case LLSLURL::HOME_LOCATION: + location_combo->setCurrentByIndex( 0 ); // home location + break; + case LLSLURL::LAST_LOCATION: + location_combo->setCurrentByIndex( 1 ); // last location + break; + default: + LL_WARNS("AppInit")<<"invalid login slurl, using home"<setCurrentByIndex(1); // home location + break; + } + + updateLocationSelectorsVisibility(); +} + +void LLPanelLogin::setLocation(const LLSLURL& slurl) +{ + LL_DEBUGS("AppInit")<<"setting Location "<getRootView()->removeChild( LLPanelLogin::sInstance ); - - gFocusMgr.setDefaultKeyboardFocus(NULL); + LLPanelLogin::sInstance->getParent()->removeChild( LLPanelLogin::sInstance ); delete sInstance; sInstance = NULL; @@ -855,103 +794,79 @@ void LLPanelLogin::setAlwaysRefresh(bool refresh) void LLPanelLogin::updateGridCombo() { - const std::string &defaultGrid = gHippoGridManager->getDefaultGridNick(); - const std::string ¤tGrid = gHippoGridManager->getCurrentGridNick(); + const std::string &defaultGrid = gHippoGridManager->getDefaultGridName(); + LLComboBox *grids = getChild("grids_combo"); - S32 selectIndex = -1, i = 0; + std::string top_entry; + grids->removeall(); - if (defaultGrid != "") { - grids->add(defaultGrid); - selectIndex = i++; - } + + const HippoGridInfo *curGrid = gHippoGridManager->getCurrentGrid(); + const HippoGridInfo *defGrid = gHippoGridManager->getGrid(defaultGrid); + HippoGridManager::GridIterator it, end = gHippoGridManager->endGrid(); - for (it = gHippoGridManager->beginGrid(); it != end; ++it) { + for (it = gHippoGridManager->beginGrid(); it != end; ++it) + { std::string grid = it->second->getGridName(); - if (grid != defaultGrid) { - grids->add(grid); - if (grid == currentGrid) selectIndex = i; - i++; - } + if(grid.empty() || it->second == defGrid || it->second == curGrid) + continue; + grids->add(grid); } - if (selectIndex >= 0) { - grids->setCurrentByIndex(selectIndex); - } else { + if(curGrid || defGrid) + { + if(defGrid) + grids->add(defGrid->getGridName(),ADD_TOP); + if(curGrid && defGrid != curGrid) + grids->add(curGrid->getGridName(),ADD_TOP); + grids->setCurrentByIndex(0); + } + else + { grids->setLabel(LLStringExplicit("")); // LLComboBox::removeall() does not clear the label } } -// static -void LLPanelLogin::refreshLoginPage() -{ - if (!sInstance || (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP)) - return; - - sInstance->updateGridCombo(); - - sInstance->childSetVisible("create_new_account_text", - !gHippoGridManager->getConnectedGrid()->getRegisterUrl().empty()); - sInstance->childSetVisible("forgot_password_text", - !gHippoGridManager->getConnectedGrid()->getPasswordUrl().empty()); - - // kick off a request to grab the url manually - gResponsePtr = LLIamHereLogin::build(sInstance); - - std::string login_page = gHippoGridManager->getConnectedGrid()->getLoginPage(); - if (!login_page.empty()) { - LLHTTPClient::head(login_page, gResponsePtr.get()); - } else { - sInstance->setSiteIsAlive(false); - } -} - void LLPanelLogin::loadLoginPage() { if (!sInstance) return; - sInstance->updateGridCombo(); - std::ostringstream login_uri; + sInstance->updateGridCombo(); - std::string login_page = gHippoGridManager->getConnectedGrid()->getLoginPage(); - if (login_page.empty()) + std::string login_page_str = gHippoGridManager->getCurrentGrid()->getLoginPage(); + if (login_page_str.empty()) { sInstance->setSiteIsAlive(false); return; } - - login_uri << login_page; - + // Use the right delimeter depending on how LLURI parses the URL - LLURI login_page_uri = LLURI(login_page); - std::string first_query_delimiter = "&"; - if (login_page_uri.queryMap().size() == 0) + LLURI login_page = LLURI(login_page_str); + LLSD params(login_page.queryMap()); + + LL_DEBUGS("AppInit") << "login_page: " << login_page << LL_ENDL; + + // Language + params["lang"] = LLUI::getLanguage(); + + // First Login? + if (gSavedSettings.getBOOL("FirstLoginThisInstall")) { - first_query_delimiter = "?"; - } - - // Language - std::string language = LLUI::getLanguage(); - login_uri << first_query_delimiter<<"lang=" << language; - - // First Login? - if (gSavedSettings.getBOOL("FirstLoginThisInstall")) + params["firstlogin"] = "TRUE"; // not bool: server expects string TRUE + } + + if(login_page_str.find("secondlife.com") == -1) { - login_uri << "&firstlogin=TRUE"; - } - - std::string version = llformat("%d.%d.%d (%d)", - gVersionMajor, gVersionMinor, gVersionPatch, gVersionBuild); - - if(login_page.find("secondlife.com") == -1) { - login_uri << "&channel=" << LLWeb::curlEscape(gVersionChannel); - login_uri << "&version=" << LLWeb::curlEscape(version); + params["version"]= llformat("%d.%d.%d (%d)", + gVersionMajor, gVersionMinor, gVersionPatch, gVersionBuild); + params["channel"] = gVersionChannel; } // Grid - if (gHippoGridManager->getConnectedGrid()->isSecondLife()) { + if (gHippoGridManager->getCurrentGrid()->isSecondLife()) { // find second life grid from login URI // yes, this is heuristic, but hey, it is just to get the right login page... - std::string tmp = gHippoGridManager->getConnectedGrid()->getLoginUri(); + std::string tmp = gHippoGridManager->getCurrentGrid()->getLoginUri(); int i = tmp.find(".lindenlab.com"); if (i != std::string::npos) { tmp = tmp.substr(0, i); @@ -960,109 +875,35 @@ void LLPanelLogin::loadLoginPage() i = tmp.rfind('/'); if (i != std::string::npos) { tmp = tmp.substr(i+1); - login_uri << "&grid=" << LLWeb::curlEscape(tmp); + params["grid"] = tmp; } } } - else if (gHippoGridManager->getConnectedGrid()->isOpenSimulator()){ - login_uri << "&grid=" << gHippoGridManager->getConnectedGrid()->getGridNick(); - } - else if (gHippoGridManager->getConnectedGrid()->getPlatform() == HippoGridInfo::PLATFORM_AURORA) + else if (gHippoGridManager->getCurrentGrid()->isOpenSimulator()) { - login_uri << "&grid=" << LLWeb::curlEscape(LLViewerLogin::getInstance()->getGridLabel()); + params["grid"] = gHippoGridManager->getCurrentGrid()->getGridNick(); } + else if (gHippoGridManager->getCurrentGrid()->getPlatform() == HippoGridInfo::PLATFORM_AURORA) + { + params["grid"] = LLViewerLogin::getInstance()->getGridLabel(); + } + + // add OS info + params["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); + // Make an LLURI with this augmented info + LLURI login_uri(LLURI::buildHTTP(login_page.authority(), + login_page.path(), + params)); gViewerWindow->setMenuBackgroundColor(false, !LLViewerLogin::getInstance()->isInProductionGrid()); gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); - -#if USE_VIEWER_AUTH - LLURLSimString::sInstance.parse(); - - std::string location; - std::string region; - std::string password; - - if (LLURLSimString::parse()) - { - std::ostringstream oRegionStr; - location = "specify"; - oRegionStr << LLURLSimString::sInstance.mSimName << "/" << LLURLSimString::sInstance.mX << "/" - << LLURLSimString::sInstance.mY << "/" - << LLURLSimString::sInstance.mZ; - region = oRegionStr.str(); - } - else - { - if (gSavedSettings.getBOOL("LoginLastLocation")) - { - location = "last"; - } - else - { - location = "home"; - } - } - - std::string firstname, lastname; - - if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) - { - LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); - firstname = cmd_line_login[0].asString(); - lastname = cmd_line_login[1].asString(); - password = cmd_line_login[2].asString(); - } - - if (firstname.empty()) - { - firstname = gSavedSettings.getString("FirstName"); - } - - if (lastname.empty()) - { - lastname = gSavedSettings.getString("LastName"); - } - - std::string curl_region = LLWeb::curlEscape(region); - - login_uri <<"firstname=" << firstname << - "&lastname=" << lastname << "&location=" << location << "®ion=" << curl_region; - - if (!password.empty()) - { - login_uri << "&password=" << password; - } - else if (!(password = load_password_from_disk()).empty()) - { - login_uri << "&password=$1$" << password; - } - if (gAutoLogin) - { - login_uri << "&auto_login=TRUE"; - } - if (gSavedSettings.getBOOL("ShowStartLocation")) - { - login_uri << "&show_start_location=TRUE"; - } - if (gSavedSettings.getBOOL("RememberPassword")) - { - login_uri << "&remember_password=TRUE"; - } - BOOL show_server = sInstance ? sInstance->mShowServerCombo : FALSE; - if (show_server || gSavedSettings.getBOOL("ForceShowGrid")) - { - login_uri << "&show_grid=TRUE"; - } -#endif - LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - - if (web_browser->getCurrentNavUrl() != login_uri.str()) + if (web_browser->getCurrentNavUrl() != login_uri.asString()) { LL_DEBUGS("AppInit") << "loading: " << login_uri << LL_ENDL; - web_browser->navigateTo( login_uri.str(), "text/html" ); + web_browser->navigateTo( login_uri.asString(), "text/html" ); } } @@ -1116,7 +957,7 @@ void LLPanelLogin::onClickConnect(void *) } else { - if (gHippoGridManager->getConnectedGrid()->getRegisterUrl().empty()) { + if (gHippoGridManager->getCurrentGrid()->getRegisterUrl().empty()) { LLNotificationsUtil::add("MustHaveAccountToLogInNoLinks"); } else { LLNotificationsUtil::add("MustHaveAccountToLogIn", LLSD(), LLSD(), @@ -1147,7 +988,7 @@ bool LLPanelLogin::newAccountAlertCallback(const LLSD& notification, const LLSD& // static void LLPanelLogin::onClickNewAccount() { - const std::string &url = gHippoGridManager->getConnectedGrid()->getRegisterUrl(); + const std::string &url = gHippoGridManager->getCurrentGrid()->getRegisterUrl(); if (!url.empty()) { llinfos << "Going to account creation URL." << llendl; LLWeb::loadURLExternal(url); @@ -1165,29 +1006,6 @@ void LLPanelLogin::onClickGrids(void*) LLFloaterPreference::switchTab(LLPreferenceCore::TAB_GRIDS); } -// static -void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl, void*) -{ - gHippoGridManager->setCurrentGrid(ctrl->getValue()); - LLPanelLogin::refreshLoginPage(); -} - -// *NOTE: This function is dead as of 2008 August. I left it here in case -// we suddenly decide to put the Quit button back. JC -// static -void LLPanelLogin::onClickQuit(void*) -{ - if (sInstance && sInstance->mCallback) - { - // tell the responder we're not here anymore - if ( gResponsePtr ) - gResponsePtr->setParent( 0 ); - - sInstance->mCallback(1, sInstance->mCallbackData); - } -} - - // static void LLPanelLogin::onClickVersion(void*) { @@ -1199,7 +1017,7 @@ void LLPanelLogin::onClickForgotPassword() { if (sInstance ) { - const std::string &url = gHippoGridManager->getConnectedGrid()->getPasswordUrl(); + const std::string &url = gHippoGridManager->getCurrentGrid()->getPasswordUrl(); if (!url.empty()) { LLWeb::loadURLExternal(url); } else { @@ -1218,6 +1036,63 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller) } } +// static +//void LLPanelLogin::updateServer() +void LLPanelLogin::refreshLoginPage() +{ + if (!sInstance || (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP)) + return; + + sInstance->updateGridCombo(); + + sInstance->childSetVisible("create_new_account_text", + !gHippoGridManager->getCurrentGrid()->getRegisterUrl().empty()); + sInstance->childSetVisible("forgot_password_text", + !gHippoGridManager->getCurrentGrid()->getPasswordUrl().empty()); + + std::string login_page = gHippoGridManager->getCurrentGrid()->getLoginPage(); + if (!login_page.empty()) + { + LLMediaCtrl* web_browser = sInstance->getChild("login_html"); + if (web_browser->getCurrentNavUrl() != login_page) + { + if(gResponsePtr) + gResponsePtr->setParent(0); //Tell our previous responder that we no longer require its result. + gResponsePtr.reset(); //Deref previous responder + + llinfos << "Firing off lookup for " << login_page << llendl; + // kick off a request to grab the url manually + gResponsePtr = LLIamHereLogin::build(sInstance); + LLHTTPClient::head(login_page, gResponsePtr.get()); + } + } + else + { + if(gResponsePtr) + gResponsePtr->setParent(0); //Tell our previous responder that we no longer require its result. + gResponsePtr.reset(); //Deref previous responder + sInstance->setSiteIsAlive(false); + } +} + +// static +//void LLPanelLogin::onSelectServer() +void LLPanelLogin::onSelectGrid(LLUICtrl *ctrl) +{ + gHippoGridManager->setCurrentGrid(ctrl->getValue()); + LLPanelLogin::refreshLoginPage(); +} + +void LLPanelLogin::onLocationSLURL() +{ + LLComboBox* location_combo = getChild("start_location_combo"); + std::string location = location_combo->getValue().asString(); + LL_DEBUGS("AppInit")< #include "llmediactrl.h" // LLMediaCtrlObserver #include "llsavedlogins.h" +#include "llslurl.h" class LLUIImage; class LLComboBox; @@ -78,14 +79,12 @@ public: */ static void setFields(const LLSavedLoginEntry& entry, bool takeFocus = false); - //static void addServer(const std::string& server, S32 domain_name); - static void refreshLocation( bool force_visible ); + static void getFields(std::string *firstname, std::string *lastname, std::string *password); - static void getFields(std::string *firstname, std::string *lastname, - std::string *password); - - //static BOOL isGridComboDirty(); - static void getLocation(std::string &location); + static void setLocation(const LLSLURL& slurl); + + /// Call when preferences that control visibility may have changed + static void updateLocationSelectorsVisibility(); static void close(); @@ -102,19 +101,21 @@ public: // inherited from LLViewerMediaObserver /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + /// to be called from LLStartUp::setStartSLURL + static void onUpdateStartSLURL(const LLSLURL& new_start_slurl); + private: void reshapeBrowser(); + void onLocationSLURL(); + static void onClickConnect(void*); static void onClickNewAccount(); static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response); static void onClickGrids(void*); - static void onSelectGrid(LLUICtrl *ctrl, void*); - static void onClickQuit(void*); + static void onSelectGrid(LLUICtrl *ctrl); static void onClickVersion(void*); static void onClickForgotPassword(); static void onPassKey(LLLineEditor* caller); - //static void onSelectServer(LLUICtrl*, void*); - //static void onServerComboLostFocus(LLFocusableElement*, void*); static void onSelectLoginEntry(LLUICtrl*, void*); void onLoginComboLostFocus(LLComboBox* combo_box); static void onNameCheckChanged(LLUICtrl* ctrl, void* data); @@ -154,7 +155,6 @@ private: static LLPanelLogin* sInstance; static BOOL sCapslockDidNotification; - BOOL mHtmlAvailable; LLSavedLogins mLoginHistoryData; }; diff --git a/indra/newview/llpanelmediahud.cpp b/indra/newview/llpanelmediahud.cpp deleted file mode 100644 index ad47eb732..000000000 --- a/indra/newview/llpanelmediahud.cpp +++ /dev/null @@ -1,664 +0,0 @@ -/** - * @file llpanelmsgs.cpp - * @brief Message popup preferences panel - * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-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" - -//LLPanelMediaHUD -#include "llagent.h" -#include "llagentcamera.h" -#include "llparcel.h" -#include "llpanel.h" -#include "llselectmgr.h" -#include "llrender.h" -#include "lldrawable.h" -#include "llviewerwindow.h" -#include "llwindow.h" -#include "lluictrlfactory.h" -#include "llbutton.h" -#include "llface.h" -#include "llhudview.h" -#include "lliconctrl.h" -#include "lltoolpie.h" -#include "llviewercamera.h" -#include "llpanelmediahud.h" -#include "llpluginclassmedia.h" -#include "llviewercontrol.h" -#include "llviewerparcelmgr.h" -#include "llviewermedia.h" -#include "llviewermediafocus.h" -#include "llvovolume.h" -#include "llweb.h" - -glh::matrix4f glh_get_current_modelview(); -glh::matrix4f glh_get_current_projection(); - -const F32 ZOOM_NEAR_PADDING = 1.0f; -const F32 ZOOM_MEDIUM_PADDING = 1.2f; -const F32 ZOOM_FAR_PADDING = 1.5f; - -// -// LLPanelMediaHUD -// - -LLPanelMediaHUD::LLPanelMediaHUD(viewer_media_t media_impl) - : mMediaImpl(media_impl) -{ - mMediaFocus = false; - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_hud.xml"); - mMouseMoveTimer.reset(); - mFadeTimer.stop(); - mCurrentZoom = ZOOM_NONE; - mScrollState = SCROLL_NONE; -} -LLPanelMediaHUD::~LLPanelMediaHUD() -{ - mMediaImpl = NULL; -} - -BOOL LLPanelMediaHUD::postBuild() -{ - LLButton* close_btn = getChild("close"); - close_btn->setClickedCallback(onClickClose, this); - - LLButton* back_btn = getChild("back"); - back_btn->setClickedCallback(onClickBack, this); - - LLButton* fwd_btn = getChild("fwd"); - fwd_btn->setClickedCallback(onClickForward, this); - - LLButton* home_btn = getChild("home"); - home_btn->setClickedCallback(onClickHome, this); - - LLButton* stop_btn = getChild("stop"); - stop_btn->setClickedCallback(onClickStop, this); - - LLButton* media_stop_btn = getChild("media_stop"); - media_stop_btn->setClickedCallback(onClickStop, this); - - LLButton* reload_btn = getChild("reload"); - reload_btn->setClickedCallback(onClickReload, this); - - LLButton* play_btn = getChild("play"); - play_btn->setClickedCallback(onClickPlay, this); - - LLButton* pause_btn = getChild("pause"); - pause_btn->setClickedCallback(onClickPause, this); - - LLButton* open_btn = getChild("new_window"); - open_btn->setClickedCallback(onClickOpen, this); - - LLButton* zoom_btn = getChild("zoom_frame"); - zoom_btn->setClickedCallback(onClickZoom, this); - - LLButton* open_btn_h = getChild("new_window_hover"); - open_btn_h->setClickedCallback(onClickOpen, this); - - LLButton* zoom_btn_h = getChild("zoom_frame_hover"); - zoom_btn_h->setClickedCallback(onClickZoom, this); - - LLButton* scroll_up_btn = getChild("scrollup"); - scroll_up_btn->setClickedCallback(onScrollUp, this); - scroll_up_btn->setHeldDownCallback(onScrollUpHeld, this); - scroll_up_btn->setMouseUpCallback(onScrollStop, this); - LLButton* scroll_left_btn = getChild("scrollleft"); - scroll_left_btn->setClickedCallback(onScrollLeft, this); - scroll_left_btn->setHeldDownCallback(onScrollLeftHeld, this); - scroll_left_btn->setMouseUpCallback(onScrollStop, this); - LLButton* scroll_right_btn = getChild("scrollright"); - scroll_right_btn->setClickedCallback(onScrollRight, this); - scroll_right_btn->setHeldDownCallback(onScrollLeftHeld, this); - scroll_right_btn->setMouseUpCallback(onScrollStop, this); - LLButton* scroll_down_btn = getChild("scrolldown"); - scroll_down_btn->setClickedCallback(onScrollDown, this); - scroll_down_btn->setHeldDownCallback(onScrollDownHeld, this); - scroll_down_btn->setMouseUpCallback(onScrollStop, this); - - mMouseInactiveTime = gSavedSettings.getF32("MediaControlTimeout"); - mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); - - mCurrentZoom = ZOOM_NONE; - // clicks on HUD buttons do not remove keyboard focus from media - setIsChrome(TRUE); - return TRUE; -} - -void LLPanelMediaHUD::updateShape() -{ - const S32 MIN_HUD_WIDTH=200; - const S32 MIN_HUD_HEIGHT=120; - - LLPluginClassMedia* media_plugin = NULL; - if(mMediaImpl.notNull() && mMediaImpl->hasMedia()) - { - media_plugin = mMediaImpl->getMediaPlugin(); - } - - // Early out for no media plugin - if(media_plugin == NULL) - { - setVisible(FALSE); - return; - } - - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - bool can_navigate = parcel->getMediaAllowNavigate(); - - // LLObjectSelectionHandle selection = LLViewerMediaFocus::getInstance()->getSelection(); - - LLSelectNode* nodep = mMediaFocus ? LLSelectMgr::getInstance()->getSelection()->getFirstNode() : LLSelectMgr::getInstance()->getHoverNode(); - if(! nodep) - { - return; - } - setVisible(FALSE); - LLViewerObject* objectp = nodep->getObject(); - - if (objectp) - { - - // Set the state of the buttons - LLButton* back_btn = getChild("back"); - LLButton* fwd_btn = getChild("fwd"); - LLButton* reload_btn = getChild("reload"); - LLButton* play_btn = getChild("play"); - LLButton* pause_btn = getChild("pause"); - LLButton* stop_btn = getChild("stop"); - LLButton* media_stop_btn = getChild("media_stop"); - LLButton* home_btn = getChild("home"); - LLButton* close_btn = getChild("close"); - LLButton* open_btn = getChild("new_window"); - LLPanel* media_focused_panel = getChild("media_focused_controls"); - LLPanel* media_hover_panel = getChild("media_hover_controls"); - back_btn->setVisible(true); - fwd_btn->setVisible(true); - reload_btn->setVisible(true); - stop_btn->setVisible(false); - home_btn->setVisible(true); - close_btn->setVisible(true); - open_btn->setVisible(true); - - - if(mMediaFocus) - { - back_btn->setEnabled(mMediaImpl->canNavigateBack() && can_navigate); - fwd_btn->setEnabled(mMediaImpl->canNavigateForward() && can_navigate); - stop_btn->setEnabled(can_navigate); - home_btn->setEnabled(can_navigate); - LLPluginClassMediaOwner::EMediaStatus result = media_plugin->getStatus(); - - if(media_plugin->pluginSupportsMediaTime()) - { - reload_btn->setEnabled(FALSE); - reload_btn->setVisible(FALSE); - media_stop_btn->setVisible(TRUE); - home_btn->setVisible(FALSE); - back_btn->setEnabled(TRUE); - fwd_btn->setEnabled(TRUE); - switch(result) - { - case LLPluginClassMediaOwner::MEDIA_PLAYING: - play_btn->setEnabled(FALSE); - play_btn->setVisible(FALSE); - pause_btn->setEnabled(TRUE); - pause_btn->setVisible(TRUE); - media_stop_btn->setEnabled(TRUE); - break; - case LLPluginClassMediaOwner::MEDIA_PAUSED: - default: - pause_btn->setEnabled(FALSE); - pause_btn->setVisible(FALSE); - play_btn->setEnabled(TRUE); - play_btn->setVisible(TRUE); - media_stop_btn->setEnabled(FALSE); - break; - } - } - else - { - play_btn->setVisible(FALSE); - pause_btn->setVisible(FALSE); - media_stop_btn->setVisible(FALSE); - if(result == LLPluginClassMediaOwner::MEDIA_LOADING) - { - reload_btn->setEnabled(FALSE); - reload_btn->setVisible(FALSE); - stop_btn->setEnabled(TRUE); - stop_btn->setVisible(TRUE); - } - else - { - reload_btn->setEnabled(TRUE); - reload_btn->setVisible(TRUE); - stop_btn->setEnabled(FALSE); - stop_btn->setVisible(FALSE); - } - } - } - media_focused_panel->setVisible(mMediaFocus); - media_hover_panel->setVisible(!mMediaFocus); - - if(media_plugin == NULL) - // Handle Scrolling - switch (mScrollState) - { - case SCROLL_UP: - media_plugin->scrollEvent(0, -1, MASK_NONE); - break; - case SCROLL_DOWN: - media_plugin->scrollEvent(0, 1, MASK_NONE); - break; - case SCROLL_LEFT: - mMediaImpl->handleKeyHere(KEY_LEFT, MASK_NONE); - break; - case SCROLL_RIGHT: - mMediaImpl->handleKeyHere(KEY_RIGHT, MASK_NONE); - break; - case SCROLL_NONE: - default: - break; - } - LLBBox screen_bbox; - setVisible(TRUE); - glh::matrix4f mat = glh_get_current_projection()*glh_get_current_modelview(); - std::vector::iterator vert_it; - std::vector::iterator vert_end; - std::vector vect_face; - - LLVolume* volume = objectp->getVolume(); - - if (volume) - { - const LLVolumeFace& vf = volume->getVolumeFace(nodep->getLastSelectedTE()); - - const LLVector3* ext = (LLVector3*)vf.mExtents->getF32ptr(); - - LLVector3 center = (ext[0]+ext[1])*0.5f; - LLVector3 size = (ext[1]-ext[0])*0.5f; - LLVector3 vert[] = - { - center + size.scaledVec(LLVector3(1,1,1)), - center + size.scaledVec(LLVector3(-1,1,1)), - center + size.scaledVec(LLVector3(1,-1,1)), - center + size.scaledVec(LLVector3(-1,-1,1)), - center + size.scaledVec(LLVector3(1,1,-1)), - center + size.scaledVec(LLVector3(-1,1,-1)), - center + size.scaledVec(LLVector3(1,-1,-1)), - center + size.scaledVec(LLVector3(-1,-1,-1)), - }; - - LLVOVolume* vo = (LLVOVolume*) objectp; - - for (U32 i = 0; i < 8; i++) - { - vect_face.push_back(vo->volumePositionToAgent(vert[i])); - } - } - vert_it = vect_face.begin(); - vert_end = vect_face.end(); - - LLVector3 min = LLVector3(1,1,1); - LLVector3 max = LLVector3(-1,-1,-1); - for(; vert_it != vert_end; ++vert_it) - { - // project silhouette vertices into screen space - glh::vec3f screen_vert = glh::vec3f(vert_it->mV); - mat.mult_matrix_vec(screen_vert); - - // add to screenspace bounding box - update_min_max(min, max, LLVector3(screen_vert.v)); - } - - LLCoordGL screen_min; - screen_min.mX = llround((F32)gViewerWindow->getWindowWidth() * (min.mV[VX] + 1.f) * 0.5f); - screen_min.mY = llround((F32)gViewerWindow->getWindowHeight() * (min.mV[VY] + 1.f) * 0.5f); - - LLCoordGL screen_max; - screen_max.mX = llround((F32)gViewerWindow->getWindowWidth() * (max.mV[VX] + 1.f) * 0.5f); - screen_max.mY = llround((F32)gViewerWindow->getWindowHeight() * (max.mV[VY] + 1.f) * 0.5f); - - // grow panel so that screenspace bounding box fits inside "media_region" element of HUD - LLRect media_hud_rect; - getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_hud_rect); - LLView* media_region = getChild("media_region"); - media_hud_rect.mLeft -= media_region->getRect().mLeft; - media_hud_rect.mBottom -= media_region->getRect().mBottom; - media_hud_rect.mTop += getRect().getHeight() - media_region->getRect().mTop; - media_hud_rect.mRight += getRect().getWidth() - media_region->getRect().mRight; - - // keep all parts of HUD on-screen - media_hud_rect.intersectWith(getParent()->getLocalRect()); - - // If we had to clip the rect, don't display the border - childSetVisible("bg_image", false); - - // clamp to minimum size, keeping centered - media_hud_rect.setCenterAndSize(media_hud_rect.getCenterX(), media_hud_rect.getCenterY(), - llmax(MIN_HUD_WIDTH, media_hud_rect.getWidth()), llmax(MIN_HUD_HEIGHT, media_hud_rect.getHeight())); - - setShape(media_hud_rect); - - // Test mouse position to see if the cursor is stationary - LLCoordWindow cursor_pos_window; - getWindow()->getCursorPosition(&cursor_pos_window); - - // If last pos is not equal to current pos, the mouse has moved - // We need to reset the timer, and make sure the panel is visible - if(cursor_pos_window.mX != mLastCursorPos.mX || - cursor_pos_window.mY != mLastCursorPos.mY || - mScrollState != SCROLL_NONE) - { - mMouseMoveTimer.start(); - mLastCursorPos = cursor_pos_window; - } - - // Mouse has been stationary, but not for long enough to fade the UI - if(mMouseMoveTimer.getElapsedTimeF32() < mMouseInactiveTime) - { - // If we have started fading, reset the alpha values - if(mFadeTimer.getStarted()) - { - F32 alpha = 1.0f; - setAlpha(alpha); - mFadeTimer.stop(); - } - } - // If we need to start fading the UI (and we have not already started) - else if (!mFadeTimer.getStarted()) - { - mFadeTimer.start(); - } - } -} -/*virtual*/ -void LLPanelMediaHUD::draw() -{ - if(mFadeTimer.getStarted()) - { - if(mFadeTimer.getElapsedTimeF32() >= mControlFadeTime) - { - setVisible(FALSE); - } - else - { - F32 time = mFadeTimer.getElapsedTimeF32(); - F32 alpha = llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); - setAlpha(alpha); - } - } - LLPanel::draw(); -} -void LLPanelMediaHUD::setAlpha(F32 alpha) -{ - LLViewQuery query; - - LLView* query_view = mMediaFocus ? getChildView("media_focused_controls") : getChildView("media_hover_controls"); - viewList_t children = query(query_view); - for (viewList_t::iterator child_iter = children.begin(); - child_iter != children.end(); ++child_iter) - { - LLUICtrl* ctrl = dynamic_cast(*child_iter); - if (ctrl) - ctrl->setAlpha(alpha); - } - - LLPanel::setAlpha(alpha); -} -BOOL LLPanelMediaHUD::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); -} -bool LLPanelMediaHUD::isMouseOver() -{ - if( ! getVisible() ) - { - return false; - } - LLRect screen_rect; - LLCoordWindow cursor_pos_window; - getWindow()->getCursorPosition(&cursor_pos_window); - - localRectToScreen(getLocalRect(), &screen_rect); - // screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y); - - if(screen_rect.pointInRect(cursor_pos_window.mX, cursor_pos_window.mY)) - { - return true; - } - return false; -} - -//static -void LLPanelMediaHUD::onClickClose(void* user_data) -{ - LLViewerMediaFocus::getInstance()->setFocusFace(FALSE, NULL, 0, NULL); - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mCurrentZoom != ZOOM_NONE) - { - // gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - this_panel->mCurrentZoom = ZOOM_NONE; - } - this_panel->setVisible(FALSE); - -} - -//static -void LLPanelMediaHUD::onClickBack(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - if(this_panel->mMediaImpl->getMediaPlugin()->pluginSupportsMediaTime()) - { - this_panel->mMediaImpl->getMediaPlugin()->start(-2.0); - } - else - { - this_panel->mMediaImpl->getMediaPlugin()->browse_back(); - } - - } -} -//static -void LLPanelMediaHUD::onClickForward(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - if(this_panel->mMediaImpl->getMediaPlugin()->pluginSupportsMediaTime()) - { - this_panel->mMediaImpl->getMediaPlugin()->start(2.0); - } - else - { - this_panel->mMediaImpl->getMediaPlugin()->browse_forward(); - } - } -} -//static -void LLPanelMediaHUD::onClickHome(void* user_data) -{ - //LLViewerMedia::navigateHome(); - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->navigateHome(); - } -} -//static -void LLPanelMediaHUD::onClickOpen(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - LLWeb::loadURL(this_panel->mMediaImpl->getMediaURL()); - } -} -//static -void LLPanelMediaHUD::onClickReload(void* user_data) -{ - //LLViewerMedia::navigateHome(); - LLPanelMediaHUD* this_panel = static_cast (user_data); - LLViewerObject* objectp = LLSelectMgr::getInstance()->getSelection()->getFirstObject(); - if(objectp && this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->navigateTo(objectp->getMediaURL()); - } -} -//static -void LLPanelMediaHUD::onClickPlay(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->start(); - } -} -//static -void LLPanelMediaHUD::onClickPause(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->pause(); - } -} -//static -void LLPanelMediaHUD::onClickStop(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if (this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - if(this_panel->mMediaImpl->getMediaPlugin()->pluginSupportsMediaTime()) - { - this_panel->mMediaImpl->getMediaPlugin()->stop(); - } - else - { - this_panel->mMediaImpl->getMediaPlugin()->browse_stop(); - } - } -} -//static -void LLPanelMediaHUD::onClickZoom(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->nextZoomLevel(); -} -void LLPanelMediaHUD::nextZoomLevel() -{ - F32 zoom_padding = 0.0f; - S32 last_zoom_level = (S32)mCurrentZoom; - mCurrentZoom = (EZoomLevel)((last_zoom_level + 1) % (S32)ZOOM_END); - - switch (mCurrentZoom) - { - case ZOOM_NONE: - { - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - break; - } - case ZOOM_MEDIUM: - { - zoom_padding = ZOOM_MEDIUM_PADDING; - break; - } - default: - { - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - break; - } - } - - if (zoom_padding > 0.0f) - LLViewerMediaFocus::getInstance()->setCameraZoom(zoom_padding); -} -void LLPanelMediaHUD::onScrollUp(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->scrollEvent(0, -1, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollUpHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_UP; -} -void LLPanelMediaHUD::onScrollRight(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->handleKeyHere(KEY_RIGHT, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollRightHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_RIGHT; -} - -void LLPanelMediaHUD::onScrollLeft(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull()) - { - this_panel->mMediaImpl->handleKeyHere(KEY_LEFT, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollLeftHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_LEFT; -} - -void LLPanelMediaHUD::onScrollDown(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - if(this_panel->mMediaImpl.notNull() && this_panel->mMediaImpl->hasMedia()) - { - this_panel->mMediaImpl->getMediaPlugin()->scrollEvent(0, 1, MASK_NONE); - } -} -void LLPanelMediaHUD::onScrollDownHeld(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_DOWN; -} - -void LLPanelMediaHUD::onScrollStop(void* user_data) -{ - LLPanelMediaHUD* this_panel = static_cast (user_data); - this_panel->mScrollState = SCROLL_NONE; -} diff --git a/indra/newview/llpanelmediahud.h b/indra/newview/llpanelmediahud.h deleted file mode 100644 index 1d1f5e1be..000000000 --- a/indra/newview/llpanelmediahud.h +++ /dev/null @@ -1,111 +0,0 @@ -/** - * @file llpanelmediahud.h - * @brief Media hud panel - * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-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$ - */ - -#ifndef LL_PANELMEDIAHUD_H -#define LL_PANELMEDIAHUD_H - -#include "llpanel.h" -#include "llviewermedia.h" - -#include "llcoord.h" - -class LLViewerMediaImpl; - -class LLPanelMediaHUD : public LLPanel -{ -public: - LLPanelMediaHUD(viewer_media_t media_impl); - virtual ~LLPanelMediaHUD(); - /*virtual*/ BOOL postBuild(); - virtual void draw(); - virtual void setAlpha(F32 alpha); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - void updateShape(); - bool isMouseOver(); - void setMediaFocus(bool b) { mMediaFocus = b; } - void nextZoomLevel(); - void resetZoomLevel() { mCurrentZoom = ZOOM_NONE; } - - LLHandle getHandle() const { return getDerivedHandle(); } - - void setMediaImpl(viewer_media_t media_impl) { mMediaImpl = media_impl; } - - - enum EZoomLevel - { - ZOOM_NONE = 0, - ZOOM_MEDIUM = 1, - ZOOM_END - }; - enum EScrollDir - { - SCROLL_UP = 0, - SCROLL_DOWN, - SCROLL_LEFT, - SCROLL_RIGHT, - SCROLL_NONE - }; - -private: - static void onClickClose(void* user_data); - static void onClickBack(void* user_data); - static void onClickForward(void* user_data); - static void onClickHome(void* user_data); - static void onClickOpen(void* user_data); - static void onClickReload(void* user_data); - static void onClickPlay(void* user_data); - static void onClickPause(void* user_data); - static void onClickStop(void* user_data); - static void onClickZoom(void* user_data); - static void onScrollUp(void* user_data); - static void onScrollUpHeld(void* user_data); - static void onScrollLeft(void* user_data); - static void onScrollLeftHeld(void* user_data); - static void onScrollRight(void* user_data); - static void onScrollRightHeld(void* user_data); - static void onScrollDown(void* user_data); - static void onScrollDownHeld(void* user_data); - static void onScrollStop(void* user_data); - - bool mMediaFocus; - LLMatrix4 mLastCameraMat; - EZoomLevel mCurrentZoom; - EScrollDir mScrollState; - LLCoordWindow mLastCursorPos; - LLFrameTimer mMouseMoveTimer; - LLFrameTimer mFadeTimer; - F32 mMouseInactiveTime; - F32 mControlFadeTime; - viewer_media_t mMediaImpl; -}; - -#endif // LL_PANELMEDIAHUD_H diff --git a/indra/newview/llpanelmediasettingsgeneral.cpp b/indra/newview/llpanelmediasettingsgeneral.cpp new file mode 100644 index 000000000..f8ede13ba --- /dev/null +++ b/indra/newview/llpanelmediasettingsgeneral.cpp @@ -0,0 +1,515 @@ +/** + * @file llpanelmediasettingsgeneral.cpp + * @brief LLPanelMediaSettingsGeneral class implementation + * + * $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 "llpanelmediasettingsgeneral.h" + +// library includes +#include "llcombobox.h" +#include "llcheckboxctrl.h" +#include "llnotificationsutil.h" +#include "llspinctrl.h" +#include "lluictrlfactory.h" + +// project includes +#include "llagent.h" +#include "llviewerwindow.h" +#include "llviewermedia.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llbutton.h" +#include "lltexturectrl.h" +#include "llurl.h" +#include "llwindow.h" +#include "llmediaentry.h" +#include "llmediactrl.h" +#include "llpanelcontents.h" +#include "llpermissions.h" +#include "llpluginclassmedia.h" +#include "llfloatermediasettings.h" +#include "llfloatertools.h" +#include "lltrans.h" +#include "lltextbox.h" +#include "llpanelmediasettingssecurity.h" + +const char *CHECKERBOARD_DATA_URL = "data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22100%%22 height=%22100%%22 %3E%3Cdefs%3E%3Cpattern id=%22checker%22 patternUnits=%22userSpaceOnUse%22 x=%220%22 y=%220%22 width=%22128%22 height=%22128%22 viewBox=%220 0 128 128%22 %3E%3Crect x=%220%22 y=%220%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3Crect x=%2264%22 y=%2264%22 width=%2264%22 height=%2264%22 fill=%22#ddddff%22 /%3E%3C/pattern%3E%3C/defs%3E%3Crect x=%220%22 y=%220%22 width=%22100%%22 height=%22100%%22 fill=%22url(#checker)%22 /%3E%3C/svg%3E"; + +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsGeneral::LLPanelMediaSettingsGeneral() : + mAutoLoop( NULL ), + mFirstClick( NULL ), + mAutoZoom( NULL ), + mAutoPlay( NULL ), + mAutoScale( NULL ), + mWidthPixels( NULL ), + mHeightPixels( NULL ), + mHomeURL( NULL ), + mCurrentURL( NULL ), + mParent( NULL ), + mMediaEditable(false) +{ + // build dialog from XML + LLUICtrlFactory::getInstance()->buildPanel(this,"panel_media_settings_general.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsGeneral::postBuild() +{ + // connect member vars with UI widgets + mAutoLoop = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_LOOP_KEY ); + mAutoPlay = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_PLAY_KEY ); + mAutoScale = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_SCALE_KEY ); + mAutoZoom = getChild< LLCheckBoxCtrl >( LLMediaEntry::AUTO_ZOOM_KEY ); + mCurrentURL = getChild< LLTextBox >( LLMediaEntry::CURRENT_URL_KEY ); + mFirstClick = getChild< LLCheckBoxCtrl >( LLMediaEntry::FIRST_CLICK_INTERACT_KEY ); + mHeightPixels = getChild< LLSpinCtrl >( LLMediaEntry::HEIGHT_PIXELS_KEY ); + mHomeURL = getChild< LLLineEditor >( LLMediaEntry::HOME_URL_KEY ); + mWidthPixels = getChild< LLSpinCtrl >( LLMediaEntry::WIDTH_PIXELS_KEY ); + mPreviewMedia = getChild("preview_media"); + mFailWhiteListText = getChild( "home_fails_whitelist_label" ); + + // watch commit action for HOME URL + childSetCommitCallback( LLMediaEntry::HOME_URL_KEY, onCommitHomeURL, this); + childSetCommitCallback( "current_url_reset_btn",onBtnResetCurrentUrl, this); + + // interrogates controls and updates widgets as required + updateMediaPreview(); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsGeneral::~LLPanelMediaSettingsGeneral() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::draw() +{ + // housekeeping + LLPanel::draw(); + + // TODO: we need to call this repeatedly until the floater panels are fully + // created but once we have a valid answer, we should stop looking here - the + // commit callback will handle it + checkHomeUrlPassesWhitelist(); + + // enable/disable pixel values image entry based on auto scale checkbox + if ( mAutoScale->getValue().asBoolean() == false ) + { + getChildView( LLMediaEntry::WIDTH_PIXELS_KEY )->setEnabled( true ); + getChildView( LLMediaEntry::HEIGHT_PIXELS_KEY )->setEnabled( true ); + } + else + { + getChildView( LLMediaEntry::WIDTH_PIXELS_KEY )->setEnabled( false ); + getChildView( LLMediaEntry::HEIGHT_PIXELS_KEY )->setEnabled( false ); + }; + + // enable/disable UI based on type of media + bool reset_button_is_active = true; + if( mPreviewMedia ) + { + LLPluginClassMedia* media_plugin = mPreviewMedia->getMediaPlugin(); + if( media_plugin ) + { + // turn off volume (if we can) for preview. Note: this really only + // works for QuickTime movies right now - no way to control the + // volume of a flash app embedded in a page for example + media_plugin->setVolume( 0 ); + + // some controls are only appropriate for time or browser type plugins + // so we selectively enable/disable them - need to do it in draw + // because the information from plugins arrives assynchronously + bool show_time_controls = media_plugin->pluginSupportsMediaTime(); + if ( show_time_controls ) + { + getChildView( LLMediaEntry::CURRENT_URL_KEY )->setEnabled( false ); + reset_button_is_active = false; + getChildView("current_url_label")->setEnabled(false ); + getChildView( LLMediaEntry::AUTO_LOOP_KEY )->setEnabled( true ); + } + else + { + getChildView( LLMediaEntry::CURRENT_URL_KEY )->setEnabled( true ); + reset_button_is_active = true; + getChildView("current_url_label")->setEnabled(true ); + getChildView( LLMediaEntry::AUTO_LOOP_KEY )->setEnabled( false ); + }; + }; + }; + + // current URL can change over time, update it here + updateCurrentUrl(); + + LLPermissions perm; + bool user_can_press_reset = mMediaEditable; + + // several places modify this widget so we must collect states in one place + if ( reset_button_is_active ) + { + // user has perms to press reset button and it is active + if ( user_can_press_reset ) + { + getChildView("current_url_reset_btn")->setEnabled(true ); + } + // user does not has perms to press reset button and it is active + else + { + getChildView("current_url_reset_btn")->setEnabled(false ); + }; + } + else + // reset button is inactive so we just slam it to off - other states don't matter + { + getChildView("current_url_reset_btn")->setEnabled(false ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::clearValues( void* userdata, bool editable) +{ + LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; + self->mAutoLoop->clear(); + self->mAutoPlay->clear(); + self->mAutoScale->clear(); + self->mAutoZoom ->clear(); + self->mCurrentURL->setValue(""); + self->mFirstClick->clear(); + self->mHeightPixels->clear(); + self->mHomeURL->clear(); + self->mWidthPixels->clear(); + self->mAutoLoop ->setEnabled(editable); + self->mAutoPlay ->setEnabled(editable); + self->mAutoScale ->setEnabled(editable); + self->mAutoZoom ->setEnabled(editable); + self->mCurrentURL ->setEnabled(editable); + self->mFirstClick ->setEnabled(editable); + self->mHeightPixels ->setEnabled(editable); + self->mHomeURL ->setEnabled(editable); + self->mWidthPixels ->setEnabled(editable); + self->updateMediaPreview(); +} + +// static +bool LLPanelMediaSettingsGeneral::isMultiple() +{ + // IF all the faces have media (or all dont have media) + if ( LLFloaterMediaSettings::getInstance()->mIdenticalHasMediaInfo ) + { + if(LLFloaterMediaSettings::getInstance()->mMultipleMedia) + { + return true; + } + + } + else + { + if(LLFloaterMediaSettings::getInstance()->mMultipleValidMedia) + { + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::initValues( void* userdata, const LLSD& _media_settings, bool editable) +{ + LLPanelMediaSettingsGeneral *self =(LLPanelMediaSettingsGeneral *)userdata; + self->mMediaEditable = editable; + + LLSD media_settings = _media_settings; + + if ( LLPanelMediaSettingsGeneral::isMultiple() ) + { + // *HACK: "edit" the incoming media_settings + media_settings[LLMediaEntry::CURRENT_URL_KEY] = LLTrans::getString("Multiple Media"); + media_settings[LLMediaEntry::HOME_URL_KEY] = LLTrans::getString("Multiple Media"); + } + + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::AUTO_LOOP_KEY, self->mAutoLoop, "LLCheckBoxCtrl" }, + { LLMediaEntry::AUTO_PLAY_KEY, self->mAutoPlay, "LLCheckBoxCtrl" }, + { LLMediaEntry::AUTO_SCALE_KEY, self->mAutoScale, "LLCheckBoxCtrl" }, + { LLMediaEntry::AUTO_ZOOM_KEY, self->mAutoZoom, "LLCheckBoxCtrl" }, + { LLMediaEntry::CURRENT_URL_KEY, self->mCurrentURL, "LLTextBox" }, + { LLMediaEntry::HEIGHT_PIXELS_KEY, self->mHeightPixels, "LLSpinCtrl" }, + { LLMediaEntry::HOME_URL_KEY, self->mHomeURL, "LLLineEditor" }, + { LLMediaEntry::FIRST_CLICK_INTERACT_KEY, self->mFirstClick, "LLCheckBoxCtrl" }, + { LLMediaEntry::WIDTH_PIXELS_KEY, self->mWidthPixels, "LLSpinCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLLineEditor" ) + { + static_cast< LLLineEditor* >( data_set[ i ].ctrl_ptr )-> + setText( media_settings[ base_key ].asString() ); + } + else + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + else + if ( data_set[ i ].ctrl_type == "LLComboBox" ) + static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )-> + setCurrentByIndex( media_settings[ base_key ].asInteger() ); + else + if ( data_set[ i ].ctrl_type == "LLSpinCtrl" ) + static_cast< LLSpinCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asInteger() ); + + data_set[ i ].ctrl_ptr->setEnabled(self->mMediaEditable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; + + // interrogates controls and updates widgets as required + self->updateMediaPreview(); +} + +//////////////////////////////////////////////////////////////////////////////// +// Helper to set media control to media URL as required +void LLPanelMediaSettingsGeneral::updateMediaPreview() +{ + if ( mHomeURL->getValue().asString().length() > 0 ) + { + if(mPreviewMedia->getCurrentNavUrl() != mHomeURL->getValue().asString()) + { + mPreviewMedia->navigateTo( mHomeURL->getValue().asString() ); + } + } + else + // new home URL will be empty if media is deleted so display a + // "preview goes here" data url page + { + if(mPreviewMedia->getCurrentNavUrl() != CHECKERBOARD_DATA_URL) + { + mPreviewMedia->navigateTo( CHECKERBOARD_DATA_URL ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// + +// virtual +void LLPanelMediaSettingsGeneral::onClose(bool app_quitting) +{ + if(mPreviewMedia) + { + mPreviewMedia->unloadMediaSource(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::checkHomeUrlPassesWhitelist() +{ + // parent floater has not constructed the security panel yet + if ( mParent->getPanelSecurity() == 0 ) + return; + + std::string home_url = getHomeUrl(); + if ( home_url.empty() || mParent->getPanelSecurity()->urlPassesWhiteList( home_url ) ) + { + // Home URL is empty or passes the white list so hide the warning message + mFailWhiteListText->setVisible( false ); + } + else + { + // Home URL does not pass the white list so show the warning message + mFailWhiteListText->setVisible( true ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::onCommitHomeURL( LLUICtrl* ctrl, void *userdata ) +{ + LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; + + // check home url passes whitelist and display warning if not + self->checkHomeUrlPassesWhitelist(); + + self->updateMediaPreview(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsGeneral::onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata) +{ + LLPanelMediaSettingsGeneral* self =(LLPanelMediaSettingsGeneral *)userdata; + self->navigateHomeSelectedFace(false); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::preApply() +{ + // Make sure the home URL entry is committed + mHomeURL->onCommit(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::getValues( LLSD &fill_me_in, bool include_tentative ) +{ + if (include_tentative || !mAutoLoop->getTentative()) fill_me_in[LLMediaEntry::AUTO_LOOP_KEY] = (LLSD::Boolean)mAutoLoop->getValue(); + if (include_tentative || !mAutoPlay->getTentative()) fill_me_in[LLMediaEntry::AUTO_PLAY_KEY] = (LLSD::Boolean)mAutoPlay->getValue(); + if (include_tentative || !mAutoScale->getTentative()) fill_me_in[LLMediaEntry::AUTO_SCALE_KEY] = (LLSD::Boolean)mAutoScale->getValue(); + if (include_tentative || !mAutoZoom->getTentative()) fill_me_in[LLMediaEntry::AUTO_ZOOM_KEY] = (LLSD::Boolean)mAutoZoom->getValue(); + //Don't fill in current URL: this is only supposed to get changed via navigate + // if (include_tentative || !mCurrentURL->getTentative()) fill_me_in[LLMediaEntry::CURRENT_URL_KEY] = mCurrentURL->getValue(); + if (include_tentative || !mHeightPixels->getTentative()) fill_me_in[LLMediaEntry::HEIGHT_PIXELS_KEY] = (LLSD::Integer)mHeightPixels->getValue(); + // Don't fill in the home URL if it is the special "Multiple Media" string! + if ((include_tentative || !mHomeURL->getTentative()) + && LLTrans::getString("Multiple Media") != mHomeURL->getValue()) + fill_me_in[LLMediaEntry::HOME_URL_KEY] = (LLSD::String)mHomeURL->getValue(); + if (include_tentative || !mFirstClick->getTentative()) fill_me_in[LLMediaEntry::FIRST_CLICK_INTERACT_KEY] = (LLSD::Boolean)mFirstClick->getValue(); + if (include_tentative || !mWidthPixels->getTentative()) fill_me_in[LLMediaEntry::WIDTH_PIXELS_KEY] = (LLSD::Integer)mWidthPixels->getValue(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::postApply() +{ + // Make sure to navigate to the home URL if the current URL is empty and + // autoplay is on + navigateHomeSelectedFace(true); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::setParent( LLFloaterMediaSettings* parent ) +{ + mParent = parent; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLPanelMediaSettingsGeneral::navigateHomeSelectedFace(bool only_if_current_is_empty) +{ + struct functor_navigate_media : public LLSelectedTEGetFunctor< bool> + { + functor_navigate_media(bool flag) : only_if_current_is_empty(flag) {} + bool get( LLViewerObject* object, S32 face ) + { + if ( object && object->getTE(face) && object->permModify() ) + { + const LLMediaEntry *media_data = object->getTE(face)->getMediaData(); + if ( media_data ) + { + if (!only_if_current_is_empty || (media_data->getCurrentURL().empty() && media_data->getAutoPlay())) + { + viewer_media_t media_impl = + LLViewerMedia::getMediaImplFromTextureID(object->getTE(face)->getMediaData()->getMediaID()); + if(media_impl) + { + media_impl->navigateHome(); + return true; + } + } + } + } + return false; + }; + bool only_if_current_is_empty; + + } functor_navigate_media(only_if_current_is_empty); + + bool all_face_media_navigated = false; + LLObjectSelectionHandle selected_objects =LLSelectMgr::getInstance()->getSelection(); + selected_objects->getSelectedTEValue( &functor_navigate_media, all_face_media_navigated ); + + // Note: we don't update the 'current URL' field until the media data itself changes + + return all_face_media_navigated; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const std::string LLPanelMediaSettingsGeneral::getHomeUrl() +{ + return mHomeURL->getValue().asString(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsGeneral::updateCurrentUrl() +{ + // Get the current URL from the selection + const LLMediaEntry default_media_data; + std::string value_str = default_media_data.getCurrentURL(); + struct functor_getter_current_url : public LLSelectedTEGetFunctor< std::string > + { + functor_getter_current_url(const LLMediaEntry& entry): mMediaEntry(entry) {} + + std::string get( LLViewerObject* object, S32 face ) + { + if ( object ) + if ( object->getTE(face) ) + if ( object->getTE(face)->getMediaData() ) + return object->getTE(face)->getMediaData()->getCurrentURL(); + return mMediaEntry.getCurrentURL(); + }; + + const LLMediaEntry & mMediaEntry; + + } func_current_url(default_media_data); + bool identical = LLSelectMgr::getInstance()->getSelection()->getSelectedTEValue( &func_current_url, value_str ); + mCurrentURL->setText(value_str); + mCurrentURL->setTentative(identical); + + if ( LLPanelMediaSettingsGeneral::isMultiple() ) + { + mCurrentURL->setText(LLTrans::getString("Multiple Media")); + } +} diff --git a/indra/newview/llpanelmediasettingsgeneral.h b/indra/newview/llpanelmediasettingsgeneral.h new file mode 100644 index 000000000..0ae1401ab --- /dev/null +++ b/indra/newview/llpanelmediasettingsgeneral.h @@ -0,0 +1,100 @@ +/** + * @file llpanelmediasettingsgeneral.h + * @brief LLPanelMediaSettingsGeneral class definition + * + * $LicenseInfo:firstyear=2007&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$ + */ + +#ifndef LL_LLPANELMEDIAMEDIASETTINGSGENERAL_H +#define LL_LLPANELMEDIAMEDIASETTINGSGENERAL_H + +#include "llpanel.h" + +class LLButton; +class LLCheckBoxCtrl; +class LLLineEditor; +class LLSpinCtrl; +class LLTextureCtrl; +class LLMediaCtrl; +class LLTextBox; +class LLFloaterMediaSettings; + +class LLPanelMediaSettingsGeneral : public LLPanel +{ +public: + LLPanelMediaSettingsGeneral(); + ~LLPanelMediaSettingsGeneral(); + + // XXX TODO: put these into a common parent class? + // Hook that the floater calls before applying changes from the panel + void preApply(); + // Function that asks the panel to fill in values associated with the panel + // 'include_tentative' means fill in tentative values as well, otherwise do not + void getValues(LLSD &fill_me_in, bool include_tentative = true); + // Hook that the floater calls after applying changes to the panel + void postApply(); + + BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onClose(bool app_quitting); + + void setParent( LLFloaterMediaSettings* parent ); + static void initValues( void* userdata, const LLSD& media_settings ,bool editable); + static void clearValues( void* userdata, bool editable); + + // Navigates the current selected face to the Home URL. + // If 'only_if_current_is_empty' is "true", it only performs + // the operation if: 1) the current URL is empty, and 2) auto play is true. + bool navigateHomeSelectedFace(bool only_if_current_is_empty); + + void updateMediaPreview(); + + const std::string getHomeUrl(); + +protected: + LLFloaterMediaSettings* mParent; + bool mMediaEditable; + +private: + void updateCurrentUrl(); + + static void onBtnResetCurrentUrl(LLUICtrl* ctrl, void *userdata); + static void onCommitHomeURL(LLUICtrl* ctrl, void *userdata ); + + static bool isMultiple(); + + void checkHomeUrlPassesWhitelist(); + + LLCheckBoxCtrl* mAutoLoop; + LLCheckBoxCtrl* mFirstClick; + LLCheckBoxCtrl* mAutoZoom; + LLCheckBoxCtrl* mAutoPlay; + LLCheckBoxCtrl* mAutoScale; + LLSpinCtrl* mWidthPixels; + LLSpinCtrl* mHeightPixels; + LLLineEditor* mHomeURL; + LLTextBox* mCurrentURL; + LLMediaCtrl* mPreviewMedia; + LLTextBox* mFailWhiteListText; +}; + +#endif // LL_LLPANELMEDIAMEDIASETTINGSGENERAL_H diff --git a/indra/newview/llpanelmediasettingspermissions.cpp b/indra/newview/llpanelmediasettingspermissions.cpp new file mode 100644 index 000000000..d9e5d4ad2 --- /dev/null +++ b/indra/newview/llpanelmediasettingspermissions.cpp @@ -0,0 +1,285 @@ +/** + * @file llpanelmediasettingspermissions.cpp + * @brief LLPanelMediaSettingsPermissions class implementation + * + * note that "permissions" tab is really "Controls" tab - refs to 'perms' and + * 'permissions' not changed to 'controls' since we don't want to change + * shared files in server code and keeping everything the same seemed best. + * + * $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 "llpanelmediasettingspermissions.h" +#include "llpanelcontents.h" +#include "llcombobox.h" +#include "llcheckboxctrl.h" +#include "llspinctrl.h" +#include "llurlhistory.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "llnamebox.h" +#include "lltrans.h" +#include "llfloatermediasettings.h" + +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsPermissions::LLPanelMediaSettingsPermissions() : + mControls( NULL ), + mPermsOwnerInteract( 0 ), + mPermsOwnerControl( 0 ), + mPermsGroupName( 0 ), + mPermsGroupInteract( 0 ), + mPermsGroupControl( 0 ), + mPermsWorldInteract( 0 ), + mPermsWorldControl( 0 ) +{ + // build dialog from XML + //buildFromFile( "panel_media_settings_permissions.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this,"panel_media_settings_permissions.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsPermissions::postBuild() +{ + // connect member vars with UI widgets + mControls = getChild< LLComboBox >( LLMediaEntry::CONTROLS_KEY ); + mPermsOwnerInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_OWNER_INTERACT_KEY ); + mPermsOwnerControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_OWNER_CONTROL_KEY ); + mPermsGroupInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_GROUP_INTERACT_KEY ); + mPermsGroupControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_GROUP_CONTROL_KEY ); + mPermsWorldInteract = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_ANYONE_INTERACT_KEY ); + mPermsWorldControl = getChild< LLCheckBoxCtrl >( LLPanelContents::PERMS_ANYONE_CONTROL_KEY ); + + mPermsGroupName = getChild< LLNameBox >( "perms_group_name" ); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsPermissions::~LLPanelMediaSettingsPermissions() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +void LLPanelMediaSettingsPermissions::draw() +{ + // housekeeping + LLPanel::draw(); + + getChild("perms_group_name")->setValue(LLStringUtil::null); + LLUUID group_id; + BOOL groups_identical = LLSelectMgr::getInstance()->selectGetGroup(group_id); + if (groups_identical) + { + if(mPermsGroupName) + { + mPermsGroupName->setNameID(group_id, true); + } + } + else + { + if(mPermsGroupName) + { + mPermsGroupName->setNameID(LLUUID::null, TRUE); + mPermsGroupName->refresh(LLUUID::null, std::string(), true); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsPermissions::clearValues( void* userdata, bool editable) +{ + LLPanelMediaSettingsPermissions *self =(LLPanelMediaSettingsPermissions *)userdata; + + self->mControls->clear(); + self->mPermsOwnerInteract->clear(); + self->mPermsOwnerControl->clear(); + self->mPermsGroupInteract->clear(); + self->mPermsGroupControl->clear(); + self->mPermsWorldInteract->clear(); + self->mPermsWorldControl->clear(); + + self->mControls->setEnabled(editable); + self->mPermsOwnerInteract->setEnabled(editable); + self->mPermsOwnerControl->setEnabled(editable); + self->mPermsGroupInteract->setEnabled(editable); + self->mPermsGroupControl->setEnabled(editable); + self->mPermsWorldInteract->setEnabled(editable); + self->mPermsWorldControl->setEnabled(editable); + + self->getChild< LLTextBox >("controls_label")->setEnabled(editable); + self->getChild< LLTextBox >("owner_label")->setEnabled(editable); + self->getChild< LLTextBox >("group_label")->setEnabled(editable); + self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); + self->getChild< LLTextBox >("anyone_label")->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsPermissions::initValues( void* userdata, const LLSD& media_settings , bool editable) +{ + LLPanelMediaSettingsPermissions *self =(LLPanelMediaSettingsPermissions *)userdata; + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::CONTROLS_KEY, self->mControls, "LLComboBox" }, + { LLPanelContents::PERMS_OWNER_INTERACT_KEY, self->mPermsOwnerInteract, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_OWNER_CONTROL_KEY, self->mPermsOwnerControl, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_GROUP_INTERACT_KEY, self->mPermsGroupInteract, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_GROUP_CONTROL_KEY, self->mPermsGroupControl, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_ANYONE_INTERACT_KEY, self->mPermsWorldInteract, "LLCheckBoxCtrl" }, + { LLPanelContents::PERMS_ANYONE_CONTROL_KEY, self->mPermsWorldControl, "LLCheckBoxCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + { + // Most recent change to the "sense" of these checkboxes + // means the value in the checkbox matches that on the server + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + } + else + if ( data_set[ i ].ctrl_type == "LLComboBox" ) + static_cast< LLComboBox* >( data_set[ i ].ctrl_ptr )-> + setCurrentByIndex( media_settings[ base_key ].asInteger() ); + + data_set[ i ].ctrl_ptr->setEnabled(editable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; + + // *NOTE: If any of a particular flavor is tentative, we have to disable + // them all because of an architectural issue: namely that we represent + // these as a bit field, and we can't selectively apply only one bit to all selected + // faces if they don't match. Also see the *NOTE below. + if ( self->mPermsOwnerInteract->getTentative() || + self->mPermsGroupInteract->getTentative() || + self->mPermsWorldInteract->getTentative()) + { + self->mPermsOwnerInteract->setEnabled(false); + self->mPermsGroupInteract->setEnabled(false); + self->mPermsWorldInteract->setEnabled(false); + } + if ( self->mPermsOwnerControl->getTentative() || + self->mPermsGroupControl->getTentative() || + self->mPermsWorldControl->getTentative()) + { + self->mPermsOwnerControl->setEnabled(false); + self->mPermsGroupControl->setEnabled(false); + self->mPermsWorldControl->setEnabled(false); + } + + self->getChild< LLTextBox >("controls_label")->setEnabled(editable); + self->getChild< LLTextBox >("owner_label")->setEnabled(editable); + self->getChild< LLTextBox >("group_label")->setEnabled(editable); + self->getChild< LLNameBox >("perms_group_name")->setEnabled(editable); + self->getChild< LLTextBox >("anyone_label")->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsPermissions::preApply() +{ + // no-op +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsPermissions::getValues( LLSD &fill_me_in, bool include_tentative ) +{ + // moved over from the 'General settings' tab + if (include_tentative || !mControls->getTentative()) fill_me_in[LLMediaEntry::CONTROLS_KEY] = (LLSD::Integer)mControls->getCurrentIndex(); + + // *NOTE: For some reason, gcc does not like these symbol references in the + // expressions below (inside the static_casts). I have NO idea why :(. + // For some reason, assigning them to const temp vars here fixes the link + // error. Bizarre. + const U8 none = LLMediaEntry::PERM_NONE; + const U8 owner = LLMediaEntry::PERM_OWNER; + const U8 group = LLMediaEntry::PERM_GROUP; + const U8 anyone = LLMediaEntry::PERM_ANYONE; + const LLSD::Integer control = static_cast( + (mPermsOwnerControl->getValue() ? owner : none ) | + (mPermsGroupControl->getValue() ? group: none ) | + (mPermsWorldControl->getValue() ? anyone : none )); + const LLSD::Integer interact = static_cast( + (mPermsOwnerInteract->getValue() ? owner: none ) | + (mPermsGroupInteract->getValue() ? group : none ) | + (mPermsWorldInteract->getValue() ? anyone : none )); + + // *TODO: This will fill in the values of all permissions values, even if + // one or more is tentative. This is not quite the user expectation...what + // it should do is only change the bit that was made "untentative", but in + // a multiple-selection situation, this isn't possible given the architecture + // for how settings are applied. + if (include_tentative || + !mPermsOwnerControl->getTentative() || + !mPermsGroupControl->getTentative() || + !mPermsWorldControl->getTentative()) + { + fill_me_in[LLMediaEntry::PERMS_CONTROL_KEY] = control; + } + if (include_tentative || + !mPermsOwnerInteract->getTentative() || + !mPermsGroupInteract->getTentative() || + !mPermsWorldInteract->getTentative()) + { + fill_me_in[LLMediaEntry::PERMS_INTERACT_KEY] = interact; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsPermissions::postApply() +{ + // no-op +} + + diff --git a/indra/newview/llpanelmediasettingspermissions.h b/indra/newview/llpanelmediasettingspermissions.h new file mode 100644 index 000000000..f97672018 --- /dev/null +++ b/indra/newview/llpanelmediasettingspermissions.h @@ -0,0 +1,73 @@ +/** + * @file llpanelmediasettingspermissions.h + * @brief LLPanelMediaSettingsPermissions class definition + * + * note that "permissions" tab is really "Controls" tab - refs to 'perms' and + * 'permissions' not changed to 'controls' since we don't want to change + * shared files in server code and keeping everything the same seemed best. + * + * $LicenseInfo:firstyear=2007&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$ + */ + +#ifndef LL_LLPANELMEDIAMEDIASETTINGSPERMISSIONS_H +#define LL_LLPANELMEDIAMEDIASETTINGSPERMISSIONS_H + +#include "llpanel.h" +#include "lluuid.h" + +class LLComboBox; +class LLCheckBoxCtrl; +class LLNameBox; + +class LLPanelMediaSettingsPermissions : public LLPanel +{ +public: + LLPanelMediaSettingsPermissions(); + ~LLPanelMediaSettingsPermissions(); + + BOOL postBuild(); + virtual void draw(); + + // XXX TODO: put these into a common parent class? + // Hook that the floater calls before applying changes from the panel + void preApply(); + // Function that asks the panel to fill in values associated with the panel + // 'include_tentative' means fill in tentative values as well, otherwise do not + void getValues(LLSD &fill_me_in, bool include_tentative = true); + // Hook that the floater calls after applying changes to the panel + void postApply(); + + static void initValues( void* userdata, const LLSD& media_settings, bool editable ); + static void clearValues( void* userdata, bool editable); + +private: + LLComboBox* mControls; + LLCheckBoxCtrl* mPermsOwnerInteract; + LLCheckBoxCtrl* mPermsOwnerControl; + LLNameBox* mPermsGroupName; + LLCheckBoxCtrl* mPermsGroupInteract; + LLCheckBoxCtrl* mPermsGroupControl; + LLCheckBoxCtrl* mPermsWorldInteract; + LLCheckBoxCtrl* mPermsWorldControl; +}; + +#endif // LL_LLPANELMEDIAMEDIASETTINGSPERMISSIONS_H diff --git a/indra/newview/llpanelmediasettingssecurity.cpp b/indra/newview/llpanelmediasettingssecurity.cpp new file mode 100644 index 000000000..509560b37 --- /dev/null +++ b/indra/newview/llpanelmediasettingssecurity.cpp @@ -0,0 +1,366 @@ +/** + * @file llpanelmediasettingssecurity.cpp + * @brief LLPanelMediaSettingsSecurity class implementation + * + * $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 "llpanelmediasettingssecurity.h" + +#include "llpanelcontents.h" +#include "llcheckboxctrl.h" +#include "llnotificationsutil.h" +#include "llscrolllistctrl.h" +#include "lluictrlfactory.h" +#include "llwindow.h" +#include "llviewerwindow.h" +#include "llsdutil.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "lltextbox.h" +#include "llfloaterwhitelistentry.h" +#include "llfloatermediasettings.h" + +//////////////////////////////////////////////////////////////////////////////// +// +LLPanelMediaSettingsSecurity::LLPanelMediaSettingsSecurity() : + mParent( NULL ) +{ + mCommitCallbackRegistrar.add("Media.whitelistAdd", boost::bind(&LLPanelMediaSettingsSecurity::onBtnAdd, this)); + mCommitCallbackRegistrar.add("Media.whitelistDelete", boost::bind(&LLPanelMediaSettingsSecurity::onBtnDel, this)); + + // build dialog from XML + //buildFromFile( "panel_media_settings_security.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this,"panel_media_settings_security.xml"); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLPanelMediaSettingsSecurity::postBuild() +{ + mEnableWhiteList = getChild< LLCheckBoxCtrl >( LLMediaEntry::WHITELIST_ENABLE_KEY ); + mWhiteListList = getChild< LLScrollListCtrl >( LLMediaEntry::WHITELIST_KEY ); + mHomeUrlFailsWhiteListText = getChild( "home_url_fails_whitelist" ); + + setDefaultBtn("whitelist_add"); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +LLPanelMediaSettingsSecurity::~LLPanelMediaSettingsSecurity() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::draw() +{ + // housekeeping + LLPanel::draw(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::initValues( void* userdata, const LLSD& media_settings , bool editable) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + std::string base_key( "" ); + std::string tentative_key( "" ); + + struct + { + std::string key_name; + LLUICtrl* ctrl_ptr; + std::string ctrl_type; + + } data_set [] = + { + { LLMediaEntry::WHITELIST_ENABLE_KEY, self->mEnableWhiteList, "LLCheckBoxCtrl" }, + { LLMediaEntry::WHITELIST_KEY, self->mWhiteListList, "LLScrollListCtrl" }, + { "", NULL , "" } + }; + + for( int i = 0; data_set[ i ].key_name.length() > 0; ++i ) + { + base_key = std::string( data_set[ i ].key_name ); + tentative_key = base_key + std::string( LLPanelContents::TENTATIVE_SUFFIX ); + + bool enabled_overridden = false; + + // TODO: CP - I bet there is a better way to do this using Boost + if ( media_settings[ base_key ].isDefined() ) + { + if ( data_set[ i ].ctrl_type == "LLCheckBoxCtrl" ) + { + static_cast< LLCheckBoxCtrl* >( data_set[ i ].ctrl_ptr )-> + setValue( media_settings[ base_key ].asBoolean() ); + } + else + if ( data_set[ i ].ctrl_type == "LLScrollListCtrl" ) + { + // get control + LLScrollListCtrl* list = static_cast< LLScrollListCtrl* >( data_set[ i ].ctrl_ptr ); + list->deleteAllItems(); + + // points to list of white list URLs + LLSD url_list = media_settings[ base_key ]; + + // better be the whitelist + llassert(data_set[ i ].ctrl_ptr == self->mWhiteListList); + + // If tentative, don't add entries + if (media_settings[ tentative_key ].asBoolean()) + { + self->mWhiteListList->setEnabled(false); + enabled_overridden = true; + } + else { + // iterate over them and add to scroll list + LLSD::array_iterator iter = url_list.beginArray(); + while( iter != url_list.endArray() ) + { + std::string entry = *iter; + self->addWhiteListEntry( entry ); + ++iter; + } + } + }; + if ( ! enabled_overridden) data_set[ i ].ctrl_ptr->setEnabled(editable); + data_set[ i ].ctrl_ptr->setTentative( media_settings[ tentative_key ].asBoolean() ); + }; + }; + + // initial update - hides/shows status messages etc. + self->updateWhitelistEnableStatus(); +} + +//////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::clearValues( void* userdata , bool editable) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + self->mEnableWhiteList->clear(); + self->mWhiteListList->deleteAllItems(); + self->mEnableWhiteList->setEnabled(editable); + self->mWhiteListList->setEnabled(editable); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::preApply() +{ + // no-op +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::getValues( LLSD &fill_me_in, bool include_tentative ) +{ + if (include_tentative || !mEnableWhiteList->getTentative()) + fill_me_in[LLMediaEntry::WHITELIST_ENABLE_KEY] = (LLSD::Boolean)mEnableWhiteList->getValue(); + + if (include_tentative || !mWhiteListList->getTentative()) + { + // iterate over white list and extract items + std::vector< LLScrollListItem* > whitelist_items = mWhiteListList->getAllData(); + std::vector< LLScrollListItem* >::iterator iter = whitelist_items.begin(); + + // *NOTE: need actually set the key to be an emptyArray(), or the merge + // we do with this LLSD will think there's nothing to change. + fill_me_in[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray(); + while( iter != whitelist_items.end() ) + { + LLScrollListCell* cell = (*iter)->getColumn( ENTRY_COLUMN ); + std::string whitelist_url = cell->getValue().asString(); + + fill_me_in[ LLMediaEntry::WHITELIST_KEY ].append( whitelist_url ); + ++iter; + }; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::postApply() +{ + // no-op +} + +/////////////////////////////////////////////////////////////////////////////// +// Try to make a valid URL if a fragment ( +// white list list box widget and build a list to test against. Can also +const std::string LLPanelMediaSettingsSecurity::makeValidUrl( const std::string& src_url ) +{ + // use LLURI to determine if we have a valid scheme + LLURI candidate_url( src_url ); + if ( candidate_url.scheme().empty() ) + { + // build a URL comprised of default scheme and the original fragment + const std::string default_scheme( "http://" ); + return default_scheme + src_url; + }; + + // we *could* test the "default scheme" + "original fragment" URL again + // using LLURI to see if it's valid but I think the outcome is the same + // in either case - our only option is to return the original URL + + // we *think* the original url passed in was valid + return src_url; +} + +/////////////////////////////////////////////////////////////////////////////// +// wrapper for testing a URL against the whitelist. We grab entries from +// white list list box widget and build a list to test against. +bool LLPanelMediaSettingsSecurity::urlPassesWhiteList( const std::string& test_url ) +{ + // If the whitlelist list is tentative, it means we have multiple settings. + // In that case, we have no choice but to return true + if ( mWhiteListList->getTentative() ) return true; + + // the checkUrlAgainstWhitelist(..) function works on a vector + // of strings for the white list entries - in this panel, the white list + // is stored in the widgets themselves so we need to build something compatible. + std::vector< std::string > whitelist_strings; + whitelist_strings.clear(); // may not be required - I forget what the spec says. + + // step through whitelist widget entries and grab them as strings + std::vector< LLScrollListItem* > whitelist_items = mWhiteListList->getAllData(); + std::vector< LLScrollListItem* >::iterator iter = whitelist_items.begin(); + while( iter != whitelist_items.end() ) + { + LLScrollListCell* cell = (*iter)->getColumn( ENTRY_COLUMN ); + std::string whitelist_url = cell->getValue().asString(); + + whitelist_strings.push_back( whitelist_url ); + + ++iter; + }; + + // possible the URL is just a fragment so we validize it + const std::string valid_url = makeValidUrl( test_url ); + + // indicate if the URL passes whitelist + return LLMediaEntry::checkUrlAgainstWhitelist( valid_url, whitelist_strings ); +} + +/////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::updateWhitelistEnableStatus() +{ + // get the value for home URL and make it a valid URL + const std::string valid_url = makeValidUrl( mParent->getHomeUrl() ); + + // now check to see if the home url passes the whitelist in its entirity + if ( urlPassesWhiteList( valid_url ) ) + { + mEnableWhiteList->setEnabled( true ); + mHomeUrlFailsWhiteListText->setVisible( false ); + } + else + { + mEnableWhiteList->set( false ); + mEnableWhiteList->setEnabled( false ); + mHomeUrlFailsWhiteListText->setVisible( true ); + }; +} + +/////////////////////////////////////////////////////////////////////////////// +// Add an entry to the whitelist scrollbox and indicate if the current +// home URL passes this entry or not using an icon +void LLPanelMediaSettingsSecurity::addWhiteListEntry( const std::string& entry ) +{ + // grab the home url + std::string home_url( "" ); + if ( mParent ) + home_url = mParent->getHomeUrl(); + + // try to make a valid URL based on what the user entered - missing scheme for example + const std::string valid_url = makeValidUrl( home_url ); + + // check the home url against this single whitelist entry + std::vector< std::string > whitelist_entries; + whitelist_entries.push_back( entry ); + bool home_url_passes_entry = LLMediaEntry::checkUrlAgainstWhitelist( valid_url, whitelist_entries ); + + // build an icon cell based on whether or not the home url pases it or not + LLSD row; + if ( home_url_passes_entry || home_url.empty() ) + { + row[ "columns" ][ ICON_COLUMN ][ "type" ] = "icon"; + row[ "columns" ][ ICON_COLUMN ][ "value" ] = ""; + row[ "columns" ][ ICON_COLUMN ][ "width" ] = 20; + } + else + { + row[ "columns" ][ ICON_COLUMN ][ "type" ] = "icon"; + row[ "columns" ][ ICON_COLUMN ][ "value" ] = "Parcel_Exp_Color.png"; + row[ "columns" ][ ICON_COLUMN ][ "width" ] = 20; + }; + + // always add in the entry itself + row[ "columns" ][ ENTRY_COLUMN ][ "type" ] = "text"; + row[ "columns" ][ ENTRY_COLUMN ][ "value" ] = entry; + + // add to the white list scroll box + mWhiteListList->addElement( row ); +}; + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::onBtnAdd( void* userdata ) +{ + + LLPanelMediaSettingsSecurity* self = (LLPanelMediaSettingsSecurity*)userdata; + LLFloaterWhiteListEntry::getInstance()->open(); + + for(LLView* parent = self->getParent(); parent !=NULL; parent = parent->getParent()) + { + if(dynamic_cast(parent)) + { + LLFloaterWhiteListEntry::getInstance()->centerWithin(parent->getRect()); + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// static +void LLPanelMediaSettingsSecurity::onBtnDel( void* userdata ) +{ + LLPanelMediaSettingsSecurity *self =(LLPanelMediaSettingsSecurity *)userdata; + + self->mWhiteListList->deleteSelectedItems(); + + // contents of whitelist changed so recheck it against home url + self->updateWhitelistEnableStatus(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLPanelMediaSettingsSecurity::setParent( LLFloaterMediaSettings* parent ) +{ + mParent = parent; +}; diff --git a/indra/newview/llpanelmediasettingssecurity.h b/indra/newview/llpanelmediasettingssecurity.h new file mode 100644 index 000000000..fe8e84357 --- /dev/null +++ b/indra/newview/llpanelmediasettingssecurity.h @@ -0,0 +1,82 @@ +/** + * @file llpanelmediasettingssecurity.h + * @brief LLPanelMediaSettingsSecurity class definition + * + * $LicenseInfo:firstyear=2007&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$ + */ + +#ifndef LL_LLPANELMEDIAMEDIASETTINGSSECURITY_H +#define LL_LLPANELMEDIAMEDIASETTINGSSECURITY_H + +#include "llpanel.h" + +class LLCheckBoxCtrl; +class LLScrollListCtrl; +class LLTextBox; +class LLFloaterMediaSettings; + +class LLPanelMediaSettingsSecurity : public LLPanel +{ +public: + LLPanelMediaSettingsSecurity(); + ~LLPanelMediaSettingsSecurity(); + + BOOL postBuild(); + virtual void draw(); + + // XXX TODO: put these into a common parent class? + // Hook that the floater calls before applying changes from the panel + void preApply(); + // Function that asks the panel to fill in values associated with the panel + // 'include_tentative' means fill in tentative values as well, otherwise do not + void getValues(LLSD &fill_me_in, bool include_tentative = true); + // Hook that the floater calls after applying changes to the panel + void postApply(); + + static void initValues( void* userdata, const LLSD& media_settings, bool editable); + static void clearValues( void* userdata, bool editable); + void addWhiteListEntry( const std::string& url ); + void setParent( LLFloaterMediaSettings* parent ); + bool urlPassesWhiteList( const std::string& test_url ); + const std::string makeValidUrl( const std::string& src_url ); + + void updateWhitelistEnableStatus(); + +protected: + LLFloaterMediaSettings* mParent; + +private: + enum ColumnIndex + { + ICON_COLUMN = 0, + ENTRY_COLUMN = 1, + }; + + LLCheckBoxCtrl* mEnableWhiteList; + LLScrollListCtrl* mWhiteListList; + LLTextBox* mHomeUrlFailsWhiteListText; + + static void onBtnAdd(void*); + static void onBtnDel(void*); +}; + +#endif // LL_LLPANELMEDIAMEDIASETTINGSSECURITY_H diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp new file mode 100644 index 000000000..9438dfa79 --- /dev/null +++ b/indra/newview/llpanelnearbymedia.cpp @@ -0,0 +1,1314 @@ +/** + * @file llpanelnearbymedia.cpp + * @brief Management interface for muting and controlling nearby media + * + * $LicenseInfo:firstyear=2005&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 "llpanelnearbymedia.h" + +#include "llaudioengine.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llresizebar.h" +#include "llresizehandle.h" +#include "llscrolllistctrl.h" +#include "llslider.h" +#include "llsliderctrl.h" +#include "llagent.h" +#include "llagentui.h" +#include "llbutton.h" +#include "lltextbox.h" +#include "llviewermedia.h" +#include "llviewerparcelmedia.h" +#include "llviewerregion.h" +#include "llviewermediafocus.h" +#include "llviewerparcelmgr.h" +#include "llparcel.h" +#include "llpluginclassmedia.h" +#include "llvovolume.h" +#include "llstatusbar.h" +#include "llsdutil.h" +#include "llvieweraudio.h" +#include "lluictrlfactory.h" + +#include "llfloaterpreference.h" // for the gear icon +#include "lltabcontainer.h" + +#include + +#include "lloverlaybar.h" +#include "llmediaremotectrl.h" +#include "llchatbar.h" +#include "lltoolbar.h" + +extern LLControlGroup gSavedSettings; + +static const LLUUID PARCEL_MEDIA_LIST_ITEM_UUID = LLUUID("CAB5920F-E484-4233-8621-384CF373A321"); +static const LLUUID PARCEL_AUDIO_LIST_ITEM_UUID = LLUUID("DF4B020D-8A24-4B95-AB5D-CA970D694822"); + +// +// LLPanelNearByMedia +// + + +LLPanelNearByMedia::LLPanelNearByMedia(bool standalone_panel) +: mMediaList(NULL), + mEnableAllCtrl(NULL), + mAllMediaDisabled(false), + mDebugInfoVisible(false), + mParcelMediaItem(NULL), + mParcelAudioItem(NULL), + mStandalonePanel(standalone_panel) +{ + mHoverTimer.stop(); + + mParcelAudioAutoStart = gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getBOOL("MediaTentativeAutoPlay"); + + gSavedSettings.getControl(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING)->getSignal()->connect(boost::bind(&LLPanelNearByMedia::handleMediaAutoPlayChanged, this, _2)); + + mCommitCallbackRegistrar.add("MediaListCtrl.EnableAll", boost::bind(&LLPanelNearByMedia::onClickEnableAll, this)); + mCommitCallbackRegistrar.add("MediaListCtrl.DisableAll", boost::bind(&LLPanelNearByMedia::onClickDisableAll, this)); + mCommitCallbackRegistrar.add("MediaListCtrl.GoMediaPrefs", boost::bind(&LLPanelNearByMedia::onAdvancedButtonClick, this)); + mCommitCallbackRegistrar.add("MediaListCtrl.MoreLess", boost::bind(&LLPanelNearByMedia::onMoreLess, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Stop", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaStop, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Play", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaPlay, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Pause", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaPause, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Mute", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaMute, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Volume", boost::bind(&LLPanelNearByMedia::onCommitSelectedMediaVolume, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Zoom", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaZoom, this)); + mCommitCallbackRegistrar.add("SelectedMediaCtrl.Unzoom", boost::bind(&LLPanelNearByMedia::onClickSelectedMediaUnzoom, this)); + + //buildFromFile( "panel_nearby_media.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_nearby_media.xml"); +} + +LLPanelNearByMedia::~LLPanelNearByMedia() +{ + //Wipe the mInNearbyMediaList value for all impls since we don't need them anymore. + LLViewerMedia::impl_list impls = LLViewerMedia::getPriorityList(); + LLViewerMedia::impl_list::iterator priority_iter; + for(priority_iter = impls.begin(); priority_iter != impls.end(); priority_iter++) + { + (*priority_iter)->setInNearbyMediaList(false); + } +} + +BOOL LLPanelNearByMedia::postBuild() +{ + LLPanel::postBuild(); + + if(mStandalonePanel) + { + const S32 RESIZE_BAR_THICKNESS = 6; + LLResizeBar::Params p; + p.rect = LLRect(0, RESIZE_BAR_THICKNESS, getRect().getWidth(), 0); + p.name = "resizebar_bottom"; + p.min_size = getRect().getHeight(); + p.side = LLResizeBar::BOTTOM; + p.resizing_view = this; + addChild( LLUICtrlFactory::create(p) ); + + p.rect = LLRect( 0, getRect().getHeight(), RESIZE_BAR_THICKNESS, 0); + p.name = "resizebar_left"; + p.min_size = getRect().getWidth(); + p.side = LLResizeBar::LEFT; + addChild( LLUICtrlFactory::create(p) ); + + LLResizeHandle::Params resize_handle_p; + resize_handle_p.rect = LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ); + resize_handle_p.mouse_opaque(false); + resize_handle_p.min_width(getRect().getWidth()); + resize_handle_p.min_height(getRect().getHeight()); + resize_handle_p.corner(LLResizeHandle::LEFT_BOTTOM); + addChild(LLUICtrlFactory::create(resize_handle_p)); + } + + mNearbyMediaPanel = getChild("nearby_media_panel"); + mMediaList = getChild("media_list"); + mEnableAllCtrl = getChild("all_nearby_media_enable_btn"); + mDisableAllCtrl = getChild("all_nearby_media_disable_btn"); + mShowCtrl = getChild("show_combo"); + + // Dynamic (selection-dependent) controls + mStopCtrl = getChild("stop"); + mPlayCtrl = getChild("play"); + mPauseCtrl = getChild("pause"); + mMuteCtrl = getChild("mute"); + mVolumeSliderCtrl = getChild("volume_slider_ctrl"); + mZoomCtrl = getChild("zoom"); + mUnzoomCtrl = getChild("unzoom"); + mVolumeSlider = getChild("volume_slider"); + mMuteBtn = getChild("mute_btn"); + + mEmptyNameString = getString("empty_item_text"); + mParcelMediaName = getString("parcel_media_name"); + mParcelAudioName = getString("parcel_audio_name"); + mPlayingString = getString("playing_suffix"); + + mMediaList->setDoubleClickCallback(onZoomMedia, this); + mMediaList->sortByColumnIndex(PROXIMITY_COLUMN, TRUE); + mMediaList->sortByColumnIndex(VISIBILITY_COLUMN, FALSE); + + refreshList(); + updateControls(); + updateColumns(); + + LLView* minimized_controls = getChildView("minimized_controls"); + mMoreRect = getRect(); + mLessRect = getRect(); + mLessRect.mBottom = minimized_controls->getRect().mBottom; + + getChild("more_btn")->setVisible(false); + onMoreLess(); + + return TRUE; +} + +void LLPanelNearByMedia::handleMediaAutoPlayChanged(const LLSD& newvalue) +{ + // update mParcelAudioAutoStart if AUTO_PLAY_MEDIA_SETTING changes + mParcelAudioAutoStart = gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getBOOL("MediaTentativeAutoPlay"); +} + +/*virtual*/ +void LLPanelNearByMedia::onMouseEnter(S32 x, S32 y, MASK mask) +{ + mHoverTimer.stop(); + LLPanel::onMouseEnter(x,y,mask); +} + + +/*virtual*/ +void LLPanelNearByMedia::onMouseLeave(S32 x, S32 y, MASK mask) +{ + if(mStandalonePanel) + mHoverTimer.start(); + LLPanel::onMouseLeave(x,y,mask); +} + +/*virtual*/ +void LLPanelNearByMedia::onTopLost() +{ + setVisible(FALSE); +} + + +/*virtual*/ +void LLPanelNearByMedia::handleVisibilityChange ( BOOL new_visibility ) +{ + if (mStandalonePanel && new_visibility) + { + mHoverTimer.start(); // timer will be stopped when mouse hovers over panel + } + else + { + mHoverTimer.stop(); + } +} + +/*virtual*/ +void LLPanelNearByMedia::reshape(S32 width, S32 height, BOOL called_from_parent) +{ + LLPanel::reshape(width, height, called_from_parent); + + LLButton* more_btn = findChild("more_btn"); + if (!mStandalonePanel || more_btn && more_btn->getValue().asBoolean()) + { + mMoreRect = getRect(); + } + +} + +const F32 AUTO_CLOSE_FADE_TIME_START= 4.0f; +const F32 AUTO_CLOSE_FADE_TIME_END = 5.0f; + +/*virtual*/ +void LLPanelNearByMedia::draw() +{ + // keep bottom of panel on screen + LLRect screen_rect = calcScreenRect(); + if (screen_rect.mBottom < 0) + { + LLRect new_rect = getRect(); + new_rect.mBottom += 0 - screen_rect.mBottom; + setShape(new_rect); + } + + refreshList(); + updateControls(); + + + F32 alpha = mHoverTimer.getStarted() + ? clamp_rescale(mHoverTimer.getElapsedTimeF32(), AUTO_CLOSE_FADE_TIME_START, AUTO_CLOSE_FADE_TIME_END, 1.f, 0.f) + : 1.0f; + + LLViewDrawContext context(alpha); + + LLPanel::draw(); + + if (alpha == 0.f) + { + setVisible(false); + } +} + +/*virtual*/ +BOOL LLPanelNearByMedia::handleHover(S32 x, S32 y, MASK mask) +{ + LLPanel::handleHover(x, y, mask); + + // If we are hovering over this panel, make sure to clear any hovered media + // ID. Note that the more general solution would be to clear this ID when + // the mouse leaves the in-scene view, but that proved to be problematic. + // See EXT-5517 + LLViewerMediaFocus::getInstance()->clearHover(); + + // Always handle + return true; +} + +bool LLPanelNearByMedia::getParcelAudioAutoStart() +{ + return mParcelAudioAutoStart; +} + +LLScrollListItem* LLPanelNearByMedia::addListItem(const LLUUID &id) +{ + if (NULL == mMediaList) return NULL; + + // Just set up the columns -- the values will be filled in by updateListItem(). + + LLSD row; + row["id"] = id; + + LLSD &columns = row["columns"]; + columns[CHECKBOX_COLUMN]["name"]="media_checkbox_ctrl"; + columns[CHECKBOX_COLUMN]["column"] = "media_checkbox_ctrl"; + columns[CHECKBOX_COLUMN]["type"] = "checkbox"; + //if(mDebugInfoVisible) + { + columns[PROXIMITY_COLUMN]["name"]="media_proximity"; + columns[PROXIMITY_COLUMN]["column"] = "media_proximity"; + columns[PROXIMITY_COLUMN]["value"] = ""; + columns[VISIBILITY_COLUMN]["name"]="media_visibility"; + columns[VISIBILITY_COLUMN]["column"] = "media_visibility"; + columns[VISIBILITY_COLUMN]["value"] = ""; + columns[CLASS_COLUMN]["name"]="media_class"; + columns[CLASS_COLUMN]["column"] = "media_class"; + columns[CLASS_COLUMN]["type"] = "text"; + columns[CLASS_COLUMN]["value"] = ""; + } + columns[NAME_COLUMN]["name"] = "media_name"; + columns[NAME_COLUMN]["column"] = "media_name"; + columns[NAME_COLUMN]["type"] = "text"; + columns[NAME_COLUMN]["value"] = ""; + //if(mDebugInfoVisible) + { + columns[DEBUG_COLUMN]["name"] = "media_debug"; + columns[DEBUG_COLUMN]["column"] = "media_debug"; + columns[DEBUG_COLUMN]["type"] = "text"; + columns[DEBUG_COLUMN]["value"] = ""; + } + + LLScrollListItem* new_item = mMediaList->addElement(row); + if (NULL != new_item) + { + LLScrollListCheck* scroll_list_check = dynamic_cast(new_item->getColumn(CHECKBOX_COLUMN)); + if (scroll_list_check) + { + LLCheckBoxCtrl *check = scroll_list_check->getCheckBox(); + check->setCommitCallback(boost::bind(&LLPanelNearByMedia::onCheckItem, this, _1, id)); + } + } + return new_item; +} + +extern char const* PRIORITYToString(LLViewerMediaImpl::EPriority priority); +void LLPanelNearByMedia::updateListItem(LLScrollListItem* item, LLViewerMediaImpl* impl) +{ + std::string item_name; + std::string item_tooltip; + std::string debug_str; + LLPanelNearByMedia::MediaClass media_class = MEDIA_CLASS_ALL; + + getNameAndUrlHelper(impl, item_name, item_tooltip, mEmptyNameString); + // Focused + if (impl->hasFocus()) + { + media_class = MEDIA_CLASS_FOCUSED; + } + // Is attached to another avatar? + else if (impl->isAttachedToAnotherAvatar()) + { + media_class = MEDIA_CLASS_ON_OTHERS; + } + // Outside agent parcel + else if (!impl->isInAgentParcel()) + { + media_class = MEDIA_CLASS_OUTSIDE_PARCEL; + } + else { + // inside parcel + media_class = MEDIA_CLASS_WITHIN_PARCEL; + } + + if(mDebugInfoVisible) + { + debug_str += llformat("%g/", (float)impl->getInterest()); + + // proximity distance is actually distance squared -- display it as straight distance. + debug_str += llformat("%g/", (F32) sqrt(impl->getProximityDistance())); + + // s += llformat("%g/", (float)impl->getCPUUsage()); + // s += llformat("%g/", (float)impl->getApproximateTextureInterest()); + debug_str += llformat("%g/", (float)(NULL == impl->getSomeObject()) ? 0.0 : impl->getSomeObject()->getPixelArea()); + + debug_str += PRIORITYToString(impl->getPriority()); + + if(impl->hasMedia()) + { + debug_str += '@'; + } + else if(impl->isPlayable()) + { + debug_str += '+'; + } + else if(impl->isForcedUnloaded()) + { + debug_str += '!'; + } + } + + updateListItem(item, + item_name, + item_tooltip, + impl->getProximity(), + impl->isMediaDisabled(), + impl->hasMedia(), + impl->isMediaTimeBased() && impl->isMediaPlaying(), + media_class, + debug_str); +} + +void LLPanelNearByMedia::updateListItem(LLScrollListItem* item, + const std::string &item_name, + const std::string &item_tooltip, + S32 proximity, + bool is_disabled, + bool has_media, + bool is_time_based_and_playing, + LLPanelNearByMedia::MediaClass media_class, + const std::string &debug_str) +{ + LLScrollListCell* cell = item->getColumn(PROXIMITY_COLUMN); + if(cell) + { + // since we are forced to sort by text, encode sort order as string + std::string proximity_string = STRINGIZE(proximity); + std::string old_proximity_string = cell->getValue().asString(); + if(proximity_string != old_proximity_string) + { + cell->setValue(proximity_string); + mMediaList->setNeedsSort(true); + } + } + + cell = item->getColumn(CHECKBOX_COLUMN); + if(cell) + { + cell->setValue(!is_disabled); + } + + cell = item->getColumn(VISIBILITY_COLUMN); + if(cell) + { + S32 old_visibility = cell->getValue(); + // *HACK ALERT: force ordering of Media before Audio before the rest of the list + S32 new_visibility = + item->getUUID() == PARCEL_MEDIA_LIST_ITEM_UUID ? 3 + : item->getUUID() == PARCEL_AUDIO_LIST_ITEM_UUID ? 2 + : (has_media) ? 1 + : ((is_disabled) ? 0 + : -1); + cell->setValue(STRINGIZE(new_visibility)); + if (new_visibility != old_visibility) + { + mMediaList->setNeedsSort(true); + } + } + + cell = item->getColumn(NAME_COLUMN); + if(cell) + { + std::string name = item_name; + std::string old_name = cell->getValue().asString(); + if (has_media) + { + name += " " + mPlayingString; + } + if (name != old_name) + { + cell->setValue(name); + } + item->setToolTip(item_tooltip); + + // *TODO: Make these font styles/colors configurable via XUI + U8 font_style = LLFontGL::NORMAL; + LLColor4 cell_color = LLUI::sColorsGroup->getColor("DefaultListText"); + + // Only colorize by class in debug + if (mDebugInfoVisible) + { + switch (media_class) { + case MEDIA_CLASS_FOCUSED: + cell_color = LLColor4::yellow; + break; + case MEDIA_CLASS_ON_OTHERS: + cell_color = LLColor4::red; + break; + case MEDIA_CLASS_OUTSIDE_PARCEL: + cell_color = LLColor4::orange; + break; + case MEDIA_CLASS_WITHIN_PARCEL: + default: + break; + } + } + if (is_disabled) + { + if (mDebugInfoVisible) + { + font_style |= LLFontGL::ITALIC; + cell_color = LLUI::sColorsGroup->getColor("DefaultListText"); + } + else { + // Dim it if it is disabled + cell_color.setAlpha(0.25); + } + } + // Dim it if it isn't "showing" + else if (!has_media) + { + cell_color.setAlpha(0.25); + } + // Bold it if it is time-based media and it is playing + else if (is_time_based_and_playing) + { + if (mDebugInfoVisible) font_style |= LLFontGL::BOLD; + } + cell->setColor(cell_color); + LLScrollListText *text_cell = dynamic_cast (cell); + if (text_cell) + { + text_cell->setFontStyle(font_style); + } + } + + cell = item->getColumn(CLASS_COLUMN); + if(cell) + { + // TODO: clean this up! + cell->setValue(STRINGIZE(media_class)); + } + + if(mDebugInfoVisible) + { + cell = item->getColumn(DEBUG_COLUMN); + if(cell) + { + cell->setValue(debug_str); + } + } +} + +void LLPanelNearByMedia::removeListItem(const LLUUID &id) +{ + if (NULL == mMediaList) return; + + mMediaList->deleteSingleItem(mMediaList->getItemIndex(id)); +} + +void LLPanelNearByMedia::refreshParcelItems() +{ + // + // First add/remove the "fake" items Parcel Media and Parcel Audio. + // These items will have special UUIDs + // PARCEL_MEDIA_LIST_ITEM_UUID + // PARCEL_AUDIO_LIST_ITEM_UUID + // + // Get the filter choice. + const LLSD &choice_llsd = mShowCtrl->getSelectedValue(); + MediaClass choice = (MediaClass)choice_llsd.asInteger(); + // Only show "special parcel items" if "All" or "Within" filter + // (and if media is "enabled") + bool should_include = (choice == MEDIA_CLASS_ALL || choice == MEDIA_CLASS_WITHIN_PARCEL); + + // First Parcel Media: add or remove it as necessary + if (gSavedSettings.getBOOL("AudioStreamingMedia") &&should_include && LLViewerMedia::hasParcelMedia()) + { + // Yes, there is parcel media. + if (NULL == mParcelMediaItem) + { + mParcelMediaItem = addListItem(PARCEL_MEDIA_LIST_ITEM_UUID); + mMediaList->setNeedsSort(true); + } + } + else { + if (NULL != mParcelMediaItem) { + removeListItem(PARCEL_MEDIA_LIST_ITEM_UUID); + mParcelMediaItem = NULL; + mMediaList->setNeedsSort(true); + } + } + + // ... then update it + if (NULL != mParcelMediaItem) + { + std::string name, url, tooltip; + getNameAndUrlHelper(LLViewerParcelMedia::getParcelMedia(), name, url, ""); + if (name.empty() || name == url) + { + tooltip = url; + } + else + { + tooltip = name + " : " + url; + } + LLViewerMediaImpl *impl = LLViewerParcelMedia::getParcelMedia(); + updateListItem(mParcelMediaItem, + mParcelMediaName, + tooltip, + -2, // Proximity closer than anything else, before Parcel Audio + impl == NULL || impl->isMediaDisabled(), + impl != NULL && !LLViewerParcelMedia::getURL().empty(), + impl != NULL && impl->isMediaTimeBased() && impl->isMediaPlaying(), + MEDIA_CLASS_ALL, + "parcel media"); + } + + // Next Parcel Audio: add or remove it as necessary (don't show if disabled in prefs) + if (should_include && LLViewerMedia::hasParcelAudio() && gSavedSettings.getBOOL("AudioStreamingMusic")) + { + // Yes, there is parcel audio. + if (NULL == mParcelAudioItem) + { + mParcelAudioItem = addListItem(PARCEL_AUDIO_LIST_ITEM_UUID); + mMediaList->setNeedsSort(true); + } + } + else { + if (NULL != mParcelAudioItem) { + removeListItem(PARCEL_AUDIO_LIST_ITEM_UUID); + mParcelAudioItem = NULL; + mMediaList->setNeedsSort(true); + } + } + + // ... then update it + if (NULL != mParcelAudioItem) + { + bool is_playing = LLViewerMedia::isParcelAudioPlaying(); + + std::string url; + url = LLViewerMedia::getParcelAudioURL(); + + updateListItem(mParcelAudioItem, + mParcelAudioName, + url, + -1, // Proximity after Parcel Media, but closer than anything else + (!is_playing), + is_playing, + is_playing, + MEDIA_CLASS_ALL, + "parcel audio"); + } +} + +void LLPanelNearByMedia::refreshList() +{ + bool all_items_deleted = false; + + if(!mMediaList) + { + // None of this makes any sense if the media list isn't there. + return; + } + + // Check whether the debug column has been shown/hidden. + bool debug_info_visible = gSavedSettings.getBOOL("MediaPerformanceManagerDebug"); + if(debug_info_visible != mDebugInfoVisible) + { + mDebugInfoVisible = debug_info_visible; + + // Clear all items so the list gets regenerated. + mMediaList->deleteAllItems(); + mParcelAudioItem = NULL; + mParcelMediaItem = NULL; + all_items_deleted = true; + + updateColumns(); + } + + refreshParcelItems(); + + // Get the canonical list from LLViewerMedia + LLViewerMedia::impl_list impls = LLViewerMedia::getPriorityList(); + LLViewerMedia::impl_list::iterator priority_iter; + + U32 enabled_count = 0; + U32 disabled_count = 0; + + // iterate over the impl list, creating rows as necessary. + for(priority_iter = impls.begin(); priority_iter != impls.end(); priority_iter++) + { + LLViewerMediaImpl *impl = *priority_iter; + + // If we just emptied out the list, every flag needs to be reset. + if(all_items_deleted) + { + impl->setInNearbyMediaList(false); + } + + if (!impl->isParcelMedia()) + { + LLUUID media_id = impl->getMediaTextureID(); + S32 proximity = impl->getProximity(); + // This is expensive (i.e. a linear search) -- don't use it here. We now use mInNearbyMediaList instead. + //S32 index = mMediaList->getItemIndex(media_id); + if (proximity < 0 || !shouldShow(impl)) + { + if (impl->getInNearbyMediaList()) + { + // There's a row for this impl -- remove it. + removeListItem(media_id); + impl->setInNearbyMediaList(false); + } + } + else + { + if (!impl->getInNearbyMediaList()) + { + // We don't have a row for this impl -- add one. + addListItem(media_id); + impl->setInNearbyMediaList(true); + } + } + // Update counts + if (impl->isMediaDisabled()) + { + disabled_count++; + } + else { + enabled_count++; + } + } + } + mDisableAllCtrl->setEnabled((gSavedSettings.getBOOL("AudioStreamingMusic") || + gSavedSettings.getBOOL("AudioStreamingMedia")) && + (LLViewerMedia::isAnyMediaShowing() || + LLViewerMedia::isParcelMediaPlaying() || + LLViewerMedia::isParcelAudioPlaying())); + + mEnableAllCtrl->setEnabled( (gSavedSettings.getBOOL("AudioStreamingMusic") || + gSavedSettings.getBOOL("AudioStreamingMedia")) && + (disabled_count > 0 || + // parcel media (if we have it, and it isn't playing, enable "start") + (LLViewerMedia::hasParcelMedia() && ! LLViewerMedia::isParcelMediaPlaying()) || + // parcel audio (if we have it, and it isn't playing, enable "start") + (LLViewerMedia::hasParcelAudio() && ! LLViewerMedia::isParcelAudioPlaying()))); + + // Iterate over the rows in the control, updating ones whose impl exists, and deleting ones whose impl has gone away. + std::vector items = mMediaList->getAllData(); + + for (std::vector::iterator item_it = items.begin(); + item_it != items.end(); + ++item_it) + { + LLScrollListItem* item = (*item_it); + LLUUID row_id = item->getUUID(); + + if (row_id != PARCEL_MEDIA_LIST_ITEM_UUID && + row_id != PARCEL_AUDIO_LIST_ITEM_UUID) + { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(row_id); + if(impl) + { + updateListItem(item, impl); + } + else + { + // This item's impl has been deleted -- remove the row. + // Removing the row won't throw off our iteration, since we have a local copy of the array. + // We just need to make sure we don't access this item after the delete. + removeListItem(row_id); + } + } + } + + // Set the selection to whatever media impl the media focus/hover is on. + // This is an experiment, and can be removed by ifdefing out these 4 lines. + LLUUID media_target = LLViewerMediaFocus::getInstance()->getControlsMediaID(); + if(media_target.notNull()) + { + mMediaList->selectByID(media_target); + } +} + +void LLPanelNearByMedia::updateColumns() +{ + if (!mDebugInfoVisible) + { + if (mMediaList->getColumn(CHECKBOX_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(VISIBILITY_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(PROXIMITY_COLUMN)) mMediaList->getColumn(PROXIMITY_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(CLASS_COLUMN)) mMediaList->getColumn(CLASS_COLUMN)->setWidth(-1); + if (mMediaList->getColumn(DEBUG_COLUMN)) mMediaList->getColumn(DEBUG_COLUMN)->setWidth(-1); + } + else { + if (mMediaList->getColumn(CHECKBOX_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(20); + if (mMediaList->getColumn(VISIBILITY_COLUMN)) mMediaList->getColumn(VISIBILITY_COLUMN)->setWidth(20); + if (mMediaList->getColumn(PROXIMITY_COLUMN)) mMediaList->getColumn(PROXIMITY_COLUMN)->setWidth(30); + if (mMediaList->getColumn(CLASS_COLUMN)) mMediaList->getColumn(CLASS_COLUMN)->setWidth(20); + if (mMediaList->getColumn(DEBUG_COLUMN)) mMediaList->getColumn(DEBUG_COLUMN)->setWidth(200); + } +} + +void LLPanelNearByMedia::onClickEnableAll() +{ + LLViewerMedia::setAllMediaEnabled(true); +} + +void LLPanelNearByMedia::onClickDisableAll() +{ + LLViewerMedia::setAllMediaEnabled(false); +} + +void LLPanelNearByMedia::onClickEnableParcelMedia() +{ + if ( ! LLViewerMedia::isParcelMediaPlaying() ) + { + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); + } +} + +void LLPanelNearByMedia::onClickDisableParcelMedia() +{ + // This actually unloads the impl, as opposed to "stop"ping the media + LLViewerParcelMedia::stop(); +} + +void LLPanelNearByMedia::onCheckItem(LLUICtrl* ctrl, const LLUUID &row_id) +{ + LLCheckBoxCtrl* check = static_cast(ctrl); + + setDisabled(row_id, ! check->getValue()); +} + +bool LLPanelNearByMedia::setDisabled(const LLUUID &row_id, bool disabled) +{ + if (row_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + if (disabled) + { + onClickParcelAudioStop(); + } + else + { + onClickParcelAudioPlay(); + } + return true; + } + else if (row_id == PARCEL_MEDIA_LIST_ITEM_UUID) + { + if (disabled) + { + onClickDisableParcelMedia(); + } + else + { + onClickEnableParcelMedia(); + } + return true; + } + else { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(row_id); + if(impl) + { + impl->setDisabled(disabled, true); + return true; + } + } + return false; +} + +//static +void LLPanelNearByMedia::onZoomMedia(void* user_data) +{ + LLPanelNearByMedia* panelp = (LLPanelNearByMedia*)user_data; + LLUUID media_id = panelp->mMediaList->getValue().asUUID(); + + LLViewerMediaFocus::getInstance()->focusZoomOnMedia(media_id); +} + +void LLPanelNearByMedia::onClickParcelMediaPlay() +{ + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); +} + +void LLPanelNearByMedia::onClickParcelMediaStop() +{ + if (LLViewerParcelMedia::getParcelMedia()) + { + // This stops the media playing, as opposed to unloading it like + // LLViewerParcelMedia::stop() does + LLViewerParcelMedia::getParcelMedia()->stop(); + } +} + +void LLPanelNearByMedia::onClickParcelMediaPause() +{ + LLViewerParcelMedia::pause(); +} + +void LLPanelNearByMedia::onClickParcelAudioPlay() +{ + // User *explicitly* started the internet stream, so keep the stream + // playing and updated as they cross to other parcels etc. + mParcelAudioAutoStart = true; + if (!gAudiop) + return; + + if (LLAudioEngine::AUDIO_PAUSED == gAudiop->isInternetStreamPlaying()) + { + // 'false' means unpause + gAudiop->pauseInternetStream(false); + } + else + { + gAudiop->startInternetStream(LLViewerMedia::getParcelAudioURL()); + } +} + +void LLPanelNearByMedia::onClickParcelAudioStop() +{ + // User *explicitly* stopped the internet stream, so don't + // re-start audio when i.e. they move to another parcel, until + // they explicitly start it again. + mParcelAudioAutoStart = false; + if (!gAudiop) + return; + + gAudiop->stopInternetStream(); +} + +void LLPanelNearByMedia::onClickParcelAudioPause() +{ + if (!gAudiop) + return; + + // 'true' means pause + gAudiop->pauseInternetStream(true); +} + +bool LLPanelNearByMedia::shouldShow(LLViewerMediaImpl* impl) +{ + const LLSD &choice_llsd = mShowCtrl->getSelectedValue(); + MediaClass choice = (MediaClass)choice_llsd.asInteger(); + + switch (choice) + { + case MEDIA_CLASS_ALL: + return true; + break; + case MEDIA_CLASS_WITHIN_PARCEL: + return impl->isInAgentParcel(); + break; + case MEDIA_CLASS_OUTSIDE_PARCEL: + return ! impl->isInAgentParcel(); + break; + case MEDIA_CLASS_ON_OTHERS: + return impl->isAttachedToAnotherAvatar(); + break; + default: + break; + } + return true; +} + +void LLPanelNearByMedia::onAdvancedButtonClick() +{ + // bring up the prefs floater + + LLFloaterPreference::show(NULL); + LLFloaterPreference* prefsfloater = LLFloaterPreference::sInstance; + + if (prefsfloater) + { + // grab the 'audio' panel from the preferences floater and + // bring it the front! + LLTabContainer* tabcontainer = prefsfloater->getChild("pref core"); + if (tabcontainer) + { + tabcontainer->selectTabByName("Media panel"); + } + } +} + +void LLPanelNearByMedia::onMoreLess() +{ + bool is_more = !mStandalonePanel || getChild("more_btn")->getToggleState(); + mNearbyMediaPanel->setVisible(is_more); + + // enable resizing when expanded + if(mStandalonePanel) + getChildView("resizebar_bottom")->setEnabled(is_more); + + LLRect new_rect = is_more ? mMoreRect : mLessRect; + new_rect.translate(getRect().mRight - new_rect.mRight, getRect().mTop - new_rect.mTop); + + setShape(new_rect); + + getChild("more_btn")->setVisible(mStandalonePanel); +} + +void LLPanelNearByMedia::updateControls() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + if (!LLViewerMedia::hasParcelAudio() || !gSavedSettings.getBOOL("AudioStreamingMusic")) + { + // disable controls if audio streaming music is disabled from preference + showDisabledControls(); + } + else { + showTimeBasedControls(LLViewerMedia::isParcelAudioPlaying(), + false, // include_zoom + false, // is_zoomed + gSavedSettings.getBOOL("MuteMusic"), + gSavedSettings.getF32("AudioLevelMusic") ); + } + } + else if (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) + { + if (!LLViewerMedia::hasParcelMedia() || !gSavedSettings.getBOOL("AudioStreamingMedia")) + { + // disable controls if audio streaming media is disabled from preference + showDisabledControls(); + } + else { + LLViewerMediaImpl* impl = LLViewerParcelMedia::getParcelMedia(); + if (NULL == impl) + { + // Just means it hasn't started yet + showBasicControls(false, false, false, false, 0); + } + else if (impl->isMediaTimeBased()) + { + showTimeBasedControls(impl->isMediaPlaying(), + false, // include_zoom + false, // is_zoomed + impl->getVolume() == 0.0, + impl->getVolume() ); + } + else { + // non-time-based parcel media + showBasicControls(LLViewerMedia::isParcelMediaPlaying(), + false, + false, + impl->getVolume() == 0.0, + impl->getVolume()); + } + } + } + else { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + + if (NULL == impl || !gSavedSettings.getBOOL("AudioStreamingMedia")) + { + showDisabledControls(); + } + else { + if (impl->isMediaTimeBased()) + { + showTimeBasedControls(impl->isMediaPlaying(), + ! impl->isParcelMedia(), // include_zoom + LLViewerMediaFocus::getInstance()->isZoomed(), + impl->getVolume() == 0.0, + impl->getVolume()); + } + else { + showBasicControls(!impl->isMediaDisabled(), + ! impl->isParcelMedia(), // include_zoom + LLViewerMediaFocus::getInstance()->isZoomed(), + impl->getVolume() == 0.0, + impl->getVolume()); + } + } + } +} + +void LLPanelNearByMedia::showBasicControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume) +{ + mStopCtrl->setVisible(playing); + mPlayCtrl->setVisible(!playing); + mPauseCtrl->setVisible(false); + mVolumeSliderCtrl->setVisible(true); + mMuteCtrl->setVisible(true); + mMuteBtn->setValue(muted); + mVolumeSlider->setValue(volume); + mZoomCtrl->setVisible(include_zoom && !is_zoomed); + mUnzoomCtrl->setVisible(include_zoom && is_zoomed); + mStopCtrl->setEnabled(true); + mZoomCtrl->setEnabled(true); +} + +void LLPanelNearByMedia::showTimeBasedControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume) +{ + mStopCtrl->setVisible(true); + mPlayCtrl->setVisible(!playing); + mPauseCtrl->setVisible(playing); + mMuteCtrl->setVisible(true); + mVolumeSliderCtrl->setVisible(true); + mZoomCtrl->setVisible(include_zoom); + mZoomCtrl->setVisible(include_zoom && !is_zoomed); + mUnzoomCtrl->setVisible(include_zoom && is_zoomed); + mStopCtrl->setEnabled(true); + mZoomCtrl->setEnabled(true); + mMuteBtn->setValue(muted); + mVolumeSlider->setValue(volume); +} + +void LLPanelNearByMedia::showDisabledControls() +{ + mStopCtrl->setVisible(true); + mPlayCtrl->setVisible(false); + mPauseCtrl->setVisible(false); + mMuteCtrl->setVisible(false); + mVolumeSliderCtrl->setVisible(false); + mZoomCtrl->setVisible(true); + mUnzoomCtrl->setVisible(false); + mStopCtrl->setEnabled(false); + mZoomCtrl->setEnabled(false); +} + +void LLPanelNearByMedia::onClickSelectedMediaStop() +{ + setDisabled(mMediaList->getValue().asUUID(), true); +} + +void LLPanelNearByMedia::onClickSelectedMediaPlay() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + + // First enable it + setDisabled(selected_media_id, false); + + // Special code to make play "unpause" if time-based and playing + if (selected_media_id != PARCEL_AUDIO_LIST_ITEM_UUID) + { + LLViewerMediaImpl *impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ? + ((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl) + { + if (impl->isMediaTimeBased() && impl->isMediaPaused()) + { + // Aha! It's really time-based media that's paused, so unpause + impl->play(); + return; + } + else if (impl->isParcelMedia()) + { + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); + } + } + } +} + +void LLPanelNearByMedia::onClickSelectedMediaPause() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + onClickParcelAudioPause(); + } + else if (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) + { + onClickParcelMediaPause(); + } + else { + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl && impl->isMediaTimeBased() && impl->isMediaPlaying()) + { + impl->pause(); + } + } +} + +void LLPanelNearByMedia::onClickSelectedMediaMute() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + gSavedSettings.setBOOL("MuteMusic", mMuteBtn->getValue()); + } + else { + LLViewerMediaImpl* impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ? + ((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl) + { + F32 volume = impl->getVolume(); + if(volume > 0.0) + { + impl->setVolume(0.0); + } + else if (mVolumeSlider->getValueF32() == 0.0) + { + impl->setVolume(1.0); + mVolumeSlider->setValue(1.0); + } + else + { + impl->setVolume(mVolumeSlider->getValueF32()); + } + } + } +} + +void LLPanelNearByMedia::onCommitSelectedMediaVolume() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID) + { + F32 vol = mVolumeSlider->getValueF32(); + gSavedSettings.setF32("AudioLevelMusic", vol); + } + else { + LLViewerMediaImpl* impl = (selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) ? + ((LLViewerMediaImpl*)LLViewerParcelMedia::getParcelMedia()) : LLViewerMedia::getMediaImplFromTextureID(selected_media_id); + if (NULL != impl) + { + impl->setVolume(mVolumeSlider->getValueF32()); + } + } +} + +void LLPanelNearByMedia::onClickSelectedMediaZoom() +{ + LLUUID selected_media_id = mMediaList->getValue().asUUID(); + if (selected_media_id == PARCEL_AUDIO_LIST_ITEM_UUID || selected_media_id == PARCEL_MEDIA_LIST_ITEM_UUID) + return; + LLViewerMediaFocus::getInstance()->focusZoomOnMedia(selected_media_id); +} + +void LLPanelNearByMedia::onClickSelectedMediaUnzoom() +{ + LLViewerMediaFocus::getInstance()->unZoom(); +} + + +// static +void LLPanelNearByMedia::getNameAndUrlHelper(LLViewerMediaImpl* impl, std::string& name, std::string & url, const std::string &defaultName) +{ + if (NULL == impl) return; + + name = impl->getName(); + url = impl->getCurrentMediaURL(); // This is the URL the media impl actually has loaded + if (url.empty()) + { + url = impl->getMediaEntryURL(); // This is the current URL from the media data + } + if (url.empty()) + { + url = impl->getHomeURL(); // This is the home URL from the media data + } + if (name.empty()) + { + name = url; + } + if (name.empty()) + { + name = defaultName; + } +} + +// static +void* createNearbyMediaPanel(void* userdata) +{ + return new LLPanelNearByMedia(false); +} + +LLFloaterNearbyMedia::LLFloaterNearbyMedia() +{ + mFactoryMap["nearby_media"] = LLCallbackMap(createNearbyMediaPanel, this); + LLUICtrlFactory::getInstance()->buildFloater(this,"floater_nearby_media.xml",&mFactoryMap,false); +} + +// static +void LLFloaterNearbyMedia::updateClass() +{ + if(gSavedSettings.getBOOL("ShowNearbyMediaFloater")) + LLFloaterNearbyMedia::getInstance()->open(); + else if(LLFloaterNearbyMedia::instanceExists()) + LLFloaterNearbyMedia::getInstance()->close(); +} + +void LLFloaterNearbyMedia::onClose(bool app_quitting) +{ + if(!app_quitting) + gSavedSettings.setBOOL("ShowNearbyMediaFloater", false); + LLFloater::onClose(app_quitting); +} + +void LLFloaterNearbyMedia::onOpen() +{ + //Mess around with control rect to not change unless the user actually tweaked it.. + LLRect rect = gSavedSettings.getRect("FloaterNearbyMediaRect"); + if(!rect.isEmpty()) + { + setRectControl("FloaterNearbyMediaRect"); + applyRectControl(); + } + else + { + const LLRect media_rect = gOverlayBar->mMediaRemote->calcScreenRect(); + setOrigin(media_rect.mLeft - getRect().getWidth(), media_rect.mBottom + gOverlayBar->mChatBar->getRect().getHeight()); + } + gSavedSettings.setBOOL("ShowNearbyMediaFloater", true); +} + +// virtual +void LLFloaterNearbyMedia::handleReshape(const LLRect& new_rect, bool by_user) +{ + if(by_user && getRectControl().empty()) + { + setRectControl("FloaterNearbyMediaRect"); + } + LLFloater::handleReshape(new_rect, by_user); +} + diff --git a/indra/newview/llpanelnearbymedia.h b/indra/newview/llpanelnearbymedia.h new file mode 100644 index 000000000..e6d0a4d9b --- /dev/null +++ b/indra/newview/llpanelnearbymedia.h @@ -0,0 +1,198 @@ +/** + * @file llpanelnearbymedia.h + * @brief Management interface for muting and controlling nearby media + * + * $LicenseInfo:firstyear=2005&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$ + */ + +#ifndef LL_LLPANELNEARBYMEDIA_H +#define LL_LLPANELNEARBYMEDIA_H + +#include "llpanel.h" +#include "llfloater.h" +#include "llsingleton.h" + +class LLPanelNearbyMedia; +class LLButton; +class LLScrollListCtrl; +class LLSlider; +class LLSliderCtrl; +class LLCheckBoxCtrl; +class LLTextBox; +class LLComboBox; +class LLViewerMediaImpl; + +class LLPanelNearByMedia : public LLPanel +{ +public: + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void draw(); + /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); + /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ void onTopLost(); + /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); + /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + + // this is part of the nearby media *dialog* so we can track whether + // the user *implicitly* wants audio on or off via their *explicit* + // interaction with our buttons. + bool getParcelAudioAutoStart(); + + // callback for when the auto play media preference changes + // to update mParcelAudioAutoStart + void handleMediaAutoPlayChanged(const LLSD& newvalue); + + LLPanelNearByMedia(bool standalone_panel = true); + virtual ~LLPanelNearByMedia(); + +private: + + enum ColumnIndex { + CHECKBOX_COLUMN = 0, + PROXIMITY_COLUMN = 1, + VISIBILITY_COLUMN = 2, + CLASS_COLUMN = 3, + NAME_COLUMN = 4, + DEBUG_COLUMN = 5 + }; + + // Media "class" enumeration + enum MediaClass { + MEDIA_CLASS_ALL = 0, + MEDIA_CLASS_FOCUSED = 1, + MEDIA_CLASS_WITHIN_PARCEL = 2, + MEDIA_CLASS_OUTSIDE_PARCEL = 3, + MEDIA_CLASS_ON_OTHERS = 4 + }; + + // Add/remove an LLViewerMediaImpl to/from the list + LLScrollListItem* addListItem(const LLUUID &id); + void updateListItem(LLScrollListItem* item, LLViewerMediaImpl* impl); + void updateListItem(LLScrollListItem* item, + const std::string &item_name, + const std::string &item_tooltip, + S32 proximity, + bool is_disabled, + bool has_media, + bool is_time_based_and_playing, + MediaClass media_class, + const std::string &debug_str); + void removeListItem(const LLUUID &id); + + // Refresh the list in the UI + void refreshList(); + + void refreshParcelItems(); + + // UI Callbacks + void onClickEnableAll(); + void onClickDisableAll(); + void onClickEnableParcelMedia(); + void onClickDisableParcelMedia(); + void onClickMuteParcelMedia(); + void onParcelMediaVolumeSlider(); + void onClickParcelMediaPlay(); + void onClickParcelMediaStop(); + void onClickParcelMediaPause(); + void onClickParcelAudioPlay(); + void onClickParcelAudioStop(); + void onClickParcelAudioPause(); + void onCheckAutoPlay(); + void onAdvancedButtonClick(); + void onMoreLess(); + + void onCheckItem(LLUICtrl* ctrl, const LLUUID &row_id); + + static void onZoomMedia(void* user_data); + +private: + bool setDisabled(const LLUUID &id, bool disabled); + + static void getNameAndUrlHelper(LLViewerMediaImpl* impl, std::string& name, std::string & url, const std::string &defaultName); + + void updateColumns(); + + bool shouldShow(LLViewerMediaImpl* impl); + + void showBasicControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume); + void showTimeBasedControls(bool playing, bool include_zoom, bool is_zoomed, bool muted, F32 volume); + void showDisabledControls(); + void updateControls(); + + void onClickSelectedMediaStop(); + void onClickSelectedMediaPlay(); + void onClickSelectedMediaPause(); + void onClickSelectedMediaMute(); + void onCommitSelectedMediaVolume(); + void onClickSelectedMediaZoom(); + void onClickSelectedMediaUnzoom(); + + LLUICtrl* mNearbyMediaPanel; + LLScrollListCtrl* mMediaList; + LLUICtrl* mEnableAllCtrl; + LLUICtrl* mDisableAllCtrl; + LLComboBox* mShowCtrl; + + // Dynamic (selection-dependent) controls + LLUICtrl* mStopCtrl; + LLUICtrl* mPlayCtrl; + LLUICtrl* mPauseCtrl; + LLUICtrl* mMuteCtrl; + LLUICtrl* mVolumeSliderCtrl; + LLUICtrl* mZoomCtrl; + LLUICtrl* mUnzoomCtrl; + LLSlider* mVolumeSlider; + LLButton* mMuteBtn; + + bool mAllMediaDisabled; + bool mDebugInfoVisible; + bool mParcelAudioAutoStart; + std::string mEmptyNameString; + std::string mPlayingString; + std::string mParcelMediaName; + std::string mParcelAudioName; + + LLRect mMoreRect; + LLRect mLessRect; + LLFrameTimer mHoverTimer; + LLScrollListItem* mParcelMediaItem; + LLScrollListItem* mParcelAudioItem; + + bool mStandalonePanel; +}; + +class LLFloaterNearbyMedia : public LLFloater, public LLSingleton +{ +public: + LLFloaterNearbyMedia(); + + static void updateClass(); + + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ void onOpen(); + + virtual void handleReshape(const LLRect& new_rect, bool by_user); +}; + +#endif // LL_LLPANELNEARBYMEDIA_H diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index 40a79bcef..326fb952d 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -149,8 +149,7 @@ BOOL LLPanelPermissions::postBuild() childSetCommitCallback("clickaction",LLPanelPermissions::onCommitClickAction,this); childSetCommitCallback("search_check",LLPanelPermissions::onCommitIncludeInSearch,this); - mLabelGroupName = new LLNameBox("Group Name", getChild("Group Name Proxy")->getRect()); - addChild(mLabelGroupName); + mLabelGroupName = getChild("Group Name Proxy"); if (!gHippoGridManager->getCurrentGrid()->isSecondLife()) LFSimFeatureHandler::instance().setSupportsExportCallback(boost::bind(&LLPanelPermissions::refresh, this)); diff --git a/indra/newview/llpanelprimmediacontrols.cpp b/indra/newview/llpanelprimmediacontrols.cpp new file mode 100644 index 000000000..3ebc38710 --- /dev/null +++ b/indra/newview/llpanelprimmediacontrols.cpp @@ -0,0 +1,1358 @@ +/** + * @file llpanelmsgs.cpp + * @brief Message popup preferences panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-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 "llagent.h" +#include "llagentcamera.h" +#include "llparcel.h" +#include "llpanel.h" +#include "llselectmgr.h" +#include "llmediaentry.h" +#include "llrender.h" +#include "lldrawable.h" +#include "llviewerwindow.h" +#include "lluictrlfactory.h" +#include "llbutton.h" +#include "llface.h" +#include "llcombobox.h" +#include "lllayoutstack.h" +#include "llslider.h" +#include "llhudview.h" +#include "lliconctrl.h" +#include "lltoolpie.h" +#include "llviewercamera.h" +#include "llviewerobjectlist.h" +#include "llpanelprimmediacontrols.h" +#include "llpluginclassmedia.h" +#include "llprogressbar.h" +#include "llsliderctrl.h" +#include "llstring.h" +#include "llviewercontrol.h" +#include "llviewerdisplay.h" +#include "llviewerparcelmgr.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" +#include "llvovolume.h" +#include "llweb.h" +#include "llwindow.h" +#include "llfloatertools.h" // to enable hide if build tools are up +#include "llvector4a.h" +#include "lllayoutstack.h" + +// Functions pulled from pipeline.cpp +glh::matrix4f glh_get_current_modelview(); +glh::matrix4f glh_get_current_projection(); +// Functions pulled from llviewerdisplay.cpp +bool get_hud_matrices(glh::matrix4f &proj, glh::matrix4f &model); + +// Warning: make sure these two match! +const LLPanelPrimMediaControls::EZoomLevel LLPanelPrimMediaControls::kZoomLevels[] = { ZOOM_NONE, ZOOM_MEDIUM }; +const int LLPanelPrimMediaControls::kNumZoomLevels = 2; + +// +// LLPanelPrimMediaControls +// + +LLPanelPrimMediaControls::LLPanelPrimMediaControls() : + mAlpha(1.f), + mCurrentURL(""), + mPreviousURL(""), + mPauseFadeout(false), + mUpdateSlider(true), + mClearFaceOnFade(false), + mCurrentRate(0.0), + mMovieDuration(0.0), + mTargetObjectID(LLUUID::null), + mTargetObjectFace(0), + mTargetImplID(LLUUID::null), + mTargetObjectNormal(LLVector3::zero), + mZoomObjectID(LLUUID::null), + mZoomObjectFace(0), + mVolumeSliderVisible(0), + mHideImmediately(false) +{ + mCommitCallbackRegistrar.add("MediaCtrl.Close", boost::bind(&LLPanelPrimMediaControls::onClickClose, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Back", boost::bind(&LLPanelPrimMediaControls::onClickBack, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Forward", boost::bind(&LLPanelPrimMediaControls::onClickForward, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Home", boost::bind(&LLPanelPrimMediaControls::onClickHome, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Stop", boost::bind(&LLPanelPrimMediaControls::onClickStop, this)); + mCommitCallbackRegistrar.add("MediaCtrl.MediaStop", boost::bind(&LLPanelPrimMediaControls::onClickMediaStop, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Reload", boost::bind(&LLPanelPrimMediaControls::onClickReload, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Play", boost::bind(&LLPanelPrimMediaControls::onClickPlay, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Pause", boost::bind(&LLPanelPrimMediaControls::onClickPause, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Open", boost::bind(&LLPanelPrimMediaControls::onClickOpen, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Zoom", boost::bind(&LLPanelPrimMediaControls::onClickZoom, this)); + mCommitCallbackRegistrar.add("MediaCtrl.CommitURL", boost::bind(&LLPanelPrimMediaControls::onCommitURL, this)); + mCommitCallbackRegistrar.add("MediaCtrl.JumpProgress", boost::bind(&LLPanelPrimMediaControls::onCommitSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeUp", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeUp, this)); + mCommitCallbackRegistrar.add("MediaCtrl.CommitVolumeDown", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeDown, this)); + mCommitCallbackRegistrar.add("MediaCtrl.Volume", boost::bind(&LLPanelPrimMediaControls::onCommitVolumeSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.ToggleMute", boost::bind(&LLPanelPrimMediaControls::onToggleMute, this)); + mCommitCallbackRegistrar.add("MediaCtrl.ShowVolumeSlider", boost::bind(&LLPanelPrimMediaControls::showVolumeSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.HideVolumeSlider", boost::bind(&LLPanelPrimMediaControls::hideVolumeSlider, this)); + mCommitCallbackRegistrar.add("MediaCtrl.SkipBack", boost::bind(&LLPanelPrimMediaControls::onClickSkipBack, this)); + mCommitCallbackRegistrar.add("MediaCtrl.SkipForward", boost::bind(&LLPanelPrimMediaControls::onClickSkipForward, this)); + + //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_media_hud.xml"); + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_prim_media_controls.xml"); + mInactivityTimer.reset(); + mFadeTimer.stop(); + mCurrentZoom = ZOOM_NONE; + mScrollState = SCROLL_NONE; + + mPanelHandle.bind(this); + + mInactiveTimeout = gSavedSettings.getF32("MediaControlTimeout"); + mControlFadeTime = gSavedSettings.getF32("MediaControlFadeTime"); +} + +LLPanelPrimMediaControls::~LLPanelPrimMediaControls() +{ +} + +BOOL LLPanelPrimMediaControls::postBuild() +{ + mMediaRegion = getChild("media_region"); + mBackCtrl = getChild("back"); + mFwdCtrl = getChild("fwd"); + mReloadCtrl = getChild("reload"); + mPlayCtrl = getChild("play"); + mPauseCtrl = getChild("pause"); + mStopCtrl = getChild("stop"); + mMediaStopCtrl = getChild("media_stop"); + mHomeCtrl = getChild("home"); + mUnzoomCtrl = getChild("close"); // This is actually "unzoom" + mOpenCtrl = getChild("new_window"); + mZoomCtrl = getChild("zoom_frame"); + mMediaProgressPanel = getChild("media_progress_indicator"); + mMediaProgressBar = getChild("media_progress_bar"); + mMediaAddressCtrl = getChild("media_address"); + mMediaAddress = getChild("media_address_url"); + mMediaPlaySliderPanel = getChild("media_play_position"); + mMediaPlaySliderCtrl = getChild("media_play_slider"); + mSkipFwdCtrl = getChild("skip_forward"); + mSkipBackCtrl = getChild("skip_back"); + mVolumeCtrl = getChild("media_volume"); + mMuteBtn = getChild("media_mute_button"); + mVolumeSliderCtrl = getChild("volume_slider"); + mWhitelistIcon = getChild("media_whitelist_flag"); + mSecureLockIcon = getChild("media_secure_lock_flag"); + mMediaControlsStack = getChild("media_controls"); + mLeftBookend = getChild("left_bookend"); + mRightBookend = getChild("right_bookend"); + mBackgroundImage = LLUI::getUIImage(getString("control_background_image_name")); + mVolumeSliderBackgroundImage = LLUI::getUIImage(getString("control_background_image_name")); + LLStringUtil::convertToF32(getString("skip_step"), mSkipStep); + LLStringUtil::convertToS32(getString("min_width"), mMinWidth); + LLStringUtil::convertToS32(getString("min_height"), mMinHeight); + LLStringUtil::convertToF32(getString("zoom_near_padding"), mZoomNearPadding); + LLStringUtil::convertToF32(getString("zoom_medium_padding"), mZoomMediumPadding); + LLStringUtil::convertToF32(getString("zoom_far_padding"), mZoomFarPadding); + LLStringUtil::convertToS32(getString("top_world_view_avoid_zone"), mTopWorldViewAvoidZone); + + // These are currently removed...but getChild creates a "dummy" widget. + // This class handles them missing. + mMediaPanelScroll = findChild("media_panel_scroll"); + mScrollUpCtrl = findChild("scrollup"); + mScrollLeftCtrl = findChild("scrollleft"); + mScrollRightCtrl = findChild("scrollright"); + mScrollDownCtrl = findChild("scrolldown"); + + if (mScrollUpCtrl) + { + mScrollUpCtrl->setClickedCallback(onScrollUp, this); + mScrollUpCtrl->setHeldDownCallback(onScrollUpHeld, this); + mScrollUpCtrl->setMouseUpCallback(onScrollStop, this); + } + if (mScrollLeftCtrl) + { + mScrollLeftCtrl->setClickedCallback(onScrollLeft, this); + mScrollLeftCtrl->setHeldDownCallback(onScrollLeftHeld, this); + mScrollLeftCtrl->setMouseUpCallback(onScrollStop, this); + } + if (mScrollRightCtrl) + { + mScrollRightCtrl->setClickedCallback(onScrollRight, this); + mScrollRightCtrl->setHeldDownCallback(onScrollRightHeld, this); + mScrollRightCtrl->setMouseUpCallback(onScrollStop, this); + } + if (mScrollDownCtrl) + { + mScrollDownCtrl->setClickedCallback(onScrollDown, this); + mScrollDownCtrl->setHeldDownCallback(onScrollDownHeld, this); + mScrollDownCtrl->setMouseUpCallback(onScrollStop, this); + } + + mMediaAddress->setFocusReceivedCallback(boost::bind(&LLPanelPrimMediaControls::onInputURL, _1, this )); + + gAgent.setMouselookModeInCallback(boost::bind(&LLPanelPrimMediaControls::onMouselookModeIn, this)); + + mCurrentZoom = ZOOM_NONE; + // clicks on buttons do not remove keyboard focus from media + setIsChrome(TRUE); + return TRUE; +} + +void LLPanelPrimMediaControls::setMediaFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull() && objectp.notNull()) + { + LLUUID prev_id = mTargetImplID; + mTargetImplID = media_impl->getMediaTextureID(); + mTargetObjectID = objectp->getID(); + mTargetObjectFace = face; + mTargetObjectNormal = pick_normal; + mClearFaceOnFade = false; + + if (prev_id != mTargetImplID) + mVolumeSliderCtrl->setValue(media_impl->getVolume()); + } + else + { + // This happens on a timer now. +// mTargetImplID = LLUUID::null; +// mTargetObjectID = LLUUID::null; +// mTargetObjectFace = 0; + mClearFaceOnFade = true; + } + + updateShape(); +} + +void LLPanelPrimMediaControls::focusOnTarget() +{ + // Sets the media focus to the current target of the LLPanelPrimMediaControls. + // This is how we transition from hover to focus when the user clicks on a control. + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if(media_impl) + { + if(!media_impl->hasFocus()) + { + // The current target doesn't have media focus -- focus on it. + LLViewerObject* objectp = getTargetObject(); + LLViewerMediaFocus::getInstance()->setFocusFace(objectp, mTargetObjectFace, media_impl, mTargetObjectNormal); + } + } +} + +LLViewerMediaImpl* LLPanelPrimMediaControls::getTargetMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mTargetImplID); +} + +LLViewerObject* LLPanelPrimMediaControls::getTargetObject() +{ + return gObjectList.findObject(mTargetObjectID); +} + +LLPluginClassMedia* LLPanelPrimMediaControls::getTargetMediaPlugin() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if(impl && impl->hasMedia()) + { + return impl->getMediaPlugin(); + } + + return NULL; +} + +void LLPanelPrimMediaControls::updateShape() +{ + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + LLViewerObject* objectp = getTargetObject(); + + if(!media_impl || gFloaterTools->getVisible()) + { + setVisible(FALSE); + return; + } + + LLPluginClassMedia* media_plugin = NULL; + if(media_impl->hasMedia()) + { + media_plugin = media_impl->getMediaPlugin(); + } + + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + bool can_navigate = parcel->getMediaAllowNavigate(); + bool enabled = false; + bool is_zoomed = (mCurrentZoom != ZOOM_NONE) && (mTargetObjectID == mZoomObjectID) && (mTargetObjectFace == mZoomObjectFace); + // There is no such thing as "has_focus" being different from normal controls set + // anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus' + // to 'true' (or, actually, we use a setting) + bool has_focus = (gSavedSettings.getBOOL("PrimMediaControlsUseHoverControlSet")) ? media_impl->hasFocus() : true; + setVisible(enabled); + + if (objectp) + { + bool mini_controls = false; + LLMediaEntry *media_data = objectp->getTE(mTargetObjectFace)->getMediaData(); + if (media_data && NULL != dynamic_cast(objectp)) + { + // Don't show the media controls if we do not have permissions + enabled = dynamic_cast(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL); + mini_controls = (LLMediaEntry::MINI == media_data->getControls()); + } + const bool is_hud = objectp->isHUDAttachment(); + + // + // Set the state of the buttons + // + + // XXX RSP: TODO: FIXME: clean this up so that it is clearer what mode we are in, + // and that only the proper controls get made visible/enabled according to that mode. + mBackCtrl->setVisible(has_focus); + mFwdCtrl->setVisible(has_focus); + mReloadCtrl->setVisible(has_focus); + mStopCtrl->setVisible(false); + mHomeCtrl->setVisible(has_focus); + mZoomCtrl->setVisible(!is_zoomed); + mUnzoomCtrl->setVisible(is_zoomed); + mOpenCtrl->setVisible(true); + mMediaAddressCtrl->setVisible(has_focus && !mini_controls); + mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); + mVolumeCtrl->setVisible(false); + + mWhitelistIcon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); + // Disable zoom if HUD + mZoomCtrl->setEnabled(!is_hud); + mUnzoomCtrl->setEnabled(!is_hud); + mSecureLockIcon->setVisible(false); + mCurrentURL = media_impl->getCurrentMediaURL(); + + mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate); + mFwdCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateForward() && can_navigate); + mStopCtrl->setEnabled(has_focus && can_navigate); + mHomeCtrl->setEnabled(has_focus && can_navigate); + LLPluginClassMediaOwner::EMediaStatus result = ((media_impl != NULL) && media_impl->hasMedia()) ? media_plugin->getStatus() : LLPluginClassMediaOwner::MEDIA_NONE; + + mVolumeCtrl->setVisible(has_focus); + mVolumeCtrl->setEnabled(has_focus); + mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); + mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); + + if(media_plugin && media_plugin->pluginSupportsMediaTime()) + { + mReloadCtrl->setEnabled(false); + mReloadCtrl->setVisible(false); + mMediaStopCtrl->setVisible(has_focus); + mHomeCtrl->setVisible(has_focus); + mBackCtrl->setVisible(false); + mFwdCtrl->setVisible(false); + mMediaAddressCtrl->setVisible(false); + mMediaAddressCtrl->setEnabled(false); + mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); + mMediaPlaySliderPanel->setEnabled(has_focus && !mini_controls); + mSkipFwdCtrl->setVisible(has_focus && !mini_controls); + mSkipFwdCtrl->setEnabled(has_focus && !mini_controls); + mSkipBackCtrl->setVisible(has_focus && !mini_controls); + mSkipBackCtrl->setEnabled(has_focus && !mini_controls); + + mVolumeCtrl->setVisible(has_focus); + mVolumeCtrl->setEnabled(has_focus); + mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); + mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); + + mWhitelistIcon->setVisible(false); + mSecureLockIcon->setVisible(false); + if (mMediaPanelScroll) + { + mMediaPanelScroll->setVisible(false); + mScrollUpCtrl->setVisible(false); + mScrollDownCtrl->setVisible(false); + mScrollRightCtrl->setVisible(false); + mScrollDownCtrl->setVisible(false); + } + + F32 volume = media_impl->getVolume(); + // movie's url changed + if(mCurrentURL!=mPreviousURL) + { + mMovieDuration = media_plugin->getDuration(); + mPreviousURL = mCurrentURL; + } + + if(mMovieDuration == 0) + { + mMovieDuration = media_plugin->getDuration(); + mMediaPlaySliderCtrl->setValue(0); + mMediaPlaySliderCtrl->setEnabled(false); + } + // TODO: What if it's not fully loaded + + if(mUpdateSlider && mMovieDuration!= 0) + { + F64 current_time = media_plugin->getCurrentTime(); + F32 percent = current_time / mMovieDuration; + mMediaPlaySliderCtrl->setValue(percent); + mMediaPlaySliderCtrl->setEnabled(true); + } + + // video vloume + if(volume <= 0.0) + { + mMuteBtn->setToggleState(true); + } + else if (volume >= 1.0) + { + mMuteBtn->setToggleState(false); + } + else + { + mMuteBtn->setToggleState(false); + } + + switch(result) + { + case LLPluginClassMediaOwner::MEDIA_PLAYING: + mPlayCtrl->setEnabled(FALSE); + mPlayCtrl->setVisible(FALSE); + mPauseCtrl->setEnabled(TRUE); + mPauseCtrl->setVisible(has_focus); + + break; + case LLPluginClassMediaOwner::MEDIA_PAUSED: + default: + mPauseCtrl->setEnabled(FALSE); + mPauseCtrl->setVisible(FALSE); + mPlayCtrl->setEnabled(TRUE); + mPlayCtrl->setVisible(has_focus); + break; + } + } + else // web based + { + if(media_plugin) + { + mCurrentURL = media_plugin->getLocation(); + } + else + { + mCurrentURL.clear(); + } + + mPlayCtrl->setVisible(FALSE); + mPauseCtrl->setVisible(FALSE); + mMediaStopCtrl->setVisible(FALSE); + mMediaAddressCtrl->setVisible(has_focus && !mini_controls); + mMediaAddressCtrl->setEnabled(has_focus && !mini_controls); + mMediaPlaySliderPanel->setVisible(FALSE); + mMediaPlaySliderPanel->setEnabled(FALSE); + mSkipFwdCtrl->setVisible(FALSE); + mSkipFwdCtrl->setEnabled(FALSE); + mSkipBackCtrl->setVisible(FALSE); + mSkipBackCtrl->setEnabled(FALSE); + + if(media_impl->getVolume() <= 0.0) + { + mMuteBtn->setToggleState(true); + } + else + { + mMuteBtn->setToggleState(false); + } + + if (mMediaPanelScroll) + { + mMediaPanelScroll->setVisible(has_focus); + mScrollUpCtrl->setVisible(has_focus); + mScrollDownCtrl->setVisible(has_focus); + mScrollRightCtrl->setVisible(has_focus); + mScrollDownCtrl->setVisible(has_focus); + } + // TODO: get the secure lock bool from media plug in + std::string prefix = std::string("https://"); + std::string test_prefix = mCurrentURL.substr(0, prefix.length()); + LLStringUtil::toLower(test_prefix); + if(test_prefix == prefix) + { + mSecureLockIcon->setVisible(has_focus); + } + + if(mCurrentURL!=mPreviousURL) + { + setCurrentURL(); + mPreviousURL = mCurrentURL; + } + + if(result == LLPluginClassMediaOwner::MEDIA_LOADING) + { + mReloadCtrl->setEnabled(FALSE); + mReloadCtrl->setVisible(FALSE); + mStopCtrl->setEnabled(TRUE); + mStopCtrl->setVisible(has_focus); + } + else + { + mReloadCtrl->setEnabled(TRUE); + mReloadCtrl->setVisible(has_focus); + mStopCtrl->setEnabled(FALSE); + mStopCtrl->setVisible(FALSE); + } + } + + + if(media_plugin) + { + // + // Handle progress bar + // + if(LLPluginClassMediaOwner::MEDIA_LOADING == media_plugin->getStatus()) + { + mMediaProgressPanel->setVisible(true); + mMediaProgressBar->setValue(media_plugin->getProgressPercent()); + } + else + { + mMediaProgressPanel->setVisible(false); + } + } + + if(media_impl) + { + // + // Handle Scrolling + // + switch (mScrollState) + { + case SCROLL_UP: + media_impl->scrollWheel(0, -1, MASK_NONE); + break; + case SCROLL_DOWN: + media_impl->scrollWheel(0, 1, MASK_NONE); + break; + case SCROLL_LEFT: + media_impl->scrollWheel(1, 0, MASK_NONE); + // media_impl->handleKeyHere(KEY_LEFT, MASK_NONE); + break; + case SCROLL_RIGHT: + media_impl->scrollWheel(-1, 0, MASK_NONE); + // media_impl->handleKeyHere(KEY_RIGHT, MASK_NONE); + break; + case SCROLL_NONE: + default: + break; + } + } + + setVisible(enabled); + + // + // Calculate position and shape of the controls + // + std::vector::iterator vert_it; + std::vector::iterator vert_end; + std::vector vect_face; + + LLVolume* volume = objectp->getVolume(); + + if (volume) + { + const LLVolumeFace& vf = volume->getVolumeFace(mTargetObjectFace); + + LLVector3 ext[2]; + ext[0].set(vf.mExtents[0].getF32ptr()); + ext[1].set(vf.mExtents[1].getF32ptr()); + + LLVector3 center = (ext[0]+ext[1])*0.5f; + LLVector3 size = (ext[1]-ext[0])*0.5f; + LLVector3 vert[] = + { + center + size.scaledVec(LLVector3(1,1,1)), + center + size.scaledVec(LLVector3(-1,1,1)), + center + size.scaledVec(LLVector3(1,-1,1)), + center + size.scaledVec(LLVector3(-1,-1,1)), + center + size.scaledVec(LLVector3(1,1,-1)), + center + size.scaledVec(LLVector3(-1,1,-1)), + center + size.scaledVec(LLVector3(1,-1,-1)), + center + size.scaledVec(LLVector3(-1,-1,-1)), + }; + + LLVOVolume* vo = (LLVOVolume*) objectp; + + for (U32 i = 0; i < 8; i++) + { + vect_face.push_back(vo->volumePositionToAgent(vert[i])); + } + } + vert_it = vect_face.begin(); + vert_end = vect_face.end(); + + glh::matrix4f mat; + if (!is_hud) + { + mat = glh_get_current_projection() * glh_get_current_modelview(); + } + else { + glh::matrix4f proj, modelview; + if (get_hud_matrices(proj, modelview)) + mat = proj * modelview; + } + LLVector3 min = LLVector3(1,1,1); + LLVector3 max = LLVector3(-1,-1,-1); + for(; vert_it != vert_end; ++vert_it) + { + // project silhouette vertices into screen space + glh::vec3f screen_vert = glh::vec3f(vert_it->mV); + mat.mult_matrix_vec(screen_vert); + + // add to screenspace bounding box + update_min_max(min, max, LLVector3(screen_vert.v)); + } + + // convert screenspace bbox to pixels (in screen coords) + LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); + LLCoordGL screen_min; + screen_min.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.mV[VX] + 1.f) * 0.5f); + screen_min.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.mV[VY] + 1.f) * 0.5f); + + LLCoordGL screen_max; + screen_max.mX = llround((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.mV[VX] + 1.f) * 0.5f); + screen_max.mY = llround((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.mV[VY] + 1.f) * 0.5f); + + // grow panel so that screenspace bounding box fits inside "media_region" element of panel + LLRect media_panel_rect; + // Get the height of the controls (less the volume slider) + S32 controls_height = mMediaControlsStack->getRect().getHeight() - mVolumeSliderCtrl->getRect().getHeight(); + getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_panel_rect); + media_panel_rect.mTop += controls_height; + + // keep all parts of panel on-screen + // Area of the top of the world view to avoid putting the controls + window_rect.mTop -= mTopWorldViewAvoidZone; + // Don't include "spacing" bookends on left & right of the media controls + window_rect.mLeft -= mLeftBookend->getRect().getWidth(); + window_rect.mRight += mRightBookend->getRect().getWidth(); + // Don't include the volume slider + window_rect.mBottom -= mVolumeSliderCtrl->getRect().getHeight(); + media_panel_rect.intersectWith(window_rect); + + // clamp to minimum size, keeping rect inside window + S32 centerX = media_panel_rect.getCenterX(); + S32 centerY = media_panel_rect.getCenterY(); + // Shrink screen rect by min width and height, to ensure containment + window_rect.stretch(-mMinWidth/2, -mMinHeight/2); + window_rect.clampPointToRect(centerX, centerY); + media_panel_rect.setCenterAndSize(centerX, centerY, + llmax(mMinWidth, media_panel_rect.getWidth()), + llmax(mMinHeight, media_panel_rect.getHeight())); + + // Finally set the size of the panel + setShape(media_panel_rect, true); + + // Test mouse position to see if the cursor is stationary + LLCoordWindow cursor_pos_window; + getWindow()->getCursorPosition(&cursor_pos_window); + + // If last pos is not equal to current pos, the mouse has moved + // We need to reset the timer, and make sure the panel is visible + if(cursor_pos_window.mX != mLastCursorPos.mX || + cursor_pos_window.mY != mLastCursorPos.mY || + mScrollState != SCROLL_NONE) + { + mInactivityTimer.start(); + mLastCursorPos = cursor_pos_window; + } + + if(isMouseOver() || hasFocus()) + { + // Never fade the controls if the mouse is over them or they have keyboard focus. + mFadeTimer.stop(); + } + else if(!mClearFaceOnFade && (mInactivityTimer.getElapsedTimeF32() < mInactiveTimeout)) + { + // Mouse is over the object, but has not been stationary for long enough to fade the UI + mFadeTimer.stop(); + } + else if(! mFadeTimer.getStarted() ) + { + // we need to start fading the UI (and we have not already started) + mFadeTimer.reset(); + mFadeTimer.start(); + } + else + { + // I don't think this is correct anymore. This is done in draw() after the fade has completed. + // setVisible(FALSE); + } + } +} + +/*virtual*/ +void LLPanelPrimMediaControls::draw() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if (impl) + { + LLNotificationPtr notification = impl->getCurrentNotification(); + if (notification != mActiveNotification) + { + mActiveNotification = notification; + if (notification) + { + showNotification(notification); + } + else + { + hideNotification(); + } + } + } + + F32 alpha = getDrawContext().mAlpha; + if(mHideImmediately) + { + //hide this panel + clearFaceOnFade(); + + mHideImmediately = false; + } + else if(mFadeTimer.getStarted()) + { + F32 time = mFadeTimer.getElapsedTimeF32(); + alpha *= llmax(lerp(1.0, 0.0, time / mControlFadeTime), 0.0f); + + if(time >= mControlFadeTime) + { + //hide this panel + clearFaceOnFade(); + } + } + + // Build rect for icon area in coord system of this panel + // Assumes layout_stack is a direct child of this panel + mMediaControlsStack->updateLayout(); + + // adjust for layout stack spacing + S32 space = mMediaControlsStack->getPanelSpacing() + 2; + LLRect controls_bg_area = mMediaControlsStack->getRect(); + + controls_bg_area.mTop += space + 2; + + // adjust to ignore space from volume slider + controls_bg_area.mBottom += mVolumeSliderCtrl->getRect().getHeight(); + + // adjust to ignore space from left bookend padding + controls_bg_area.mLeft += mLeftBookend->getRect().getWidth() - space; + + // ignore space from right bookend padding + controls_bg_area.mRight -= mRightBookend->getRect().getWidth() - space - 2; + + // draw control background UI image + mBackgroundImage->draw( controls_bg_area, UI_VERTEX_COLOR % alpha); + + // draw volume slider background UI image + if (mVolumeSliderCtrl->getVisible()) + { + LLRect volume_slider_rect; + screenRectToLocal(mVolumeSliderCtrl->calcScreenRect(), &volume_slider_rect); + mVolumeSliderBackgroundImage->draw(volume_slider_rect, UI_VERTEX_COLOR % alpha); + } + + { + LLViewDrawContext context(alpha); + LLPanel::draw(); + } +} + +BOOL LLPanelPrimMediaControls::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + mInactivityTimer.start(); + return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); +} + +BOOL LLPanelPrimMediaControls::handleMouseDown(S32 x, S32 y, MASK mask) +{ + mInactivityTimer.start(); + return LLPanel::handleMouseDown(x, y, mask); +} + +BOOL LLPanelPrimMediaControls::handleMouseUp(S32 x, S32 y, MASK mask) +{ + mInactivityTimer.start(); + return LLPanel::handleMouseUp(x, y, mask); +} + +BOOL LLPanelPrimMediaControls::handleKeyHere( KEY key, MASK mask ) +{ + mInactivityTimer.start(); + return LLPanel::handleKeyHere(key, mask); +} + +bool LLPanelPrimMediaControls::isMouseOver() +{ + bool result = false; + + if( getVisible() ) + { + LLCoordWindow cursor_pos_window; + LLCoordScreen cursor_pos_screen; + LLCoordGL cursor_pos_gl; + S32 x, y; + getWindow()->getCursorPosition(&cursor_pos_window); + cursor_pos_gl = cursor_pos_window.convert(); + + if(mMediaControlsStack->getVisible()) + { + mMediaControlsStack->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); + + LLView *hit_child = mMediaControlsStack->childFromPoint(x, y); + if(hit_child && hit_child->getVisible()) + { + // This was useful for debugging both coordinate translation and view hieararchy problems... + // llinfos << "mouse coords: " << x << ", " << y << " hit child " << hit_child->getName() << llendl; + + // This will be a direct child of the LLLayoutStack, which should be a layout_panel. + // These may not shown/hidden by the logic in updateShape(), so we need to do another hit test on the children of the layout panel, + // which are the actual controls. + hit_child->screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &x, &y); + + LLView *hit_child_2 = hit_child->childFromPoint(x, y); + if(hit_child_2 && hit_child_2->getVisible()) + { + // This was useful for debugging both coordinate translation and view hieararchy problems... + // llinfos << " mouse coords: " << x << ", " << y << " hit child 2 " << hit_child_2->getName() << llendl; + result = true; + } + } + } + } + + return result; +} + + +void LLPanelPrimMediaControls::onClickClose() +{ + close(); +} + +void LLPanelPrimMediaControls::close() +{ + resetZoomLevel(true); + LLViewerMediaFocus::getInstance()->clearFocus(); + setVisible(FALSE); +} + + +void LLPanelPrimMediaControls::onClickBack() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl =getTargetMediaImpl(); + + if (impl) + { + impl->navigateBack(); + } +} + +void LLPanelPrimMediaControls::onClickForward() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if (impl) + { + impl->navigateForward(); + } +} + +void LLPanelPrimMediaControls::onClickHome() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateHome(); + } +} + +void LLPanelPrimMediaControls::onClickOpen() +{ + LLViewerMediaImpl* impl = getTargetMediaImpl(); + if(impl) + { + LLWeb::loadURL(impl->getCurrentMediaURL()); + } +} + +void LLPanelPrimMediaControls::onClickReload() +{ + focusOnTarget(); + + //LLViewerMedia::navigateHome(); + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateReload(); + } +} + +void LLPanelPrimMediaControls::onClickPlay() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->play(); + } +} + +void LLPanelPrimMediaControls::onClickPause() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->pause(); + } +} + +void LLPanelPrimMediaControls::onClickStop() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->navigateStop(); + } +} + +void LLPanelPrimMediaControls::onClickMediaStop() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if(impl) + { + impl->stop(); + } +} + +void LLPanelPrimMediaControls::onClickSkipBack() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl =getTargetMediaImpl(); + + if (impl) + { + impl->skipBack(mSkipStep); + } +} + +void LLPanelPrimMediaControls::onClickSkipForward() +{ + focusOnTarget(); + + LLViewerMediaImpl* impl = getTargetMediaImpl(); + + if (impl) + { + impl->skipForward(mSkipStep); + } +} + +void LLPanelPrimMediaControls::onClickZoom() +{ + focusOnTarget(); + + if(mCurrentZoom == ZOOM_NONE) + { + nextZoomLevel(); + } +} + +void LLPanelPrimMediaControls::nextZoomLevel() +{ + LLViewerObject* objectp = getTargetObject(); + if(objectp && objectp->isHUDAttachment()) + { + // Never allow zooming on HUD attachments. + return; + } + + int index = 0; + while (index < kNumZoomLevels) + { + if (kZoomLevels[index] == mCurrentZoom) + { + index++; + break; + } + index++; + } + mCurrentZoom = kZoomLevels[index % kNumZoomLevels]; + updateZoom(); +} + +void LLPanelPrimMediaControls::resetZoomLevel(bool reset_camera) +{ + if(mCurrentZoom != ZOOM_NONE) + { + mCurrentZoom = ZOOM_NONE; + if(reset_camera) + { + updateZoom(); + } + } +} + +void LLPanelPrimMediaControls::updateZoom() +{ + F32 zoom_padding = 0.0f; + switch (mCurrentZoom) + { + case ZOOM_NONE: + { + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); + break; + } + case ZOOM_FAR: + { + zoom_padding = mZoomFarPadding; + break; + } + case ZOOM_MEDIUM: + { + zoom_padding = mZoomMediumPadding; + break; + } + case ZOOM_NEAR: + { + zoom_padding = mZoomNearPadding; + break; + } + default: + { + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); + break; + } + } + + if (zoom_padding > 0.0f) + { + // since we only zoom into medium for now, always set zoom_in constraint to true + LLViewerMediaFocus::setCameraZoom(getTargetObject(), mTargetObjectNormal, zoom_padding, true); + } + + // Remember the object ID/face we zoomed into, so we can update the zoom icon appropriately + mZoomObjectID = mTargetObjectID; + mZoomObjectFace = mTargetObjectFace; +} + +void LLPanelPrimMediaControls::onScrollUp(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(0, -1, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollUpHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_UP; +} +void LLPanelPrimMediaControls::onScrollRight(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(-1, 0, MASK_NONE); +// impl->handleKeyHere(KEY_RIGHT, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollRightHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_RIGHT; +} + +void LLPanelPrimMediaControls::onScrollLeft(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(1, 0, MASK_NONE); +// impl->handleKeyHere(KEY_LEFT, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollLeftHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_LEFT; +} + +void LLPanelPrimMediaControls::onScrollDown(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->focusOnTarget(); + + LLViewerMediaImpl* impl = this_panel->getTargetMediaImpl(); + + if(impl) + { + impl->scrollWheel(0, 1, MASK_NONE); + } +} +void LLPanelPrimMediaControls::onScrollDownHeld(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_DOWN; +} + +void LLPanelPrimMediaControls::onScrollStop(void* user_data) +{ + LLPanelPrimMediaControls* this_panel = static_cast (user_data); + this_panel->mScrollState = SCROLL_NONE; +} + +void LLPanelPrimMediaControls::onCommitURL() +{ + focusOnTarget(); + + std::string url = mMediaAddress->getValue().asString(); + if(getTargetMediaImpl() && !url.empty()) + { + getTargetMediaImpl()->navigateTo( url, "", true); + + // Make sure keyboard focus is set to the media focus object. + gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); + + } + mPauseFadeout = false; + mFadeTimer.start(); +} + + +void LLPanelPrimMediaControls::onInputURL(LLFocusableElement* caller, void *userdata) +{ + + LLPanelPrimMediaControls* this_panel = static_cast (userdata); + this_panel->focusOnTarget(); + + this_panel->mPauseFadeout = true; + this_panel->mFadeTimer.stop(); + this_panel->mFadeTimer.reset(); + +} + +void LLPanelPrimMediaControls::setCurrentURL() +{ +#ifdef USE_COMBO_BOX_FOR_MEDIA_URL +// LLComboBox* media_address_combo = getChild("media_address_combo"); +// // redirects will navigate momentarily to about:blank, don't add to history +// if (media_address_combo && mCurrentURL != "about:blank") +// { +// media_address_combo->remove(mCurrentURL); +// media_address_combo->add(mCurrentURL); +// media_address_combo->selectByValue(mCurrentURL); +// } +#else // USE_COMBO_BOX_FOR_MEDIA_URL + if (mMediaAddress && mCurrentURL != "about:blank") + { + mMediaAddress->setValue(mCurrentURL); + } +#endif // USE_COMBO_BOX_FOR_MEDIA_URL +} + +void LLPanelPrimMediaControls::onCommitSlider() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + // get slider value + F64 slider_value = mMediaPlaySliderCtrl->getValue().asReal(); + if(slider_value <= 0.0) + { + media_impl->stop(); + } + else + { + media_impl->seek(slider_value*mMovieDuration); + //mUpdateSlider= false; + } + } +} + +void LLPanelPrimMediaControls::onCommitVolumeUp() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + volume += 0.1f; + if(volume >= 1.0f) + { + volume = 1.0f; + } + + media_impl->setVolume(volume); + mMuteBtn->setToggleState(false); + } +} + +void LLPanelPrimMediaControls::onCommitVolumeDown() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + volume -= 0.1f; + if(volume <= 0.0f) + { + volume = 0.0f; + } + + media_impl->setVolume(volume); + mMuteBtn->setToggleState(false); + } +} + +void LLPanelPrimMediaControls::onCommitVolumeSlider() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); + } +} + +void LLPanelPrimMediaControls::onToggleMute() +{ + focusOnTarget(); + + LLViewerMediaImpl* media_impl = getTargetMediaImpl(); + if (media_impl) + { + F32 volume = media_impl->getVolume(); + + if(volume > 0.0) + { + media_impl->setVolume(0.0); + } + else if (mVolumeSliderCtrl->getValueF32() == 0.0) + { + media_impl->setVolume(1.0); + mVolumeSliderCtrl->setValue(1.0); + } + else + { + media_impl->setVolume(mVolumeSliderCtrl->getValueF32()); + } + } +} + +void LLPanelPrimMediaControls::showVolumeSlider() +{ + mVolumeSliderVisible++; +} + +void LLPanelPrimMediaControls::hideVolumeSlider() +{ + mVolumeSliderVisible--; +} + +bool LLPanelPrimMediaControls::shouldVolumeSliderBeVisible() +{ + return mVolumeSliderVisible > 0; +} + + +void LLPanelPrimMediaControls::clearFaceOnFade() +{ + if(mClearFaceOnFade) + { + // Hiding this object makes scroll events go missing after it fades out + // (see DEV-41755 for a full description of the train wreck). + // Only hide the controls when we're untargeting. + setVisible(FALSE); + + mClearFaceOnFade = false; + mVolumeSliderVisible = 0; + mTargetImplID = LLUUID::null; + mTargetObjectID = LLUUID::null; + mTargetObjectFace = 0; + } +} + +void LLPanelPrimMediaControls::onMouselookModeIn() +{ + LLViewerMediaFocus::getInstance()->clearHover(); + mHideImmediately = true; +} + +void LLPanelPrimMediaControls::showNotification(LLNotificationPtr notify) +{ + LLNotifications::instance().add(notify); +} + +void LLPanelPrimMediaControls::hideNotification() +{ +} diff --git a/indra/newview/llpanelprimmediacontrols.h b/indra/newview/llpanelprimmediacontrols.h new file mode 100644 index 000000000..7db687d23 --- /dev/null +++ b/indra/newview/llpanelprimmediacontrols.h @@ -0,0 +1,227 @@ +/** + * @file llpanelmediahud.h + * @brief Media hud panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-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$ + */ + +#ifndef LL_PANELPRIMMEDIACONTROLS_H +#define LL_PANELPRIMMEDIACONTROLS_H + +#include "llpanel.h" +#include "llviewermedia.h" +#include "llnotificationptr.h" +#include "llcoord.h" + +class LLButton; +class LLIconCtrl; +class LLLayoutStack; +class LLProgressBar; +class LLSliderCtrl; +class LLViewerMediaImpl; + +class LLPanelPrimMediaControls : public LLPanel +{ +public: + LLPanelPrimMediaControls(); + virtual ~LLPanelPrimMediaControls(); + /*virtual*/ BOOL postBuild(); + virtual void draw(); + virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + + virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); + virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); + virtual BOOL handleKeyHere(KEY key, MASK mask); + + void updateShape(); + bool isMouseOver(); + + void showNotification(LLNotificationPtr notify); + void hideNotification(); + + + enum EZoomLevel + { + ZOOM_NONE = 0, + ZOOM_FAR, + ZOOM_MEDIUM, + ZOOM_NEAR + }; + + EZoomLevel getZoomLevel() const { return mCurrentZoom; } + void nextZoomLevel(); + void resetZoomLevel(bool reset_camera = true); + void close(); + + LLHandle getHandle() const { return mPanelHandle; } + void setMediaFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + + + static const EZoomLevel kZoomLevels[]; + static const int kNumZoomLevels; + + enum EScrollDir + { + SCROLL_UP = 0, + SCROLL_DOWN, + SCROLL_LEFT, + SCROLL_RIGHT, + SCROLL_NONE + }; + +private: + void onClickClose(); + void onClickBack(); + void onClickForward(); + void onClickHome(); + void onClickOpen(); + void onClickReload(); + void onClickPlay(); + void onClickPause(); + void onClickStop(); + void onClickZoom(); + void onClickSkipBack(); + void onClickSkipForward(); + void onClickMediaStop(); + void onCommitURL(); + + void updateZoom(); + void setCurrentURL(); + void onCommitSlider(); + + void onCommitVolumeUp(); + void onCommitVolumeDown(); + void onCommitVolumeSlider(); + void onToggleMute(); + void showVolumeSlider(); + void hideVolumeSlider(); + bool shouldVolumeSliderBeVisible(); + + static void onScrollUp(void* user_data); + static void onScrollUpHeld(void* user_data); + static void onScrollLeft(void* user_data); + static void onScrollLeftHeld(void* user_data); + static void onScrollRight(void* user_data); + static void onScrollRightHeld(void* user_data); + static void onScrollDown(void* user_data); + static void onScrollDownHeld(void* user_data); + static void onScrollStop(void* user_data); + + static void onInputURL(LLFocusableElement* caller, void *userdata); + static bool hasControlsPermission(LLViewerObject *obj, const LLMediaEntry *media_entry); + + void focusOnTarget(); + + LLViewerMediaImpl* getTargetMediaImpl(); + LLViewerObject* getTargetObject(); + LLPluginClassMedia* getTargetMediaPlugin(); + +private: + + void clearFaceOnFade(); + + void onMouselookModeIn(); + + LLView *mMediaRegion; + LLUICtrl *mBackCtrl; + LLUICtrl *mFwdCtrl; + LLUICtrl *mReloadCtrl; + LLUICtrl *mPlayCtrl; + LLUICtrl *mPauseCtrl; + LLUICtrl *mStopCtrl; + LLUICtrl *mMediaStopCtrl; + LLUICtrl *mHomeCtrl; + LLUICtrl *mUnzoomCtrl; + LLUICtrl *mOpenCtrl; + LLUICtrl *mSkipBackCtrl; + LLUICtrl *mSkipFwdCtrl; + LLUICtrl *mZoomCtrl; + LLPanel *mMediaProgressPanel; + LLProgressBar *mMediaProgressBar; + LLUICtrl *mMediaAddressCtrl; + LLUICtrl *mMediaAddress; + LLUICtrl *mMediaPlaySliderPanel; + LLUICtrl *mMediaPlaySliderCtrl; + LLUICtrl *mVolumeCtrl; + LLButton *mMuteBtn; + LLSliderCtrl *mVolumeSliderCtrl; + LLIconCtrl *mWhitelistIcon; + LLIconCtrl *mSecureLockIcon; + LLLayoutStack *mMediaControlsStack; + LLUICtrl *mLeftBookend; + LLUICtrl *mRightBookend; + LLUIImage* mBackgroundImage; + LLUIImage* mVolumeSliderBackgroundImage; + F32 mSkipStep; + S32 mMinWidth; + S32 mMinHeight; + F32 mZoomNearPadding; + F32 mZoomMediumPadding; + F32 mZoomFarPadding; + S32 mTopWorldViewAvoidZone; + + LLUICtrl *mMediaPanelScroll; + LLButton *mScrollUpCtrl; + LLButton *mScrollLeftCtrl; + LLButton *mScrollRightCtrl; + LLButton *mScrollDownCtrl; + + bool mPauseFadeout; + bool mUpdateSlider; + bool mClearFaceOnFade; + bool mHideImmediately; + + LLMatrix4 mLastCameraMat; + EZoomLevel mCurrentZoom; + EScrollDir mScrollState; + LLCoordWindow mLastCursorPos; + LLFrameTimer mInactivityTimer; + LLFrameTimer mFadeTimer; + F32 mInactiveTimeout; + F32 mControlFadeTime; + LLRootHandle mPanelHandle; + F32 mAlpha; + std::string mCurrentURL; + std::string mPreviousURL; + F64 mCurrentRate; + F64 mMovieDuration; + + LLUUID mTargetObjectID; + S32 mTargetObjectFace; + LLUUID mTargetImplID; + LLVector3 mTargetObjectNormal; + + LLUUID mZoomObjectID; + S32 mZoomObjectFace; + + S32 mVolumeSliderVisible; + + LLNotificationPtr mActiveNotification; +}; + +#endif // LL_PANELMEDIAHUD_H diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 009965c40..aa2c5dbd2 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -59,7 +59,6 @@ std::string getProfileURL(const std::string& agent_name) return url; } -#ifdef AI_UNUSED class LLProfileHandler : public LLCommandHandler { public: @@ -79,13 +78,12 @@ public: } }; LLProfileHandler gProfileHandler; -#endif // AI_UNUSED class LLAgentHandler : public LLCommandHandler { public: // requires trusted browser to trigger - LLAgentHandler() : LLCommandHandler("agent", true/*UNTRUSTED_THROTTLE*/) { } + LLAgentHandler() : LLCommandHandler("agent", UNTRUSTED_THROTTLE) { } bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) @@ -104,13 +102,13 @@ public: return true; } - /* Singu TODO if (verb == "inspect") { - LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); + LLAvatarActions::showProfile(avatar_id); + //Singu TODO: inspect? + //LLFloaterReg::showInstance("inspect_avatar", LLSD().with("avatar_id", avatar_id)); return true; } - */ if (verb == "im") { diff --git a/indra/newview/llpanelvoiceeffect.cpp b/indra/newview/llpanelvoiceeffect.cpp new file mode 100755 index 000000000..f80a58a93 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.cpp @@ -0,0 +1,163 @@ +/** + * @file llpanelvoiceeffect.cpp + * @author Aimee + * @brief Panel to select Voice Morphs. + * + * $LicenseInfo:firstyear=2010&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 "llpanelvoiceeffect.h" + +#include "llcombobox.h" +#include "llfloatervoiceeffect.h" +#include "lltrans.h" + +LLPanelVoiceEffect::LLPanelVoiceEffect() + : mVoiceEffectCombo(NULL) +{ + mCommitCallbackRegistrar.add("Voice.CommitVoiceEffect", boost::bind(&LLPanelVoiceEffect::onCommitVoiceEffect, this)); +} + +LLPanelVoiceEffect::~LLPanelVoiceEffect() +{ + /* + LLView* combo_list_view = mVoiceEffectCombo->getChildView("ComboBox"); + LLTransientFloaterMgr::getInstance()->removeControlView(combo_list_view); + */ + + if(LLVoiceClient::instanceExists()) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->removeObserver(this); + } + } +} + +// virtual +BOOL LLPanelVoiceEffect::postBuild() +{ + mVoiceEffectCombo = getChild("voice_effect"); + + /* + // Need to tell LLTransientFloaterMgr about the combo list, otherwise it can't + // be clicked while in a docked floater as it extends outside the floater area. + LLView* combo_list_view = mVoiceEffectCombo->getChildView("ComboBox"); + LLTransientFloaterMgr::getInstance()->addControlView(combo_list_view); + */ + + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (effect_interface) + { + effect_interface->addObserver(this); + } + + update(true); + + return TRUE; +} + +////////////////////////////////////////////////////////////////////////// +/// PRIVATE SECTION +////////////////////////////////////////////////////////////////////////// + +void LLPanelVoiceEffect::onCommitVoiceEffect() +{ + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) + { + mVoiceEffectCombo->setEnabled(false); + return; + } + + LLSD value = mVoiceEffectCombo->getValue(); + if (value.asInteger() == PREVIEW_VOICE_EFFECTS) + { + // Open the Voice Morph preview floater + LLFloaterVoiceEffect::showInstance(); + } + else if (value.asInteger() == GET_VOICE_EFFECTS) + { + // Open the voice morphing info web page + LLWeb::loadURL(LLTrans::getString("voice_morphing_url")); + } + else + { + effect_interface->setVoiceEffect(value.asUUID()); + } + + mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect()); +} + +// virtual +void LLPanelVoiceEffect::onVoiceEffectChanged(bool effect_list_updated) +{ + update(effect_list_updated); +} + +void LLPanelVoiceEffect::update(bool list_updated) +{ + if (mVoiceEffectCombo) + { + LLVoiceEffectInterface* effect_interface = LLVoiceClient::instance().getVoiceEffectInterface(); + if (!effect_interface) return; + if (list_updated) + { + // Add the default "No Voice Morph" entry. + mVoiceEffectCombo->removeall(); + mVoiceEffectCombo->add(getString("no_voice_effect"), LLUUID::null); + mVoiceEffectCombo->addSeparator(); + + // Add entries for each Voice Morph. + const voice_effect_list_t& effect_list = effect_interface->getVoiceEffectList(); + if (!effect_list.empty()) + { + for (voice_effect_list_t::const_iterator it = effect_list.begin(); it != effect_list.end(); ++it) + { + mVoiceEffectCombo->add(it->first, it->second, ADD_BOTTOM); + } + + mVoiceEffectCombo->addSeparator(); + } + + // Add the fixed entries to go to the preview floater or marketing page. + mVoiceEffectCombo->add(getString("preview_voice_effects"), PREVIEW_VOICE_EFFECTS); + mVoiceEffectCombo->add(getString("get_voice_effects"), GET_VOICE_EFFECTS); + } + + if (effect_interface && LLVoiceClient::instance().isVoiceWorking()) + { + // Select the current Voice Morph. + mVoiceEffectCombo->setValue(effect_interface->getVoiceEffect()); + mVoiceEffectCombo->setEnabled(true); + } + else + { + // If voice isn't working or Voice Effects are not supported disable the control. + mVoiceEffectCombo->setValue(LLUUID::null); + mVoiceEffectCombo->setEnabled(false); + } + } +} diff --git a/indra/newview/llpanelvoiceeffect.h b/indra/newview/llpanelvoiceeffect.h new file mode 100755 index 000000000..27face087 --- /dev/null +++ b/indra/newview/llpanelvoiceeffect.h @@ -0,0 +1,67 @@ +/** + * @file llpanelvoiceeffect.h + * @author Aimee + * @brief Panel to select Voice Effects. + * + * $LicenseInfo:firstyear=2010&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$ + */ + +#ifndef LL_PANELVOICEEFFECT_H +#define LL_PANELVOICEEFFECT_H + +#include "llpanel.h" +#include "llvoiceclient.h" + +class LLComboBox; + +class LLPanelVoiceEffect + : public LLPanel + , public LLVoiceEffectObserver +{ +public: + LOG_CLASS(LLPanelVoiceEffect); + + LLPanelVoiceEffect(); + virtual ~LLPanelVoiceEffect(); + + virtual BOOL postBuild(); + +private: + void onCommitVoiceEffect(); + void update(bool list_updated); + + /// Called by voice effect provider when voice effect list is changed. + virtual void onVoiceEffectChanged(bool effect_list_updated); + + // Fixed entries in the Voice Morph list + typedef enum e_voice_effect_combo_items + { + NO_VOICE_EFFECT = 0, + PREVIEW_VOICE_EFFECTS = 1, + GET_VOICE_EFFECTS = 2 + } EVoiceEffectComboItems; + + LLComboBox* mVoiceEffectCombo; +}; + + +#endif //LL_PANELVOICEEFFECT_H diff --git a/indra/newview/llpanelweb.cpp b/indra/newview/llpanelweb.cpp index 9f60ecda4..986b5580a 100644 --- a/indra/newview/llpanelweb.cpp +++ b/indra/newview/llpanelweb.cpp @@ -44,17 +44,6 @@ #include "llviewerwindow.h" #include "llpluginclassmedia.h" -// helper functions for getting/freeing the web browser media -// if creating/destroying these is too slow, we'll need to create -// a static member and update all our static callbacks -viewer_media_t get_web_media() -{ - - viewer_media_t media_source = LLViewerMedia::newMediaImpl("", LLUUID::null, 0, 0, 0, 0, "text/html"); - - return media_source; -} - LLPanelWeb::LLPanelWeb() { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_preferences_web.xml"); @@ -98,15 +87,14 @@ void LLPanelWeb::apply() bool value = childGetValue("use_external_browser").asString() == "external" ? true : false; gSavedSettings.setBOOL("UseExternalBrowser", value); - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - { - media_source->getMediaPlugin()->enable_cookies(childGetValue("cookies_enabled")); + LLViewerMedia::setCookiesEnabled(getChild("cookies_enabled")->getValue()); - bool proxy_enable = childGetValue("web_proxy_enabled"); - std::string proxy_address = childGetValue("web_proxy_editor"); - int proxy_port = childGetValue("web_proxy_port"); - media_source->getMediaPlugin()->proxy_setup(proxy_enable, proxy_address, proxy_port); + if (hasChild("web_proxy_enabled") && hasChild("web_proxy_editor") && hasChild("web_proxy_port")) + { + bool proxy_enable = getChild("web_proxy_enabled")->getValue(); + std::string proxy_address = getChild("web_proxy_editor")->getValue(); + int proxy_port = getChild("web_proxy_port")->getValue(); + LLViewerMedia::setProxyConfig(proxy_enable, proxy_address, proxy_port); } } @@ -126,9 +114,7 @@ bool LLPanelWeb::callback_clear_browser_cache(const LLSD& notification, const LL S32 option = LLNotification::getSelectedOption(notification, response); if ( option == 0 ) // YES { - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - media_source->getMediaPlugin()->clear_cache(); + LLViewerMedia::clearAllCaches(); } return false; } @@ -145,25 +131,11 @@ bool LLPanelWeb::callback_clear_cookies(const LLSD& notification, const LLSD& re S32 option = LLNotification::getSelectedOption(notification, response); if ( option == 0 ) // YES { - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - media_source->getMediaPlugin()->clear_cookies(); + LLViewerMedia::clearAllCookies(); } return false; } -// static -void LLPanelWeb::onCommitCookies(LLUICtrl* ctrl, void* data) -{ - LLPanelWeb* self = (LLPanelWeb*)data; - LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl; - - if (!self || !check) return; - - viewer_media_t media_source = get_web_media(); - if (media_source && media_source->hasMedia()) - media_source->getMediaPlugin()->enable_cookies(check->get()); -} // static void LLPanelWeb::onCommitWebProxyEnabled(LLUICtrl* ctrl, void* data) { @@ -174,6 +146,4 @@ void LLPanelWeb::onCommitWebProxyEnabled(LLUICtrl* ctrl, void* data) self->childSetEnabled("web_proxy_editor", check->get()); self->childSetEnabled("web_proxy_port", check->get()); self->childSetEnabled("proxy_text_label", check->get()); - - } diff --git a/indra/newview/llpanelweb.h b/indra/newview/llpanelweb.h index 449b9cb29..8e64a0ec6 100644 --- a/indra/newview/llpanelweb.h +++ b/indra/newview/llpanelweb.h @@ -51,7 +51,6 @@ private: static void onClickClearCookies(void*); static bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response); static bool callback_clear_cookies(const LLSD& notification, const LLSD& response); - static void onCommitCookies(LLUICtrl* ctrl, void* data); static void onCommitWebProxyEnabled(LLUICtrl* ctrl, void* data); }; diff --git a/indra/newview/llparticipantlist.h b/indra/newview/llparticipantlist.h index 1d4841bba..1177b778a 100644 --- a/indra/newview/llparticipantlist.h +++ b/indra/newview/llparticipantlist.h @@ -27,13 +27,13 @@ #ifndef LL_PARTICIPANTLIST_H #define LL_PARTICIPANTLIST_H -#include "llpanel.h" +#include "lllayoutstack.h" class LLSpeakerMgr; class LLScrollListCtrl; class LLUICtrl; -class LLParticipantList : public LLPanel +class LLParticipantList : public LLLayoutPanel { LOG_CLASS(LLParticipantList); public: diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp index ad6ab788f..394dfce31 100644 --- a/indra/newview/llprogressview.cpp +++ b/indra/newview/llprogressview.cpp @@ -46,6 +46,7 @@ #include "llagent.h" #include "llbutton.h" +#include "llcallbacklist.h" #include "llfocusmgr.h" #include "llprogressbar.h" #include "llstartup.h" @@ -55,28 +56,28 @@ #include "llappviewer.h" #include "llweb.h" #include "lluictrlfactory.h" +#include "llpanellogin.h" LLProgressView* LLProgressView::sInstance = NULL; S32 gStartImageWidth = 1; S32 gStartImageHeight = 1; -const F32 FADE_IN_TIME = 1.f; - -const std::string ANIMATION_FILENAME = "Login Sequence "; -const std::string ANIMATION_SUFFIX = ".jpg"; -const F32 TOTAL_LOGIN_TIME = 10.f; // seconds, wild guess at time from GL context to actual world view -S32 gLastStartAnimationFrame = 0; // human-style indexing, first image = 1 -const S32 ANIMATION_FRAMES = 1; //13; +const F32 FADE_TO_WORLD_TIME = 1.0f; // XUI:translate LLProgressView::LLProgressView(const std::string& name, const LLRect &rect) : LLPanel(name, rect, FALSE), mPercentDone( 0.f ), mURLInMessage(false), - mMouseDownInActiveArea( false ) + mMouseDownInActiveArea( false ), + mFadeToWorldTimer(), + mFadeFromLoginTimer(), + mStartupComplete(false) { LLUICtrlFactory::getInstance()->buildPanel(this, "panel_progress.xml"); reshape(rect.getWidth(), rect.getHeight()); + mFadeToWorldTimer.stop(); + mFadeFromLoginTimer.stop(); } BOOL LLProgressView::postBuild() @@ -85,12 +86,13 @@ BOOL LLProgressView::postBuild() mCancelBtn = getChild("cancel_btn"); mCancelBtn->setClickedCallback( boost::bind(&LLProgressView::onCancelButtonClicked) ); - mFadeTimer.stop(); getChild("title_text")->setText(LLStringExplicit(LLAppViewer::instance()->getSecondLifeTitle())); getChild("message_text")->setClickedCallback(boost::bind(&LLProgressView::onClickMessage, this)); + // hidden initially, until we need it + setVisible(FALSE); sInstance = this; return TRUE; } @@ -98,6 +100,9 @@ BOOL LLProgressView::postBuild() LLProgressView::~LLProgressView() { + // Just in case something went wrong, make sure we deregister our idle callback. + gIdleCallbacks.deleteFunction(onIdle, this); + gFocusMgr.releaseFocusIfNeeded( this ); sInstance = NULL; @@ -123,34 +128,44 @@ BOOL LLProgressView::handleKeyHere(KEY key, MASK mask) return TRUE; } +void LLProgressView::revealIntroPanel() +{ + getParent()->sendChildToFront(this); + mFadeFromLoginTimer.start(); + gIdleCallbacks.addFunction(onIdle, this); +} +void LLProgressView::setStartupComplete() +{ + mStartupComplete = true; + + mFadeFromLoginTimer.stop(); + mFadeToWorldTimer.start(); +} void LLProgressView::setVisible(BOOL visible) { + // hiding progress view if (getVisible() && !visible) { - mFadeTimer.start(); + LLPanel::setVisible(FALSE); } - else if (!getVisible() && visible) + // showing progress view + else if (visible && (!getVisible() || mFadeToWorldTimer.getStarted())) { - gFocusMgr.setTopCtrl(this); setFocus(TRUE); - mFadeTimer.stop(); - mProgressTimer.start(); - LLPanel::setVisible(visible); - } + mFadeToWorldTimer.stop(); + LLPanel::setVisible(TRUE); + } } -void LLProgressView::draw() +void LLProgressView::drawStartTexture(F32 alpha) { - static LLTimer timer; - - // Paint bitmap if we've got one gGL.pushMatrix(); if (gStartTexture) { LLGLSUIDefault gls_ui; - gGL.getTexUnit(0)->bind(gStartTexture); - gGL.color4f(1.f, 1.f, 1.f, mFadeTimer.getStarted() ? clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, FADE_IN_TIME, 1.f, 0.f) : 1.f); + gGL.getTexUnit(0)->bind(gStartTexture.get()); + gGL.color4f(1.f, 1.f, 1.f, alpha); F32 image_aspect = (F32)gStartImageWidth / (F32)gStartImageHeight; S32 width = getRect().getWidth(); S32 height = getRect().getHeight(); @@ -176,20 +191,51 @@ void LLProgressView::draw() gl_rect_2d(getRect()); } gGL.popMatrix(); +} - // Handle fade-in animation - if (mFadeTimer.getStarted()) + +void LLProgressView::draw() +{ + static LLTimer timer; + + if (mFadeFromLoginTimer.getStarted()) { + F32 alpha = clamp_rescale(mFadeFromLoginTimer.getElapsedTimeF32(), 0.f, FADE_TO_WORLD_TIME, 0.f, 1.f); + LLViewDrawContext context(alpha); + + drawStartTexture(alpha); + LLPanel::draw(); - if (mFadeTimer.getElapsedTimeF32() > FADE_IN_TIME) + return; + } + + // handle fade out to world view when we're asked to + if (mFadeToWorldTimer.getStarted()) + { + // draw fading panel + F32 alpha = clamp_rescale(mFadeToWorldTimer.getElapsedTimeF32(), 0.f, FADE_TO_WORLD_TIME, 1.f, 0.f); + LLViewDrawContext context(alpha); + + drawStartTexture(alpha); + LLPanel::draw(); + + // faded out completely - remove panel and reveal world + if (mFadeToWorldTimer.getElapsedTimeF32() > FADE_TO_WORLD_TIME ) { - gFocusMgr.removeTopCtrlWithoutCallback(this); - LLPanel::setVisible(FALSE); + mFadeToWorldTimer.stop(); + + // Fade is complete, release focus + gFocusMgr.releaseFocusIfNeeded( this ); + + // turn off panel that hosts intro so we see the world + setVisible(FALSE); + gStartTexture = NULL; } return; } + drawStartTexture(1.0f); // draw children LLPanel::draw(); } @@ -226,7 +272,10 @@ void LLProgressView::setCancelButtonVisible(BOOL b, const std::string& label) // static void LLProgressView::onCancelButtonClicked() { - if (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE) + // Quitting viewer here should happen only when "Quit" button is pressed while starting up. + // Check for startup state is used here instead of teleport state to avoid quitting when + // cancel is pressed while teleporting inside region (EXT-4911) + if (LLStartUp::getStartupState() < STATE_STARTED) { LLAppViewer::instance()->requestQuit(); } @@ -265,3 +314,21 @@ void LLProgressView::onClickMessage(void* data) } } } + + +// static +void LLProgressView::onIdle(void* user_data) +{ + LLProgressView* self = (LLProgressView*) user_data; + + // Close login panel on mFadeToWorldTimer expiration. + if (self->mFadeFromLoginTimer.getStarted() && + self->mFadeFromLoginTimer.getElapsedTimeF32() > FADE_TO_WORLD_TIME) + { + self->mFadeFromLoginTimer.stop(); + LLPanelLogin::close(); + + // Nothing to do anymore. + gIdleCallbacks.deleteFunction(onIdle, user_data); + } +} diff --git a/indra/newview/llprogressview.h b/indra/newview/llprogressview.h index 79f107112..bff970e29 100644 --- a/indra/newview/llprogressview.h +++ b/indra/newview/llprogressview.h @@ -49,6 +49,7 @@ public: BOOL postBuild(); /*virtual*/ void draw(); + void drawStartTexture(F32 alpha); /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); @@ -60,6 +61,10 @@ public: // Set it to NULL when you want to eliminate the message. void setMessage(const std::string& msg); + void revealIntroPanel(); + + void setStartupComplete(); + void setCancelButtonVisible(BOOL b, const std::string& label); static void onCancelButtonClicked(); @@ -70,13 +75,14 @@ protected: F32 mPercentDone; std::string mMessage; LLButton* mCancelBtn; - LLFrameTimer mFadeTimer; - LLFrameTimer mProgressTimer; + LLFrameTimer mFadeToWorldTimer; + LLFrameTimer mFadeFromLoginTimer; LLRect mOutlineRect; bool mMouseDownInActiveArea; + bool mStartupComplete; bool mURLInMessage; - static LLProgressView* sInstance; + static void onIdle(void* user_data); }; #endif // LL_LLPROGRESSVIEW_H diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 300330cb6..646f83f2e 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -35,6 +35,7 @@ #include "lldbstrings.h" #include "lleconomy.h" #include "llgl.h" +#include "llmediaentry.h" #include "llrender.h" #include "llnotifications.h" #include "llpermissions.h" @@ -1895,47 +1896,79 @@ void LLSelectMgr::selectionSetFullbright(U8 fullbright) getSelection()->applyToObjects(&sendfunc); } -void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url) -{ - U8 media_flags = LLTextureEntry::MF_NONE; - if (media_type == LLViewerObject::MEDIA_SET) - { - media_flags = LLTextureEntry::MF_HAS_MEDIA; - } - +// This function expects media_data to be a map containing relevant +// media data name/value pairs (e.g. home_url, etc.) +void LLSelectMgr::selectionSetMedia(U8 media_type, const LLSD &media_data) +{ struct f : public LLSelectedTEFunctor { U8 mMediaFlags; - f(const U8& t) : mMediaFlags(t) {} + const LLSD &mMediaData; + f(const U8& t, const LLSD& d) : mMediaFlags(t), mMediaData(d) {} bool apply(LLViewerObject* object, S32 te) { if (object->permModify()) { - // update viewer side color in anticipation of update from simulator - object->setTEMediaFlags(te, mMediaFlags); + // If we are adding media, then check the current state of the + // media data on this face. + // - If it does not have media, AND we are NOT setting the HOME URL, then do NOT add media to this + // face. + // - If it does not have media, and we ARE setting the HOME URL, add media to this face. + // - If it does already have media, add/update media to/on this face + // If we are removing media, just do it (ignore the passed-in LLSD). + if (mMediaFlags & LLTextureEntry::MF_HAS_MEDIA) + { + llassert(mMediaData.isMap()); + const LLTextureEntry *texture_entry = object->getTE(te); + if (!mMediaData.isMap() || + (NULL != texture_entry) && !texture_entry->hasMedia() && !mMediaData.has(LLMediaEntry::HOME_URL_KEY)) + { + // skip adding/updating media + } + else { + // Add/update media + object->setTEMediaFlags(te, mMediaFlags); + LLVOVolume *vo = dynamic_cast(object); + llassert(NULL != vo); + if (NULL != vo) + { + vo->syncMediaData(te, mMediaData, true/*merge*/, true/*ignore_agent*/); + } + } + } + else + { + // delete media (or just set the flags) + object->setTEMediaFlags(te, mMediaFlags); + } } return true; } - } setfunc(media_flags); + } setfunc(media_type, media_data); getSelection()->applyToTEs(&setfunc); - - struct g : public LLSelectedObjectFunctor + + struct f2 : public LLSelectedObjectFunctor { - U8 media_type; - const std::string& media_url ; - g(U8 a, const std::string& b) : media_type(a), media_url(b) {} virtual bool apply(LLViewerObject* object) { if (object->permModify()) { object->sendTEUpdate(); - object->setMediaType(media_type); - object->setMediaURL(media_url); + LLVOVolume *vo = dynamic_cast(object); + llassert(NULL != vo); + // It's okay to skip this object if hasMedia() is false... + // the sendTEUpdate() above would remove all media data if it were + // there. + if (NULL != vo && vo->hasMedia()) + { + // Send updated media data FOR THE ENTIRE OBJECT + vo->sendMediaDataUpdate(); + } } return true; } - } sendfunc(media_type, media_url); - getSelection()->applyToObjects(&sendfunc); + } func2; + mSelectedObjects->applyToObjects( &func2 ); } void LLSelectMgr::selectionSetGlow(F32 glow) @@ -4097,6 +4130,7 @@ void LLSelectMgr::deselectAllIfTooFar() if ( (gSavedSettings.getBOOL("LimitSelectDistance") || (fRlvFartouch) ) // [/RLVa:KB] && (!mSelectedObjects->getPrimaryObject() || !mSelectedObjects->getPrimaryObject()->isAvatar()) + && (mSelectedObjects->getPrimaryObject() != LLViewerMediaFocus::getInstance()->getFocusedObject()) && !mSelectedObjects->isAttachment() && !selectionCenter.isExactlyZero()) { @@ -5538,7 +5572,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { LLUUID inspect_item_id = LLFloaterInspect::getSelectedUUID(); - LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getSelectedUUID(); + LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getFocusedObjectID(); // //for (S32 pass = 0; pass < 2; pass++) //{ diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index 7aa6eea13..22175932f 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -528,7 +528,7 @@ public: void selectionSetTexGen( U8 texgen ); void selectionSetShiny( U8 shiny ); void selectionSetFullbright( U8 fullbright ); - void selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url); + void selectionSetMedia( U8 media_type, const LLSD &media_data ); void selectionSetClickAction(U8 action); void selectionSetIncludeInSearch(bool include_in_search); void selectionSetGlow(const F32 glow); @@ -663,6 +663,8 @@ public: void sendAttach(U8 attachment_point, bool replace=true); void sendDetach(); void sendDropAttachment(); + void sendLink(); + void sendDelink(); //void sendHinge(U8 type); //void sendDehinge(); void sendSelect(); @@ -703,8 +705,7 @@ private: void (*pack_body)(LLSelectNode* node, void *user_data), void *user_data, ESendType send_type); - void sendLink(); - void sendDelink(); + static void packAgentID( void *); static void packAgentAndSessionID(void* user_data); @@ -844,5 +845,52 @@ template bool LLObjectSelection::getSelectedTEValue(LLSelectedTEGet return identical; } +// Templates +//----------------------------------------------------------------------------- +// isMultipleTEValue iterate through all TEs and test for uniqueness +// with certain return value ignored when performing the test. +// e.g. when testing if the selection has a unique non-empty homeurl : +// you can set ignore_value = "" and it will only compare among the non-empty +// homeUrls and ignore the empty ones. +//----------------------------------------------------------------------------- +template bool LLObjectSelection::isMultipleTEValue(LLSelectedTEGetFunctor* func, const T& ignore_value) +{ + bool have_first = false; + T selected_value = T(); + + // Now iterate through all TEs to test for sameness + bool unique = TRUE; + for (iterator iter = begin(); iter != end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* object = node->getObject(); + for (S32 te = 0; te < object->getNumTEs(); ++te) + { + if (!node->isTESelected(te)) + { + continue; + } + T value = func->get(object, te); + if(value == ignore_value) + { + continue; + } + if (!have_first) + { + have_first = true; + } + else + { + if (value !=selected_value ) + { + unique = false; + return !unique; + } + } + } + } + return !unique; +} + #endif diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp new file mode 100644 index 000000000..b1a30f78b --- /dev/null +++ b/indra/newview/llslurl.cpp @@ -0,0 +1,562 @@ +/** + * @file llurlsimstring.cpp (was llsimurlstring.cpp) + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. + * + * $LicenseInfo:firstyear=2010&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 "llslurl.h" + +#include "llpanellogin.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llfiltersd2xmlrpc.h" +#include "curl/curl.h" +#include "hippogridmanager.h" + +const char* LLSLURL::SLURL_HTTP_SCHEME = "http"; +const char* LLSLURL::SLURL_HTTPS_SCHEME = "https"; +const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife"; +const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife"; +const char* LLSLURL::SLURL_COM = "slurl.com"; +// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag +// text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this +// version is required also. + +const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com"; +const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com"; +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_APP_PATH = "app"; +const char* LLSLURL::SLURL_REGION_PATH = "region"; +const char* LLSLURL::SIM_LOCATION_HOME = "home"; +const char* LLSLURL::SIM_LOCATION_LAST = "last"; + +const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; +const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; + +const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; +const char* DEFAULT_SLURL_BASE = "https://%s/region/"; +const char* DEFAULT_APP_SLURL_BASE = "x-grid-location-info://%s/app"; + +#define MAINGRID "secondlife" +// resolve a simstring from a slurl +LLSLURL::LLSLURL(const std::string& slurl) +{ + // by default we go to agni. + mType = INVALID; + + if(slurl == SIM_LOCATION_HOME) + { + mType = HOME_LOCATION; + } + else if(slurl.empty() || (slurl == SIM_LOCATION_LAST)) + { + mType = LAST_LOCATION; + } + else + { + LLURI slurl_uri; + // parse the slurl as a uri + if(slurl.find(':') == std::string::npos) + { + // There may be no scheme ('secondlife:' etc.) passed in. In that case + // we want to normalize the slurl by putting the appropriate scheme + // in front of the slurl. So, we grab the appropriate slurl base + // from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or + // https:///region/ for Standalone grid (the word region, not the region name) + // these slurls are typically passed in from the 'starting location' box on the login panel, + // where the user can type in /// + //std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase(); + //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. + std::string fixed_slurl; + + if(gHippoGridManager->getCurrentGrid()->isSecondLife()) + { + if(gHippoGridManager->getCurrentGrid()->isInProductionGrid()) + fixed_slurl = MAIN_GRID_SLURL_BASE; + else + fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); + } + else + fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); + + //std::string fixed_slurl = MAIN_GRID_SLURL_BASE; + + // the slurl that was passed in might have a prepended /, or not. So, + // we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife//// + // or some such. + + if(slurl[0] == '/') + { + fixed_slurl += slurl.substr(1); + } + else + { + fixed_slurl += slurl; + } + // We then load the slurl into a LLURI form + slurl_uri = LLURI(fixed_slurl); + } + else + { + // as we did have a scheme, implying a URI style slurl, we + // simply parse it as a URI + slurl_uri = LLURI(slurl); + } + + LLSD path_array = slurl_uri.pathArray(); + + // determine whether it's a maingrid URI or an Standalone/open style URI + // by looking at the scheme. If it's a 'secondlife:' slurl scheme or + // 'sl:' scheme, we know it's maingrid + + // At the end of this if/else block, we'll have determined the grid, + // and the slurl type (APP or LOCATION) + if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME) + { + // parse a maingrid style slurl. We know the grid is maingrid + // so grab it. + // A location slurl for maingrid (with the special schemes) can be in the form + // secondlife:///// + // or + // secondlife:///secondlife//// + // where if grid is empty, it specifies Agni + + // An app style slurl for maingrid can be + // secondlife:///app/ + // where an empty grid implies Agni + + // we'll start by checking the top of the 'path' which will be + // either 'app', 'secondlife', or . + + // default to maingrid + + mGrid = MAINGRID; + + if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_APP_PATH)) + { + // it's in the form secondlife:///(app|secondlife) + // so parse the grid name to derive the grid ID + if (!slurl_uri.hostName().empty()) + { + if(slurl_uri.hostName() == "util.agni.lindenlab.com") + mGrid = MAINGRID; + else if(slurl_uri.hostName() == "util.aditi.lindenlab.com") + mGrid = "secondlife_beta"; + else + { + HippoGridInfo* grid = gHippoGridManager->getGrid(slurl_uri.hostName()); + mGrid = grid ? grid->getGridNick() : gHippoGridManager->getDefaultGridNick(); + } + } + else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) + { + // If the slurl is in the form secondlife:///secondlife/ form, + // then we are in fact on maingrid. + mGrid = MAINGRID; + } + else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + // for app style slurls, where no grid name is specified, assume the currently + // selected or logged in grid. + mGrid = gHippoGridManager->getCurrentGridNick(); + } + + if(mGrid.empty()) + { + // we couldn't find the grid in the grid manager, so bail + LL_WARNS("AppInit")<<"unable to find grid"< or /app/, so it must be secondlife:// + // therefore the hostname will be the region name, and it's a location type + mType = LOCATION; + // 'normalize' it so the region name is in fact the head of the path_array + path_array.insert(0, slurl_uri.hostName()); + } + } + else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) + { + // We're dealing with either a Standalone style slurl or slurl.com slurl + if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) || + (slurl_uri.hostName() == LLSLURL::WWW_SLURL_COM) || + (slurl_uri.hostName() == LLSLURL::MAPS_SECONDLIFE_COM)) + { + // slurl.com implies maingrid + mGrid = MAINGRID; + } + else + { + // Don't try to match any old http:/// URL as a SLurl. + // SLE SLurls will have the grid hostname in the URL, so only + // match http URLs if the hostname matches the grid hostname + // (or its a slurl.com or maps.secondlife.com URL). + if ((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME || + slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) && + slurl_uri.hostName() != gHippoGridManager->getCurrentGridNick()) + { + return; + } + + // As it's a Standalone grid/open, we will always have a hostname, as Standalone/open style + // urls are properly formed, unlike the stinky maingrid style + mGrid = slurl_uri.hostName(); + } + if (path_array.size() == 0) + { + // um, we need a path... + return; + } + + // we need to normalize the urls so + // the path portion starts with the 'command' that we want to do + // it can either be region or app. + if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)) + { + // strip off 'region' or 'secondlife' + path_array.erase(0); + // it's a location + mType = LOCATION; + } + else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + mType = APP; + path_array.erase(0); + // leave app appended. + } + else + { + // not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL + return; + } + } + else + { + // invalid scheme, so bail + return; + } + + + if(path_array.size() == 0) + { + // we gotta have some stuff after the specifier as to whether it's a region or command + return; + } + + // now that we know whether it's an app slurl or a location slurl, + // parse the slurl into the proper data structures. + if(mType == APP) + { + // grab the app command type and strip it (could be a command to jump somewhere, + // or whatever ) + mAppCmd = path_array[0].asString(); + path_array.erase(0); + + // Grab the parameters + mAppPath = path_array; + // and the query + mAppQuery = slurl_uri.query(); + mAppQueryMap = slurl_uri.queryMap(); + return; + } + else if(mType == LOCATION) + { + // at this point, head of the path array should be [ , , , ] where x, y and z + // are collectively optional + // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + path_array.erase(0); + + // parse the x, y, and optionally z + if(path_array.size() >= 2) + { + + mPosition = LLVector3(path_array); // this construction handles LLSD without all components (values default to 0.f) + if((F32(mPosition[VX]) < 0.f) || + (mPosition[VX] > REGION_WIDTH_METERS) || + (F32(mPosition[VY]) < 0.f) || + (mPosition[VY] > REGION_WIDTH_METERS) || + (F32(mPosition[VZ]) < 0.f) || + (mPosition[VZ] > REGION_HEIGHT_METERS)) + { + mType = INVALID; + return; + } + + } + else + { + // if x, y and z were not fully passed in, go to the middle of the region. + // teleport will adjust the actual location to make sure you're on the ground + // and such + mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0); + } + } + } +} + + +// Create a slurl for the middle of the region +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region) +{ + mGrid = grid; + mRegion = region; + mType = LOCATION; + mPosition = LLVector3((F64)REGION_WIDTH_METERS/2, (F64)REGION_WIDTH_METERS/2, 0); +} + + + +// create a slurl given the position. The position will be modded with the region +// width handling global positions as well +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3& position) +{ + mGrid = grid; + mRegion = region; + S32 x = llround( (F32)fmod( position[VX], (F32)REGION_WIDTH_METERS ) ); + S32 y = llround( (F32)fmod( position[VY], (F32)REGION_WIDTH_METERS ) ); + S32 z = llround( (F32)position[VZ] ); + mType = LOCATION; + mPosition = LLVector3(x, y, z); +} + + +// create a simstring +LLSLURL::LLSLURL(const std::string& region, + const LLVector3& position) +{ + *this = LLSLURL(gHippoGridManager->getCurrentGridNick(), + region, position); +} + +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3d& global_position) +{ + HippoGridInfo* gridp = gHippoGridManager->getGrid(grid); + *this = LLSLURL(gridp ? gridp->getGridNick() : gHippoGridManager->getDefaultGridNick(), + region, LLVector3(global_position.mdV[VX], + global_position.mdV[VY], + global_position.mdV[VZ])); +} + +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& region, + const LLVector3d& global_position) +{ + *this = LLSLURL(gHippoGridManager->getCurrentGridNick(), + region, global_position); +} + +LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb) +{ + mType = APP; + mAppCmd = command; + mAppPath = LLSD::emptyArray(); + mAppPath.append(LLSD(id)); + mAppPath.append(LLSD(verb)); +} + + +std::string LLSLURL::getSLURLString() const +{ + switch(mType) + { + case HOME_LOCATION: + return SIM_LOCATION_HOME; + case LAST_LOCATION: + return SIM_LOCATION_LAST; + case LOCATION: + { + // lookup the grid + S32 x = llround( (F32)mPosition[VX] ); + S32 y = llround( (F32)mPosition[VY] ); + S32 z = llround( (F32)mPosition[VZ] ); + //return LLGridManager::getInstance()->getSLURLBase(mGrid) + + //Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded. + std::string fixed_slurl; + if(gHippoGridManager->getCurrentGrid()->isSecondLife()) + { + if(gHippoGridManager->getCurrentGrid()->isInProductionGrid()) + fixed_slurl = MAIN_GRID_SLURL_BASE; + else + fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); + } + else + fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()); + return fixed_slurl + + LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); + } + case APP: + { + std::ostringstream app_url; + //app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd; + //Singu TODO: Implement LLHippoGridMgr::getAppSLURLBase some day. For now it's hardcoded. + if(gHippoGridManager->getCurrentGrid()->isSecondLife()) + app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd; + else + app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()) << "/" << mAppCmd; + for(LLSD::array_const_iterator i = mAppPath.beginArray(); + i != mAppPath.endArray(); + i++) + { + app_url << "/" << i->asString(); + } + if(mAppQuery.length() > 0) + { + app_url << "?" << mAppQuery; + } + return app_url.str(); + } + default: + LL_WARNS("AppInit") << "Unexpected SLURL type for SLURL string" << (int)mType << LL_ENDL; + return std::string(); + } +} + +std::string LLSLURL::getLoginString() const +{ + + std::stringstream unescaped_start; + switch(mType) + { + case LOCATION: + unescaped_start << "uri:" + << mRegion << "&" + << llround(mPosition[0]) << "&" + << llround(mPosition[1]) << "&" + << llround(mPosition[2]); + break; + case HOME_LOCATION: + unescaped_start << "home"; + break; + case LAST_LOCATION: + unescaped_start << "last"; + break; + default: + LL_WARNS("AppInit") << "Unexpected SLURL type ("<<(int)mType <<")for login string"<< LL_ENDL; + break; + } + return xml_escape_string(unescaped_start.str()); +} + +bool LLSLURL::operator==(const LLSLURL& rhs) +{ + if(rhs.mType != mType) return false; + switch(mType) + { + case LOCATION: + return ((mGrid == rhs.mGrid) && + (mRegion == rhs.mRegion) && + (mPosition == rhs.mPosition)); + case APP: + return getSLURLString() == rhs.getSLURLString(); + + case HOME_LOCATION: + case LAST_LOCATION: + return true; + default: + return false; + } +} + +bool LLSLURL::operator !=(const LLSLURL& rhs) +{ + return !(*this == rhs); +} + +std::string LLSLURL::getLocationString() const +{ + return llformat("%s/%d/%d/%d", + mRegion.c_str(), + (int)llround(mPosition[0]), + (int)llround(mPosition[1]), + (int)llround(mPosition[2])); +} + +// static +const std::string LLSLURL::typeName[NUM_SLURL_TYPES] = +{ + "INVALID", + "LOCATION", + "HOME_LOCATION", + "LAST_LOCATION", + "APP", + "HELP" +}; + +std::string LLSLURL::getTypeString(SLURL_TYPE type) +{ + std::string name; + if ( type >= INVALID && type < NUM_SLURL_TYPES ) + { + name = LLSLURL::typeName[type]; + } + else + { + name = llformat("Out of Range (%d)",type); + } + return name; +} + + +std::string LLSLURL::asString() const +{ + std::ostringstream result; + result + << " mType: " << LLSLURL::getTypeString(mType) + << " mGrid: " + getGrid() + << " mRegion: " + getRegion() + << " mPosition: " << mPosition + << " mAppCmd:" << getAppCmd() + << " mAppPath:" + getAppPath().asString() + << " mAppQueryMap:" + getAppQueryMap().asString() + << " mAppQuery: " + getAppQuery() + ; + + return result.str(); +} + diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h new file mode 100644 index 000000000..b86cf7949 --- /dev/null +++ b/indra/newview/llslurl.h @@ -0,0 +1,114 @@ +/** + * @file llslurl.h + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. + * + * $LicenseInfo:firstyear=2010&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$ + */ +#ifndef LLSLURL_H +#define LLSLURL_H + +#include "llstring.h" + + +// represents a location in a grid + +class LLSLURL +{ +public: + static const char* SLURL_HTTPS_SCHEME; + static const char* SLURL_HTTP_SCHEME; + static const char* SLURL_SL_SCHEME; + static const char* SLURL_SECONDLIFE_SCHEME; + static const char* SLURL_SECONDLIFE_PATH; + static const char* SLURL_COM; + static const char* WWW_SLURL_COM; + static const char* SECONDLIFE_COM; + static const char* MAPS_SECONDLIFE_COM; + static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; + static LLSLURL START_LOCATION; + static const char* SIM_LOCATION_HOME; + static const char* SIM_LOCATION_LAST; + static const char* SLURL_APP_PATH; + static const char* SLURL_REGION_PATH; + + // if you modify this enumeration, update typeName as well + enum SLURL_TYPE { + INVALID, + LOCATION, + HOME_LOCATION, + LAST_LOCATION, + APP, + HELP, + NUM_SLURL_TYPES // must be last + }; + + + LLSLURL(): mType(INVALID) { } + LLSLURL(const std::string& slurl); + LLSLURL(const std::string& grid, const std::string& region); + LLSLURL(const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb); + + SLURL_TYPE getType() const { return mType; } + + std::string getSLURLString() const; + std::string getLoginString() const; + std::string getLocationString() const; + std::string getGrid() const { return mGrid; } + std::string getRegion() const { return mRegion; } + LLVector3 getPosition() const { return mPosition; } + std::string getAppCmd() const { return mAppCmd; } + std::string getAppQuery() const { return mAppQuery; } + LLSD getAppQueryMap() const { return mAppQueryMap; } + LLSD getAppPath() const { return mAppPath; } + + bool isValid() const { return mType != INVALID; } + bool isSpatial() const { return (mType == LAST_LOCATION) || (mType == HOME_LOCATION) || (mType == LOCATION); } + + bool operator==(const LLSLURL& rhs); + bool operator!=(const LLSLURL&rhs); + + std::string asString() const ; + +protected: + static const std::string typeName[NUM_SLURL_TYPES]; + /// Get a human-readable version of the type for logging + static std::string getTypeString(SLURL_TYPE type); + + SLURL_TYPE mType; + + // used for Apps and Help + std::string mAppCmd; + LLSD mAppPath; + LLSD mAppQueryMap; + std::string mAppQuery; + + std::string mGrid; // reference to grid manager grid + std::string mRegion; + LLVector3 mPosition; +}; + +#endif // LLSLURL_H diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 7f493e7ed..2f3b7e33a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -86,6 +86,7 @@ #include "llsecondlifeurls.h" #include "llstring.h" #include "lltexteditor.h" +#include "llurlentry.h" #include "lluserrelations.h" #include "sgversion.h" #include "llviewercontrol.h" @@ -166,7 +167,6 @@ #include "lltrans.h" #include "llui.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" #include "llurlhistory.h" #include "llurlwhitelist.h" #include "lluserauth.h" @@ -263,7 +263,7 @@ extern S32 gStartImageHeight; // static LLHost gAgentSimHost; -static BOOL gSkipOptionalUpdate = FALSE; +//static BOOL gSkipOptionalUpdate = FALSE; static bool gGotUseCircuitCodeAck = false; static std::string sInitialOutfit; @@ -273,6 +273,7 @@ static boost::signals2::connection sWearablesLoadedCon; static bool gUseCircuitCallbackCalled = false; EStartupState LLStartUp::gStartupState = STATE_FIRST; +LLSLURL LLStartUp::sStartSLURL; static U64 gFirstSimHandle = 0; @@ -295,7 +296,7 @@ void show_first_run_dialog(); bool first_run_dialog_callback(const LLSD& notification, const LLSD& response); void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); bool login_alert_status(const LLSD& notification, const LLSD& response); -void update_app(BOOL mandatory, const std::string& message); +//void update_app(BOOL mandatory, const std::string& message); bool update_dialog_callback(const LLSD& notification, const LLSD& response); void login_packet_failed(void**, S32 result); void use_circuit_callback(void**, S32 result); @@ -308,6 +309,7 @@ void release_start_screen(); void reset_login(); void apply_udp_blacklist(const std::string& csv); bool process_login_success_response(std::string &password); +void transition_back_to_login_panel(const std::string& emsg); void callback_cache_name(const LLUUID& id, const std::string& full_name, bool is_group) { @@ -392,7 +394,7 @@ bool idle_startup() static LLUUID web_login_key; static std::string password; static std::vector requested_options; - static std::string redirect_uri; + static std::string redirect_uri; static LLVector3 initial_sun_direction(1.f, 0.f, 0.f); static LLVector3 agent_start_position_region(10.f, 10.f, 10.f); // default for when no space server @@ -402,8 +404,6 @@ bool idle_startup() static bool show_connect_box = true; - static bool samename = false; - // HACK: These are things from the main loop that usually aren't done // until initialization is complete, but need to be done here for things // to work. @@ -414,7 +414,7 @@ bool idle_startup() if (gNoRender) { // HACK, skip optional updates if you're running drones - gSkipOptionalUpdate = TRUE; + //gSkipOptionalUpdate = TRUE; } else { @@ -684,7 +684,7 @@ bool idle_startup() #endif // !LL_WINDOWS ) { - gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODEX(gSavedSettings.getBOOL("SHEnableFMODExProfiler")); + gAudiop = (LLAudioEngine *) new LLAudioEngine_FMODEX(gSavedSettings.getBOOL("SHEnableFMODExProfiler"),gSavedSettings.getBOOL("SHEnableFMODEXVerboseDebugging")); } #endif @@ -755,11 +755,11 @@ bool idle_startup() // // Log on to system // - if (!LLStartUp::sSLURLCommand.empty()) + /*if (!LLStartUp::sSLURLCommand.empty()) { // this might be a secondlife:///app/login URL gLoginHandler.parseDirectLogin(LLStartUp::sSLURLCommand); - } + }*/ if (!gLoginHandler.getFirstName().empty() || !gLoginHandler.getLastName().empty() || !gLoginHandler.getWebLoginKey().isNull() ) @@ -784,11 +784,8 @@ bool idle_startup() pass.hex_digest(md5pass); password = md5pass; -#ifdef USE_VIEWER_AUTH - show_connect_box = true; -#else show_connect_box = false; -#endif + gSavedSettings.setBOOL("AutoLogin", TRUE); } else if (gSavedSettings.getBOOL("AutoLogin")) @@ -798,11 +795,7 @@ bool idle_startup() password = LLStartUp::loadPasswordFromDisk(); gSavedSettings.setBOOL("RememberPassword", TRUE); -#ifdef USE_VIEWER_AUTH - show_connect_box = true; -#else show_connect_box = false; -#endif } else { @@ -863,6 +856,7 @@ bool idle_startup() if (show_connect_box) { + LL_DEBUGS("AppInit") << "show_connect_box on" << LL_ENDL; // Load all the name information out of the login view // NOTE: Hits "Attempted getFields with no login view shown" warning, since we don't // show the login view until login_show() is called below. @@ -888,13 +882,15 @@ bool idle_startup() if (login_history.size() > 0) { LLPanelLogin::setFields(*login_history.getEntries().rbegin()); + display_startup(); } else { LLPanelLogin::setFields(firstname, lastname, password); + display_startup(); LLPanelLogin::giveFocus(); } - display_startup(); + gSavedSettings.setBOOL("FirstRunThisInstall", FALSE); @@ -902,6 +898,7 @@ bool idle_startup() } else { + LL_DEBUGS("AppInit") << "show_connect_box off, skipping to STATE_LOGIN_CLEANUP" << LL_ENDL; // skip directly to message template verification LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); } @@ -1027,10 +1024,10 @@ bool idle_startup() // Set PerAccountSettingsFile to the default value. gSavedSettings.setString("PerAccountSettingsFile", gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, - LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount") - ) - ); + LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount"))); + // Note: can't store warnings files per account because some come up before login + // Overwrite default user settings with user settings LLAppViewer::instance()->loadSettingsFromDirectory(AIReadAccess(gSettings), "Account"); @@ -1095,12 +1092,12 @@ bool idle_startup() if (show_connect_box) { - std::string location; + /*std::string location; LLPanelLogin::getLocation( location ); LLURLSimString::setString( location ); - + */ // END TODO - LLPanelLogin::close(); + //LLPanelLogin::close(); } //For HTML parsing in text boxes. @@ -1127,27 +1124,21 @@ bool idle_startup() #endif // RLV_EXTENSION_STARTLOCATION { // Force login at the last location - agent_location_id = START_LOCATION_ID_LAST; - gSavedSettings.setBOOL("LoginLastLocation", FALSE); - - // Clear some things that would cause us to divert to a user-specified location - LLURLSimString::setString(LLURLSimString::sLocationStringLast); - LLStartUp::sSLURLCommand.clear(); - } else + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); + } // [/RLVa:KB] - if (LLURLSimString::parse()) - { - // a startup URL was specified - agent_location_id = START_LOCATION_ID_URL; - } - else if (gSavedSettings.getBOOL("LoginLastLocation")) - { - agent_location_id = START_LOCATION_ID_LAST; // last location - } - else - { - agent_location_id = START_LOCATION_ID_HOME; // home - } + switch (LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + agent_location_id = START_LOCATION_ID_URL; + break; + case LLSLURL::LAST_LOCATION: + agent_location_id = START_LOCATION_ID_LAST; + break; + default: + agent_location_id = START_LOCATION_ID_HOME; + break; + } gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -1160,6 +1151,7 @@ bool idle_startup() gViewerWindow->setShowProgress(!gSavedSettings.getBOOL("AscentDisableLogoutScreens")); gViewerWindow->setProgressCancelButtonVisible(TRUE, LLTrans::getString("Quit")); + gViewerWindow->revealIntroPanel(); // Poke the VFS, which could potentially block for a while if // Windows XP is acting up set_startup_status(0.07f, LLTrans::getString("LoginVerifyingCache"), LLStringUtil::null); @@ -1244,97 +1236,41 @@ bool idle_startup() auth_method = "login_to_simulator"; auth_desc = LLTrans::getString("LoginInProgress"); + set_startup_status(progress, auth_desc, auth_message); LLStartUp::setStartupState( STATE_XMLRPC_LEGACY_LOGIN ); // XMLRPC } - // OGPX : Note that this uses existing STATE_LOGIN_AUTHENTICATE in viewer, - // and also inserts two new states for LEGACY (where Legacy in this case - // was LLSD HTTP Post in OGP9, and not XML-RPC). - // - // The OGP login daisy chains together several POSTs that must complete successfully - // in order for startup state to finally get set to STATE_LOGIN_PROCESS_RESPONSE. - // - - if (STATE_LOGIN_AUTHENTICATE == LLStartUp::getStartupState()) - { - redirect_uri.clear(); - - LL_DEBUGS("AppInit") << "STATE_LOGIN_AUTHENTICATE" << LL_ENDL; - set_startup_status(progress, auth_desc, auth_message); - - LLSD args; - LLSD identifier; - LLSD authenticator; - - identifier["type"] = "agent"; - identifier["first_name"] = firstname; - identifier["last_name"] = lastname; - authenticator["type"] = "hash"; - authenticator["algorithm"] = "md5"; - authenticator["secret"] = password; - args["identifier"] = identifier; - args["authenticator"] = authenticator; - - - //args["firstname"] = firstname; - //args["lastname"] = lastname; - //args["md5-password"] = password; - - // allows you to 'suggest' which agent service you'd like to use - std::string agenturi = gSavedSettings.getString("CmdLineAgentURI"); - if (!agenturi.empty()) - { - args["agent_url"] = agenturi; - } - - char hashed_mac_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ - LLMD5 hashed_mac; - hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES ); - hashed_mac.finalize(); - hashed_mac.hex_digest(hashed_mac_string); - args["mac_address"] = hashed_mac_string; - - args["id0"] = LLAppViewer::instance()->getSerialNumber(); - - args["agree_to_tos"] = gAcceptTOS; - args["read_critical"] = gAcceptCriticalMessage; - - LLViewerLogin* vl = LLViewerLogin::getInstance(); - std::string grid_uri = vl->getCurrentGridURI(); - - gAcceptTOS = FALSE; - gAcceptCriticalMessage = FALSE; - - LLStartUp::setStartupState(STATE_WAIT_LEGACY_LOGIN); - return FALSE; - } - if (STATE_XMLRPC_LEGACY_LOGIN == LLStartUp::getStartupState()) { lldebugs << "STATE_XMLRPC_LEGACY_LOGIN" << llendl; progress += 0.02f; display_startup(); + LLSLURL start_slurl = LLStartUp::getStartSLURL(); std::stringstream start; - if (LLURLSimString::parse()) + LLSLURL::SLURL_TYPE start_slurl_type = start_slurl.getType(); + switch ( start_slurl_type ) { + case LLSLURL::LOCATION: + { // a startup URL was specified std::stringstream unescaped_start; unescaped_start << "uri:" - << LLURLSimString::sInstance.mSimName << "&" - << LLURLSimString::sInstance.mX << "&" - << LLURLSimString::sInstance.mY << "&" - << LLURLSimString::sInstance.mZ; + << start_slurl.getRegion() << "&" + << start_slurl.getPosition().mV[VX] << "&" + << start_slurl.getPosition().mV[VY] << "&" + << start_slurl.getPosition().mV[VZ]; start << xml_escape_string(unescaped_start.str()); - - } - else if (gSavedSettings.getBOOL("LoginLastLocation")) - { - start << "last"; - } - else - { + } + break; + case LLSLURL::HOME_LOCATION: start << "home"; + break; + case LLSLURL::LAST_LOCATION: + start << "last"; + break; + default: + break; } char hashed_mac_string[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */ @@ -1343,7 +1279,6 @@ bool idle_startup() hashed_mac.finalize(); hashed_mac.hex_digest(hashed_mac_string); - LLViewerLogin* vl = LLViewerLogin::getInstance(); std::string grid_uri = vl->getCurrentGridURI(); if(!redirect_uri.empty()) @@ -1364,7 +1299,8 @@ bool idle_startup() lastname, password, // web_login_key, start.str(), - gSkipOptionalUpdate, + //gSkipOptionalUpdate, + true, gAcceptTOS, gAcceptCriticalMessage, gLastExecEvent, @@ -1440,7 +1376,7 @@ bool idle_startup() LL_DEBUGS("AppInit") << "STATE_LOGIN_PROCESS_RESPONSE" << LL_ENDL; std::ostringstream emsg; bool quit = false; - bool update = false; + //bool update = false; bool successful_login = false; LLUserAuth::UserAuthcode error = LLUserAuth::getInstance()->authResponse(); // reset globals @@ -1460,6 +1396,11 @@ bool idle_startup() reason_response = response["reason"].asString(); message_response = response["message"].asString(); message_id = response["message_id"].asString(); + { + std::stringstream dump_str; + dump_str << response; + llinfos << dump_str.str() << llendl; + } if(login_response == "true") { @@ -1487,6 +1428,7 @@ bool idle_startup() } else { + emsg << LLTrans::getString("LoginFailed") + "\n"; if (!message_response.empty()) @@ -1509,6 +1451,7 @@ bool idle_startup() if (show_connect_box) { LL_DEBUGS("AppInit") << "Need tos agreement" << LL_ENDL; + LLStartUp::setStartupState( STATE_UPDATE_CHECK ); LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS, message_response); @@ -1544,7 +1487,7 @@ bool idle_startup() // Clear the password password = ""; } - if(reason_response == "update") + /*if(reason_response == "update") { auth_message = message_response; update = true; @@ -1560,7 +1503,7 @@ bool idle_startup() gSkipOptionalUpdate = TRUE; return false; } - } + }*/ } break; default: @@ -1587,14 +1530,6 @@ bool idle_startup() break; } - if (update || gSavedSettings.getBOOL("ForceMandatoryUpdate")) - { - gSavedSettings.setBOOL("ForceMandatoryUpdate", FALSE); - update_app(TRUE, auth_message); - LLStartUp::setStartupState( STATE_UPDATE_CHECK ); - return false; - } - // Version update and we're not showing the dialog if(quit) { @@ -1634,8 +1569,7 @@ bool idle_startup() LLSD args; args["ERROR_MESSAGE"] = emsg.str(); LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); - gSavedSettings.setBOOL("AutoLogin", FALSE); + transition_back_to_login_panel(emsg.str()); show_connect_box = true; } } @@ -1650,10 +1584,11 @@ bool idle_startup() // Bounce back to the login screen. LLSD args; args["ERROR_MESSAGE"] = emsg.str(); + LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); - reset_login(); - gSavedSettings.setBOOL("AutoLogin", FALSE); + transition_back_to_login_panel(emsg.str()); show_connect_box = true; + return FALSE; } return FALSE; } @@ -1741,8 +1676,6 @@ bool idle_startup() // object is created. I think this must be done after setting the region. JC gAgent.setPositionAgent(agent_start_position_region); - wlfPanel_AdvSettings::fixPanel(); - display_startup(); LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT ); return FALSE; @@ -1864,9 +1797,6 @@ bool idle_startup() handleCloudSettingsChanged(LLSD()); display_startup(); - // Move the progress view in front of the UI - gViewerWindow->moveProgressViewToFront(); - display_startup(); LLError::logToFixedBuffer(gDebugView->mDebugConsolep); display_startup(); @@ -2487,38 +2417,24 @@ bool idle_startup() // JC - 7/20/2002 gViewerWindow->sendShapeToSim(); + // The reason we show the alert is because we want to + // reduce confusion for when you log in and your provided + // location is not your expected location. So, if this is + // your first login, then you do not have an expectation, + // thus, do not show this alert. if (!gAgent.isFirstLogin()) { - bool url_ok = LLURLSimString::sInstance.parse(); - if (!((gAgentStartLocation == "url" && url_ok) || - (!url_ok && ((gAgentStartLocation == "last" && gSavedSettings.getBOOL("LoginLastLocation")) || - (gAgentStartLocation == "home" && !gSavedSettings.getBOOL("LoginLastLocation")))))) - { - // The reason we show the alert is because we want to - // reduce confusion for when you log in and your provided - // location is not your expected location. So, if this is - // your first login, then you do not have an expectation, - // thus, do not show this alert. - LLSD args; - if (url_ok) - { - args["TYPE"] = "desired"; - args["HELP"] = ""; - } - else if (gSavedSettings.getBOOL("LoginLastLocation")) - { - args["TYPE"] = "last"; - args["HELP"] = ""; - } - else - { - args["TYPE"] = "home"; - args["HELP"] = "You may want to set a new home location."; - } - LLNotificationsUtil::add("AvatarMoved", args); - } - else + llinfos << "gAgentStartLocation : " << gAgentStartLocation << llendl; + LLSLURL start_slurl = LLStartUp::getStartSLURL(); + LL_DEBUGS("AppInit") << "start slurl "<getWindow()->resetBusyCount(); gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW); LL_DEBUGS("AppInit") << "Done releasing bitmap" << LL_ENDL; - gViewerWindow->setShowProgress(FALSE); + + gViewerWindow->setStartupComplete(); gViewerWindow->setProgressCancelButtonVisible(FALSE); display_startup(); @@ -2703,7 +2643,7 @@ bool idle_startup() } // If we've got a startup URL, dispatch it - LLStartUp::dispatchURL(); + //LLStartUp::dispatchURL(); // Retrieve information about the land data // (just accessing this the first time will fetch it, @@ -2923,7 +2863,7 @@ void show_first_run_dialog() bool first_run_dialog_callback(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if (0 == option) { LL_DEBUGS("AppInit") << "First run dialog cancelling" << LL_ENDL; @@ -2973,7 +2913,7 @@ void set_startup_status(const F32 frac, const std::string& string, const std::st bool login_alert_status(const LLSD& notification, const LLSD& response) { - S32 option = LLNotification::getSelectedOption(notification, response); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); // Buttons switch( option ) { @@ -2987,7 +2927,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) } case 2: // Teleport // Restart the login process, starting at our home locaton - LLURLSimString::setString(LLURLSimString::sLocationStringHome); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); break; default: @@ -2998,201 +2938,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) return false; } -void update_app(BOOL mandatory, const std::string& auth_msg) -{ - // store off config state, as we might quit soon - gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), TRUE); - std::ostringstream message; - - // *TODO:translate - std::string msg; - if (!auth_msg.empty()) - { - msg = "(" + auth_msg + ") \n"; - } - - LLSD args; - args["MESSAGE"] = msg; - - LLSD payload; - payload["mandatory"] = mandatory; - -/* - We're constructing one of the following 6 strings here: - "DownloadWindowsMandatory" - "DownloadWindowsReleaseForDownload" - "DownloadWindows" - "DownloadMacMandatory" - "DownloadMacReleaseForDownload" - "DownloadMac" - - I've called them out explicitly in this comment so that they can be grepped for. - - Also, we assume that if we're not Windows we're Mac. If we ever intend to support - Linux with autoupdate, this should be an explicit #elif LL_DARWIN, but - we'd rather deliver the wrong message than no message, so until Linux is supported - we'll leave it alone. - */ - std::string notification_name = "Download"; - -#if LL_WINDOWS - notification_name += "Windows"; -#else - notification_name += "Mac"; -#endif - - if (mandatory) - { - notification_name += "Mandatory"; - } - else - { -#if LL_RELEASE_FOR_DOWNLOAD - notification_name += "ReleaseForDownload"; -#endif - } - - LLNotifications::instance().add(notification_name, args, payload, update_dialog_callback); - -} - -bool update_dialog_callback(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotification::getSelectedOption(notification, response); - std::string update_exe_path; - bool mandatory = notification["payload"]["mandatory"].asBoolean(); - -#if !LL_RELEASE_FOR_DOWNLOAD - if (option == 2) - { - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); - return false; - } -#endif - - if (option == 1) - { - // ...user doesn't want to do it - if (mandatory) - { - LLAppViewer::instance()->forceQuit(); - // Bump them back to the login screen. - //reset_login(); - } - else - { - LLStartUp::setStartupState( STATE_LOGIN_AUTH_INIT ); - } - return false; - } - - LLSD query_map = LLSD::emptyMap(); - // *TODO place os string in a global constant -#if LL_WINDOWS - query_map["os"] = "win"; -#elif LL_DARWIN - query_map["os"] = "mac"; -#elif LL_LINUX - query_map["os"] = "lnx"; -#elif LL_SOLARIS - query_map["os"] = "sol"; -#endif - // *TODO change userserver to be grid on both viewer and sim, since - // userserver no longer exists. - query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); - // - query_map["channel"] = gVersionChannel; - - // *TODO constantize this guy - // *NOTE: This URL is also used in win_setup/lldownloader.cpp - LLURI update_url = LLURI::buildHTTP("secondlife.com", 80, "update.php", query_map); - - if(LLAppViewer::sUpdaterInfo) - { - delete LLAppViewer::sUpdaterInfo ; - } - LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; - -#if LL_WINDOWS - LLAppViewer::sUpdaterInfo->mUpdateExePath = gDirUtilp->getTempFilename(); - if (LLAppViewer::sUpdaterInfo->mUpdateExePath.empty()) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - // We're hosed, bail - LL_WARNS("AppInit") << "LLDir::getTempFilename() failed" << LL_ENDL; - LLAppViewer::instance()->forceQuit(); - return false; - } - - LLAppViewer::sUpdaterInfo->mUpdateExePath += ".exe"; - - std::string updater_source = gDirUtilp->getAppRODataDir(); - updater_source += gDirUtilp->getDirDelimiter(); - updater_source += "updater.exe"; - - LL_DEBUGS("AppInit") << "Calling CopyFile source: " << updater_source - << " dest: " << LLAppViewer::sUpdaterInfo->mUpdateExePath - << LL_ENDL; - - - if (!CopyFileA(updater_source.c_str(), LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str(), FALSE)) - { - delete LLAppViewer::sUpdaterInfo ; - LLAppViewer::sUpdaterInfo = NULL ; - - LL_WARNS("AppInit") << "Unable to copy the updater!" << LL_ENDL; - LLAppViewer::instance()->forceQuit(); - return false; - } - - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) - { - // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); - }; - - LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\""; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL; - - //Explicitly remove the marker file, otherwise we pass the lock onto the child process and things get weird. - LLAppViewer::instance()->removeMarkerFile(); // In case updater fails - -#elif LL_DARWIN - // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) - { - // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); - }; - - LLAppViewer::sUpdaterInfo->mUpdateExePath = "'"; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += update_url.asString(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -name \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += LLAppViewer::instance()->getSecondLifeTitle(); - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" -bundleid \""; - LLAppViewer::sUpdaterInfo->mUpdateExePath += gVersionBundleID; - LLAppViewer::sUpdaterInfo->mUpdateExePath += "\" &"; - - LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << LL_ENDL; - - // Run the auto-updater. - system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */ - -#elif LL_LINUX || LL_SOLARIS - OSMessageBox("Automatic updating is not yet implemented for Linux.\n" - "Please download the latest version from www.secondlife.com.", - LLStringUtil::null, OSMB_OK); -#endif - LLAppViewer::instance()->forceQuit(); - return false; -} void use_circuit_callback(void**, S32 result) { @@ -3616,8 +3362,6 @@ std::string LLStartUp::startupStateToString(EStartupState state) RTNENUM( STATE_LOGIN_VOICE_LICENSE ); RTNENUM( STATE_UPDATE_CHECK ); RTNENUM( STATE_LOGIN_AUTH_INIT ); - RTNENUM( STATE_LOGIN_AUTHENTICATE ); - RTNENUM( STATE_WAIT_LEGACY_LOGIN ); RTNENUM( STATE_XMLRPC_LEGACY_LOGIN ); RTNENUM( STATE_LOGIN_NO_DATA_YET ); RTNENUM( STATE_LOGIN_DOWNLOADING ); @@ -3691,8 +3435,6 @@ void reset_login() //--------------------------------------------------------------------------- -std::string LLStartUp::sSLURLCommand; - bool LLStartUp::canGoFullscreen() { return gStartupState >= STATE_WORLD_INIT; @@ -3749,67 +3491,64 @@ void LLStartUp::cleanupNameCache() delete gCacheName; gCacheName = NULL; } + bool LLStartUp::dispatchURL() { // ok, if we've gotten this far and have a startup URL - if (!sSLURLCommand.empty()) + if (!getStartSLURL().isValid()) { - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser); + return false; } - else if (LLURLSimString::parse()) + if(getStartSLURL().getType() != LLSLURL::APP) { + // If we started with a location, but we're already // at that location, don't pop dialogs open. LLVector3 pos = gAgent.getPositionAgent(); - F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX; - F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY; + LLVector3 slurlpos = getStartSLURL().getPosition(); + F32 dx = pos.mV[VX] - slurlpos.mV[VX]; + F32 dy = pos.mV[VY] - slurlpos.mV[VY]; const F32 SLOP = 2.f; // meters - if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName() + if( getStartSLURL().getRegion() != gAgent.getRegion()->getName() || (dx*dx > SLOP*SLOP) || (dy*dy > SLOP*SLOP) ) { - std::string url = LLURLSimString::getURL(); - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, web, trusted_browser); + LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), "clicked", + NULL, false); } return true; } return false; } -bool login_alert_done(const LLSD& notification, const LLSD& response) +void LLStartUp::setStartSLURL(const LLSLURL& slurl) { - LLPanelLogin::giveFocus(); - return false; + LL_DEBUGS("AppInit")<banUdpMessage(item); - - start = comma + 1; - - } - while(comma < csv.length()); - + return sStartSLURL; } /* @@ -4064,6 +3803,36 @@ bool LLStartUp::startLLProxy() return proxy_ok; } + +bool login_alert_done(const LLSD& notification, const LLSD& response) +{ + LLPanelLogin::giveFocus(); + return false; +} +void apply_udp_blacklist(const std::string& csv) +{ + + std::string::size_type start = 0; + std::string::size_type comma = 0; + do + { + comma = csv.find(",", start); + if (comma == std::string::npos) + { + comma = csv.length(); + } + std::string item(csv, start, comma-start); + + lldebugs << "udp_blacklist " << item << llendl; + gMessageSystem->banUdpMessage(item); + + start = comma + 1; + + } + while(comma < csv.length()); + +} + bool process_login_success_response(std::string& password) { LLSD response = LLUserAuth::getInstance()->getResponse(); @@ -4079,9 +3848,17 @@ bool process_login_success_response(std::string& password) if(!text.empty()) gAgentID.set(text); gDebugInfo["AgentID"] = text; + // Agent id needed for parcel info request in LLUrlEntryParcel + // to resolve parcel name. + LLUrlEntryParcel::setAgentID(gAgentID); + text = response["session_id"].asString(); if(!text.empty()) gAgentSessionID.set(text); gDebugInfo["SessionID"] = text; + + // Session id needed for parcel info request in LLUrlEntryParcel + // to resolve parcel name. + LLUrlEntryParcel::setSessionID(gAgentSessionID); text = response["secure_session_id"].asString(); if(!text.empty()) gAgent.mSecureSessionID.set(text); @@ -4120,11 +3897,11 @@ bool process_login_success_response(std::string& password) std::string history_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "saved_logins_sg2.xml"); LLSavedLogins history_data = LLSavedLogins::loadFile(history_file); - std::string grid_nick = gHippoGridManager->getConnectedGrid()->getGridName(); - history_data.deleteEntry(firstname, lastname, grid_nick); + std::string grid_name = gHippoGridManager->getConnectedGrid()->getGridName(); + history_data.deleteEntry(firstname, lastname, grid_name); if (gSavedSettings.getBOOL("RememberLogin")) { - LLSavedLoginEntry login_entry(firstname, lastname, password, grid_nick); + LLSavedLoginEntry login_entry(firstname, lastname, password, grid_name); history_data.addEntry(login_entry); } else @@ -4409,8 +4186,6 @@ bool process_login_success_response(std::string& password) LLViewerMedia::openIDSetup(openid_url, openid_token); } - gIMMgr->loadIgnoreGroup(); - bool success = false; // JC: gesture loading done below, when we have an asset system // in place. Don't delete/clear user_credentials until then. @@ -4426,3 +4201,9 @@ bool process_login_success_response(std::string& password) return success; } +void transition_back_to_login_panel(const std::string& emsg) +{ + // Bounce back to the login screen. + reset_login(); // calls LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + gSavedSettings.setBOOL("AutoLogin", FALSE); +} diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 021cba775..f53d9d5b0 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -37,6 +37,7 @@ class LLViewerTexture ; class LLEventPump; +class LLSLURL; #include "llviewerstats.h" @@ -58,8 +59,6 @@ typedef enum { STATE_LOGIN_VOICE_LICENSE, // Show license agreement for using voice STATE_UPDATE_CHECK, // Wait for user at a dialog box (updates, term-of-service, etc) STATE_LOGIN_AUTH_INIT, // Start login to SL servers - STATE_LOGIN_AUTHENTICATE, // Do authentication voodoo - STATE_WAIT_LEGACY_LOGIN, // Waiting for legacy login STATE_XMLRPC_LEGACY_LOGIN, // XMLRPC for legacy login, OGPX maintain legacy XMLRPC STATE_LOGIN_NO_DATA_YET, // Waiting for authentication replies to start STATE_LOGIN_DOWNLOADING, // Waiting for authentication replies to download @@ -133,14 +132,14 @@ public: // the viewer, dispatch it static void postStartupState(); - static std::string sSLURLCommand; - // *HACK: On startup, if we were passed a secondlife://app/do/foo - // command URL, store it for later processing. + static void setStartSLURL(const LLSLURL& slurl); + static LLSLURL& getStartSLURL(); static bool startLLProxy(); // Initialize the SOCKS 5 proxy static LLViewerStats::PhaseMap& getPhases() { return *sPhases; } private: + static LLSLURL sStartSLURL; static std::string startupStateToString(EStartupState state); static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index b14941a84..e5f14641e 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -1016,7 +1016,7 @@ class LLBalanceHandler : public LLCommandHandler { public: // Requires "trusted" browser/URL source - LLBalanceHandler() : LLCommandHandler("balance", true) { } + LLBalanceHandler() : LLCommandHandler("balance", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (tokens.size() == 1 diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp index 6f4b2014c..18155eb58 100644 --- a/indra/newview/lltexturectrl.cpp +++ b/indra/newview/lltexturectrl.cpp @@ -120,6 +120,7 @@ public: const LLRect& rect, const std::string& label, PermissionMask immediate_filter_perm_mask, + PermissionMask dnd_filter_perm_mask, PermissionMask non_immediate_filter_perm_mask, BOOL can_apply_immediately, const std::string& fallback_image_name); @@ -211,6 +212,7 @@ protected: LLFilterEditor* mFilterEdit; LLInventoryPanel* mInventoryPanel; PermissionMask mImmediateFilterPermMask; + PermissionMask mDnDFilterPermMask; PermissionMask mNonImmediateFilterPermMask; BOOL mCanApplyImmediately; BOOL mNoCopyTextureSelected; @@ -225,6 +227,7 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( const LLRect& rect, const std::string& label, PermissionMask immediate_filter_perm_mask, + PermissionMask dnd_filter_perm_mask, PermissionMask non_immediate_filter_perm_mask, BOOL can_apply_immediately, const std::string& fallback_image_name) @@ -248,6 +251,7 @@ LLFloaterTexturePicker::LLFloaterTexturePicker( mActive( TRUE ), mFilterEdit(NULL), mImmediateFilterPermMask(immediate_filter_perm_mask), + mDnDFilterPermMask(dnd_filter_perm_mask), mNonImmediateFilterPermMask(non_immediate_filter_perm_mask), mContextConeOpacity(0.f), mSelectedItemPinned(FALSE) @@ -363,8 +367,8 @@ BOOL LLFloaterTexturePicker::handleDragAndDrop( if (mod) item_perm_mask |= PERM_MODIFY; if (xfer) item_perm_mask |= PERM_TRANSFER; - - PermissionMask filter_perm_mask = mImmediateFilterPermMask; + //PermissionMask filter_perm_mask = getFilterPermMask(); Commented out due to no-copy texture loss. + PermissionMask filter_perm_mask = mDnDFilterPermMask; if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask ) { if (drop) @@ -1166,9 +1170,6 @@ LLXMLNodePtr LLTextureCtrl::getXML(bool save_children) const LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("texture_picker"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent); @@ -1199,7 +1200,7 @@ LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactor } LLTextureCtrl* texture_picker = new LLTextureCtrl( - name, + "texture_picker", rect, label, LLUUID(image_id), @@ -1332,6 +1333,7 @@ void LLTextureCtrl::showPicker(BOOL take_focus) rect, mLabel, mImmediateFilterPermMask, + mDnDFilterPermMask, mNonImmediateFilterPermMask, mCanApplyImmediately, mFallbackImageName); diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h index 15ef2c500..27afeba3d 100644 --- a/indra/newview/lltexturectrl.h +++ b/indra/newview/lltexturectrl.h @@ -132,6 +132,8 @@ public: void setImmediateFilterPermMask(PermissionMask mask) { mImmediateFilterPermMask = mask; } + void setDnDFilterPermMask(PermissionMask mask) + { mDnDFilterPermMask = mask; } void setNonImmediateFilterPermMask(PermissionMask mask) { mNonImmediateFilterPermMask = mask; } PermissionMask getImmediateFilterPermMask() { return mImmediateFilterPermMask; } @@ -185,6 +187,7 @@ private: BOOL mAllowInvisibleTexture; // If true, the user can select "Invisible" as an option LLCoordGL mLastFloaterLeftTop; PermissionMask mImmediateFilterPermMask; + PermissionMask mDnDFilterPermMask; PermissionMask mNonImmediateFilterPermMask; BOOL mCanApplyImmediately; BOOL mNeedsRawImageData; diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index 6ebeb870a..0740fc38b 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -66,6 +66,21 @@ LLTool::~LLTool() } } +BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +{ + BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); + + // This behavior was moved here from LLViewerWindow::handleAnyMouseClick, so it can be selectively overridden by LLTool subclasses. + if(down && result) + { + // This is necessary to force clicks in the world to cause edit + // boxes that might have keyboard focus to relinquish it, and hence + // cause a commit to update their value. JC + gFocusMgr.setKeyboardFocus(NULL); + } + + return result; +} BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask) { diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index f954a8c24..3fec2c8e8 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -55,6 +55,7 @@ public: virtual BOOL isView() const { return FALSE; } // Virtual functions inherited from LLMouseHandler + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp index c884af9bb..791f8f248 100644 --- a/indra/newview/lltoolbar.cpp +++ b/indra/newview/lltoolbar.cpp @@ -114,7 +114,7 @@ F32 LLToolBar::sInventoryAutoOpenTime = 1.f; // LLToolBar::LLToolBar() -: LLPanel() +: LLLayoutPanel() #if LL_DARWIN , mResizeHandle(NULL) #endif // LL_DARWIN diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h index 24b05c8ae..28fcba9df 100644 --- a/indra/newview/lltoolbar.h +++ b/indra/newview/lltoolbar.h @@ -34,6 +34,7 @@ #define LL_LLTOOLBAR_H #include "llpanel.h" +#include "lllayoutstack.h" #include "llframetimer.h" @@ -47,7 +48,7 @@ extern S32 TOOL_BAR_HEIGHT; class LLFlyoutButton; class LLToolBar -: public LLPanel +: public LLLayoutPanel { public: LLToolBar(); diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index f3a1831db..e3dce440d 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -46,6 +46,7 @@ #include "llhoverview.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" +#include "llmediaentry.h" #include "llmenugl.h" #include "llmutelist.h" #include "llselectmgr.h" @@ -91,6 +92,15 @@ LLToolPie::LLToolPie() { } +BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) +{ + BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); + + // This override DISABLES the keyboard focus reset that LLTool::handleAnyMouseClick adds. + // LLToolPie will do the right thing in its pick callback. + + return result; +} BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) { @@ -650,12 +660,9 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) if(!object) { - gViewerWindow->setCursor(UI_CURSOR_ARROW); + //gViewerWindow->setCursor(UI_CURSOR_ARROW); // We need to clear media hover flag - if (LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); - } + LLViewerMediaFocus::getInstance()->clearHover(); } return TRUE; @@ -894,7 +901,6 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs()) { - LLSelectMgr::getInstance()->deselect(); LLViewerMediaFocus::getInstance()->clearFocus(); return false; @@ -905,30 +911,33 @@ bool LLToolPie::handleMediaClick(const LLPickInfo& pick) if(!tep) return false; - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); - if (media_impl.isNull() || !media_impl->hasMedia()) + LLMediaEntry* mep = (tep->hasMedia()) ? tep->getMediaData() : NULL; + if(!mep) return false; + + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); if (gSavedSettings.getBOOL("MediaOnAPrimUI")) { - LLObjectSelectionHandle selection = LLViewerMediaFocus::getInstance()->getSelection(); - if (!selection->contains(pick.getObject(), pick.mObjectFace)) + if (!LLViewerMediaFocus::getInstance()->isFocusedOnFace(pick.getObject(), pick.mObjectFace) || media_impl.isNull()) { - LLViewerMediaFocus::getInstance()->setFocusFace(TRUE, pick.getObject(), pick.mObjectFace, media_impl); + // It's okay to give this a null impl + LLViewerMediaFocus::getInstance()->setFocusFace(pick.getObject(), pick.mObjectFace, media_impl, pick.mNormal); } else { - media_impl->mouseDown(pick.mXYCoords.mX, pick.mXYCoords.mY, gKeyboard->currentMask(TRUE)); - media_impl->mouseCapture(); // the mouse-up will happen when capture is lost + // Make sure keyboard focus is set to the media focus object. + gFocusMgr.setKeyboardFocus(LLViewerMediaFocus::getInstance()); + LLEditMenuHandler::gEditMenuHandler = LLViewerMediaFocus::instance().getFocusedMediaImpl(); + + media_impl->mouseDown(pick.mUVCoords, gKeyboard->currentMask(TRUE)); + mMediaMouseCaptureID = mep->getMediaID(); + setMouseCapture(TRUE); // This object will send a mouse-up to the media when it loses capture. } return true; } - LLSelectMgr::getInstance()->deselect(); LLViewerMediaFocus::getInstance()->clearFocus(); return false; @@ -948,38 +957,47 @@ bool LLToolPie::handleMediaHover(const LLPickInfo& pick) pick.mObjectFace < 0 || pick.mObjectFace >= objectp->getNumTEs() ) { - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + LLViewerMediaFocus::getInstance()->clearHover(); return false; } - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - // is media playing on this face? + // Does this face have media? const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); - viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); - if (tep - && media_impl.notNull() - && media_impl->hasMedia() + if(!tep) + return false; + + const LLMediaEntry* mep = tep->hasMedia() ? tep->getMediaData() : NULL; + if (mep && gSavedSettings.getBOOL("MediaOnAPrimUI")) - { - if(LLViewerMediaFocus::getInstance()->getFocus()) + { + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(mep->getMediaID()); + + if(media_impl.notNull()) { - media_impl->mouseMove(pick.mXYCoords.mX, pick.mXYCoords.mY, gKeyboard->currentMask(TRUE)); - } + // Update media hover object + if (!LLViewerMediaFocus::getInstance()->isHoveringOverFace(objectp, pick.mObjectFace)) + { + LLViewerMediaFocus::getInstance()->setHoverFace(objectp, pick.mObjectFace, media_impl, pick.mNormal); + } + + // If this is the focused media face, send mouse move events. + if (LLViewerMediaFocus::getInstance()->isFocusedOnFace(objectp, pick.mObjectFace)) + { + media_impl->mouseMove(pick.mUVCoords, gKeyboard->currentMask(TRUE)); + gViewerWindow->setCursor(media_impl->getLastSetCursor()); + } + else + { + // This is not the focused face -- set the default cursor. + gViewerWindow->setCursor(UI_CURSOR_ARROW); + } - // Set mouse over flag if unset - if (! LLViewerMediaFocus::getInstance()->getMouseOverFlag()) - { - LLSelectMgr::getInstance()->setHoverObject(objectp, pick.mObjectFace); - LLViewerMediaFocus::getInstance()->setMouseOverFlag(true, media_impl); - LLViewerMediaFocus::getInstance()->setPickInfo(pick); + return true; } - - return true; } - LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + + // In all other cases, clear media hover. + LLViewerMediaFocus::getInstance()->clearHover(); return false; } diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index f811c53c2..5f3c75c3b 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -46,6 +46,7 @@ public: LLToolPie( ); // Virtual functions inherited from LLMouseHandler + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index a9971abbd..dcf955cef 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -39,51 +39,46 @@ #include "llfloaterurldisplay.h" #include "llfloaterdirectory.h" #include "llfloaterworldmap.h" -#include "llfloatermediabrowser.h" #include "llpanellogin.h" +#include "llregionhandle.h" +#include "llslurl.h" #include "llstartup.h" // gStartupState -#include "llurlsimstring.h" #include "llweb.h" #include "llworldmap.h" #include "llworldmapmessage.h" +#include "llviewernetwork.h" + +#include "hippogridmanager.h" // library includes +#include "llnotificationsutil.h" #include "llsd.h" -const std::string SLURL_SL_HELP_PREFIX = "secondlife://app."; -const std::string SLURL_SL_PREFIX = "sl://"; -const std::string SLURL_SECONDLIFE_PREFIX = "secondlife://"; -const std::string SLURL_SLURL_PREFIX = "http://slurl.com/secondlife/"; -const std::string SLURL_SLURL_ALT_PREFIX = "http://maps.secondlife.com/secondlife/"; - -const std::string SLURL_APP_TOKEN = "app/"; - class LLURLDispatcherImpl { public: - static bool isSLURL(const std::string& url); - - static bool isSLURLCommand(const std::string& url); - - static bool dispatch(const std::string& url, + static bool dispatch(const LLSLURL& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser); // returns true if handled or explicitly blocked. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const LLSLURL& slurl); private: - static bool dispatchCore(const std::string& url, + static bool dispatchCore(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); // handles both left and right click - static bool dispatchHelp(const std::string& url, bool right_mouse); + static bool dispatchHelp(const LLSLURL& slurl, bool right_mouse); // Handles sl://app.floater.html.help by showing Help floater. // Returns true if handled. - static bool dispatchApp(const std::string& url, + static bool dispatchApp(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); @@ -91,233 +86,158 @@ private: // by showing panel in Search floater. // Returns true if handled or explicitly blocked. - static bool dispatchRegion(const std::string& url, bool right_mouse); + static bool dispatchRegion(const LLSLURL& slurl, const std::string& nav_type, bool right_mouse); // handles secondlife://Ahern/123/45/67/ // Returns true if handled. - static void regionHandleCallback(U64 handle, const std::string& url, + static void regionHandleCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a location has been resolved to a // region name - static void regionNameCallback(U64 handle, const std::string& url, + static void regionNameCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a region name has been resolved to a // location in-world, used by places-panel display. - static bool matchPrefix(const std::string& url, const std::string& prefix); - - static std::string stripProtocol(const std::string& url); - friend class LLTeleportHandler; }; // static -bool LLURLDispatcherImpl::isSLURL(const std::string& url) -{ - if (matchPrefix(url, SLURL_SL_HELP_PREFIX)) return true; - if (matchPrefix(url, SLURL_SL_PREFIX)) return true; - if (matchPrefix(url, SLURL_SECONDLIFE_PREFIX)) return true; - if (matchPrefix(url, SLURL_SLURL_PREFIX)) return true; - if (matchPrefix(url, SLURL_SLURL_ALT_PREFIX)) return true; - return false; -} - -// static -bool LLURLDispatcherImpl::isSLURLCommand(const std::string& url) -{ - if (matchPrefix(url, SLURL_SL_PREFIX + SLURL_APP_TOKEN) - || matchPrefix(url, SLURL_SECONDLIFE_PREFIX + "/" + SLURL_APP_TOKEN) - || matchPrefix(url, SLURL_SLURL_PREFIX + SLURL_APP_TOKEN) - || matchPrefix(url, SLURL_SLURL_ALT_PREFIX + SLURL_APP_TOKEN)) - { - return true; - } - return false; -} - -// static -bool LLURLDispatcherImpl::dispatchCore(const std::string& url, +bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - if (url.empty()) return false; - if (dispatchHelp(url, right_mouse)) return true; - if (dispatchApp(url, right_mouse, web, trusted_browser)) return true; - if (dispatchRegion(url, right_mouse)) return true; + //if (dispatchHelp(slurl, right_mouse)) return true; + switch(slurl.getType()) + { + case LLSLURL::APP: + return dispatchApp(slurl, nav_type, right_mouse, web, trusted_browser); + case LLSLURL::LOCATION: + return dispatchRegion(slurl, nav_type, right_mouse); + default: + return false; + } /* // Inform the user we can't handle this std::map args; - args["SLURL"] = url; + args["SLURL"] = slurl; r; */ - - return false; } // static -bool LLURLDispatcherImpl::dispatch(const std::string& url, +bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser) { - llinfos << "url: " << url << llendl; const bool right_click = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, nav_type, right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url) +bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) { - llinfos << "url: " << url << llendl; const bool right_click = true; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, "clicked", right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchHelp(const std::string& url, bool right_mouse) -{ -#if LL_LIBXUL_ENABLED - if (matchPrefix(url, SLURL_SL_HELP_PREFIX)) - { - gViewerHtmlHelp.show(); - return true; - } -#endif - return false; -} - -// static -bool LLURLDispatcherImpl::dispatchApp(const std::string& url, +bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, + const std::string& nav_type, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - if (!isSLURL(url)) - { - return false; - } - - LLURI uri(url); - LLSD pathArray = uri.pathArray(); - pathArray.erase(0); // erase "app" - std::string cmd = pathArray.get(0); - pathArray.erase(0); // erase "cmd" + llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl; + const LLSD& query_map = LLURI::queryMap(slurl.getAppQuery()); bool handled = LLCommandDispatcher::dispatch( - cmd, pathArray, uri.queryMap(), web, trusted_browser); - return handled; + slurl.getAppCmd(), slurl.getAppPath(), query_map, web, nav_type, trusted_browser); + + // alert if we didn't handle this secondlife:///app/ SLURL + // (but still return true because it is a valid app SLURL) + if (! handled) + { + LLNotificationsUtil::add("UnsupportedCommandSLURL"); + } + return true; } // static -bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mouse) +bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, const std::string& nav_type, bool right_mouse) { - if (!isSLURL(url)) - { + if(slurl.getType() != LLSLURL::LOCATION) + { return false; - } - + } // Before we're logged in, need to update the startup screen // to tell the user where they are going. if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { - // Parse it and stash in globals, it will be dispatched in - // STATE_CLEANUP. - LLURLSimString::setString(url); // We're at the login screen, so make sure user can see // the login location box to know where they are going. - LLPanelLogin::refreshLocation( true ); + LLPanelLogin::setLocation(slurl); return true; } - std::string sim_string = stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - LLFloaterURLDisplay* url_displayp = LLFloaterURLDisplay::getInstance(LLSD()); - url_displayp->setName(region_name); - // Request a region handle by name - LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), LLURLDispatcherImpl::regionNameCallback, - url, - false); // don't teleport + slurl.getSLURLString(), + LLUI::sConfigGroup->getBOOL("SLURLTeleportDirectly")); // don't teleport return true; } /*static*/ -void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - LLVector3 local_pos; - local_pos.mV[VX] = (F32)x; - local_pos.mV[VY] = (F32)y; - local_pos.mV[VZ] = (F32)z; - - - // determine whether the point is in this region - if ((x >= 0) && (x < REGION_WIDTH_UNITS) && - (y >= 0) && (y < REGION_WIDTH_UNITS)) - { - // if so, we're done - regionHandleCallback(region_handle, url, snapshot_id, teleport); - } - - else - { - // otherwise find the new region from the location - - // add the position to get the new region - LLVector3d global_pos = from_region_handle(region_handle) + LLVector3d(local_pos); - - U64 new_region_handle = to_region_handle(global_pos); - LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, - LLURLDispatcherImpl::regionHandleCallback, - url, teleport); - } + + if(slurl.getType() == LLSLURL::LOCATION) + { + regionHandleCallback(region_handle, slurl, snapshot_id, teleport); + } } /* static */ -void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - // remap x and y to local coordinates - S32 local_x = x % REGION_WIDTH_UNITS; - S32 local_y = y % REGION_WIDTH_UNITS; - if (local_x < 0) - local_x += REGION_WIDTH_UNITS; - if (local_y < 0) - local_y += REGION_WIDTH_UNITS; - - LLVector3 local_pos; - local_pos.mV[VX] = (F32)local_x; - local_pos.mV[VY] = (F32)local_y; - local_pos.mV[VZ] = (F32)z; - + // we can't teleport cross grid at this point + HippoGridInfo* new_grid = gHippoGridManager->getGrid(slurl.getGrid()); + if( new_grid + != gHippoGridManager->getCurrentGrid()) + { + LLSD args; + args["SLURL"] = slurl.getLocationString(); + args["CURRENT_GRID"] = gHippoGridManager->getCurrentGrid()->getGridName(); + + std::string grid_label = new_grid ? new_grid->getGridName() : ""; + + if(!grid_label.empty()) + { + args["GRID"] = grid_label; + } + else + { + args["GRID"] = slurl.getGrid() + " (Unrecognized)"; + } + LLNotificationsUtil::add("CantTeleportToGrid", args); + return; + } + LLVector3d global_pos = from_region_handle(region_handle); + LLVector3 local_pos = slurl.getPosition(); + global_pos += LLVector3d(local_pos); if (teleport) { - LLVector3d global_pos = from_region_handle(region_handle); - global_pos += LLVector3d(local_pos); + gAgent.teleportViaLocation(global_pos); if(gFloaterWorldMap) { @@ -335,46 +255,11 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str { url_displayp->setSnapshotDisplay(snapshot_id); } - std::string locationString = llformat("%s %d, %d, %d", region_name.c_str(), x, y, z); + std::string locationString = llformat("%s %i, %i, %i", slurl.getRegion().c_str(), (S32)local_pos.mV[VX],(S32)local_pos.mV[VY],(S32)local_pos.mV[VZ]); url_displayp->setLocationString(locationString); } } -// static -bool LLURLDispatcherImpl::matchPrefix(const std::string& url, const std::string& prefix) -{ - std::string test_prefix = url.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - return test_prefix == prefix; -} - -// static -std::string LLURLDispatcherImpl::stripProtocol(const std::string& url) -{ - std::string stripped = url; - if (matchPrefix(stripped, SLURL_SL_HELP_PREFIX)) - { - stripped.erase(0, SLURL_SL_HELP_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SL_PREFIX)) - { - stripped.erase(0, SLURL_SL_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SECONDLIFE_PREFIX)) - { - stripped.erase(0, SLURL_SECONDLIFE_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SLURL_PREFIX)) - { - stripped.erase(0, SLURL_SLURL_PREFIX.length()); - } - else if (matchPrefix(stripped, SLURL_SLURL_ALT_PREFIX)) - { - stripped.erase(0, SLURL_SLURL_ALT_PREFIX.length()); - } - return stripped; -} - //--------------------------------------------------------------------------- // Teleportation links are handled here because they are tightly coupled // to URL parsing and sim-fragment parsing @@ -384,7 +269,7 @@ public: // Teleport requests *must* come from a trusted browser // inside the app, otherwise a malicious web page could // cause a constant teleport loop. JC - LLTeleportHandler() : LLCommandHandler("teleport", true) { } + LLTeleportHandler() : LLCommandHandler("teleport", UNTRUSTED_BLOCK) { } bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) @@ -393,18 +278,21 @@ public: // a global position, and teleport to it if (tokens.size() < 1) return false; - // Region names may be %20 escaped. - std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]); - - // build secondlife://De%20Haro/123/45/67 for use in callback - std::string url = SLURL_SECONDLIFE_PREFIX; - for (int i = 0; i < tokens.size(); ++i) + LLVector3 coords(128, 128, 0); + if (tokens.size() <= 4) { - url += tokens[i].asString() + "/"; + coords = LLVector3(tokens[1].asReal(), + tokens[2].asReal(), + tokens[3].asReal()); } + + // Region names may be %20 escaped. + + std::string region_name = LLURI::unescape(tokens[0]); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, LLURLDispatcherImpl::regionHandleCallback, - url, + LLSLURL(region_name, coords).getSLURLString(), true); // teleport return true; } @@ -414,50 +302,32 @@ LLTeleportHandler gTeleportHandler; //--------------------------------------------------------------------------- // static -bool LLURLDispatcher::isSLURL(const std::string& url) -{ - return LLURLDispatcherImpl::isSLURL(url); -} - -// static -bool LLURLDispatcher::isSLURLCommand(const std::string& url) -{ - return LLURLDispatcherImpl::isSLURLCommand(url); -} - -// static -bool LLURLDispatcher::dispatch(const std::string& url, +bool LLURLDispatcher::dispatch(const std::string& slurl, + const std::string& nav_type, LLMediaCtrl* web, bool trusted_browser) { - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), nav_type, web, trusted_browser); } // static -bool LLURLDispatcher::dispatchRightClick(const std::string& url) +bool LLURLDispatcher::dispatchRightClick(const std::string& slurl) { - return LLURLDispatcherImpl::dispatchRightClick(url); + return LLURLDispatcherImpl::dispatchRightClick(LLSLURL(slurl)); } // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) { // *NOTE: Text editors are considered sources of trusted URLs - // in order to make objectim and avatar profile links in chat - // history work. While a malicious resident could chat an app - // SLURL, the receiving resident will see it and must affirmatively + // in order to make avatar profile links in chat history work. + // While a malicious resident could chat an app SLURL, the + // receiving resident will see it and must affirmatively // click on it. // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), "clicked", web, trusted_browser); } -// static -std::string LLURLDispatcher::buildSLURL(const std::string& regionname, - S32 x, S32 y, S32 z) -{ - std::string slurl = SLURL_SLURL_PREFIX + regionname + llformat("/%d/%d/%d",x,y,z); - slurl = LLWeb::escapeURL( slurl ); - return slurl; -} + diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index c947e5e37..82e1d3152 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -38,15 +38,11 @@ class LLMediaCtrl; class LLURLDispatcher { public: - static bool isSLURL(const std::string& url); - // Is this any sort of secondlife:// or sl:// URL? - - static bool isSLURLCommand(const std::string& url); - // Is this a special secondlife://app/ URL? - - static bool dispatch(const std::string& url, + + static bool dispatch(const std::string& slurl, + const std::string& nav_type, LLMediaCtrl* web, - bool trusted_browser); + bool trusted_browser); // At startup time and on clicks in internal web browsers, // teleport, open map, or run requested command. // @param url @@ -60,12 +56,9 @@ public: // that navigates to trusted (Linden Lab) pages. // Returns true if someone handled the URL. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const std::string& slurl); - static bool dispatchFromTextEditor(const std::string& url); - - static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - // builds: http://slurl.com/secondlife/RegionName/x/y/z/ + static bool dispatchFromTextEditor(const std::string& slurl); }; #endif diff --git a/indra/newview/llurlsimstring.cpp b/indra/newview/llurlsimstring.cpp deleted file mode 100644 index 200345cae..000000000 --- a/indra/newview/llurlsimstring.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/** - * @file llurlsimstring.cpp (was llsimurlstring.cpp) - * @brief Handles "SLURL fragments" like Ahern/123/45 for - * startup processing, login screen, prefs, etc. - * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-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 "llurlsimstring.h" - -#include "llpanellogin.h" -#include "llviewercontrol.h" - -#include // curl_unescape, curl_free -#ifdef DEBUG_CURLIO -#include "debug_libcurl.h" -#endif - -//static -LLURLSimString LLURLSimString::sInstance; -std::string LLURLSimString::sLocationStringHome("My Home"); -std::string LLURLSimString::sLocationStringLast("My Last Location"); - -// "secondlife://simname/x/y/z" -> "simname/x/y/z" -// (actually .*//foo -> foo) -// static -void LLURLSimString::setString(const std::string& sim_string) -{ - sInstance.mSimString.clear(); - sInstance.mSimName.clear(); - sInstance.mParseState = NOT_PARSED; - if (sim_string == sLocationStringHome) - { - gSavedSettings.setBOOL("LoginLastLocation", FALSE); - } - else if (sim_string == sLocationStringLast) - { - gSavedSettings.setBOOL("LoginLastLocation", TRUE); - } - else - { - char* curlstr = curl_unescape(sim_string.c_str(), sim_string.size()); - std::string tstring = std::string(curlstr); - curl_free(curlstr); - std::string::size_type idx = tstring.find("//"); - idx = (idx == std::string::npos) ? 0 : idx+2; - sInstance.mSimString = tstring.substr(idx); - } -} - -// "/100" -> 100 -// static -std::string::size_type LLURLSimString::parseGridIdx(const std::string& in_string, - std::string::size_type idx0, - std::string::size_type* res) -{ - if (idx0 == std::string::npos || in_string[idx0] != '/') - { - return std::string::npos; // parse error - } - idx0++; - std::string::size_type idx1 = in_string.find_first_of('/', idx0); - std::string::size_type len = (idx1 == std::string::npos) ? std::string::npos : idx1-idx0; - std::string tstring = in_string.substr(idx0,len); - if (!tstring.empty()) - { - std::string::size_type val = atoi(tstring.c_str()); - *res = val; - } - return idx1; -} - -// "simname/x/y/z" -> mSimName = simname, mX = x, mY = y, mZ = z -// static -bool LLURLSimString::parse() -{ - if (sInstance.mParseState == NOT_SET) - { - return false; - } - if (sInstance.mParseState == NOT_PARSED) - { - if (parse(sInstance.mSimString, - &sInstance.mSimName, - &sInstance.mX, - &sInstance.mY, - &sInstance.mZ)) - { - sInstance.mParseState = PARSE_OK; - } - else - { - sInstance.mParseState = PARSE_FAIL; - } - } - return (sInstance.mParseState == PARSE_OK); -} - -// static -bool LLURLSimString::parse(const std::string& sim_string, - std::string *region_name, - S32 *x, S32 *y, S32 *z) -{ - // strip any bogus initial '/' - std::string::size_type idx0 = sim_string.find_first_not_of('/'); - if (idx0 == std::string::npos) idx0 = 0; - - std::string::size_type idx1 = sim_string.find_first_of('/', idx0); - std::string::size_type len = (idx1 == std::string::npos) ? std::string::npos : idx1-idx0; - std::string tstring = sim_string.substr(idx0,len); - *region_name = unescapeRegionName(tstring); - if (!region_name->empty()) - { - // return position data if found. otherwise leave passed-in values alone. (DEV-18380) -MG - if (idx1 != std::string::npos) - { - std::string::size_type xs = *x, ys = *y, zs = *z; - idx1 = parseGridIdx(sim_string, idx1, &xs); - idx1 = parseGridIdx(sim_string, idx1, &ys); - idx1 = parseGridIdx(sim_string, idx1, &zs); - *x = xs; - *y = ys; - *z = zs; - } - - return true; - } - else - { - return false; - } -} - -// static -std::string LLURLSimString::getURL() -{ - std::string url; - if (sInstance.mParseState == PARSE_OK) - { - url = llformat("secondlife://%s/%d/%d/%d/", - sInstance.mSimName.c_str(), - sInstance.mX, - sInstance.mY, - sInstance.mZ); - } - return url; -} - -// static -std::string LLURLSimString::unescapeRegionName(std::string region_name) -{ - std::string result; - char* curlstr = curl_unescape(region_name.c_str(), region_name.size()); - result = std::string(curlstr); - curl_free(curlstr); - return result; -} diff --git a/indra/newview/llurlsimstring.h b/indra/newview/llurlsimstring.h deleted file mode 100644 index ea56da47b..000000000 --- a/indra/newview/llurlsimstring.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * @file llsimurlstring.h - * @brief Handles "SLURL fragments" like Ahern/123/45 for - * startup processing, login screen, prefs, etc. - * - * $LicenseInfo:firstyear=2006&license=viewergpl$ - * - * Copyright (c) 2006-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$ - */ -#ifndef LLSIMURLSTRING_H -#define LLSIMURLSTRING_H - -#include "llstring.h" - -class LLURLSimString -{ -public: - enum { NOT_SET=0, NOT_PARSED=1, PARSE_OK=2, PARSE_FAIL=-1 }; - - static void setString(const std::string& url); - // Accepts all sorts of fragments: - // secondlife://RegionName/1/2/ - // sl://RegionName/1/2/3/ - // //Ahern/123/45/ - // Ahern - - static bool parse(); - // Returns true if we have an URL fragment in the static instance - // (and it parsed correctly, which is basically always because - // any bare region string is a valid fragment). - - static bool parse(const std::string& sim_string, std::string *region_name, S32 *x, S32 *y, S32 *z); - // Parse a sim string "Ahern/1/2" and return location data, - // doesn't affect static instance. - - static std::string getURL(); - // Get the canonical URL secondlife://RegionName/123/45/6/ - - static std::string unescapeRegionName(std::string region_name); - // Does URL unescaping, in particular %20 -> space - - LLURLSimString() : mX(128), mY(128), mZ(0), mParseState(NOT_PARSED) {} - -private: - static std::string::size_type parseGridIdx(const std::string& in_string, - std::string::size_type idx0, - std::string::size_type* res); - -public: - static LLURLSimString sInstance; - static std::string sLocationStringHome; - static std::string sLocationStringLast; - -public: - std::string mSimString; // "name/x/y/z" - std::string mSimName; - S32 mX,mY,mZ; - S32 mParseState; -}; - -#endif diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 175af63a3..ee4162dce 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -34,44 +34,142 @@ #include "llviewermedia.h" + +#include "llviewermedia.h" + +#include "llagent.h" +#include "llagentcamera.h" #include "llappviewer.h" +#include "llaudioengine.h" // for gAudiop +#include "llcallbacklist.h" #include "lldir.h" #include "lldiriterator.h" #include "llevent.h" // LLSimpleListener -#include "llhoverview.h" +#include "llfloaterwebcontent.h" // for handling window close requests and geometry change requests in media browser windows. +#include "llfocusmgr.h" #include "llhttpclient.h" #include "llkeyboard.h" +#include "llmarketplacefunctions.h" +#include "llmediaentry.h" #include "llmimetypes.h" +#include "llmutelist.h" #include "llnotifications.h" #include "llnotificationsutil.h" +#include "llpanelprofile.h" +#include "llparcel.h" #include "llpluginclassmedia.h" #include "llplugincookiestore.h" #include "llurldispatcher.h" #include "lluuid.h" +#include "llvieweraudio.h" #include "llviewermediafocus.h" #include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llviewerparcelmedia.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" #include "llviewertexture.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "llvovolume.h" +#include "llwebprofile.h" #include "llwindow.h" #include "llvieweraudio.h" -#include "llweb.h" -#include "llwebprofile.h" + +#include "aifilepicker.h" +#include "llstartup.h" + +#include // for SkinFolder listener +#include std::string getProfileURL(const std::string& agent_name); -//#include "viewerversion.h" + +/*static*/ const char* LLViewerMedia::AUTO_PLAY_MEDIA_SETTING = "ParcelMediaAutoPlayEnable"; +/*static*/ const char* LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING = "MediaShowOnOthers"; +/*static*/ const char* LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING = "MediaShowWithinParcel"; +/*static*/ const char* LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING = "MediaShowOutsideParcel"; + class AIHTTPTimeoutPolicy; extern AIHTTPTimeoutPolicy mimeDiscoveryResponder_timeout; extern AIHTTPTimeoutPolicy viewerMediaOpenIDResponder_timeout; extern AIHTTPTimeoutPolicy viewerMediaWebProfileResponder_timeout; -// Merov: Temporary definitions while porting the new viewer media code to Snowglobe -const int LEFT_BUTTON = 0; -const int RIGHT_BUTTON = 1; +// Move this to its own file. + +LLViewerMediaEventEmitter::~LLViewerMediaEventEmitter() +{ + observerListType::iterator iter = mObservers.begin(); + + while( iter != mObservers.end() ) + { + LLViewerMediaObserver *self = *iter; + iter++; + remObserver(self); + } +} /////////////////////////////////////////////////////////////////////////////// -// Helper class that tries to download a URL from a web site and calls a method +// +bool LLViewerMediaEventEmitter::addObserver( LLViewerMediaObserver* observer ) +{ + if ( ! observer ) + return false; + + if ( std::find( mObservers.begin(), mObservers.end(), observer ) != mObservers.end() ) + return false; + + mObservers.push_back( observer ); + observer->mEmitters.push_back( this ); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaEventEmitter::remObserver( LLViewerMediaObserver* observer ) +{ + if ( ! observer ) + return false; + + mObservers.remove( observer ); + observer->mEmitters.remove(this); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void LLViewerMediaEventEmitter::emitEvent( LLPluginClassMedia* media, LLViewerMediaObserver::EMediaEvent event ) +{ + // Broadcast the event to any observers. + observerListType::iterator iter = mObservers.begin(); + while( iter != mObservers.end() ) + { + LLViewerMediaObserver *self = *iter; + ++iter; + self->handleMediaEvent( media, event ); + } +} + +// Move this to its own file. +LLViewerMediaObserver::~LLViewerMediaObserver() +{ + std::list::iterator iter = mEmitters.begin(); + + while( iter != mEmitters.end() ) + { + LLViewerMediaEventEmitter *self = *iter; + iter++; + self->remObserver( this ); + } +} + + +// Move this to its own file. +// helper class that tries to download a URL from a web site and calls a method // on the Panel Land Media and to discover the MIME type class LLMimeDiscoveryResponder : public LLHTTPClient::ResponderHeadersOnly { @@ -81,7 +179,19 @@ public: : mMediaImpl(media_impl), mDefaultMimeType(default_mime_type), mInitialized(false) - {} + { + if(mMediaImpl->mMimeTypeProbe != NULL) + { + llerrs << "impl already has an outstanding responder" << llendl; + } + + mMediaImpl->mMimeTypeProbe = this; + } + + ~LLMimeDiscoveryResponder() + { + disconnectOwner(); + } /*virtual*/ void completedHeaders(U32 status, std::string const& reason, AIHTTPReceivedHeaders const& headers) { @@ -106,12 +216,17 @@ public: void completeAny(U32 status, const std::string& mime_type) { - if(!mInitialized && ! mime_type.empty()) + // the call to initializeMedia may disconnect the responder, which will clear mMediaImpl. + // Make a local copy so we can call loadURI() afterwards. + LLViewerMediaImpl *impl = mMediaImpl; + + if(impl && !mInitialized && ! mime_type.empty()) { - if (mMediaImpl->initializeMedia(mime_type)) + if(impl->initializeMedia(mime_type)) { mInitialized = true; - mMediaImpl->play(); + impl->loadURI(); + disconnectOwner(); } } } @@ -119,6 +234,26 @@ public: /*virtual*/ AIHTTPTimeoutPolicy const& getHTTPTimeoutPolicy(void) const { return mimeDiscoveryResponder_timeout; } /*virtual*/ char const* getName(void) const { return "LLMimeDiscoveryResponder"; } + void cancelRequest() + { + disconnectOwner(); + } + +private: + void disconnectOwner() + { + if(mMediaImpl) + { + if(mMediaImpl->mMimeTypeProbe != this) + { + llerrs << "internal error: mMediaImpl->mMimeTypeProbe != this" << llendl; + } + + mMediaImpl->mMimeTypeProbe = NULL; + } + mMediaImpl = NULL; + } + public: viewer_media_t mMediaImpl; @@ -219,55 +354,35 @@ private: std::string mHost; }; + LLPluginCookieStore *LLViewerMedia::sCookieStore = NULL; LLURL LLViewerMedia::sOpenIDURL; std::string LLViewerMedia::sOpenIDCookie; -typedef std::list impl_list; -static impl_list sViewerMediaImplList; +LLPluginClassMedia* LLViewerMedia::sSpareBrowserMediaSource = NULL; +static LLViewerMedia::impl_list sViewerMediaImplList; +static LLViewerMedia::impl_id_map sViewerMediaTextureIDMap; +static LLTimer sMediaCreateTimer; +static const F32 LLVIEWERMEDIA_CREATE_DELAY = 1.0f; +static F32 sGlobalVolume = 1.0f; +static bool sForceUpdate = false; +static LLUUID sOnlyAudibleTextureID = LLUUID::null; +static F64 sLowestLoadableImplInterest = 0.0f; +static bool sAnyMediaShowing = false; +static boost::signals2::connection sTeleportFinishConnection; static std::string sUpdatedCookies; static const char *PLUGIN_COOKIE_FILE_NAME = "plugin_cookies.txt"; ////////////////////////////////////////////////////////////////////////////////////////// -// LLViewerMedia - -////////////////////////////////////////////////////////////////////////////////////////// -// static -viewer_media_t LLViewerMedia::newMediaImpl(const std::string& media_url, - const LLUUID& texture_id, - S32 media_width, - S32 media_height, - U8 media_auto_scale, - U8 media_loop, - std::string mime_type) +static void add_media_impl(LLViewerMediaImpl* media) { - LLViewerMediaImpl* media_impl = getMediaImplFromTextureID(texture_id); - if(media_impl == NULL || texture_id.isNull()) - { - // Create the media impl - media_impl = new LLViewerMediaImpl(media_url, texture_id, media_width, media_height, media_auto_scale, media_loop, mime_type); - sViewerMediaImplList.push_back(media_impl); - } - else - { - media_impl->stop(); - media_impl->mTextureId = texture_id; - media_impl->mMediaURL = media_url; - media_impl->mMediaWidth = media_width; - media_impl->mMediaHeight = media_height; - media_impl->mMediaAutoScale = media_auto_scale; - media_impl->mMediaLoop = media_loop; - if(! media_url.empty()) - media_impl->navigateTo(media_url, mime_type, true); - } - return media_impl; + sViewerMediaImplList.push_back(media); } ////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::removeMedia(LLViewerMediaImpl* media) +static void remove_media_impl(LLViewerMediaImpl* media) { - impl_list::iterator iter = sViewerMediaImplList.begin(); - impl_list::iterator end = sViewerMediaImplList.end(); + LLViewerMedia::impl_list::iterator iter = sViewerMediaImplList.begin(); + LLViewerMedia::impl_list::iterator end = sViewerMediaImplList.end(); for(; iter != end; iter++) { @@ -279,22 +394,160 @@ void LLViewerMedia::removeMedia(LLViewerMediaImpl* media) } } +class LLViewerMediaMuteListObserver : public LLMuteListObserver +{ + /* virtual */ void onChange() { LLViewerMedia::muteListChanged();} +}; + +static LLViewerMediaMuteListObserver sViewerMediaMuteListObserver; +static bool sViewerMediaMuteListObserverInitialized = false; + + +////////////////////////////////////////////////////////////////////////////////////////// +// LLViewerMedia + +////////////////////////////////////////////////////////////////////////////////////////// +// static +viewer_media_t LLViewerMedia::newMediaImpl( + const LLUUID& texture_id, + S32 media_width, + S32 media_height, + U8 media_auto_scale, + U8 media_loop) +{ + LLViewerMediaImpl* media_impl = getMediaImplFromTextureID(texture_id); + if(media_impl == NULL || texture_id.isNull()) + { + // Create the media impl + media_impl = new LLViewerMediaImpl(texture_id, media_width, media_height, media_auto_scale, media_loop); + } + else + { + media_impl->unload(); + media_impl->setTextureID(texture_id); + media_impl->mMediaWidth = media_width; + media_impl->mMediaHeight = media_height; + media_impl->mMediaAutoScale = media_auto_scale; + media_impl->mMediaLoop = media_loop; + } + + return media_impl; +} + +viewer_media_t LLViewerMedia::updateMediaImpl(LLMediaEntry* media_entry, const std::string& previous_url, bool update_from_self) +{ + // Try to find media with the same media ID + viewer_media_t media_impl = getMediaImplFromTextureID(media_entry->getMediaID()); + + lldebugs << "called, current URL is \"" << media_entry->getCurrentURL() + << "\", previous URL is \"" << previous_url + << "\", update_from_self is " << (update_from_self?"true":"false") + << llendl; + + bool was_loaded = false; + bool needs_navigate = false; + + if(media_impl) + { + was_loaded = media_impl->hasMedia(); + + media_impl->setHomeURL(media_entry->getHomeURL()); + + media_impl->mMediaAutoScale = media_entry->getAutoScale(); + media_impl->mMediaLoop = media_entry->getAutoLoop(); + media_impl->mMediaWidth = media_entry->getWidthPixels(); + media_impl->mMediaHeight = media_entry->getHeightPixels(); + media_impl->mMediaAutoPlay = media_entry->getAutoPlay(); + media_impl->mMediaEntryURL = media_entry->getCurrentURL(); + LLPluginClassMedia* plugin = media_impl->getMediaPlugin(); + if (plugin) + { + plugin->setAutoScale(media_impl->mMediaAutoScale); + plugin->setLoop(media_impl->mMediaLoop); + plugin->setSize(media_entry->getWidthPixels(), media_entry->getHeightPixels()); + } + + bool url_changed = (media_impl->mMediaEntryURL != previous_url); + if(media_impl->mMediaEntryURL.empty()) + { + if(url_changed) + { + // The current media URL is now empty. Unload the media source. + media_impl->unload(); + + lldebugs << "Unloading media instance (new current URL is empty)." << llendl; + } + } + else + { + // The current media URL is not empty. + // If (the media was already loaded OR the media was set to autoplay) AND this update didn't come from this agent, + // do a navigate. + bool auto_play = media_impl->isAutoPlayable(); + if((was_loaded || auto_play) && !update_from_self) + { + needs_navigate = url_changed; + } + + lldebugs << "was_loaded is " << (was_loaded?"true":"false") + << ", auto_play is " << (auto_play?"true":"false") + << ", needs_navigate is " << (needs_navigate?"true":"false") << llendl; + } + } + else + { + media_impl = newMediaImpl( + media_entry->getMediaID(), + media_entry->getWidthPixels(), + media_entry->getHeightPixels(), + media_entry->getAutoScale(), + media_entry->getAutoLoop()); + + media_impl->setHomeURL(media_entry->getHomeURL()); + media_impl->mMediaAutoPlay = media_entry->getAutoPlay(); + media_impl->mMediaEntryURL = media_entry->getCurrentURL(); + if(media_impl->isAutoPlayable()) + { + needs_navigate = true; + } + } + + if(media_impl) + { + if(needs_navigate) + { + media_impl->navigateTo(media_impl->mMediaEntryURL, "", true, true); + lldebugs << "navigating to URL " << media_impl->mMediaEntryURL << llendl; + } + else if(!media_impl->mMediaURL.empty() && (media_impl->mMediaURL != media_impl->mMediaEntryURL)) + { + // If we already have a non-empty media URL set and we aren't doing a navigate, update the media URL to match the media entry. + media_impl->mMediaURL = media_impl->mMediaEntryURL; + + // If this causes a navigate at some point (such as after a reload), it should be considered server-driven so it isn't broadcast. + media_impl->mNavigateServerRequest = true; + + lldebugs << "updating URL in the media impl to " << media_impl->mMediaEntryURL << llendl; + } + } + + return media_impl; +} + ////////////////////////////////////////////////////////////////////////////////////////// // static LLViewerMediaImpl* LLViewerMedia::getMediaImplFromTextureID(const LLUUID& texture_id) { - impl_list::iterator iter = sViewerMediaImplList.begin(); - impl_list::iterator end = sViewerMediaImplList.end(); - - for(; iter != end; iter++) + LLViewerMediaImpl* result = NULL; + + // Look up the texture ID in the texture id->impl map. + impl_id_map::iterator iter = sViewerMediaTextureIDMap.find(texture_id); + if(iter != sViewerMediaTextureIDMap.end()) { - LLViewerMediaImpl* media_impl = *iter; - if(media_impl->getMediaTextureID() == texture_id) - { - return media_impl; - } + result = iter->second; } - return NULL; + + return result; } ////////////////////////////////////////////////////////////////////////////////////////// @@ -379,28 +632,186 @@ bool LLViewerMedia::textureHasMedia(const LLUUID& texture_id) // static void LLViewerMedia::setVolume(F32 volume) { + if(volume != sGlobalVolume || sForceUpdate) + { + sGlobalVolume = volume; + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + pimpl->updateVolume(); + } + + sForceUpdate = false; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +F32 LLViewerMedia::getVolume() +{ + return sGlobalVolume; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::muteListChanged() +{ + // When the mute list changes, we need to check mute status on all impls. impl_list::iterator iter = sViewerMediaImplList.begin(); impl_list::iterator end = sViewerMediaImplList.end(); for(; iter != end; iter++) { LLViewerMediaImpl* pimpl = *iter; - LLPluginClassMedia* plugin = pimpl->getMediaPlugin(); - if(plugin) - { - plugin->setVolume(volume); - } - else - { - llwarns << "Plug-in already destroyed" << llendl; - } + pimpl->mNeedsMuteCheck = true; } } ////////////////////////////////////////////////////////////////////////////////////////// // static -void LLViewerMedia::updateMedia() +bool LLViewerMedia::isInterestingEnough(const LLVOVolume *object, const F64 &object_interest) { + bool result = false; + + if (NULL == object) + { + result = false; + } + // Focused? Then it is interesting! + else if (LLViewerMediaFocus::getInstance()->getFocusedObjectID() == object->getID()) + { + result = true; + } + // Selected? Then it is interesting! + // XXX Sadly, 'contains()' doesn't take a const :( + else if (LLSelectMgr::getInstance()->getSelection()->contains(const_cast(object))) + { + result = true; + } + else + { + lldebugs << "object interest = " << object_interest << ", lowest loadable = " << sLowestLoadableImplInterest << llendl; + if(object_interest >= sLowestLoadableImplInterest) + result = true; + } + + return result; +} + +LLViewerMedia::impl_list &LLViewerMedia::getPriorityList() +{ + return sViewerMediaImplList; +} + +// This is the predicate function used to sort sViewerMediaImplList by priority. +bool LLViewerMedia::priorityComparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2) +{ + if(i1->isForcedUnloaded() && !i2->isForcedUnloaded()) + { + // Muted or failed items always go to the end of the list, period. + return false; + } + else if(i2->isForcedUnloaded() && !i1->isForcedUnloaded()) + { + // Muted or failed items always go to the end of the list, period. + return true; + } + else if(i1->hasFocus()) + { + // The item with user focus always comes to the front of the list, period. + return true; + } + else if(i2->hasFocus()) + { + // The item with user focus always comes to the front of the list, period. + return false; + } + else if(i1->isParcelMedia()) + { + // The parcel media impl sorts above all other inworld media, unless one has focus. + return true; + } + else if(i2->isParcelMedia()) + { + // The parcel media impl sorts above all other inworld media, unless one has focus. + return false; + } + else if(i1->getUsedInUI() && !i2->getUsedInUI()) + { + // i1 is a UI element, i2 is not. This makes i1 "less than" i2, so it sorts earlier in our list. + return true; + } + else if(i2->getUsedInUI() && !i1->getUsedInUI()) + { + // i2 is a UI element, i1 is not. This makes i2 "less than" i1, so it sorts earlier in our list. + return false; + } + else if(i1->isPlayable() && !i2->isPlayable()) + { + // Playable items sort above ones that wouldn't play even if they got high enough priority + return true; + } + else if(!i1->isPlayable() && i2->isPlayable()) + { + // Playable items sort above ones that wouldn't play even if they got high enough priority + return false; + } + else if(i1->getInterest() == i2->getInterest()) + { + // Generally this will mean both objects have zero interest. In this case, sort on distance. + return (i1->getProximityDistance() < i2->getProximityDistance()); + } + else + { + // The object with the larger interest value should be earlier in the list, so we reverse the sense of the comparison here. + return (i1->getInterest() > i2->getInterest()); + } +} + +static bool proximity_comparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2) +{ + if(i1->getProximityDistance() < i2->getProximityDistance()) + { + return true; + } + else if(i1->getProximityDistance() > i2->getProximityDistance()) + { + return false; + } + else + { + // Both objects have the same distance. This most likely means they're two faces of the same object. + // They may also be faces on different objects with exactly the same distance (like HUD objects). + // We don't actually care what the sort order is for this case, as long as it's stable and doesn't change when you enable/disable media. + // Comparing the impl pointers gives a completely arbitrary ordering, but it will be stable. + return (i1 < i2); + } +} + +static LLFastTimer::DeclareTimer FTM_MEDIA_UPDATE("Update Media"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SPARE_IDLE("Spare Idle"); +static LLFastTimer::DeclareTimer FTM_MEDIA_UPDATE_INTEREST("Update/Interest"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SORT("Sort"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SORT2("Sort 2"); +static LLFastTimer::DeclareTimer FTM_MEDIA_MISC("Misc"); + + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::updateMedia(void *dummy_arg) +{ + LLFastTimer t1(FTM_MEDIA_UPDATE); + + // Enable/disable the plugin read thread + LLPluginProcessParent::setUseReadThread(gSavedSettings.getBOOL("PluginUseReadThread")); + + // HACK: we always try to keep a spare running webkit plugin around to improve launch times. + createSpareBrowserMediaSource(); + + sAnyMediaShowing = false; sUpdatedCookies = getCookieStore()->getChangedCookies(); if(!sUpdatedCookies.empty()) { @@ -411,11 +822,334 @@ void LLViewerMedia::updateMedia() impl_list::iterator iter = sViewerMediaImplList.begin(); impl_list::iterator end = sViewerMediaImplList.end(); + { + LLFastTimer t(FTM_MEDIA_UPDATE_INTEREST); + for(; iter != end;) + { + LLViewerMediaImpl* pimpl = *iter++; + pimpl->update(); + pimpl->calculateInterest(); + } + } + + // Let the spare media source actually launch + if(sSpareBrowserMediaSource) + { + LLFastTimer t(FTM_MEDIA_SPARE_IDLE); + sSpareBrowserMediaSource->idle(); + } + + { + LLFastTimer t(FTM_MEDIA_SORT); + // Sort the static instance list using our interest criteria + sViewerMediaImplList.sort(priorityComparitor); + } + + // Go through the list again and adjust according to priority. + iter = sViewerMediaImplList.begin(); + end = sViewerMediaImplList.end(); + + F64 total_cpu = 0.0f; + int impl_count_total = 0; + int impl_count_interest_low = 0; + int impl_count_interest_normal = 0; + + std::vector proximity_order; + + bool inworld_media_enabled = gSavedSettings.getBOOL("AudioStreamingMedia"); + bool inworld_audio_enabled = gSavedSettings.getBOOL("AudioStreamingMusic"); + U32 max_instances = gSavedSettings.getU32("PluginInstancesTotal"); + U32 max_normal = gSavedSettings.getU32("PluginInstancesNormal"); + U32 max_low = gSavedSettings.getU32("PluginInstancesLow"); + F32 max_cpu = gSavedSettings.getF32("PluginInstancesCPULimit"); + // Setting max_cpu to 0.0 disables CPU usage checking. + bool check_cpu_usage = (max_cpu != 0.0f); + + LLViewerMediaImpl* lowest_interest_loadable = NULL; + + // Notes on tweakable params: + // max_instances must be set high enough to allow the various instances used in the UI (for the help browser, search, etc.) to be loaded. + // If max_normal + max_low is less than max_instances, things will tend to get unloaded instead of being set to slideshow. + + { + LLFastTimer t(FTM_MEDIA_MISC); + for(; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + + LLViewerMediaImpl::EPriority new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + + if(pimpl->isForcedUnloaded() || (impl_count_total >= (int)max_instances)) + { + // Never load muted or failed impls. + // Hard limit on the number of instances that will be loaded at one time + new_priority = LLViewerMediaImpl::PRIORITY_UNLOADED; + } + else if(!pimpl->getVisible()) + { + new_priority = LLViewerMediaImpl::PRIORITY_HIDDEN; + } + else if(pimpl->hasFocus()) + { + new_priority = LLViewerMediaImpl::PRIORITY_HIGH; + impl_count_interest_normal++; // count this against the count of "normal" instances for priority purposes + } + else if(pimpl->getUsedInUI()) + { + new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + impl_count_interest_normal++; + } + else if(pimpl->isParcelMedia()) + { + new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + impl_count_interest_normal++; + } + else + { + // Look at interest and CPU usage for instances that aren't in any of the above states. + + // Heuristic -- if the media texture's approximate screen area is less than 1/4 of the native area of the texture, + // turn it down to low instead of normal. This may downsample for plugins that support it. + bool media_is_small = false; + F64 approximate_interest = pimpl->getApproximateTextureInterest(); + if(approximate_interest == 0.0f) + { + // this media has no current size, which probably means it's not loaded. + media_is_small = true; + } + else if(pimpl->getInterest() < (approximate_interest / 4)) + { + media_is_small = true; + } + + if(pimpl->getInterest() == 0.0f) + { + // This media is completely invisible, due to being outside the view frustrum or out of range. + new_priority = LLViewerMediaImpl::PRIORITY_HIDDEN; + } + else if(check_cpu_usage && (total_cpu > max_cpu)) + { + // Higher priority plugins have already used up the CPU budget. Set remaining ones to slideshow priority. + new_priority = LLViewerMediaImpl::PRIORITY_SLIDESHOW; + } + else if((impl_count_interest_normal < (int)max_normal) && !media_is_small) + { + // Up to max_normal inworld get normal priority + new_priority = LLViewerMediaImpl::PRIORITY_NORMAL; + impl_count_interest_normal++; + } + else if (impl_count_interest_low + impl_count_interest_normal < (int)max_low + (int)max_normal) + { + // The next max_low inworld get turned down + new_priority = LLViewerMediaImpl::PRIORITY_LOW; + impl_count_interest_low++; + + // Set the low priority size for downsampling to approximately the size the texture is displayed at. + { + F32 approximate_interest_dimension = (F32) sqrt(pimpl->getInterest()); + + pimpl->setLowPrioritySizeLimit(llround(approximate_interest_dimension)); + } + } + else + { + // Any additional impls (up to max_instances) get very infrequent time + new_priority = LLViewerMediaImpl::PRIORITY_SLIDESHOW; + } + } + + if(!pimpl->getUsedInUI() && (new_priority != LLViewerMediaImpl::PRIORITY_UNLOADED)) + { + // This is a loadable inworld impl -- the last one in the list in this class defines the lowest loadable interest. + lowest_interest_loadable = pimpl; + + impl_count_total++; + } + + // Overrides if the window is minimized or we lost focus (taking care + // not to accidentally "raise" the priority either) + if (!gViewerWindow->getActive() /* viewer window minimized? */ + && new_priority > LLViewerMediaImpl::PRIORITY_HIDDEN) + { + new_priority = LLViewerMediaImpl::PRIORITY_HIDDEN; + } + else if (!gFocusMgr.getAppHasFocus() /* viewer window lost focus? */ + && new_priority > LLViewerMediaImpl::PRIORITY_LOW) + { + new_priority = LLViewerMediaImpl::PRIORITY_LOW; + } + + if(!inworld_media_enabled) + { + // If inworld media is locked out, force all inworld media to stay unloaded. + if(!pimpl->getUsedInUI()) + { + new_priority = LLViewerMediaImpl::PRIORITY_UNLOADED; + } + } + // update the audio stream here as well + if( !inworld_audio_enabled) + { + if(LLViewerMedia::isParcelAudioPlaying() && gAudiop && LLViewerMedia::hasParcelAudio()) + { + gAudiop->stopInternetStream(); + } + } + pimpl->setPriority(new_priority); + + if(pimpl->getUsedInUI()) + { + // Any impls used in the UI should not be in the proximity list. + pimpl->mProximity = -1; + } + else + { + proximity_order.push_back(pimpl); + } + + total_cpu += pimpl->getCPUUsage(); + + if (!pimpl->getUsedInUI() && pimpl->hasMedia()) + { + sAnyMediaShowing = true; + } + + } + } + + // Re-calculate this every time. + sLowestLoadableImplInterest = 0.0f; + + // Only do this calculation if we've hit the impl count limit -- up until that point we always need to load media data. + if(lowest_interest_loadable && (impl_count_total >= (int)max_instances)) + { + // Get the interest value of this impl's object for use by isInterestingEnough + LLVOVolume *object = lowest_interest_loadable->getSomeObject(); + if(object) + { + // NOTE: Don't use getMediaInterest() here. We want the pixel area, not the total media interest, + // so that we match up with the calculation done in LLMediaDataClient. + sLowestLoadableImplInterest = object->getPixelArea(); + } + } + + if(gSavedSettings.getBOOL("MediaPerformanceManagerDebug")) + { + // Give impls the same ordering as the priority list + // they're already in the right order for this. + } + else + { + LLFastTimer t(FTM_MEDIA_SORT2); + // Use a distance-based sort for proximity values. + std::stable_sort(proximity_order.begin(), proximity_order.end(), proximity_comparitor); + } + + // Transfer the proximity order to the proximity fields in the objects. + for(int i = 0; i < (int)proximity_order.size(); i++) + { + proximity_order[i]->mProximity = i; + } + + LL_DEBUGS("PluginPriority") << "Total reported CPU usage is " << total_cpu << llendl; + +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::isAnyMediaShowing() +{ + return sAnyMediaShowing; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setAllMediaEnabled(bool val) +{ + // Set "tentative" autoplay first. We need to do this here or else + // re-enabling won't start up the media below. + gSavedSettings.setBOOL("MediaTentativeAutoPlay", val); + + // Then + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + for(; iter != end; iter++) { LLViewerMediaImpl* pimpl = *iter; - pimpl->update(); + if (!pimpl->getUsedInUI()) + { + pimpl->setDisabled(!val); + } } + + // Also do Parcel Media and Parcel Audio + if (val) + { + if (!LLViewerMedia::isParcelMediaPlaying() && LLViewerMedia::hasParcelMedia()) + { + LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); + } + + if (gSavedSettings.getBOOL("AudioStreamingMusic") && + !LLViewerMedia::isParcelAudioPlaying() && + gAudiop && + LLViewerMedia::hasParcelAudio()) + { + if (LLAudioEngine::AUDIO_PAUSED == gAudiop->isInternetStreamPlaying()) + { + // 'false' means unpause + gAudiop->pauseInternetStream(false); + } + else + { + gAudiop->startInternetStream(LLViewerMedia::getParcelAudioURL()); + } + } + } + else { + // This actually unloads the impl, as opposed to "stop"ping the media + LLViewerParcelMedia::stop(); + if (gAudiop) + { + gAudiop->stopInternetStream(); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::isParcelMediaPlaying() +{ + return (LLViewerMedia::hasParcelMedia() && LLViewerParcelMedia::getParcelMedia() && LLViewerParcelMedia::getParcelMedia()->hasMedia()); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::isParcelAudioPlaying() +{ + return (LLViewerMedia::hasParcelAudio() && gAudiop && LLAudioEngine::AUDIO_PLAYING == gAudiop->isInternetStreamPlaying()); +} + +bool LLViewerMedia::onAuthSubmit(const LLSD& notification, const LLSD& response) +{ + LLViewerMediaImpl *impl = LLViewerMedia::getMediaImplFromTextureID(notification["payload"]["media_id"]); + if(impl) + { + LLPluginClassMedia* media = impl->getMediaPlugin(); + if(media) + { + if (response["ok"]) + { + media->sendAuthResponse(true, response["username"], response["password"]); + } + else + { + media->sendAuthResponse(false, "", ""); + } + } + } + return false; } ///////////////////////////////////////////////////////////////////////////////////////// @@ -471,12 +1205,9 @@ void LLViewerMedia::clearAllCookies() LLDirIterator dir_iter(base_dir, "*_*"); while (dir_iter.next(filename)) { - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += "browser_profile"; - target += gDirUtilp->getDirDelimiter(); - target += "cookies"; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, "browser_profile"); + gDirUtilp->append(target, "cookies"); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { @@ -484,10 +1215,8 @@ void LLViewerMedia::clearAllCookies() } // Other accounts may have new-style cookie files too -- delete them as well - target = base_dir; - target += filename; - target += gDirUtilp->getDirDelimiter(); - target += PLUGIN_COOKIE_FILE_NAME; + target = gDirUtilp->add(base_dir, filename); + gDirUtilp->append(target, PLUGIN_COOKIE_FILE_NAME); lldebugs << "target = " << target << llendl; if(LLFile::isfile(target)) { @@ -530,6 +1259,24 @@ void LLViewerMedia::setCookiesEnabled(bool enabled) } } } + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setProxyConfig(bool enable, const std::string &host, int port) +{ + // Set the proxy config for all loaded plugins + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + for (; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + LLPluginClassMedia* plugin = pimpl->getMediaPlugin(); + if(plugin) + { + plugin->proxy_setup(enable, host, port); + } + } +} ///////////////////////////////////////////////////////////////////////////////////////// // static @@ -761,72 +1508,258 @@ void LLViewerMedia::openIDCookieResponse(const std::string &cookie) setOpenIDCookie(); } + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::proxyWindowOpened(const std::string &target, const std::string &uuid) +{ + if(uuid.empty()) + return; + + for (impl_list::iterator iter = sViewerMediaImplList.begin(); iter != sViewerMediaImplList.end(); iter++) + { + LLPluginClassMedia* plugin = (*iter)->getMediaPlugin(); + if(plugin && plugin->pluginSupportsMediaBrowser()) + { + plugin->proxyWindowOpened(target, uuid); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::proxyWindowClosed(const std::string &uuid) +{ + if(uuid.empty()) + return; + + for (impl_list::iterator iter = sViewerMediaImplList.begin(); iter != sViewerMediaImplList.end(); iter++) + { + LLPluginClassMedia* plugin = (*iter)->getMediaPlugin(); + if(plugin && plugin->pluginSupportsMediaBrowser()) + { + plugin->proxyWindowClosed(uuid); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::createSpareBrowserMediaSource() +{ + // If we don't have a spare browser media source, create one. + // However, if PluginAttachDebuggerToPlugins is set then don't spawn a spare + // SLPlugin process in order to not be confused by an unrelated gdb terminal + // popping up at the moment we start a media plugin. + if (!sSpareBrowserMediaSource && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")) + { + // The null owner will keep the browser plugin from fully initializing + // (specifically, it keeps LLPluginClassMedia from negotiating a size change, + // which keeps MediaPluginWebkit::initBrowserWindow from doing anything until we have some necessary data, like the background color) + sSpareBrowserMediaSource = LLViewerMediaImpl::newSourceFromMediaType("text/html", NULL, 0, 0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// static +LLPluginClassMedia* LLViewerMedia::getSpareBrowserMediaSource() +{ + LLPluginClassMedia* result = sSpareBrowserMediaSource; + sSpareBrowserMediaSource = NULL; + return result; +}; + +bool LLViewerMedia::hasInWorldMedia() +{ + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + // This should be quick, because there should be very few non-in-world-media impls + for (; iter != end; iter++) + { + LLViewerMediaImpl* pimpl = *iter; + if (!pimpl->getUsedInUI() && !pimpl->isParcelMedia()) + { + // Found an in-world media impl + return true; + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::hasParcelMedia() +{ + return !LLViewerParcelMedia::getURL().empty(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::hasParcelAudio() +{ + return !LLViewerMedia::getParcelAudioURL().empty(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +std::string LLViewerMedia::getParcelAudioURL() +{ + return LLViewerParcelMgr::getInstance()->getAgentParcel()->getMusicURL(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::initClass() +{ + gIdleCallbacks.addFunction(LLViewerMedia::updateMedia, NULL); + sTeleportFinishConnection = LLViewerParcelMgr::getInstance()-> + setTeleportFinishedCallback(boost::bind(&LLViewerMedia::onTeleportFinished)); +} + ////////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerMedia::cleanupClass() { - // This is no longer necessary, since the list is no longer smart pointers. -#if 0 - while(!sViewerMediaImplList.empty()) - { - sViewerMediaImplList.pop_back(); - } -#endif + gIdleCallbacks.deleteFunction(LLViewerMedia::updateMedia, NULL); + sTeleportFinishConnection.disconnect(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::onTeleportFinished() +{ + // On teleport, clear this setting (i.e. set it to true) + gSavedSettings.setBOOL("MediaTentativeAutoPlay", true); +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setOnlyAudibleMediaTextureID(const LLUUID& texture_id) +{ + sOnlyAudibleTextureID = texture_id; + sForceUpdate = true; } ////////////////////////////////////////////////////////////////////////////////////////// // LLViewerMediaImpl ////////////////////////////////////////////////////////////////////////////////////////// -LLViewerMediaImpl::LLViewerMediaImpl(const std::string& media_url, - const LLUUID& texture_id, +LLViewerMediaImpl::LLViewerMediaImpl( const LLUUID& texture_id, S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop, - const std::string& mime_type) + U8 media_loop) : mMovieImageHasMips(false), - mTextureId(texture_id), mMediaWidth(media_width), mMediaHeight(media_height), mMediaAutoScale(media_auto_scale), mMediaLoop(media_loop), - mMediaURL(media_url), - mMimeType(mime_type), mNeedsNewTexture(true), mTextureUsedWidth(0), mTextureUsedHeight(0), mSuspendUpdates(false), mVisible(true), + mLastSetCursor( UI_CURSOR_ARROW ), + mMediaNavState( MEDIANAVSTATE_NONE ), + mInterest(0.0f), + mUsedInUI(false), mHasFocus(false), + mPriority(PRIORITY_UNLOADED), + mNavigateRediscoverType(false), + mNavigateServerRequest(false), + mMediaSourceFailed(false), + mRequestedVolume(1.0f), + mIsMuted(false), + mNeedsMuteCheck(false), + mPreviousMediaState(MEDIA_NONE), + mPreviousMediaTime(0.0f), + mIsDisabled(false), + mIsParcelMedia(false), + mProximity(-1), + mProximityDistance(0.0f), + mMimeTypeProbe(NULL), + mMediaAutoPlay(false), + mInNearbyMediaList(false), mClearCache(false), - mBackgroundColor(LLColor4::white) + mBackgroundColor(LLColor4::white), + mNavigateSuspended(false), + mNavigateSuspendedDeferred(false), + mIsUpdated(false), + mTrustedBrowser(false), + mZoomFactor(1.0) { - createMediaSource(); + + // Set up the mute list observer if it hasn't been set up already. + if(!sViewerMediaMuteListObserverInitialized) + { + LLMuteList::getInstance()->addObserver(&sViewerMediaMuteListObserver); + sViewerMediaMuteListObserverInitialized = true; + } + + add_media_impl(this); + + setTextureID(texture_id); + + // connect this media_impl to the media texture, creating it if it doesn't exist.0 + // This is necessary because we need to be able to use getMaxVirtualSize() even if the media plugin is not loaded. + LLViewerMediaTexture* media_tex = LLViewerTextureManager::getMediaTexture(mTextureId); + if(media_tex) + { + media_tex->setMediaImpl(); + } + } ////////////////////////////////////////////////////////////////////////////////////////// LLViewerMediaImpl::~LLViewerMediaImpl() { destroyMediaSource(); - LLViewerMedia::removeMedia(this); + + LLViewerMediaTexture::removeMediaImplFromTexture(mTextureId) ; + + setTextureID(); + remove_media_impl(this); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::emitEvent(LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event) +{ + // Broadcast to observers using the superclass version + LLViewerMediaEventEmitter::emitEvent(plugin, event); + + // If this media is on one or more LLVOVolume objects, tell them about the event as well. + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + while(iter != mObjectList.end()) + { + LLVOVolume *self = *iter; + ++iter; + self->mediaEvent(this, plugin, event); + } } ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) { - if((mPluginBase == NULL) || (mMimeType != mime_type)) + bool mimeTypeChanged = (mMimeType != mime_type); + bool pluginChanged = (LLMIMETypes::implType(mCurrentMimeType) != LLMIMETypes::implType(mime_type)); + if(!mPluginBase || pluginChanged) { if(! initializePlugin(mime_type)) { - LL_WARNS("Plugin") << "plugin intialization failed for mime type: " << mime_type << LL_ENDL; + /*LL_WARNS("Plugin") << "plugin intialization failed for mime type: " << mime_type << LL_ENDL; LLSD args; args["MIME_TYPE"] = mime_type; LLNotificationsUtil::add("NoPlugin", args); - return false; + return false;*/ } } + else if(mimeTypeChanged) + { + // The same plugin should be able to handle the new media -- just update the stored mime type. + mMimeType = mime_type; + } // play(); return (mPluginBase != NULL); @@ -835,28 +1768,46 @@ bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::createMediaSource() { + if(mPriority == PRIORITY_UNLOADED) + { + // This media shouldn't be created yet. + return; + } + if(! mMediaURL.empty()) { - navigateTo(mMediaURL, mMimeType, true); + navigateInternal(); } else if(! mMimeType.empty()) { - initializeMedia(mMimeType); + if (!initializeMedia(mMimeType)) + { + LL_WARNS("Media") << "Failed to initialize media for mime type " << mMimeType << LL_ENDL; + } } - + } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::destroyMediaSource() { mNeedsNewTexture = true; - if (!mPluginBase) + + // Tell the viewer media texture it's no longer active + LLViewerMediaTexture* oldImage = LLViewerTextureManager::findMediaTexture( mTextureId ); + if (oldImage) { - return; + oldImage->setPlaying(FALSE) ; } - // Restore the texture - updateMovieImage(LLUUID::null, false); - destroyPlugin(); + + cancelMimeTypeProbe(); + + if(mPluginBase) + { + mPluginBase->setDeleteOK(true) ; + destroyPlugin(); + } + } ////////////////////////////////////////////////////////////////////////////////////////// @@ -867,9 +1818,25 @@ void LLViewerMediaImpl::setMediaType(const std::string& media_type) ////////////////////////////////////////////////////////////////////////////////////////// /*static*/ -LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height) +LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target) { std::string plugin_basename = LLMIMETypes::implType(media_type); + LLPluginClassMedia* media_source = NULL; + + // HACK: we always try to keep a spare running webkit plugin around to improve launch times. + // If a spare was already created before PluginAttachDebuggerToPlugins was set, don't use it. + if(plugin_basename == "media_plugin_webkit" && !gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins")) + { + media_source = LLViewerMedia::getSpareBrowserMediaSource(); + if(media_source) + { + media_source->setOwner(owner); + media_source->setTarget(target); + media_source->setSize(default_width, default_height); + + return media_source; + } + } if(plugin_basename.empty()) { @@ -909,7 +1876,7 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ } else { - LLPluginClassMedia* media_source = new LLPluginClassMedia(owner); + media_source = new LLPluginClassMedia(owner); media_source->setSize(default_width, default_height); media_source->setUserDataPath(user_data_path); media_source->setLanguageCode(LLUI::getLanguage()); @@ -929,6 +1896,8 @@ LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_ bool media_plugin_debugging_enabled = gSavedSettings.getBOOL("MediaPluginDebugging"); media_source->enableMediaPluginDebugging( media_plugin_debugging_enabled ); + media_source->setTarget(target); + const std::string plugin_dir = gDirUtilp->getLLPluginDir(); if (media_source->init(launcher_name, plugin_dir, plugin_name, gSavedSettings.getBOOL("PluginAttachDebuggerToPlugins"))) { @@ -967,7 +1936,21 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) // and unconditionally set the mime type mMimeType = media_type; - LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight); + if(mPriority == PRIORITY_UNLOADED) + { + // This impl should not be loaded at this time. + LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL; + + return false; + } + + // If we got here, we want to ignore previous init failures. + mMediaSourceFailed = false; + + // Save the MIME type that really caused the plugin to load + mCurrentMimeType = mMimeType; + + LLPluginClassMedia* media_source = newSourceFromMediaType(mMimeType, this, mMediaWidth, mMediaHeight, mTarget); if (media_source) { @@ -990,6 +1973,8 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) std::string ca_path = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "CA.pem" ); media_source->addCertificateFilePath( ca_path ); + media_source->proxy_setup(gSavedSettings.getBOOL("BrowserProxyEnabled"), gSavedSettings.getString("BrowserProxyAddress"), gSavedSettings.getS32("BrowserProxyPort")); + if(mClearCache) { mClearCache = false; @@ -1008,13 +1993,71 @@ bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) } mPluginBase = media_source; + mPluginBase->setDeleteOK(false) ; + updateVolume(); return true; } + // Make sure the timer doesn't try re-initing this plugin repeatedly until something else changes. + mMediaSourceFailed = true; + return false; } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::loadURI() +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + // trim whitespace from front and back of URL - fixes EXT-5363 + LLStringUtil::trim( mMediaURL ); + + // *HACK: we don't know if the URI coming in is properly escaped + // (the contract doesn't specify whether it is escaped or not. + // but LLQtWebKit expects it to be, so we do our best to encode + // special characters) + // The strings below were taken right from http://www.ietf.org/rfc/rfc1738.txt + // Note especially that '%' and '/' are there. + std::string uri = LLURI::escape(mMediaURL, + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "$-_.+" + "!*'()," + "{}|\\^~[]`" + "<>#%" + ";/?:@&=", + false); + llinfos << "Asking media source to load URI: " << uri << llendl; + + + plugin->loadURI( uri ); + + // A non-zero mPreviousMediaTime means that either this media was previously unloaded by the priority code while playing/paused, + // or a seek happened before the media loaded. In either case, seek to the saved time. + if(mPreviousMediaTime != 0.0f) + { + seek(mPreviousMediaTime); + } + + if(mPreviousMediaState == MEDIA_PLAYING) + { + // This media was playing before this instance was unloaded. + start(); + } + else if(mPreviousMediaState == MEDIA_PAUSED) + { + // This media was paused before this instance was unloaded. + pause(); + } + else + { + // No relevant previous media play state -- if we're loading the URL, we want to start playing. + start(); + } + } +} void LLViewerMediaImpl::setSize(int width, int height) { LLPluginClassMedia* plugin = getMediaPlugin(); @@ -1026,32 +2069,38 @@ void LLViewerMediaImpl::setSize(int width, int height) } } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::showNotification(LLNotificationPtr notify) +{ + mNotification = notify; +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::hideNotification() +{ + mNotification.reset(); +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::play() { LLPluginClassMedia* plugin = getMediaPlugin(); - // first stop any previously playing media - // stop(); - - // plugin->addObserver( this ); + // If the media source isn't there, try to initialize it and load an URL. if (!plugin) { - if(!initializePlugin(mMimeType)) + if(!initializeMedia(mMimeType)) { - // Plugin failed initialization... should assert or something + // This may be the case where the plugin's priority is PRIORITY_UNLOADED return; } - plugin = getMediaPlugin(); + + // Only do this if the media source was just loaded. + loadURI(); } - // updateMovieImage(mTextureId, true); - - plugin->loadURI( mMediaURL ); - if(/*plugin->pluginSupportsMediaTime()*/ true) - { - start(); - } + // always start the media + start(); } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1073,6 +2122,10 @@ void LLViewerMediaImpl::pause() { plugin->pause(); } + else + { + mPreviousMediaState = MEDIA_PAUSED; + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1083,6 +2136,10 @@ void LLViewerMediaImpl::start() { plugin->start(); } + else + { + mPreviousMediaState = MEDIA_PLAYING; + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1093,19 +2150,99 @@ void LLViewerMediaImpl::seek(F32 time) { plugin->seek(time); } + else + { + // Save the seek time to be set when the media is loaded. + mPreviousMediaTime = time; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::skipBack(F32 step_scale) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + if(plugin->pluginSupportsMediaTime()) + { + F64 back_step = plugin->getCurrentTime() - (plugin->getDuration()*step_scale); + if(back_step < 0.0) + { + back_step = 0.0; + } + plugin->seek(back_step); + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::skipForward(F32 step_scale) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + if(plugin->pluginSupportsMediaTime()) + { + F64 forward_step = plugin->getCurrentTime() + (plugin->getDuration()*step_scale); + if(forward_step > plugin->getDuration()) + { + forward_step = plugin->getDuration(); + } + plugin->seek(forward_step); + } + } } ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::setVolume(F32 volume) +{ + mRequestedVolume = volume; + updateVolume(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::updateVolume() { LLPluginClassMedia* plugin = getMediaPlugin(); - if (plugin) + if(plugin) { - plugin->setVolume(volume); + // always scale the volume by the global media volume + F32 volume = mRequestedVolume * LLViewerMedia::getVolume(); + + if (mProximityCamera > 0) + { + if (mProximityCamera > gSavedSettings.getF32("MediaRollOffMax")) + { + volume = 0; + } + else if (mProximityCamera > gSavedSettings.getF32("MediaRollOffMin")) + { + // attenuated_volume = 1 / (roll_off_rate * (d - min))^2 + // the +1 is there so that for distance 0 the volume stays the same + F64 adjusted_distance = mProximityCamera - gSavedSettings.getF32("MediaRollOffMin"); + F64 attenuation = 1.0 + (gSavedSettings.getF32("MediaRollOffRate") * adjusted_distance); + attenuation = 1.0 / (attenuation * attenuation); + // the attenuation multiplier should never be more than one since that would increase volume + volume = volume * llmin(1.0, attenuation); + } + } + + if (sOnlyAudibleTextureID == LLUUID::null || sOnlyAudibleTextureID == mTextureId) + { + plugin->setVolume(volume); + } + else + { + plugin->setVolume(0.0f); + } } } ////////////////////////////////////////////////////////////////////////////////////////// +F32 LLViewerMediaImpl::getVolume() +{ + return mRequestedVolume; +} void LLViewerMediaImpl::focus(bool focus) { mHasFocus = focus; @@ -1132,6 +2269,17 @@ bool LLViewerMediaImpl::hasFocus() const return mHasFocus; } +std::string LLViewerMediaImpl::getCurrentMediaURL() +{ + if(!mCurrentMediaURL.empty()) + { + return mCurrentMediaURL; + } + + return mMediaURL; +} + +////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::clearCache() { LLPluginClassMedia* plugin = getMediaPlugin(); @@ -1145,6 +2293,18 @@ void LLViewerMediaImpl::clearCache() } } + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::setPageZoomFactor( double factor ) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin && factor != mZoomFactor) + { + mZoomFactor = factor; + plugin->set_page_zoom_factor( factor ); + } +} + ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::mouseDown(S32 x, S32 y, MASK mask, S32 button) { @@ -1299,6 +2459,78 @@ BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask) } ////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::updateJavascriptObject() +{ + static LLFrameTimer timer ; + + LLPluginClassMedia* plugin = getMediaPlugin(); + if ( plugin ) + { + // flag to expose this information to internal browser or not. + bool enable = gSavedSettings.getBOOL("BrowserEnableJSObject"); + + if(!enable) + { + return ; //no need to go further. + } + + if(timer.getElapsedTimeF32() < 1.0f) + { + return ; //do not update more than once per second. + } + timer.reset() ; + + plugin->jsEnableObject( enable ); + + // these values are only menaingful after login so don't set them before + //bool logged_in = LLLoginInstance::getInstance()->authSuccess(); + bool logged_in = (LLStartUp::getStartupState() >= STATE_STARTED); + if ( logged_in ) + { + // current location within a region + LLVector3 agent_pos = gAgent.getPositionAgent(); + double x = agent_pos.mV[ VX ]; + double y = agent_pos.mV[ VY ]; + double z = agent_pos.mV[ VZ ]; + plugin->jsAgentLocationEvent( x, y, z ); + + // current location within the grid + LLVector3d agent_pos_global = gAgent.getLastPositionGlobal(); + double global_x = agent_pos_global.mdV[ VX ]; + double global_y = agent_pos_global.mdV[ VY ]; + double global_z = agent_pos_global.mdV[ VZ ]; + plugin->jsAgentGlobalLocationEvent( global_x, global_y, global_z ); + + // current agent orientation + double rotation = atan2( gAgent.getAtAxis().mV[VX], gAgent.getAtAxis().mV[VY] ); + double angle = rotation * RAD_TO_DEG; + if ( angle < 0.0f ) angle = 360.0f + angle; // TODO: has to be a better way to get orientation! + plugin->jsAgentOrientationEvent( angle ); + + // current region agent is in + std::string region_name(""); + LLViewerRegion* region = gAgent.getRegion(); + if ( region ) + { + region_name = region->getName(); + }; + plugin->jsAgentRegionEvent( region_name ); + } + + // language code the viewer is set to + plugin->jsAgentLanguageEvent( LLUI::getLanguage() ); + + // maturity setting the agent has selected + if ( gAgent.prefersAdult() ) + plugin->jsAgentMaturityEvent( "GMA" ); // Adult means see adult, mature and general content + else + if ( gAgent.prefersMature() ) + plugin->jsAgentMaturityEvent( "GM" ); // Mature means see mature and general content + else + if ( gAgent.prefersPG() ) + plugin->jsAgentMaturityEvent( "G" ); // PG means only see General content + } +} const std::string& LLViewerMediaImpl::getName() const { LLPluginClassMedia* plugin = getMediaPlugin(); @@ -1309,70 +2541,178 @@ const std::string& LLViewerMediaImpl::getName() const return LLStringUtil::null; }; + ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::navigateHome() +void LLViewerMediaImpl::navigateBack() { LLPluginClassMedia* plugin = getMediaPlugin(); if (plugin) { - plugin->loadURI( mHomeURL ); + plugin->browse_back(); } } ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type) +void LLViewerMediaImpl::navigateForward() { LLPluginClassMedia* plugin = getMediaPlugin(); - if(rediscover_type) + if (plugin) + { + plugin->browse_forward(); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateReload() +{ + navigateTo(getCurrentMediaURL(), "", true, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateHome() +{ + bool rediscover_mimetype = mHomeMimeType.empty(); + navigateTo(mHomeURL, mHomeMimeType, rediscover_mimetype, false); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::unload() +{ + // Unload the media impl and clear its state. + destroyMediaSource(); + resetPreviousMediaState(); + mMediaURL.clear(); + mMimeType.clear(); + mCurrentMediaURL.clear(); + mCurrentMimeType.clear(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type, bool server_request) +{ + cancelMimeTypeProbe(); + + if(mMediaURL != url) + { + // Don't carry media play state across distinct URLs. + resetPreviousMediaState(); + } + + // Always set the current URL and MIME type. + mMediaURL = url; + mMimeType = mime_type; + + // Clear the current media URL, since it will no longer be correct. + mCurrentMediaURL.clear(); + + // if mime type discovery was requested, we'll need to do it when the media loads + mNavigateRediscoverType = rediscover_type; + + // and if this was a server request, the navigate on load will also need to be one. + mNavigateServerRequest = server_request; + + // An explicit navigate resets the "failed" flag. + mMediaSourceFailed = false; + + if(mPriority == PRIORITY_UNLOADED) + { + // Helpful to have media urls in log file. Shouldn't be spammy. + llinfos << "NOT LOADING media id= " << mTextureId << " url=" << url << " mime_type=" << mime_type << llendl; + + // This impl should not be loaded at this time. + LL_DEBUGS("PluginPriority") << this << "Not loading (PRIORITY_UNLOADED)" << LL_ENDL; + + return; + } + + navigateInternal(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateInternal() +{ + // Helpful to have media urls in log file. Shouldn't be spammy. + llinfos << "media id= " << mTextureId << " url=" << mMediaURL << " mime_type=" << mMimeType << llendl; + + if(mNavigateSuspended) + { + llwarns << "Deferring navigate." << llendl; + mNavigateSuspendedDeferred = true; + return; + } + + if(mMimeTypeProbe != NULL) + { + llwarns << "MIME type probe already in progress -- bailing out." << llendl; + return; + } + + if(mNavigateServerRequest) + { + setNavState(MEDIANAVSTATE_SERVER_SENT); + } + else + { + setNavState(MEDIANAVSTATE_NONE); + } + + // If the caller has specified a non-empty MIME type, look that up in our MIME types list. + // If we have a plugin for that MIME type, use that instead of attempting auto-discovery. + // This helps in supporting legacy media content where the server the media resides on returns a bogus MIME type + // but the parcel owner has correctly set the MIME type in the parcel media settings. + + if(!mMimeType.empty() && (mMimeType != LLMIMETypes::getDefaultMimeType())) + { + std::string plugin_basename = LLMIMETypes::implType(mMimeType); + if(!plugin_basename.empty()) + { + // We have a plugin for this mime type + mNavigateRediscoverType = false; + } + } + + if(mNavigateRediscoverType) { - LLURI uri(url); + LLURI uri(mMediaURL); std::string scheme = uri.scheme(); - if(scheme.empty() || ("http" == scheme || "https" == scheme)) + if(scheme.empty() || "http" == scheme || "https" == scheme) { - if(mime_type.empty()) - { - LLHTTPClient::getHeaderOnly(url, new LLMimeDiscoveryResponder(this, "text/html")); - } - else if(initializeMedia(mime_type) && (plugin = getMediaPlugin())) - { - plugin->loadURI( url ); - } + // If we don't set an Accept header, LLHTTPClient will add one like this: + // Accept: application/llsd+xml + // which is really not what we want. + AIHTTPHeaders headers; + headers.addHeader("Accept", "*/*"); + headers.addHeader("Cookie", ""); + LLHTTPClient::getHeaderOnly( mMediaURL, new LLMimeDiscoveryResponder(this, "text/html"), headers); } else if("data" == scheme || "file" == scheme || "about" == scheme) { // FIXME: figure out how to really discover the type for these schemes // We use "data" internally for a text/html url for loading the login screen - if(initializeMedia("text/html") && (plugin = getMediaPlugin())) + if(initializeMedia("text/html")) { - plugin->loadURI( url ); + loadURI(); } } else { // This catches 'rtsp://' urls - if(initializeMedia(scheme) && (plugin = getMediaPlugin())) + if(initializeMedia(scheme)) { - plugin->loadURI( url ); + loadURI(); } } } - else if (plugin) + else if(initializeMedia(mMimeType)) { - plugin->loadURI( url ); - } - else if(initializeMedia(mime_type) && (plugin = getMediaPlugin())) - { - plugin->loadURI( url ); + loadURI(); } else { - LL_WARNS("Media") << "Couldn't navigate to: " << url << " as there is no media type for: " << mime_type << LL_ENDL; - return; + LL_WARNS("Media") << "Couldn't navigate to: " << mMediaURL << " as there is no media type for: " << mMimeType << LL_ENDL; } - mMediaURL = url; - } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1476,50 +2816,55 @@ bool LLViewerMediaImpl::canNavigateBack() return result; } - ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::updateMovieImage(const LLUUID& uuid, BOOL active) -{ - // IF the media image hasn't changed, do nothing - if (mTextureId == uuid) - { - return; - } - // If we have changed media uuid, restore the old one - if (!mTextureId.isNull()) - { - LLViewerTexture* oldImage = LLViewerTextureManager::findTexture( mTextureId ); - if (oldImage) - { - // Casting to LLViewerMediaTexture is a huge hack. Implement LLViewerMediaTexture some time later. - ((LLViewerMediaTexture*)oldImage)->reinit(mMovieImageHasMips); - oldImage->mIsMediaTexture = FALSE; - } - } - // If the movie is playing, set the new media image - if (active && !uuid.isNull()) - { - LLViewerTexture* viewerImage = LLViewerTextureManager::findTexture( uuid ); - if( viewerImage ) - { - mTextureId = uuid; - // Can't use mipmaps for movies because they don't update the full image - // Casting to LLViewerMediaTexture is a huge hack. Implement LLViewerMediaTexture some time later. - mMovieImageHasMips = ((LLViewerMediaTexture*)viewerImage)->getUseMipMaps(); - ((LLViewerMediaTexture*)viewerImage)->reinit(FALSE); - viewerImage->mIsMediaTexture = TRUE; - } - } -} +static LLFastTimer::DeclareTimer FTM_MEDIA_DO_UPDATE("Do Update"); +static LLFastTimer::DeclareTimer FTM_MEDIA_GET_DATA("Get Data"); +static LLFastTimer::DeclareTimer FTM_MEDIA_SET_SUBIMAGE("Set Subimage"); + -////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::update() { - LLPluginClassMedia* plugin = getMediaPlugin(); + LLFastTimer t(FTM_MEDIA_DO_UPDATE); - if(plugin) + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(plugin == NULL) { - // If we didn't just create the impl, it may need to get cookie updates. + if(mPriority == PRIORITY_UNLOADED) + { + // This media source should not be loaded. + } + else if(mPriority <= PRIORITY_SLIDESHOW) + { + // Don't load new instances that are at PRIORITY_SLIDESHOW or below. They're just kept around to preserve state. + } + else if(mMimeTypeProbe != NULL) + { + // this media source is doing a MIME type probe -- don't try loading it again. + } + else + { + // This media may need to be loaded. + if(sMediaCreateTimer.hasExpired()) + { + LL_DEBUGS("PluginPriority") << this << ": creating media based on timer expiration" << LL_ENDL; + createMediaSource(); + sMediaCreateTimer.setTimerExpirySec(LLVIEWERMEDIA_CREATE_DELAY); + } + else + { + LL_DEBUGS("PluginPriority") << this << ": NOT creating media (waiting on timer)" << LL_ENDL; + } + } + } + else + { + updateVolume(); + + // TODO: this is updated every frame - is this bad? + updateJavascriptObject(); + + // If we didn't just create the impl, it may need to get cookie updates. if(!sUpdatedCookies.empty()) { // TODO: Only send cookies to plugins that need them @@ -1527,15 +2872,28 @@ void LLViewerMediaImpl::update() } } - if (!plugin) + + if(plugin == NULL) { return; } + // Make sure a navigate doesn't happen during the idle -- it can cause mMediaSource to get destroyed, which can cause a crash. + setNavigateSuspended(true); + plugin->idle(); + + setNavigateSuspended(false); + + plugin = getMediaPlugin(); + if(plugin == NULL) + { + return; + } if (plugin->isPluginExited()) { + resetPreviousMediaState(); destroyMediaSource(); return; } @@ -1550,11 +2908,15 @@ void LLViewerMediaImpl::update() return; } - LLViewerTexture* placeholder_image = updatePlaceholderImage(); + LLViewerMediaTexture* placeholder_image = updatePlaceholderImage(); if(placeholder_image) { LLRect dirty_rect; + + // Since we're updating this texture, we know it's playing. Tell the texture to do its replacement magic so it gets rendered. + placeholder_image->setPlaying(TRUE); + if (plugin->getDirty(&dirty_rect)) { // Constrain the dirty rect to be inside the texture @@ -1566,21 +2928,28 @@ void LLViewerMediaImpl::update() if(width > 0 && height > 0) { - U8* data = plugin->getBitsData(); + U8* data = NULL; + { + LLFastTimer t(FTM_MEDIA_GET_DATA); + data = plugin->getBitsData(); + } // Offset the pixels pointer to match x_pos and y_pos data += ( x_pos * plugin->getTextureDepth() * plugin->getBitsWidth() ); data += ( y_pos * plugin->getTextureDepth() ); - placeholder_image->setSubImage( - data, - plugin->getBitsWidth(), - plugin->getBitsHeight(), - x_pos, - y_pos, - width, - height, - TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.) + { + LLFastTimer t(FTM_MEDIA_SET_SUBIMAGE); + placeholder_image->setSubImage( + data, + plugin->getBitsWidth(), + plugin->getBitsHeight(), + x_pos, + y_pos, + width, + height, + TRUE); // force a fast update (i.e. don't call analyzeAlpha, etc.) + } } @@ -1597,7 +2966,7 @@ void LLViewerMediaImpl::updateImagesMediaStreams() ////////////////////////////////////////////////////////////////////////////////////////// -/*LLViewerMediaTexture*/LLViewerTexture* LLViewerMediaImpl::updatePlaceholderImage() +LLViewerMediaTexture* LLViewerMediaImpl::updatePlaceholderImage() { if(mTextureId.isNull()) { @@ -1605,14 +2974,12 @@ void LLViewerMediaImpl::updateImagesMediaStreams() return NULL; } - LLViewerMediaTexture* placeholder_image = (LLViewerMediaTexture*)LLViewerTextureManager::getFetchedTexture( mTextureId ); - placeholder_image->getLastReferencedTimer()->reset(); - + LLViewerMediaTexture* placeholder_image = LLViewerTextureManager::getMediaTexture( mTextureId ); + LLPluginClassMedia* plugin = getMediaPlugin(); if (mNeedsNewTexture || placeholder_image->getUseMipMaps() - || !placeholder_image->mIsMediaTexture || (placeholder_image->getWidth() != plugin->getTextureWidth()) || (placeholder_image->getHeight() != plugin->getTextureHeight()) || (mTextureUsedWidth != plugin->getWidth()) @@ -1648,10 +3015,11 @@ void LLViewerMediaImpl::updateImagesMediaStreams() placeholder_image->createGLTexture(discard_level, raw); // placeholder_image->setExplicitFormat() - placeholder_image->setUseMipMaps(FALSE); + //placeholder_image->setUseMipMaps(FALSE); // MEDIAOPT: set this dynamically on play/stop - placeholder_image->mIsMediaTexture = true; + // FIXME +// placeholder_image->mIsMediaTexture = true; mNeedsNewTexture = false; // If the amount of the texture being drawn by the media goes down in either width or height, @@ -1665,7 +3033,7 @@ void LLViewerMediaImpl::updateImagesMediaStreams() ////////////////////////////////////////////////////////////////////////////////////////// -LLUUID LLViewerMediaImpl::getMediaTextureID() +LLUUID LLViewerMediaImpl::getMediaTextureID() const { return mTextureId; } @@ -1681,18 +3049,13 @@ void LLViewerMediaImpl::setVisible(bool visible) if(plugin && plugin->isPluginExited()) { destroyMediaSource(); + plugin = NULL; } - if(!plugin) { createMediaSource(); } } - - if(plugin) - { - plugin->setPriority(mVisible?LLPluginClassBasic::PRIORITY_NORMAL:LLPluginClassBasic::PRIORITY_SLEEP); - } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -1701,36 +3064,6 @@ void LLViewerMediaImpl::mouseCapture() gFocusMgr.setMouseCapture(this); } -////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::getTextureSize(S32 *texture_width, S32 *texture_height) -{ - LLPluginClassMedia* plugin = getMediaPlugin(); - if(plugin && plugin->textureValid()) - { - S32 real_texture_width = plugin->getBitsWidth(); - S32 real_texture_height = plugin->getBitsHeight(); - - { - // The "texture width" coming back from the plugin may not be a power of two (thanks to webkit). - // It will be the correct "data width" to pass to setSubImage - int i; - - for(i = 1; i < real_texture_width; i <<= 1) - ; - *texture_width = i; - - for(i = 1; i < real_texture_height; i <<= 1) - ; - *texture_height = i; - } - - } - else - { - *texture_width = 0; - *texture_height = 0; - } -} ////////////////////////////////////////////////////////////////////////////////////////// void LLViewerMediaImpl::scaleMouse(S32 *mouse_x, S32 *mouse_y) { @@ -1745,6 +3078,20 @@ void LLViewerMediaImpl::scaleMouse(S32 *mouse_x, S32 *mouse_y) #endif } +////////////////////////////////////////////////////////////////////////////////////////// +bool LLViewerMediaImpl::isMediaTimeBased() +{ + bool result = false; + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(plugin) + { + result = plugin->pluginSupportsMediaTime(); + } + + return result; +} + ////////////////////////////////////////////////////////////////////////////////////////// bool LLViewerMediaImpl::isMediaPlaying() { @@ -1777,29 +3124,337 @@ bool LLViewerMediaImpl::isMediaPaused() ////////////////////////////////////////////////////////////////////////////////////////// // -bool LLViewerMediaImpl::hasMedia() +bool LLViewerMediaImpl::hasMedia() const { return mPluginBase != NULL; } ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event) +// +void LLViewerMediaImpl::resetPreviousMediaState() { + mPreviousMediaState = MEDIA_NONE; + mPreviousMediaTime = 0.0f; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +void LLViewerMediaImpl::setDisabled(bool disabled, bool forcePlayOnEnable) +{ + if(mIsDisabled != disabled) + { + // Only do this on actual state transitions. + mIsDisabled = disabled; + + if(mIsDisabled) + { + // We just disabled this media. Clear all state. + unload(); + } + else + { + // We just (re)enabled this media. Do a navigate if auto-play is in order. + if(isAutoPlayable() || forcePlayOnEnable) + { + navigateTo(mMediaEntryURL, "", true, true); + } + } + + } +}; + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isForcedUnloaded() const +{ + if(mIsMuted || mMediaSourceFailed || mIsDisabled) + { + return true; + } + + // If this media's class is not supposed to be shown, unload + if (!shouldShowBasedOnClass()) + { + return true; + } + + return false; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isPlayable() const +{ + if(isForcedUnloaded()) + { + // All of the forced-unloaded criteria also imply not playable. + return false; + } + + if(hasMedia()) + { + // Anything that's already playing is, by definition, playable. + return true; + } + + if(!mMediaURL.empty()) + { + // If something has navigated the instance, it's ready to be played. + return true; + } + + return false; +} + +static void handle_pick_file_request_continued(LLPluginClassMedia* plugin, AIFilePicker* filepicker) +{ + std::string response; + + if(filepicker->hasFilename()) + { + response = filepicker->getFilename(); + } + + plugin->sendPickFileResponse(response); +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent event) +{ + bool pass_through = true; switch(event) { - case MEDIA_EVENT_PLUGIN_FAILED: + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: { + LL_DEBUGS("Media") << "MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is: " << plugin->getClickURL() << LL_ENDL; + std::string url = plugin->getClickURL(); + std::string nav_type = plugin->getClickNavType(); + LLURLDispatcher::dispatch(url, nav_type, NULL, mTrustedBrowser); + } + break; + case MEDIA_EVENT_CLICK_LINK_HREF: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << plugin->getClickTarget() << "\", uri is " << plugin->getClickURL() << LL_ENDL; + }; + break; + case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH: + { + // The plugin failed to load properly. Make sure the timer doesn't retry. + // TODO: maybe mark this plugin as not loadable somehow? + mMediaSourceFailed = true; + + // Reset the last known state of the media to defaults. + resetPreviousMediaState(); + + // TODO: may want a different message for this case? LLSD args; - args["PLUGIN"] = LLMIMETypes::implType(mMimeType); + args["PLUGIN"] = LLMIMETypes::implType(mCurrentMimeType); LLNotificationsUtil::add("MediaPluginFailed", args); } break; + + case MEDIA_EVENT_PLUGIN_FAILED: + { + // The plugin crashed. + mMediaSourceFailed = true; + + // Reset the last known state of the media to defaults. + resetPreviousMediaState(); + + LLSD args; + args["PLUGIN"] = LLMIMETypes::implType(mCurrentMimeType); + // SJB: This is getting called every frame if the plugin fails to load, continuously respawining the alert! + //LLNotificationsUtil::add("MediaPluginFailed", args); + } + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << plugin->getCursorName() << LL_ENDL; + + std::string cursor = plugin->getCursorName(); + + if(cursor == "arrow") + mLastSetCursor = UI_CURSOR_ARROW; + else if(cursor == "ibeam") + mLastSetCursor = UI_CURSOR_IBEAM; + else if(cursor == "splith") + mLastSetCursor = UI_CURSOR_SIZEWE; + else if(cursor == "splitv") + mLastSetCursor = UI_CURSOR_SIZENS; + else if(cursor == "hand") + mLastSetCursor = UI_CURSOR_HAND; + else // for anything else, default to the arrow + mLastSetCursor = UI_CURSOR_ARROW; + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_BEGIN: + { + LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_BEGIN, uri is: " << plugin->getNavigateURI() << LL_ENDL; + hideNotification(); + + if(getNavState() == MEDIANAVSTATE_SERVER_SENT) + { + setNavState(MEDIANAVSTATE_SERVER_BEGUN); + } + else + { + setNavState(MEDIANAVSTATE_BEGUN); + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE: + { + LL_DEBUGS("Media") << "MEDIA_EVENT_NAVIGATE_COMPLETE, uri is: " << plugin->getNavigateURI() << LL_ENDL; + + std::string url = plugin->getNavigateURI(); + if(getNavState() == MEDIANAVSTATE_BEGUN) + { + if(mCurrentMediaURL == url) + { + // This is a navigate that takes us to the same url as the previous navigate. + setNavState(MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS); + } + else + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED); + } + } + else if(getNavState() == MEDIANAVSTATE_SERVER_BEGUN) + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED); + } + else + { + // all other cases need to leave the state alone. + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_LOCATION_CHANGED: + { + LL_DEBUGS("Media") << "MEDIA_EVENT_LOCATION_CHANGED, uri is: " << plugin->getLocation() << LL_ENDL; + + std::string url = plugin->getLocation(); + + if(getNavState() == MEDIANAVSTATE_BEGUN) + { + if(mCurrentMediaURL == url) + { + // This is a navigate that takes us to the same url as the previous navigate. + setNavState(MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS); + } + else + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_FIRST_LOCATION_CHANGED); + } + } + else if(getNavState() == MEDIANAVSTATE_SERVER_BEGUN) + { + mCurrentMediaURL = url; + setNavState(MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED); + } + else + { + // Don't track redirects. + setNavState(MEDIANAVSTATE_NONE); + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_PICK_FILE_REQUEST: + { + AIFilePicker* filepicker = AIFilePicker::create(); + filepicker->open(FFLOAD_ALL, "", "openfile", true); + filepicker->run(boost::bind(&handle_pick_file_request_continued, plugin, filepicker)); + // Display a file picker + //std::string response; + + /*LLFilePicker& picker = LLFilePicker::instance(); + if (!picker.getOpenFile(LLFilePicker::FFLOAD_ALL)) + { + // The user didn't pick a file -- the empty response string will indicate this. + } + + response = picker.getFirstFile(); + + plugin->sendPickFileResponse(response);*/ + } + break; + + + case LLViewerMediaObserver::MEDIA_EVENT_AUTH_REQUEST: + { + LLNotification::Params auth_request_params("AuthRequest"); + + // pass in host name and realm for site (may be zero length but will always exist) + LLSD args; + LLURL raw_url( plugin->getAuthURL().c_str() ); + args["HOST_NAME"] = raw_url.getAuthority(); + args["REALM"] = plugin->getAuthRealm(); + auth_request_params.substitutions = args; + + auth_request_params.payload = LLSD().with("media_id", mTextureId); + auth_request_params.functor(boost::bind(&LLViewerMedia::onAuthSubmit, _1, _2)); + LLNotifications::instance().add(auth_request_params); + }; + break; + + case LLViewerMediaObserver::MEDIA_EVENT_CLOSE_REQUEST: + { + std::string uuid = plugin->getClickUUID(); + + llinfos << "MEDIA_EVENT_CLOSE_REQUEST for uuid " << uuid << llendl; + + if(uuid.empty()) + { + // This close request is directed at this instance, let it fall through. + } + else + { + // This close request is directed at another instance + pass_through = false; + LLFloaterWebContent::closeRequest(uuid); + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_GEOMETRY_CHANGE: + { + std::string uuid = plugin->getClickUUID(); + + llinfos << "MEDIA_EVENT_GEOMETRY_CHANGE for uuid " << uuid << llendl; + + if(uuid.empty()) + { + // This geometry change request is directed at this instance, let it fall through. + } + else + { + // This request is directed at another instance + pass_through = false; + LLFloaterWebContent::geometryChanged(uuid, plugin->getGeometryX(), plugin->getGeometryY(), plugin->getGeometryWidth(), plugin->getGeometryHeight()); + } + } + break; + default: break; } - // Just chain the event to observers. - emitEvent(self, event); + + if(pass_through) + { + // Just chain the event to observers. + emitEvent(plugin, event); + } } + //////////////////////////////////////////////////////////////////////////////// // virtual void LLViewerMediaImpl::handleCookieSet(LLPluginClassMedia* self, const std::string &cookie) @@ -1873,6 +3528,133 @@ LLViewerMediaImpl::canPaste() const return FALSE; } +void LLViewerMediaImpl::setUpdated(BOOL updated) +{ + mIsUpdated = updated ; +} + +BOOL LLViewerMediaImpl::isUpdated() +{ + return mIsUpdated ; +} + +static LLFastTimer::DeclareTimer FTM_MEDIA_CALCULATE_INTEREST("Calculate Interest"); + +void LLViewerMediaImpl::calculateInterest() +{ + LLFastTimer t(FTM_MEDIA_CALCULATE_INTEREST); + LLViewerMediaTexture* texture = LLViewerTextureManager::findMediaTexture( mTextureId ); + + if(texture != NULL) + { + mInterest = texture->getMaxVirtualSize(); + } + else + { + // This will be a relatively common case now, since it will always be true for unloaded media. + mInterest = 0.0f; + } + + // Calculate distance from the avatar, for use in the proximity calculation. + mProximityDistance = 0.0f; + mProximityCamera = 0.0f; + if(!mObjectList.empty()) + { + // Just use the first object in the list. We could go through the list and find the closest object, but this should work well enough. + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + LLVOVolume* objp = *iter ; + llassert_always(objp != NULL) ; + + // The distance calculation is invalid for HUD attachments -- leave both mProximityDistance and mProximityCamera at 0 for them. + if(!objp->isHUDAttachment()) + { + LLVector3d obj_global = objp->getPositionGlobal() ; + LLVector3d agent_global = gAgent.getPositionGlobal() ; + LLVector3d global_delta = agent_global - obj_global ; + mProximityDistance = global_delta.magVecSquared(); // use distance-squared because it's cheaper and sorts the same. + + LLVector3d camera_delta = gAgentCamera.getCameraPositionGlobal() - obj_global; + mProximityCamera = camera_delta.magVec(); + } + } + + if(mNeedsMuteCheck) + { + // Check all objects this instance is associated with, and those objects' owners, against the mute list + mIsMuted = false; + + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + for(; iter != mObjectList.end() ; ++iter) + { + LLVOVolume *obj = *iter; + llassert(obj); + if (!obj) continue; + if(LLMuteList::getInstance() && + LLMuteList::getInstance()->isMuted(obj->getID())) + { + mIsMuted = true; + } + else + { + // We won't have full permissions data for all objects. Attempt to mute objects when we can tell their owners are muted. + if (LLSelectMgr::getInstance()) + { + LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(obj); + if(obj_perm) + { + if(LLMuteList::getInstance() && + LLMuteList::getInstance()->isMuted(obj_perm->getOwner())) + mIsMuted = true; + } + } + } + } + + mNeedsMuteCheck = false; + } +} + +F64 LLViewerMediaImpl::getApproximateTextureInterest() +{ + F64 result = 0.0f; + + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + result = plugin->getFullWidth(); + result *= plugin->getFullHeight(); + } + else + { + // No media source is loaded -- all we have to go on is the texture size that has been set on the impl, if any. + result = mMediaWidth; + result *= mMediaHeight; + } + + return result; +} + +void LLViewerMediaImpl::setUsedInUI(bool used_in_ui) +{ + mUsedInUI = used_in_ui; + + // HACK: Force elements used in UI to load right away. + // This fixes some issues where UI code that uses the browser instance doesn't expect it to be unloaded. + if(mUsedInUI && (mPriority == PRIORITY_UNLOADED)) + { + if(getVisible()) + { + setPriority(PRIORITY_NORMAL); + } + else + { + setPriority(PRIORITY_HIDDEN); + } + + createMediaSource(); + } +}; + void LLViewerMediaImpl::setBackgroundColor(LLColor4 color) { mBackgroundColor = color; @@ -1883,3 +3665,316 @@ void LLViewerMediaImpl::setBackgroundColor(LLColor4 color) plugin->setBackgroundColor(mBackgroundColor); } }; + +F64 LLViewerMediaImpl::getCPUUsage() const +{ + F64 result = 0.0f; + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(plugin) + { + result = plugin->getCPUUsage(); + } + + return result; +} + +char const* PRIORITYToString(LLViewerMediaImpl::EPriority priority) +{ + switch(priority) + { + case LLViewerMediaImpl::PRIORITY_UNLOADED: return "unloaded"; + case LLViewerMediaImpl::PRIORITY_HIDDEN: return "hidden"; + case LLViewerMediaImpl::PRIORITY_SLIDESHOW: return "slideshow"; + case LLViewerMediaImpl::PRIORITY_LOW: return "low"; + case LLViewerMediaImpl::PRIORITY_NORMAL: return "normal"; + case LLViewerMediaImpl::PRIORITY_HIGH: return "high"; + default: return "UNKNOWN"; + } +} + +void LLViewerMediaImpl::setPriority(EPriority priority) +{ + if(mPriority != priority) + { + LL_DEBUGS("PluginPriority") + << "changing priority of media id " << mTextureId + << " from " << ::PRIORITYToString(mPriority) + << " to " << ::PRIORITYToString(priority) + << LL_ENDL; + } + + mPriority = priority; + + LLPluginClassMedia* plugin = getMediaPlugin(); + + if(priority == PRIORITY_UNLOADED) + { + if(plugin) + { + // Need to unload the media source + + // First, save off previous media state + mPreviousMediaState = plugin->getStatus(); + mPreviousMediaTime = plugin->getCurrentTime(); + + destroyMediaSource(); + plugin = NULL; + } + } + + if(plugin) + { + if(mPriority >= PRIORITY_LOW) + plugin->setPriority((LLPluginClassBasic::EPriority)((U32)mPriority-((U32)PRIORITY_LOW-1))); + else + plugin->setPriority(LLPluginClassBasic::PRIORITY_SLEEP); + } + + // NOTE: loading (or reloading) media sources whose priority has risen above PRIORITY_UNLOADED is done in update(). +} + +void LLViewerMediaImpl::setLowPrioritySizeLimit(int size) +{ + LLPluginClassMedia* plugin = getMediaPlugin(); + if(plugin) + { + plugin->setLowPrioritySizeLimit(size); + } +} + +void LLViewerMediaImpl::setNavState(EMediaNavState state) +{ + mMediaNavState = state; + + switch (state) + { + case MEDIANAVSTATE_NONE: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_NONE" << llendl; break; + case MEDIANAVSTATE_BEGUN: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_BEGUN" << llendl; break; + case MEDIANAVSTATE_FIRST_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_FIRST_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS" << llendl; break; + case MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS" << llendl; break; + case MEDIANAVSTATE_SERVER_SENT: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_SENT" << llendl; break; + case MEDIANAVSTATE_SERVER_BEGUN: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_BEGUN" << llendl; break; + case MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED" << llendl; break; + case MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED: LL_DEBUGS("Media") << "Setting nav state to MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED" << llendl; break; + } +} + +void LLViewerMediaImpl::setNavigateSuspended(bool suspend) +{ + if(mNavigateSuspended != suspend) + { + mNavigateSuspended = suspend; + if(!suspend) + { + // We're coming out of suspend. If someone tried to do a navigate while suspended, do one now instead. + if(mNavigateSuspendedDeferred) + { + mNavigateSuspendedDeferred = false; + navigateInternal(); + } + } + } +} + +void LLViewerMediaImpl::cancelMimeTypeProbe() +{ + if(mMimeTypeProbe != NULL) + { + // There doesn't seem to be a way to actually cancel an outstanding request. + // Simulate it by telling the LLMimeDiscoveryResponder not to write back any results. + mMimeTypeProbe->cancelRequest(); + + // The above should already have set mMimeTypeProbe to NULL. + if(mMimeTypeProbe != NULL) + { + llerrs << "internal error: mMimeTypeProbe is not NULL after cancelling request." << llendl; + } + } +} + +void LLViewerMediaImpl::addObject(LLVOVolume* obj) +{ + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + for(; iter != mObjectList.end() ; ++iter) + { + if(*iter == obj) + { + return ; //already in the list. + } + } + + mObjectList.push_back(obj) ; + mNeedsMuteCheck = true; +} + +void LLViewerMediaImpl::removeObject(LLVOVolume* obj) +{ + mObjectList.remove(obj) ; + mNeedsMuteCheck = true; +} + +const std::list< LLVOVolume* >* LLViewerMediaImpl::getObjectList() const +{ + return &mObjectList ; +} + +LLVOVolume *LLViewerMediaImpl::getSomeObject() +{ + LLVOVolume *result = NULL; + + std::list< LLVOVolume* >::iterator iter = mObjectList.begin() ; + if(iter != mObjectList.end()) + { + result = *iter; + } + + return result; +} + +void LLViewerMediaImpl::setTextureID(LLUUID id) +{ + if(id != mTextureId) + { + if(mTextureId.notNull()) + { + // Remove this item's entry from the map + sViewerMediaTextureIDMap.erase(mTextureId); + } + + if(id.notNull()) + { + sViewerMediaTextureIDMap.insert(LLViewerMedia::impl_id_map::value_type(id, this)); + } + + mTextureId = id; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isAutoPlayable() const +{ + return (mMediaAutoPlay && + gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getBOOL("MediaTentativeAutoPlay")); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::shouldShowBasedOnClass() const +{ + // If this is parcel media or in the UI, return true always + if (getUsedInUI() || isParcelMedia()) return true; + + bool attached_to_another_avatar = isAttachedToAnotherAvatar(); + bool inside_parcel = isInAgentParcel(); + + // llinfos << " hasFocus = " << hasFocus() << + // " others = " << (attached_to_another_avatar && gSavedSettings.getBOOL(LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING)) << + // " within = " << (inside_parcel && gSavedSettings.getBOOL(LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING)) << + // " outside = " << (!inside_parcel && gSavedSettings.getBOOL(LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING)) << llendl; + + // If it has focus, we should show it + // This is incorrect, and causes EXT-6750 (disabled attachment media still plays) +// if (hasFocus()) +// return true; + + // If it is attached to an avatar and the pref is off, we shouldn't show it + if (attached_to_another_avatar) + { + static LLCachedControl show_media_on_others(gSavedSettings, LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING); + return show_media_on_others; + } + if (inside_parcel) + { + static LLCachedControl show_media_within_parcel(gSavedSettings, LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING); + + return show_media_within_parcel; + } + else + { + static LLCachedControl show_media_outside_parcel(gSavedSettings, LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING); + + return show_media_outside_parcel; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isAttachedToAnotherAvatar() const +{ + bool result = false; + + std::list< LLVOVolume* >::const_iterator iter = mObjectList.begin(); + std::list< LLVOVolume* >::const_iterator end = mObjectList.end(); + for ( ; iter != end; iter++) + { + if (isObjectAttachedToAnotherAvatar(*iter)) + { + result = true; + break; + } + } + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +//static +bool LLViewerMediaImpl::isObjectAttachedToAnotherAvatar(LLVOVolume *obj) +{ + bool result = false; + LLXform *xform = obj; + // Walk up parent chain + while (NULL != xform) + { + LLViewerObject *object = dynamic_cast (xform); + if (NULL != object) + { + LLVOAvatar *avatar = object->asAvatar(); + if ((NULL != avatar) && (avatar != gAgentAvatarp)) + { + result = true; + break; + } + } + xform = xform->getParent(); + } + return result; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaImpl::isInAgentParcel() const +{ + bool result = false; + + std::list< LLVOVolume* >::const_iterator iter = mObjectList.begin(); + std::list< LLVOVolume* >::const_iterator end = mObjectList.end(); + for ( ; iter != end; iter++) + { + LLVOVolume *object = *iter; + if (LLViewerMediaImpl::isObjectInAgentParcel(object)) + { + result = true; + break; + } + } + return result; +} + +LLNotificationPtr LLViewerMediaImpl::getCurrentNotification() const +{ + return mNotification; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// static +bool LLViewerMediaImpl::isObjectInAgentParcel(LLVOVolume *obj) +{ + return (LLViewerParcelMgr::getInstance()->inAgentParcel(obj->getPositionGlobal())); +} diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index 0c9e4e77f..8be43590c 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -34,48 +34,109 @@ #define LLVIEWERMEDIA_H #include "llfocusmgr.h" +#include "lleditmenuhandler.h" + #include "llpanel.h" -#include "llviewermediaeventemitter.h" -#include "llviewerpluginmanager.h" +#include "llpluginclassmediaowner.h" + +#include "llviewermediaobserver.h" + #include "llpluginclassmedia.h" #include "v4color.h" +#include "llnotificationptr.h" + #include "llurl.h" +#include "llviewerpluginmanager.h" + class LLViewerMediaImpl; class LLUUID; -class LLSD; -class LLViewerTexture; +class LLViewerMediaTexture; +class LLMediaEntry; +class LLVOVolume; +class LLMimeDiscoveryResponder; class LLPluginCookieStore; class AIHTTPHeaders; typedef LLPointer viewer_media_t; +/////////////////////////////////////////////////////////////////////////////// +// +class LLViewerMediaEventEmitter +{ +public: + virtual ~LLViewerMediaEventEmitter(); + + bool addObserver( LLViewerMediaObserver* subject ); + bool remObserver( LLViewerMediaObserver* subject ); + virtual void emitEvent(LLPluginClassMedia* self, LLViewerMediaObserver::EMediaEvent event); + +private: + typedef std::list< LLViewerMediaObserver* > observerListType; + observerListType mObservers; +}; + +class LLViewerMediaImpl; class LLViewerMedia { LOG_CLASS(LLViewerMedia); - public: - // Special case early init for just web browser component - // so we can show login screen. See .cpp file for details. JC - - static viewer_media_t newMediaImpl(const std::string& media_url, - const LLUUID& texture_id, - S32 media_width, - S32 media_height, - U8 media_auto_scale, - U8 media_loop, - std::string mime_type = "none/none"); - - static void removeMedia(LLViewerMediaImpl* media); - static LLViewerMediaImpl* getMediaImplFromTextureID(const LLUUID& texture_id); - static std::string getCurrentUserAgent(); - static void updateBrowserUserAgent(); - static bool handleSkinCurrentChanged(const LLSD& /*newvalue*/); - static bool textureHasMedia(const LLUUID& texture_id); - static void setVolume(F32 volume); - - static void updateMedia(); - - static void cleanupClass(); +public: + + // String to get/set media autoplay in gSavedSettings + static const char* AUTO_PLAY_MEDIA_SETTING; + static const char* SHOW_MEDIA_ON_OTHERS_SETTING; + static const char* SHOW_MEDIA_WITHIN_PARCEL_SETTING; + static const char* SHOW_MEDIA_OUTSIDE_PARCEL_SETTING; + + typedef std::list impl_list; + + typedef std::map impl_id_map; + // Special case early init for just web browser component + // so we can show login screen. See .cpp file for details. JC + + static viewer_media_t newMediaImpl(const LLUUID& texture_id, + S32 media_width = 0, + S32 media_height = 0, + U8 media_auto_scale = false, + U8 media_loop = false); + + static viewer_media_t updateMediaImpl(LLMediaEntry* media_entry, const std::string& previous_url, bool update_from_self); + static LLViewerMediaImpl* getMediaImplFromTextureID(const LLUUID& texture_id); + static std::string getCurrentUserAgent(); + static void updateBrowserUserAgent(); + static bool handleSkinCurrentChanged(const LLSD& /*newvalue*/); + static bool textureHasMedia(const LLUUID& texture_id); + static void setVolume(F32 volume); + + // Is any media currently "showing"? Includes Parcel Media. Does not include media in the UI. + static bool isAnyMediaShowing(); + // Set all media enabled or disabled, depending on val. Does not include media in the UI. + static void setAllMediaEnabled(bool val); + + static void updateMedia(void* dummy_arg = NULL); + + static void initClass(); + static void cleanupClass(); + + static F32 getVolume(); + static void muteListChanged(); + static bool isInterestingEnough(const LLVOVolume* object, const F64 &object_interest); + + // Returns the priority-sorted list of all media impls. + static impl_list &getPriorityList(); + + // This is the comparitor used to sort the list. + static bool priorityComparitor(const LLViewerMediaImpl* i1, const LLViewerMediaImpl* i2); + + // These are just helper functions for the convenience of others working with media + static bool hasInWorldMedia(); + static std::string getParcelAudioURL(); + static bool hasParcelMedia(); + static bool hasParcelAudio(); + static bool isParcelMediaPlaying(); + static bool isParcelAudioPlaying(); + + static bool onAuthSubmit(const LLSD& notification, const LLSD& response); // Clear all cookies for all plugins static void clearAllCookies(); @@ -86,6 +147,9 @@ class LLViewerMedia // Set the "cookies enabled" flag for all loaded plugins static void setCookiesEnabled(bool enabled); + // Set the proxy config for all loaded plugins + static void setProxyConfig(bool enable, const std::string &host, int port); + static LLPluginCookieStore *getCookieStore(); static void loadCookieFile(); static void saveCookieFile(); @@ -95,15 +159,25 @@ class LLViewerMedia static void openIDSetup(const std::string &openid_url, const std::string &openid_token); static void openIDCookieResponse(const std::string &cookie); + + static void proxyWindowOpened(const std::string &target, const std::string &uuid); + static void proxyWindowClosed(const std::string &uuid); + + static void createSpareBrowserMediaSource(); + static LLPluginClassMedia* getSpareBrowserMediaSource(); + + static void setOnlyAudibleMediaTextureID(const LLUUID& texture_id); static AIHTTPHeaders getHeaders(); private: static void setOpenIDCookie(); - + static void onTeleportFinished(); + static LLPluginCookieStore *sCookieStore; static LLURL sOpenIDURL; static std::string sOpenIDCookie; + static LLPluginClassMedia* sSpareBrowserMediaSource; }; // Implementation functions not exported into header file @@ -112,32 +186,44 @@ class LLViewerMediaImpl { LOG_CLASS(LLViewerMediaImpl); public: - + friend class LLViewerMedia; - - LLViewerMediaImpl(const std::string& media_url, + friend class LLMimeDiscoveryResponder; + + LLViewerMediaImpl( const LLUUID& texture_id, S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop, - const std::string& mime_type); + U8 media_loop); ~LLViewerMediaImpl(); + + // Override inherited version from LLViewerMediaEventEmitter + virtual void emitEvent(LLPluginClassMedia* self, LLViewerMediaObserver::EMediaEvent event); + void createMediaSource(); void destroyMediaSource(); void setMediaType(const std::string& media_type); bool initializeMedia(const std::string& mime_type); bool initializePlugin(const std::string& media_type); + void loadURI(); LLPluginClassMedia* getMediaPlugin() const { return (LLPluginClassMedia*)mPluginBase; } void setSize(int width, int height); + void showNotification(LLNotificationPtr notify); + void hideNotification(); + void play(); void stop(); void pause(); void start(); void seek(F32 time); + void skipBack(F32 step_scale); + void skipForward(F32 step_scale); void setVolume(F32 volume); + void updateVolume(); + F32 getVolume(); void focus(bool focus); // True if the impl has user focus. bool hasFocus() const; @@ -150,37 +236,68 @@ public: void mouseDoubleClick(S32 x,S32 y, MASK mask, S32 button = 0); void scrollWheel(S32 x, S32 y, MASK mask); void mouseCapture(); - + + void navigateBack(); + void navigateForward(); + void navigateReload(); void navigateHome(); - void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false); + void unload(); + void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false, bool server_request = false); + void navigateInternal(); void navigateStop(); bool handleKeyHere(KEY key, MASK mask); bool handleUnicodeCharHere(llwchar uni_char); bool canNavigateForward(); bool canNavigateBack(); - std::string getMediaURL() { return mMediaURL; } + std::string getMediaURL() const { return mMediaURL; } + std::string getCurrentMediaURL(); std::string getHomeURL() { return mHomeURL; } - void setHomeURL(const std::string& home_url) { mHomeURL = home_url; } + std::string getMediaEntryURL() { return mMediaEntryURL; } + void setHomeURL(const std::string& home_url, const std::string& mime_type = LLStringUtil::null) { mHomeURL = home_url; mHomeMimeType = mime_type;}; void clearCache(); + void setPageZoomFactor( double factor ); std::string getMimeType() { return mMimeType; } - void getTextureSize(S32 *texture_width, S32 *texture_height); void scaleMouse(S32 *mouse_x, S32 *mouse_y); void scaleTextureCoords(const LLVector2& texture_coords, S32 *x, S32 *y); - void updateMovieImage(const LLUUID& image_id, BOOL active); void update(); void updateImagesMediaStreams(); - LLUUID getMediaTextureID(); + LLUUID getMediaTextureID() const; - void suspendUpdates(bool suspend) { mSuspendUpdates = suspend; }; + void suspendUpdates(bool suspend) { mSuspendUpdates = suspend; } void setVisible(bool visible); + bool getVisible() const { return mVisible; } + bool isVisible() const { return mVisible; } + bool isMediaTimeBased(); bool isMediaPlaying(); bool isMediaPaused(); - bool hasMedia(); + bool hasMedia() const; + bool isMediaFailed() const { return mMediaSourceFailed; } + void setMediaFailed(bool val) { mMediaSourceFailed = val; } + void resetPreviousMediaState(); + + void setDisabled(bool disabled, bool forcePlayOnEnable = false); + bool isMediaDisabled() const { return mIsDisabled; }; + + void setInNearbyMediaList(bool in_list) { mInNearbyMediaList = in_list; } + bool getInNearbyMediaList() { return mInNearbyMediaList; } + + // returns true if this instance should not be loaded (disabled, muted object, crashed, etc.) + bool isForcedUnloaded() const; + + // returns true if this instance could be playable based on autoplay setting, current load state, etc. + bool isPlayable() const; + + void setIsParcelMedia(bool is_parcel_media) { mIsParcelMedia = is_parcel_media; } + bool isParcelMedia() const { return mIsParcelMedia; } + ECursorType getLastSetCursor() { return mLastSetCursor; } + + void setTarget(const std::string& target) { mTarget = target; } + // utility function to create a ready-to-use media instance from a desired media type. - static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height); + static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height, const std::string target = LLStringUtil::null); // Internally set our desired browser user agent string, including // the Second Life version and skin name. Used because we can @@ -208,12 +325,13 @@ public: /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask) {return FALSE; }; /*virtual*/ const std::string& getName() const; /*virtual*/ BOOL isView() const { return FALSE; }; + /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {}; /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {}; /*virtual*/ BOOL hasMouseCapture() { return gFocusMgr.getMouseCapture() == this; }; // Inherited from LLPluginClassMediaOwner - /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent); + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* plugin, LLPluginClassMediaOwner::EMediaEvent); /*virtual*/ void handleCookieSet(LLPluginClassMedia* self, const std::string &cookie); // LLEditMenuHandler overrides @@ -225,32 +343,152 @@ public: /*virtual*/ void paste(); /*virtual*/ BOOL canPaste() const; + + void addObject(LLVOVolume* obj) ; + void removeObject(LLVOVolume* obj) ; + const std::list< LLVOVolume* >* getObjectList() const ; + LLVOVolume *getSomeObject(); + void setUpdated(BOOL updated) ; + BOOL isUpdated() ; + + // updates the javascript object in the embedded browser with viewer values + void updateJavascriptObject(); + + // Updates the "interest" value in this object + void calculateInterest(); + F64 getInterest() const { return mInterest; }; + F64 getApproximateTextureInterest(); + S32 getProximity() const { return mProximity; }; + F64 getProximityDistance() const { return mProximityDistance; }; + + // Mark this object as being used in a UI panel instead of on a prim + // This will be used as part of the interest sorting algorithm. + void setUsedInUI(bool used_in_ui); + bool getUsedInUI() const { return mUsedInUI; }; - bool mNeedsNewTexture; void setBackgroundColor(LLColor4 color); + + F64 getCPUUsage() const; + + typedef enum + { + PRIORITY_UNLOADED, // media plugin isn't even loaded. + PRIORITY_HIDDEN, // media is not being displayed or is out of view, don't need to do graphic updates, but may still update audio, playhead, etc. + PRIORITY_SLIDESHOW, // media is in the far distance, updates very infrequently + PRIORITY_LOW, // media is in the distance, may be rendered at reduced size + PRIORITY_NORMAL, // normal (default) priority + PRIORITY_HIGH // media has user focus and/or is taking up most of the screen + }EPriority; + + void setPriority(EPriority priority); + EPriority getPriority() { return mPriority; }; + + void setLowPrioritySizeLimit(int size); + + void setTextureID(LLUUID id = LLUUID::null); + + bool isTrustedBrowser() { return mTrustedBrowser; } + void setTrustedBrowser(bool trusted) { mTrustedBrowser = trusted; } + + typedef enum + { + MEDIANAVSTATE_NONE, // State is outside what we need to track for navigation. + MEDIANAVSTATE_BEGUN, // a MEDIA_EVENT_NAVIGATE_BEGIN has been received which was not server-directed + MEDIANAVSTATE_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a non-server-directed BEGIN + MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS, // Same as above, but the new URL is identical to the previously navigated URL. + MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED, // we received a NAVIGATE_COMPLETE event before the first LOCATION_CHANGED + MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS,// Same as above, but the new URL is identical to the previously navigated URL. + MEDIANAVSTATE_SERVER_SENT, // server-directed nav has been requested, but MEDIA_EVENT_NAVIGATE_BEGIN hasn't been received yet + MEDIANAVSTATE_SERVER_BEGUN, // MEDIA_EVENT_NAVIGATE_BEGIN has been received which was server-directed + MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED, // first LOCATION_CHANGED event after a server-directed BEGIN + MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED // we received a NAVIGATE_COMPLETE event before the first LOCATION_CHANGED + + }EMediaNavState; + + // Returns the current nav state of the media. + // note that this will be updated BEFORE listeners and objects receive media messages + EMediaNavState getNavState() { return mMediaNavState; } + void setNavState(EMediaNavState state); + + void setNavigateSuspended(bool suspend); + bool isNavigateSuspended() { return mNavigateSuspended; }; + + void cancelMimeTypeProbe(); + + // Is this media attached to an avatar *not* self + bool isAttachedToAnotherAvatar() const; + + // Is this media in the agent's parcel? + bool isInAgentParcel() const; + + // get currently active notification associated with this media instance + LLNotificationPtr getCurrentNotification() const; + +private: + bool isAutoPlayable() const; + bool shouldShowBasedOnClass() const; + static bool isObjectAttachedToAnotherAvatar(LLVOVolume *obj); + static bool isObjectInAgentParcel(LLVOVolume *obj); + private: // a single media url with some data and an impl. + F64 mZoomFactor; LLUUID mTextureId; bool mMovieImageHasMips; - std::string mMediaURL; + std::string mMediaURL; // The last media url set with NavigateTo std::string mHomeURL; + std::string mHomeMimeType; // forced mime type for home url std::string mMimeType; + std::string mCurrentMediaURL; // The most current media url from the plugin (via the "location changed" or "navigate complete" events). + std::string mCurrentMimeType; // The MIME type that caused the currently loaded plugin to be loaded. S32 mLastMouseX; // save the last mouse coord we get, so when we lose capture we can simulate a mouseup at that point. S32 mLastMouseY; S32 mMediaWidth; S32 mMediaHeight; bool mMediaAutoScale; bool mMediaLoop; + bool mNeedsNewTexture; S32 mTextureUsedWidth; S32 mTextureUsedHeight; bool mSuspendUpdates; bool mVisible; + ECursorType mLastSetCursor; + EMediaNavState mMediaNavState; + F64 mInterest; + bool mUsedInUI; bool mHasFocus; + EPriority mPriority; + bool mNavigateRediscoverType; + bool mNavigateServerRequest; + bool mMediaSourceFailed; + F32 mRequestedVolume; + bool mIsMuted; + bool mNeedsMuteCheck; + int mPreviousMediaState; + F64 mPreviousMediaTime; + bool mIsDisabled; + bool mIsParcelMedia; + S32 mProximity; + F64 mProximityDistance; + F64 mProximityCamera; + LLMimeDiscoveryResponder *mMimeTypeProbe; + bool mMediaAutoPlay; + std::string mMediaEntryURL; + bool mInNearbyMediaList; // used by LLPanelNearbyMedia::refreshList() for performance reasons bool mClearCache; LLColor4 mBackgroundColor; + bool mNavigateSuspended; + bool mNavigateSuspendedDeferred; + bool mTrustedBrowser; + std::string mTarget; + LLNotificationPtr mNotification; private: - /*LLViewerMediaTexture*/LLViewerTexture *updatePlaceholderImage(); + BOOL mIsUpdated ; + std::list< LLVOVolume* > mObjectList ; + +private: + LLViewerMediaTexture *updatePlaceholderImage(); }; #endif // LLVIEWERMEDIA_H diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp index 6adff36c6..de7bfc767 100644 --- a/indra/newview/llviewermedia_streamingaudio.cpp +++ b/indra/newview/llviewermedia_streamingaudio.cpp @@ -35,6 +35,7 @@ #include "linden_common.h" #include "llpluginclassmedia.h" +#include "llpluginclassmediaowner.h" #include "llviewermedia.h" #include "llviewermedia_streamingaudio.h" @@ -63,18 +64,18 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url) if (!mMediaPlugin) // lazy-init the underlying media plugin { mMediaPlugin = initializeMedia("audio/mpeg"); // assumes that whatever media implementation supports mp3 also supports vorbis. - llinfos << "mMediaPlugin is now " << mMediaPlugin << llendl; + llinfos << "streaming audio mMediaPlugin is now " << mMediaPlugin << llendl; } if(!mMediaPlugin) return; - + if (!url.empty()) { llinfos << "Starting internet stream: " << url << llendl; mURL = url; mMediaPlugin->loadURI ( url ); mMediaPlugin->start(); - llinfos << "Playing....." << llendl; + llinfos << "Playing stream..." << llendl; } else { llinfos << "setting stream to NULL"<< llendl; mURL.clear(); @@ -84,11 +85,9 @@ void LLStreamingAudio_MediaPlugins::start(const std::string& url) void LLStreamingAudio_MediaPlugins::stop() { - llinfos << "entered LLStreamingAudio_MediaPlugins::stop()" << llendl; - + llinfos << "Stopping internet stream." << llendl; if(mMediaPlugin) { - llinfos << "Stopping internet stream: " << mURL << llendl; mMediaPlugin->stop(); } @@ -102,10 +101,12 @@ void LLStreamingAudio_MediaPlugins::pause(int pause) if(pause) { + llinfos << "Pausing internet stream." << llendl; mMediaPlugin->pause(); } else { + llinfos << "Unpausing internet stream." << llendl; mMediaPlugin->start(); } } @@ -119,20 +120,21 @@ void LLStreamingAudio_MediaPlugins::update() int LLStreamingAudio_MediaPlugins::isPlaying() { if (!mMediaPlugin) - return 0; + return 0; // stopped - // *TODO: can probably do better than this - if (mMediaPlugin->isPluginRunning()) - { - return 1; // Active and playing - } + LLPluginClassMediaOwner::EMediaStatus status = + mMediaPlugin->getStatus(); - if (mMediaPlugin->isPluginExited()) + switch (status) { + case LLPluginClassMediaOwner::MEDIA_LOADING: // but not MEDIA_LOADED + case LLPluginClassMediaOwner::MEDIA_PLAYING: + return 1; // Active and playing + case LLPluginClassMediaOwner::MEDIA_PAUSED: + return 2; // paused + default: return 0; // stopped } - - return 2; // paused } void LLStreamingAudio_MediaPlugins::setGain(F32 vol) diff --git a/indra/newview/llviewermediaeventemitter.cpp b/indra/newview/llviewermediaeventemitter.cpp deleted file mode 100644 index 39e0b2b52..000000000 --- a/indra/newview/llviewermediaeventemitter.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/** - * @file llviewermediaeventemitter.cpp - * @brief Implementation of LLViewerMediaEventEmitter - * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2010, 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 "llviewermediaeventemitter.h" -#include "llviewermediaobserver.h" -#include - -LLViewerMediaEventEmitter::~LLViewerMediaEventEmitter() -{ - observerListType::iterator iter = mObservers.begin(); - - while (iter != mObservers.end()) - { - LLViewerMediaObserver* self = *iter; - ++iter; - remObserver(self); - } -} - -bool LLViewerMediaEventEmitter::addObserver(LLViewerMediaObserver* observer) -{ - if (!observer) - return false; - - if (std::find(mObservers.begin(), mObservers.end(), observer) != mObservers.end()) - return false; - - mObservers.push_back(observer); - observer->mEmitters.push_back(this); - - return true; -} - -bool LLViewerMediaEventEmitter::remObserver(LLViewerMediaObserver* observer) -{ - if (!observer) - return false; - - mObservers.remove(observer); - observer->mEmitters.remove(this); - - return true; -} - -void LLViewerMediaEventEmitter::emitEvent(LLPluginClassMedia* media, LLPluginClassMediaOwner::EMediaEvent event) -{ - observerListType::iterator iter = mObservers.begin(); - - while (iter != mObservers.end()) - { - LLViewerMediaObserver* self = *iter; - ++iter; - self->handleMediaEvent(media, event); - } -} diff --git a/indra/newview/llviewermediaeventemitter.h b/indra/newview/llviewermediaeventemitter.h deleted file mode 100644 index 17453a9e1..000000000 --- a/indra/newview/llviewermediaeventemitter.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file llviewermediaeventemitter.h - * @brief Helper class - * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2010, 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$ - */ - -#ifndef LLVIEWERMEDIAEVENTEMITTER_H -#define LLVIEWERMEDIAEVENTEMITTER_H - -#include "llpluginclassmediaowner.h" -#include - -class LLViewerMediaObserver; -class LLPluginClassMedia; - -class LLViewerMediaEventEmitter -{ -public: - virtual ~LLViewerMediaEventEmitter(); - - bool addObserver(LLViewerMediaObserver* subject); - bool remObserver(LLViewerMediaObserver* subject); - void emitEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event); - -private: - typedef std::list< LLViewerMediaObserver*> observerListType; - observerListType mObservers; -}; - -#endif // LLVIEWERMEDIAEVENTEMITTER_H diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp index 9ad2c8f0b..701d4981b 100644 --- a/indra/newview/llviewermediafocus.cpp +++ b/indra/newview/llviewermediafocus.cpp @@ -2,31 +2,25 @@ * @file llviewermediafocus.cpp * @brief Governs focus on Media prims * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -36,7 +30,7 @@ //LLViewerMediaFocus #include "llviewerobjectlist.h" -#include "llpanelmediahud.h" +#include "llpanelprimmediacontrols.h" #include "llpluginclassmedia.h" #include "llagent.h" #include "llagentcamera.h" @@ -49,12 +43,18 @@ #include "llparcel.h" #include "llviewerparcelmgr.h" #include "llweb.h" +#include "llmediaentry.h" +#include "llkeyboard.h" +#include "lltoolmgr.h" +#include "llvovolume.h" + // // LLViewerMediaFocus // LLViewerMediaFocus::LLViewerMediaFocus() -: mMouseOverFlag(false) +: mFocusedObjectFace(0), + mHoverObjectFace(0) { } @@ -64,83 +64,136 @@ LLViewerMediaFocus::~LLViewerMediaFocus() // Clean up in cleanupClass() instead. } -void LLViewerMediaFocus::cleanupClass() -{ - LLViewerMediaFocus *self = LLViewerMediaFocus::getInstance(); +void LLViewerMediaFocus::setFocusFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if(self) + LLViewerMediaImpl *old_media_impl = getFocusedMediaImpl(); + if(old_media_impl) { - // mMediaHUD will have been deleted by this point -- don't try to delete it. - - /* Richard says: - all widgets are supposed to be destroyed at the same time - you shouldn't hold on to pointer to them outside of ui code - you can use the LLHandle approach - if you want to be type safe, you'll need to add a LLRootHandle to whatever derived class you are pointing to - look at llview::gethandle - its our version of a weak pointer - */ - if(self->mMediaHUD.get()) - { - self->mMediaHUD.get()->setMediaImpl(NULL); - } - self->mMediaImpl = NULL; + old_media_impl->focus(false); } -} + // Always clear the current selection. If we're setting focus on a face, we'll reselect the correct object below. + LLSelectMgr::getInstance()->deselectAll(); + mSelection = NULL; - -void LLViewerMediaFocus::setFocusFace( BOOL b, LLPointer objectp, S32 face, viewer_media_t media_impl ) -{ - LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - if (b && media_impl.notNull()) + if (media_impl.notNull() && objectp.notNull()) { - mMediaImpl = media_impl; - LLSelectMgr::getInstance()->deselectAll(); - LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + bool face_auto_zoom = false; - mFocus = LLSelectMgr::getInstance()->getSelection(); - if(mMediaHUD.get() && ! parcel->getMediaPreventCameraZoom()) + mFocusedImplID = media_impl->getMediaTextureID(); + mFocusedObjectID = objectp->getID(); + mFocusedObjectFace = face; + mFocusedObjectNormal = pick_normal; + + // Set the selection in the selection manager so we can draw the focus ring. + mSelection = LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + + // Focusing on a media face clears its disable flag. + media_impl->setDisabled(false); + + LLTextureEntry* tep = objectp->getTE(face); + if(tep->hasMedia()) { - mMediaHUD.get()->resetZoomLevel(); - mMediaHUD.get()->nextZoomLevel(); + LLMediaEntry* mep = tep->getMediaData(); + face_auto_zoom = mep->getAutoZoom(); + if(!media_impl->hasMedia()) + { + std::string url = mep->getCurrentURL().empty() ? mep->getHomeURL() : mep->getCurrentURL(); + media_impl->navigateTo(url, "", true); + } } - if (!mFocus->isEmpty()) + else { - gFocusMgr.setKeyboardFocus(this); + // This should never happen. + llwarns << "Can't find media entry for focused face" << llendl; } - mObjectID = objectp->getID(); - // LLViewerMedia::addObserver(this, mObjectID); + media_impl->focus(true); + gFocusMgr.setKeyboardFocus(this); + LLViewerMediaImpl* impl = getFocusedMediaImpl(); + if (impl) + { + LLEditMenuHandler::gEditMenuHandler = impl; + } + + // We must do this before processing the media HUD zoom, or it may zoom to the wrong face. + update(); + if(mMediaControls.get()) + { + if(face_auto_zoom && ! parcel->getMediaPreventCameraZoom()) + { + // Zoom in on this face + mMediaControls.get()->resetZoomLevel(false); + mMediaControls.get()->nextZoomLevel(); + } + else + { + // Reset the controls' zoom level without moving the camera. + // This fixes the case where clicking focus between two non-autozoom faces doesn't change the zoom-out button back to a zoom-in button. + mMediaControls.get()->resetZoomLevel(false); + } + } } else { - gFocusMgr.setKeyboardFocus(NULL); - if(! parcel->getMediaPreventCameraZoom()) + if(hasFocus()) { - if (!mFocus->isEmpty()) - { - gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); - } - } - mFocus = NULL; - // LLViewerMedia::remObserver(this, mObjectID); - - // Null out the media hud media pointer - if(mMediaHUD.get()) - { - mMediaHUD.get()->setMediaImpl(NULL); + gFocusMgr.setKeyboardFocus(NULL); } - // and null out the media impl - mMediaImpl = NULL; - } - if(mMediaHUD.get()) - { - mMediaHUD.get()->setMediaFocus(b); + LLViewerMediaImpl* impl = getFocusedMediaImpl(); + if (LLEditMenuHandler::gEditMenuHandler == impl) + { + LLEditMenuHandler::gEditMenuHandler = NULL; + } + + + mFocusedImplID = LLUUID::null; + if (objectp.notNull()) + { + // Still record the focused object...it may mean we need to load media data. + // This will aid us in determining this object is "important enough" + mFocusedObjectID = objectp->getID(); + mFocusedObjectFace = face; + } + else { + mFocusedObjectID = LLUUID::null; + mFocusedObjectFace = 0; + } } } + +void LLViewerMediaFocus::clearFocus() +{ + setFocusFace(NULL, 0, NULL); +} + +void LLViewerMediaFocus::setHoverFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal) +{ + if (media_impl.notNull()) + { + mHoverImplID = media_impl->getMediaTextureID(); + mHoverObjectID = objectp->getID(); + mHoverObjectFace = face; + mHoverObjectNormal = pick_normal; + } + else + { + mHoverObjectID = LLUUID::null; + mHoverObjectFace = 0; + mHoverImplID = LLUUID::null; + } +} + +void LLViewerMediaFocus::clearHover() +{ + setHoverFace(NULL, 0, NULL); +} + + bool LLViewerMediaFocus::getFocus() { if (gFocusMgr.getKeyboardFocus() == this) @@ -150,22 +203,15 @@ bool LLViewerMediaFocus::getFocus() return false; } -// This function selects an ideal viewing distance given a selection bounding box, normal, and padding value -void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) +// This function selects an ideal viewing distance based on the focused object, pick normal, and padding value +void LLViewerMediaFocus::setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only) { - LLPickInfo& pick = LLToolPie::getInstance()->getPick(); - - if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) - { - pick = mPickInfo; - setFocusFace(true, pick.getObject(), pick.mObjectFace, mMediaImpl); - } - - if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + if (object) { gAgentCamera.setFocusOnAvatar(FALSE, ANIMATE); - LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + LLBBox bbox = object->getBoundingBoxAgent(); + LLVector3d center = gAgent.getPosGlobalFromAgent(bbox.getCenterAgent()); F32 height; F32 width; F32 depth; @@ -173,8 +219,10 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) F32 distance; // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. - F32 aspect_ratio = getBBoxAspectRatio(selection_bbox, pick.mNormal, &height, &width, &depth); + F32 aspect_ratio = getBBoxAspectRatio(bbox, normal, &height, &width, &depth); F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); + + lldebugs << "normal = " << normal << ", aspect_ratio = " << aspect_ratio << ", camera_aspect = " << camera_aspect << llendl; // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for // a screen in a landscape aspect ratio), however there is an edge case where the aspect ratio of the object is @@ -191,88 +239,137 @@ void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect()); distance = width * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); + + lldebugs << "using width (" << width << "), angle_of_view = " << angle_of_view << ", distance = " << distance << llendl; } else { angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView()); distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); + + lldebugs << "using height (" << height << "), angle_of_view = " << angle_of_view << ", distance = " << distance << llendl; } distance += depth * 0.5; // Finally animate the camera to this new position and focal point - gAgentCamera.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(pick.mNormal * distance), - LLSelectMgr::getInstance()->getSelectionCenterGlobal(), LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); + LLVector3d camera_pos, target_pos; + // The target lookat position is the center of the selection (in global coords) + target_pos = center; + // Target look-from (camera) position is "distance" away from the target along the normal + LLVector3d pickNormal = LLVector3d(normal); + pickNormal.normalize(); + camera_pos = target_pos + pickNormal * distance; + if (pickNormal == LLVector3d::z_axis || pickNormal == LLVector3d::z_axis_neg) + { + // If the normal points directly up, the camera will "flip" around. + // We try to avoid this by adjusting the target camera position a + // smidge towards current camera position + // *NOTE: this solution is not perfect. All it attempts to solve is the + // "looking down" problem where the camera flips around when it animates + // to that position. You still are not guaranteed to be looking at the + // media in the correct orientation. What this solution does is it will + // put the camera into position keeping as best it can the current + // orientation with respect to the face. In other words, if before zoom + // the media appears "upside down" from the camera, after zooming it will + // still be upside down, but at least it will not flip. + LLVector3d cur_camera_pos = LLVector3d(gAgentCamera.getCameraPositionGlobal()); + LLVector3d delta = (cur_camera_pos - camera_pos); + F64 len = delta.length(); + delta.normalize(); + // Move 1% of the distance towards original camera location + camera_pos += 0.01 * len * delta; + } + + // If we are not allowing zooming out and the old camera position is closer to + // the center then the new intended camera position, don't move camera and return + if (zoom_in_only && + (dist_vec_squared(gAgentCamera.getCameraPositionGlobal(), target_pos) < dist_vec_squared(camera_pos, target_pos))) + { + return; + } + + gAgentCamera.setCameraPosAndFocusGlobal(camera_pos, target_pos, object->getID() ); + + } + else + { + // If we have no object, focus back on the avatar. + gAgentCamera.setFocusOnAvatar(TRUE, ANIMATE); } } void LLViewerMediaFocus::onFocusReceived() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(true); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->focus(true); LLFocusableElement::onFocusReceived(); } void LLViewerMediaFocus::onFocusLost() { - if(mMediaImpl.notNull()) - mMediaImpl->focus(false); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->focus(false); + gViewerWindow->focusClient(); - mFocus = NULL; LLFocusableElement::onFocusLost(); } -void LLViewerMediaFocus::setMouseOverFlag(bool b, viewer_media_t media_impl) -{ - if (b && media_impl.notNull()) - { - if(! mMediaHUD.get()) - { - LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(mMediaImpl); - mMediaHUD = media_hud->getHandle(); - gHUDView->addChild(media_hud); - } - mMediaHUD.get()->setMediaImpl(media_impl); - mMediaImpl = media_impl; - } - mMouseOverFlag = b; -} -LLUUID LLViewerMediaFocus::getSelectedUUID() -{ - LLViewerObject* object = mFocus->getFirstObject(); - return object ? object->getID() : LLUUID::null; -} -#if 0 // Must re-implement when the new media api event system is ready -void LLViewerMediaFocus::onNavigateComplete( const EventType& event_in ) -{ - if (hasFocus() && mLastURL != event_in.getStringValue()) - { - LLViewerMedia::focus(true, mObjectID); - // spoof mouse event to reassert focus - LLViewerMedia::mouseDown(1,1, mObjectID); - LLViewerMedia::mouseUp(1,1, mObjectID); - } - mLastURL = event_in.getStringValue(); -} -#endif + BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) { - if(mMediaImpl.notNull()) - mMediaImpl->handleKeyHere(key, mask); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + { + media_impl->handleKeyHere(key, mask); + + if (KEY_ESCAPE == key) + { + // Reset camera zoom in this case. + if(mFocusedImplID.notNull()) + { + if(mMediaControls.get()) + { + mMediaControls.get()->resetZoomLevel(true); + } + } + + clearFocus(); + } + + /*if ( KEY_F1 == key && LLUI::sHelpImpl && mMediaControls.get()) + { + std::string help_topic; + if (mMediaControls.get()->findHelpTopic(help_topic)) + { + LLUI::sHelpImpl->showTopic(help_topic); + } + }*/ + } + return true; } BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) { - if(mMediaImpl.notNull()) - mMediaImpl->handleUnicodeCharHere(uni_char); + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl) + media_impl->handleUnicodeCharHere(uni_char); return true; } BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) { BOOL retval = FALSE; - if(mFocus.notNull() && mMediaImpl.notNull() && mMediaImpl->hasMedia()) + LLViewerMediaImpl* media_impl = getFocusedMediaImpl(); + if(media_impl && media_impl->hasMedia()) { - mMediaImpl->getMediaPlugin()->scrollEvent(x, y, clicks); + // the scrollEvent() API's x and y are not the same as handleScrollWheel's x and y. + // The latter is the position of the mouse at the time of the event + // The former is the 'scroll amount' in x and y, respectively. + // All we have for 'scroll amount' here is 'clicks'. + // We're also not passed the keyboard modifier mask, but we can get that from gKeyboard. + media_impl->getMediaPlugin()->scrollEvent(0, clicks, gKeyboard->currentMask(TRUE)); retval = TRUE; } return retval; @@ -280,19 +377,68 @@ BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) void LLViewerMediaFocus::update() { - if (mMediaHUD.get()) + if(mFocusedImplID.notNull()) { - if(mFocus.notNull() || mMouseOverFlag || mMediaHUD.get()->isMouseOver()) + // We have a focused impl/face. + if(!getFocus()) { - // mMediaHUD.get()->setVisible(true); - mMediaHUD.get()->updateShape(); + // We've lost keyboard focus -- check to see whether the media controls have it + if(mMediaControls.get() && mMediaControls.get()->hasFocus()) + { + // the media controls have focus -- don't clear. + } + else + { + // Someone else has focus -- back off. + clearFocus(); + } } - else + else if(LLToolMgr::getInstance()->inBuildMode()) { - mMediaHUD.get()->setVisible(false); + // Build tools are selected -- clear focus. + clearFocus(); } } + + + LLViewerMediaImpl *media_impl = getFocusedMediaImpl(); + LLViewerObject *viewer_object = getFocusedObject(); + S32 face = mFocusedObjectFace; + LLVector3 normal = mFocusedObjectNormal; + + if(!media_impl || !viewer_object) + { + media_impl = getHoverMediaImpl(); + viewer_object = getHoverObject(); + face = mHoverObjectFace; + normal = mHoverObjectNormal; + } + + if(media_impl && viewer_object) + { + // We have an object and impl to point at. + + // Make sure the media HUD object exists. + if(! mMediaControls.get()) + { + LLPanelPrimMediaControls* media_controls = new LLPanelPrimMediaControls(); + mMediaControls = media_controls->getHandle(); + gHUDView->addChild(media_controls); + } + mMediaControls.get()->setMediaFace(viewer_object, face, media_impl, normal); + } + else + { + // The media HUD is no longer needed. + if(mMediaControls.get()) + { + mMediaControls.get()->setMediaFace(NULL, 0, NULL); + } + + } } + + // This function calculates the aspect ratio and the world aligned components of a selection bounding box. F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth) { @@ -305,40 +451,38 @@ F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& LLVector3 bbox_max = bbox.getExtentLocal(); F32 dot1 = 0.f; F32 dot2 = 0.f; + + lldebugs << "bounding box local size = " << bbox_max << ", local_normal = " << local_normal << llendl; // The largest component of the localized normal vector is the depth component // meaning that the other two are the legs of the rectangle. local_normal.abs(); - if(local_normal.mV[VX] > local_normal.mV[VY]) + + // Using temporary variables for these makes the logic a bit more readable. + bool XgtY = (local_normal.mV[VX] > local_normal.mV[VY]); + bool XgtZ = (local_normal.mV[VX] > local_normal.mV[VZ]); + bool YgtZ = (local_normal.mV[VY] > local_normal.mV[VZ]); + + if(XgtY && XgtZ) { - if(local_normal.mV[VX] > local_normal.mV[VZ]) - { - // Use the y and z comps - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VX]; - } - else - { - // Use the x and y comps - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VZ]; - } + lldebugs << "x component of normal is longest, using y and z" << llendl; + comp1.mV[VY] = bbox_max.mV[VY]; + comp2.mV[VZ] = bbox_max.mV[VZ]; + *depth = bbox_max.mV[VX]; } - else if(local_normal.mV[VY] > local_normal.mV[VZ]) + else if(!XgtY && YgtZ) { - // Use the x and z comps + lldebugs << "y component of normal is longest, using x and z" << llendl; comp1.mV[VX] = bbox_max.mV[VX]; comp2.mV[VZ] = bbox_max.mV[VZ]; *depth = bbox_max.mV[VY]; } else { - // Use the x and y comps - comp1.mV[VY] = bbox_max.mV[VY]; - comp2.mV[VZ] = bbox_max.mV[VZ]; - *depth = bbox_max.mV[VX]; + lldebugs << "z component of normal is longest, using x and y" << llendl; + comp1.mV[VX] = bbox_max.mV[VX]; + comp2.mV[VY] = bbox_max.mV[VY]; + *depth = bbox_max.mV[VZ]; } // The height is the vector closest to vertical in the bbox coordinate space (highest dot product value) @@ -348,13 +492,114 @@ F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& { *height = comp1.length(); *width = comp2.length(); + + lldebugs << "comp1 = " << comp1 << ", height = " << *height << llendl; + lldebugs << "comp2 = " << comp2 << ", width = " << *width << llendl; } else { *height = comp2.length(); *width = comp1.length(); + + lldebugs << "comp2 = " << comp2 << ", height = " << *height << llendl; + lldebugs << "comp1 = " << comp1 << ", width = " << *width << llendl; } + + lldebugs << "returning " << (*width / *height) << llendl; // Return the aspect ratio. return *width / *height; } + +bool LLViewerMediaFocus::isFocusedOnFace(LLPointer objectp, S32 face) +{ + return objectp->getID() == mFocusedObjectID && face == mFocusedObjectFace; +} + +bool LLViewerMediaFocus::isHoveringOverFace(LLPointer objectp, S32 face) +{ + return objectp->getID() == mHoverObjectID && face == mHoverObjectFace; +} + + +LLViewerMediaImpl* LLViewerMediaFocus::getFocusedMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mFocusedImplID); +} + +LLViewerObject* LLViewerMediaFocus::getFocusedObject() +{ + return gObjectList.findObject(mFocusedObjectID); +} + +LLViewerMediaImpl* LLViewerMediaFocus::getHoverMediaImpl() +{ + return LLViewerMedia::getMediaImplFromTextureID(mHoverImplID); +} + +LLViewerObject* LLViewerMediaFocus::getHoverObject() +{ + return gObjectList.findObject(mHoverObjectID); +} + +void LLViewerMediaFocus::focusZoomOnMedia(LLUUID media_id) +{ + LLViewerMediaImpl* impl = LLViewerMedia::getMediaImplFromTextureID(media_id); + + if(impl) + { + // Get the first object from the media impl's object list. This is completely arbitrary, but should suffice. + LLVOVolume *obj = impl->getSomeObject(); + if(obj) + { + // This media is attached to at least one object. Figure out which face it's on. + S32 face = obj->getFaceIndexWithMediaImpl(impl, -1); + + // We don't have a proper pick normal here, and finding a face's real normal is... complicated. + LLVector3 normal = obj->getApproximateFaceNormal(face); + if(normal.isNull()) + { + // If that didn't work, use the inverse of the camera "look at" axis, which should keep the camera pointed in the same direction. +// llinfos << "approximate face normal invalid, using camera direction." << llendl; + normal = LLViewerCamera::getInstance()->getAtAxis(); + normal *= (F32)-1.0f; + } + + // Attempt to focus/zoom on that face. + setFocusFace(obj, face, impl, normal); + + if(mMediaControls.get()) + { + mMediaControls.get()->resetZoomLevel(); + mMediaControls.get()->nextZoomLevel(); + } + } + } +} + +void LLViewerMediaFocus::unZoom() +{ + if(mMediaControls.get()) + { + mMediaControls.get()->resetZoomLevel(); + } +} + +bool LLViewerMediaFocus::isZoomed() const +{ + return (mMediaControls.get() && mMediaControls.get()->getZoomLevel() != LLPanelPrimMediaControls::ZOOM_NONE); +} + +LLUUID LLViewerMediaFocus::getControlsMediaID() +{ + if(getFocusedMediaImpl()) + { + return mFocusedImplID; + } + else if(getHoverMediaImpl()) + { + return mHoverImplID; + } + + return LLUUID::null; +} diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h index ed9597e61..f03dd8751 100644 --- a/indra/newview/llviewermediafocus.h +++ b/indra/newview/llviewermediafocus.h @@ -2,31 +2,25 @@ * @file llpanelmsgs.h * @brief Message popup preferences panel * - * $LicenseInfo:firstyear=2003&license=viewergpl$ - * - * Copyright (c) 2003-2009, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * 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 + * Copyright (C) 2010, Linden Research, Inc. * - * 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 + * 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. * - * 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. + * 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. * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * 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$ */ @@ -41,7 +35,7 @@ #include "llselectmgr.h" class LLViewerMediaImpl; -class LLPanelMediaHUD; +class LLPanelPrimMediaControls; class LLViewerMediaFocus : public LLFocusableElement, @@ -51,40 +45,66 @@ public: LLViewerMediaFocus(); ~LLViewerMediaFocus(); - static void cleanupClass(); - - void setFocusFace(BOOL b, LLPointer objectp, S32 face, viewer_media_t media_impl); - void clearFocus() { setFocusFace(false, NULL, 0, NULL); } + // Set/clear the face that has media focus (takes keyboard input and has the full set of controls) + void setFocusFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + void clearFocus(); + + // Set/clear the face that has "media hover" (has the mimimal set of controls to zoom in or pop out into a media browser). + // If a media face has focus, the media hover will be ignored. + void setHoverFace(LLPointer objectp, S32 face, viewer_media_t media_impl, LLVector3 pick_normal = LLVector3::zero); + void clearHover(); + /*virtual*/ bool getFocus(); - /*virtual*/ // void onNavigateComplete( const EventType& event_in ); - /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); - LLUUID getSelectedUUID(); - LLObjectSelectionHandle getSelection() { return mFocus; } - void update(); + + static void setCameraZoom(LLViewerObject* object, LLVector3 normal, F32 padding_factor, bool zoom_in_only = false); + static F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); - void setCameraZoom(F32 padding_factor); - void setMouseOverFlag(bool b, viewer_media_t media_impl = NULL); - bool getMouseOverFlag() { return mMouseOverFlag; } - void setPickInfo(LLPickInfo pick_info) { mPickInfo = pick_info; } - F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); + bool isFocusedOnFace(LLPointer objectp, S32 face); + bool isHoveringOverFace(LLPointer objectp, S32 face); + + // These look up (by uuid) and return the values that were set with setFocusFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getFocusedMediaImpl(); + LLViewerObject* getFocusedObject(); + S32 getFocusedFace() { return mFocusedObjectFace; } + LLUUID getFocusedObjectID() { return mFocusedObjectID; } + + // These look up (by uuid) and return the values that were set with setHoverFace. They will return null if the objects have been destroyed. + LLViewerMediaImpl* getHoverMediaImpl(); + LLViewerObject* getHoverObject(); + S32 getHoverFace() { return mHoverObjectFace; } + + // Try to focus/zoom on the specified media (if it's on an object in world). + void focusZoomOnMedia(LLUUID media_id); + // Are we zoomed in? + bool isZoomed() const; + void unZoom(); + + // Return the ID of the media instance the controls are currently attached to (either focus or hover). + LLUUID getControlsMediaID(); protected: /*virtual*/ void onFocusReceived(); /*virtual*/ void onFocusLost(); private: - LLObjectSelectionHandle mFocus; - std::string mLastURL; - bool mMouseOverFlag; - LLPickInfo mPickInfo; - LLHandle mMediaHUD; - LLUUID mObjectID; - viewer_media_t mMediaImpl; + + LLHandle mMediaControls; + LLObjectSelectionHandle mSelection; + + LLUUID mFocusedObjectID; + S32 mFocusedObjectFace; + LLUUID mFocusedImplID; + LLVector3 mFocusedObjectNormal; + + LLUUID mHoverObjectID; + S32 mHoverObjectFace; + LLUUID mHoverImplID; + LLVector3 mHoverObjectNormal; }; diff --git a/indra/newview/llviewermediaobserver.cpp b/indra/newview/llviewermediaobserver.cpp deleted file mode 100644 index 6bed14311..000000000 --- a/indra/newview/llviewermediaobserver.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file llviewermediaobserver.cpp - * @brief Implementation of class LLViewerMediaObserver. - * - * $LicenseInfo:firstyear=2007&license=viewergpl$ - * - * Copyright (c) 2007-2010, 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 "llviewermediaeventemitter.h" -#include "llviewermediaobserver.h" - -LLViewerMediaObserver::~LLViewerMediaObserver() -{ - std::list::iterator iter = mEmitters.begin(); - - while (iter != mEmitters.end()) - { - LLViewerMediaEventEmitter* self = *iter; - ++iter; - self->remObserver(this); - } -} diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 572648025..8df0599dd 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -82,7 +82,6 @@ #include "llfloatergesture.h" #include "llfloatergodtools.h" #include "llfloaterhtmlcurrency.h" -#include "llfloatermediabrowser.h" // gViewerHtmlHelp #include "llfloaterhud.h" #include "llfloaterinspect.h" #include "llfloaterinventory.h" @@ -110,7 +109,9 @@ #include "llfloaterteleporthistory.h" #include "llfloatertest.h" #include "llfloatertools.h" +#include "llfloatervoiceeffect.h" #include "llfloaterwater.h" +#include "llfloaterwebcontent.h" #include "llfloaterwindlight.h" #include "llfloaterworldmap.h" #include "llfloatermemleak.h" @@ -3355,15 +3356,6 @@ class LLAvatarGiveCard : public view_listener_t } }; - -void login_done(S32 which, void *user) -{ - llinfos << "Login done " << which << llendl; - - LLPanelLogin::close(); -} - - bool callback_leave_group(const LLSD& notification, const LLSD& response) { S32 option = LLNotification::getSelectedOption(notification, response); @@ -6384,6 +6376,7 @@ struct MenuFloaterDict : public LLSingleton registerFloater ("script info"); registerFloater ("stat bar"); registerFloater ("teleport history"); + registerFloater ("voice effect"); registerFloater ("pathfinding_characters"); registerFloater ("pathfinding_linksets"); @@ -6442,11 +6435,12 @@ class LLShowFloater : public view_listener_t LLViewerParcelMgr::getInstance()->startBuyLand(); } - else if (floater_name == "help f1") + //Singu TODO: Re-implement f1 help. + /*else if (floater_name == "help f1") { llinfos << "Spawning HTML help window" << llendl; gViewerHtmlHelp.show(); - } + }*/ else if (floater_name == "complaint reporter") { @@ -8688,7 +8682,7 @@ static void handle_load_from_xml_continued(AIFilePicker* filepicker) void handle_web_browser_test(void*) { - LLFloaterMediaBrowser::showInstance("http://secondlife.com/app/search/slurls.html"); + LLWeb::loadURL("http://secondlife.com/app/search/slurls.html"); } void handle_buy_currency_test(void*) diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 5f74bb054..2a09ccbd7 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -82,6 +82,7 @@ #include "llselectmgr.h" #include "llstartup.h" #include "llsky.h" +#include "llslurl.h" #include "llstatenums.h" #include "llstatusbar.h" #include "llimview.h" @@ -1860,6 +1861,63 @@ bool goto_url_callback(const LLSD& notification, const LLSD& response) return false; } static LLNotificationFunctorRegistration goto_url_callback_reg("GotoURL", goto_url_callback); +static bool parse_lure_bucket(const std::string& bucket, + U64& region_handle, + LLVector3& pos, + LLVector3& look_at, + U8& region_access) +{ + // tokenize the bucket + typedef boost::tokenizer > tokenizer; + boost::char_separator sep("|", "", boost::keep_empty_tokens); + tokenizer tokens(bucket, sep); + tokenizer::iterator iter = tokens.begin(); + + S32 gx,gy,rx,ry,rz,lx,ly,lz; + try + { + gx = boost::lexical_cast((*(iter)).c_str()); + gy = boost::lexical_cast((*(++iter)).c_str()); + rx = boost::lexical_cast((*(++iter)).c_str()); + ry = boost::lexical_cast((*(++iter)).c_str()); + rz = boost::lexical_cast((*(++iter)).c_str()); + lx = boost::lexical_cast((*(++iter)).c_str()); + ly = boost::lexical_cast((*(++iter)).c_str()); + lz = boost::lexical_cast((*(++iter)).c_str()); + } + catch( boost::bad_lexical_cast& ) + { + LL_WARNS("parse_lure_bucket") + << "Couldn't parse lure bucket." + << LL_ENDL; + return false; + } + // Grab region access + region_access = SIM_ACCESS_MIN; + if (++iter != tokens.end()) + { + std::string access_str((*iter).c_str()); + LLStringUtil::trim(access_str); + if ( access_str == "A" ) + { + region_access = SIM_ACCESS_ADULT; + } + else if ( access_str == "M" ) + { + region_access = SIM_ACCESS_MATURE; + } + else if ( access_str == "PG" ) + { + region_access = SIM_ACCESS_PG; + } + } + + pos.setVec((F32)rx, (F32)ry, (F32)rz); + look_at.setVec((F32)lx, (F32)ly, (F32)lz); + + region_handle = to_region_handle(gx, gy); + return true; +} // Strip out "Resident" for display, but only if the message came from a user // (rather than a script) @@ -1978,8 +2036,11 @@ std::string replace_wildcards(std::string autoresponse, const LLUUID& id, const // Add in their legacy name boost::algorithm::replace_all(autoresponse, "#n", name); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + // Add in our location's slurl - boost::algorithm::replace_all(autoresponse, "#r", gAgent.getSLURL()); + boost::algorithm::replace_all(autoresponse, "#r", slurl.getSLURLString()); // Add in their display name LLAvatarName av_name; @@ -2783,7 +2844,9 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) std::string saved; if(offline == IM_OFFLINE) { - saved = llformat("(Saved %s) ", formatted_time(timestamp).c_str()); + LLStringUtil::format_map_t args; + args["[LONG_TIMESTAMP]"] = formatted_time(timestamp); + saved = LLTrans::getString("Saved_message", args); } buffer = separator_string + saved + message.substr(message_offset); gIMMgr->addMessage( @@ -2959,15 +3022,69 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) } } // [/RLVa:KB] + LLVector3 pos, look_at; + U64 region_handle(0); + U8 region_access(SIM_ACCESS_MIN); + std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); + std::string region_access_str = LLStringUtil::null; + std::string region_access_icn = LLStringUtil::null; + std::string region_access_lc = LLStringUtil::null; + bool canUserAccessDstRegion = true; + bool doesUserRequireMaturityIncrease = false; + + if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + { + region_access_str = LLViewerRegion::accessToString(region_access); + region_access_icn = LLViewerRegion::getAccessIcon(region_access); + region_access_lc = region_access_str; + LLStringUtil::toLower(region_access_lc); + + if (!gAgent.isGodlike()) + { + switch (region_access) + { + case SIM_ACCESS_MIN : + case SIM_ACCESS_PG : + break; + case SIM_ACCESS_MATURE : + if (gAgent.isTeen()) + { + canUserAccessDstRegion = false; + } + else if (gAgent.prefersPG()) + { + doesUserRequireMaturityIncrease = true; + } + break; + case SIM_ACCESS_ADULT : + if (!gAgent.isAdult()) + { + canUserAccessDstRegion = false; + } + else if (!gAgent.prefersAdult()) + { + doesUserRequireMaturityIncrease = true; + } + break; + default : + llassert(0); + break; + } + } + } LLSD args; // *TODO: Translate -> [FIRST] [LAST] (maybe) args["NAME"] = name; args["MESSAGE"] = message; + args["MATURITY_STR"] = region_access_str; + args["MATURITY_ICON"] = region_access_icn; + args["REGION_CONTENT_MATURITY"] = region_access_lc; LLSD payload; payload["from_id"] = from_id; payload["lure_id"] = session_id; payload["godlike"] = FALSE; + payload["region_maturity"] = region_access; //LLNotificationsUtil::add("TeleportOffered", args, payload); // [RLVa:KB] - Checked: 2010-12-11 (RLVa-1.2.2c) | Modified: RLVa-1.2.2c @@ -2982,62 +3099,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) { LLNotificationsUtil::add("TeleportOffered", args, payload); // - if(binary_bucket_size) - { - char* dest = new char[binary_bucket_size]; - strncpy(dest, (char*)binary_bucket, binary_bucket_size-1); /* Flawfinder: ignore */ - dest[binary_bucket_size-1] = '\0'; - - llinfos << "IM_LURE_USER binary_bucket " << dest << llendl; - - std::string str(dest); - typedef boost::tokenizer > tokenizer; - boost::char_separator sep("|","",boost::keep_empty_tokens); - tokenizer tokens(str, sep); - tokenizer::iterator iter = tokens.begin(); - std::string global_x_str(*iter++); - std::string global_y_str(*iter++); - std::string x_str(*iter++); - std::string y_str(*iter++); - std::string z_str(*iter++); - // skip what I think must be LookAt - if(iter != tokens.end()) - iter++; // x - if(iter != tokens.end()) - iter++; // y - if(iter != tokens.end()) - iter++; // z - std::string mat_str(""); - if(iter != tokens.end()) - mat_str.assign(*iter++); - mat_str = utf8str_trim(mat_str); - - llinfos << "IM_LURE_USER tokenized " << global_x_str << "|" << global_y_str << "|" << x_str << "|" << y_str << "|" << z_str << "|" << mat_str << llendl; - - std::istringstream gxstr(global_x_str); - int global_x; - gxstr >> global_x; - - std::istringstream gystr(global_y_str); - int global_y; - gystr >> global_y; - - std::istringstream xstr(x_str); - int x; - xstr >> x; - - std::istringstream ystr(y_str); - int y; - ystr >> y; - - std::istringstream zstr(z_str); - int z; - zstr >> z; - - llinfos << "IM_LURE_USER parsed " << global_x << "|" << global_y << "|" << x << "|" << y << "|" << z << "|" << mat_str << llendl; - - gAgent.showLureDestination(name, global_x, global_y, x, y, z, mat_str); - } + gAgent.showLureDestination(name, region_handle, pos.mV[VX], pos.mV[VY], pos.mV[VZ]); // } // [/RLVa:KB] @@ -3048,10 +3110,71 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) case IM_GODLIKE_LURE_USER: { + LLVector3 pos, look_at; + U64 region_handle(0); + U8 region_access(SIM_ACCESS_MIN); + std::string region_info = ll_safe_string((char*)binary_bucket, binary_bucket_size); + std::string region_access_str = LLStringUtil::null; + std::string region_access_icn = LLStringUtil::null; + std::string region_access_lc = LLStringUtil::null; + + bool canUserAccessDstRegion = true; + bool doesUserRequireMaturityIncrease = false; + + if (parse_lure_bucket(region_info, region_handle, pos, look_at, region_access)) + { + region_access_str = LLViewerRegion::accessToString(region_access); + region_access_icn = LLViewerRegion::getAccessIcon(region_access); + region_access_lc = region_access_str; + LLStringUtil::toLower(region_access_lc); + + if (!gAgent.isGodlike()) + { + switch (region_access) + { + case SIM_ACCESS_MIN : + case SIM_ACCESS_PG : + break; + case SIM_ACCESS_MATURE : + if (gAgent.isTeen()) + { + canUserAccessDstRegion = false; + } + else if (gAgent.prefersPG()) + { + doesUserRequireMaturityIncrease = true; + } + break; + case SIM_ACCESS_ADULT : + if (!gAgent.isAdult()) + { + canUserAccessDstRegion = false; + } + else if (!gAgent.prefersAdult()) + { + doesUserRequireMaturityIncrease = true; + } + break; + default : + llassert(0); + break; + } + } + } + + LLSD args; + // *TODO: Translate -> [FIRST] [LAST] (maybe) + args["NAME"] = name; + args["MESSAGE"] = message; + args["MATURITY_STR"] = region_access_str; + args["MATURITY_ICON"] = region_access_icn; + args["REGION_CONTENT_MATURITY"] = region_access_lc; LLSD payload; payload["from_id"] = from_id; payload["lure_id"] = session_id; payload["godlike"] = TRUE; + payload["region_maturity"] = region_access; + // do not show a message box, because you're about to be // teleported. LLNotifications::instance().forceResponse(LLNotification::Params("TeleportOffered").payload(payload), 0); @@ -4320,7 +4443,9 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) { // Chat the "back" SLURL. (DEV-4907) - LLChat chat(LLTrans::getString("completed_from") + " " + gAgent.getTeleportSourceSLURL()); + LLSLURL slurl; + gAgent.getTeleportSourceSLURL(slurl); + LLChat chat(LLTrans::getString("completed_from") + " " + slurl.getSLURLString()); chat.mSourceType = CHAT_SOURCE_SYSTEM; LLFloaterChat::addChatHistory(chat); @@ -7181,8 +7306,23 @@ void send_group_notice(const LLUUID& group_id, bool handle_lure_callback(const LLSD& notification, const LLSD& response) { + static const unsigned OFFER_RECIPIENT_LIMIT = 250; + if(notification["payload"]["ids"].size() > OFFER_RECIPIENT_LIMIT) + { + // More than OFFER_RECIPIENT_LIMIT targets will overload the message + // producing an llerror. + LLSD args; + args["OFFERS"] = notification["payload"]["ids"].size(); + args["LIMIT"] = static_cast(OFFER_RECIPIENT_LIMIT); + LLNotificationsUtil::add("TooManyTeleportOffers", args); + return false; + } + std::string text = response["message"].asString(); - S32 option = LLNotification::getSelectedOption(notification, response); + LLSLURL slurl; + LLAgentUI::buildSLURL(slurl); + text.append("\r\n").append(slurl.getSLURLString()); + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) { diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index c721e936a..362048462 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -97,6 +97,7 @@ #include "llviewernetwork.h" #include "llvowlsky.h" #include "llmanip.h" +#include "llmediaentry.h" // [RLVa:KB] #include "rlvhandler.h" diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index b5522be92..5935942b6 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -156,8 +156,9 @@ public: // Return codes for processUpdateMessage enum { MEDIA_URL_REMOVED = 0x1, - MEDIA_URL_ADDED = 0x2, - MEDIA_URL_UPDATED = 0x4, + MEDIA_URL_ADDED = 0x2, + MEDIA_URL_UPDATED = 0x4, + MEDIA_FLAGS_CHANGED = 0x8, INVALID_UPDATE = 0x80000000 }; @@ -324,7 +325,7 @@ public: /*virtual*/ S32 setTEGlow(const U8 te, const F32 glow); /*virtual*/ BOOL setMaterial(const U8 material); virtual void setTEImage(const U8 te, LLViewerTexture *imagep); // Not derived from LLPrimitive - void changeTEImage(S32 index, LLViewerTexture* new_image) ; + virtual void changeTEImage(S32 index, LLViewerTexture* new_image) ; LLViewerTexture *getTEImage(const U8 te) const; void fitFaceTexture(const U8 face); diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 26b43e526..0e8e97f3a 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -193,7 +193,7 @@ void LLViewerParcelMedia::play(LLParcel* parcel, bool filter) if (!parcel) return; - if (!gSavedSettings.getBOOL("AudioStreamingVideo")) + if (!gSavedSettings.getBOOL("AudioStreamingMedia")) return; std::string media_url = parcel->getMediaURL(); @@ -215,41 +215,57 @@ void LLViewerParcelMedia::play(LLParcel* parcel, bool filter) S32 media_width = parcel->getMediaWidth(); S32 media_height = parcel->getMediaHeight(); - // Debug print - // LL_DEBUGS("Media") << "Play media type : " << mime_type << ", url : " << media_url << LL_ENDL; - - if (!sMediaImpl || (sMediaImpl && - (sMediaImpl->getMediaURL() != media_url || - sMediaImpl->getMimeType() != mime_type || - sMediaImpl->getMediaTextureID() != placeholder_texture_id))) + if(sMediaImpl) { - if (sMediaImpl) + // If the url and mime type are the same, call play again + if(sMediaImpl->getMediaURL() == media_url + && sMediaImpl->getMimeType() == mime_type + && sMediaImpl->getMediaTextureID() == placeholder_texture_id) { - // Delete the old media impl first so they don't fight over the texture. - sMediaImpl->stop(); + LL_DEBUGS("Media") << "playing with existing url " << media_url << LL_ENDL; + + sMediaImpl->play(); } + // Else if the texture id's are the same, navigate and rediscover type + // MBW -- This causes other state from the previous parcel (texture size, autoscale, and looping) to get re-used incorrectly. + // It's also not really necessary -- just creating a new instance is fine. +// else if(sMediaImpl->getMediaTextureID() == placeholder_texture_id) +// { +// sMediaImpl->navigateTo(media_url, mime_type, true); +// } + else + { + // Since the texture id is different, we need to generate a new impl - LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; - - // There is no media impl, or it has just been deprecated, make a new one - sMediaImpl = LLViewerMedia::newMediaImpl(media_url, placeholder_texture_id, - media_width, media_height, media_auto_scale, - media_loop, mime_type); + // Delete the old one first so they don't fight over the texture. + sMediaImpl = NULL; + + // A new impl will be created below. } - - // The url, mime type and texture are now the same, call play again - if (sMediaImpl->getMediaURL() == media_url - && sMediaImpl->getMimeType() == mime_type - && sMediaImpl->getMediaTextureID() == placeholder_texture_id) - { - LL_DEBUGS("Media") << "playing with existing url " << media_url << LL_ENDL; - - sMediaImpl->play(); } - LLFirstUse::useMedia(); + // Don't ever try to play if the media type is set to "none/none" + if(stricmp(mime_type.c_str(), LLMIMETypes::getDefaultMimeType().c_str()) != 0) + { + if(!sMediaImpl) + { + LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; - LLViewerParcelMediaAutoPlay::playStarted(); + // There is no media impl, make a new one + sMediaImpl = LLViewerMedia::newMediaImpl( + placeholder_texture_id, + media_width, + media_height, + media_auto_scale, + media_loop); + sMediaImpl->setIsParcelMedia(true); + sMediaImpl->navigateTo(media_url, mime_type, true); + } + + LLFirstUse::useMedia(); + + LLViewerParcelMediaAutoPlay::playStarted(); + } } // static @@ -327,6 +343,33 @@ std::string LLViewerParcelMedia::getMimeType() return sMediaImpl.notNull() ? sMediaImpl->getMimeType() : LLMIMETypes::getDefaultMimeType(); } +//static +std::string LLViewerParcelMedia::getURL() +{ + std::string url; + if(sMediaImpl.notNull()) + url = sMediaImpl->getMediaURL(); + + if(stricmp(LLViewerParcelMgr::getInstance()->getAgentParcel()->getMediaType().c_str(), LLMIMETypes::getDefaultMimeType().c_str()) != 0) + { + if (url.empty()) + url = LLViewerParcelMgr::getInstance()->getAgentParcel()->getMediaCurrentURL(); + + if (url.empty()) + url = LLViewerParcelMgr::getInstance()->getAgentParcel()->getMediaURL(); + } + + return url; +} + +//static +std::string LLViewerParcelMedia::getName() +{ + if(sMediaImpl.notNull()) + return sMediaImpl->getName(); + return ""; +} + viewer_media_t LLViewerParcelMedia::getParcelMedia() { return sMediaImpl; @@ -619,12 +662,12 @@ bool callback_play_media(const LLSD& notification, const LLSD& response, LLParce S32 option = LLNotification::getSelectedOption(notification, response); if (option == 0) { - gSavedSettings.setBOOL("AudioStreamingVideo", TRUE); + gSavedSettings.setBOOL("AudioStreamingMedia", TRUE); LLViewerParcelMedia::play(parcel); } else { - gSavedSettings.setBOOL("AudioStreamingVideo", FALSE); + gSavedSettings.setBOOL("AudioStreamingMedia", FALSE); } gSavedSettings.setWarning("FirstStreamingVideo", FALSE); return false; diff --git a/indra/newview/llviewerparcelmedia.h b/indra/newview/llviewerparcelmedia.h index 50e7aaa2c..f6c85bf3b 100644 --- a/indra/newview/llviewerparcelmedia.h +++ b/indra/newview/llviewerparcelmedia.h @@ -86,6 +86,8 @@ class LLViewerParcelMedia : public LLViewerMediaObserver static LLViewerMediaImpl::EMediaStatus getStatus(); static std::string getMimeType(); + static std::string getURL(); + static std::string getName(); static viewer_media_t getParcelMedia(); static void processParcelMediaCommandMessage( LLMessageSystem *msg, void ** ); diff --git a/indra/newview/llviewerparcelmediaautoplay.cpp b/indra/newview/llviewerparcelmediaautoplay.cpp index 523fc0e29..ecd887440 100644 --- a/indra/newview/llviewerparcelmediaautoplay.cpp +++ b/indra/newview/llviewerparcelmediaautoplay.cpp @@ -35,12 +35,14 @@ #include "llviewerparcelmedia.h" #include "llviewercontrol.h" #include "llviewermedia.h" +#include "llviewerregion.h" #include "llparcel.h" #include "llviewerparcelmgr.h" #include "lluuid.h" #include "message.h" #include "llviewertexturelist.h" // for texture stats #include "llagent.h" +#include "llmimetypes.h" const F32 AUTOPLAY_TIME = 5; // how many seconds before we autoplay const F32 AUTOPLAY_SIZE = 24*24; // how big the texture must be (pixel area) before we autoplay @@ -48,6 +50,8 @@ const F32 AUTOPLAY_SPEED = 0.1f; // how slow should the agent be moving t LLViewerParcelMediaAutoPlay::LLViewerParcelMediaAutoPlay() : LLEventTimer(1), + + mLastParcelID(-1), mPlayed(FALSE), mTimeInParcel(0) { @@ -81,39 +85,54 @@ void LLViewerParcelMediaAutoPlay::playStarted() BOOL LLViewerParcelMediaAutoPlay::tick() { LLParcel *this_parcel = NULL; + LLViewerRegion *this_region = NULL; std::string this_media_url; + std::string this_media_type; LLUUID this_media_texture_id; S32 this_parcel_id = 0; + LLUUID this_region_id; + + this_region = gAgent.getRegion(); + + if (this_region) + { + this_region_id = this_region->getRegionID(); + } this_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (this_parcel) { - this_media_url = std::string(this_parcel->getMediaURL()); + this_media_url = this_parcel->getMediaURL(); + + this_media_type = this_parcel->getMediaType(); this_media_texture_id = this_parcel->getMediaID(); this_parcel_id = this_parcel->getLocalID(); } - if (this_parcel_id != mLastParcelID) + if (this_parcel_id != mLastParcelID || + this_region_id != mLastRegionID) { // we've entered a new parcel mPlayed = FALSE; // we haven't autoplayed yet mTimeInParcel = 0; // reset our timer mLastParcelID = this_parcel_id; + mLastRegionID = this_region_id; } - mTimeInParcel += mPeriod; // increase mTimeInParcel by the amount of time between ticks + mTimeInParcel += mPeriod; // increase mTimeInParcel by the amount of time between ticks - if ((!mPlayed) && // if we've never played - (mTimeInParcel > AUTOPLAY_TIME) && // and if we've been here for so many seconds - (this_media_url.size() != 0) && // and if the parcel has media - (LLViewerParcelMedia::sMediaImpl.isNull())) // and if the media is not already playing + if ((!mPlayed) && // if we've never played + (mTimeInParcel > AUTOPLAY_TIME) && // and if we've been here for so many seconds + (!this_media_url.empty()) && // and if the parcel has media + (stricmp(this_media_type.c_str(), LLMIMETypes::getDefaultMimeType().c_str()) != 0) && + (LLViewerParcelMedia::sMediaImpl.isNull())) // and if the media is not already playing { - if (this_media_texture_id.notNull()) // and if the media texture is good + if (this_media_texture_id.notNull()) // and if the media texture is good { - LLViewerTexture *image = LLViewerTextureManager::getFetchedTexture(this_media_texture_id, FALSE); + LLViewerMediaTexture *image = LLViewerTextureManager::getMediaTexture(this_media_texture_id, FALSE) ; F32 image_size = 0; diff --git a/indra/newview/llviewerparcelmediaautoplay.h b/indra/newview/llviewerparcelmediaautoplay.h index 243da2f38..40142c1dd 100644 --- a/indra/newview/llviewerparcelmediaautoplay.h +++ b/indra/newview/llviewerparcelmediaautoplay.h @@ -34,6 +34,7 @@ #define LLVIEWERPARCELMEDIAAUTOPLAY_H #include "lleventtimer.h" +#include "lluuid.h" // timer to automatically play media class LLViewerParcelMediaAutoPlay : LLEventTimer @@ -47,6 +48,7 @@ class LLViewerParcelMediaAutoPlay : LLEventTimer private: S32 mLastParcelID; + LLUUID mLastRegionID; BOOL mPlayed; F32 mTimeInParcel; }; diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index 85b87ab18..7e9f8ea5e 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -181,7 +181,7 @@ class LLRegionHandler : public LLCommandHandler { public: // requests will be throttled from a non-trusted browser - LLRegionHandler() : LLCommandHandler("region", /*V3: UNTRUSTED_THROTTLE*/ true) {} + LLRegionHandler() : LLCommandHandler("region", UNTRUSTED_THROTTLE) {} bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) { @@ -204,7 +204,7 @@ public: } // Process the SLapp as if it was a secondlife://{PLACE} SLurl - LLURLDispatcher::dispatch(url, /*V3: "clicked",*/ web, true); + LLURLDispatcher::dispatch(url, "clicked", web, true); return true; } }; @@ -1662,8 +1662,8 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames) capabilityNames.append("MeshUploadFlag"); capabilityNames.append("NavMeshGenerationStatus"); capabilityNames.append("NewFileAgentInventory"); - /*capabilityNames.append("ObjectMedia"); - capabilityNames.append("ObjectMediaNavigate");*/ + capabilityNames.append("ObjectMedia"); + capabilityNames.append("ObjectMediaNavigate"); capabilityNames.append("ObjectNavMeshProperties"); capabilityNames.append("ParcelNavigateMedia"); //Singu Note: Removed by Baker, do we need this? capabilityNames.append("ParcelPropertiesUpdate"); diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 29072ce93..9704fe2cb 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -1633,9 +1633,6 @@ LLXMLNodePtr LLViewerTextEditor::getXML(bool save_children) const LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory) { - std::string name("text_editor"); - node->getAttributeString("name", name); - LLRect rect; createRect(node, rect, parent, LLRect()); @@ -1656,7 +1653,7 @@ LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlF text.erase(max_text_length); } - LLViewerTextEditor* text_editor = new LLViewerTextEditor(name, + LLViewerTextEditor* text_editor = new LLViewerTextEditor("text_editor", rect, max_text_length, LLStringUtil::null, diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index a1de57f87..19681d38e 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -59,9 +59,7 @@ //#include "lltextureatlasmanager.h" #include "lltextureentry.h" #include "lltexturemanagerbridge.h" -#if NEW_MEDIA_TEXTURE #include "llmediaentry.h" -#endif //NEW_MEDIA_TEXTURE #include "llvovolume.h" #include "llviewermedia.h" /////////////////////////////////////////////////////////////////////////////// @@ -73,9 +71,7 @@ LLPointer LLViewerFetchedTexture::sMissingAssetImagep = LLPointer LLViewerFetchedTexture::sWhiteImagep = NULL; LLPointer LLViewerFetchedTexture::sDefaultImagep = NULL; LLPointer LLViewerFetchedTexture::sSmokeImagep = NULL; -#if NEW_MEDIA_TEXTURE LLViewerMediaTexture::media_map_t LLViewerMediaTexture::sMediaMap ; -#endif //NEW_MEDIA_TEXTURE #if 0 LLTexturePipelineTester* LLViewerTextureManager::sTesterp = NULL ; #endif @@ -167,26 +163,22 @@ void LLLoadedCallbackEntry::cleanUpCallbackList(LLLoadedCallbackEntry::source_ca } } -#if NEW_MEDIA_TEXTURE LLViewerMediaTexture* LLViewerTextureManager::createMediaTexture(const LLUUID &media_id, BOOL usemipmaps, LLImageGL* gl_image) { return new LLViewerMediaTexture(media_id, usemipmaps, gl_image) ; } -#endif //NEW_MEDIA_TEXTURE LLViewerTexture* LLViewerTextureManager::findTexture(const LLUUID& id) { LLViewerTexture* tex ; //search fetched texture list tex = gTextureList.findImage(id) ; - -#if NEW_MEDIA_TEXTURE + //search media texture list if(!tex) { tex = LLViewerTextureManager::findMediaTexture(id) ; } -#endif //NEW_MEDIA_TEXTURE return tex ; } @@ -194,8 +186,7 @@ LLViewerFetchedTexture* LLViewerTextureManager::findFetchedTexture(const LLUUID { return gTextureList.findImage(id); } - -#if NEW_MEDIA_TEXTURE + LLViewerMediaTexture* LLViewerTextureManager::findMediaTexture(const LLUUID &media_id) { return LLViewerMediaTexture::findMediaTexture(media_id) ; @@ -213,7 +204,6 @@ LLViewerMediaTexture* LLViewerTextureManager::getMediaTexture(const LLUUID& id, return tex ; } -#endif //NEW_MEDIA_TEXTURE LLViewerFetchedTexture* LLViewerTextureManager::staticCastToFetchedTexture(LLTexture* tex, BOOL report_error) { @@ -419,9 +409,7 @@ void LLViewerTextureManager::cleanup() LLViewerFetchedTexture::sMissingAssetImagep = NULL; LLViewerFetchedTexture::sWhiteImagep = NULL; -#if NEW_MEDIA_TEXTURE - LLViewerMediaTexture::cleanUpClass() ; -#endif //NEW_MEDIA_TEXTURE + LLViewerMediaTexture::cleanUpClass() ; } //---------------------------------------------------------------------------------------------- @@ -500,9 +488,7 @@ bool LLViewerTexture::isMemoryForTextureLow() return low_mem ; } -#if NEW_MEDIA_TEXTURE static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE_MEDIA("Media"); -#endif //NEW_MEDIA_TEXTURE #if 0 static LLFastTimer::DeclareTimer FTM_TEXTURE_UPDATE_TEST("Test"); #endif @@ -519,12 +505,10 @@ void LLViewerTexture::updateClass(const F32 velocity, const F32 angular_velocity tester->update() ; } #endif -#if NEW_MEDIA_TEXTURE { LLFastTimer t(FTM_TEXTURE_UPDATE_MEDIA); LLViewerMediaTexture::updateClass() ; } -#endif //NEW_MEDIA_TEXTURE sBoundTextureMemoryInBytes = LLImageGL::sBoundTextureMemoryInBytes;//in bytes sTotalTextureMemoryInBytes = LLImageGL::sGlobalTextureMemoryInBytes;//in bytes @@ -634,9 +618,6 @@ void LLViewerTexture::init(bool firstinit) mNumVolumes = 0; mFaceList.clear() ; mVolumeList.clear(); -#if !NEW_MEDIA_TEXTURE - mIsMediaTexture = false; -#endif //!NEW_MEDIA_TEXTURE } //virtual @@ -3027,7 +3008,6 @@ bool LLViewerLODTexture::scaleDown() //---------------------------------------------------------------------------------------------- //start of LLViewerMediaTexture //---------------------------------------------------------------------------------------------- -#if NEW_MEDIA_TEXTURE //static void LLViewerMediaTexture::updateClass() { @@ -3112,7 +3092,7 @@ LLViewerMediaTexture::LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps, LL setMediaImpl() ; - setCategory(LLViewerTexture::MEDIA) ; + setCategory(LLGLTexture::MEDIA) ; LLViewerTexture* tex = gTextureList.findImage(mID) ; if(tex) //this media is a parcel media for tex. @@ -3130,7 +3110,7 @@ LLViewerMediaTexture::~LLViewerMediaTexture() tex->setParcelMedia(NULL) ; } } -#endif //NEW_MEDIA_TEXTURE + void LLViewerMediaTexture::reinit(BOOL usemipmaps /* = TRUE */) { llassert(mGLTexturep.notNull()) ; @@ -3150,7 +3130,7 @@ void LLViewerMediaTexture::setUseMipMaps(BOOL mipmap) mGLTexturep->setUseMipMaps(mipmap) ; } } -#if NEW_MEDIA_TEXTURE + //virtual S8 LLViewerMediaTexture::getType() const { @@ -3524,7 +3504,6 @@ F32 LLViewerMediaTexture::getMaxVirtualSize() return mMaxVirtualSize ; } -#endif //NEW_MEDIA_TEXTURE //---------------------------------------------------------------------------------------------- //end of LLViewerMediaTexture //---------------------------------------------------------------------------------------------- diff --git a/indra/newview/llviewertexture.h b/indra/newview/llviewertexture.h index cf8c6d003..b378f578d 100644 --- a/indra/newview/llviewertexture.h +++ b/indra/newview/llviewertexture.h @@ -518,7 +518,6 @@ private: // class LLViewerMediaTexture : public LLViewerTexture { -#if NEW_MEDIA_TEXTURE protected: /*virtual*/ ~LLViewerMediaTexture() ; @@ -526,16 +525,11 @@ public: LLViewerMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ; /*virtual*/ S8 getType() const; -#endif //NEW_MEDIA_TEXTURE -#if !NEW_MEDIA_TEXTURE -public: -#endif //!NEW_MEDIA_TEXTURE void reinit(BOOL usemipmaps = TRUE); BOOL getUseMipMaps() {return mUseMipMaps ; } void setUseMipMaps(BOOL mipmap) ; -#if NEW_MEDIA_TEXTURE void setPlaying(BOOL playing) ; BOOL isPlaying() const {return mIsPlaying;} void setMediaImpl() ; @@ -580,7 +574,6 @@ public: private: typedef std::map< LLUUID, LLPointer > media_map_t ; static media_map_t sMediaMap ; -#endif //NEW_MEDIA_TEXTURE }; //just an interface class, do not create instance from this class. @@ -604,17 +597,15 @@ public: // static LLViewerTexture* findTexture(const LLUUID& id) ; static LLViewerFetchedTexture* findFetchedTexture(const LLUUID& id) ; -#if NEW_MEDIA_TEXTURE static LLViewerMediaTexture* findMediaTexture(const LLUUID& id) ; - + static LLViewerMediaTexture* createMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ; // //"get-texture" will create a new texture if the texture does not exist. // static LLViewerMediaTexture* getMediaTexture(const LLUUID& id, BOOL usemipmaps = TRUE, LLImageGL* gl_image = NULL) ; -#endif //NEW_MEDIA_TEXTURE - + static LLPointer getLocalTexture(BOOL usemipmaps = TRUE, BOOL generate_gl_tex = TRUE); static LLPointer getLocalTexture(const LLUUID& id, BOOL usemipmaps, BOOL generate_gl_tex = TRUE) ; static LLPointer getLocalTexture(const LLImageRaw* raw, BOOL usemipmaps) ; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index 80efedbed..298640e74 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -185,9 +185,7 @@ void LLViewerTextureList::doPreloadImages() static std::string get_texture_list_name() { - //return std::string("texture_list_") + gSavedSettings.getString("LoginLocation") + ".xml"; - bool login_last = gSavedSettings.getBOOL("LoginLastLocation"); - return std::string("texture_list_") + (login_last?"last":"home") + ".xml"; + return std::string("texture_list_") + gSavedSettings.getString("LoginLocation") + ".xml"; } void LLViewerTextureList::doPrefetchImages() @@ -744,12 +742,7 @@ void LLViewerTextureList::updateImages(F32 max_time) } } } - //Required for old media system - if (!gNoRender && !gGLManager.mIsDisabled) - { - LLFastTimer t(FTM_IMAGE_MEDIA); - LLViewerMedia::updateMedia(); - } + { LLFastTimer t(FTM_IMAGE_STATS); updateImagesUpdateStats(); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index e8efa11a7..6a1cf636a 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -73,6 +73,7 @@ #include "lltimer.h" #include "timing.h" #include "llviewermenu.h" +#include "llmediaentry.h" #include "raytrace.h" // newview includes @@ -172,7 +173,6 @@ #include "llworldmapview.h" #include "pipeline.h" #include "llappviewer.h" -#include "llurlsimstring.h" #include "llviewerdisplay.h" #include "llspatialpartition.h" #include "llviewerjoystick.h" @@ -185,6 +185,8 @@ #include "llfloaternotificationsconsole.h" +#include "llpanelnearbymedia.h" + // [RLVa:KB] #include "rlvhandler.h" // [/RLVa:KB] @@ -742,6 +744,12 @@ public: static const LLCachedControl beacons_visible("BeaconsVisible",false); if (LLPipeline::getRenderBeacons(NULL) && beacons_visible) { + if (LLPipeline::getRenderMOAPBeacons(NULL)) + { + addText(xpos, ypos, "Viewing media beacons (white)"); + ypos += y_inc; + } + if (LLPipeline::toggleRenderTypeControlNegated((void*)LLPipeline::RENDER_TYPE_PARTICLES)) { addText(xpos, ypos, particle_hiding); @@ -939,6 +947,12 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleMouseUp(local_x, local_y, mask); } + // Mark the click as handled and return if we aren't within the root view to avoid spurious bugs + if( !mRootView->pointInView(x, y) ) + { + return TRUE; + } + // Give the UI views a chance to process the click if( mRootView->handleAnyMouseClick(x, y, mask, clicktype, down) ) { @@ -953,43 +967,17 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK llinfos << buttonname << " Mouse " << buttonstatestr << " not handled by view" << llendl; } - if (down) + // Do not allow tool manager to handle mouseclicks if we have disconnected + if(!gDisconnected && LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) ) { - // Do not allow tool manager to handle mouseclicks if we have disconnected - if (gDisconnected) - { - return FALSE; - } - - if (LLToolMgr::getInstance()->getCurrentTool()->handleAnyMouseClick( x, y, mask, clicktype, down ) ) - { - // This is necessary to force clicks in the world to cause edit - // boxes that might have keyboard focus to relinquish it, and hence - // cause a commit to update their value. JC - gFocusMgr.setKeyboardFocus(NULL); - return TRUE; - } + return TRUE; } - else - { - mWindow->releaseMouse(); + - LLTool *tool = LLToolMgr::getInstance()->getCurrentTool(); - if( !handled ) - { - handled = mRootView->handleAnyMouseClick(x, y, mask, clicktype, down); - } - - if( !handled ) - { - if (tool) - { - handled = tool->handleAnyMouseClick(x, y, mask, clicktype, down); - } - } - } - - return (!down); + // If we got this far on a down-click, it wasn't handled. + // Up-clicks, though, are always handled as far as the OS is concerned. + BOOL default_rtn = !down; + return default_rtn; } BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1003,8 +991,12 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma // try handling as a double-click first, then a single-click if that // wasn't handled. BOOL down = TRUE; - return handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_DOUBLELEFT, down) || - handleMouseDown(window, pos, mask); + if (handleAnyMouseClick(window, pos, mask, + LLMouseHandler::CLICK_DOUBLELEFT, down)) + { + return TRUE; + } + return handleMouseDown(window, pos, mask); } BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1078,6 +1070,149 @@ BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MAS return TRUE; } +LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data) +{ + LLWindowCallbacks::DragNDropResult result = LLWindowCallbacks::DND_NONE; + + const bool prim_media_dnd_enabled = gSavedSettings.getBOOL("PrimMediaDragNDrop"); + const bool slurl_dnd_enabled = gSavedSettings.getBOOL("SLURLDragNDrop"); + + if ( prim_media_dnd_enabled || slurl_dnd_enabled ) + { + switch(action) + { + // Much of the handling for these two cases is the same. + case LLWindowCallbacks::DNDA_TRACK: + case LLWindowCallbacks::DNDA_DROPPED: + case LLWindowCallbacks::DNDA_START_TRACKING: + { + bool drop = (LLWindowCallbacks::DNDA_DROPPED == action); + + if (slurl_dnd_enabled) + { + LLSLURL dropped_slurl(data); + if(dropped_slurl.isSpatial()) + { + if (drop) + { + LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), "clicked", NULL, true ); + return LLWindowCallbacks::DND_MOVE; + } + return LLWindowCallbacks::DND_COPY; + } + } + + if (prim_media_dnd_enabled) + { + LLPickInfo pick_info = pickImmediate( pos.mX, pos.mY, TRUE /*BOOL pick_transparent*/ ); + + LLUUID object_id = pick_info.getObjectID(); + S32 object_face = pick_info.mObjectFace; + std::string url = data; + + lldebugs << "Object: picked at " << pos.mX << ", " << pos.mY << " - face = " << object_face << " - URL = " << url << llendl; + + LLVOVolume *obj = dynamic_cast(static_cast(pick_info.getObject())); + + if (obj && !obj->getRegion()->getCapability("ObjectMedia").empty()) + { + LLTextureEntry *te = obj->getTE(object_face); + + // can modify URL if we can modify the object or we have navigate permissions + bool allow_modify_url = obj->permModify() || obj->hasMediaPermission( te->getMediaData(), LLVOVolume::MEDIA_PERM_INTERACT ); + + if (te && allow_modify_url ) + { + if (drop) + { + // object does NOT have media already + if ( ! te->hasMedia() ) + { + // we are allowed to modify the object + if ( obj->permModify() ) + { + // Create new media entry + LLSD media_data; + // XXX Should we really do Home URL too? + media_data[LLMediaEntry::HOME_URL_KEY] = url; + media_data[LLMediaEntry::CURRENT_URL_KEY] = url; + media_data[LLMediaEntry::AUTO_PLAY_KEY] = true; + obj->syncMediaData(object_face, media_data, true, true); + // XXX This shouldn't be necessary, should it ?!? + if (obj->getMediaImpl(object_face)) + obj->getMediaImpl(object_face)->navigateReload(); + obj->sendMediaDataUpdate(); + + result = LLWindowCallbacks::DND_COPY; + } + } + else + // object HAS media already + { + // URL passes the whitelist + if (te->getMediaData()->checkCandidateUrl( url ) ) + { + // just navigate to the URL + if (obj->getMediaImpl(object_face)) + { + obj->getMediaImpl(object_face)->navigateTo(url); + } + else + { + // This is very strange. Navigation should + // happen via the Impl, but we don't have one. + // This sends it to the server, which /should/ + // trigger us getting it. Hopefully. + LLSD media_data; + media_data[LLMediaEntry::CURRENT_URL_KEY] = url; + obj->syncMediaData(object_face, media_data, true, true); + obj->sendMediaDataUpdate(); + } + result = LLWindowCallbacks::DND_LINK; + + } + } + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = NULL; + + } + else + { + // Check the whitelist, if there's media (otherwise just show it) + if (te->getMediaData() == NULL || te->getMediaData()->checkCandidateUrl(url)) + { + if ( obj != mDragHoveredObject) + { + // Highlight the dragged object + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = obj; + LLSelectMgr::getInstance()->highlightObjectOnly(mDragHoveredObject); + } + result = (! te->hasMedia()) ? LLWindowCallbacks::DND_COPY : LLWindowCallbacks::DND_LINK; + + } + } + } + } + } + } + break; + + case LLWindowCallbacks::DNDA_STOP_TRACKING: + // The cleanup case below will make sure things are unhilighted if necessary. + break; + } + + if (prim_media_dnd_enabled && + result == LLWindowCallbacks::DND_NONE && !mDragHoveredObject.isNull()) + { + LLSelectMgr::getInstance()->unhighlightObjectOnly(mDragHoveredObject); + mDragHoveredObject = NULL; + } + } + + return result; +} BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; @@ -1405,7 +1540,8 @@ void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) std::string url = (const char*)data; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - if (LLURLDispatcher::dispatch(url, web, trusted_browser)) + // don't treat slapps coming from external browsers as "clicks" as this would bypass throttling + if (LLURLDispatcher::dispatch(url, "", web, trusted_browser)) { // bring window to foreground, as it has just been "launched" from a URL mWindow->bringToFront(); @@ -1495,7 +1631,6 @@ LLViewerWindow::LLViewerWindow( mMouseInWindow( FALSE ), mLastMask( MASK_NONE ), mToolStored( NULL ), - mSuppressToolbox( FALSE ), mHideCursorPermanent( FALSE ), mCursorHidden(FALSE), mIgnoreActivate( FALSE ), @@ -1503,7 +1638,8 @@ LLViewerWindow::LLViewerWindow( mResDirty(false), //mStatesDirty(false), //Singu Note: No longer needed. State update is now in restoreGL. mIsFullscreenChecked(false), - mCurrResolutionIndex(0) + mCurrResolutionIndex(0), + mProgressView(NULL) { LLNotificationChannel::buildChannel("VW_alerts", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alert")); LLNotificationChannel::buildChannel("VW_alertmodal", "Visible", LLNotificationFilters::filterBy(&LLNotification::getType, "alertmodal")); @@ -1555,9 +1691,11 @@ LLViewerWindow::LLViewerWindow( { LL_WARNS("Window") << " Someone took over my signal/exception handler (post createWindow)!" << LL_ENDL; } - + + const bool do_not_enforce = false; + mWindow->setMinSize(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, do_not_enforce); // root view not set LLCoordScreen scr; - mWindow->getSize(&scr); + mWindow->getSize(&scr); if(fullscreen && ( scr.mX!=width || scr.mY!=height)) { @@ -1957,11 +2095,24 @@ void LLViewerWindow::adjustControlRectanglesForFirstUse(const LLRect& window) void LLViewerWindow::initWorldUI() { pre_init_menus(); + if(!gMenuHolder) + { + // + // Tools for building + // + init_menus(); + } +} +// initWorldUI that wasn't before logging in. Some of this may require the access the 'LindenUserDir'. +void LLViewerWindow::initWorldUI_postLogin() +{ S32 height = mRootView->getRect().getHeight(); S32 width = mRootView->getRect().getWidth(); LLRect full_window(0, height, width, 0); + //============================================ + //Begin LLViewerWindow::initWorlUI // Don't re-enter if objects are alreay created if (gBottomPanel == NULL) { @@ -1969,18 +2120,15 @@ void LLViewerWindow::initWorldUI() gBottomPanel = new LLBottomPanel(mRootView->getRect()); mRootView->addChild(gBottomPanel); + LLFloaterNearbyMedia::updateClass(); //Dependent on the overlay panel being fully initialized. + // View for hover information gHoverView = new LLHoverView(std::string("gHoverView"), full_window); gHoverView->setVisible(TRUE); mRootView->addChild(gHoverView); gIMMgr = LLIMMgr::getInstance(); - - // - // Tools for building - // - - init_menus(); + gIMMgr->loadIgnoreGroup(); // Toolbox floater gFloaterTools = new LLFloaterTools(); @@ -1999,19 +2147,16 @@ void LLViewerWindow::initWorldUI() // put behind everything else in the UI mRootView->addChildInBack(gHUDView); } + //End LLViewerWindow::initWorlUI + //============================================ + LLPanel* panel_ssf_container = getRootView()->getChild("state_management_buttons_container"); panel_ssf_container->setVisible(TRUE); LLMenuOptionPathfindingRebakeNavmesh::getInstance()->initialize(); -} -// initWorldUI that wasn't before logging in. Some of this may require the access the 'LindenUserDir'. -void LLViewerWindow::initWorldUI_postLogin() -{ - S32 height = mRootView->getRect().getHeight(); - S32 width = mRootView->getRect().getWidth(); - LLRect full_window(0, height, width, 0); + // Don't re-enter if objects are alreay created. if (!gStatusBar) @@ -2059,6 +2204,7 @@ void LLViewerWindow::initWorldUI_postLogin() LLFloaterChatterBox::createInstance(LLSD()); } + mRootView->sendChildToFront(mProgressView); } // Destroy the UI @@ -2086,7 +2232,8 @@ void LLViewerWindow::shutdownViews() mRootView = NULL; llinfos << "RootView deleted." << llendl ; - LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit(); + if(LLMenuOptionPathfindingRebakeNavmesh::instanceExists()) + LLMenuOptionPathfindingRebakeNavmesh::getInstance()->quit(); // Automatically deleted as children of mRootView. Fix the globals. gFloaterTools = NULL; @@ -2293,6 +2440,7 @@ void LLViewerWindow::reshape(S32 width, S32 height) LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width); LLViewerStats::getInstance()->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height); gResizeScreenTexture = TRUE; + LLLayoutStack::updateClass(); } } @@ -2575,7 +2723,8 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) } // Explicit hack for debug menu. - if ((mask == (MASK_SHIFT | MASK_CONTROL)) && + //Singu note: We do not use the ForceShowGrid setting. Grid selection should always be visible. + /*if ((mask == (MASK_SHIFT | MASK_CONTROL)) && ('G' == key || 'g' == key)) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) //on splash page @@ -2584,9 +2733,9 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) gSavedSettings.setBOOL("ForceShowGrid", visible); // Initialize visibility (and don't force visibility - use prefs) - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationSelectorsVisibility(); } - } + }*/ // Debugging view for unified notifications: CTRL-SHIFT-5 // *FIXME: Having this special-cased right here (just so this can be invoked from the login screen) sucks. @@ -2871,6 +3020,8 @@ void LLViewerWindow::updateUI() LLFastTimer t(ftm); static std::string last_handle_msg; + // animate layout stacks so we have up to date rect for world view + LLLayoutStack::updateClass(); LLView::sMouseHandlerMessage.clear(); @@ -2893,213 +3044,363 @@ void LLViewerWindow::updateUI() } updateMouseDelta(); - - + if (gNoRender) { return; } - - // clean up current focus - LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (cur_focus) - { - if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) - { - gFocusMgr.releaseFocusIfNeeded(cur_focus); - - LLUICtrl* parent = cur_focus->getParentUICtrl(); - const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); - while(parent) - { - if (parent->isCtrl() && - (parent->hasTabStop() || parent == focus_root) && - !parent->getIsChrome() && - parent->isInVisibleChain() && - parent->isInEnabledChain()) - { - if (!parent->focusFirstItem()) - { - parent->setFocus(TRUE); - } - break; - } - parent = parent->getParentUICtrl(); - } - } - else if (cur_focus->isFocusRoot()) - { - // focus roots keep trying to delegate focus to their first valid descendant - // this assumes that focus roots are not valid focus holders on their own - cur_focus->focusFirstItem(); - } - } + + updateKeyboardFocus(); BOOL handled = FALSE; - BOOL handled_by_top_ctrl = FALSE; LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); - LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture(); - if( mouse_captor ) - { - // Pass hover events to object capturing mouse events. - S32 local_x; - S32 local_y; - mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); - handled = mouse_captor->handleHover(local_x, local_y, mask); - if (LLView::sDebugMouseHandling) - { - llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; - } + LLView* captor_view = dynamic_cast(mouse_captor); - if( !handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; - } + //FIXME: only include captor and captor's ancestors if mouse is truly over them --RN + + //build set of views containing mouse cursor by traversing UI hierarchy and testing + //screen rect against mouse cursor + view_handle_set_t mouse_hover_set; + + // constraint mouse enter events to children of mouse captor + LLView* root_view = captor_view; + + // if mouse captor doesn't exist or isn't a LLView + // then allow mouse enter events on entire UI hierarchy + if (!root_view) + { + root_view = mRootView; } - else - { - if (top_ctrl) - { - S32 local_x, local_y; - top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); - handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask); - handled_by_top_ctrl = TRUE; - } - if ( !handled ) + // only update mouse hover set when UI is visible (since we shouldn't send hover events to invisible UI +// if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + // include all ancestors of captor_view as automatically having mouse + if (captor_view) { - // x and y are from last time mouse was in window - // mMouseInWindow tracks *actual* mouse location - if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) + LLView* captor_parent_view = captor_view->getParent(); + while(captor_parent_view) { - if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) - { - last_handle_msg = LLView::sMouseHandlerMessage; - llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; - } - handled = TRUE; - } - else if (LLView::sDebugMouseHandling) - { - if (last_handle_msg != LLStringUtil::null) - { - last_handle_msg.clear(); - llinfos << "Hover not handled by view" << llendl; - } + mouse_hover_set.insert(captor_parent_view->getHandle()); + captor_parent_view = captor_parent_view->getParent(); } } - if( !handled ) + // while the top_ctrl contains the mouse cursor, only it and its descendants will receive onMouseEnter events + if (top_ctrl && top_ctrl->calcScreenBoundingRect().pointInRect(x, y)) { - lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl; - } - } - - // *NOTE: sometimes tools handle the mouse as a captor, so this - // logic is a little confusing - LLTool *tool = NULL; - if (gHoverView) - { - tool = LLToolMgr::getInstance()->getCurrentTool(); - - if(!handled && tool) - { - handled = tool->handleHover(x, y, mask); - - if (!mWindow->isCursorHidden()) + // iterator over contents of top_ctrl, and throw into mouse_hover_set + for (LLView::tree_iterator_t it = top_ctrl->beginTreeDFS(); + it != top_ctrl->endTreeDFS(); + ++it) { - gHoverView->updateHover(tool); + LLView* viewp = *it; + if (viewp->getVisible() + && viewp->calcScreenBoundingRect().pointInRect(x, y)) + { + // we have a view that contains the mouse, add it to the set + mouse_hover_set.insert(viewp->getHandle()); + } + else + { + // skip this view and all of its children + it.skipDescendants(); + } } } else { - // Cancel hovering if any UI element handled the event. - gHoverView->cancelHover(); + // walk UI tree in depth-first order + for (LLView::tree_iterator_t it = root_view->beginTreeDFS(); + it != root_view->endTreeDFS(); + ++it) + { + LLView* viewp = *it; + // calculating the screen rect involves traversing the parent, so this is less than optimal + if (viewp->getVisible() + && viewp->calcScreenBoundingRect().pointInRect(x, y)) + { + + // if this view is mouse opaque, nothing behind it should be in mouse_hover_set + if (viewp->getMouseOpaque()) + { + // constrain further iteration to children of this widget + it = viewp->beginTreeDFS(); + } + + // we have a view that contains the mouse, add it to the set + mouse_hover_set.insert(viewp->getHandle()); + } + else + { + // skip this view and all of its children + it.skipDescendants(); + } + } + } + } + + typedef std::vector > view_handle_list_t; + + // call onMouseEnter() on all views which contain the mouse cursor but did not before + view_handle_list_t mouse_enter_views; + std::set_difference(mouse_hover_set.begin(), mouse_hover_set.end(), + mMouseHoverViews.begin(), mMouseHoverViews.end(), + std::back_inserter(mouse_enter_views)); + for (view_handle_list_t::iterator it = mouse_enter_views.begin(); + it != mouse_enter_views.end(); + ++it) + { + LLView* viewp = it->get(); + if (viewp) + { + LLRect view_screen_rect = viewp->calcScreenRect(); + viewp->onMouseEnter(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask); + } + } + + // call onMouseLeave() on all views which no longer contain the mouse cursor + view_handle_list_t mouse_leave_views; + std::set_difference(mMouseHoverViews.begin(), mMouseHoverViews.end(), + mouse_hover_set.begin(), mouse_hover_set.end(), + std::back_inserter(mouse_leave_views)); + for (view_handle_list_t::iterator it = mouse_leave_views.begin(); + it != mouse_leave_views.end(); + ++it) + { + LLView* viewp = it->get(); + if (viewp) + { + LLRect view_screen_rect = viewp->calcScreenRect(); + viewp->onMouseLeave(x - view_screen_rect.mLeft, y - view_screen_rect.mBottom, mask); + } + } + + // store resulting hover set for next frame + swap(mMouseHoverViews, mouse_hover_set); + + // only handle hover events when UI is enabled +// if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + + if( mouse_captor ) + { + // Pass hover events to object capturing mouse events. + S32 local_x; + S32 local_y; + mouse_captor->screenPointToLocal( x, y, &local_x, &local_y ); + handled = mouse_captor->handleHover(local_x, local_y, mask); + if (LLView::sDebugMouseHandling) + { + llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl; + } + + if( !handled ) + { + lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl; + } + } + else + { + if (top_ctrl) + { + S32 local_x, local_y; + top_ctrl->screenPointToLocal( x, y, &local_x, &local_y ); + handled = top_ctrl->pointInView(local_x, local_y) && top_ctrl->handleHover(local_x, local_y, mask); + } + + if ( !handled ) + { + // x and y are from last time mouse was in window + // mMouseInWindow tracks *actual* mouse location + if (mMouseInWindow && mRootView->handleHover(x, y, mask) ) + { + if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg) + { + last_handle_msg = LLView::sMouseHandlerMessage; + llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl; + } + handled = TRUE; + } + else if (LLView::sDebugMouseHandling) + { + if (last_handle_msg != LLStringUtil::null) + { + last_handle_msg.clear(); + llinfos << "Hover not handled by view" << llendl; + } + } + } + + if( !handled ) + { + lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl; + } } + // *NOTE: sometimes tools handle the mouse as a captor, so this + // logic is a little confusing + LLTool *tool = NULL; + if (gHoverView) + { + tool = LLToolMgr::getInstance()->getCurrentTool(); + + if(!handled && tool) + { + handled = tool->handleHover(x, y, mask); + + if (!mWindow->isCursorHidden()) + { + gHoverView->updateHover(tool); + } + } + else + { + // Cancel hovering if any UI element handled the event. + gHoverView->cancelHover(); + } + + } + + // Show a new tool tip (or update one that is already shown) + BOOL tool_tip_handled = FALSE; + std::string tool_tip_msg; + static const LLCachedControl tool_tip_delay("ToolTipDelay",.7f); + F32 tooltip_delay = tool_tip_delay; + //HACK: hack for tool-based tooltips which need to pop up more quickly + //Also for show xui names as tooltips debug mode + if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames) + { + static const LLCachedControl drag_and_drop_tool_tip_delay("DragAndDropToolTipDelay",.1f); + tooltip_delay = drag_and_drop_tool_tip_delay; + } + if( handled && + gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay && + !mWindow->isCursorHidden() ) + { + LLRect screen_sticky_rect; + LLMouseHandler *mh; + S32 local_x, local_y; + if (mouse_captor) + { + mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); + mh = mouse_captor; + } + else if (top_ctrl) + { + top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); + mh = top_ctrl; + } + else + { + local_x = x; local_y = y; + mh = mRootView; + } + + BOOL tooltip_vis = FALSE; + if (shouldShowToolTipFor(mh)) + { + tool_tip_handled = mh->handleToolTip(local_x, local_y, tool_tip_msg, &screen_sticky_rect ); + + if( tool_tip_handled && !tool_tip_msg.empty() ) + { + mToolTipStickyRect = screen_sticky_rect; + mToolTip->setWrappedText( tool_tip_msg, 200 ); + mToolTip->reshapeToFitText(); + mToolTip->setOrigin( x, y ); + LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0); + mToolTip->translateIntoRect( virtual_window_rect, FALSE ); + tooltip_vis = TRUE; + } + } + + if (mToolTip) + { + mToolTip->setVisible( tooltip_vis ); + } + } + } + + updateLayout(); + + mLastMousePoint = mCurrentMousePoint; + + // cleanup unused selections when no modal dialogs are open + if (LLModalDialog::activeCount() == 0) + { + LLViewerParcelMgr::getInstance()->deselectUnused(); + } + + if (LLModalDialog::activeCount() == 0) + { + LLSelectMgr::getInstance()->deselectUnused(); + } + + // per frame picking - for tooltips and changing cursor over interactive objects + static S32 previous_x = -1; + static S32 previous_y = -1; + static BOOL mouse_moved_since_pick = FALSE; + + if ((previous_x != x) || (previous_y != y)) + mouse_moved_since_pick = TRUE; + + static const LLCachedControl picks_moving("PicksPerSecondMouseMoving",5.f); + static const LLCachedControl picks_stationary("PicksPerSecondMouseStationary",0.f); + if( !getCursorHidden() + // When in-world media is in focus, pick every frame so that browser mouse-overs, dragging scrollbars, etc. work properly. + && (LLViewerMediaFocus::getInstance()->getFocus() + || ((mouse_moved_since_pick) && (picks_moving > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_moving)) + || ((!mouse_moved_since_pick) && (picks_stationary > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_stationary)))) + { + mouse_moved_since_pick = FALSE; + mPickTimer.reset(); + pickAsync(getCurrentMouseX(), getCurrentMouseY(), mask, hoverPickCallback, TRUE, TRUE); + } + + previous_x = x; + previous_y = y; + + return; +} + +/* static */ +void LLViewerWindow::hoverPickCallback(const LLPickInfo& pick_info) +{ + gViewerWindow->mHoverPick = pick_info; +} + +void LLViewerWindow::updateLayout() +{ + static const LLCachedControl freeze_time("FreezeTime",0); + LLTool* tool = LLToolMgr::getInstance()->getCurrentTool(); + if (gHoverView != NULL && + gFloaterTools != NULL + && tool != NULL + && tool != gToolNull + && tool != LLToolCompInspect::getInstance() + && tool != LLToolDragAndDrop::getInstance() + && !freeze_time) + { // Suppress the toolbox view if our source tool was the pie tool, // and we've overridden to something else. - mSuppressToolbox = + bool suppress_toolbox = (LLToolMgr::getInstance()->getBaseTool() == LLToolPie::getInstance()) && (LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance()); - } - - // Show a new tool tip (or update one that is already shown) - BOOL tool_tip_handled = FALSE; - std::string tool_tip_msg; - static const LLCachedControl tool_tip_delay("ToolTipDelay",.7f); - F32 tooltip_delay = tool_tip_delay; - //HACK: hack for tool-based tooltips which need to pop up more quickly - //Also for show xui names as tooltips debug mode - if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames) - { - static const LLCachedControl drag_and_drop_tool_tip_delay("DragAndDropToolTipDelay",.1f); - tooltip_delay = drag_and_drop_tool_tip_delay; - } - if( handled && - gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay && - !mWindow->isCursorHidden() ) - { - LLRect screen_sticky_rect; - LLMouseHandler *mh; - S32 local_x, local_y; - if (mouse_captor) - { - mouse_captor->screenPointToLocal(x, y, &local_x, &local_y); - mh = mouse_captor; - } - else if (handled_by_top_ctrl) - { - top_ctrl->screenPointToLocal(x, y, &local_x, &local_y); - mh = top_ctrl; - } - else - { - local_x = x; local_y = y; - mh = mRootView; - } - - BOOL tooltip_vis = FALSE; - if (shouldShowToolTipFor(mh)) - { - tool_tip_handled = mh->handleToolTip(local_x, local_y, tool_tip_msg, &screen_sticky_rect ); - - if( tool_tip_handled && !tool_tip_msg.empty() ) - { - mToolTipStickyRect = screen_sticky_rect; - mToolTip->setWrappedText( tool_tip_msg, 200 ); - mToolTip->reshapeToFitText(); - mToolTip->setOrigin( x, y ); - LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0); - mToolTip->translateIntoRect( virtual_window_rect, FALSE ); - tooltip_vis = TRUE; - } - } - - if (mToolTip) - { - mToolTip->setVisible( tooltip_vis ); - } - } - - static const LLCachedControl freeze_time("FreezeTime",0); - if (tool && tool != gToolNull && tool != LLToolCompInspect::getInstance() && tool != LLToolDragAndDrop::getInstance() && !freeze_time) - { LLMouseHandler *captor = gFocusMgr.getMouseCapture(); // With the null, inspect, or drag and drop tool, don't muck // with visibility. if (gFloaterTools->isMinimized() - || (tool != LLToolPie::getInstance() // not default tool - && tool != LLToolCompGun::getInstance() // not coming out of mouselook - && !mSuppressToolbox // not override in third person - && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode - && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset - && (!captor || captor->isView())) // not dragging - ) + || (tool != LLToolPie::getInstance() // not default tool + && tool != LLToolCompGun::getInstance() // not coming out of mouselook + && !suppress_toolbox // not override in third person + && LLToolMgr::getInstance()->getCurrentToolset() != gFaceEditToolset // not special mode + && LLToolMgr::getInstance()->getCurrentToolset() != gMouselookToolset + && (!captor || dynamic_cast(captor) != NULL))) // not dragging + { // Force floater tools to be visible (unless minimized) if (!gFloaterTools->getVisible()) @@ -3108,6 +3409,7 @@ void LLViewerWindow::updateUI() } // Update the location of the blue box tool popup LLCoordGL select_center_screen; + MASK mask = gKeyboard->currentMask(TRUE); gFloaterTools->updatePopup( select_center_screen, mask ); } else @@ -3135,7 +3437,7 @@ void LLViewerWindow::updateUI() } // Update rectangles for the various toolbars - if (gOverlayBar && gNotifyBoxView && gConsole && gToolBar) + if (gOverlayBar && gNotifyBoxView && gConsole && gToolBar && gHUDView) { LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1); @@ -3193,40 +3495,6 @@ void LLViewerWindow::updateUI() gConsole->setRect(console_rect); } - mLastMousePoint = mCurrentMousePoint; - - // last ditch force of edit menu to selection manager - if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) - { - LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance(); - } - - if (gFloaterView->getCycleMode()) - { - // sync all floaters with their focus state - gFloaterView->highlightFocusedFloater(); - gSnapshotFloaterView->highlightFocusedFloater(); - if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0) - { - // control key no longer held down, finish cycle mode - gFloaterView->setCycleMode(FALSE); - - gFloaterView->syncFloaterTabOrder(); - } - else - { - // user holding down CTRL, don't update tab order of floaters - } - } - else - { - // update focused floater - gFloaterView->highlightFocusedFloater(); - gSnapshotFloaterView->highlightFocusedFloater(); - // make sure floater visible order is in sync with tab order - gFloaterView->syncFloaterTabOrder(); - } - static const LLCachedControl chat_bar_steals_focus("ChatBarStealsFocus",true); if (chat_bar_steals_focus && gChatBar @@ -3235,49 +3503,6 @@ void LLViewerWindow::updateUI() { gChatBar->startChat(NULL); } - - // cleanup unused selections when no modal dialogs are open - if (LLModalDialog::activeCount() == 0) - { - LLViewerParcelMgr::getInstance()->deselectUnused(); - } - - if (LLModalDialog::activeCount() == 0) - { - LLSelectMgr::getInstance()->deselectUnused(); - } - - // per frame picking - for tooltips and changing cursor over interactive objects - static S32 previous_x = -1; - static S32 previous_y = -1; - static BOOL mouse_moved_since_pick = FALSE; - - if ((previous_x != x) || (previous_y != y)) - mouse_moved_since_pick = TRUE; - - static const LLCachedControl picks_moving("PicksPerSecondMouseMoving",5.f); - static const LLCachedControl picks_stationary("PicksPerSecondMouseStationary",0.f); - if( !getCursorHidden() - // When in-world media is in focus, pick every frame so that browser mouse-overs, dragging scrollbars, etc. work properly. - && (LLViewerMediaFocus::getInstance()->getFocus() - || ((mouse_moved_since_pick) && (picks_moving > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_moving)) - || ((!mouse_moved_since_pick) && (picks_stationary > 0.0) && (mPickTimer.getElapsedTimeF32() > 1.0f / picks_stationary)))) - { - mouse_moved_since_pick = FALSE; - mPickTimer.reset(); - pickAsync(getCurrentMouseX(), getCurrentMouseY(), mask, hoverPickCallback, TRUE, TRUE); - } - - previous_x = x; - previous_y = y; - - return; -} - -/* static */ -void LLViewerWindow::hoverPickCallback(const LLPickInfo& pick_info) -{ - gViewerWindow->mHoverPick = pick_info; } void LLViewerWindow::updateMouseDelta() @@ -3324,6 +3549,81 @@ void LLViewerWindow::updateMouseDelta() mMouseVelocityStat.addValue(mouse_vel.magVec()); } +void LLViewerWindow::updateKeyboardFocus() +{ + if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) + { + gFocusMgr.setKeyboardFocus(NULL); + } + + // clean up current focus + LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (cur_focus) + { + if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) + { + gFocusMgr.releaseFocusIfNeeded(cur_focus); + + LLUICtrl* parent = cur_focus->getParentUICtrl(); + const LLUICtrl* focus_root = cur_focus->findRootMostFocusRoot(); + while(parent) + { + if (parent->isCtrl() + && (parent->hasTabStop() || parent == focus_root) + && !parent->getIsChrome() + && parent->isInVisibleChain() + && parent->isInEnabledChain()) + { + if (!parent->focusFirstItem()) + { + parent->setFocus(TRUE); + } + break; + } + parent = parent->getParentUICtrl(); + } + } + else if (cur_focus->isFocusRoot()) + { + // focus roots keep trying to delegate focus to their first valid descendant + // this assumes that focus roots are not valid focus holders on their own + cur_focus->focusFirstItem(); + } + } + // last ditch force of edit menu to selection manager + if (LLEditMenuHandler::gEditMenuHandler == NULL && LLSelectMgr::getInstance()->getSelection()->getObjectCount()) + { + LLEditMenuHandler::gEditMenuHandler = LLSelectMgr::getInstance(); + } + + if (gFloaterView->getCycleMode()) + { + // sync all floaters with their focus state + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + MASK mask = gKeyboard->currentMask(TRUE); + if ((mask & MASK_CONTROL) == 0) + { + // control key no longer held down, finish cycle mode + gFloaterView->setCycleMode(FALSE); + + gFloaterView->syncFloaterTabOrder(); + } + else + { + // user holding down CTRL, don't update tab order of floaters + } + } + else + { + // update focused floater + gFloaterView->highlightFocusedFloater(); + gSnapshotFloaterView->highlightFocusedFloater(); + // make sure floater visible order is in sync with tab order + gFloaterView->syncFloaterTabOrder(); + } +} + void LLViewerWindow::saveLastMouse(const LLCoordGL &point) { // Store last mouse location. @@ -4846,6 +5146,13 @@ void LLViewerWindow::setup3DViewport(S32 x_offset, S32 y_offset) glViewport(gGLViewport[0], gGLViewport[1], gGLViewport[2], gGLViewport[3]); } +void LLViewerWindow::revealIntroPanel() +{ + if (mProgressView) + { + mProgressView->revealIntroPanel(); + } +} void LLViewerWindow::setShowProgress(const BOOL show) { @@ -4855,12 +5162,11 @@ void LLViewerWindow::setShowProgress(const BOOL show) } } -void LLViewerWindow::moveProgressViewToFront() +void LLViewerWindow::setStartupComplete() { - if (mProgressView && mRootView) + if (mProgressView) { - mRootView->removeChild(mProgressView); - mRootView->addChild(mProgressView); + mProgressView->setStartupComplete(); } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 2eb84e8db..9d47812d9 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -163,7 +163,8 @@ public: /*virtual*/ BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask); /*virtual*/ BOOL handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask); /*virtual*/ BOOL handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask); - /*virtual*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask); + /*virtual*/ LLWindowCallbacks::DragNDropResult handleDragNDrop(LLWindow *window, LLCoordGL pos, MASK mask, LLWindowCallbacks::DragNDropAction action, std::string data); + void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask); /*virtual*/ void handleMouseLeave(LLWindow *window); /*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y); /*virtual*/ void handleFocus(LLWindow *window); @@ -273,17 +274,20 @@ public: void setShowProgress(const BOOL show); BOOL getShowProgress() const; - void moveProgressViewToFront(); void setProgressString(const std::string& string); void setProgressPercent(const F32 percent); void setProgressMessage(const std::string& msg); void setProgressCancelButtonVisible( BOOL b, const std::string& label = LLStringUtil::null ); LLProgressView *getProgressView() const; + void revealIntroPanel(); + void setStartupComplete(); void updateObjectUnderCursor(); - void updateUI(); // Once per frame, update UI based on mouse position + void updateUI(); // Once per frame, update UI based on mouse position, calls following update* functions + void updateLayout(); void updateMouseDelta(); + void updateKeyboardFocus(); BOOL handleKey(KEY key, MASK mask); void handleScrollWheel (S32 clicks); @@ -430,12 +434,12 @@ protected: BOOL mMouseInWindow; // True if the mouse is over our window or if we have captured the mouse. BOOL mFocusCycleMode; + typedef std::set > view_handle_set_t; + view_handle_set_t mMouseHoverViews; // Variables used for tool override switching based on modifier keys. JC MASK mLastMask; // used to detect changes in modifier mask LLTool* mToolStored; // the tool we're overriding - BOOL mSuppressToolbox; // sometimes hide the toolbox, despite - // having a camera tool selected BOOL mHideCursorPermanent; // true during drags, mouselook BOOL mCursorHidden; LLPickInfo mLastPick; @@ -462,6 +466,9 @@ protected: static std::string sSnapshotDir; static std::string sMovieBaseName; + + // Object temporarily hovered over while dragging + LLPointer mDragHoveredObject; }; class LLBottomPanel : public LLPanel diff --git a/indra/newview/llvoicecallhandler.cpp b/indra/newview/llvoicecallhandler.cpp new file mode 100644 index 000000000..0f68f7972 --- /dev/null +++ b/indra/newview/llvoicecallhandler.cpp @@ -0,0 +1,69 @@ + /** + * @file llvoicecallhandler.cpp + * @brief slapp to handle avatar to avatar voice call. + * + * $LicenseInfo:firstyear=2001&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 "llcommandhandler.h" +#include "llavataractions.h" +//#include "llnotificationsutil.h" +//#include "llui.h" + +class LLVoiceCallAvatarHandler : public LLCommandHandler +{ +public: + // requires trusted browser to trigger + LLVoiceCallAvatarHandler() : LLCommandHandler("voicecallavatar", UNTRUSTED_THROTTLE) + { + } + + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web) + { + /*if (!LLUI::sSettingGroups["config"]->getBOOL("EnableVoiceCall")) + { + LLNotificationsUtil::add("NoVoiceCall", LLSD(), LLSD(), std::string("SwitchToStandardSkinAndQuit")); + return true; + }*/ + + //Make sure we have some parameters + if (params.size() == 0) + { + return false; + } + + //Get the ID + LLUUID id; + if (!id.set( params[0], FALSE )) + { + return false; + } + + //instigate call with this avatar + LLAvatarActions::startCall( id ); + return true; + } +}; + +LLVoiceCallAvatarHandler gVoiceCallAvatarHandler; + diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index 0184e1dec..4ce04b028 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -29,6 +29,7 @@ #include "llagent.h" #include "llhttpclient.h" #include "llimview.h" +#include "llnotifications.h" #include "llnotificationsutil.h" #include "llvoicechannel.h" diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp index a56b4c5af..d74c3a061 100644 --- a/indra/newview/llvoicevivox.cpp +++ b/indra/newview/llvoicevivox.cpp @@ -5320,7 +5320,9 @@ void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) bool LLVivoxVoiceClient::voiceEnabled() { - return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice"); + static const LLCachedControl enable_voice_chat("EnableVoiceChat",true); + static const LLCachedControl cmdline_disable_voice("CmdLineDisableVoice",false); + return enable_voice_chat && !cmdline_disable_voice; } void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled) diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index b3faa57ac..f16436493 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -363,6 +363,12 @@ BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable) facep->mCenterLocal = part->mPosAgent; facep->setFaceColor(part->mColor); facep->setTexture(part->mImagep); + + //check if this particle texture is replaced by a parcel media texture. + if(part->mImagep.notNull() && part->mImagep->hasParcelMedia()) + { + part->mImagep->getParcelMedia()->addMediaToFace(facep) ; + } mPixelArea = tot_area * pixel_meter_ratio; const F32 area_scale = 10.f; // scale area to increase priority a bit diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index dbf9e1295..ae4ea1e9d 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -35,6 +35,8 @@ #include "llvovolume.h" +#include + #include "llviewercontrol.h" #include "lldir.h" #include "llflexibleobject.h" @@ -47,6 +49,7 @@ #include "llvolumemessage.h" #include "material_codes.h" #include "message.h" +#include "llpluginclassmedia.h" // for code in the mediaEvent handler #include "object_flags.h" #include "llagentconstants.h" #include "lldrawable.h" @@ -69,8 +72,11 @@ #include "pipeline.h" #include "llsdutil.h" #include "llmatrix4a.h" +#include "llmediaentry.h" +#include "llmediadataclient.h" #include "llmeshrepository.h" #include "llagent.h" +#include "llviewermediafocus.h" #include "lldatapacker.h" #include "llviewershadermgr.h" #include "llvoavatar.h" @@ -94,10 +100,114 @@ F32 LLVOVolume::sDistanceFactor = 1.0f; S32 LLVOVolume::sNumLODChanges = 0; S32 LLVOVolume::mRenderComplexity_last = 0; S32 LLVOVolume::mRenderComplexity_current = 0; +LLPointer LLVOVolume::sObjectMediaClient = NULL; +LLPointer LLVOVolume::sObjectMediaNavigateClient = NULL; + static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles"); static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); static LLFastTimer::DeclareTimer FTM_VOLUME_TEXTURES("Volume Textures"); +// Implementation class of LLMediaDataClientObject. See llmediadataclient.h +class LLMediaDataClientObjectImpl : public LLMediaDataClientObject +{ +public: + LLMediaDataClientObjectImpl(LLVOVolume *obj, bool isNew) : mObject(obj), mNew(isNew) + { + mObject->addMDCImpl(); + } + ~LLMediaDataClientObjectImpl() + { + mObject->removeMDCImpl(); + } + + virtual U8 getMediaDataCount() const + { return mObject->getNumTEs(); } + + virtual LLSD getMediaDataLLSD(U8 index) const + { + LLSD result; + LLTextureEntry *te = mObject->getTE(index); + if (NULL != te) + { + llassert((te->getMediaData() != NULL) == te->hasMedia()); + if (te->getMediaData() != NULL) + { + result = te->getMediaData()->asLLSD(); + // XXX HACK: workaround bug in asLLSD() where whitelist is not set properly + // See DEV-41949 + if (!result.has(LLMediaEntry::WHITELIST_KEY)) + { + result[LLMediaEntry::WHITELIST_KEY] = LLSD::emptyArray(); + } + } + } + return result; + } + virtual bool isCurrentMediaUrl(U8 index, const std::string &url) const + { + LLTextureEntry *te = mObject->getTE(index); + if (te) + { + if (te->getMediaData()) + { + return (te->getMediaData()->getCurrentURL() == url); + } + } + return url.empty(); + } + + virtual LLUUID getID() const + { return mObject->getID(); } + + virtual void mediaNavigateBounceBack(U8 index) + { mObject->mediaNavigateBounceBack(index); } + + virtual bool hasMedia() const + { return mObject->hasMedia(); } + + virtual void updateObjectMediaData(LLSD const &data, const std::string &version_string) + { mObject->updateObjectMediaData(data, version_string); } + + virtual F64 getMediaInterest() const + { + F64 interest = mObject->getTotalMediaInterest(); + if (interest < (F64)0.0) + { + // media interest not valid yet, try pixel area + interest = mObject->getPixelArea(); + // HACK: force recalculation of pixel area if interest is the "magic default" of 1024. + if (interest == 1024.f) + { + const_cast(static_cast(mObject))->setPixelAreaAndAngle(gAgent); + interest = mObject->getPixelArea(); + } + } + return interest; + } + + virtual bool isInterestingEnough() const + { + return LLViewerMedia::isInterestingEnough(mObject, getMediaInterest()); + } + + virtual std::string getCapabilityUrl(const std::string &name) const + { return mObject->getRegion()->getCapability(name); } + + virtual bool isDead() const + { return mObject->isDead(); } + + virtual U32 getMediaVersion() const + { return LLTextureEntry::getVersionFromMediaVersionString(mObject->getMediaURL()); } + + virtual bool isNew() const + { return mNew; } + +private: + LLPointer mObject; + bool mNew; +}; + + LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), mVolumeImpl(NULL) @@ -115,7 +225,11 @@ LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *re mLODChanged = FALSE; mSculptChanged = FALSE; mSpotLightPriority = 0.f; + + mMediaImplList.resize(getNumTEs()); + mLastFetchedMediaVersion = -1; mIndexInTex = 0; + mMDCImplCount = 0; } LLVOVolume::~LLVOVolume() @@ -124,12 +238,35 @@ LLVOVolume::~LLVOVolume() mTextureAnimp = NULL; delete mVolumeImpl; mVolumeImpl = NULL; + + if(!mMediaImplList.empty()) + { + for(U32 i = 0 ; i < mMediaImplList.size() ; i++) + { + if(mMediaImplList[i].notNull()) + { + mMediaImplList[i]->removeObject(this) ; + } + } + } } void LLVOVolume::markDead() { if (!mDead) { + if(getMDCImplCount() > 0) + { + LLMediaDataClientObject::ptr_t obj = new LLMediaDataClientObjectImpl(const_cast(this), false); + if (sObjectMediaClient) sObjectMediaClient->removeFromQueue(obj); + if (sObjectMediaNavigateClient) sObjectMediaNavigateClient->removeFromQueue(obj); + } + + // Detach all media impls from this object + for(U32 i = 0 ; i < mMediaImplList.size() ; i++) + { + removeMediaImpl(i); + } if (mSculptTexture.notNull()) { @@ -144,11 +281,26 @@ void LLVOVolume::markDead() // static void LLVOVolume::initClass() { + // gSavedSettings better be around + if (gSavedSettings.getBOOL("PrimMediaMasterEnabled")) + { + const F32 queue_timer_delay = gSavedSettings.getF32("PrimMediaRequestQueueDelay"); + const F32 retry_timer_delay = gSavedSettings.getF32("PrimMediaRetryTimerDelay"); + const U32 max_retries = gSavedSettings.getU32("PrimMediaMaxRetries"); + const U32 max_sorted_queue_size = gSavedSettings.getU32("PrimMediaMaxSortedQueueSize"); + const U32 max_round_robin_queue_size = gSavedSettings.getU32("PrimMediaMaxRoundRobinQueueSize"); + sObjectMediaClient = new LLObjectMediaDataClient(queue_timer_delay, retry_timer_delay, max_retries, + max_sorted_queue_size, max_round_robin_queue_size); + sObjectMediaNavigateClient = new LLObjectMediaNavigateClient(queue_timer_delay, retry_timer_delay, + max_retries, max_sorted_queue_size, max_round_robin_queue_size); + } } // static void LLVOVolume::cleanupClass() { + sObjectMediaClient = NULL; + sObjectMediaNavigateClient = NULL; } U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, @@ -157,6 +309,7 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, LLDataPacker *dp) { LLColor4U color; + const S32 teDirtyBits = (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR|TEM_CHANGE_MEDIA); // Do base class updates... U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp); @@ -224,10 +377,16 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, // // Unpack texture entry data // - if (unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR)) + + S32 result = unpackTEMessage(mesgsys, _PREHASH_ObjectData, (S32) block_num); + if (result & teDirtyBits) { updateTEData(); } + if (result & TEM_CHANGE_MEDIA) + { + retval |= MEDIA_FLAGS_CHANGED; + } } else { @@ -262,9 +421,16 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, llwarns << "Bogus TE data in " << getID() << llendl; } - else if (res2 & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR)) + else { - updateTEData(); + if (res2 & teDirtyBits) + { + updateTEData(); + } + if (res2 & TEM_CHANGE_MEDIA) + { + retval |= MEDIA_FLAGS_CHANGED; + } } U32 value = dp->getPassFlags(); @@ -302,13 +468,38 @@ U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys, U8 tdpbuffer[1024]; LLDataPackerBinaryBuffer tdp(tdpbuffer, 1024); mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureEntry, tdpbuffer, 0, block_num); - if ( unpackTEMessage(tdp) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR)) + S32 result = unpackTEMessage(tdp); + if (result & teDirtyBits) { updateTEData(); } + if (result & TEM_CHANGE_MEDIA) + { + retval |= MEDIA_FLAGS_CHANGED; + } } } } + if (retval & (MEDIA_URL_REMOVED | MEDIA_URL_ADDED | MEDIA_URL_UPDATED | MEDIA_FLAGS_CHANGED)) + { + // If only the media URL changed, and it isn't a media version URL, + // ignore it + if ( ! ( retval & (MEDIA_URL_ADDED | MEDIA_URL_UPDATED) && + mMedia && ! mMedia->mMediaURL.empty() && + ! LLTextureEntry::isMediaVersionString(mMedia->mMediaURL) ) ) + { + // If the media changed at all, request new media data + LL_DEBUGS("MediaOnAPrim") << "Media update: " << getID() << ": retval=" << retval << " Media URL: " << + ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; + requestMediaDataUpdate(retval & MEDIA_FLAGS_CHANGED); + } + else { + LL_INFOS("MediaOnAPrim") << "Ignoring media update for: " << getID() << " Media URL: " << + ((mMedia) ? mMedia->mMediaURL : std::string("")) << LL_ENDL; + } + } + // ...and clean up any media impls + cleanUpMediaImpls(); return retval; } @@ -1187,6 +1378,20 @@ void LLVOVolume::regenFaces() facep->setTEOffset(i); facep->setTexture(getTEImage(i)); facep->setViewerObject(this); + + // If the face had media on it, this will have broken the link between the LLViewerMediaTexture and the face. + // Re-establish the link. + if((int)mMediaImplList.size() > i) + { + if(mMediaImplList[i]) + { + LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[i]->getMediaTextureID()) ; + if(media_tex) + { + media_tex->addMediaToFace(facep) ; + } + } + } } if (!count_changed) @@ -1540,6 +1745,58 @@ BOOL LLVOVolume::isRootEdit() const return TRUE; } +//virtual +void LLVOVolume::setNumTEs(const U8 num_tes) +{ + const U8 old_num_tes = getNumTEs() ; + + if(old_num_tes && old_num_tes < num_tes) //new faces added + { + LLViewerObject::setNumTEs(num_tes) ; + + if(mMediaImplList.size() >= old_num_tes && mMediaImplList[old_num_tes -1].notNull())//duplicate the last media textures if exists. + { + mMediaImplList.resize(num_tes) ; + const LLTextureEntry* te = getTE(old_num_tes - 1) ; + for(U8 i = old_num_tes; i < num_tes ; i++) + { + setTE(i, *te) ; + mMediaImplList[i] = mMediaImplList[old_num_tes -1] ; + } + mMediaImplList[old_num_tes -1]->setUpdated(TRUE) ; + } + } + else if(old_num_tes > num_tes && mMediaImplList.size() > num_tes) //old faces removed + { + U8 end = mMediaImplList.size() ; + for(U8 i = num_tes; i < end ; i++) + { + removeMediaImpl(i) ; + } + mMediaImplList.resize(num_tes) ; + + LLViewerObject::setNumTEs(num_tes) ; + } + else + { + LLViewerObject::setNumTEs(num_tes) ; + } + + return ; +} + +//virtual +void LLVOVolume::changeTEImage(S32 index, LLViewerTexture* imagep) +{ + BOOL changed = (mTEImages[index] != imagep); + LLViewerObject::changeTEImage(index, imagep); + if (changed) + { + gPipeline.markTextured(mDrawable); + mFaceMappingChanged = TRUE; + } +} + void LLVOVolume::setTEImage(const U8 te, LLViewerTexture *imagep) { BOOL changed = (mTEImages[te] != imagep); @@ -1727,6 +1984,503 @@ void LLVOVolume::updateTEData() }*/ } +bool LLVOVolume::hasMedia() const +{ + bool result = false; + const U8 numTEs = getNumTEs(); + for (U8 i = 0; i < numTEs; i++) + { + const LLTextureEntry* te = getTE(i); + if(te->hasMedia()) + { + result = true; + break; + } + } + return result; +} + +LLVector3 LLVOVolume::getApproximateFaceNormal(U8 face_id) +{ + LLVolume* volume = getVolume(); + LLVector4a result; + result.clear(); + + LLVector3 ret; + + if (volume && face_id < volume->getNumVolumeFaces()) + { + const LLVolumeFace& face = volume->getVolumeFace(face_id); + for (S32 i = 0; i < (S32)face.mNumVertices; ++i) + { + result.add(face.mNormals[i]); + } + + LLVector3 ret(result.getF32ptr()); + ret = volumeDirectionToAgent(ret); + ret.normVec(); + } + + return ret; +} + +void LLVOVolume::requestMediaDataUpdate(bool isNew) +{ + if (sObjectMediaClient) + sObjectMediaClient->fetchMedia(new LLMediaDataClientObjectImpl(this, isNew)); +} + +bool LLVOVolume::isMediaDataBeingFetched() const +{ + // I know what I'm doing by const_casting this away: this is just + // a wrapper class that is only going to do a lookup. + return (sObjectMediaClient) ? sObjectMediaClient->isInQueue(new LLMediaDataClientObjectImpl(const_cast(this), false)) : false; +} + +void LLVOVolume::cleanUpMediaImpls() +{ + // Iterate through our TEs and remove any Impls that are no longer used + const U8 numTEs = getNumTEs(); + for (U8 i = 0; i < numTEs; i++) + { + const LLTextureEntry* te = getTE(i); + if( ! te->hasMedia()) + { + // Delete the media IMPL! + removeMediaImpl(i) ; + } + } +} + +void LLVOVolume::updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version) +{ + // media_data_array is an array of media entry maps + // media_version is the version string in the response. + U32 fetched_version = LLTextureEntry::getVersionFromMediaVersionString(media_version); + + // Only update it if it is newer! + if ( (S32)fetched_version > mLastFetchedMediaVersion) + { + mLastFetchedMediaVersion = fetched_version; + //llinfos << "updating:" << this->getID() << " " << ll_pretty_print_sd(media_data_array) << llendl; + + LLSD::array_const_iterator iter = media_data_array.beginArray(); + LLSD::array_const_iterator end = media_data_array.endArray(); + U8 texture_index = 0; + for (; iter != end; ++iter, ++texture_index) + { + syncMediaData(texture_index, *iter, false/*merge*/, false/*ignore_agent*/); + } + } +} + +void LLVOVolume::syncMediaData(S32 texture_index, const LLSD &media_data, bool merge, bool ignore_agent) +{ + if(mDead) + { + // If the object has been marked dead, don't process media updates. + return; + } + + LLTextureEntry *te = getTE(texture_index); + if(!te) + { + return ; + } + + LL_DEBUGS("MediaOnAPrim") << "BEFORE: texture_index = " << texture_index + << " hasMedia = " << te->hasMedia() << " : " + << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl; + + std::string previous_url; + LLMediaEntry* mep = te->getMediaData(); + if(mep) + { + // Save the "current url" from before the update so we can tell if + // it changes. + previous_url = mep->getCurrentURL(); + } + + if (merge) + { + te->mergeIntoMediaData(media_data); + } + else { + // XXX Question: what if the media data is undefined LLSD, but the + // update we got above said that we have media flags?? Here we clobber + // that, assuming the data from the service is more up-to-date. + te->updateMediaData(media_data); + } + + mep = te->getMediaData(); + if(mep) + { + bool update_from_self = false; + if (!ignore_agent) + { + LLUUID updating_agent = LLTextureEntry::getAgentIDFromMediaVersionString(getMediaURL()); + update_from_self = (updating_agent == gAgent.getID()); + } + viewer_media_t media_impl = LLViewerMedia::updateMediaImpl(mep, previous_url, update_from_self); + + addMediaImpl(media_impl, texture_index) ; + } + else + { + removeMediaImpl(texture_index); + } + + LL_DEBUGS("MediaOnAPrim") << "AFTER: texture_index = " << texture_index + << " hasMedia = " << te->hasMedia() << " : " + << ((NULL == te->getMediaData()) ? "NULL MEDIA DATA" : ll_pretty_print_sd(te->getMediaData()->asLLSD())) << llendl; +} + +void LLVOVolume::mediaNavigateBounceBack(U8 texture_index) +{ + // Find the media entry for this navigate + const LLMediaEntry* mep = NULL; + viewer_media_t impl = getMediaImpl(texture_index); + LLTextureEntry *te = getTE(texture_index); + if(te) + { + mep = te->getMediaData(); + } + + if (mep && impl) + { + std::string url = mep->getCurrentURL(); + // Look for a ":", if not there, assume "http://" + if (!url.empty() && std::string::npos == url.find(':')) + { + url = "http://" + url; + } + // If the url we're trying to "bounce back" to is either empty or not + // allowed by the whitelist, try the home url. If *that* doesn't work, + // set the media as failed and unload it + if (url.empty() || !mep->checkCandidateUrl(url)) + { + url = mep->getHomeURL(); + // Look for a ":", if not there, assume "http://" + if (!url.empty() && std::string::npos == url.find(':')) + { + url = "http://" + url; + } + } + if (url.empty() || !mep->checkCandidateUrl(url)) + { + // The url to navigate back to is not good, and we have nowhere else + // to go. + LL_WARNS("MediaOnAPrim") << "FAILED to bounce back URL \"" << url << "\" -- unloading impl" << LL_ENDL; + impl->setMediaFailed(true); + } + else { + // Okay, navigate now + LL_INFOS("MediaOnAPrim") << "bouncing back to URL: " << url << LL_ENDL; + impl->navigateTo(url, "", false, true); + } + } +} + +bool LLVOVolume::hasMediaPermission(const LLMediaEntry* media_entry, MediaPermType perm_type) +{ + // NOTE: This logic ALMOST duplicates the logic in the server (in particular, in llmediaservice.cpp). + if (NULL == media_entry ) return false; // XXX should we assert here? + + // The agent has permissions if: + // - world permissions are on, or + // - group permissions are on, and agent_id is in the group, or + // - agent permissions are on, and agent_id is the owner + + // *NOTE: We *used* to check for modify permissions here (i.e. permissions were + // granted if permModify() was true). However, this doesn't make sense in the + // viewer: we don't want to show controls or allow interaction if the author + // has deemed it so. See DEV-42115. + + U8 media_perms = (perm_type == MEDIA_PERM_INTERACT) ? media_entry->getPermsInteract() : media_entry->getPermsControl(); + + // World permissions + if (0 != (media_perms & LLMediaEntry::PERM_ANYONE)) + { + return true; + } + + // Group permissions + else if (0 != (media_perms & LLMediaEntry::PERM_GROUP)) + { + LLPermissions* obj_perm = LLSelectMgr::getInstance()->findObjectPermissions(this); + if (obj_perm && gAgent.isInGroup(obj_perm->getGroup())) + { + return true; + } + } + + // Owner permissions + else if (0 != (media_perms & LLMediaEntry::PERM_OWNER) && permYouOwner()) + { + return true; + } + + return false; + +} + +void LLVOVolume::mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location) +{ + bool block_navigation = false; + // FIXME: if/when we allow the same media impl to be used by multiple faces, the logic here will need to be fixed + // to deal with multiple face indices. + int face_index = getFaceIndexWithMediaImpl(impl, -1); + + // Find the media entry for this navigate + LLMediaEntry* mep = NULL; + LLTextureEntry *te = getTE(face_index); + if(te) + { + mep = te->getMediaData(); + } + + if(mep) + { + if(!mep->checkCandidateUrl(new_location)) + { + block_navigation = true; + } + if (!block_navigation && !hasMediaPermission(mep, MEDIA_PERM_INTERACT)) + { + block_navigation = true; + } + } + else + { + LL_WARNS("MediaOnAPrim") << "Couldn't find media entry!" << LL_ENDL; + } + + if(block_navigation) + { + LL_INFOS("MediaOnAPrim") << "blocking navigate to URI " << new_location << LL_ENDL; + + // "bounce back" to the current URL from the media entry + mediaNavigateBounceBack(face_index); + } + else if (sObjectMediaNavigateClient) + { + + LL_DEBUGS("MediaOnAPrim") << "broadcasting navigate with URI " << new_location << LL_ENDL; + + sObjectMediaNavigateClient->navigate(new LLMediaDataClientObjectImpl(this, false), face_index, new_location); + } +} + +void LLVOVolume::mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event) +{ + switch(event) + { + + case LLViewerMediaObserver::MEDIA_EVENT_LOCATION_CHANGED: + { + switch(impl->getNavState()) + { + case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED: + { + // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back. + mediaNavigated(impl, plugin, plugin->getLocation()); + } + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_FIRST_LOCATION_CHANGED_SPURIOUS: + // This navigate didn't change the current URL. + LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL; + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_FIRST_LOCATION_CHANGED: + // This is the first location changed event after the start of a server-directed nav. Don't broadcast it. + LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL; + break; + + default: + // This is a subsequent location-changed due to a redirect. Don't broadcast. + LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (redirect)" << LL_ENDL; + break; + } + } + break; + + case LLViewerMediaObserver::MEDIA_EVENT_NAVIGATE_COMPLETE: + { + switch(impl->getNavState()) + { + case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED: + { + // This is the first location changed event after the start of a non-server-directed nav. It may need to be broadcast or bounced back. + mediaNavigated(impl, plugin, plugin->getNavigateURI()); + } + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_COMPLETE_BEFORE_LOCATION_CHANGED_SPURIOUS: + // This navigate didn't change the current URL. + LL_DEBUGS("MediaOnAPrim") << " NOT broadcasting navigate (spurious)" << LL_ENDL; + break; + + case LLViewerMediaImpl::MEDIANAVSTATE_SERVER_COMPLETE_BEFORE_LOCATION_CHANGED: + // This is the the navigate complete event from a server-directed nav. Don't broadcast it. + LL_INFOS("MediaOnAPrim") << " NOT broadcasting navigate (server-directed)" << LL_ENDL; + break; + + default: + // For all other states, the navigate should have been handled by LOCATION_CHANGED events already. + break; + } + } + break; + + default: + break; + } + +} + +void LLVOVolume::sendMediaDataUpdate() +{ + if (sObjectMediaClient) + sObjectMediaClient->updateMedia(new LLMediaDataClientObjectImpl(this, false)); +} + +void LLVOVolume::removeMediaImpl(S32 texture_index) +{ + if(mMediaImplList.size() <= (U32)texture_index || mMediaImplList[texture_index].isNull()) + { + return ; + } + + //make the face referencing to mMediaImplList[texture_index] to point back to the old texture. + if(mDrawable && texture_index < mDrawable->getNumFaces()) + { + LLFace* facep = mDrawable->getFace(texture_index) ; + if(facep) + { + LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; + if(media_tex) + { + media_tex->removeMediaFromFace(facep) ; + } + } + } + + //check if some other face(s) of this object reference(s)to this media impl. + S32 i ; + S32 end = (S32)mMediaImplList.size() ; + for(i = 0; i < end ; i++) + { + if( i != texture_index && mMediaImplList[i] == mMediaImplList[texture_index]) + { + break ; + } + } + + if(i == end) //this object does not need this media impl. + { + mMediaImplList[texture_index]->removeObject(this) ; + } + + mMediaImplList[texture_index] = NULL ; + return ; +} + +void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) +{ + if((S32)mMediaImplList.size() < texture_index + 1) + { + mMediaImplList.resize(texture_index + 1) ; + } + + if(mMediaImplList[texture_index].notNull()) + { + if(mMediaImplList[texture_index] == media_impl) + { + return ; + } + + removeMediaImpl(texture_index) ; + } + + mMediaImplList[texture_index] = media_impl; + media_impl->addObject(this) ; + + //add the face to show the media if it is in playing + if(mDrawable) + { + LLFace* facep(NULL); + if( texture_index < mDrawable->getNumFaces() ) + { + facep = mDrawable->getFace(texture_index) ; + } + + if(facep) + { + LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; + if(media_tex) + { + media_tex->addMediaToFace(facep) ; + } + } + else //the face is not available now, start media on this face later. + { + media_impl->setUpdated(TRUE) ; + } + } + return ; +} + +viewer_media_t LLVOVolume::getMediaImpl(U8 face_id) const +{ + if(mMediaImplList.size() > face_id) + { + return mMediaImplList[face_id]; + } + return NULL; +} + +F64 LLVOVolume::getTotalMediaInterest() const +{ + // If this object is currently focused, this object has "high" interest + if (LLViewerMediaFocus::getInstance()->getFocusedObjectID() == getID()) + return F64_MAX; + + F64 interest = (F64)-1.0; // means not interested; + + // If this object is selected, this object has "high" interest, but since + // there can be more than one, we still add in calculated impl interest + // XXX Sadly, 'contains()' doesn't take a const :( + if (LLSelectMgr::getInstance()->getSelection()->contains(const_cast(this))) + interest = F64_MAX / 2.0; + + int i = 0; + const int end = getNumTEs(); + for ( ; i < end; ++i) + { + const viewer_media_t &impl = getMediaImpl(i); + if (!impl.isNull()) + { + if (interest == (F64)-1.0) interest = (F64)0.0; + interest += impl->getInterest(); + } + } + return interest; +} + +S32 LLVOVolume::getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id) +{ + S32 end = (S32)mMediaImplList.size() ; + for(S32 face_id = start_face_id + 1; face_id < end; face_id++) + { + if(mMediaImplList[face_id] == media_impl) + { + return face_id ; + } + } + return -1 ; +} + //---------------------------------------------------------------------------- void LLVOVolume::setLightTextureID(LLUUID id) @@ -2393,10 +3147,10 @@ U32 LLVOVolume::getRenderCost(texture_cost_t &textures) const { invisi = 1; } - /*if (face->hasMedia()) + if (face->hasMedia()) { media_faces++; - }*/ + } if (te) { diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h index a83a33680..86ebdf584 100644 --- a/indra/newview/llvovolume.h +++ b/indra/newview/llvovolume.h @@ -35,6 +35,7 @@ #include "llviewerobject.h" #include "llviewertexture.h" +#include "llviewermedia.h" #include "llframetimer.h" #include "llapr.h" #include "m3math.h" // LLMatrix3 @@ -44,9 +45,13 @@ class LLViewerTextureAnim; class LLDrawPool; class LLSelectNode; +class LLObjectMediaDataClient; +class LLObjectMediaNavigateClient; class LLVOAvatar; class LLMeshSkinInfo; +typedef std::vector media_list_t; + enum LLVolumeInterfaceType { INTERFACE_FLEXIBLE = 1, @@ -174,6 +179,8 @@ public: /*virtual*/ void setScale(const LLVector3 &scale, BOOL damped); + /*virtual*/ void changeTEImage(S32 index, LLViewerTexture* new_image) ; + /*virtual*/ void setNumTEs(const U8 num_tes); /*virtual*/ void setTEImage(const U8 te, LLViewerTexture *imagep); /*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid); /*virtual*/ S32 setTEColor(const U8 te, const LLColor3 &color); @@ -255,6 +262,41 @@ public: BOOL canBeFlexible() const; BOOL setIsFlexible(BOOL is_flexible); + + // Functions that deal with media, or media navigation + + // Update this object's media data with the given media data array + // (typically this is only called upon a response from a server request) + void updateObjectMediaData(const LLSD &media_data_array, const std::string &media_version); + + // Bounce back media at the given index to its current URL (or home URL, if current URL is empty) + void mediaNavigateBounceBack(U8 texture_index); + + // Returns whether or not this object has permission to navigate or control + // the given media entry + enum MediaPermType { + MEDIA_PERM_INTERACT, MEDIA_PERM_CONTROL + }; + bool hasMediaPermission(const LLMediaEntry* media_entry, MediaPermType perm_type); + + void mediaNavigated(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, std::string new_location); + void mediaEvent(LLViewerMediaImpl *impl, LLPluginClassMedia* plugin, LLViewerMediaObserver::EMediaEvent event); + + + // Sync the given media data with the impl and the given te + void syncMediaData(S32 te, const LLSD &media_data, bool merge, bool ignore_agent); + + // Send media data update to the simulator. + void sendMediaDataUpdate(); + + viewer_media_t getMediaImpl(U8 face_id) const; + S32 getFaceIndexWithMediaImpl(const LLViewerMediaImpl* media_impl, S32 start_face_id); + F64 getTotalMediaInterest() const; + + bool hasMedia() const; + + LLVector3 getApproximateFaceNormal(U8 face_id); + // tag: vaa emerald local_asset_browser void setSculptChanged(BOOL has_changed) { mSculptChanged = has_changed; } @@ -263,6 +305,12 @@ public: // Returns 'true' iff the media data for this object is in flight bool isMediaDataBeingFetched() const; + // Returns the "last fetched" media version, or -1 if not fetched yet + S32 getLastFetchedMediaVersion() const { return mLastFetchedMediaVersion; } + + void addMDCImpl() { ++mMDCImplCount; } + void removeMDCImpl() { --mMDCImplCount; } + S32 getMDCImplCount() { return mMDCImplCount; } //rigged volume update (for raycasting) @@ -288,10 +336,16 @@ protected: // stats tracking for render complexity static S32 mRenderComplexity_last; static S32 mRenderComplexity_current; + + void requestMediaDataUpdate(bool isNew); + void cleanUpMediaImpls(); + void addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) ; + void removeMediaImpl(S32 texture_index) ; public: static S32 getRenderComplexityMax() {return mRenderComplexity_last;} static void updateRenderComplexity(); + LLViewerTextureAnim *mTextureAnimp; U8 mTexAnimMode; private: @@ -311,7 +365,10 @@ private: LLVolumeInterface *mVolumeImpl; LLPointer mSculptTexture; LLPointer mLightTexture; + media_list_t mMediaImplList; + S32 mLastFetchedMediaVersion; // as fetched from the server, starts as -1 S32 mIndexInTex; + S32 mMDCImplCount; LLPointer mRiggedVolume; @@ -321,6 +378,9 @@ public: static F32 sLODFactor; // LOD scale factor static F32 sDistanceFactor; // LOD distance factor + static LLPointer sObjectMediaClient; + static LLPointer sObjectMediaNavigateClient; + protected: static S32 sNumLODChanges; diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index a39530724..2e05207cc 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -521,7 +521,7 @@ bool LLWaterParamManager::removeParamSet(const std::string& name, bool delete_fr bool LLWaterParamManager::isSystemPreset(const std::string& preset_name) const { // *TODO: file system access is excessive here. - return gDirUtilp->fileExists(getSysDir() + LLWeb::curlEscape(preset_name) + ".xml"); + return gDirUtilp->fileExists(getSysDir() + LLURI::escape(preset_name) + ".xml"); } void LLWaterParamManager::getPresetNames(preset_name_list_t& presets) const diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 4dbcfc578..514db939c 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -40,43 +40,117 @@ #include "llagent.h" #include "llappviewer.h" -#include "llfloatermediabrowser.h" +#include "llfloaterwebcontent.h" #include "llparcel.h" +#include "llsd.h" +#include "llui.h" +#include "lluri.h" #include "llviewercontrol.h" +#include "llviewermedia.h" #include "llviewernetwork.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llviewerwindow.h" +#include "llnotificationsutil.h" +#include "llalertdialog.h" #include "sgversion.h" +bool on_load_url_external_response(const LLSD& notification, const LLSD& response, bool async ); + +class URLLoader : public LLAlertDialog::URLLoader +{ + virtual void load(const std::string& url , bool force_open_externally) + { + if (force_open_externally) + { + LLWeb::loadURLExternal(url); + } + else + { + LLWeb::loadURL(url); + } + } +}; +static URLLoader sAlertURLLoader; + + // static void LLWeb::initClass() { LLAlertDialog::setURLLoader(&sAlertURLLoader); } + + + // static -void LLWeb::loadURL(const std::string& url) +void LLWeb::loadURL(const std::string& url, const std::string& target, const std::string& uuid) { - if (gSavedSettings.getBOOL("UseExternalBrowser")) + if(target == "_internal") + { + // Force load in the internal browser, as if with a blank target. + loadURLInternal(url, "", uuid); + } + else if (gSavedSettings.getBOOL("UseExternalBrowser") || (target == "_external")) { loadURLExternal(url); } else { - LLFloaterMediaBrowser::showInstance(url); + loadURLInternal(url, target, uuid); } } - // static -void LLWeb::loadURLExternal(const std::string& url) +// Explicitly open a Web URL using the Web content floater +void LLWeb::loadURLInternal(const std::string &url, const std::string& target, const std::string& uuid) { - std::string escaped_url = escapeURL(url); - gViewerWindow->getWindow()->spawnWebBrowser(escaped_url,true); + LLFloaterWebContent::Params p; + p.url(url).target(target).id(uuid); + LLFloaterWebContent::showInstance("web_content", p); } +// static +void LLWeb::loadURLExternal(const std::string& url, const std::string& uuid) +{ + loadURLExternal(url, true, uuid); +} + +// static +void LLWeb::loadURLExternal(const std::string& url, bool async, const std::string& uuid) +{ + // Act like the proxy window was closed, since we won't be able to track targeted windows in the external browser. + LLViewerMedia::proxyWindowClosed(uuid); + + if(gSavedSettings.getBOOL("DisableExternalBrowser")) + { + // Don't open an external browser under any circumstances. + llwarns << "Blocked attempt to open external browser." << llendl; + return; + } + + LLSD payload; + payload["url"] = url; + LLNotificationsUtil::add( "WebLaunchExternalTarget", LLSD(), payload, boost::bind(on_load_url_external_response, _1, _2, async)); +} + +// static +bool on_load_url_external_response(const LLSD& notification, const LLSD& response, bool async ) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if ( 0 == option ) + { + LLSD payload = notification["payload"]; + std::string url = payload["url"].asString(); + std::string escaped_url = LLWeb::escapeURL(url); + if (gViewerWindow) + { + gViewerWindow->getWindow()->spawnWebBrowser(escaped_url, async); + } + } + return false; +} // static std::string LLWeb::curlEscape(const std::string& url) @@ -153,6 +227,7 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, substitution["VERSION_BUILD"] = gVersionBuild; substitution["CHANNEL"] = gVersionChannel; substitution["GRID"] = LLViewerLogin::getInstance()->getGridLabel(); + substitution["GRID_LOWERCASE"] = utf8str_tolower(LLViewerLogin::getInstance()->getGridLabel()); substitution["OS"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); substitution["SESSION_ID"] = gAgent.getSessionID(); substitution["FIRST_LOGIN"] = gAgent.isFirstLogin(); @@ -191,12 +266,3 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, return LLWeb::escapeURL(expanded_url); } - -// virtual -void LLWeb::URLLoader::load(const std::string& url) -{ - loadURL(url); -} - -// static -LLWeb::URLLoader LLWeb::sAlertURLLoader; diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h index 73f5c7ebe..5c88f1843 100644 --- a/indra/newview/llweb.h +++ b/indra/newview/llweb.h @@ -35,21 +35,28 @@ #define LL_LLWEB_H #include -#include "llalertdialog.h" +/// +/// The LLWeb class provides various static methods to display the +/// contents of a Url in a web browser. Variations are provided to +/// let you specifically use the Second Life internal browser, the +/// operating system's default browser, or to respect the user's +/// setting for which of these two they prefer to use with SL. +/// class LLWeb { public: static void initClass(); - // Loads unescaped url in either internal web browser or external - // browser, depending on user settings. - static void loadURL(const std::string& url); - - static void loadURL(const char* url) { loadURL( ll_safe_string(url) ); } + /// Load the given url in the operating system's web browser, async if we want to return immediately + /// before browser has spawned + static void loadURLExternal(const std::string& url) {loadURLExternal(url, LLStringUtil::null);} + static void loadURLExternal(const std::string& url, const std::string& uuid); + static void loadURLExternal(const std::string& url, bool async, const std::string& uuid = LLStringUtil::null); - // Loads unescaped url in external browser. - static void loadURLExternal(const std::string& url); + static void loadURL(const std::string& url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); + // load content using built-in browser + static void loadURLInternal(const std::string &url, const std::string& target = LLStringUtil::null, const std::string& uuid = LLStringUtil::null); // Behaves like the old curl_escape. static std::string curlEscape(const std::string& url); @@ -60,13 +67,6 @@ public: /// Expands various strings like [LANG], [VERSION], etc. in a URL static std::string expandURLSubstitutions(const std::string &url, const LLSD &default_subs); - - class URLLoader : public LLAlertDialog::URLLoader - { - virtual void load(const std::string& url); - }; - - static URLLoader sAlertURLLoader; }; #endif diff --git a/indra/newview/llworldmapmessage.cpp b/indra/newview/llworldmapmessage.cpp index b25a3db75..5729bcb11 100644 --- a/indra/newview/llworldmapmessage.cpp +++ b/indra/newview/llworldmapmessage.cpp @@ -227,11 +227,12 @@ void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) gFloaterWorldMap->trackLocation(pos_global); } + U64 handle = to_region_handle(x_world, y_world); // Handle the SLURL callback if any url_callback_t callback = LLWorldMapMessage::getInstance()->mSLURLCallback; if(callback != NULL) { - U64 handle = to_region_handle(x_world, y_world); + // Check if we reached the requested region if ((LLStringUtil::compareInsensitive(LLWorldMapMessage::getInstance()->mSLURLRegionName, name)==0) || (LLWorldMapMessage::getInstance()->mSLURLRegionHandle == handle)) @@ -244,8 +245,7 @@ void LLWorldMapMessage::processMapBlockReply(LLMessageSystem* msg, void**) } } if( gAgent.mPendingLure && - (U16)(gAgent.mPendingLure->mPosGlobal.mdV[0] / REGION_WIDTH_UNITS) == x_regions && - (U16)(gAgent.mPendingLure->mPosGlobal.mdV[1] / REGION_WIDTH_UNITS) == y_regions ) + gAgent.mPendingLure->mRegionHandle == handle) { gAgent.onFoundLureDestination(); } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 508b8bb2c..2d42aff94 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -82,6 +82,7 @@ #include "lltool.h" #include "lltoolmgr.h" #include "llviewercamera.h" +#include "llviewermediafocus.h" #include "llviewertexturelist.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -295,6 +296,7 @@ S32 LLPipeline::sCompiles = 0; BOOL LLPipeline::sPickAvatar = TRUE; BOOL LLPipeline::sDynamicLOD = TRUE; BOOL LLPipeline::sShowHUDAttachments = TRUE; +BOOL LLPipeline::sRenderMOAPBeacons = FALSE; BOOL LLPipeline::sRenderPhysicalBeacons = TRUE; BOOL LLPipeline::sRenderScriptedBeacons = FALSE; BOOL LLPipeline::sRenderScriptedTouchBeacons = TRUE; @@ -1416,7 +1418,7 @@ U32 LLPipeline::getPoolTypeFromTE(const LLTextureEntry* te, LLViewerTexture* ima bool alpha = te->getColor().mV[3] < 0.999f; if (imagep) { - alpha = alpha || (imagep->getComponents() == 4 && ! imagep->mIsMediaTexture) || (imagep->getComponents() == 2); + alpha = alpha || (imagep->getComponents() == 4 && imagep->getType() != LLViewerTexture::MEDIA_TEXTURE) || (imagep->getComponents() == 2); } if (alpha) @@ -3328,6 +3330,47 @@ void renderPhysicalBeacons(LLDrawable* drawablep) } } +void renderMOAPBeacons(LLDrawable* drawablep) +{ + LLViewerObject *vobj = drawablep->getVObj(); + + if(!vobj || vobj->isAvatar()) + return; + + BOOL beacon=FALSE; + U8 tecount=vobj->getNumTEs(); + for(int x=0;xgetTE(x)->hasMedia()) + { + beacon=TRUE; + break; + } + } + if(beacon==TRUE) + { + if (gPipeline.sRenderBeacons) + { + static const LLCachedControl DebugBeaconLineWidth("DebugBeaconLineWidth",1); + gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 1.f, 1.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f), DebugBeaconLineWidth); + } + + if (gPipeline.sRenderHighlight) + { + S32 face_id; + S32 count = drawablep->getNumFaces(); + for (face_id = 0; face_id < count; face_id++) + { + LLFace * facep = drawablep->getFace(face_id); + if (facep) + { + gPipeline.mHighlightFaces.push_back(facep); + } + } + } +} +} + void renderParticleBeacons(LLDrawable* drawablep) { // Look for attachments, objects, etc. @@ -3541,6 +3584,11 @@ void LLPipeline::postSort(LLCamera& camera) forAllVisibleDrawables(renderPhysicalBeacons); } + if(sRenderMOAPBeacons) + { + forAllVisibleDrawables(renderMOAPBeacons); + } + if (sRenderParticleBeacons) { forAllVisibleDrawables(renderParticleBeacons); @@ -6007,6 +6055,24 @@ BOOL LLPipeline::getRenderScriptedTouchBeacons(void*) return sRenderScriptedTouchBeacons; } +// static +void LLPipeline::setRenderMOAPBeacons(BOOL val) +{ + sRenderMOAPBeacons = val; +} + +// static +void LLPipeline::toggleRenderMOAPBeacons(void*) +{ + sRenderMOAPBeacons = !sRenderMOAPBeacons; +} + +// static +BOOL LLPipeline::getRenderMOAPBeacons(void*) +{ + return sRenderMOAPBeacons; +} + // static void LLPipeline::setRenderPhysicalBeacons(BOOL val) { @@ -6727,7 +6793,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield, b LLVector3 focus_point; - /*LLViewerObject* obj = LLViewerMediaFocus::getInstance()->getFocusedObject(); + LLViewerObject* obj = LLViewerMediaFocus::getInstance()->getFocusedObject(); if (obj && obj->mDrawable && obj->isSelected()) { //focus on selected media object S32 face_idx = LLViewerMediaFocus::getInstance()->getFocusedFace(); @@ -6739,7 +6805,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield, b focus_point = face->getPositionAgent(); } } - }*/ + } if (focus_point.isExactlyZero()) { diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index b8ede8af7..3bde9fbcd 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -370,6 +370,10 @@ public: static void toggleRenderSoundBeacons(void* data); static BOOL getRenderSoundBeacons(void* data); + static void setRenderMOAPBeacons(BOOL val); + static void toggleRenderMOAPBeacons(void * data); + static BOOL getRenderMOAPBeacons(void * data); + static void setRenderPhysicalBeacons(BOOL val); static void toggleRenderPhysicalBeacons(void* data); static BOOL getRenderPhysicalBeacons(void* data); @@ -778,6 +782,7 @@ protected: S32 mLightingDetail; static BOOL sRenderPhysicalBeacons; + static BOOL sRenderMOAPBeacons; static BOOL sRenderScriptedTouchBeacons; static BOOL sRenderScriptedBeacons; static BOOL sRenderParticleBeacons; diff --git a/indra/newview/rlvcommon.cpp b/indra/newview/rlvcommon.cpp index a4c8ad281..f7eec9322 100644 --- a/indra/newview/rlvcommon.cpp +++ b/indra/newview/rlvcommon.cpp @@ -28,6 +28,7 @@ #include "llviewerstats.h" #include "llvoavatar.h" #include "llworld.h" +#include "llnotifications.h" #include "../lscript/lscript_byteformat.h" //Need LSCRIPTRunTimePermissionBits and SCRIPT_PERMISSION_* diff --git a/indra/newview/skins/default/textures/Flag.png b/indra/newview/skins/default/textures/Flag.png new file mode 100644 index 000000000..66739e9bf Binary files /dev/null and b/indra/newview/skins/default/textures/Flag.png differ diff --git a/indra/newview/skins/default/textures/Parcel_Exp_Color.png b/indra/newview/skins/default/textures/Parcel_Exp_Color.png new file mode 100644 index 000000000..4813d3719 Binary files /dev/null and b/indra/newview/skins/default/textures/Parcel_Exp_Color.png differ diff --git a/indra/newview/skins/default/textures/go-home-4.png b/indra/newview/skins/default/textures/go-home.png similarity index 100% rename from indra/newview/skins/default/textures/go-home-4.png rename to indra/newview/skins/default/textures/go-home.png diff --git a/indra/newview/skins/default/textures/go-media-pause.png b/indra/newview/skins/default/textures/go-media-pause.png new file mode 100644 index 000000000..8aea17088 Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-pause.png differ diff --git a/indra/newview/skins/default/textures/go-media-play.png b/indra/newview/skins/default/textures/go-media-play.png new file mode 100644 index 000000000..daa40e3c3 Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-play.png differ diff --git a/indra/newview/skins/default/textures/go-media-skip-backwards.png b/indra/newview/skins/default/textures/go-media-skip-backwards.png new file mode 100644 index 000000000..02497b165 Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-skip-backwards.png differ diff --git a/indra/newview/skins/default/textures/go-media-skip-forward.png b/indra/newview/skins/default/textures/go-media-skip-forward.png new file mode 100644 index 000000000..97090e1f1 Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-skip-forward.png differ diff --git a/indra/newview/skins/default/textures/go-media-stop.png b/indra/newview/skins/default/textures/go-media-stop.png new file mode 100644 index 000000000..80a54d1b2 Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-stop.png differ diff --git a/indra/newview/skins/default/textures/go-media-unzoom.png b/indra/newview/skins/default/textures/go-media-unzoom.png new file mode 100644 index 000000000..0f45c2c9c Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-unzoom.png differ diff --git a/indra/newview/skins/default/textures/go-media-zoom.png b/indra/newview/skins/default/textures/go-media-zoom.png new file mode 100644 index 000000000..b06c0fd52 Binary files /dev/null and b/indra/newview/skins/default/textures/go-media-zoom.png differ diff --git a/indra/newview/skins/default/textures/go-next-4.png b/indra/newview/skins/default/textures/go-next.png similarity index 100% rename from indra/newview/skins/default/textures/go-next-4.png rename to indra/newview/skins/default/textures/go-next.png diff --git a/indra/newview/skins/default/textures/go-previous-4.png b/indra/newview/skins/default/textures/go-previous.png similarity index 100% rename from indra/newview/skins/default/textures/go-previous-4.png rename to indra/newview/skins/default/textures/go-previous.png diff --git a/indra/newview/skins/default/textures/view-refresh-5.png b/indra/newview/skins/default/textures/go-reload.png similarity index 100% rename from indra/newview/skins/default/textures/view-refresh-5.png rename to indra/newview/skins/default/textures/go-reload.png diff --git a/indra/newview/skins/default/textures/go-stop.png b/indra/newview/skins/default/textures/go-stop.png new file mode 100644 index 000000000..08dab9a32 Binary files /dev/null and b/indra/newview/skins/default/textures/go-stop.png differ diff --git a/indra/newview/skins/default/textures/lock.png b/indra/newview/skins/default/textures/lock.png new file mode 100644 index 000000000..d23cb4d29 Binary files /dev/null and b/indra/newview/skins/default/textures/lock.png differ diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index a49fdd786..92938cd5c 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -403,11 +403,15 @@ - - - - - + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/floater_active_speakers.xml b/indra/newview/skins/default/xui/en-us/floater_active_speakers.xml index a808fc5f8..7eb229a8c 100644 --- a/indra/newview/skins/default/xui/en-us/floater_active_speakers.xml +++ b/indra/newview/skins/default/xui/en-us/floater_active_speakers.xml @@ -5,10 +5,11 @@ width="250"> - + + sort_column="2" top="-46"> diff --git a/indra/newview/skins/default/xui/en-us/floater_beacons.xml b/indra/newview/skins/default/xui/en-us/floater_beacons.xml index ff9ed0b0a..68724ff1e 100644 --- a/indra/newview/skins/default/xui/en-us/floater_beacons.xml +++ b/indra/newview/skins/default/xui/en-us/floater_beacons.xml @@ -3,23 +3,68 @@ width="250" height="255" name="beacons" title="Beacons" rect_control="FloaterBeaconsRect" control_name="ShowBeaconsFloater"> - - - - - - - - - - - Beacon Width: - - - + + + + + + + + + + Beacon Width: + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/floater_directory.xml b/indra/newview/skins/default/xui/en-us/floater_directory.xml index 6817602b0..bb88ba00c 100644 --- a/indra/newview/skins/default/xui/en-us/floater_directory.xml +++ b/indra/newview/skins/default/xui/en-us/floater_directory.xml @@ -27,11 +27,11 @@ select_on_focus="false" tab_group="1" tool_tip="Search Second Life" width="160" spell_check="true" /> + name="radio zoom" radio_style="true" width="114" > + + + + mouse_opaque="true" name="slider zoom" width="134" > + + + name="radio orbit" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio pan" radio_style="true" width="114" > + + + name="radio move" radio_style="true" width="114" > + + + name="radio lift" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio spin" radio_style="true" width="114" > + + + name="radio position" radio_style="true" width="114" > + + + name="radio rotate" radio_style="true" width="114" > + + + mouse_opaque="true" name="radio stretch" radio_style="true" width="123" > + + + name="radio select face" radio_style="true" width="114" > + + + name="radio align" radio_style="true" width="114" > + + + name="checkbox edit linked parts" width="114"> + + Reference + @@ -249,29 +314,53 @@ + name="radio select land" radio_style="true" width="114" > + + + name="radio flatten" radio_style="true" width="114" > + + + name="radio raise" radio_style="true" width="114" > + + + name="radio lower" radio_style="true" width="114" > + + + name="radio smooth" radio_style="true" width="114" > + + + name="radio noise" radio_style="true" width="114" > + + + name="radio revert" radio_style="true" width="114" > + + Group: - - The Lindens - + + + + + + + + + + Subscribe Now + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/floater_voice_license.xml b/indra/newview/skins/default/xui/en-us/floater_voice_license.xml index 4596134c8..40e75b4a4 100644 --- a/indra/newview/skins/default/xui/en-us/floater_voice_license.xml +++ b/indra/newview/skins/default/xui/en-us/floater_voice_license.xml @@ -28,6 +28,7 @@ To use voice, you must accept the license agreement. diff --git a/indra/newview/skins/default/xui/en-us/floater_voice_wizard.xml b/indra/newview/skins/default/xui/en-us/floater_voice_wizard.xml deleted file mode 100644 index 71acd0fc8..000000000 --- a/indra/newview/skins/default/xui/en-us/floater_voice_wizard.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/floater_whitelist_entry.xml b/indra/newview/skins/default/xui/en-us/floater_whitelist_entry.xml new file mode 100644 index 000000000..f55afb222 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/floater_whitelist_entry.xml @@ -0,0 +1,29 @@ + + + + + Enter a URL or URL pattern to add to the list of allowed domains + + + + + + + + + + + + + + Note: Residents can override this setting + + + + + + Size: + + + + + + X + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml b/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml new file mode 100644 index 000000000..d3a58fed5 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_media_settings_permissions.xml @@ -0,0 +1,172 @@ + + + + + Controls: + + + + Standard + + + Mini + + + + + Owner + + + + + + + + Group: + + + + + + + + + + Anyone + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml b/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml new file mode 100644 index 000000000..828635262 --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_media_settings_security.xml @@ -0,0 +1,91 @@ + + + + + + + Entries that the home page fails against are marked: + + + + +Warning: the home page specified in the General tab fails to +pass this whitelist. It has been disabled until a valid entry +has been added. + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_nearby_media.xml b/indra/newview/skins/default/xui/en-us/panel_nearby_media.xml new file mode 100644 index 000000000..f4ad5defe --- /dev/null +++ b/indra/newview/skins/default/xui/en-us/panel_nearby_media.xml @@ -0,0 +1,382 @@ + + + (%ld media items) + <empty> + Parcel Streaming Media + Parcel Streaming Audio + (playing) + + + + + + + + + + Show: + + + +All + + +In this Parcel + + +Outside this Parcel + + +On other Avatars + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml b/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml index 4e2339287..a5e734b12 100644 --- a/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml +++ b/indra/newview/skins/default/xui/en-us/panel_notifications_channel.xml @@ -1,16 +1,20 @@ - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/indra/newview/skins/default/xui/en-us/panel_progress.xml b/indra/newview/skins/default/xui/en-us/panel_progress.xml index da8ae77d5..fdda05605 100644 --- a/indra/newview/skins/default/xui/en-us/panel_progress.xml +++ b/indra/newview/skins/default/xui/en-us/panel_progress.xml @@ -2,15 +2,15 @@ - + - - - - - + + + + + - + diff --git a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml index 38178813f..4c6b39881 100644 --- a/indra/newview/skins/default/xui/en-us/panel_toolbar.xml +++ b/indra/newview/skins/default/xui/en-us/panel_toolbar.xml @@ -2,19 +2,45 @@ Redock Windows - - -