Roster management, adding new contacts

The base for roster management (RosterEditWindow) has been made, and
adding new contacts works. Up next is contact removal and editing.

This leverages a new template (as defined in a protocol's
CayaProtocol::SettingsTemplate()), "roster," which should contain all
slots pertinent to editing/adding a contact member.

Two new API messages were added for this― IM_CONTACT_LIST_CONTACT_ADDED
and IM_CONTACT_LIST_CONTACT_REMOVED. The former will functionally just
be IM_CONTACT_INFO, but with some semantical meaning.

A new CayaMessage (CAYA_EDIT_ROSTER) was also added.

TemplateWindow was also edited to this end: Now, like RosterWindow/View,
it can be given a specific accounts' instance id, and it will prevent
the selection of another account. A new constructor was also added, to
allow a ProtocolTemplate to be explicitly passed to it― probably from
the program itself.
This commit is contained in:
Jaidyn Ann 2021-06-19 18:25:58 -05:00
parent 3d8942af60
commit f4342d9310
13 changed files with 310 additions and 51 deletions

View File

@ -56,4 +56,7 @@ const uint32 CAYA_REQUEST_HELP = 'CYhm';
//! Display a "user info" window //! Display a "user info" window
const uint32 CAYA_USER_INFO = 'CYuw'; const uint32 CAYA_USER_INFO = 'CYuw';
//! Edit the contact roster
const uint32 CAYA_EDIT_ROSTER = 'CYer';
#endif // _CAYA_MESSAGES_H #endif // _CAYA_MESSAGES_H

View File

