Add "system buffer" per protocol

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.
This commit is contained in:
Jaidyn Ann 2021-08-12 15:43:52 -05:00
parent d16f397fe6
commit 057e7fba9b
13 changed files with 168 additions and 115 deletions

View File

@ -70,7 +70,13 @@ enum im_what_code {
IM_MESSAGE_SENT = 21,
/*! Chat message received →App
Requires: String "chat_id", String "user_id", String "body" */
To send a normal chat message, specify chat_id and user_id if
user_id is ommitted, the message is treated as a "system message"
printed in chat.
If chat_id is ommitted, the message is sent to the protocol's system
buffer, rather than a specific conversation.
Requires: String "body"
Allows: String "chat_id", String "user_id", String "user_name" */
IM_MESSAGE_RECEIVED = 22,
/*! Logs received →App

View File

@ -317,7 +317,6 @@ Conversation::SetNotifyIconBitmap(BBitmap* icon)
{
if (icon != NULL) {
fIcon = icon;
GetView()->UpdateIcon();
NotifyPointer(PTR_ROOM_BITMAP, (void*)icon);
return true;
}

View File

@ -17,8 +17,13 @@
#include "Account.h"
#include "AppMessages.h"
#include "ChatProtocolMessages.h"
#include "Conversation.h"
#include "ConversationAccountItem.h"
#include "ConversationView.h"
#include "MainWindow.h"
#include "NotifyMessage.h"
#include "TheApp.h"
ProtocolLooper::ProtocolLooper(ChatProtocol* protocol, int64 instance)
@ -36,8 +41,10 @@ ProtocolLooper::ProtocolLooper(ChatProtocol* protocol, int64 instance)
BString name(protocol->FriendlySignature());
name << " - " << account->Name();
SetName(name.String());
_InitChatView();
Run();
}
@ -65,6 +72,22 @@ ProtocolLooper::MessageReceived(BMessage* msg)
}
ConversationView*
ProtocolLooper::GetView()
{
return fSystemChatView;
}
void
ProtocolLooper::ShowView()
{
MainWindow* win = ((TheApp*)be_app)->GetMainWindow();
win->SetConversation(NULL);
win->SetConversationView(fSystemChatView);
}
ChatProtocol*
ProtocolLooper::Protocol()
{
@ -213,7 +236,7 @@ ProtocolLooper::GetListItem()
{
if (fListItem == NULL)
fListItem = new ConversationAccountItem(fProtocol->GetName(),
fInstance);
fInstance, this);
return fListItem;
}
@ -228,3 +251,26 @@ ProtocolLooper::LoadCommands()
fCommands.AddItem(cmd->GetName(), cmd);
}
}
void
ProtocolLooper::_InitChatView()
{
fSystemChatView = new ConversationView();
BMessage clear(kClearText);
fSystemChatView->MessageReceived(&clear);
BMessage init(IM_MESSAGE);
init.AddInt32("im_what", IM_MESSAGE_RECEIVED);
init.AddString("user_name", "Cardie");
init.AddString("body", B_TRANSLATE("I'm rearing to go!"));
fSystemChatView->MessageReceived(&init);
fSystemChatView->ObserveString(STR_ROOM_NAME, fProtocol->GetName());
fSystemChatView->ObserveString(STR_ROOM_SUBJECT, "System buffer");
BBitmap* icon = fProtocol->Icon();
if (icon != NULL)
fSystemChatView->ObservePointer(PTR_ROOM_BITMAP, (void*)icon);
}

View File

@ -19,6 +19,7 @@
class Contact;
class Conversation;
class ConversationAccountItem;
class ConversationView;
class User;
@ -34,6 +35,10 @@ public:
void MessageReceived(BMessage* msg);
ConversationView*
GetView();
void ShowView();
ChatProtocol* Protocol();
ChatMap Conversations() const;
@ -64,6 +69,8 @@ public:
void LoadCommands();
private:
void _InitChatView();
ChatProtocol* fProtocol;
int64 fInstance;
@ -74,6 +81,8 @@ private:
UserMap fUserMap;
CommandMap fCommands;
ConversationView*
fSystemChatView;
ConversationAccountItem*
fListItem;
};

View File

@ -32,6 +32,7 @@
#include "Cardie.h"
#include "ChatProtocol.h"
#include "ConversationInfoWindow.h"
#include "ConversationView.h"
#include "ChatProtocolMessages.h"
#include "Flags.h"
#include "ImageCache.h"
@ -461,8 +462,14 @@ Server::ImMessage(BMessage* msg)
}
break;
}
case IM_MESSAGE_SENT:
case IM_MESSAGE_RECEIVED:
if (msg->HasString("chat_id") == false) {
ProtocolLooper* looper = _LooperFromMessage(msg);
if (looper != NULL)
looper->GetView()->MessageReceived(msg);
return B_SKIP_MESSAGE;
}
case IM_MESSAGE_SENT:
case IM_ROOM_JOINED:
case IM_ROOM_CREATED:
case IM_ROOM_METADATA:
@ -992,7 +999,7 @@ Server::_EnsureConversation(BMessage* message)
if (!message || (looper = _LooperFromMessage(message)) == NULL)
return NULL;
BString chat_id = message->FindString("chat_id");
BString chat_id = message->GetString("chat_id", "");
Conversation* item = NULL;
if (chat_id.IsEmpty() == false) {

View File

@ -5,11 +5,15 @@
#include "ConversationAccountItem.h"
#include "ProtocolLooper.h"
ConversationAccountItem::ConversationAccountItem(const char* name, int64 instance)
ConversationAccountItem::ConversationAccountItem(const char* name,
int64 instance, ProtocolLooper* looper)
:
BStringItem(name),
fInstance(instance)
fInstance(instance),
fProtocolLooper(looper)
{
}
@ -21,3 +25,8 @@ ConversationAccountItem::GetInstance()
}
ProtocolLooper*
ConversationAccountItem::GetLooper()
{
return fProtocolLooper;
}

View File

@ -8,16 +8,20 @@
#include <StringItem.h>
class Conversation;
class ProtocolLooper;
class ConversationAccountItem : public BStringItem {
public:
ConversationAccountItem(const char* name, int64 instance);
ConversationAccountItem(const char* name, int64 instance,
ProtocolLooper* looper);
int64 GetInstance();
ProtocolLooper* GetLooper();
private:
int64 fInstance;
ProtocolLooper* fProtocolLooper;
};

View File

@ -75,16 +75,22 @@ ConversationListView::MessageReceived(BMessage* msg)
switch (msg->what) {
case kOpenSelectedChat:
{
ConversationItem* item;
ConversationItem* citem;
ConversationAccountItem* caitem;
int32 selIndex = CurrentSelection();
if (selIndex >= 0
&& (item = (ConversationItem*)ItemAt(selIndex)) != NULL
&& item->OutlineLevel() == 1)
item->GetConversation()->ShowView(false, true);
&& (citem = (ConversationItem*)ItemAt(selIndex)) != NULL
&& citem->OutlineLevel() == 1)
citem->GetConversation()->ShowView(false, true);
else if (selIndex >= 0
&& (caitem = (ConversationAccountItem*)ItemAt(selIndex))
!= NULL
&& caitem->OutlineLevel() == 0)
caitem->GetLooper()->ShowView();
break;
}
default:
BListView::MessageReceived(msg);
}
@ -95,18 +101,10 @@ void
ConversationListView::MouseDown(BPoint where)
{
int32 selection = CurrentSelection();
BOutlineListView::MouseDown(where);
int32 newSel = CurrentSelection();
// Don't allow selecting an AccountItem
if (newSel >= 0 && ItemAt(newSel)->OutlineLevel() == 0) {
Select(selection);
return;
}
// Don't allow deselecting a room
// Don't allow deselecting anything
if (newSel < 0 && selection >= 0)
Select(selection);
@ -158,49 +156,6 @@ ConversationListView::SortConversation(Conversation* chat)
}
int32
ConversationListView::CountConversations()
{
int32 count = 0;
for (int32 i = 0; i < CountItems(); i++)
if (ItemAt(i)->OutlineLevel() == 1)
count++;
return count;
}
int32
ConversationListView::ConversationIndexOf(Conversation* chat)
{
ConversationItem* item = chat->GetListItem();
int32 index = IndexOf(item);
int32 chatIndex = index;
if (item == NULL || index < 0)
return -1;
for (int i = 0; i < index; i++)
if (ItemAt(i)->OutlineLevel() == 0) // If AccountItem
chatIndex--;
return chatIndex;
}
void
ConversationListView::SelectConversation(int32 index)
{
for (int32 i = 0, cindex = -1; i < CountItems(); i++) {
if (ItemAt(i)->OutlineLevel() == 1) // If ConversationItem
cindex++;
if (cindex == index) {
Select(i);
break;
}
}
}
BPopUpMenu*
ConversationListView::_ConversationPopUp()
{

View File

@ -24,10 +24,6 @@ public:
void RemoveConversation(Conversation* chat);
void SortConversation(Conversation* chat);
int32 CountConversations();
int32 ConversationIndexOf(Conversation* chat);
void SelectConversation(int32 index);
private:
BPopUpMenu* _ConversationPopUp();
BPopUpMenu* _BlankPopUp();

View File

@ -59,7 +59,7 @@ ConversationView::AttachedToWindow()
{
while (fMessageQueue.IsEmpty() == false) {
BMessage* msg = fMessageQueue.RemoveItemAt(0);
ImMessage(msg);
MessageReceived(msg);
}
if (fConversation != NULL) {
if (fNameTextView->Text() != fConversation->GetName())
@ -95,6 +95,9 @@ ConversationView::MessageReceived(BMessage* message)
fSendView->ScrollToOffset(0);
break;
}
case kClearText:
_AppendOrEnqueueMessage(message);
break;
case IM_MESSAGE:
ImMessage(message);
break;
@ -243,14 +246,6 @@ ConversationView::SetConversation(Conversation* chat)
}
void
ConversationView::UpdateIcon()
{
if (fConversation != NULL && fConversation->IconBitmap() != NULL)
fIcon->SetBitmap(fConversation->IconBitmap());
}
void
ConversationView::UpdateUserList(UserMap users)
{
@ -299,6 +294,21 @@ ConversationView::ObserveString(int32 what, BString str)
}
void
ConversationView::ObservePointer(int32 what, void* ptr)
{
switch (what)
{
case PTR_ROOM_BITMAP:
{
if (ptr != NULL)
fIcon->SetBitmap((BBitmap*)ptr);
break;
}
}
}
void
ConversationView::GetWeights(float* horizChat, float* horizList,
float* vertChat, float* vertSend)
@ -401,6 +411,13 @@ ConversationView::_AppendOrEnqueueMessage(BMessage* msg)
void
ConversationView::_AppendMessage(BMessage* msg)
{
// 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!
BStringList user_ids, user_names, bodies;
if (msg->FindStrings("body", &bodies) != B_OK)
return;

View File

@ -24,6 +24,9 @@ class User;
class UserListView;
const uint32 kClearText = 'CVct';
class ConversationView : public BGroupView, public Observer, public Notifier {
public:
ConversationView(Conversation* chat = NULL);
@ -36,12 +39,11 @@ public:
Conversation* GetConversation();
void SetConversation(Conversation* chat);
void UpdateIcon();
void UpdateUserList(UserMap users);
void InvalidateUserList();
void ObserveString(int32 what, BString str);
void ObservePointer(int32 what, void* ptr);
void GetWeights(float* horizChat, float* horizList,
float* vertChat, float* vertSend);

View File

@ -210,23 +210,17 @@ MainWindow::MessageReceived(BMessage* message)
}
case APP_MOVE_UP:
{
if (fConversation == NULL)
break;
int32 index = fListView->ConversationIndexOf(fConversation);
int32 index = fListView->CurrentSelection();
if (index > 0)
fListView->SelectConversation(index - 1);
fListView->Select(index - 1);
break;
}
case APP_MOVE_DOWN:
{
if (fConversation == NULL)
break;
int32 index = fListView->ConversationIndexOf(fConversation);
int32 count = fListView->CountConversations();
int32 index = fListView->CurrentSelection();
int32 count = fListView->CountItems();
if (index < (count - 1))
fListView->SelectConversation(index + 1);
fListView->Select(index + 1);
break;
}
case APP_REPLICANT_STATUS_SET:
@ -346,6 +340,24 @@ MainWindow::WorkspaceActivated(int32 workspace, bool active)
void
MainWindow::SetConversation(Conversation* chat)
{
fConversation = chat;
if (chat != NULL) {
SetConversationView(chat->GetView());
BString title(chat->GetName());
title << "" << APP_NAME;
SetTitle(title.String());
}
else {
SetConversationView(fBackupChatView);
SetTitle(APP_NAME);
}
}
void
MainWindow::SetConversationView(ConversationView* chatView)
{
// Save split weights
float weightChat = fRightView->ItemWeight((int32)0);
@ -354,17 +366,7 @@ MainWindow::SetConversation(Conversation* chat)
fChatView->GetWeights(&horizChat, &horizList, &vertChat, &vertSend);
fRightView->RemoveChild(fRightView->FindView("chatView"));
if (chat != NULL) {
fChatView = chat->GetView();
fConversation = chat;
BString title(chat->GetName());
title << "" << APP_NAME;
SetTitle(title.String());
}
else
SetTitle(APP_NAME);
fChatView = chatView;
fRightView->AddChild(fChatView, 9);
@ -418,18 +420,16 @@ MainWindow::SetConversation(Conversation* chat)
void
MainWindow::RemoveConversation(Conversation* chat)
{
int32 index = fListView->ConversationIndexOf(chat);
SetConversation(NULL);
int32 index = fListView->IndexOf(chat->GetListItem());
if (index > 0)
index--;
fListView->RemoveConversation(chat);
if (fListView->CountConversations() == 0) {
fChatView = new ConversationView();
SetConversation(NULL);
}
else
fListView->SelectConversation(index);
if (fListView->CountItems() > 0)
fListView->Select(index);
_ToggleMenuItems();
}
@ -451,7 +451,8 @@ MainWindow::_InitInterface()
// Right-side of window, Chat + Textbox
fRightView = new BSplitView(B_VERTICAL, 0);
fChatView = new ConversationView();
fBackupChatView = new ConversationView();
fChatView = fBackupChatView;
// Load weights from settings
float horizChat, horizList, vertChat, vertSend;
@ -587,7 +588,7 @@ MainWindow::_ToggleMenuItems()
BMenuItem* windowMenuItem = fMenuBar->FindItem(B_TRANSLATE("Window"));
BMenu* windowMenu = windowMenuItem->Submenu();
enabled = (fListView->CountConversations() > 0);
enabled = (fListView->CountItems() > 0);
for (int i = 0; i < windowMenu->CountItems(); i++)
windowMenu->ItemAt(i)->SetEnabled(enabled);
@ -611,8 +612,8 @@ MainWindow::_EnsureConversationItem(BMessage* msg)
_ToggleMenuItems();
}
if (fListView->CountConversations() == 1)
fListView->SelectConversation(0);
if (fListView->CountItems() == 1)
fListView->Select(0);
return item;
}
return NULL;

View File

@ -41,6 +41,7 @@ public:
bool active);
void SetConversation(Conversation* chat);
void SetConversationView(ConversationView* chatView);
void RemoveConversation(Conversation* chat);
void SortConversation(Conversation* chat);
@ -75,8 +76,9 @@ private:
// Right panel, chat
BSplitView* fRightView;
ConversationView* fChatView;
Conversation* fConversation;
ConversationView* fChatView;
ConversationView* fBackupChatView;
};