Pull and merge from ssh://stinson@hg.lindenlab.com/richard/viewer-chui/.

meow-7.2.2
Todd Stinson 2012-07-27 15:38:03 -07:00
commit c1aa0cc4fc
34 changed files with 2251 additions and 81 deletions

View File

@ -181,7 +181,8 @@ namespace LLInitParam
bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent)
{
if (!deserializeBlock(p, std::make_pair(name_stack.begin(), name_stack.end()), true))
Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end());
if (!deserializeBlock(p, range, true))
{
if (!silent)
{
@ -321,7 +322,7 @@ namespace LLInitParam
return true;
}
bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool ignored)
bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored)
{
BlockDescriptor& block_data = mostDerivedBlockDescriptor();
bool names_left = name_stack_range.first != name_stack_range.second;

View File

@ -572,7 +572,7 @@ namespace LLInitParam
};
typedef bool(*merge_func_t)(Param&, const Param&, bool);
typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, bool);
typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool);
typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
typedef bool(*validation_func_t)(const Param*);
@ -633,38 +633,38 @@ namespace LLInitParam
class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed
};
//TODO: implement in terms of owned_ptr
template<typename T>
//TODO: implement in terms of owned_ptr
template<typename T>
class LazyValue
{
{
public:
LazyValue()
: mPtr(NULL)
{}
: mPtr(NULL)
{}
~LazyValue()
{
delete mPtr;
}
{
delete mPtr;
}
LazyValue(const T& value)
{
{
mPtr = new T(value);
}
LazyValue(const LazyValue& other)
: mPtr(NULL)
{
{
*this = other;
}
}
LazyValue& operator = (const LazyValue& other)
{
if (!other.mPtr)
{
{
delete mPtr;
mPtr = NULL;
}
mPtr = NULL;
}
else
{
if (!mPtr)
@ -675,9 +675,9 @@ namespace LLInitParam
{
*mPtr = *(other.mPtr);
}
}
return *this;
}
return *this;
}
bool operator==(const LazyValue& other) const
{
@ -685,13 +685,13 @@ namespace LLInitParam
return *mPtr == *other.mPtr;
}
bool empty() const
{
return mPtr == NULL;
}
bool empty() const
{
return mPtr == NULL;
}
void set(const T& other)
{
void set(const T& other)
{
if (!mPtr)
{
mPtr = new T(other);
@ -702,36 +702,36 @@ namespace LLInitParam
}
}
const T& get() const
{
const T& get() const
{
return *ensureInstance();
}
}
T& get()
{
T& get()
{
return *ensureInstance();
}
operator const T&() const
{
return get();
}
}
private:
// lazily allocate an instance of T
T* ensureInstance() const
private:
// lazily allocate an instance of T
T* ensureInstance() const
{
if (mPtr == NULL)
{
if (mPtr == NULL)
{
mPtr = new T();
}
return mPtr;
}
mPtr = new T();
}
return mPtr;
}
private:
private:
mutable T* mPtr;
};
mutable T* mPtr;
};
// root class of all parameter blocks
@ -838,7 +838,7 @@ namespace LLInitParam
// Blocks can override this to do custom tracking of changes
virtual void paramChanged(const Param& changed_param, bool user_provided) {}
bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const;
@ -913,7 +913,7 @@ namespace LLInitParam
}
U32 getEnclosingBlockOffset() const
{
{
return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow;
}
@ -970,7 +970,7 @@ namespace LLInitParam
bool isProvided() const { return Param::anyProvided(); }
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
self_t& typed_param = static_cast<self_t&>(param);
// no further names in stack, attempt to parse value now
@ -1128,7 +1128,7 @@ namespace LLInitParam
}
}
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
self_t& typed_param = static_cast<self_t&>(param);
// attempt to parse block...
@ -1287,7 +1287,7 @@ namespace LLInitParam
}
};
// container of non-block parameters
// list of non-block parameters
template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, NOT_BLOCK>
: public Param
@ -1316,7 +1316,7 @@ namespace LLInitParam
bool isProvided() const { return Param::anyProvided(); }
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
Parser::name_stack_range_t new_name_stack_range(name_stack_range);
self_t& typed_param = static_cast<self_t&>(param);
@ -1499,7 +1499,7 @@ namespace LLInitParam
}
};
// container of block parameters
// list of block parameters
template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, IS_A_BLOCK>
: public Param
@ -1528,30 +1528,35 @@ namespace LLInitParam
bool isProvided() const { return Param::anyProvided(); }
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
Parser::name_stack_range_t new_name_stack_range(name_stack_range);
self_t& typed_param = static_cast<self_t&>(param);
bool new_value = false;
bool new_array_value = false;
// pop first element if empty string
if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
{
new_value |= new_name_stack_range.first->second;
new_array_value = new_name_stack_range.first->second;
++new_name_stack_range.first;
}
if (new_name || typed_param.mValues.empty())
if (new_name || new_array_value || typed_param.mValues.empty())
{
new_value = true;
typed_param.mValues.push_back(value_t());
}
param_value_t& value = typed_param.mValues.back();
// attempt to parse block...
if(value.deserializeBlock(parser, new_name_stack_range, new_name))
{
typed_param.setProvided();
if (new_array_value)
{
name_stack_range.first->second = false;
}
return true;
}
else if(named_value_t::valueNamesExist())
@ -1565,6 +1570,10 @@ namespace LLInitParam
{
typed_param.mValues.back().setValueName(name);
typed_param.setProvided();
if (new_array_value)
{
name_stack_range.first->second = false;
}
return true;
}
@ -2039,7 +2048,7 @@ namespace LLInitParam
}
}
static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack_range, bool new_name)
static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
if (name_stack_range.first == name_stack_range.second)
{
@ -2062,7 +2071,7 @@ namespace LLInitParam
// dummy writer interfaces
template<typename T>
Deprecated& operator =(const T& val)
{
{
// do nothing
return *this;
}
@ -2178,14 +2187,14 @@ namespace LLInitParam
return mValue.getValue();
}
bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name)
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
if (new_name)
{
resetToDefault();
}
return mValue.deserializeBlock(p, name_stack_range, new_name);
}
}
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
{
@ -2271,12 +2280,12 @@ namespace LLInitParam
return mValue.getValue();
}
bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name)
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
if (new_name)
{
mCurParam = getBlockDescriptor().mAllParams.begin();
}
}
if (name_stack_range.first == name_stack_range.second
&& mCurParam != getBlockDescriptor().mAllParams.end())
{
@ -2288,7 +2297,7 @@ namespace LLInitParam
if (deserialize_func
&& paramp
&& deserialize_func(*paramp, p, name_stack_range, new_name))
{
{
++mCurParam;
return true;
}
@ -2300,7 +2309,7 @@ namespace LLInitParam
else
{
return mValue.deserializeBlock(p, name_stack_range, new_name);
}
}
}
void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const self_t* diff_block = NULL) const
@ -2353,7 +2362,7 @@ namespace LLInitParam
: T(),
mValidated(false)
{}
ParamValue(const default_value_t& value)
: T(value.getValue()),
mValidated(false)
@ -2401,7 +2410,7 @@ namespace LLInitParam
return mValue.get().getValue();
}
bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name)
bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
return mValue.get().deserializeBlock(p, name_stack_range, new_name);
}
@ -2425,7 +2434,7 @@ namespace LLInitParam
{
return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite);
}
bool validateBlock(bool emit_errors = true) const
{
return mValue.empty() || mValue.get().validateBlock(emit_errors);
@ -2510,7 +2519,7 @@ namespace LLInitParam
LLSD& getValue() { return mValue; }
// block param interface
LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack_range, bool new_name);
LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
LL_COMMON_API void serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const BaseBlock* diff_block = NULL) const;
bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
{
@ -2540,10 +2549,10 @@ namespace LLInitParam
} EValueAge;
typedef ParamValue<T> derived_t;
typedef CustomParamValue<T> self_t;
typedef Block<derived_t> block_t;
typedef CustomParamValue<T> self_t;
typedef Block<derived_t> block_t;
typedef T default_value_t;
typedef T value_t;
typedef T value_t;
typedef void baseblock_base_class_t;
@ -2553,7 +2562,7 @@ namespace LLInitParam
mValidated(false)
{}
bool deserializeBlock(Parser& parser, Parser::name_stack_range_t name_stack_range, bool new_name)
bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
{
derived_t& typed_param = static_cast<derived_t&>(*this);
// try to parse direct value T

View File

@ -303,7 +303,7 @@ namespace LLInitParam
{
// LLSD specialization
// block param interface
bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack, bool new_name)
bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack, bool new_name)
{
if (name_stack.first == name_stack.second
&& p.readValue<LLSD>(mValue))

View File

@ -131,6 +131,9 @@ set(viewer_SOURCE_FILES
llcommandlineparser.cpp
llcompilequeue.cpp
llconfirmationmanager.cpp
llconversationlog.cpp
llconversationloglist.cpp
llconversationloglistitem.cpp
llcurrencyuimanager.cpp
llcylinder.cpp
lldateutil.cpp
@ -187,6 +190,8 @@ set(viewer_SOURCE_FILES
llfloaterbuyland.cpp
llfloatercamera.cpp
llfloatercolorpicker.cpp
llfloaterconversationlog.cpp
llfloaterconversationpreview.cpp
llfloaterdeleteenvpreset.cpp
llfloaterdestinations.cpp
llfloaterdisplayname.cpp
@ -692,6 +697,9 @@ set(viewer_HEADER_FILES
llcommandlineparser.h
llcompilequeue.h
llconfirmationmanager.h
llconversationlog.h
llconversationloglist.h
llconversationloglistitem.h
llcurrencyuimanager.h
llcylinder.h
lldateutil.h
@ -748,6 +756,8 @@ set(viewer_HEADER_FILES
llfloaterbuyland.h
llfloatercamera.h
llfloatercolorpicker.h
llfloaterconversationlog.h
llfloaterconversationpreview.h
llfloaterdeleteenvpreset.h
llfloaterdestinations.h
llfloaterdisplayname.h

View File

@ -10052,6 +10052,28 @@
<key>Value</key>
<integer>2</integer>
</map>
<key>CallLogSortOrder</key>
<map>
<key>Comment</key>
<string>Specifies sort order for Call Log (0 = by name, 1 = by date)</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>U32</string>
<key>Value</key>
<integer>2</integer>
</map>
<key>SortFriendsFirst</key>
<map>
<key>Comment</key>
<string>Specifies whether friends will be sorted first in Call Log</string>
<key>Persist</key>
<integer>1</integer>
<key>Type</key>
<string>Boolean</string>
<key>Value</key>
<integer>1</integer>
</map>
<key>ShowPGSearchAll</key>
<map>
<key>Comment</key>

View File

@ -59,6 +59,7 @@
#include "llares.h"
#include "llcurl.h"
#include "llcalc.h"
#include "llconversationlog.h"
#include "lltexturestats.h"
#include "lltexturestats.h"
#include "llviewerwindow.h"
@ -1824,6 +1825,9 @@ bool LLAppViewer::cleanup()
// save mute list. gMuteList used to also be deleted here too.
LLMuteList::getInstance()->cache(gAgent.getID());
//save call log list
LLConversationLog::instance().cache();
if (mPurgeOnExit)
{
llinfos << "Purging all cache files on exit" << llendflush;

View File

@ -0,0 +1,336 @@
/**
* @file llconversationlog.h
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 "llagent.h"
#include "llconversationlog.h"
#include "lltrans.h"
struct Conversation_params
{
Conversation_params(time_t time)
: mTime(time),
mTimestamp(LLConversation::createTimestamp(time))
{}
time_t mTime;
std::string mTimestamp;
SessionType mConversationType;
std::string mConversationName;
std::string mHistoryFileName;
LLUUID mSessionID;
LLUUID mParticipantID;
bool mIsVoice;
bool mHasOfflineIMs;
};
/************************************************************************/
/* LLConversation implementation */
/************************************************************************/
LLConversation::LLConversation(const Conversation_params& params)
: mTime(params.mTime),
mTimestamp(params.mTimestamp),
mConversationType(params.mConversationType),
mConversationName(params.mConversationName),
mHistoryFileName(params.mHistoryFileName),
mSessionID(params.mSessionID),
mParticipantID(params.mParticipantID),
mIsVoice(params.mIsVoice),
mHasOfflineIMs(params.mHasOfflineIMs)
{
setListenIMFloaterOpened();
}
LLConversation::LLConversation(const LLIMModel::LLIMSession& session)
: mTime(time_corrected()),
mTimestamp(createTimestamp(mTime)),
mConversationType(session.mSessionType),
mConversationName(session.mName),
mHistoryFileName(session.mHistoryFileName),
mSessionID(session.mSessionID),
mParticipantID(session.mOtherParticipantID),
mIsVoice(session.mStartedAsIMCall),
mHasOfflineIMs(session.mHasOfflineMessage)
{
setListenIMFloaterOpened();
}
LLConversation::LLConversation(const LLConversation& conversation)
{
mTime = conversation.getTime();
mTimestamp = conversation.getTimestamp();
mConversationType = conversation.getConversationType();
mConversationName = conversation.getConversationName();
mHistoryFileName = conversation.getHistoryFileName();
mSessionID = conversation.getSessionID();
mParticipantID = conversation.getParticipantID();
mIsVoice = conversation.isVoice();
mHasOfflineIMs = conversation.hasOfflineMessages();
setListenIMFloaterOpened();
}
LLConversation::~LLConversation()
{
mIMFloaterShowedConnection.disconnect();
}
void LLConversation::onIMFloaterShown(const LLUUID& session_id)
{
if (mSessionID == session_id)
{
mHasOfflineIMs = false;
}
}
// static
const std::string LLConversation::createTimestamp(const time_t& utc_time)
{
std::string timeStr;
LLSD substitution;
substitution["datetime"] = (S32) utc_time;
timeStr = "["+LLTrans::getString ("TimeMonth")+"]/["
+LLTrans::getString ("TimeDay")+"]/["
+LLTrans::getString ("TimeYear")+"] ["
+LLTrans::getString ("TimeHour")+"]:["
+LLTrans::getString ("TimeMin")+"]";
LLStringUtil::format (timeStr, substitution);
return timeStr;
}
void LLConversation::setListenIMFloaterOpened()
{
LLIMFloater* floater = LLIMFloater::findInstance(mSessionID);
bool has_offline_ims = !mIsVoice && mHasOfflineIMs;
bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus();
// we don't need to listen for im floater with this conversation is opened
// if floater is already opened or this conversation doesn't have unread offline messages
if (has_offline_ims && !ims_are_read)
{
mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversation::onIMFloaterShown, this, _1));
}
}
/************************************************************************/
/* LLConversationLog implementation */
/************************************************************************/
LLConversationLog::LLConversationLog()
{
loadFromFile(getFileName());
LLIMMgr::instance().addSessionObserver(this);
LLAvatarTracker::instance().addObserver(this);
}
void LLConversationLog::logConversation(const LLConversation& conversation)
{
mConversations.push_back(conversation);
notifyObservers();
}
void LLConversationLog::removeConversation(const LLConversation& conversation)
{
conversations_vec_t::iterator conv_it = mConversations.begin();
for(; conv_it != mConversations.end(); ++conv_it)
{
if (conv_it->getSessionID() == conversation.getSessionID() && conv_it->getTime() == conversation.getTime())
{
mConversations.erase(conv_it);
notifyObservers();
return;
}
}
}
const LLConversation* LLConversationLog::getConversation(const LLUUID& session_id)
{
conversations_vec_t::const_iterator conv_it = mConversations.begin();
for(; conv_it != mConversations.end(); ++conv_it)
{
if (conv_it->getSessionID() == session_id)
{
return &*conv_it;
}
}
return NULL;
}
void LLConversationLog::addObserver(LLConversationLogObserver* observer)
{
mObservers.insert(observer);
}
void LLConversationLog::removeObserver(LLConversationLogObserver* observer)
{
mObservers.erase(observer);
}
void LLConversationLog::sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id)
{
LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(session_id);
if (session)
{
LLConversation conversation(*session);
LLConversationLog::instance().logConversation(conversation);
}
}
// LLFriendObserver
void LLConversationLog::changed(U32 mask)
{
if (mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE))
{
notifyObservers();
}
}
void LLConversationLog::cache()
{
saveToFile(getFileName());
}
std::string LLConversationLog::getFileName()
{
std::string agent_id_string;
gAgent.getID().toString(agent_id_string);
return gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_string) + ".call_log";
}
bool LLConversationLog::saveToFile(const std::string& filename)
{
if(!filename.size())
{
llwarns << "Call log list filename is empty!" << llendl;
return false;
}
LLFILE* fp = LLFile::fopen(filename, "wb");
if (!fp)
{
llwarns << "Couldn't open call log list" << filename << llendl;
return false;
}
std::string participant_id;
std::string conversation_id;
conversations_vec_t::const_iterator conv_it = mConversations.begin();
for (; conv_it != mConversations.end(); ++conv_it)
{
conv_it->getSessionID().toString(conversation_id);
conv_it->getParticipantID().toString(participant_id);
// examples of two file entries
// [1343221177] 0 1 0 John Doe| 7e4ec5be-783f-49f5-71dz-16c58c64c145 4ec62a74-c246-0d25-2af6-846beac2aa55 john.doe|
// [1343222639] 2 0 0 Ad-hoc Conference| c3g67c89-c479-4c97-b21d-32869bcfe8rc 68f1c33e-4135-3e3e-a897-8c9b23115c09 Ad-hoc Conference hash597394a0-9982-766d-27b8-c75560213b9a|
fprintf(fp, "[%d] %d %d %d %s| %s %s %s|\n",
(S32)conv_it->getTime(),
(S32)conv_it->getConversationType(),
(S32)conv_it->isVoice(),
(S32)conv_it->hasOfflineMessages(),
conv_it->getConversationName().c_str(),
participant_id.c_str(),
conversation_id.c_str(),
conv_it->getHistoryFileName().c_str());
}
fclose(fp);
return true;
}
bool LLConversationLog::loadFromFile(const std::string& filename)
{
if(!filename.size())
{
llwarns << "Call log list filename is empty!" << llendl;
return false;
}
LLFILE* fp = LLFile::fopen(filename, "rb");
if (!fp)
{
llwarns << "Couldn't open call log list" << filename << llendl;
return false;
}
char buffer[MAX_STRING];
char conv_name_buffer[MAX_STRING];
char part_id_buffer[MAX_STRING];
char conv_id_buffer[MAX_STRING];
char history_file_name[MAX_STRING];
int is_voice;
int has_offline_ims;
int stype;
S32 time;
while (!feof(fp) && fgets(buffer, MAX_STRING, fp))
{
conv_name_buffer[0] = '\0';
part_id_buffer[0] = '\0';
conv_id_buffer[0] = '\0';
sscanf(buffer, "[%d] %d %d %d %[^|]| %s %s %[^|]|",
&time,
&stype,
&is_voice,
&has_offline_ims,
conv_name_buffer,
part_id_buffer,
conv_id_buffer,
history_file_name);
Conversation_params params(time);
params.mConversationType = (SessionType)stype;
params.mIsVoice = is_voice;
params.mHasOfflineIMs = has_offline_ims;
params.mConversationName = std::string(conv_name_buffer);
params.mParticipantID = LLUUID(part_id_buffer);
params.mSessionID = LLUUID(conv_id_buffer);
params.mHistoryFileName = std::string(history_file_name);
LLConversation conversation(params);
mConversations.push_back(conversation);
}
fclose(fp);
notifyObservers();
return true;
}
void LLConversationLog::notifyObservers()
{
std::set<LLConversationLogObserver*>::const_iterator iter = mObservers.begin();
for (; iter != mObservers.end(); ++iter)
{
(*iter)->changed();
}
}

View File

@ -0,0 +1,164 @@
/**
* @file llconversationlog.h
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 LLCONVERSATIONLOG_H_
#define LLCONVERSATIONLOG_H_
#include "llcallingcard.h"
#include "llimfloater.h"
#include "llimview.h"
class LLConversationLogObserver;
struct Conversation_params;
typedef LLIMModel::LLIMSession::SType SessionType;
/*
* This class represents a particular session(conversation) of any type(im/voice/p2p/group/...) by storing some of session's data.
* Each LLConversation object has a corresponding visual representation in a form of LLConversationLogListItem.
*/
class LLConversation
{
public:
LLConversation(const Conversation_params& params);
LLConversation(const LLIMModel::LLIMSession& session);
LLConversation(const LLConversation& conversation);
~LLConversation();
const SessionType& getConversationType() const { return mConversationType; }
const std::string& getConversationName() const { return mConversationName; }
const std::string& getHistoryFileName() const { return mHistoryFileName; }
const LLUUID& getSessionID() const { return mSessionID; }
const LLUUID& getParticipantID() const { return mParticipantID; }
const std::string& getTimestamp() const { return mTimestamp; }
const time_t& getTime() const { return mTime; }
bool isVoice() const { return mIsVoice; }
bool hasOfflineMessages() const { return mHasOfflineIMs; }
/*
* Resets flag of unread offline message to false when im floater with this conversation is opened.
*/
void onIMFloaterShown(const LLUUID& session_id);
/*
* returns string representation(in form of: mm/dd/yyyy hh:mm) of time when conversation was started
*/
static const std::string createTimestamp(const time_t& utc_time);
private:
/*
* If conversation has unread offline messages sets callback for opening LLIMFloater
* with this conversation.
*/
void setListenIMFloaterOpened();
boost::signals2::connection mIMFloaterShowedConnection;
time_t mTime; // start time of conversation
SessionType mConversationType;
std::string mConversationName;
std::string mHistoryFileName;
LLUUID mSessionID;
LLUUID mParticipantID;
bool mIsVoice;
bool mHasOfflineIMs;
std::string mTimestamp; // conversation start time in form of: mm/dd/yyyy hh:mm
};
/**
* LLConversationLog stores all agent's conversations.
* This class is responsible for creating and storing LLConversation objects when im or voice session starts.
* Also this class saves/retrieves conversations to/from file.
*
* Also please note that it may be several conversations with the same sessionID stored in the conversation log.
* To distinguish two conversations with the same sessionID it's also needed to compare their creation date.
*/
class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObserver, LLFriendObserver
{
friend class LLSingleton<LLConversationLog>;
public:
/**
* adds conversation to the conversation list and notifies observers
*/
void logConversation(const LLConversation& conversation);
void removeConversation(const LLConversation& conversation);
/**
* Returns first conversation with matched session_id
*/
const LLConversation* getConversation(const LLUUID& session_id);
void addObserver(LLConversationLogObserver* observer);
void removeObserver(LLConversationLogObserver* observer);
const std::vector<LLConversation>& getConversations() { return mConversations; }
// LLIMSessionObserver triggers
virtual void sessionAdded(const LLUUID& session_id, const std::string& name, const LLUUID& other_participant_id);
virtual void sessionVoiceOrIMStarted(const LLUUID& session_id){} // Stub
virtual void sessionRemoved(const LLUUID& session_id){} // Stub
virtual void sessionIDUpdated(const LLUUID& old_session_id, const LLUUID& new_session_id){} // Stub
// LLFriendObserver trigger
virtual void changed(U32 mask);
/**
* public method which is called on viewer exit to save conversation log
*/
void cache();
private:
LLConversationLog();
void notifyObservers();
/**
* constructs file name in which conversations log will be saved
* file name template: agentID.call_log.
* For example: a086icaa-782d-88d0-ae29-987a55c99sss.call_log
*/
std::string getFileName();
bool saveToFile(const std::string& filename);
bool loadFromFile(const std::string& filename);
typedef std::vector<LLConversation> conversations_vec_t;
std::vector<LLConversation> mConversations;
std::set<LLConversationLogObserver*> mObservers;
};
class LLConversationLogObserver
{
public:
virtual ~LLConversationLogObserver(){}
virtual void changed() = 0;
};
#endif /* LLCONVERSATIONLOG_H_ */

View File

@ -0,0 +1,422 @@
/**
* @file llconversationloglist.cpp
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 "llavataractions.h"
#include "llagent.h"
#include "llfloaterreg.h"
#include "llfloaterconversationpreview.h"
#include "llgroupactions.h"
#include "llconversationloglist.h"
#include "llconversationloglistitem.h"
#include "llviewermenu.h"
static LLDefaultChildRegistry::Register<LLConversationLogList> r("conversation_log_list");
static LLConversationLogListNameComparator NAME_COMPARATOR;
static LLConversationLogListDateComparator DATE_COMPARATOR;
LLConversationLogList::LLConversationLogList(const Params& p)
: LLFlatListViewEx(p),
mIsDirty(true)
{
LLConversationLog::instance().addObserver(this);
// Set up context menu.
LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar;
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar check_registrar;
LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
registrar.add ("Calllog.Action", boost::bind(&LLConversationLogList::onCustomAction, this, _2));
check_registrar.add ("Calllog.Check", boost::bind(&LLConversationLogList::isActionChecked,this, _2));
enable_registrar.add("Calllog.Enable", boost::bind(&LLConversationLogList::isActionEnabled,this, _2));
LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>(
"menu_conversation_log_gear.xml",
gMenuHolder,
LLViewerMenuHolderGL::child_registry_t::instance());
if(context_menu)
{
mContextMenu = context_menu->getHandle();
}
mIsFriendsOnTop = gSavedSettings.getBOOL("SortFriendsFirst");
}
LLConversationLogList::~LLConversationLogList()
{
if (mContextMenu.get())
{
mContextMenu.get()->die();
}
LLConversationLog::instance().removeObserver(this);
}
void LLConversationLogList::draw()
{
if (mIsDirty)
{
refresh();
}
LLFlatListViewEx::draw();
}
BOOL LLConversationLogList::handleRightMouseDown(S32 x, S32 y, MASK mask)
{
BOOL handled = LLUICtrl::handleRightMouseDown(x, y, mask);
LLToggleableMenu* context_menu = mContextMenu.get();
{
context_menu->buildDrawLabels();
if (context_menu && size())
context_menu->updateParent(LLMenuGL::sMenuContainer);
LLMenuGL::showPopup(this, context_menu, x, y);
}
return handled;
}
void LLConversationLogList::setNameFilter(const std::string& filter)
{
std::string filter_upper = filter;
LLStringUtil::toUpper(filter_upper);
if (mNameFilter != filter_upper)
{
mNameFilter = filter_upper;
setDirty();
}
}
bool LLConversationLogList::findInsensitive(std::string haystack, const std::string& needle_upper)
{
LLStringUtil::toUpper(haystack);
return haystack.find(needle_upper) != std::string::npos;
}
void LLConversationLogList::sortByName()
{
setComparator(&NAME_COMPARATOR);
sort();
}
void LLConversationLogList::sortByDate()
{
setComparator(&DATE_COMPARATOR);
sort();
}
void LLConversationLogList::toggleSortFriendsOnTop()
{
mIsFriendsOnTop = !mIsFriendsOnTop;
gSavedSettings.setBOOL("SortFriendsFirst", mIsFriendsOnTop);
sort();
}
void LLConversationLogList::changed()
{
refresh();
}
void LLConversationLogList::addNewItem(const LLConversation* conversation)
{
LLConversationLogListItem* item = new LLConversationLogListItem(&*conversation);
if (!mNameFilter.empty())
{
item->highlightNameDate(mNameFilter);
}
addItem(item, conversation->getSessionID(), ADD_TOP);
}
void LLConversationLogList::refresh()
{
rebuildList();
sort();
mIsDirty = false;
}
void LLConversationLogList::rebuildList()
{
clear();
bool have_filter = !mNameFilter.empty();
const std::vector<LLConversation>& conversations = LLConversationLog::instance().getConversations();
std::vector<LLConversation>::const_iterator iter = conversations.begin();
for (; iter != conversations.end(); ++iter)
{
bool not_found = have_filter && !findInsensitive(iter->getConversationName(), mNameFilter) && !findInsensitive(iter->getTimestamp(), mNameFilter);
if (not_found)
continue;
addNewItem(&*iter);
}
}
void LLConversationLogList::onCustomAction(const LLSD& userdata)
{
const std::string command_name = userdata.asString();
const LLUUID& selected_id = getSelectedConversation()->getParticipantID();
LLIMModel::LLIMSession::SType stype = getSelectedSessionType();
if ("im" == command_name)
{
switch (stype)
{
case LLIMModel::LLIMSession::P2P_SESSION:
LLAvatarActions::startIM(selected_id);
break;
case LLIMModel::LLIMSession::GROUP_SESSION:
LLGroupActions::startIM(selected_id);
break;
default:
break;
}
}
else if ("call" == command_name)
{
switch (stype)
{
case LLIMModel::LLIMSession::P2P_SESSION:
LLAvatarActions::startCall(selected_id);
break;
case LLIMModel::LLIMSession::GROUP_SESSION:
LLGroupActions::startCall(selected_id);
break;
default:
break;
}
}
else if ("view_profile" == command_name)
{
switch (stype)
{
case LLIMModel::LLIMSession::P2P_SESSION:
LLAvatarActions::showProfile(selected_id);
break;
case LLIMModel::LLIMSession::GROUP_SESSION:
LLGroupActions::show(selected_id);
break;
default:
break;
}
}
else if ("chat_history" == command_name)
{
const LLUUID& session_id = getSelectedConversation()->getSessionID();
LLFloaterReg::showInstance("preview_conversation", session_id, true);
}
else if ("offer_teleport" == command_name)
{
LLAvatarActions::offerTeleport(selected_id);
}
else if("add_rem_friend" == command_name)
{
if (LLAvatarActions::isFriend(selected_id))
{
LLAvatarActions::removeFriendDialog(selected_id);
}
else
{
LLAvatarActions::requestFriendshipDialog(selected_id);
}
}
else if ("invite_to_group" == command_name)
{
LLAvatarActions::inviteToGroup(selected_id);
}
else if ("show_on_map" == command_name)
{
LLAvatarActions::showOnMap(selected_id);
}
else if ("share" == command_name)
{
LLAvatarActions::share(selected_id);
}
else if ("pay" == command_name)
{
LLAvatarActions::pay(selected_id);
}
else if ("block" == command_name)
{
LLAvatarActions::toggleBlock(selected_id);
}
}
bool LLConversationLogList::isActionEnabled(const LLSD& userdata)
{
if (numSelected() != 1)
{
return false;
}
const std::string command_name = userdata.asString();
LLIMModel::LLIMSession::SType stype = getSelectedSessionType();
const LLUUID& selected_id = getSelectedConversation()->getParticipantID();
bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == stype;
bool is_group = LLIMModel::LLIMSession::GROUP_SESSION == stype;
if ("can_im" == command_name || "can_view_profile" == command_name)
{
return is_p2p || is_group;
}
else if ("can_view_chat_history" == command_name)
{
return true;
}
else if ("can_call" == command_name)
{
return (is_p2p || is_group) && LLAvatarActions::canCall();
}
else if ("add_rem_friend" == command_name ||
"can_invite_to_group" == command_name ||
"can_share" == command_name ||
"can_block" == command_name ||
"can_pay" == command_name)
{
return is_p2p;
}
else if("can_offer_teleport" == command_name)
{
return is_p2p && LLAvatarActions::canOfferTeleport(selected_id);
}
else if ("can_show_on_map")
{
return is_p2p && ((LLAvatarTracker::instance().isBuddyOnline(selected_id) && is_agent_mappable(selected_id)) || gAgent.isGodlike());
}
return false;
}
bool LLConversationLogList::isActionChecked(const LLSD& userdata)
{
const std::string command_name = userdata.asString();
const LLUUID& selected_id = getSelectedConversation()->getParticipantID();
bool is_p2p = LLIMModel::LLIMSession::P2P_SESSION == getSelectedSessionType();
if ("is_blocked" == command_name)
{
return is_p2p && LLAvatarActions::isBlocked(selected_id);
}
else if ("is_friend" == command_name)
{
return is_p2p && LLAvatarActions::isFriend(selected_id);
}
return false;
}
LLIMModel::LLIMSession::SType LLConversationLogList::getSelectedSessionType()
{
const LLConversationLogListItem* item = getSelectedConversationPanel();
if (item)
{
return item->getConversation()->getConversationType();
}
return LLIMModel::LLIMSession::NONE_SESSION;
}
const LLConversationLogListItem* LLConversationLogList::getSelectedConversationPanel()
{
LLPanel* panel = LLFlatListViewEx::getSelectedItem();
LLConversationLogListItem* conv_panel = dynamic_cast<LLConversationLogListItem*>(panel);
return conv_panel;
}
const LLConversation* LLConversationLogList::getSelectedConversation()
{
const LLConversationLogListItem* panel = getSelectedConversationPanel();
if (panel)
{
return panel->getConversation();
}
return NULL;
}
bool LLConversationLogListItemComparator::compare(const LLPanel* item1, const LLPanel* item2) const
{
const LLConversationLogListItem* conversation_item1 = dynamic_cast<const LLConversationLogListItem*>(item1);
const LLConversationLogListItem* conversation_item2 = dynamic_cast<const LLConversationLogListItem*>(item2);
if (!conversation_item1 || !conversation_item2)
{
llerror("conversation_item1 and conversation_item2 cannot be null", 0);
return true;
}
return doCompare(conversation_item1, conversation_item2);
}
bool LLConversationLogListNameComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const
{
std::string name1 = conversation1->getConversation()->getConversationName();
std::string name2 = conversation2->getConversation()->getConversationName();
const LLUUID& id1 = conversation1->getConversation()->getParticipantID();
const LLUUID& id2 = conversation2->getConversation()->getParticipantID();
LLStringUtil::toUpper(name1);
LLStringUtil::toUpper(name2);
bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst");
if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2)))
{
return LLAvatarActions::isFriend(id1);
}
return name1 < name2;
}
bool LLConversationLogListDateComparator::doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const
{
time_t date1 = conversation1->getConversation()->getTime();
time_t date2 = conversation2->getConversation()->getTime();
const LLUUID& id1 = conversation1->getConversation()->getParticipantID();
const LLUUID& id2 = conversation2->getConversation()->getParticipantID();
bool friends_first = gSavedSettings.getBOOL("SortFriendsFirst");
if (friends_first && (LLAvatarActions::isFriend(id1) ^ LLAvatarActions::isFriend(id2)))
{
return LLAvatarActions::isFriend(id1);
}
return date1 > date2;
}

View File

@ -0,0 +1,143 @@
/**
* @file llconversationloglist.h
*
* $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 LLCONVERSATIONLOGLIST_H_
#define LLCONVERSATIONLOGLIST_H_
#include "llconversationlog.h"
#include "llflatlistview.h"
#include "lltoggleablemenu.h"
class LLConversationLogListItem;
/**
* List of all agent's conversations. I.e. history of conversations.
* This list represents contents of the LLConversationLog.
* Each change in LLConversationLog leads to rebuilding this list, so
* it's always in actual state.
*/
class LLConversationLogList: public LLFlatListViewEx, public LLConversationLogObserver
{
LOG_CLASS(LLConversationLogList);
public:
struct Params : public LLInitParam::Block<Params, LLFlatListViewEx::Params>
{
Params(){};
};
LLConversationLogList(const Params& p);
virtual ~LLConversationLogList();
virtual void draw();
virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
LLToggleableMenu* getContextMenu() const { return mContextMenu.get(); }
void addNewItem(const LLConversation* conversation);
void setNameFilter(const std::string& filter);
void sortByName();
void sortByDate();
void toggleSortFriendsOnTop();
bool getSortFriendsOnTop() const { return mIsFriendsOnTop; }
/**
* Changes from LLConversationLogObserver
*/
virtual void changed();
private:
void setDirty(bool dirty = true) { mIsDirty = dirty; }
void refresh();
/**
* Clears list and re-adds items from LLConverstationLog
* If filter is not empty re-adds items which match the filter
*/
void rebuildList();
bool findInsensitive(std::string haystack, const std::string& needle_upper);
void onCustomAction (const LLSD& userdata);
bool isActionEnabled(const LLSD& userdata);
bool isActionChecked(const LLSD& userdata);
LLIMModel::LLIMSession::SType getSelectedSessionType();
const LLConversationLogListItem* getSelectedConversationPanel();
const LLConversation* getSelectedConversation();
LLHandle<LLToggleableMenu> mContextMenu;
bool mIsDirty;
bool mIsFriendsOnTop;
std::string mNameFilter;
};
/**
* Abstract comparator for ConversationLogList items
*/
class LLConversationLogListItemComparator : public LLFlatListView::ItemComparator
{
LOG_CLASS(LLConversationLogListItemComparator);
public:
LLConversationLogListItemComparator() {};
virtual ~LLConversationLogListItemComparator() {};
virtual bool compare(const LLPanel* item1, const LLPanel* item2) const;
protected:
virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const = 0;
};
class LLConversationLogListNameComparator : public LLConversationLogListItemComparator
{
LOG_CLASS(LLConversationLogListNameComparator);
public:
LLConversationLogListNameComparator() {};
virtual ~LLConversationLogListNameComparator() {};
protected:
virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const;
};
class LLConversationLogListDateComparator : public LLConversationLogListItemComparator
{
LOG_CLASS(LLConversationLogListDateComparator);
public:
LLConversationLogListDateComparator() {};
virtual ~LLConversationLogListDateComparator() {};
protected:
virtual bool doCompare(const LLConversationLogListItem* conversation1, const LLConversationLogListItem* conversation2) const;
};
#endif /* LLCONVERSATIONLOGLIST_H_ */

View File

@ -0,0 +1,157 @@
/**
* @file llconversationloglistitem.cpp
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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"
// llui
#include "lliconctrl.h"
#include "lltextbox.h"
#include "lltextutil.h"
// newview
#include "llavatariconctrl.h"
#include "llconversationlog.h"
#include "llconversationloglistitem.h"
#include "llgroupiconctrl.h"
#include "llinventoryicon.h"
LLConversationLogListItem::LLConversationLogListItem(const LLConversation* conversation)
: LLPanel(),
mConversation(conversation),
mConversationName(NULL),
mConversationDate(NULL)
{
buildFromFile("panel_conversation_log_list_item.xml");
LLIMFloater* floater = LLIMFloater::findInstance(mConversation->getSessionID());
bool has_offline_ims = !mConversation->isVoice() && mConversation->hasOfflineMessages();
bool ims_are_read = LLIMFloater::isVisible(floater) && floater->hasFocus();
if (has_offline_ims && !ims_are_read)
{
mIMFloaterShowedConnection = LLIMFloater::setIMFloaterShowedCallback(boost::bind(&LLConversationLogListItem::onIMFloaterShown, this, _1));
}
}
LLConversationLogListItem::~LLConversationLogListItem()
{
mIMFloaterShowedConnection.disconnect();
}
BOOL LLConversationLogListItem::postBuild()
{
initIcons();
// set conversation name
mConversationName = getChild<LLTextBox>("conversation_name");
mConversationName->setValue(mConversation->getConversationName());
// set conversation date and time
mConversationDate = getChild<LLTextBox>("date_time");
mConversationDate->setValue(mConversation->getTimestamp());
getChild<LLButton>("delete_btn")->setClickedCallback(boost::bind(&LLConversationLogListItem::onRemoveBtnClicked, this));
return TRUE;
}
void LLConversationLogListItem::initIcons()
{
switch (mConversation->getConversationType())
{
case LLIMModel::LLIMSession::P2P_SESSION:
case LLIMModel::LLIMSession::ADHOC_SESSION:
{
LLAvatarIconCtrl* avatar_icon = getChild<LLAvatarIconCtrl>("avatar_icon");
avatar_icon->setVisible(TRUE);
avatar_icon->setValue(mConversation->getParticipantID());
break;
}
case LLIMModel::LLIMSession::GROUP_SESSION:
{
LLGroupIconCtrl* group_icon = getChild<LLGroupIconCtrl>("group_icon");
group_icon->setVisible(TRUE);
group_icon->setValue(mConversation->getSessionID());
break;
}
default:
break;
}
if (mConversation->isVoice())
{
getChild<LLIconCtrl>("voice_session_icon")->setVisible(TRUE);
}
else
{
if (mConversation->hasOfflineMessages())
{
getChild<LLIconCtrl>("unread_ims_icon")->setVisible(TRUE);
}
}
}
void LLConversationLogListItem::onMouseEnter(S32 x, S32 y, MASK mask)
{
getChildView("hovered_icon")->setVisible(true);
LLPanel::onMouseEnter(x, y, mask);
}
void LLConversationLogListItem::onMouseLeave(S32 x, S32 y, MASK mask)
{
getChildView("hovered_icon")->setVisible(false);
LLPanel::onMouseLeave(x, y, mask);
}
void LLConversationLogListItem::setValue(const LLSD& value)
{
if (!value.isMap() || !value.has("selected"))
{
return;
}
getChildView("selected_icon")->setVisible(value["selected"]);
}
void LLConversationLogListItem::onIMFloaterShown(const LLUUID& session_id)
{
if (mConversation->getSessionID() == session_id)
{
getChild<LLIconCtrl>("unread_ims_icon")->setVisible(FALSE);
}
}
void LLConversationLogListItem::onRemoveBtnClicked()
{
LLConversationLog::instance().removeConversation(*mConversation);
}
void LLConversationLogListItem::highlightNameDate(const std::string& highlited_text)
{
LLStyle::Params params;
LLTextUtil::textboxSetHighlightedVal(mConversationName, params, mConversation->getConversationName(), highlited_text);
LLTextUtil::textboxSetHighlightedVal(mConversationDate, params, mConversation->getTimestamp(), highlited_text);
}

View File

@ -0,0 +1,77 @@
/**
* @file llconversationloglistitem.h
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 LLCONVERSATIONLOGLISTITEM_H_
#define LLCONVERSATIONLOGLISTITEM_H_
#include "llimfloater.h"
#include "llpanel.h"
class LLTextBox;
class LLConversation;
/**
* This class is a visual representation of LLConversation, each of which is LLConversationLog entry.
* LLConversationLogList consists of these LLConversationLogListItems.
* LLConversationLogListItem consists of:
* conversaion_type_icon
* conversaion_name
* conversaion_date
* Also LLConversationLogListItem holds pointer to its LLConversationLog.
*/
class LLConversationLogListItem : public LLPanel
{
public:
LLConversationLogListItem(const LLConversation* conversation);
virtual ~LLConversationLogListItem();
void onMouseEnter(S32 x, S32 y, MASK mask);
void onMouseLeave(S32 x, S32 y, MASK mask);
virtual void setValue(const LLSD& value);
virtual BOOL postBuild();
void onIMFloaterShown(const LLUUID& session_id);
void onRemoveBtnClicked();
const LLConversation* getConversation() const { return mConversation; }
void highlightNameDate(const std::string& highlited_text);
private:
void initIcons();
const LLConversation* mConversation;
LLTextBox* mConversationName;
LLTextBox* mConversationDate;
boost::signals2::connection mIMFloaterShowedConnection;
};
#endif /* LLCONVERSATIONLOGITEM_H_ */

View File

@ -0,0 +1,127 @@
/**
* @file llfloaterconversationlog.cpp
* @brief Functionality of the "conversation log" floater
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 "llconversationloglist.h"
#include "llfiltereditor.h"
#include "llfloaterconversationlog.h"
#include "llmenubutton.h"
LLFloaterConversationLog::LLFloaterConversationLog(const LLSD& key)
: LLFloater(key),
mConversationLogList(NULL)
{
mCommitCallbackRegistrar.add("CallLog.Action", boost::bind(&LLFloaterConversationLog::onCustomAction, this, _2));
mEnableCallbackRegistrar.add("CallLog.Check", boost::bind(&LLFloaterConversationLog::isActionChecked, this, _2));
}
BOOL LLFloaterConversationLog::postBuild()
{
mConversationLogList = getChild<LLConversationLogList>("conversation_log_list");
switch (gSavedSettings.getU32("CallLogSortOrder"))
{
case E_SORT_BY_NAME:
mConversationLogList->sortByName();
break;
case E_SORT_BY_DATE:
mConversationLogList->sortByDate();
break;
}
// Use the context menu of the Conversation list for the Conversation tab gear menu.
LLToggleableMenu* conversations_gear_menu = mConversationLogList->getContextMenu();
if (conversations_gear_menu)
{
getChild<LLMenuButton>("conversations_gear_btn")->setMenu(conversations_gear_menu, LLMenuButton::MP_BOTTOM_LEFT);
}
getChild<LLFilterEditor>("people_filter_input")->setCommitCallback(boost::bind(&LLFloaterConversationLog::onFilterEdit, this, _2));
return LLFloater::postBuild();
}
void LLFloaterConversationLog::draw()
{
LLFloater::draw();
}
void LLFloaterConversationLog::onFilterEdit(const std::string& search_string)
{
std::string filter = search_string;
LLStringUtil::trimHead(filter);
mConversationLogList->setNameFilter(filter);
}
void LLFloaterConversationLog::onCustomAction (const LLSD& userdata)
{
const std::string command_name = userdata.asString();
if ("sort_by_name" == command_name)
{
mConversationLogList->sortByName();
gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_NAME);
}
else if ("sort_by_date" == command_name)
{
mConversationLogList->sortByDate();
gSavedSettings.setU32("CallLogSortOrder", E_SORT_BY_DATE);
}
else if ("sort_friends_on_top" == command_name)
{
mConversationLogList->toggleSortFriendsOnTop();
}
}
bool LLFloaterConversationLog::isActionEnabled(const LLSD& userdata)
{
return true;
}
bool LLFloaterConversationLog::isActionChecked(const LLSD& userdata)
{
const std::string command_name = userdata.asString();
U32 sort_order = gSavedSettings.getU32("CallLogSortOrder");
if ("sort_by_name" == command_name)
{
return sort_order == E_SORT_BY_NAME;
}
else if ("sort_by_date" == command_name)
{
return sort_order == E_SORT_BY_DATE;
}
else if ("sort_friends_on_top" == command_name)
{
return gSavedSettings.getBOOL("SortFriendsFirst");
}
return false;
}

View File

@ -0,0 +1,61 @@
/**
* @file llfloaterconversationlog.h
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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_LLFLOATERCONVERSATIONLOG_H_
#define LL_LLFLOATERCONVERSATIONLOG_H_
#include "llfloater.h"
class LLConversationLogList;
class LLFloaterConversationLog : public LLFloater
{
public:
typedef enum e_sort_oder{
E_SORT_BY_NAME = 0,
E_SORT_BY_DATE = 1,
} ESortOrder;
LLFloaterConversationLog(const LLSD& key);
virtual ~LLFloaterConversationLog(){};
virtual BOOL postBuild();
virtual void draw();
void onFilterEdit(const std::string& search_string);
private:
void onCustomAction (const LLSD& userdata);
bool isActionEnabled(const LLSD& userdata);
bool isActionChecked(const LLSD& userdata);
LLConversationLogList* mConversationLogList;
};
#endif /* LLFLOATERCONVERSATIONLOG_H_ */

View File

@ -0,0 +1,112 @@
/**
* @file llfloaterconversationpreview.cpp
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 "llconversationlog.h"
#include "llfloaterconversationpreview.h"
#include "llimview.h"
#include "lllineeditor.h"
LLFloaterConversationPreview::LLFloaterConversationPreview(const LLSD& session_id)
: LLFloater(session_id),
mChatHistory(NULL),
mSessionID(session_id.asUUID())
{}
BOOL LLFloaterConversationPreview::postBuild()
{
mChatHistory = getChild<LLChatHistory>("chat_history");
const LLConversation* conv = LLConversationLog::instance().getConversation(mSessionID);
if (conv)
{
std::string name = conv->getConversationName();
LLStringUtil::format_map_t args;
args["[NAME]"] = name;
std::string title = getString("Title", args);
setTitle(title);
getChild<LLLineEditor>("description")->setValue(name);
}
return LLFloater::postBuild();
}
void LLFloaterConversationPreview::draw()
{
LLFloater::draw();
}
void LLFloaterConversationPreview::onOpen(const LLSD& session_id)
{
const LLConversation* conv = LLConversationLog::instance().getConversation(session_id);
if (!conv)
{
return;
}
std::list<LLSD> messages;
std::string file = conv->getHistoryFileName();
LLLogChat::loadAllHistory(file, messages);
if (messages.size())
{
std::ostringstream message;
std::list<LLSD>::const_iterator iter = messages.begin();
for (; iter != messages.end(); ++iter)
{
LLSD msg = *iter;
std::string time = msg["time"].asString();
LLUUID from_id = msg["from_id"].asUUID();
std::string from = msg["from"].asString();
std::string message = msg["message"].asString();
bool is_history = msg["is_history"].asBoolean();
LLChat chat;
chat.mFromID = from_id;
chat.mSessionID = session_id;
chat.mFromName = from;
chat.mTimeStr = time;
chat.mChatStyle = is_history ? CHAT_STYLE_HISTORY : chat.mChatStyle;
chat.mText = message;
appendMessage(chat);
}
}
}
void LLFloaterConversationPreview::appendMessage(const LLChat& chat)
{
if (!chat.mMuted)
{
LLSD args;
args["use_plain_text_chat_history"] = true;
args["show_time"] = true;
args["show_names_for_p2p_conv"] = true;
mChatHistory->appendMessage(chat);
}
}

View File

@ -0,0 +1,51 @@
/**
* @file llfloaterconversationpreview.h
*
* $LicenseInfo:firstyear=2012&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2012, 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 LLFLOATERCONVERSATIONPREVIEW_H_
#define LLFLOATERCONVERSATIONPREVIEW_H_
#include "llchathistory.h"
#include "llfloater.h"
class LLFloaterConversationPreview : public LLFloater
{
public:
LLFloaterConversationPreview(const LLSD& session_id);
virtual ~LLFloaterConversationPreview(){};
virtual BOOL postBuild();
virtual void draw();
virtual void onOpen(const LLSD& session_id);
private:
void appendMessage(const LLChat& chat);
LLChatHistory* mChatHistory;
LLUUID mSessionID;
};
#endif /* LLFLOATERCONVERSATIONPREVIEW_H_ */

View File

@ -60,6 +60,8 @@
#include "llnotificationmanager.h"
#include "llautoreplace.h"
floater_showed_signal_t LLIMFloater::sIMFloaterShowedSignal;
LLIMFloater::LLIMFloater(const LLUUID& session_id)
: LLIMConversation(session_id),
mLastMessageIndex(-1),
@ -771,6 +773,11 @@ void LLIMFloater::setVisible(BOOL visible)
chiclet->setToggleState(false);
}
}
if (visible)
{
sIMFloaterShowedSignal(mSessionID);
}
}
BOOL LLIMFloater::getVisible()
@ -1340,3 +1347,8 @@ void LLIMFloater::addToHost(const LLUUID& session_id)
}
}
}
boost::signals2::connection LLIMFloater::setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb)
{
return LLIMFloater::sIMFloaterShowedSignal.connect(cb);
}