@ -36,10 +36,16 @@ enum im_what_code {
// Requires: Stringlist "user_id" // Requires: Stringlist "user_id"
IM_CONTACT_LIST = 2, IM_CONTACT_LIST = 2,
//! Contact(s) was added to the server-side list →Caya //! Add a contact to the roster →Protocol
// Requires: String "user_id" // The slots for this message are determined by the protocol's
// "roster" template (CayaProtocol::SettingsTemplate("roster"))
IM_CONTACT_LIST_ADD_CONTACT = 3, IM_CONTACT_LIST_ADD_CONTACT = 3,
//! Someone has been added →Caya
// Requires: String "user_id"
// Allows: String "user_name"
IM_CONTACT_LIST_CONTACT_ADDED = 4,
//! Contact(s) removed from the server-side list →Caya //! Contact(s) removed from the server-side list →Caya
// Requires: String "user_id" // Requires: String "user_id"
IM_CONTACT_LIST_REMOVED_CONTACT = 4, IM_CONTACT_LIST_REMOVED_CONTACT = 4,
@ -113,7 +119,8 @@ enum im_what_code {
IM_GET_EXTENDED_CONTACT_INFO = 64, IM_GET_EXTENDED_CONTACT_INFO = 64,
//! Received contact information →Caya //! Received contact information →Caya
// Requires: String "user_id" // Requires: String "user_id",
// non-standard slots used by "roster" template
// Accepts: String "user_name", String "full_name" // Accepts: String "user_name", String "full_name"
IM_EXTENDED_CONTACT_INFO = 65, IM_EXTENDED_CONTACT_INFO = 65,

View File

@ -80,6 +80,7 @@ SRCS = \
application/windows/JoinWindow.cpp \ application/windows/JoinWindow.cpp \
application/windows/MainWindow.cpp \ application/windows/MainWindow.cpp \
application/windows/PreferencesWindow.cpp \ application/windows/PreferencesWindow.cpp \
application/windows/RosterEditWindow.cpp \
application/windows/RosterWindow.cpp \ application/windows/RosterWindow.cpp \
application/windows/TemplateWindow.cpp \ application/windows/TemplateWindow.cpp \
application/windows/UserInfoWindow.cpp application/windows/UserInfoWindow.cpp

View File

@ -251,8 +251,10 @@ ProtocolTemplate::Save(BView* parent, BMessage* settings, BString* errorText)
BTextControl* textControl BTextControl* textControl
= dynamic_cast<BTextControl*>(view); = dynamic_cast<BTextControl*>(view);
if (textControl && BString(textControl->Text()).IsEmpty() == true) { if (textControl && BString(textControl->Text()).IsEmpty() == true
if (error.IsEmpty() == false && errorText != NULL) && error.IsEmpty() == false)
{
if (errorText != NULL)
errorText->SetTo(error); errorText->SetTo(error);
return B_BAD_VALUE; return B_BAD_VALUE;
} }

View File

@ -31,6 +31,7 @@
#include "NotifyMessage.h" #include "NotifyMessage.h"
#include "PreferencesWindow.h" #include "PreferencesWindow.h"
#include "ReplicantStatusView.h" #include "ReplicantStatusView.h"
#include "RosterEditWindow.h"
#include "RosterWindow.h" #include "RosterWindow.h"
#include "Server.h" #include "Server.h"
#include "StatusView.h" #include "StatusView.h"
@ -46,6 +47,7 @@ MainWindow::MainWindow()
fWorkspaceChanged(false), fWorkspaceChanged(false),
fConversation(NULL), fConversation(NULL),
fRosterWindow(NULL), fRosterWindow(NULL),
fRosterEditWindow(NULL),
fServer(NULL) fServer(NULL)
{ {
_InitInterface(); _InitInterface();
@ -158,6 +160,12 @@ MainWindow::MessageReceived(BMessage* message)
fRosterWindow->Show(); fRosterWindow->Show();
break; break;
} }
case CAYA_EDIT_ROSTER:
{
fRosterEditWindow = new RosterEditWindow(fServer);
fRosterEditWindow->Show();
break;
}
case CAYA_MOVE_UP: case CAYA_MOVE_UP:
{ {
if (fConversation == NULL) if (fConversation == NULL)
@ -271,7 +279,10 @@ MainWindow::ImMessage(BMessage* msg)
case IM_CONTACT_INFO: case IM_CONTACT_INFO:
case IM_EXTENDED_CONTACT_INFO: case IM_EXTENDED_CONTACT_INFO:
case IM_STATUS_SET: case IM_STATUS_SET:
if (fRosterWindow != NULL)
fRosterWindow->PostMessage(msg); fRosterWindow->PostMessage(msg);
if (fRosterEditWindow != NULL)
fRosterEditWindow->PostMessage(msg);
break; break;
case IM_PROTOCOL_READY: case IM_PROTOCOL_READY:
@ -454,24 +465,24 @@ MainWindow::_CreateMenuBar()
// Chat // Chat
BMenu* chatMenu = new BMenu("Chat"); BMenu* chatMenu = new BMenu("Chat");
BMenuItem* joinRoom = new BMenuItem("Join room" B_UTF8_ELLIPSIS, chatMenu->AddItem(new BMenuItem("Join room" B_UTF8_ELLIPSIS,
new BMessage(CAYA_JOIN_ROOM), 'J', B_COMMAND_KEY); new BMessage(CAYA_JOIN_ROOM), 'J', B_COMMAND_KEY));
BMenuItem* invite = new BMenuItem("Invite user" B_UTF8_ELLIPSIS,
new BMessage(CAYA_SEND_INVITE), 'I', B_COMMAND_KEY);
BMenuItem* newChat = new BMenuItem("New chat" B_UTF8_ELLIPSIS,
new BMessage(CAYA_NEW_CHAT), 'M', B_COMMAND_KEY);
BMenuItem* newRoom = new BMenuItem("New room" B_UTF8_ELLIPSIS,
new BMessage(CAYA_NEW_ROOM), 'N', B_COMMAND_KEY);
chatMenu->AddItem(joinRoom);
chatMenu->AddSeparatorItem(); chatMenu->AddSeparatorItem();
chatMenu->AddItem(newChat); chatMenu->AddItem(new BMenuItem("New room" B_UTF8_ELLIPSIS,
chatMenu->AddItem(newRoom); new BMessage(CAYA_NEW_ROOM), 'N', B_COMMAND_KEY));
chatMenu->AddSeparatorItem(); chatMenu->AddItem(new BMenuItem("New chat" B_UTF8_ELLIPSIS,
chatMenu->AddItem(invite); new BMessage(CAYA_NEW_CHAT), 'M', B_COMMAND_KEY));
chatMenu->SetTargetForItems(this); chatMenu->SetTargetForItems(this);
// Roster
BMenu* rosterMenu = new BMenu("Roster");
rosterMenu->AddItem(new BMenuItem("Edit roster" B_UTF8_ELLIPSIS,
new BMessage(CAYA_EDIT_ROSTER), 'R', B_COMMAND_KEY));
rosterMenu->AddSeparatorItem();
rosterMenu->AddItem(new BMenuItem("Invite user" B_UTF8_ELLIPSIS,
new BMessage(CAYA_SEND_INVITE), 'I', B_COMMAND_KEY));
rosterMenu->SetTargetForItems(this);
// Window // Window
BMenu* windowMenu = new BMenu("Window"); BMenu* windowMenu = new BMenu("Window");
windowMenu->AddItem(new BMenuItem("Up", windowMenu->AddItem(new BMenuItem("Up",
@ -482,6 +493,7 @@ MainWindow::_CreateMenuBar()
menuBar->AddItem(programMenu); menuBar->AddItem(programMenu);
menuBar->AddItem(chatMenu); menuBar->AddItem(chatMenu);
menuBar->AddItem(rosterMenu);
menuBar->AddItem(windowMenu); menuBar->AddItem(windowMenu);
return menuBar; return menuBar;

View File

@ -20,6 +20,7 @@ class ConversationItem;
class ConversationListView; class ConversationListView;
class ConversationView; class ConversationView;
class RosterItem; class RosterItem;
class RosterEditWindow;
class RosterWindow; class RosterWindow;
class Server; class Server;
class StatusView; class StatusView;
@ -57,6 +58,7 @@ private:
Server* fServer; Server* fServer;
RosterWindow* fRosterWindow; RosterWindow* fRosterWindow;
RosterEditWindow* fRosterEditWindow;
bool fWorkspaceChanged; bool fWorkspaceChanged;
BMenuBar* fMenuBar; BMenuBar* fMenuBar;

View File

@ -0,0 +1,136 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Andrea Anzani, andrea.anzani@gmail.com
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Jaidyn Levesque, jadedctrl@teknik.io
*/
#include "RosterEditWindow.h"
#include <Button.h>
#include <LayoutBuilder.h>
#include <MenuField.h>
#include <Notification.h>
#include <ScrollView.h>
#include <SeparatorView.h>
#include "CayaMessages.h"
#include "CayaPreferences.h"
#include "CayaProtocolMessages.h"
#include "CayaUtils.h"
#include "RosterItem.h"
#include "RosterListView.h"
#include "RosterView.h"
#include "TemplateWindow.h"
const uint32 kSendMessage = 'RWSM';
const uint32 kAddMember = 'RWAM';
const uint32 kRemoveMember = 'RWRM';
const uint32 kEditMember = 'RWEM';
const uint32 kSelAccount = 'RWSA';
const uint32 kSelNoAccount = 'RWNA';
RosterEditWindow::RosterEditWindow(Server* server)
:
BWindow(BRect(0, 0, 300, 400), "Roster", B_FLOATING_WINDOW, 0),
fAccounts(server->GetAccounts()),
fServer(server)
{
fRosterView = new RosterView("buddyView", server);
fRosterView->SetInvocationMessage(new BMessage(kEditMember));
fAccountField = new BMenuField("accountMenuField", NULL,
CreateAccountMenu(fAccounts, BMessage(kSelAccount),
new BMessage(kSelNoAccount)));
font_height fontHeight;
fRosterView->GetFontHeight(&fontHeight);
int16 buttonHeight = int16(fontHeight.ascent + fontHeight.descent + 12);
BSize charButtonSize(buttonHeight, buttonHeight);
BButton* fAddButton = new BButton("+", new BMessage(kAddMember));
BButton* fRemoveButton = new BButton("-", new BMessage(kRemoveMember));
fAddButton->SetExplicitSize(charButtonSize);
fAddButton->SetEnabled(true);
fRemoveButton->SetExplicitSize(charButtonSize);
fRemoveButton->SetEnabled(false);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(fRosterView)
.AddGroup(B_HORIZONTAL, 0, 0.0)
.Add(fAccountField)
.AddGlue()
.Add(new BSeparatorView(B_VERTICAL))
.AddGroup(B_VERTICAL, 0, 0.0)
.Add(new BSeparatorView(B_HORIZONTAL))
.AddGroup(B_HORIZONTAL, 1, 0.0)
.SetInsets(1)
.Add(fRemoveButton)
.Add(fAddButton)
.End()
.Add(new BSeparatorView(B_HORIZONTAL))
.End()
.Add(new BSeparatorView(B_VERTICAL))
.End()
.End();
CenterOnScreen();
}
void
RosterEditWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kEditMember:
{
int index = message->FindInt32("index");
RosterItem* ritem = fRosterView->ListView()->RosterItemAt(index);
if (ritem == NULL)
return;
User* user = ritem->GetContact();
TemplateWindow* win =
new TemplateWindow("Editing contact", "roster", new BMessage(),
fServer, user->GetProtocolLooper()->GetInstance());
win->Show();
break;
}
case kAddMember:
{
BMessage* add = new BMessage(IM_MESSAGE);
add->AddInt32("im_what", IM_CONTACT_LIST_ADD_CONTACT);
TemplateWindow* win =
new TemplateWindow("Adding contact", "roster", add, fServer);
win->Show();
break;
}
case kSelAccount:
{
int index = message->FindInt32("index") - 1;
if (index < 0 || index > (fAccounts.CountItems() - 1))
return;
fRosterView->SetAccount(fAccounts.ValueAt(index));
break;
}
case kSelNoAccount:
fRosterView->SetAccount(-1);
break;
default:
BWindow::MessageReceived(message);
}
}
void
RosterEditWindow::UpdateListItem(RosterItem* item)
{
fRosterView->UpdateListItem(item);
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Andrea Anzani, andrea.anzani@gmail.com
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Jaidyn Levesque, jadedctrl@teknik.io
*/
#ifndef _ROSTER_EDITWINDOW_H
#define _ROSTER_EDIT_WINDOW_H
#include <Window.h>
#include "Server.h"
class BMenuField;
class RosterItem;
class RosterView;
/* A window with the a list of the user's contacts, will send a message to
the server with contact info, once a contact is selected. */
class RosterEditWindow : public BWindow {
public:
RosterEditWindow(Server* server);
void MessageReceived(BMessage* message);
void UpdateListItem(RosterItem* item);
private:
BMenuField* fAccountField;
AccountInstances fAccounts;
Server* fServer;
RosterView* fRosterView;
};
#endif // _ROSTER_EDIT_WINDOW_H

View File

@ -17,6 +17,7 @@
#include <TextControl.h> #include <TextControl.h>
#include <String.h> #include <String.h>
#include "CayaUtils.h"
#include "TemplateView.h" #include "TemplateView.h"
@ -26,7 +27,7 @@ const uint32 kAccSelected = 'JWas';
TemplateWindow::TemplateWindow(const char* title, const char* templateType, TemplateWindow::TemplateWindow(const char* title, const char* templateType,
BMessage* msg, Server* server) BMessage* msg, Server* server, bigtime_t instance)
: :
BWindow(BRect(0, 0, 400, 100), title, B_FLOATING_WINDOW, BWindow(BRect(0, 0, 400, 100), title, B_FLOATING_WINDOW,
B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
@ -38,12 +39,33 @@ TemplateWindow::TemplateWindow(const char* title, const char* templateType,
fMessage(msg), fMessage(msg),
fTarget(NULL) fTarget(NULL)
{ {
_InitInterface(); _InitInterface(instance);
_LoadTemplate(); _LoadTemplate();
CenterOnScreen(); CenterOnScreen();
} }
TemplateWindow::TemplateWindow(const char* title, ProtocolTemplate* temp,
BMessage* msg, Server* server, bigtime_t instance)
:
BWindow(BRect(0, 0, 400, 100), title, B_FLOATING_WINDOW,
B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE),
fServer(server),
fAccounts(server->GetAccounts()),
fSelectedAcc(0),
fTemplate(temp),
fMessage(msg)
{
_InitInterface(instance);
CenterOnScreen();
fTemplate = temp;
fTemplate->Load(fTemplateView);
fTemplateView->AttachedToWindow();
fTemplateView->MakeFocus(true);
}
void void
TemplateWindow::MessageReceived(BMessage* msg) TemplateWindow::MessageReceived(BMessage* msg)
{ {
@ -60,7 +82,6 @@ TemplateWindow::MessageReceived(BMessage* msg)
// Save account settings // Save account settings
if (fTemplate == NULL || fTemplateView == NULL) if (fTemplate == NULL || fTemplateView == NULL)
break; break;
BString error = "Some items are empty. Please make sure to fill " BString error = "Some items are empty. Please make sure to fill "
"out every item."; "out every item.";
BMessage* settings = new BMessage(*fMessage); BMessage* settings = new BMessage(*fMessage);
@ -96,10 +117,20 @@ TemplateWindow::SetTarget(BHandler* target)
void void
TemplateWindow::_InitInterface() TemplateWindow::_InitInterface(bigtime_t instance)
{ {
fTemplateView = new TemplateView("template"); fTemplateView = new TemplateView("template");
fMenuField = new BMenuField("accountMenuField", NULL, _CreateAccountMenu()); BMenu* menu = CreateAccountMenu(fAccounts, BMessage(kAccSelected));
fMenuField = new BMenuField("accountMenuField", NULL, menu);
if (instance > -1) {
for (int i = 0; i < fAccounts.CountItems(); i++)
if (fAccounts.ValueAt(i) == instance) {
menu->ItemAt(i)->SetMarked(true);
break;
}
fMenuField->SetEnabled(false);
}
BButton* fOkButton = new BButton("OK", new BMessage(kOK)); BButton* fOkButton = new BButton("OK", new BMessage(kOK));
if (fAccounts.CountItems() <= 0) if (fAccounts.CountItems() <= 0)
@ -124,7 +155,7 @@ TemplateWindow::_InitInterface()
void void
TemplateWindow::_LoadTemplate() TemplateWindow::_LoadTemplate()
{ {
if (fAccounts.CountItems() == 0) if (fAccounts.CountItems() == 0 || fTemplateType.IsEmpty() == true)
return; return;
ProtocolLooper* looper ProtocolLooper* looper
@ -141,23 +172,3 @@ TemplateWindow::_LoadTemplate()
fTemplateView->AttachedToWindow(); fTemplateView->AttachedToWindow();
fTemplateView->MakeFocus(true); fTemplateView->MakeFocus(true);
} }
BMenu*
TemplateWindow::_CreateAccountMenu()
{
BMenu* menu = new BMenu("accountMenu");
for (int i = 0; i < fAccounts.CountItems(); i++)
menu->AddItem(new BMenuItem(fAccounts.KeyAt(i).String(),
new BMessage(kAccSelected)));
menu->SetRadioMode(true);
menu->SetLabelFromMarked(true);
menu->ItemAt(fSelectedAcc)->SetMarked(true);
if (fAccounts.CountItems() == 0)
menu->SetEnabled(false);
return menu;
}

View File

@ -22,18 +22,24 @@ class TemplateView;
class TemplateWindow : public BWindow { class TemplateWindow : public BWindow {
public: public:
/*! Get template from selected account's protocol
* via CayaProtocol::SettingsTemplate() */
TemplateWindow(const char* title, TemplateWindow(const char* title,
const char* templateType, BMessage* msg, const char* templateType, BMessage* msg,
Server* server); Server* server, bigtime_t instance = -1);
/*! Use only the given template. */
TemplateWindow(const char* title,
ProtocolTemplate* temp, BMessage* msg,
Server* server, bigtime_t instance = -1);
virtual void MessageReceived(BMessage* msg); virtual void MessageReceived(BMessage* msg);
void SetTarget(BHandler* target); void SetTarget(BHandler* target);
private: private:
void _InitInterface(); void _InitInterface(bigtime_t instance);
void _LoadTemplate(); void _LoadTemplate();
BMenu* _CreateAccountMenu();
Server* fServer; Server* fServer;
AccountInstances fAccounts; AccountInstances fAccounts;

View File

@ -242,6 +242,18 @@ JabberHandler::Process(BMessage* msg)
_MUCModeration(msg); _MUCModeration(msg);
break; break;
case IM_CONTACT_LIST_ADD_CONTACT: {
BString user_name = msg->FindString("user_name");
BString user_id;
if (msg->FindString("user_id", &user_id) != B_OK)
break;
fClient->rosterManager()->add(gloox::JID(user_id.String()),
user_name.String(), gloox::StringList());
fClient->rosterManager()->subscribe(gloox::JID(user_id.String()),
user_name.String());
fClient->rosterManager()->synchronize();
break;
}
default: default:
return B_ERROR; return B_ERROR;
} }
@ -1184,7 +1196,7 @@ JabberHandler::_RoomTemplate()
BMessage stemplate('IMst'); BMessage stemplate('IMst');
BMessage roomIdentifier; BMessage roomIdentifier;
roomIdentifier.AddString("name", "chat_id"); roomIdentifier.AddString("name", "chat_id");
roomIdentifier.AddString("description", "Room identifier:"); roomIdentifier.AddString("description", "Room ID:");
roomIdentifier.AddString("error", "You can't create a room without a JID!\n" roomIdentifier.AddString("error", "You can't create a room without a JID!\n"
"Use the \"name@server\" format."); "Use the \"name@server\" format.");
roomIdentifier.AddInt32("type", 'CSTR'); roomIdentifier.AddInt32("type", 'CSTR');
@ -1194,6 +1206,29 @@ JabberHandler::_RoomTemplate()
} }
BMessage
JabberHandler::_RosterTemplate()
{
BMessage stemplate('IMst');
BMessage user_id;
user_id.AddString("name", "user_id");
user_id.AddString("description", "User ID:");
user_id.AddString("error", "You can't befriend an IDless miscreant!\n"
"Please use the \"name@server\" format.");
user_id.AddInt32("type", 'CSTR');
stemplate.AddMessage("setting", &user_id);
BMessage user_name;
user_name.AddString("name", "user_name");
user_name.AddString("description", "Nickname:");
user_name.AddInt32("type", 'CSTR');
stemplate.AddMessage("setting", &user_name);
return stemplate;
}
/*********************************************************************** /***********************************************************************
* gloox callbacks * gloox callbacks
**********************************************************************/ **********************************************************************/

View File

@ -97,6 +97,7 @@ protected:
BMessage _SettingsTemplate(const char* username, bool serverOption); BMessage _SettingsTemplate(const char* username, bool serverOption);
BMessage _RoomTemplate(); BMessage _RoomTemplate();
BMessage _RosterTemplate();
private: private:
CayaProtocolMessengerInterface* CayaProtocolMessengerInterface*

View File

@ -52,6 +52,8 @@ JabberProtocol::SettingsTemplate(const char* name)
return JabberHandler::_SettingsTemplate("Jabber identifier:", true); return JabberHandler::_SettingsTemplate("Jabber identifier:", true);
if (name == BString("room")) if (name == BString("room"))
return JabberHandler::_RoomTemplate(); return JabberHandler::_RoomTemplate();
if (name == BString("roster"))
return JabberHandler::_RosterTemplate();
else else
return BMessage(); return BMessage();
} }