Chat-O-Matic/application/views/ConversationListView.cpp
Jaidyn Ann ab6988d9ba Ensure selection & add account buffer to chat list
Ensures that an item in the chat list is always selected (as long as
there are items to be selected), and that an account's "buffer" (fake
conversation) is added immediately after connection, and removed
immediately after disconnection.
2021-08-14 23:37:59 -05:00

277 lines
6.4 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:
{
int32 selIndex = CurrentSelection();
if (selIndex < 0)
break;
ConversationItem* citem
= dynamic_cast<ConversationItem*>(ItemAt(selIndex));
ConversationAccountItem* caitem
= dynamic_cast<ConversationAccountItem*>(ItemAt(selIndex));
if (citem != NULL)
citem->GetConversation()->ShowView(false, true);
else if (caitem != NULL)
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) {
BPopUpMenu* menu = _ConversationPopUp();
if (menu != NULL)
menu->Go(ConvertToScreen(where), true, false);
}
else
_BlankPopUp()->Go(ConvertToScreen(where), true, false);
}
void
ConversationListView::SelectionChanged()
{
MessageReceived(new BMessage(kOpenSelectedChat));
}
void
ConversationListView::RemoveItemSelecting(BListItem* item)
{
int32 selection = CurrentSelection();
int32 itemIndex = IndexOf(item);
RemoveItem(item);
if (itemIndex == selection && CountItems() > selection)
Select(selection);
else if (itemIndex == selection && CountItems() >= 1)
Select(0);
}
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)
{
RemoveItemSelecting(chat->GetListItem());
}
void
ConversationListView::AddAccount(int64 instance)
{
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
ProtocolLooper* looper = server->GetProtocolLooper(instance);
if (looper == NULL)
return;
AddItem(looper->GetListItem());
if (CurrentSelection() < 0)
Select(0);
}
void
ConversationListView::RemoveAccount(int64 instance)
{
int32 selection = 0;
for (int i = 0; i < CountItems(); i++) {
ConversationAccountItem* item
= dynamic_cast<ConversationAccountItem*>(ItemAt(i));
if (item != NULL && item->GetInstance() == instance) {
RemoveItemSelecting(item);
break;
}
}
if (CountItems() == 0)
((TheApp*)be_app)->GetMainWindow()->SetConversation(NULL);
}
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 = dynamic_cast<ConversationItem*>(ItemAt(selIndex))) == NULL)
return NULL;
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;
}