057e7fba9b
Now, per each account, there is a read-only chat view associated with it, accessible through its item in the conversations list. This can be used to place system messages, MOTDs, insignificant errors, etc. Protocols can send text to this buffer by specifying no "chat_id" in an IM_MESSAGE_RECEIVED message.
729 lines
15 KiB
C++
729 lines
15 KiB
C++
/*
|
|
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
|
* All rights reserved. Distributed under the terms of the MIT license.
|
|
*/
|
|
|
|
#include "Conversation.h"
|
|
|
|
#include <Catalog.h>
|
|
#include <DateTimeFormat.h>
|
|
#include <Locale.h>
|
|
#include <Notification.h>
|
|
#include <StringFormat.h>
|
|
#include <StringList.h>
|
|
|
|
#include "AppPreferences.h"
|
|
#include "Cardie.h"
|
|
#include "ChatProtocolMessages.h"
|
|
#include "RenderView.h"
|
|
#include "ChatCommand.h"
|
|
#include "ConversationItem.h"
|
|
#include "ConversationView.h"
|
|
#include "Flags.h"
|
|
#include "ImageCache.h"
|
|
#include "MainWindow.h"
|
|
#include "NotifyMessage.h"
|
|
#include "ProtocolLooper.h"
|
|
#include "ProtocolManager.h"
|
|
#include "Server.h"
|
|
#include "TheApp.h"
|
|
#include "Utils.h"
|
|
|
|
|
|
Conversation::Conversation(BString id, BMessenger msgn)
|
|
:
|
|
fID(id),
|
|
fName(id),
|
|
fMessenger(msgn),
|
|
fChatView(NULL),
|
|
fLooper(NULL),
|
|
fIcon(ImageCache::Get()->GetImage("kOnePersonIcon")),
|
|
fDateFormatter(),
|
|
fRoomFlags(0),
|
|
fDisallowedFlags(0),
|
|
fNotifyMessageCount(0),
|
|
fNotifyMentionCount(0),
|
|
fUserIcon(false)
|
|
{
|
|
fConversationItem = new ConversationItem(fName.String(), this);
|
|
RegisterObserver(fConversationItem);
|
|
}
|
|
|
|
|
|
Conversation::~Conversation()
|
|
{
|
|
((TheApp*)be_app)->GetMainWindow()->RemoveConversation(this);
|
|
|
|
if (fLooper != NULL)
|
|
fLooper->RemoveConversation(this);
|
|
|
|
delete fChatView;
|
|
delete fConversationItem;
|
|
}
|
|
|
|
|
|
BString
|
|
Conversation::GetId() const
|
|
{
|
|
return fID;
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::ImMessage(BMessage* msg)
|
|
{
|
|
int32 im_what = msg->FindInt32("im_what");
|
|
|
|
switch(im_what)
|
|
{
|
|
case IM_MESSAGE_RECEIVED:
|
|
{
|
|
#undef B_TRANSLATION_CONTEXT
|
|
#define B_TRANSLATION_CONTEXT "Conversation ― Notifications"
|
|
|
|
_EnsureUser(msg);
|
|
_LogChatMessage(msg);
|
|
GetView()->MessageReceived(msg);
|
|
|
|
BString text = msg->FindString("body");
|
|
Contact* contact = GetOwnContact();
|
|
BWindow* win = fChatView->Window();
|
|
|
|
bool winFocused = (win != NULL &&
|
|
(win->IsFront() && !(win->IsMinimized())));
|
|
bool mentioned = ((contact->GetName().IsEmpty() == false
|
|
&& text.IFindFirst(contact->GetName()) != B_ERROR)
|
|
|| (text.IFindFirst(contact->GetId()) != B_ERROR));
|
|
|
|
// Send a notification, if appropriate
|
|
if (winFocused == false && AppPreferences::Get()->NotifyNewMessage
|
|
&& (fUsers.CountItems() <= 2 || mentioned == true))
|
|
{
|
|
BString notifyTitle = B_TRANSLATE("New mention");
|
|
BString notifyText = B_TRANSLATE("You've been summoned from "
|
|
"%source%.");
|
|
|
|
if (mentioned == false) {
|
|
fNotifyMessageCount++;
|
|
|
|
notifyTitle.SetTo(B_TRANSLATE("New message"));
|
|
notifyText.SetTo("");
|
|
|
|
BStringFormat pmFormat(B_TRANSLATE("{0, plural,"
|
|
"=1{You've got a new message from %source%.}"
|
|
"other{You've got # new messages from %source%.}}"));
|
|
pmFormat.Format(notifyText, fNotifyMessageCount);
|
|
}
|
|
else
|
|
fNotifyMentionCount++;
|
|
|
|
notifyText.ReplaceAll("%source%", GetName());
|
|
|
|
BBitmap* icon = IconBitmap();
|
|
if (icon == NULL)
|
|
icon = ProtocolBitmap();
|
|
|
|
|
|
BNotification notification(B_INFORMATION_NOTIFICATION);
|
|
notification.SetGroup(BString(APP_NAME));
|
|
notification.SetTitle(notifyTitle);
|
|
notification.SetIcon(icon);
|
|
notification.SetContent(notifyText);
|
|
notification.SetMessageID(fID);
|
|
notification.Send();
|
|
}
|
|
|
|
// If unattached, highlight the ConversationItem
|
|
if (win == NULL && mentioned == true)
|
|
NotifyInteger(INT_NEW_MENTION, fNotifyMentionCount);
|
|
else if (win == NULL)
|
|
NotifyInteger(INT_NEW_MESSAGE, fNotifyMessageCount);
|
|
|
|
break;
|
|
}
|
|
case IM_MESSAGE_SENT:
|
|
{
|
|
_LogChatMessage(msg);
|
|
GetView()->MessageReceived(msg);
|
|
break;
|
|
}
|
|
case IM_SEND_MESSAGE:
|
|
{
|
|
#undef B_TRANSLATION_CONTEXT
|
|
#define B_TRANSLATION_CONTEXT "Conversation ― Command info"
|
|
|
|
BString body;
|
|
if (msg->FindString("body", &body) != B_OK)
|
|
break;
|
|
|
|
if (IsCommand(body.String()) == false) {
|
|
fMessenger.SendMessage(msg);
|
|
break;
|
|
}
|
|
|
|
BString name = CommandName(body);
|
|
BString args = CommandArgs(body);
|
|
ChatCommand* cmd = _GetServer()->CommandById(name, fLooper->GetInstance());
|
|
|
|
if (cmd == NULL) {
|
|
if (name == "me")
|
|
fMessenger.SendMessage(msg);
|
|
else
|
|
_WarnUser(BString(B_TRANSLATE("That isn't a valid command. "
|
|
"Try /help for a list.")));
|
|
break;
|
|
}
|
|
|
|
BString error("");
|
|
if (cmd->Parse(args, &error, this) == false)
|
|
_WarnUser(error);
|
|
break;
|
|
}
|
|
case IM_ROOM_METADATA:
|
|
{
|
|
BString name;
|
|
if (msg->FindString("chat_name", &name) == B_OK)
|
|
SetNotifyName(name.String());
|
|
|
|
BString subject;
|
|
if (msg->FindString("subject", &subject) == B_OK)
|
|
SetNotifySubject(subject.String());
|
|
|
|
int32 defaultFlags;
|
|
if (msg->FindInt32("room_default_flags", &defaultFlags) == B_OK)
|
|
if (fRoomFlags == 0)
|
|
fRoomFlags = defaultFlags;
|
|
|
|
int32 disabledFlags;
|
|
if (msg->FindInt32("room_disallowed_flags", &disabledFlags) == B_OK)
|
|
fDisallowedFlags = disabledFlags;
|
|
_CacheRoomFlags();
|
|
break;
|
|
}
|
|
case IM_ROOM_PARTICIPANT_JOINED:
|
|
{
|
|
BString user_id;
|
|
if (msg->FindString("user_id", &user_id) != B_OK)
|
|
break;
|
|
|
|
if (UserById(user_id) == NULL) {
|
|
_EnsureUser(msg);
|
|
GetView()->MessageReceived(msg);
|
|
}
|
|
break;
|
|
}
|
|
case IM_ROOM_PARTICIPANT_LEFT:
|
|
case IM_ROOM_PARTICIPANT_KICKED:
|
|
case IM_ROOM_PARTICIPANT_BANNED:
|
|
{
|
|
BString user_id = msg->FindString("user_id");
|
|
User* user;
|
|
if (user_id.IsEmpty() == true || (user = UserById(user_id)) == NULL)
|
|
break;
|
|
|
|
GetView()->MessageReceived(msg);
|
|
RemoveUser(user);
|
|
break;
|
|
}
|
|
case IM_ROOM_ROLECHANGED:
|
|
{
|
|
BString user_id;
|
|
Role* role = _GetRole(msg);
|
|
if (msg->FindString("user_id", &user_id) != B_OK || role == NULL)
|
|
break;
|
|
|
|
SetRole(user_id, role);
|
|
GetView()->MessageReceived(msg);
|
|
break;
|
|
}
|
|
case IM_USER_NICKNAME_SET:
|
|
{
|
|
BString user_id = msg->FindString("user_id");
|
|
BString user_name = msg->FindString("user_name");
|
|
if (user_id.IsEmpty() == false && user_name.IsEmpty() == false) {
|
|
User* user = UserById(user_id);
|
|
|
|
BString text(B_TRANSLATE("** %old% has changed their nick to %new%."));
|
|
text.ReplaceAll("%new%", user_name);
|
|
if (user != NULL)
|
|
text.ReplaceAll("%old%", user->GetName());
|
|
else
|
|
text.ReplaceAll("%old%", user_id);
|
|
|
|
BMessage* notify = new BMessage(IM_MESSAGE);
|
|
notify->AddInt32("im_what", IM_MESSAGE_RECEIVED);
|
|
notify->AddString("body", text);
|
|
GetView()->MessageReceived(notify);
|
|
}
|
|
break;
|
|
}
|
|
case IM_LOGS_RECEIVED:
|
|
default:
|
|
GetView()->MessageReceived(msg);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::ObserveString(int32 what, BString str)
|
|
{
|
|
GetView()->InvalidateUserList();
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::ObserveInteger(int32 what, int32 value)
|
|
{
|
|
if (what == INT_WINDOW_FOCUSED) {
|
|
fNotifyMessageCount = 0;
|
|
fNotifyMentionCount = 0;
|
|
}
|
|
else
|
|
GetView()->InvalidateUserList();
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::ObservePointer(int32 what, void* ptr)
|
|
{
|
|
GetView()->InvalidateUserList();
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::SetNotifyName(const char* name)
|
|
{
|
|
if (BString(name) == fName)
|
|
return;
|
|
|
|
fName = name;
|
|
NotifyString(STR_ROOM_NAME, fName.String());
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::SetNotifySubject(const char* subject)
|
|
{
|
|
if (BString(subject) == fSubject)
|
|
return;
|
|
|
|
fSubject = subject;
|
|
NotifyString(STR_ROOM_SUBJECT, fSubject.String());
|
|
}
|
|
|
|
|
|
bool
|
|
Conversation::SetNotifyIconBitmap(BBitmap* icon)
|
|
{
|
|
if (icon != NULL) {
|
|
fIcon = icon;
|
|
NotifyPointer(PTR_ROOM_BITMAP, (void*)icon);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
BMessenger
|
|
Conversation::Messenger() const
|
|
{
|
|
return fMessenger;
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::SetMessenger(BMessenger messenger)
|
|
{
|
|
fMessenger = messenger;
|
|
}
|
|
|
|
|
|
ProtocolLooper*
|
|
Conversation::GetProtocolLooper() const
|
|
{
|
|
return fLooper;
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::SetProtocolLooper(ProtocolLooper* looper)
|
|
{
|
|
fLooper = looper;
|
|
_LoadRoomFlags();
|
|
}
|
|
|
|
|
|
BBitmap*
|
|
Conversation::ProtocolBitmap() const
|
|
{
|
|
ChatProtocol* protocol = fLooper->Protocol();
|
|
ChatProtocolAddOn* addOn
|
|
= ProtocolManager::Get()->ProtocolAddOn(protocol->Signature());
|
|
|
|
return addOn->ProtoIcon();
|
|
}
|
|
|
|
|
|
BBitmap*
|
|
Conversation::IconBitmap() const
|
|
{
|
|
return fIcon;
|
|
}
|
|
|
|
|
|
BString
|
|
Conversation::GetName() const
|
|
{
|
|
return fName;
|
|
}
|
|
|
|
|
|
BString
|
|
Conversation::GetSubject() const
|
|
{
|
|
return fSubject;
|
|
}
|
|
|
|
|
|
ConversationView*
|
|
Conversation::GetView()
|
|
{
|
|
if (fChatView != NULL)
|
|
return fChatView;
|
|
|
|
fChatView = new ConversationView(this);
|
|
fChatView->RegisterObserver(fConversationItem);
|
|
fChatView->RegisterObserver(this);
|
|
RegisterObserver(fChatView);
|
|
|
|
if (!(fRoomFlags & ROOM_POPULATE_LOGS))
|
|
return fChatView;
|
|
|
|
BMessage logMsg;
|
|
if (_GetChatLogs(&logMsg) == B_OK)
|
|
fChatView->MessageReceived(&logMsg);
|
|
|
|
return fChatView;
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::ShowView(bool typing, bool userAction)
|
|
{
|
|
((TheApp*)be_app)->GetMainWindow()->SetConversation(this);
|
|
}
|
|
|
|
|
|
ConversationItem*
|
|
Conversation::GetListItem()
|
|
{
|
|
return fConversationItem;
|
|
}
|
|
|
|
|
|
UserMap
|
|
Conversation::Users()
|
|
{
|
|
return fUsers;
|
|
}
|
|
|
|
|
|
User*
|
|
Conversation::UserById(BString id)
|
|
{
|
|
bool found = false;
|
|
return fUsers.ValueFor(id, &found);
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::AddUser(User* user)
|
|
{
|
|
BMessage msg;
|
|
msg.AddString("user_id", user->GetId());
|
|
msg.AddString("user_name", user->GetName());
|
|
_EnsureUser(&msg);
|
|
_SortConversationList();
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::RemoveUser(User* user)
|
|
{
|
|
fUsers.RemoveItemFor(user->GetId());
|
|
user->UnregisterObserver(this);
|
|
GetView()->UpdateUserList(fUsers);
|
|
_SortConversationList();
|
|
|
|
_UpdateIcon();
|
|
NotifyInteger(INT_ROOM_MEMBERS, fUsers.CountItems());
|
|
}
|
|
|
|
|
|
Contact*
|
|
Conversation::GetOwnContact()
|
|
{
|
|
return fLooper->GetOwnContact();
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::SetRole(BString id, Role* role)
|
|
{
|
|
Role* oldRole = fRoles.ValueFor(id);
|
|
if (oldRole != NULL) {
|
|
fRoles.RemoveItemFor(id);
|
|
delete oldRole;
|
|
}
|
|
fRoles.AddItem(id, role);
|
|
}
|
|
|
|
|
|
Role*
|
|
Conversation::GetRole(BString id)
|
|
{
|
|
return fRoles.ValueFor(id);
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_WarnUser(BString message)
|
|
{
|
|
BMessage* warning = new BMessage(IM_MESSAGE);
|
|
warning->AddInt32("im_what", IM_MESSAGE_RECEIVED);
|
|
warning->AddString("body", message.Append('\n', 1).InsertChars("-- ", 0));
|
|
GetView()->MessageReceived(warning);
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_LogChatMessage(BMessage* msg)
|
|
{
|
|
BString date;
|
|
fDateFormatter.Format(date, time(0), B_SHORT_DATE_FORMAT, B_MEDIUM_TIME_FORMAT);
|
|
|
|
BString id = msg->FindString("user_id");
|
|
BString name = msg->FindString("user_name");
|
|
BString body = msg->FindString("body");
|
|
|
|
if (id.IsEmpty() == true)
|
|
return;
|
|
|
|
if (name.IsEmpty() == true) {
|
|
User* user = UserById(id);
|
|
if (user == NULL)
|
|
name = id;
|
|
else
|
|
name = user->GetName();
|
|
}
|
|
|
|
// Binary logs
|
|
// TODO: Don't hardcode 31, expose maximum as a setting
|
|
int32 max = 31;
|
|
BStringList user_ids, user_names, bodies;
|
|
int64 times[max] = { 0 };
|
|
times[0] = (int64)time(NULL);
|
|
|
|
BMessage logMsg;
|
|
if (_GetChatLogs(&logMsg) == B_OK) {
|
|
logMsg.FindStrings("body", &bodies);
|
|
logMsg.FindStrings("user_id", &user_ids);
|
|
logMsg.FindStrings("user_name", &user_names);
|
|
|
|
int64 found;
|
|
for (int i = 0; i < max; i++)
|
|
if (logMsg.FindInt64("when", i, &found) == B_OK)
|
|
times[i + 1] = found;
|
|
|
|
bodies.Remove(max);
|
|
user_ids.Remove(max);
|
|
user_names.Remove(max);
|
|
bodies.Add(body, 0);
|
|
user_ids.Add(id, 0);
|
|
user_names.Add(name, 0);
|
|
}
|
|
|
|
BMessage newLogMsg(IM_MESSAGE);
|
|
newLogMsg.AddInt32("im_what", IM_LOGS_RECEIVED);
|
|
newLogMsg.AddStrings("body", bodies);
|
|
newLogMsg.AddStrings("user_id", user_ids);
|
|
newLogMsg.AddStrings("user_name", user_names);
|
|
newLogMsg.AddInt64("when", time(NULL));
|
|
for (int i = 0; i < max; i++)
|
|
newLogMsg.AddInt64("when", times[i]);
|
|
|
|
BFile logFile(fCachePath.Path(), B_READ_WRITE | B_OPEN_AT_END | B_CREATE_FILE);
|
|
WriteAttributeMessage(&logFile, "Chat:logs", &newLogMsg);
|
|
|
|
// Plain-text logs
|
|
BString logLine("[");
|
|
logLine << date << "] <" << name << "> " << body << "\n";
|
|
|
|
logFile.Write(logLine.String(), logLine.Length());
|
|
}
|
|
|
|
|
|
status_t
|
|
Conversation::_GetChatLogs(BMessage* msg)
|
|
{
|
|
_EnsureCachePath();
|
|
|
|
BFile logFile(fCachePath.Path(), B_READ_WRITE | B_CREATE_FILE);
|
|
|
|
return ReadAttributeMessage(&logFile, "Chat:logs", msg);
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_CacheRoomFlags()
|
|
{
|
|
_EnsureCachePath();
|
|
BFile cacheFile(fCachePath.Path(), B_READ_WRITE | B_CREATE_FILE);
|
|
if (cacheFile.InitCheck() != B_OK)
|
|
return;
|
|
|
|
cacheFile.WriteAttr("Chat:flags", B_INT32_TYPE, 0, &fRoomFlags, sizeof(int32));
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_LoadRoomFlags()
|
|
{
|
|
_EnsureCachePath();
|
|
BFile cacheFile(fCachePath.Path(), B_READ_ONLY);
|
|
if (cacheFile.InitCheck() != B_OK)
|
|
return;
|
|
|
|
cacheFile.ReadAttr("Chat:flags", B_INT32_TYPE, 0, &fRoomFlags, sizeof(int32));
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_EnsureCachePath()
|
|
{
|
|
if (fCachePath.InitCheck() == B_OK)
|
|
return;
|
|
fCachePath.SetTo(RoomCachePath(fLooper->Protocol()->GetName(),
|
|
fID.String()));
|
|
}
|
|
|
|
|
|
User*
|
|
Conversation::_EnsureUser(BMessage* msg)
|
|
{
|
|
BString id = msg->FindString("user_id");
|
|
BString name = msg->FindString("user_name");
|
|
if (id.IsEmpty() == true) return NULL;
|
|
|
|
User* user = UserById(id);
|
|
User* serverUser = fLooper->UserById(id);
|
|
|
|
// Not here, but found in server
|
|
if (user == NULL && serverUser != NULL) {
|
|
fUsers.AddItem(id, serverUser);
|
|
user = serverUser;
|
|
GetView()->UpdateUserList(fUsers);
|
|
_UpdateIcon(user);
|
|
NotifyInteger(INT_ROOM_MEMBERS, fUsers.CountItems());
|
|
}
|
|
// Not anywhere; create user
|
|
else if (user == NULL) {
|
|
user = new User(id, _GetServer()->Looper());
|
|
user->SetProtocolLooper(fLooper);
|
|
|
|
fLooper->AddUser(user);
|
|
fUsers.AddItem(id, user);
|
|
GetView()->UpdateUserList(fUsers);
|
|
_UpdateIcon(user);
|
|
NotifyInteger(INT_ROOM_MEMBERS, fUsers.CountItems());
|
|
}
|
|
|
|
if (name.IsEmpty() == false && user->GetName() != name)
|
|
user->SetNotifyName(name);
|
|
user->RegisterObserver(this);
|
|
return user;
|
|
}
|
|
|
|
|
|
Role*
|
|
Conversation::_GetRole(BMessage* msg)
|
|
{
|
|
if (!msg)
|
|
return NULL;
|
|
BString title;
|
|
int32 perms;
|
|
int32 priority;
|
|
|
|
if (msg->FindString("role_title", &title) != B_OK
|
|
|| msg->FindInt32("role_perms", &perms) != B_OK
|
|
|| msg->FindInt32("role_priority", &priority) != B_OK)
|
|
return NULL;
|
|
|
|
return new Role(title, perms, priority);
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_UpdateIcon(User* user)
|
|
{
|
|
if (_IsDefaultIcon(fIcon) == false && fUserIcon == false)
|
|
return;
|
|
|
|
// If it's a one-on-one chat, try to use the other user's icon
|
|
if (user != NULL && fUsers.CountItems() == 2
|
|
&& user->GetId() != GetOwnContact()->GetId()
|
|
&& _IsDefaultIcon(user->AvatarBitmap()) == false) {
|
|
fUserIcon = SetNotifyIconBitmap(user->AvatarBitmap());
|
|
return;
|
|
}
|
|
|
|
switch (fUsers.CountItems())
|
|
{
|
|
case 0:
|
|
case 1:
|
|
SetNotifyIconBitmap(ImageCache::Get()->GetImage("kOnePersonIcon"));
|
|
break;
|
|
case 2:
|
|
SetNotifyIconBitmap(ImageCache::Get()->GetImage("kTwoPeopleIcon"));
|
|
break;
|
|
case 3:
|
|
SetNotifyIconBitmap(ImageCache::Get()->GetImage("kThreePeopleIcon"));
|
|
break;
|
|
case 4:
|
|
SetNotifyIconBitmap(ImageCache::Get()->GetImage("kFourPeopleIcon"));
|
|
break;
|
|
default:
|
|
SetNotifyIconBitmap(ImageCache::Get()->GetImage("kMorePeopleIcon"));
|
|
break;
|
|
}
|
|
fUserIcon = false;
|
|
}
|
|
|
|
|
|
bool
|
|
Conversation::_IsDefaultIcon(BBitmap* icon)
|
|
{
|
|
return (icon == NULL
|
|
|| icon == ImageCache::Get()->GetImage("kPersonIcon")
|
|
|| icon == ImageCache::Get()->GetImage("kOnePersonIcon")
|
|
|| icon == ImageCache::Get()->GetImage("kTwoPeopleIcon")
|
|
|| icon == ImageCache::Get()->GetImage("kThreePeopleIcon")
|
|
|| icon == ImageCache::Get()->GetImage("kFourPeopleIcon")
|
|
|| icon == ImageCache::Get()->GetImage("kMorePeopleIcon"));
|
|
}
|
|
|
|
|
|
void
|
|
Conversation::_SortConversationList()
|
|
{
|
|
if (fUsers.CountItems() <= 2 || fUsers.CountItems() == 3)
|
|
((TheApp*)be_app)->GetMainWindow()->SortConversation(this);
|
|
}
|
|
|
|
|
|
Server*
|
|
Conversation::_GetServer()
|
|
{
|
|
return ((TheApp*)be_app)->GetMainWindow()->GetServer();
|
|
}
|