diff --git a/application/CayaMessages.h b/application/CayaMessages.h index 2a795ee..9bc1d94 100644 --- a/application/CayaMessages.h +++ b/application/CayaMessages.h @@ -56,4 +56,7 @@ const uint32 CAYA_REQUEST_HELP = 'CYhm'; //! Display a "user info" window const uint32 CAYA_USER_INFO = 'CYuw'; +//! Edit the contact roster +const uint32 CAYA_EDIT_ROSTER = 'CYer'; + #endif // _CAYA_MESSAGES_H diff --git a/application/CayaProtocolMessages.h b/application/CayaProtocolMessages.h index 6ba459a..15537cc 100644 --- a/application/CayaProtocolMessages.h +++ b/application/CayaProtocolMessages.h @@ -36,10 +36,16 @@ enum im_what_code { // Requires: Stringlist "user_id" IM_CONTACT_LIST = 2, - //! Contact(s) was added to the server-side list →Caya - // Requires: String "user_id" + //! Add a contact to the roster →Protocol + // The slots for this message are determined by the protocol's + // "roster" template (CayaProtocol::SettingsTemplate("roster")) 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 // Requires: String "user_id" IM_CONTACT_LIST_REMOVED_CONTACT = 4, @@ -113,7 +119,8 @@ enum im_what_code { IM_GET_EXTENDED_CONTACT_INFO = 64, //! 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" IM_EXTENDED_CONTACT_INFO = 65, diff --git a/application/Makefile b/application/Makefile index 2c45c64..e640331 100644 --- a/application/Makefile +++ b/application/Makefile @@ -80,6 +80,7 @@ SRCS = \ application/windows/JoinWindow.cpp \ application/windows/MainWindow.cpp \ application/windows/PreferencesWindow.cpp \ + application/windows/RosterEditWindow.cpp \ application/windows/RosterWindow.cpp \ application/windows/TemplateWindow.cpp \ application/windows/UserInfoWindow.cpp diff --git a/application/ProtocolTemplate.cpp b/application/ProtocolTemplate.cpp index 56c0d34..7ec861b 100644 --- a/application/ProtocolTemplate.cpp +++ b/application/ProtocolTemplate.cpp @@ -251,8 +251,10 @@ ProtocolTemplate::Save(BView* parent, BMessage* settings, BString* errorText) BTextControl* textControl = dynamic_cast(view); - if (textControl && BString(textControl->Text()).IsEmpty() == true) { - if (error.IsEmpty() == false && errorText != NULL) + if (textControl && BString(textControl->Text()).IsEmpty() == true + && error.IsEmpty() == false) + { + if (errorText != NULL) errorText->SetTo(error); return B_BAD_VALUE; } diff --git a/application/windows/MainWindow.cpp b/application/windows/MainWindow.cpp index 8eab286..d53fa5e 100644 --- a/application/windows/MainWindow.cpp +++ b/application/windows/MainWindow.cpp @@ -31,6 +31,7 @@ #include "NotifyMessage.h" #include "PreferencesWindow.h" #include "ReplicantStatusView.h" +#include "RosterEditWindow.h" #include "RosterWindow.h" #include "Server.h" #include "StatusView.h" @@ -46,6 +47,7 @@ MainWindow::MainWindow() fWorkspaceChanged(false), fConversation(NULL), fRosterWindow(NULL), + fRosterEditWindow(NULL), fServer(NULL) { _InitInterface(); @@ -158,6 +160,12 @@ MainWindow::MessageReceived(BMessage* message) fRosterWindow->Show(); break; } + case CAYA_EDIT_ROSTER: + { + fRosterEditWindow = new RosterEditWindow(fServer); + fRosterEditWindow->Show(); + break; + } case CAYA_MOVE_UP: { if (fConversation == NULL) @@ -271,7 +279,10 @@ MainWindow::ImMessage(BMessage* msg) case IM_CONTACT_INFO: case IM_EXTENDED_CONTACT_INFO: case IM_STATUS_SET: - fRosterWindow->PostMessage(msg); + if (fRosterWindow != NULL) + fRosterWindow->PostMessage(msg); + if (fRosterEditWindow != NULL) + fRosterEditWindow->PostMessage(msg); break; case IM_PROTOCOL_READY: @@ -454,24 +465,24 @@ MainWindow::_CreateMenuBar() // Chat BMenu* chatMenu = new BMenu("Chat"); - BMenuItem* joinRoom = new BMenuItem("Join room" B_UTF8_ELLIPSIS, - 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->AddItem(new BMenuItem("Join room" B_UTF8_ELLIPSIS, + new BMessage(CAYA_JOIN_ROOM), 'J', B_COMMAND_KEY)); chatMenu->AddSeparatorItem(); - chatMenu->AddItem(newChat); - chatMenu->AddItem(newRoom); - chatMenu->AddSeparatorItem(); - chatMenu->AddItem(invite); + chatMenu->AddItem(new BMenuItem("New room" B_UTF8_ELLIPSIS, + new BMessage(CAYA_NEW_ROOM), 'N', B_COMMAND_KEY)); + chatMenu->AddItem(new BMenuItem("New chat" B_UTF8_ELLIPSIS, + new BMessage(CAYA_NEW_CHAT), 'M', B_COMMAND_KEY)); 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 BMenu* windowMenu = new BMenu("Window"); windowMenu->AddItem(new BMenuItem("Up", @@ -482,6 +493,7 @@ MainWindow::_CreateMenuBar() menuBar->AddItem(programMenu); menuBar->AddItem(chatMenu); + menuBar->AddItem(rosterMenu); menuBar->AddItem(windowMenu); return menuBar; diff --git a/application/windows/MainWindow.h b/application/windows/MainWindow.h index 5d432ff..9c37d0b 100644 --- a/application/windows/MainWindow.h +++ b/application/windows/MainWindow.h @@ -20,6 +20,7 @@ class ConversationItem; class ConversationListView; class ConversationView; class RosterItem; +class RosterEditWindow; class RosterWindow; class Server; class StatusView; @@ -57,6 +58,7 @@ private: Server* fServer; RosterWindow* fRosterWindow; + RosterEditWindow* fRosterEditWindow; bool fWorkspaceChanged; BMenuBar* fMenuBar; diff --git a/application/windows/RosterEditWindow.cpp b/application/windows/RosterEditWindow.cpp new file mode 100644 index 0000000..25f2411 --- /dev/null +++ b/application/windows/RosterEditWindow.cpp @@ -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 +#include +#include +#include +#include +#include + +#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); +} diff --git a/application/windows/RosterEditWindow.h b/application/windows/RosterEditWindow.h new file mode 100644 index 0000000..975e852 --- /dev/null +++ b/application/windows/RosterEditWindow.h @@ -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 + +#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 diff --git a/application/windows/TemplateWindow.cpp b/application/windows/TemplateWindow.cpp index fc83c6f..02e2f1c 100644 --- a/application/windows/TemplateWindow.cpp +++ b/application/windows/TemplateWindow.cpp @@ -17,6 +17,7 @@ #include #include +#include "CayaUtils.h" #include "TemplateView.h" @@ -26,7 +27,7 @@ const uint32 kAccSelected = 'JWas'; 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, 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), fTarget(NULL) { - _InitInterface(); + _InitInterface(instance); _LoadTemplate(); 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 TemplateWindow::MessageReceived(BMessage* msg) { @@ -60,7 +82,6 @@ TemplateWindow::MessageReceived(BMessage* msg) // Save account settings if (fTemplate == NULL || fTemplateView == NULL) break; - BString error = "Some items are empty. Please make sure to fill " "out every item."; BMessage* settings = new BMessage(*fMessage); @@ -96,10 +117,20 @@ TemplateWindow::SetTarget(BHandler* target) void -TemplateWindow::_InitInterface() +TemplateWindow::_InitInterface(bigtime_t instance) { 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)); if (fAccounts.CountItems() <= 0) @@ -124,7 +155,7 @@ TemplateWindow::_InitInterface() void TemplateWindow::_LoadTemplate() { - if (fAccounts.CountItems() == 0) + if (fAccounts.CountItems() == 0 || fTemplateType.IsEmpty() == true) return; ProtocolLooper* looper @@ -141,23 +172,3 @@ TemplateWindow::_LoadTemplate() fTemplateView->AttachedToWindow(); 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; -} diff --git a/application/windows/TemplateWindow.h b/application/windows/TemplateWindow.h index 0b9225a..a24df36 100644 --- a/application/windows/TemplateWindow.h +++ b/application/windows/TemplateWindow.h @@ -22,18 +22,24 @@ class TemplateView; class TemplateWindow : public BWindow { public: + /*! Get template from selected account's protocol + * via CayaProtocol::SettingsTemplate() */ TemplateWindow(const char* title, 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); void SetTarget(BHandler* target); private: - void _InitInterface(); + void _InitInterface(bigtime_t instance); void _LoadTemplate(); - BMenu* _CreateAccountMenu(); Server* fServer; AccountInstances fAccounts; diff --git a/protocols/xmpp/JabberHandler.cpp b/protocols/xmpp/JabberHandler.cpp index 6a6deab..ede34f5 100644 --- a/protocols/xmpp/JabberHandler.cpp +++ b/protocols/xmpp/JabberHandler.cpp @@ -242,6 +242,18 @@ JabberHandler::Process(BMessage* msg) _MUCModeration(msg); 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: return B_ERROR; } @@ -1184,7 +1196,7 @@ JabberHandler::_RoomTemplate() BMessage stemplate('IMst'); BMessage roomIdentifier; 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" "Use the \"name@server\" format."); 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 **********************************************************************/ diff --git a/protocols/xmpp/JabberHandler.h b/protocols/xmpp/JabberHandler.h index 4f78386..90494b0 100644 --- a/protocols/xmpp/JabberHandler.h +++ b/protocols/xmpp/JabberHandler.h @@ -97,6 +97,7 @@ protected: BMessage _SettingsTemplate(const char* username, bool serverOption); BMessage _RoomTemplate(); + BMessage _RosterTemplate(); private: CayaProtocolMessengerInterface* diff --git a/protocols/xmpp/JabberProtocol.cpp b/protocols/xmpp/JabberProtocol.cpp index 6dcff17..3e796c1 100644 --- a/protocols/xmpp/JabberProtocol.cpp +++ b/protocols/xmpp/JabberProtocol.cpp @@ -52,6 +52,8 @@ JabberProtocol::SettingsTemplate(const char* name) return JabberHandler::_SettingsTemplate("Jabber identifier:", true); if (name == BString("room")) return JabberHandler::_RoomTemplate(); + if (name == BString("roster")) + return JabberHandler::_RosterTemplate(); else return BMessage(); }