View File

@ -44,6 +44,8 @@ class LLChatHistory;
class LLInventoryItem;
class LLInventoryCategory;
typedef boost::signals2::signal<void(const LLUUID& session_id)> floater_showed_signal_t;
/**
* Individual IM window that appears at the bottom of the screen,
* optionally "docked" to the bottom tray.
@ -125,7 +127,11 @@ public:
bool getStartConferenceInSameFloater() const { return mStartConferenceInSameFloater; }
static boost::signals2::connection setIMFloaterShowedCallback(const floater_showed_signal_t::slot_type& cb);
static floater_showed_signal_t sIMFloaterShowedSignal;
private:
// process focus events to set a currently active session
/* virtual */ void onFocusLost();
/* virtual */ void onFocusReceived();

View File

@ -107,6 +107,7 @@ BOOL LLIMFloaterContainer::postBuild()
p.rect = mConversationsListPanel->getLocalRect();
p.follows.flags = FOLLOWS_ALL;
p.listener = base_item;
p.root = NULL;
mConversationsRoot = LLUICtrlFactory::create<LLFolderView>(p);
mConversationsListPanel->addChild(mConversationsRoot);

View File

@ -175,10 +175,11 @@ LLIMModel::LLIMModel()
addNewMsgCallback(boost::bind(&toast_callback, _1));
}
LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice)
LLIMModel::LLIMSession::LLIMSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg)
: mSessionID(session_id),
mName(name),
mType(type),
mHasOfflineMessage(has_offline_msg),
mParticipantUnreadMessageCount(0),
mNumUnread(0),
mOtherParticipantID(other_participant_id),
@ -375,6 +376,8 @@ void LLIMModel::LLIMSession::onVoiceChannelStateChanged(const LLVoiceChannel::ES
break;
}
}
default:
break;
}
// Update speakers list when connected
if (LLVoiceChannel::STATE_CONNECTED == new_state)
@ -676,7 +679,7 @@ void LLIMModel::testMessages()
//session name should not be empty
bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type,
const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice)
const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg)
{
if (name.empty())
{
@ -690,7 +693,7 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co
return false;
}
LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice);
LLIMSession* session = new LLIMSession(session_id, name, type, other_participant_id, ids, voice, has_offline_msg);
mId2SessionMap[session_id] = session;
// When notifying observer, name of session is used instead of "name", because they may not be the
@ -702,10 +705,10 @@ bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, co
}
bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice)
bool LLIMModel::newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id, bool voice, bool has_offline_msg)
{
uuid_vec_t no_ids;
return newSession(session_id, name, type, other_participant_id, no_ids, voice);
return newSession(session_id, name, type, other_participant_id, no_ids, voice, has_offline_msg);
}
bool LLIMModel::clearSession(const LLUUID& session_id)
@ -2398,6 +2401,7 @@ void LLIMMgr::addMessage(
const LLUUID& target_id,
const std::string& from,
const std::string& msg,
bool is_offline_msg,
const std::string& session_name,
EInstantMessage dialog,
U32 parent_estate_id,
@ -2423,7 +2427,7 @@ void LLIMMgr::addMessage(
bool new_session = !hasSession(new_session_id);
if (new_session)
{
LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id);
LLIMModel::getInstance()->newSession(new_session_id, fixed_session_name, dialog, other_participant_id, false, is_offline_msg);
// When we get a new IM, and if you are a god, display a bit
// of information about the source. This is to help liaisons
@ -3307,6 +3311,7 @@ public:
from_id,
name,
buffer,
IM_OFFLINE == offline,
std::string((char*)&bin_bucket[0]),
IM_SESSION_INVITE,
message_params["parent_estate_id"].asInteger(),

View File

@ -70,10 +70,11 @@ public:
GROUP_SESSION,
ADHOC_SESSION,
AVALINE_SESSION,
NONE_SESSION,
} SType;
LLIMSession(const LLUUID& session_id, const std::string& name,
const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice);
const EInstantMessage& type, const LLUUID& other_participant_id, const uuid_vec_t& ids, bool voice, bool has_offline_msg);
virtual ~LLIMSession();
void sessionInitReplyReceived(const LLUUID& new_session_id);
@ -133,6 +134,8 @@ public:
//if IM session is created for a voice call
bool mStartedAsIMCall;
bool mHasOfflineMessage;
private:
void onAdHocNameCache(const LLAvatarName& av_name);
@ -181,10 +184,10 @@ public:
* @param name session name should not be empty, will return false if empty
*/
bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type, const LLUUID& other_participant_id,
const uuid_vec_t& ids, bool voice = false);
const uuid_vec_t& ids, bool voice = false, bool has_offline_msg = false);
bool newSession(const LLUUID& session_id, const std::string& name, const EInstantMessage& type,
const LLUUID& other_participant_id, bool voice = false);
const LLUUID& other_participant_id, bool voice = false, bool has_offline_msg = false);
/**
* Remove all session data associated with a session specified by session_id
@ -325,6 +328,7 @@ public:
const LLUUID& target_id,
const std::string& from,
const std::string& msg,
bool is_offline_msg = false,
const std::string& session_name = LLStringUtil::null,
EInstantMessage dialog = IM_NOTHING_SPECIAL,
U32 parent_estate_id = 0,

View File

@ -659,6 +659,7 @@ LLFolderView * LLInventoryPanel::createFolderView(LLInvFVBridge * bridge, bool u
p.allow_multiselect = mAllowMultiSelect;
p.show_empty_message = mShowEmptyMessage;
p.show_item_link_overlays = mShowItemLinkOverlays;
p.root = NULL;
return LLUICtrlFactory::create<LLFolderView>(p);
}

View File

@ -1563,6 +1563,7 @@ void LLPanelObjectInventory::reset()
p.listener = LLTaskInvFVBridge::createObjectBridge(this, NULL);
p.folder_indentation = -14; // subtract space normally reserved for folder expanders
p.view_model = &mInventoryViewModel;
p.root = NULL;
mFolders = LLUICtrlFactory::create<LLFolderView>(p);
// this ensures that we never say "searching..." or "no items found"
//TODO RN: make this happen by manipulating filter object directly

View File

@ -94,6 +94,7 @@
#include "llcallingcard.h"
#include "llconsole.h"
#include "llcontainerview.h"
#include "llconversationlog.h"
#include "lldebugview.h"
#include "lldrawable.h"
#include "lleventnotifier.h"
@ -1268,6 +1269,8 @@ bool idle_startup()
display_startup();
LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT );
LLConversationLog::getInstance();
return FALSE;
}

View File

@ -50,6 +50,8 @@
#include "llfloaterbump.h"
#include "llfloaterbvhpreview.h"
#include "llfloatercamera.h"
#include "llfloaterconversationlog.h"
#include "llfloaterconversationpreview.h"
#include "llfloaterdeleteenvpreset.h"
#include "llfloaterdisplayname.h"
#include "llfloatereditdaycycle.h"
@ -189,6 +191,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("camera", "floater_camera.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCamera>);
LLFloaterReg::add("chat_bar", "floater_im_session.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLNearbyChat>);
LLFloaterReg::add("compile_queue", "floater_script_queue.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterCompileQueue>);
LLFloaterReg::add("conversation", "floater_conversation_log.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationLog>);
LLFloaterReg::add("destinations", "floater_destinations.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterDestinations>);
@ -262,6 +265,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("picks", "floater_picks.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterSidePanelContainer>);
LLFloaterReg::add("pref_joystick", "floater_joystick.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterJoystick>);
LLFloaterReg::add("preview_anim", "floater_preview_animation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewAnim>, "preview");
LLFloaterReg::add("preview_conversation", "floater_conversation_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterConversationPreview>);
LLFloaterReg::add("preview_gesture", "floater_preview_gesture.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewGesture>, "preview");
LLFloaterReg::add("preview_notecard", "floater_preview_notecard.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewNotecard>, "preview");
LLFloaterReg::add("preview_script", "floater_script_preview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLPreviewLSL>, "preview");

View File

@ -2438,6 +2438,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
from_id,
name,
buffer,
IM_OFFLINE == offline,
LLStringUtil::null,
dialog,
parent_estate_id,
@ -2477,7 +2478,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
if (!gIMMgr->isNonFriendSessionNotified(session_id))
{
std::string message = LLTrans::getString("IM_unblock_only_groups_friends");
gIMMgr->addMessage(session_id, from_id, name, message);
gIMMgr->addMessage(session_id, from_id, name, message, IM_OFFLINE == offline);
gIMMgr->addNotifiedNonFriendSessionID(session_id);
}
@ -2490,6 +2491,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
from_id,
name,
buffer,
IM_OFFLINE == offline,
LLStringUtil::null,
dialog,
parent_estate_id,
@ -2830,6 +2832,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)
from_id,
name,
buffer,
IM_OFFLINE == offline,
ll_safe_string((char*)binary_bucket),
IM_SESSION_INVITE,
parent_estate_id,

View File

@ -3964,6 +3964,7 @@ void LLVivoxVoiceClient::messageEvent(
session->mCallerID,
session->mName.c_str(),
message.c_str(),
false,
LLStringUtil::null, // default arg
IM_NOTHING_SPECIAL, // default arg
0, // default arg

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<floater
can_resize="true"
positioning="cascading"
height="400"
min_height="100"
min_width="390"
layout="topleft"
name="floater_conversation_log"
save_rect="true"
single_instance="true"
reuse_instance="true"
title="CONVERSATION LOG"
width="450">
<panel
follows="left|top|right"
height="27"
layout="topleft"
left="0"
name="buttons_panel"
top="0">
<filter_editor
follows="left|top|right"
height="23"
layout="topleft"
left="8"
label="Filter People"
max_length_chars="300"
name="people_filter_input"
text_color="Black"
text_pad_left="10"
top="4"
width="364" />
<menu_button
follows="right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="Conv_toolbar_sort"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
left_pad="5"
menu_filename="menu_conversation_log_view.xml"
menu_position="bottomleft"
name="conversation_view_btn"
top="3"
width="31" />
<menu_button
follows="right"
height="25"
image_hover_unselected="Toolbar_Middle_Over"
image_overlay="OptionsMenu_Off"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
layout="topleft"
left_pad="2"
name="conversations_gear_btn"
top="3"
width="31" />
</panel>
<panel
follows="all"
height="370"
layout="topleft"
left="5"
name="buttons_panel"
right="-5"
top_pad="5">
<conversation_log_list
opaque="true"
allow_select="true"
follows="all"
height="360"
layout="topleft"
left="3"
keep_selection_visible_on_reshape="true"
item_pad="2"
multi_select="false"
name="conversation_log_list"
right="-3"
top="5" />
</panel>
</floater>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<floater
legacy_header_height="18"
can_resize="true"
default_tab_group="1"
height="361"
layout="topleft"
min_height="243"
min_width="234"
name="preview_conversation"
title="CONVERSATION:"
width="400">
<floater.string
name="Title">
CONVERSATION: [NAME]
</floater.string>
<text
type="string"
length="1"
follows="left|top"
font="SansSerif"
height="19"
layout="topleft"
left="10"
name="desc txt"
top="22"
width="90">
Description:
</text>
<line_editor
border_style="line"
border_thickness="1"
enabled="false"
follows="left|top|right"
font="SansSerif"
height="22"
layout="topleft"
left_pad="0"
max_length_bytes="127"
name="description"
width="296" />
<chat_history
font="SansSerifSmall"
follows="all"
visible="true"
height="310"
name="chat_history"
parse_highlights="true"
parse_urls="true"
left="5"
width="390">
</chat_history>
</floater>

View File

@ -56,6 +56,7 @@
image_overlay="Conv_toolbar_sort"
image_selected="Toolbar_Middle_Selected"
image_unselected="Toolbar_Middle_Off"
menu_filename="menu_participant_view.xml"
layout="topleft"
left="10"
name="sort_btn"

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<toggleable_menu
layout="topleft"
name="Conversation Context Menu">
<menu_item_call
label="IM..."
layout="topleft"
name="IM">
<on_click
function="Calllog.Action"
parameter="im" />
<on_enable
function="Calllog.Enable"
parameter="can_im" />
</menu_item_call>
<menu_item_call
label="Voice call..."
layout="topleft"
name="Call">
<on_click
function="Calllog.Action"
parameter="call" />
<on_enable
function="Calllog.Enable"
parameter="can_call" />
</menu_item_call>
<menu_item_call
label="Open chat history..."
layout="topleft"
name="Chat history">
<on_click
function="Calllog.Action"
parameter="chat_history" />
<on_enable
function="Calllog.Enable"
parameter="can_view_chat_history" />
</menu_item_call>
<menu_item_call
label="View Profile"
layout="topleft"
name="View Profile">
<on_click
function="Calllog.Action"
parameter="view_profile" />
<on_enable
function="Calllog.Enable"
parameter="can_view_profile" />
</menu_item_call>
<menu_item_call
label="Offer Teleport"
name="teleport">
<on_click
function="Calllog.Action"
parameter="offer_teleport"/>
<on_enable
function="Calllog.Enable"
parameter="can_offer_teleport"/>
</menu_item_call>
<menu_item_separator />
<menu_item_check
label="Add friend/Remove friend"
layout="topleft"
name="Friend_add_remove">
<menu_item_check.on_click
function="Calllog.Action"
parameter="add_rem_friend" />
<menu_item_check.on_check
function="Calllog.Check"
parameter="is_friend" />
<menu_item_check.on_enable
function="Calllog.Enable"
parameter="add_rem_friend" />
</menu_item_check>
<menu_item_call
label="Invite to group..."
layout="topleft"
name="Invite">
<on_click
function="Calllog.Action"
parameter="invite_to_group"/>
<on_enable
function="Calllog.Enable"
parameter="can_invite_to_group" />
</menu_item_call>
<menu_item_separator />
<menu_item_call
label="Map"
layout="topleft"
name="Map">
<on_click
function="Calllog.Action"
parameter="show_on_map" />
<on_enable
function="Calllog.Enable"
parameter="can_show_on_map" />
</menu_item_call>
<menu_item_call
label="Share"
layout="topleft"
name="Share">
<on_click
function="Calllog.Action"
parameter="share" />
<on_enable
function="Calllog.Enable"
parameter="can_share" />
</menu_item_call>
<menu_item_call
label="Pay"
layout="topleft"
name="Pay">
<on_click
function="Calllog.Action"
parameter="pay" />
<on_enable
function="Calllog.Enable"
parameter="can_pay" />
</menu_item_call>
<menu_item_check
label="Block/Unblock"
layout="topleft"
name="Block/Unblock">
<menu_item_check.on_click
function="Calllog.Action"
parameter="block"/>
<menu_item_check.on_check
function="Calllog.Check"
parameter="is_blocked" />
<menu_item_check.on_enable
function="Calllog.Enable"
parameter="can_block" />
</menu_item_check>
</toggleable_menu>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<toggleable_menu
name="menu_conversation_view"
left="0" bottom="0" visible="false"
mouse_opaque="false">
<menu_item_check
label="Sort by name"
name="sort_by_name">
<on_click
function="CallLog.Action"
parameter="sort_by_name"/>
<on_check
function="CallLog.Check"
parameter="sort_by_name"/>
</menu_item_check>
<menu_item_check
label="Sort by date"
name="sort_by_date">
<on_click
function="CallLog.Action"
parameter="sort_by_date" />
<on_check
function="CallLog.Check"
parameter="sort_by_date" />
</menu_item_check>
<menu_item_separator />
<menu_item_check
label="Sort friends on top"
name="sort_by_friends">
<on_click
function="CallLog.Action"
parameter="sort_friends_on_top" />
<on_check
function="CallLog.Check"
parameter="sort_friends_on_top" />
</menu_item_check>
</toggleable_menu>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<toggleable_menu
layout="topleft"
name="participant_manu_view">
<menu_item_check
label="Open conversation log"
name="Conversation"
visible="true">
<menu_item_check.on_check
function="Floater.Visible"
parameter="conversation" />
<menu_item_check.on_click
function="Floater.Toggle"
parameter="conversation" />
</menu_item_check>
</toggleable_menu>

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
follows="top|right|left"
height="23"
layout="topleft"
left="0"
name="conversation_log_list_item"
top="0"
width="380">
<icon
height="24"
follows="top|right|left"
image_name="ListItem_Select"
layout="topleft"
left="0"
name="selected_icon"
top="0"
visible="false"
width="380" />
<icon
follows="top|right|left"
height="24"
image_name="ListItem_Over"
layout="topleft"
left="0"
name="hovered_icon"
top="0"
visible="false"
width="380" />
<icon
default_icon_name="voice_session_icon"
follows="top|left"
height="20"
layout="topleft"
left="5"
image_name="Audio_Press"
mouse_opaque="true"
name="voice_session_icon"
top="2"
visible="false"
width="20" />
<icon
default_icon_name="incoming_unread_im_icon"
follows="top|left"
height="20"
layout="topleft"
left="5"
image_name="Movement_Backward_Off"
mouse_opaque="false"
name="unread_ims_icon"
top="2"
visible="false"
width="20" />
<avatar_icon
default_icon_name="Generic_Person"
follows="top|left"
height="20"
layout="topleft"
left_pad="5"
mouse_opaque="true"
top="2"
visible="false"
width="20" />
<group_icon
default_icon_name="Generic_Group"
follows="top|left"
height="20"
layout="topleft"
mouse_opaque="true"
top="2"
visible="false"
width="20" />
<text
follows="left|right"
font="SansSerifSmall"
font.color="DkGray"
height="15"
layout="topleft"
left_pad="5"
name="conversation_name"
parse_urls="false"
top="6"
use_ellipses="true"
width="180" />
<text
follows="right"
font="SansSerifSmall"
font.color="DkGray"
height="15"
layout="topleft"
left_pad="5"
name="date_time"
parse_urls="false"
top="6"
use_ellipses="true"
width="110"/>
<button
name="delete_btn"
layout="topleft"
follows="top|right"
image_unselected="Toast_CloseBtn"
image_selected="Toast_CloseBtn"
top="5"
left_pad="0"
height="14"
width="14"
tab_stop="false"/>
</panel>