2021-05-28 08:38:33 -05:00
|
|
|
/*
|
|
|
|
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
|
2021-06-18 01:39:34 -05:00
|
|
|
* Copyright 2021, Jaidyn Levesque. All rights reserved.
|
2021-05-28 08:38:33 -05:00
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Andrea Anzani, andrea.anzani@gmail.com
|
2021-06-18 01:39:34 -05:00
|
|
|
* Jaidyn Levesque, jadedctrl@teknik.io
|
2021-05-28 08:38:33 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ConversationView.h"
|
|
|
|
|
2021-07-24 21:11:34 -05:00
|
|
|
#include <Catalog.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
#include <LayoutBuilder.h>
|
2021-05-31 10:50:43 -05:00
|
|
|
#include <ListView.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
#include <ScrollView.h>
|
2021-07-31 11:15:51 -05:00
|
|
|
#include <SplitView.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
#include <StringList.h>
|
2021-05-31 10:50:43 -05:00
|
|
|
#include <StringView.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
|
|
|
|
#include <libinterface/BitmapView.h>
|
2021-07-29 22:00:01 -05:00
|
|
|
#include <libinterface/EnterTextView.h>
|
2021-05-28 08:38:33 -05:00
|
|
|
|
2021-06-20 12:44:20 -05:00
|
|
|
#include "AppMessages.h"
|
|
|
|
#include "AppPreferences.h"
|
|
|
|
#include "ChatProtocolMessages.h"
|
2021-05-28 08:38:33 -05:00
|
|
|
#include "Conversation.h"
|
|
|
|
#include "NotifyMessage.h"
|
2021-07-28 22:11:42 -05:00
|
|
|
#include "ProtocolManager.h"
|
2021-07-16 16:28:58 -05:00
|
|
|
#include "RenderView.h"
|
2021-07-08 16:07:03 -05:00
|
|
|
#include "SendTextView.h"
|
2021-05-31 11:56:45 -05:00
|
|
|
#include "User.h"
|
2021-05-31 10:50:43 -05:00
|
|
|
#include "UserListView.h"
|
2021-06-20 12:44:20 -05:00
|
|
|
#include "Utils.h"
|
2021-05-28 08:38:33 -05:00
|
|
|
|
|
|
|
|
2021-07-24 21:11:34 -05:00
|
|
|
#undef B_TRANSLATION_CONTEXT
|
|
|
|
#define B_TRANSLATION_CONTEXT "ConversationView"
|
|
|
|
|
|
|
|
|
2021-07-28 22:11:42 -05:00
|
|
|
ConversationView::ConversationView(Conversation* chat)
|
2021-05-28 08:38:33 -05:00
|
|
|
:
|
|
|
|
BGroupView("chatView", B_VERTICAL, B_USE_DEFAULT_SPACING),
|
2021-06-30 11:12:57 -05:00
|
|
|
fMessageQueue(),
|
2021-07-28 22:11:42 -05:00
|
|
|
fConversation(chat)
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-05-31 10:50:43 -05:00
|
|
|
_InitInterface();
|
2021-07-28 22:11:42 -05:00
|
|
|
if (chat != NULL) {
|
|
|
|
SetConversation(chat);
|
|
|
|
fUserList->SetConversation(chat);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
_FakeChat();
|
2021-05-28 22:26:32 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-05-31 10:50:43 -05:00
|
|
|
ConversationView::AttachedToWindow()
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-05-31 10:50:43 -05:00
|
|
|
while (fMessageQueue.IsEmpty() == false) {
|
|
|
|
BMessage* msg = fMessageQueue.RemoveItemAt(0);
|
2021-08-12 15:43:52 -05:00
|
|
|
MessageReceived(msg);
|
2021-05-31 10:50:43 -05:00
|
|
|
}
|
2021-06-21 02:32:49 -05:00
|
|
|
if (fConversation != NULL) {
|
|
|
|
if (fNameTextView->Text() != fConversation->GetName())
|
|
|
|
fNameTextView->SetText(fConversation->GetName());
|
|
|
|
if (fSubjectTextView->Text() != fConversation->GetSubject())
|
|
|
|
fSubjectTextView->SetText(fConversation->GetSubject());
|
|
|
|
}
|
2021-07-17 00:23:56 -05:00
|
|
|
NotifyInteger(INT_WINDOW_FOCUSED, 0);
|
2021-07-23 23:58:09 -05:00
|
|
|
fSendView->MakeFocus(true);
|
2021-07-31 20:39:13 -05:00
|
|
|
fSendView->Invalidate();
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::MessageReceived(BMessage* message)
|
|
|
|
{
|
|
|
|
switch (message->what) {
|
2021-06-20 12:44:20 -05:00
|
|
|
case APP_CHAT:
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-07-08 16:07:03 -05:00
|
|
|
BString text = fSendView->Text();
|
2021-07-28 22:11:42 -05:00
|
|
|
if (fConversation == NULL || text == "")
|
2021-05-28 08:38:33 -05:00
|
|
|
return;
|
Explicitly tie Conversations, Contacts, and Users to their ProtocolLoopers
Previously, all Conversations/Contacts/Users were stored in the Server,
each in their respective KeyMaps, identified solely by their
identifiers. This leads to the glaring problem of overlap― if the user
has multiple accounts, some users/rooms might be used or present in multiple
accounts at the same time.
Now, each accounts' Contacts, Conversations, and Users are stored in
its ProtocolLooper, making this overlap impossible. An oversight of only
allowing one user identifier to be stored (fMySelf) in Server was also fixed
this way.
This is the bulk of the work required for multi-account support― now,
the user can join the same XMPP room on two seperate accounts, and it
works perfectly.
2021-06-10 15:16:43 -05:00
|
|
|
int64 instance = fConversation->GetProtocolLooper()->GetInstance();
|
2021-05-28 08:38:33 -05:00
|
|
|
|
|
|
|
BMessage msg(IM_MESSAGE);
|
|
|
|
msg.AddInt32("im_what", IM_SEND_MESSAGE);
|
Explicitly tie Conversations, Contacts, and Users to their ProtocolLoopers
Previously, all Conversations/Contacts/Users were stored in the Server,
each in their respective KeyMaps, identified solely by their
identifiers. This leads to the glaring problem of overlap― if the user
has multiple accounts, some users/rooms might be used or present in multiple
accounts at the same time.
Now, each accounts' Contacts, Conversations, and Users are stored in
its ProtocolLooper, making this overlap impossible. An oversight of only
allowing one user identifier to be stored (fMySelf) in Server was also fixed
this way.
This is the bulk of the work required for multi-account support― now,
the user can join the same XMPP room on two seperate accounts, and it
works perfectly.
2021-06-10 15:16:43 -05:00
|
|
|
msg.AddInt64("instance", instance);
|
2021-05-28 08:38:33 -05:00
|
|
|
msg.AddString("chat_id", fConversation->GetId());
|
|
|
|
msg.AddString("body", text);
|
|
|
|
fConversation->ImMessage(&msg);
|
2021-07-08 16:07:03 -05:00
|
|
|
|
|
|
|
fSendView->SetText("");
|
2021-07-31 20:18:36 -05:00
|
|
|
fSendView->ScrollToOffset(0);
|
2021-05-28 08:38:33 -05:00
|
|
|
break;
|
|
|
|
}
|
2021-08-12 15:43:52 -05:00
|
|
|
case kClearText:
|
|
|
|
_AppendOrEnqueueMessage(message);
|
|
|
|
break;
|
2021-05-28 08:38:33 -05:00
|
|
|
case IM_MESSAGE:
|
|
|
|
ImMessage(message);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
BGroupView::MessageReceived(message);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::ImMessage(BMessage* msg)
|
|
|
|
{
|
|
|
|
int32 im_what = msg->FindInt32("im_what");
|
|
|
|
|
|
|
|
switch (im_what) {
|
2021-06-04 16:32:18 -05:00
|
|
|
case IM_ROOM_LEFT:
|
|
|
|
{
|
|
|
|
delete fConversation;
|
|
|
|
delete this;
|
|
|
|
break;
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
case IM_MESSAGE_RECEIVED:
|
|
|
|
{
|
2021-05-30 19:07:50 -05:00
|
|
|
_AppendOrEnqueueMessage(msg);
|
2021-07-17 00:23:56 -05:00
|
|
|
fReceiveView->ScrollToBottom();
|
2021-05-28 08:38:33 -05:00
|
|
|
break;
|
|
|
|
}
|
2021-05-30 19:07:50 -05:00
|
|
|
case IM_MESSAGE_SENT:
|
2021-05-28 08:38:33 -05:00
|
|
|
case IM_LOGS_RECEIVED:
|
|
|
|
{
|
2021-07-21 12:10:20 -05:00
|
|
|
_AppendOrEnqueueMessage(msg);
|
2021-07-17 00:23:56 -05:00
|
|
|
if (im_what == IM_MESSAGE_SENT)
|
|
|
|
fReceiveView->ScrollToBottom();
|
2021-05-28 08:38:33 -05:00
|
|
|
break;
|
|
|
|
}
|
2021-06-18 01:39:34 -05:00
|
|
|
case IM_ROOM_JOINED:
|
|
|
|
{
|
|
|
|
BMessage msg;
|
2021-07-24 21:11:34 -05:00
|
|
|
msg.AddString("body", B_TRANSLATE("** You joined the room.\n"));
|
2021-06-18 01:39:34 -05:00
|
|
|
_AppendOrEnqueueMessage(&msg);
|
2021-07-21 12:10:20 -05:00
|
|
|
fReceiveView->ScrollToBottom();
|
2021-06-18 01:39:34 -05:00
|
|
|
}
|
|
|
|
case IM_ROOM_CREATED:
|
|
|
|
{
|
|
|
|
BMessage msg;
|
2021-07-24 21:11:34 -05:00
|
|
|
msg.AddString("body", B_TRANSLATE("** You created the room.\n"));
|
2021-06-18 01:39:34 -05:00
|
|
|
_AppendOrEnqueueMessage(&msg);
|
2021-07-21 12:10:20 -05:00
|
|
|
fReceiveView->ScrollToBottom();
|
2021-06-18 01:39:34 -05:00
|
|
|
}
|
2021-06-07 00:03:15 -05:00
|
|
|
case IM_ROOM_PARTICIPANT_JOINED:
|
|
|
|
{
|
2021-07-24 21:11:34 -05:00
|
|
|
_UserMessage(B_TRANSLATE("%user% has joined the room.\n"),
|
|
|
|
B_TRANSLATE("%user% has joined the room (%body%).\n"),
|
|
|
|
msg);
|
2021-06-07 00:03:15 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_ROOM_PARTICIPANT_LEFT:
|
|
|
|
{
|
2021-07-24 21:11:34 -05:00
|
|
|
_UserMessage(B_TRANSLATE("%user% has left the room.\n"),
|
|
|
|
B_TRANSLATE("%user% has left the room (%body%).\n"),
|
|
|
|
msg);
|
2021-06-07 00:03:15 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_ROOM_PARTICIPANT_KICKED:
|
|
|
|
{
|
2021-07-24 21:11:34 -05:00
|
|
|
_UserMessage(B_TRANSLATE("%user% was kicked.\n"),
|
|
|
|
B_TRANSLATE("%user% was kicked (%body%).\n"),msg);
|
2021-06-07 00:03:15 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_ROOM_PARTICIPANT_BANNED:
|
|
|
|
{
|
2021-07-24 21:11:34 -05:00
|
|
|
_UserMessage(B_TRANSLATE("%user% has been banned.\n"),
|
|
|
|
B_TRANSLATE("%user% has been banned (%body%).\n"),
|
|
|
|
msg);
|
2021-06-07 00:03:15 -05:00
|
|
|
break;
|
|
|
|
}
|
2021-07-29 22:00:01 -05:00
|
|
|
case IM_ROOM_ROLECHANGED:
|
|
|
|
{
|
|
|
|
BString user_id = msg->FindString("user_id");
|
|
|
|
|
|
|
|
if (user_id == fConversation->GetOwnContact()->GetId()) {
|
|
|
|
Role* role = fConversation->GetRole(user_id);
|
|
|
|
if (role == NULL)
|
|
|
|
break;
|
|
|
|
int32 perms = role->fPerms;
|
|
|
|
fNameTextView->MakeEditable(perms & PERM_ROOM_NAME);
|
|
|
|
fSubjectTextView->MakeEditable(perms & PERM_ROOM_SUBJECT);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case IM_SET_ROOM_NAME:
|
|
|
|
case IM_SET_ROOM_SUBJECT:
|
|
|
|
{
|
|
|
|
if (fConversation == NULL)
|
|
|
|
return;
|
|
|
|
fConversation->GetProtocolLooper()->MessageReceived(msg);
|
|
|
|
|
|
|
|
// Reset to current values; if the change went through, it'll
|
|
|
|
// come back.
|
|
|
|
fNameTextView->SetText(fConversation->GetName());
|
|
|
|
fSubjectTextView->SetText(fConversation->GetSubject());
|
|
|
|
break;
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-31 10:50:43 -05:00
|
|
|
Conversation*
|
|
|
|
ConversationView::GetConversation()
|
|
|
|
{
|
|
|
|
return fConversation;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::SetConversation(Conversation* chat)
|
|
|
|
{
|
2021-06-30 11:12:57 -05:00
|
|
|
if (chat == NULL)
|
|
|
|
return;
|
2021-05-31 10:50:43 -05:00
|
|
|
fConversation = chat;
|
2021-07-29 22:00:01 -05:00
|
|
|
|
|
|
|
BMessage name(IM_MESSAGE);
|
|
|
|
name.AddInt32("im_what", IM_SET_ROOM_NAME);
|
|
|
|
name.AddString("chat_id", chat->GetId());
|
2021-06-04 13:57:04 -05:00
|
|
|
fNameTextView->SetText(chat->GetName());
|
2021-07-29 22:00:01 -05:00
|
|
|
fNameTextView->SetMessage(name, "chat_name");
|
|
|
|
fNameTextView->SetTarget(this);
|
|
|
|
|
|
|
|
BMessage subject(IM_MESSAGE);
|
|
|
|
subject.AddInt32("im_what", IM_SET_ROOM_SUBJECT);
|
|
|
|
subject.AddString("chat_id", chat->GetId());
|
2021-06-21 02:32:49 -05:00
|
|
|
fSubjectTextView->SetText(chat->GetSubject());
|
2021-07-29 22:00:01 -05:00
|
|
|
fSubjectTextView->SetMessage(subject, "subject");
|
|
|
|
fSubjectTextView->SetTarget(this);
|
|
|
|
|
2021-06-04 13:57:04 -05:00
|
|
|
fProtocolView->SetBitmap(chat->ProtocolBitmap());
|
2021-05-31 10:50:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::UpdateUserList(UserMap users)
|
|
|
|
{
|
|
|
|
fUserList->MakeEmpty();
|
|
|
|
for (int i = 0; i < users.CountItems(); i++) {
|
|
|
|
User* user = users.ValueAt(i);
|
Support generally setting own nick, own UserItems
Seperate UserItems are now created for each list, too, rather than a
single one being created per-user. This functionally works a lot nicer.
But onto more important things… now setting the user's own nick should
work quite well. Finally. =w=
This has given me a good bit of trouble over the past couple of days―
setting the user's nick *worked*, but it wouldn't propagate to its
corresponding UserItem nor its UserInfoDialog. It would, however, work
with the StatusView.
These are all registered Observers of the User itself, so if one works,
they *all* should, them all being registered to the same User.
Now, if a given User isn't found in the ProtocolLooper's user-list,
the Conversation class will take it upon itself to create a new
one and add it to both of their respective lists.
So the user's own contact would be set in the ProtocolLooper― but it
*wouldn't* be added to the user-list.
Hilarity ensues as two seperate objects for the user's own contact would
be created.
Since the StatusView is registered to the ProtocolLooper's "real" own contact
slot, it would receive all updates… but since Conversations' user-lists and
items would be registered to the Conversation-created "fake" user, they
would be borked.
Simple oversight, but wow it hecked with me. :P
2021-08-05 14:10:21 -05:00
|
|
|
if (fUserList->HasUser(user) == false) {
|
|
|
|
fUserList->AddUser(user);
|
2021-07-25 14:42:38 -05:00
|
|
|
fUserList->Sort();
|
|
|
|
}
|
2021-05-31 10:50:43 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-28 08:38:33 -05:00
|
|
|
void
|
2021-06-03 23:39:50 -05:00
|
|
|
ConversationView::InvalidateUserList()
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-06-03 23:39:50 -05:00
|
|
|
for (int i = 0; i < fUserList->CountItems(); i++)
|
|
|
|
fUserList->InvalidateItem(i);
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2021-06-03 23:39:50 -05:00
|
|
|
ConversationView::ObserveString(int32 what, BString str)
|
2021-05-28 08:38:33 -05:00
|
|
|
{
|
2021-06-04 13:57:04 -05:00
|
|
|
switch (what)
|
|
|
|
{
|
2021-06-13 01:25:28 -05:00
|
|
|
case STR_ROOM_NAME:
|
|
|
|
{
|
|
|
|
fNameTextView->SetText(str);
|
|
|
|
break;
|
|
|
|
}
|
2021-06-04 13:57:04 -05:00
|
|
|
case STR_ROOM_SUBJECT:
|
|
|
|
{
|
|
|
|
fSubjectTextView->SetText(str);
|
2021-08-11 19:46:33 -05:00
|
|
|
|
2021-08-11 20:22:42 -05:00
|
|
|
BString body = B_TRANSLATE("** The subject is now: %subject%");
|
2021-08-11 19:46:33 -05:00
|
|
|
body.ReplaceAll("%subject%", str);
|
|
|
|
|
|
|
|
BMessage topic(IM_MESSAGE);
|
|
|
|
topic.AddString("body", body);
|
|
|
|
_AppendOrEnqueueMessage(&topic);
|
2021-06-04 13:57:04 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 08:38:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-12 15:43:52 -05:00
|
|
|
void
|
|
|
|
ConversationView::ObservePointer(int32 what, void* ptr)
|
|
|
|
{
|
|
|
|
switch (what)
|
|
|
|
{
|
|
|
|
case PTR_ROOM_BITMAP:
|
|
|
|
{
|
|
|
|
if (ptr != NULL)
|
|
|
|
fIcon->SetBitmap((BBitmap*)ptr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-07-31 11:15:51 -05:00
|
|
|
void
|
|
|
|
ConversationView::GetWeights(float* horizChat, float* horizList,
|
|
|
|
float* vertChat, float* vertSend)
|
|
|
|
{
|
|
|
|
*horizChat = fHorizSplit->ItemWeight(0);
|
|
|
|
*horizList = fHorizSplit->ItemWeight(1);
|
|
|
|
*vertChat = fVertSplit->ItemWeight(0);
|
|
|
|
*vertSend = fVertSplit->ItemWeight(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::SetWeights(float horizChat, float horizList, float vertChat,
|
|
|
|
float vertSend)
|
|
|
|
{
|
|
|
|
fHorizSplit->SetItemWeight(0, horizChat, true);
|
|
|
|
fHorizSplit->SetItemWeight(1, horizList, true);
|
|
|
|
fVertSplit->SetItemWeight(0, vertChat, true);
|
|
|
|
fVertSplit->SetItemWeight(1, vertSend, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-31 10:50:43 -05:00
|
|
|
void
|
|
|
|
ConversationView::_InitInterface()
|
|
|
|
{
|
2021-07-08 16:07:03 -05:00
|
|
|
fReceiveView = new RenderView("receiveView");
|
2021-05-31 10:50:43 -05:00
|
|
|
BScrollView* scrollViewReceive = new BScrollView("receiveScrollView",
|
|
|
|
fReceiveView, B_WILL_DRAW, false, true);
|
|
|
|
|
2021-07-08 16:07:03 -05:00
|
|
|
fSendView = new SendTextView("sendView", this);
|
|
|
|
|
2021-07-29 22:00:01 -05:00
|
|
|
fNameTextView = new EnterTextView("roomName", be_bold_font, NULL, B_WILL_DRAW);
|
2021-06-04 13:57:04 -05:00
|
|
|
fNameTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
2021-07-29 19:39:22 -05:00
|
|
|
fNameTextView->SetStylable(true);
|
2021-06-04 13:57:04 -05:00
|
|
|
fNameTextView->MakeEditable(false);
|
2021-07-29 19:39:22 -05:00
|
|
|
fNameTextView->MakeResizable(true);
|
2021-05-31 10:50:43 -05:00
|
|
|
|
2021-07-29 22:00:01 -05:00
|
|
|
fSubjectTextView = new EnterTextView("roomSubject");
|
2021-06-04 13:57:04 -05:00
|
|
|
fSubjectTextView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
|
|
|
fSubjectTextView->MakeEditable(false);
|
2021-07-29 19:39:22 -05:00
|
|
|
fSubjectTextView->MakeResizable(true);
|
2021-05-31 10:50:43 -05:00
|
|
|
|
2021-06-04 13:57:04 -05:00
|
|
|
fIcon = new BitmapView("ContactIcon");
|
|
|
|
fIcon->SetExplicitMinSize(BSize(50, 50));
|
|
|
|
fIcon->SetExplicitPreferredSize(BSize(50, 50));
|
|
|
|
fIcon->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE));
|
2021-07-27 20:48:03 -05:00
|
|
|
fIcon->SetSquare(true);
|
2021-05-31 10:50:43 -05:00
|
|
|
|
|
|
|
fProtocolView = new BitmapView("protocolView");
|
|
|
|
|
|
|
|
fUserList = new UserListView("userList");
|
|
|
|
BScrollView* scrollViewUsers = new BScrollView("userScrollView",
|
|
|
|
fUserList, B_WILL_DRAW, false, true);
|
|
|
|
|
2021-07-31 11:15:51 -05:00
|
|
|
fHorizSplit = new BSplitView(B_HORIZONTAL, 0);
|
|
|
|
fVertSplit = new BSplitView(B_VERTICAL, 0);
|
|
|
|
|
2021-05-31 10:50:43 -05:00
|
|
|
BLayoutBuilder::Group<>(this, B_VERTICAL)
|
|
|
|
.AddGroup(B_HORIZONTAL)
|
2021-06-04 13:57:04 -05:00
|
|
|
.Add(fIcon)
|
|
|
|
.AddGroup(B_VERTICAL)
|
|
|
|
.Add(fNameTextView)
|
|
|
|
.Add(fSubjectTextView)
|
|
|
|
.End()
|
2021-05-31 10:50:43 -05:00
|
|
|
.Add(fProtocolView)
|
|
|
|
.End()
|
2021-07-31 11:15:51 -05:00
|
|
|
.AddSplit(fHorizSplit, 0.0)
|
|
|
|
.AddGroup(B_VERTICAL)
|
|
|
|
.AddSplit(fVertSplit, 8.0)
|
|
|
|
.Add(scrollViewReceive, 20)
|
|
|
|
.Add(fSendView, 1)
|
|
|
|
.End()
|
2021-07-08 16:07:03 -05:00
|
|
|
.End()
|
2021-05-31 10:50:43 -05:00
|
|
|
.Add(scrollViewUsers, 1)
|
2021-07-08 16:07:03 -05:00
|
|
|
.End()
|
|
|
|
.End();
|
2021-05-31 10:50:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-30 19:07:50 -05:00
|
|
|
bool
|
|
|
|
ConversationView::_AppendOrEnqueueMessage(BMessage* msg)
|
|
|
|
{
|
2021-08-11 19:46:33 -05:00
|
|
|
if (msg->HasInt64("when") == false)
|
|
|
|
msg->AddInt64("when", (int64)time(NULL));
|
|
|
|
|
2021-05-30 19:07:50 -05:00
|
|
|
// If not attached to the chat window, then re-handle this message
|
|
|
|
// later [AttachedToWindow()], since you can't edit an unattached
|
|
|
|
// RenderView.
|
|
|
|
if (Window() == NULL) {
|
|
|
|
fMessageQueue.AddItem(new BMessage(*msg));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Alright, we're good to append!
|
|
|
|
_AppendMessage(msg);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::_AppendMessage(BMessage* msg)
|
|
|
|
{
|
2021-08-12 15:43:52 -05:00
|
|
|
// If ordered to clear buffer… well, I guess we can't refuse
|
|
|
|
if (msg->what == kClearText) {
|
|
|
|
fReceiveView->SetText("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// … else we're jamming a message into this view no matter what it takes!
|
2021-08-13 12:39:47 -05:00
|
|
|
if (msg->HasInt32("face_start") == true) {
|
|
|
|
_AppendFormattedMessage(msg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For unformatted (normal or bulk) messages
|
2021-08-11 20:22:42 -05:00
|
|
|
BStringList user_ids, user_names, bodies;
|
2021-06-07 00:03:15 -05:00
|
|
|
if (msg->FindStrings("body", &bodies) != B_OK)
|
2021-06-06 12:02:26 -05:00
|
|
|
return;
|
2021-08-11 20:22:42 -05:00
|
|
|
msg->FindStrings("user_id", &user_ids);
|
|
|
|
msg->FindStrings("user_name", &user_names);
|
2021-06-06 12:02:26 -05:00
|
|
|
|
|
|
|
for (int i = bodies.CountStrings(); i >= 0; i--) {
|
2021-07-28 22:11:42 -05:00
|
|
|
User* sender = NULL;
|
|
|
|
if (fConversation != NULL)
|
2021-08-11 20:22:42 -05:00
|
|
|
sender = fConversation->UserById(user_ids.StringAt(i));
|
|
|
|
BString sender_id = user_ids.StringAt(i);
|
|
|
|
BString sender_name = user_names.StringAt(i);
|
2021-06-06 12:02:26 -05:00
|
|
|
BString body = bodies.StringAt(i);
|
2021-06-07 20:01:14 -05:00
|
|
|
rgb_color userColor = ui_color(B_PANEL_TEXT_COLOR);
|
2021-06-14 16:41:25 -05:00
|
|
|
int64 timeInt;
|
|
|
|
|
|
|
|
if (msg->FindInt64("when", i, &timeInt) != B_OK)
|
|
|
|
timeInt = (int64)time(NULL);
|
2021-06-06 12:02:26 -05:00
|
|
|
|
2021-06-07 20:01:14 -05:00
|
|
|
if (sender != NULL) {
|
2021-06-06 12:02:26 -05:00
|
|
|
sender_name = sender->GetName();
|
2021-06-07 20:01:14 -05:00
|
|
|
userColor = sender->fItemColor;
|
|
|
|
}
|
2021-06-06 12:02:26 -05:00
|
|
|
|
2021-08-11 20:22:42 -05:00
|
|
|
if (sender_name.IsEmpty() == true && sender_id.IsEmpty() == false)
|
|
|
|
sender_name = sender_id;
|
|
|
|
|
|
|
|
if (sender_id.IsEmpty() == true && sender_name.IsEmpty() == true) {
|
2021-07-25 11:28:07 -05:00
|
|
|
fReceiveView->AppendGeneric(body.String());
|
2021-06-06 12:02:26 -05:00
|
|
|
continue;
|
|
|
|
}
|
2021-05-30 19:07:50 -05:00
|
|
|
|
2021-07-31 20:39:13 -05:00
|
|
|
if (body.StartsWith("/me ")) {
|
|
|
|
BString meMsg = "** ";
|
|
|
|
meMsg << sender_name.String() << " ";
|
|
|
|
meMsg << body.RemoveFirst("/me ");
|
|
|
|
fReceiveView->AppendGeneric(meMsg.String());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-06-14 16:41:25 -05:00
|
|
|
fReceiveView->AppendMessage(sender_name.String(), body.String(),
|
|
|
|
userColor, (time_t)timeInt);
|
2021-05-30 19:07:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-08-13 12:39:47 -05:00
|
|
|
void
|
|
|
|
ConversationView::_AppendFormattedMessage(BMessage* msg)
|
|
|
|
{
|
|
|
|
int64 timeInt;
|
|
|
|
BString user_id;
|
2021-08-16 15:10:18 -05:00
|
|
|
BString user_name = msg->FindString("user_name");
|
2021-08-13 12:39:47 -05:00
|
|
|
BString body;
|
|
|
|
rgb_color userColor = ui_color(B_PANEL_TEXT_COLOR);
|
|
|
|
|
|
|
|
if (msg->FindString("body", &body) != B_OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (msg->FindInt64("when", &timeInt) != B_OK)
|
|
|
|
timeInt = (int64)time(NULL);
|
|
|
|
|
2021-08-16 15:10:18 -05:00
|
|
|
if (msg->FindString("user_id", &user_id) == B_OK) {
|
|
|
|
User* user = NULL;
|
|
|
|
if (fConversation != NULL
|
|
|
|
&& (user = fConversation->UserById(user_id)) != NULL) {
|
|
|
|
user_name = user->GetName();
|
|
|
|
userColor = user->fItemColor;
|
2021-08-13 12:39:47 -05:00
|
|
|
}
|
2021-08-16 15:10:18 -05:00
|
|
|
else if (user_name.IsEmpty() == true)
|
|
|
|
user_name = user_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (user_name.IsEmpty() == true) {
|
2021-08-13 12:39:47 -05:00
|
|
|
fReceiveView->AppendGeneric(body);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-08-16 15:10:18 -05:00
|
|
|
|
2021-08-13 12:39:47 -05:00
|
|
|
fReceiveView->AppendTimestamp(timeInt);
|
|
|
|
fReceiveView->AppendUserstamp(user_name, userColor);
|
|
|
|
|
|
|
|
// And here we append the body…
|
|
|
|
uint16 face = 0;
|
|
|
|
UInt16IntMap face_indices;
|
|
|
|
rgb_color color = ui_color(B_PANEL_TEXT_COLOR);
|
|
|
|
|
|
|
|
BFont font;
|
|
|
|
for (int i = 0; i < body.CountChars(); i++) {
|
|
|
|
_EnableStartingFaces(msg, i, &face, &face_indices);
|
|
|
|
|
|
|
|
if (face == B_REGULAR_FACE) {
|
|
|
|
font = BFont();
|
|
|
|
face = 0;
|
|
|
|
}
|
|
|
|
else if (face > 0) {
|
|
|
|
font.SetFace(face);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 bytes;
|
|
|
|
const char* curChar = body.CharAt(i, &bytes);
|
|
|
|
char append[bytes];
|
|
|
|
|
|
|
|
for (int i = 0; i < bytes; i++)
|
|
|
|
append[i] = curChar[i];
|
|
|
|
append[bytes] = '\0';
|
|
|
|
fReceiveView->Append(append, color, &font);
|
|
|
|
|
|
|
|
_DisableEndingFaces(msg, &face, &face_indices);
|
|
|
|
}
|
|
|
|
fReceiveView->Append("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::_EnableStartingFaces(BMessage* msg, int32 index, uint16* face,
|
|
|
|
UInt16IntMap* indices)
|
|
|
|
{
|
|
|
|
int32 face_start;
|
|
|
|
int32 face_length;
|
|
|
|
uint16 newFace;
|
|
|
|
|
|
|
|
int32 i = 0;
|
|
|
|
while (msg->FindInt32("face_start", i, &face_start) == B_OK) {
|
|
|
|
if (face_start == index) {
|
|
|
|
if (msg->FindInt32("face_length", i, &face_length) != B_OK)
|
|
|
|
continue;
|
|
|
|
if (msg->FindUInt16("face", i, &newFace) != B_OK)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
*face |= newFace;
|
|
|
|
indices->AddItem(newFace, face_length);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::_DisableEndingFaces(BMessage* msg, uint16* face,
|
|
|
|
UInt16IntMap* indices)
|
|
|
|
{
|
|
|
|
for (int32 i = 0; i < indices->CountItems(); i++) {
|
|
|
|
uint16 key = indices->KeyAt(i);
|
|
|
|
int32 value = indices->ValueAt(i) - 1;
|
|
|
|
|
|
|
|
indices->RemoveItemAt(i);
|
|
|
|
if (value <= 0) {
|
|
|
|
*face = *face & ~key;
|
|
|
|
if (indices->CountItems() == 0)
|
|
|
|
*face = B_REGULAR_FACE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
indices->AddItem(key, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-07 00:03:15 -05:00
|
|
|
void
|
|
|
|
ConversationView::_UserMessage(const char* format, const char* bodyFormat,
|
2021-08-13 12:39:47 -05:00
|
|
|
BMessage* msg)
|
2021-06-07 00:03:15 -05:00
|
|
|
{
|
|
|
|
BString user_id;
|
|
|
|
BString user_name = msg->FindString("user_name");
|
|
|
|
BString body = msg->FindString("body");
|
|
|
|
|
|
|
|
if (msg->FindString("user_id", &user_id) != B_OK)
|
|
|
|
return;
|
|
|
|
if (user_name.IsEmpty() == true)
|
|
|
|
user_name = user_id;
|
|
|
|
|
|
|
|
BString newBody("** ");
|
|
|
|
if (body.IsEmpty() == true)
|
|
|
|
newBody << format;
|
|
|
|
else {
|
|
|
|
newBody << bodyFormat;
|
|
|
|
newBody.ReplaceAll("%body%", body.String());
|
|
|
|
}
|
|
|
|
newBody.ReplaceAll("%user%", user_name.String());
|
|
|
|
|
|
|
|
BMessage newMsg;
|
|
|
|
newMsg.AddString("body", newBody);
|
|
|
|
_AppendOrEnqueueMessage(&newMsg);
|
2021-07-21 12:10:20 -05:00
|
|
|
fReceiveView->ScrollToBottom();
|
2021-06-07 00:03:15 -05:00
|
|
|
}
|
2021-07-28 22:11:42 -05:00
|
|
|
|
|
|
|
|
|
|
|
#undef B_TRANSLATION_CONTEXT
|
|
|
|
#define B_TRANSLATION_CONTEXT "ConversationView ― Startup messages"
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ConversationView::_FakeChat()
|
|
|
|
{
|
|
|
|
fNameTextView->SetText(B_TRANSLATE("Cardie setup"));
|
|
|
|
fSubjectTextView->SetText(B_TRANSLATE("No accounts configured, no joy."));
|
|
|
|
|
2021-08-16 15:10:18 -05:00
|
|
|
BMessage obsv(IM_MESSAGE);
|
|
|
|
obsv.AddInt32("im_what", IM_MESSAGE_RECEIVED);
|
|
|
|
obsv.AddString("user_name", B_TRANSLATE("Master Foo"));
|
|
|
|
obsv.AddString("body", B_TRANSLATE("It looks like you don't have any accounts enabled."));
|
|
|
|
_AppendOrEnqueueMessage(&obsv);
|
|
|
|
|
|
|
|
BString body = B_TRANSLATE("Manage accounts through the %menu% menu to get started.");
|
|
|
|
BString menu = B_TRANSLATE("Accounts");
|
|
|
|
int32 boldStart = body.FindFirst("%");
|
|
|
|
int32 boldLength = menu.CountChars();
|
|
|
|
body.ReplaceAll("%menu%", menu);
|
|
|
|
|
|
|
|
BMessage add(IM_MESSAGE);
|
|
|
|
add.AddInt32("im_what", IM_MESSAGE_RECEIVED);
|
|
|
|
add.AddString("user_name", B_TRANSLATE("Master Foo"));
|
|
|
|
add.AddString("body", body);
|
|
|
|
add.AddInt32("face_start", boldStart);
|
|
|
|
add.AddInt32("face_length", boldLength);
|
|
|
|
add.AddUInt16("face", B_BOLD_FACE);
|
|
|
|
_AppendOrEnqueueMessage(&add);
|
|
|
|
|
|
|
|
body = B_TRANSLATE("Afterward, you can join a room or start a chat through the %menu% menu. :-)");
|
|
|
|
menu = B_TRANSLATE("Chat");
|
|
|
|
boldStart = body.FindFirst("%");
|
|
|
|
boldLength = menu.CountChars();
|
|
|
|
body.ReplaceAll("%menu%", menu);
|
|
|
|
|
2021-07-28 22:11:42 -05:00
|
|
|
BMessage welcome(IM_MESSAGE);
|
|
|
|
welcome.AddInt32("im_what", IM_MESSAGE_RECEIVED);
|
2021-08-16 15:10:18 -05:00
|
|
|
welcome.AddString("user_name", B_TRANSLATE("Master Foo"));
|
|
|
|
welcome.AddString("body", body);
|
|
|
|
welcome.AddInt32("face_start", boldStart);
|
|
|
|
welcome.AddInt32("face_length", boldLength);
|
|
|
|
welcome.AddUInt16("face", B_BOLD_FACE);
|
2021-07-28 22:11:42 -05:00
|
|
|
_AppendOrEnqueueMessage(&welcome);
|
|
|
|
}
|