Chat-O-Matic/application/views/ConversationListView.cpp
Jaidyn Ann 057e7fba9b 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.
2021-08-12 15:43:52 -05:00

231 lines
5.5 KiB
C++

/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "ConversationListView.h"
#include <Catalog.h>
#include <PopUpMenu.h>
#include <MenuItem.h>
#include <Window.h>
#include "AppMessages.h"
#include "ChatProtocolMessages.h"
#include "Conversation.h"
#include "ConversationAccountItem.h"
#include "ConversationItem.h"
#include "MainWindow.h"
#include "ProtocolLooper.h"
#include "Server.h"
#include "TheApp.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ConversationListView"
const uint32 kOpenSelectedChat = 'CVos';
const uint32 kLeaveSelectedChat = 'CVcs';
static int
compare_by_name(const BListItem* _item1, const BListItem* _item2)
{
ConversationItem* item1 = (ConversationItem*)_item1;
ConversationItem* item2 = (ConversationItem*)_item2;
return strcasecmp(item1->GetConversation()->GetName().String(),
item2->GetConversation()->GetName().String());
}
static int
compare_conversations(const BListItem* _item1, const BListItem* _item2)
{
ConversationItem* item1 = (ConversationItem*)_item1;
ConversationItem* item2 = (ConversationItem*)_item2;
int32 userCount1 = item1->GetConversation()->Users().CountItems();
int32 userCount2 = item2->GetConversation()->Users().CountItems();
// Sort by name among chats/rooms
if ((userCount1 <= 2 && userCount2 <= 2)
|| (userCount1 > 2 && userCount2 > 2))
return compare_by_name(item1, item2);
// One-on-one chats should sort above rooms
if (userCount1 <=2 && userCount2 > 2)
return -1;
if (userCount1 > 2 && userCount2 <= 2)
return 1;
return 0;
}
ConversationListView::ConversationListView(const char* name)
: BOutlineListView(name)
{
}
void
ConversationListView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kOpenSelectedChat:
{
ConversationItem* citem;
ConversationAccountItem* caitem;
int32 selIndex = CurrentSelection();
if (selIndex >= 0
&& (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);
}
}
void
ConversationListView::MouseDown(BPoint where)
{
int32 selection = CurrentSelection();
BOutlineListView::MouseDown(where);
int32 newSel = CurrentSelection();
// Don't allow deselecting anything
if (newSel < 0 && selection >= 0)
Select(selection);
uint32 buttons = 0;
Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
if (!(buttons & B_SECONDARY_MOUSE_BUTTON))
return;
if (CurrentSelection() >= 0)
_ConversationPopUp()->Go(ConvertToScreen(where), true, false);
else
_BlankPopUp()->Go(ConvertToScreen(where), true, false);
}
void
ConversationListView::SelectionChanged()
{
MessageReceived(new BMessage(kOpenSelectedChat));
}
void
ConversationListView::AddConversation(Conversation* chat)
{
ConversationAccountItem* superItem = _EnsureAccountItem(chat);
ConversationItem* item = chat->GetListItem();
if (superItem == NULL || item == NULL)
return;
AddUnder(item, superItem);
SortItemsUnder(superItem, true, compare_conversations);
}
void
ConversationListView::RemoveConversation(Conversation* chat)
{
RemoveItem(chat->GetListItem());
}
void
ConversationListView::SortConversation(Conversation* chat)
{
ConversationAccountItem* superItem = _EnsureAccountItem(chat);
SortItemsUnder(superItem, true, compare_conversations);
}
BPopUpMenu*
ConversationListView::_ConversationPopUp()
{
BPopUpMenu* menu = new BPopUpMenu("chatPopUp");
int32 selIndex = CurrentSelection();
ConversationItem* item;
if ((item = (ConversationItem*)ItemAt(selIndex)) == NULL)
return _BlankPopUp();
Conversation* chat = item->GetConversation();
ProtocolLooper* looper = chat->GetProtocolLooper();
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
BObjectList<BMessage> items = server->ChatPopUpItems();
BObjectList<BMessage> protoItems = looper->Protocol()->ChatPopUpItems();
items.AddList(&protoItems);
for (int i = 0; i < items.CountItems(); i++) {
BMessage* itemMsg = items.ItemAt(i);
BMessage* msg = new BMessage(*itemMsg);
BMessage toSend;
msg->FindMessage("_msg", &toSend);
toSend.AddString("chat_id", chat->GetId());
toSend.AddInt64("instance", looper->GetInstance());
msg->ReplaceMessage("_msg", &toSend);
BMenuItem* item = new BMenuItem(msg);
if (msg->GetBool("x_to_protocol", true) == true)
item->SetTarget(looper);
else
item->SetTarget(Window());
menu->AddItem(item);
}
return menu;
}
BPopUpMenu*
ConversationListView::_BlankPopUp()
{
bool enabled = false;
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
if (server != NULL && server->GetAccounts().CountItems() > 0)
enabled = true;
BPopUpMenu* menu = new BPopUpMenu("blankPopUp");
BMenuItem* newChat = new BMenuItem(B_TRANSLATE("New chat" B_UTF8_ELLIPSIS),
new BMessage(APP_NEW_CHAT), 'M', B_COMMAND_KEY);
newChat->SetEnabled(enabled);
menu->AddItem(newChat);
menu->SetTargetForItems(Window());
return menu;
}
ConversationAccountItem*
ConversationListView::_EnsureAccountItem(Conversation* chat)
{
ProtocolLooper* looper;
if (chat == NULL || (looper = chat->GetProtocolLooper()) == NULL)
return NULL;
ConversationAccountItem* item = looper->GetListItem();
if (HasItem(item) == false)
AddItem(item);
return item;
}