Compare commits

...

37 Enmetoj

Author SHA1 Message Date
Jaidyn Ann 4284a8bb23
Merge pull request #26 from kallisti5/fixes
A crash fix, as well as fixes for the latest matrix_client
2023-12-30 02:28:16 +00:00
Alexander von Gluck IV f78034cb84 data/icons: Use Haiku's App_Chat icon 2023-12-22 17:18:30 -06:00
Alexander von Gluck IV 1a0ee401be protocols/matrix: Record message when from server timestamp on initial_sync 2023-12-22 17:04:26 -06:00
Alexander von Gluck IV 4964605432 protocols/matrix: Add some basic room metadata for friendly room names 2023-12-22 12:26:54 -06:00
Alexander von Gluck IV 53a5c5ed1a protocols/matrix: Give the user notice on initial sync.
* This can be 30+ seconds of silence, so give user a little
  feedback that "stuff is happening"
2023-12-22 09:00:22 -06:00
Alexander von Gluck IV d4b934093d protocols/matrix: Add some needed dependencies for the latest matrix_client 2023-12-22 08:37:32 -06:00
Alexander von Gluck IV 8f5762219e application/windows: Guard from NULL dereference finding conversation
* Happens from the native matrix plugin on a large account
2023-12-22 08:37:19 -06:00
Jaidyn Ann 8e3c961c29
Merge pull request #24 from kallisti5/fix-gcc11
libsupport: Fix build on gcc 11.2
2023-04-01 03:37:51 +00:00
Jaidyn Ann 887d7d120d
Merge pull request #25 from kallisti5/fix-matrix
protocols/matrix: Fix quick build of early matrix protocol
2023-04-01 03:37:28 +00:00
Alexander von Gluck IV 4489f80507 protocols/matrix: Fix icon 2023-03-07 12:27:36 -06:00
Alexander von Gluck IV 3f7201038d protocols/matrix: Fix quick build of early matrix protocol 2023-03-07 10:22:20 -06:00
Alexander von Gluck IV 1168785a62 libsupport: Fix build on gcc 11.2 2023-03-07 09:36:07 -06:00
Jaidyn Ann dce82c2ba2 Use Server::Get(), clean up header usage
Instead of passing around pointers to the Server, Server::Get() returns
the server's pointer (simplifying some things a good bit).
As a result, headers have been cleaned up, and some redundant KeyMaps
have been placed in Maps.h.
2022-02-23 16:04:56 -06:00
Jaidyn Ann e092128200 Don't load multiple version of the same add-on
Now solely the one of highest priority is used (e.g.,
`non-packaged/add-ons/` over `system/add-ons/`).

Fixes #20
2022-02-23 12:11:17 -06:00
Jaidyn Ann b7b45a8db0 Add preference for hiding join/part messages 2022-02-21 18:33:46 -06:00
Jaidyn Ann ce4d3c2a26 Fix checking of ConversationView's visibility
Since ConversationViews were manually removed/added previously, now
that a BCardLayout is used, the visibility-detection/some actions
change (e.g., using BView::Show instead of BView::AttachedToWindow).
2022-02-21 18:09:54 -06:00
Jaidyn Ann 6f01818e8a Use BCardLayout instead of manual BView switching
Structures MainWindow around a Conversations→BLayoutItem map &
BCardLayout, instead of directly removing and adding ConversationViews
every time.
2022-02-20 21:00:56 -06:00
Jaidyn Ann eb60c94d68 Add user documentation (HTML)
… openable through menu-bar as Program→Help.

Following Humdinger's lead here, he did a bang-up job on Calendar's docs!
His code for opening documentation in Calendar is re-used here, too.
2022-02-20 10:58:59 -06:00
Jaidyn Ann dd5add390a Remove unused smileys
They're not currently used, and vectors might be preferable, anyhow.
2022-02-20 10:17:49 -06:00
Jaidyn Ann 8d50a6b9a4 Pass relevant cache paths directly to add-ons
Now, instead of using Utils.cpp's AccountCachePath and AddOnCachePath,
and add-on should implement ChatProtocol::SetAccountCachePath and
ChatProtocol::SetAddOnCachePath.

In addition, most path-related functions in Utils now return BPaths― and
some of interest to chat add-ons can accept the path given by
ChatProtocol::SetAccountCachePath as the main argument.
2022-02-18 15:42:06 -06:00
Jaidyn Ann 866899eaad Get app name & signature from Make.pre/Make.post
It's useful to change the name and signature when you want to test
changes while using another instance of Chat-O-Matic.
2022-01-08 16:50:34 -06:00
Jaidyn Ann 4ad75cd807 Use system AboutWindow instead of custom 2021-12-14 10:28:15 -06:00
Jaidyn Ann 06df963434 Update catkeys 2021-10-29 14:03:04 -05:00
Jaidyn Ann 8456e00bd8 (matrix) Fetch own displayname, use correct user ID 2021-09-04 21:17:50 -05:00
Jaidyn Ann 604082466e (matrix) Init Matrix protocol
Just the scaffolding of a Matrix protocol using mtxclient: Room-joins
and (unencrypted) messages can be received, but everything's read-only
as yet. More to come!
2021-09-03 03:43:28 -05:00
Jaidyn Ann 2f69f2fa04 No functional change; remove unused 'join' message
… also slight formatting tweaks, safer fetching of a Conversation's
list item in MainWindow, and an extra accessor for
ConversationListView.
2021-09-03 03:35:35 -05:00
Jaidyn Ann 3689f6cee3 Searching through chat with TextSearch 2021-08-31 22:04:19 -05:00
Jaidyn Ann e2d801b84b Save user name and color on message received
In Chat-O-Matic, text messages are enqueued by the ConversationView
and appended when the ConversationView becomes attached to the window
(editing a non-attached BTextView doesn't go well).

Previously, only the receive-time was added to the enqueued message― so
that if the view doesn't come into focus for a long while after message
receipt, the timestamp is still accurate. The user's nickname and color
weren't added as well, meaning that if the user left the room before the
message was appended, the ugly user-id and default user color would be
used instead.

This appends user_color and user_name.
2021-08-31 21:00:29 -05:00
Jaidyn Ann 6a160ced88 Sort room directory by account, 'Category' column
The room directory window now has an accounts menu, to allow the user to
filter rooms by account― and a "category" column has been added, which can
optionally be filled by the protocol for more semantic sorting of rooms
(Through "category" slot of IM_ROOM_DIRECTORY).
2021-08-31 20:31:39 -05:00
Jaidyn Ann 8cb98ccf4b (purple) Room directory support 2021-08-29 22:31:56 -05:00
Jaidyn Ann f19bcba62a Add Room Directory window
This window (Chat→Room directory) is to be used for either a list of
publicly available rooms (in most protocols), or for a list of
joined-but-hidden rooms (as it'll used with libpurple, to list its
ChatBuddies).

Each room is sent individually from protocols using IM_ROOM_DIRECTORY
messages sent in response to a IM_GET_ROOM_DIRECTORY message.
2021-08-27 11:00:04 -05:00
Jaidyn Ann 5dbbb3a8ad (irc) Public room directory/list support 2021-08-27 00:25:41 -05:00
Jaidyn Ann 10b63e4138 x86 fix for ItemWeight use 2021-08-22 11:44:03 -05:00
Jaidyn Ann bcb92de53c (SendTextView) Check for important keys in each received byte 2021-08-22 11:36:35 -05:00
Jaidyn Ann c24c3187ad
Merge pull request #1 from Begasus/purple
Makefile purple, fix search for libpurple's headers
2021-08-22 10:19:37 -05:00
begasus 115631a91d Makefile purple, fix search for libpurple's headers 2021-08-22 15:31:44 +00:00
Jaidyn Ann aaea63de1a Link XMPP to libsupport 2021-08-20 23:17:36 -05:00
234 changed files with 2726 additions and 2374 deletions

View File

@ -2,6 +2,8 @@ LIBPATHS = $(OBJ_DIR)
PROTOCOL_DIR = $(OBJ_DIR)/chat-o-matic/
DEBUG_ENABLED ?= false
DEFINES := VERSION="\"0.0.2\"" \
DEFINES = VERSION="\"0.0.2\"" \
BUILD_DATE="\"$(shell date +"%Y-%m-%d %H:%M")\"" \
DEBUG_ENABLED=$(DEBUG_ENABLED)
DEBUG_ENABLED=$(DEBUG_ENABLED) \
APP_NAME="\"$(APP_NAME)"\" \
APP_SIGNATURE="\"$(APP_SIGNATURE)"\"

2
Make.pre Normal file
View File

@ -0,0 +1,2 @@
APP_NAME ?= Chat-O-Matic
APP_SIGNATURE ?= application/x-vnd.chat-o-matic

View File

@ -12,6 +12,9 @@ irc:
xmpp:
$(MAKE) -f protocols/xmpp/Makefile
matrix:
$(MAKE) -f protocols/matrix/Makefile
purple:
ifneq ($(shell uname -m), x86_gcc2)
$(MAKE) -f protocols/purple/Makefile

View File

@ -14,6 +14,8 @@ Protocols natively supported include IRC and XMPP.
Protocols generally supported through libpurple include GroupWise, Zephyr, and
[others through plugins](https://pidgin.im/plugins/?type=Protocol).
You can find the user documentation [here](http://htmlpreview.github.io/?https://github.com/JadedCtrl/Chat-O-Matic/master/documentation/Documentation.html).
## Building
You can make Chat-O-Matic and its protocols with:

View File

@ -31,11 +31,13 @@ Account::Account(bigtime_t instanceId, ChatProtocol* cayap,
fProtocol->Init(this);
// Find user's settings path
BPath path(AccountPath(addOnSignature, fProtocol->Signature()));
BPath path = AccountPath(addOnSignature, fProtocol->Signature());
if (path.InitCheck() == B_OK) {
path.Append(name);
fProtocol->SetName(name);
fProtocol->SetAccountCachePath(AccountCachePath(name));
fProtocol->SetAddOnCachePath(AddOnCachePath(addOnSignature));
// Load settings file
BFile file(path.Path(), B_READ_ONLY);

View File

@ -8,6 +8,9 @@
//! Show settings window
const uint32 APP_SHOW_SETTINGS = 'RPST';
//! Show documentation
const uint32 APP_SHOW_HELP = 'Rhlp';
//! Show accounts window
const uint32 APP_SHOW_ACCOUNTS = 'RPac';
@ -23,6 +26,9 @@ const uint32 APP_NEW_ROOM = 'CYnr';
//! Join a chat
const uint32 APP_JOIN_ROOM = 'CYjr';
//! Room directory
const uint32 APP_ROOM_DIRECTORY = 'CYrd';
//! Invite user to current chat
const uint32 APP_SEND_INVITE = 'CYin';
@ -59,6 +65,9 @@ const uint32 APP_USER_INFO = 'CYuw';
//! Display a "room info" window
const uint32 APP_ROOM_INFO = 'CYrw';
//! Open the room's logs with TextSearch
const uint32 APP_ROOM_SEARCH = 'CYrs';
//! Toggle a specific flag for a room
const uint32 APP_ROOM_FLAG = 'Rlag';

View File

@ -10,7 +10,9 @@
#include "Conversation.h"
#include "MainWindow.h"
#include "ProtocolLooper.h"
#include "TheApp.h"
#include "User.h"
#undef B_TRANSLATION_CONTEXT

View File

@ -12,10 +12,11 @@
#include <libsupport/KeyMap.h>
#include <libsupport/List.h>
#include "Maps.h"
class Conversation;
class User;
typedef KeyMap<BString, User*> UserMap;
enum cmd_arg_type
@ -61,7 +62,4 @@ private:
List<int32> fArgTypes;
};
typedef KeyMap<BString, ChatCommand*> CommandMap;
#endif // CHAT_COMMAND_H

View File

@ -1,11 +0,0 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _APP_H
#define _APP_H
#define APP_SIGNATURE "application/x-vnd.chat-o-matic"
#define APP_NAME "Chat-O-Matic"
#endif // _APP_H

View File

@ -1,5 +1,3 @@
#include "ChatOMatic.h"
resource app_signature APP_SIGNATURE;
resource app_version {
@ -18,3 +16,18 @@ resource app_flags B_SINGLE_LAUNCH;
resource file_types message;
resource vector_icon {
$"6E636966040500020106033D835C3C19B2BA8B0B3C20794769624A510E00FFFF"
$"FFB4FFE405FFFFA405020106023D835C3C19B2BA8B0B3C20794769624A510E00"
$"FFE405FFFFA405020106033D429E3C5148BB5ADA3C5C1B4A23AA46EC1800FFFF"
$"FFB47FE583FF04B10C0902093F404644383C273F2E3A244122482245224D2755"
$"245126572256245725582A5828592C5A315C2F5B345D3C5E385E425E4957475C"
$"4D4E02043F4044433D3FBC95BE953B3D333D3D493844424E4A5148534A4A0403"
$"3B2F4E2F4E2D4E2A4D2F532D522F530003334F334F364F345337533755325636"
$"59325602085645C8FEC3AFCA30C0E55E3A5E405EBA4D52B58359B71B4AB38537"
$"B51D3EB31FBA16B69933353130363E4A4641444D475346C657C27B554BCB01C6"
$"35584F5A4F080239BA0539BA6B0802BD1CBC1DBD2FBCA908023E37423904032E"
$"4535473C4739473F4540070A000100123FFFFE2FDACEAFDACE3FFFFE3AB6A5B8"
$"4CC101178400040A010100000A020101000A000202031001178210040A000104"
$"1001178400040A030104000A000407050608100117821004"
};

View File

@ -88,9 +88,11 @@ public:
//! Protocol icon
virtual BBitmap* Icon() const { return NULL; }
//! Add-on's path
virtual void SetAddOnPath(BPath path) = 0;
//! Pertinent paths
virtual BPath AddOnPath() = 0;
virtual void SetAddOnPath(BPath path) = 0;
virtual void SetAccountCachePath(BPath path) { };
virtual void SetAddOnCachePath(BPath path) { };
//! Name of account file (leaf)
virtual const char* GetName() = 0;

View File

@ -27,6 +27,13 @@ ChatProtocolAddOn::ChatProtocolAddOn(image_id image, const char* path, int32 sub
}
ChatProtocolAddOn::~ChatProtocolAddOn()
{
delete fIcon;
unload_add_on(fImage);
}
status_t
ChatProtocolAddOn::InitCheck() const
{
@ -52,7 +59,7 @@ ChatProtocol*
ChatProtocolAddOn::ProtocolAt(int32 i) const
{
ChatProtocol* proto = fGetProtocol(i);
proto->SetAddOnPath(BPath(fPath.String()));
proto->SetAddOnPath(fPath.String());
return proto;
}

View File

@ -17,6 +17,7 @@ class ChatProtocolAddOn {
public:
ChatProtocolAddOn(image_id image, const char* path,
int32 subProto=0);
~ChatProtocolAddOn();
status_t InitCheck() const;

View File

@ -262,8 +262,8 @@ enum im_what_code {
/*! Quietly add user(s) to the chat →App
Shouldn't be sent automatically on joining a room.
Requires: String "chat_id", StringList "user_id"
Accepts: StringList "user_name" */
Requires: String "chat_id", Strings "user_id"
Accepts: Strings "user_name" */
IM_ROOM_PARTICIPANTS = 159,
/*! User has explicitly joined →App
@ -405,6 +405,29 @@ enum im_what_code {
IM_ROOM_PARTICIPANT_STOPPED_TYPING = 211,
/*
* Room directory messages
*/
/*! Request a list of rooms →Protocol */
IM_GET_ROOM_DIRECTORY = 230,
/*! Send a room in the directory →App
This can be used to send either a list of publically available rooms
or a list of "hidden"/"disabled" rooms, one-by-one.
A room listed thanks to this message might be joined through
IM_JOIN_ROOM. Since IM_JOIN_ROOM accepts slots from the template, you
must fill in IM_ROOM_DIRECTORY messages with any required custom slots
you use for room-joining the message you send will be actually be
copied and sent back verbatim (with im_what changed to IM_JOIN_ROOM)
to join.
Requires: Strings "chat_id"
Allows: String "chat_name", String "subject", String "category",
int32 "user_count" */
IM_ROOM_DIRECTORY = 231,
/*
* Misc. UI messages
*/

View File

@ -35,8 +35,5 @@ Contact::_EnsureCachePath()
{
if (fCachePath.InitCheck() == B_OK)
return;
fCachePath.SetTo(ContactCachePath(fLooper->Protocol()->GetName(),
fID.String()));
fCachePath = ContactCachePath(fLooper->Protocol()->GetName(), fID.String());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* Copyright 2021-2022, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
@ -14,7 +14,6 @@
#include "AppConstants.h"
#include "AppPreferences.h"
#include "ChatOMatic.h"
#include "ChatProtocolMessages.h"
#include "RenderView.h"
#include "ChatCommand.h"
@ -26,6 +25,7 @@
#include "NotifyMessage.h"
#include "ProtocolLooper.h"
#include "ProtocolManager.h"
#include "Role.h"
#include "Server.h"
#include "TheApp.h"
#include "Utils.h"
@ -147,10 +147,10 @@ Conversation::ImMessage(BMessage* msg)
// Misc. features Caya contributors planned on adding
BWindow* mainWin = ((TheApp*)be_app)->GetMainWindow();
if (win == NULL && AppPreferences::Get()->MarkUnreadWindow == true)
if (winFocus == false && AppPreferences::Get()->MarkUnreadWindow == true)
mainWin->SetTitle(BString(mainWin->Title()).Prepend("[!]"));
if (win == NULL && AppPreferences::Get()->MoveToCurrentWorkspace)
if (winFocus == false && AppPreferences::Get()->MoveToCurrentWorkspace)
mainWin->SetWorkspaces(B_CURRENT_WORKSPACE);
if (win == NULL && AppPreferences::Get()->RaiseOnMessageReceived)
@ -158,9 +158,9 @@ Conversation::ImMessage(BMessage* msg)
// If unattached, highlight the ConversationItem
if (win == NULL && mentioned == true)
if ((win == NULL || GetView()->IsHidden() == true) && mentioned == true)
NotifyInteger(INT_NEW_MENTION, fNotifyMentionCount);
else if (win == NULL)
else if (win == NULL || GetView()->IsHidden())
NotifyInteger(INT_NEW_MESSAGE, fNotifyMessageCount);
break;
@ -187,7 +187,7 @@ Conversation::ImMessage(BMessage* msg)
BString name = CommandName(body);
BString args = CommandArgs(body);
ChatCommand* cmd = _GetServer()->CommandById(name, fLooper->GetInstance());
ChatCommand* cmd = Server::Get()->CommandById(name, fLooper->GetInstance());
if (cmd == NULL) {
if (name == "me")
@ -320,7 +320,7 @@ Conversation::ObserveString(int32 what, BString str)
void
Conversation::ObserveInteger(int32 what, int32 value)
{
if (what == INT_WINDOW_FOCUSED) {
if (what == INT_CONV_VIEW_SELECTED) {
fNotifyMessageCount = 0;
fNotifyMentionCount = 0;
}
@ -576,6 +576,10 @@ Conversation::_LogChatMessage(BMessage* msg)
BFile logFile(fCachePath.Path(), B_READ_WRITE | B_OPEN_AT_END | B_CREATE_FILE);
WriteAttributeMessage(&logFile, "Chat:logs", &logMsg);
BString mime = BString("text/plain");
logFile.WriteAttr("BEOS:TYPE", B_MIME_STRING_TYPE, 0, mime.String(),
mime.CountChars() + 1);
// Plain-text logs
// Gotta make sure the formatting's pretty!
BString date;
@ -633,8 +637,7 @@ Conversation::_EnsureCachePath()
{
if (fCachePath.InitCheck() == B_OK)
return;
fCachePath.SetTo(RoomCachePath(fLooper->Protocol()->GetName(),
fID.String()));
fCachePath = RoomCachePath(fLooper->Protocol()->GetName(), fID.String());
}
@ -653,7 +656,7 @@ Conversation::_EnsureUser(BMessage* msg, bool implicit)
user = serverUser;
// Not anywhere; create user
else if (user == NULL) {
user = new User(id, _GetServer()->Looper());
user = new User(id, Server::Get()->Looper());
user->SetProtocolLooper(fLooper);
fLooper->AddUser(user);
}
@ -757,10 +760,3 @@ Conversation::_SortConversationList()
if (fUsers.CountItems() <= 2 || fUsers.CountItems() == 3)
((TheApp*)be_app)->GetMainWindow()->SortConversation(this);
}
Server*
Conversation::_GetServer()
{
return ((TheApp*)be_app)->GetMainWindow()->GetServer();
}

View File

@ -10,22 +10,18 @@
#include <Path.h>
#include <StringList.h>
#include <libsupport/KeyMap.h>
#include "Maps.h"
#include "Notifier.h"
#include "Observer.h"
#include "Role.h"
#include "Server.h"
#include "User.h"
class BBitmap;
class Contact;
class ConversationItem;
class ConversationView;
class ProtocolLooper;
class Role;
class Server;
typedef KeyMap<BString, User*> UserMap;
typedef KeyMap<BString, Role*> RoleMap;
class User;
class Conversation : public Notifier, public Observer {
@ -76,7 +72,11 @@ public:
void SetFlags(int32 flags);
int32 DisallowedFlags() { return fDisallowedFlags; }
BPath CachePath() { return fCachePath; }
private:
typedef KeyMap<BString, Role*> RoleMap;
void _WarnUser(BString message);
void _LogChatMessage(BMessage* msg);
@ -94,8 +94,6 @@ private:
void _SortConversationList();
Server* _GetServer();
BMessenger fMessenger;
ProtocolLooper* fLooper;
ConversationView* fChatView;

View File

@ -1,3 +1,5 @@
include Make.pre
## Haiku Generic Makefile v2.6 ##
## Fill in this file to specify the project being created, and the referenced
@ -8,7 +10,7 @@
## file:///system/develop/documentation/makefile-engine.html
# The name of the binary.
NAME = Chat-O-Matic
NAME = $(APP_NAME)
# The type of binary, must be one of:
# APP: Application
@ -18,7 +20,7 @@ NAME = Chat-O-Matic
TYPE = APP
# If you plan to use localization, specify the application's MIME signature.
APP_MIME_SIG = application/x-vnd.chat-o-matic
APP_MIME_SIG = $(APP_SIGNATURE)
# The following lines tell Pe and Eddie where the SRCS, RDEFS, and RSRCS are
# so that Pe and Eddie can fill them in for you.
@ -64,6 +66,7 @@ SRCS = \
application/views/InviteDialogue.cpp \
application/views/ReplicantStatusView.cpp \
application/views/ReplicantMenuItem.cpp \
application/views/RoomListRow.cpp \
application/views/RosterItem.cpp \
application/views/RosterListView.cpp \
application/views/RosterView.cpp \
@ -74,11 +77,11 @@ SRCS = \
application/views/UserItem.cpp \
application/views/UserListView.cpp \
application/views/UserPopUp.cpp \
application/windows/AboutWindow.cpp \
application/windows/AccountsWindow.cpp \
application/windows/ConversationInfoWindow.cpp \
application/windows/MainWindow.cpp \
application/windows/PreferencesWindow.cpp \
application/windows/RoomListWindow.cpp \
application/windows/RosterEditWindow.cpp \
application/windows/RosterWindow.cpp \
application/windows/TemplateWindow.cpp \
@ -130,7 +133,7 @@ RSRCS =
# - if your library does not follow the standard library naming scheme,
# you need to specify the path to the library and it's name.
# (e.g. for mylib.a, specify "mylib.a" or "path/mylib.a")
LIBS = be expat interface localestub runview shared translation $(STDCPPLIBS)
LIBS = be columnlistview expat interface localestub runview shared translation $(STDCPPLIBS)
# Specify additional paths to directories following the standard libXXX.so
@ -143,7 +146,9 @@ LIBPATHS =
# Additional paths to look for system headers. These use the form
# "#include <header>". Directories that contain the files in SRCS are
# NOT auto-included here.
SYSTEM_INCLUDE_PATHS = libs/
SYSTEM_INCLUDE_PATHS = libs/ \
$(shell findpaths -e B_FIND_PATH_HEADERS_DIRECTORY private/interface)
# Additional paths paths to look for local headers. These use the form
# #include "header". Directories that contain the files in SRCS are
@ -166,7 +171,7 @@ LOCALES = en eo
# use. For example, setting DEFINES to "DEBUG=1" will cause the compiler
# option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" would pass
# "-DDEBUG" on the compiler's command line.
DEFINES :=
DEFINES =
# Specify the warning level. Either NONE (suppress all warnings),
# ALL (enable all warnings), or leave blank (enable default warnings).
@ -202,7 +207,6 @@ DRIVER_PATH =
## Include the Makefile-Engine
DEVEL_DIRECTORY := /boot/system/develop/
include $(DEVEL_DIRECTORY)/etc/makefile-engine
include Makefile.common
include Make.post
CC = g++

26
application/Maps.h Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright 2022, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef _MAPS_H
#define _MAPS_H
#include <String.h>
#include "libsupport/KeyMap.h"
class ChatCommand;
class Command;
class Contact;
class Conversation;
class User;
// Defining some commonly-used KeyMaps
typedef KeyMap<BString, bigtime_t> AccountInstances;
typedef KeyMap<BString, ChatCommand*> CommandMap;
typedef KeyMap<BString, Conversation*> ChatMap;
typedef KeyMap<BString, Contact*> RosterMap;
typedef KeyMap<BString, User*> UserMap;
#endif // _MAPS_H

View File

@ -21,7 +21,7 @@ enum {
INT_NEW_MESSAGE,
INT_NEW_MENTION,
INT_WINDOW_FOCUSED,
INT_CONV_VIEW_SELECTED,
INT_ACCOUNTS_UPDATED
};

View File

@ -13,12 +13,13 @@
#include "ProtocolLooper.h"
#include <Bitmap.h>
#include <Catalog.h>
#include <String.h>
#include "Account.h"
#include "AppMessages.h"
#include "ChatOMatic.h"
#include "ChatProtocolMessages.h"
#include "Contact.h"
#include "Conversation.h"
#include "ConversationAccountItem.h"
#include "ConversationView.h"
@ -254,6 +255,10 @@ ProtocolLooper::LoadCommands()
}
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Protocol system buffer"
void
ProtocolLooper::_InitChatView()
{

View File

@ -11,10 +11,9 @@
#include <ObjectList.h>
#include <String.h>
#include <libsupport/KeyMap.h>
#include "ChatProtocol.h"
#include "ChatCommand.h"
#include "Maps.h"
class Contact;
class Conversation;
@ -23,11 +22,6 @@ class ConversationView;
class User;
typedef KeyMap<BString, Conversation*> ChatMap;
typedef KeyMap<BString, Contact*> RosterMap;
typedef KeyMap<BString, User*> UserMap;
class ProtocolLooper : public BLooper {
public:
ProtocolLooper(ChatProtocol* protocol, int64 instance);

View File

@ -47,10 +47,18 @@ ProtocolManager::Init(BDirectory dir, BHandler* target)
if (id < 0)
continue;
// If add-on's API version fits then load accounts…
// Refuse to load add-on under some circumstances…
ChatProtocolAddOn* addOn = new ChatProtocolAddOn(id, path.Path());
if (addOn->Version() != APP_VERSION || ProtocolAddOn(addOn->Signature()) != NULL) {
if (addOn->Version() != APP_VERSION)
printf("%s not loaded, due to insufficient version (%i v %i).\n",
addOn->Signature(), addOn->Version(), APP_VERSION);
else if (ProtocolAddOn(addOn->Signature()) != NULL)
printf("%s not loaded, due to another instance already having been loaded.\n",
addOn->Signature());
delete addOn;
continue;
}
ret = true;
// If add-on has multiple protocols, also load them
@ -132,7 +140,7 @@ ProtocolManager::AddAccount(ChatProtocolAddOn* addOn, const char* account,
{
// If already active, don't double-dip!
bool active = false;
_Server()->GetActiveAccounts().ValueFor(BString(account), &active);
Server::Get()->GetActiveAccounts().ValueFor(BString(account), &active);
if (active == true)
return;
@ -157,7 +165,7 @@ ProtocolManager::AddAccount(ChatProtocolAddOn* addOn, const char* account,
fProtocolMap.AddItem(instanceId, cayap);
_Server()->AddProtocolLooper(instanceId, cayap);
Server::Get()->AddProtocolLooper(instanceId, cayap);
}
@ -181,7 +189,7 @@ ProtocolManager::DisableAccount(ProtocolSettings* settings, const char* account)
{
bool active = false;
int64 instance
= _Server()->GetActiveAccounts().ValueFor(BString(account), &active);
= Server::Get()->GetActiveAccounts().ValueFor(BString(account), &active);
if (active == false)
return;
@ -206,7 +214,7 @@ ProtocolManager::ToggleAccount(ProtocolSettings* settings, const char* account)
{
bool active = false;
int64 instance
= _Server()->GetActiveAccounts().ValueFor(BString(account), &active);
= Server::Get()->GetActiveAccounts().ValueFor(BString(account), &active);
if (active == true)
DisableAccount(settings, account);
@ -272,11 +280,3 @@ ProtocolManager::_MainWin()
{
return ((TheApp*)be_app)->GetMainWindow();
}
Server*
ProtocolManager::_Server()
{
MainWindow* win = _MainWin();
return win ? win->GetServer() : NULL;
}

View File

@ -64,7 +64,6 @@ private:
BEntry accountEntry, BHandler* target);
MainWindow* _MainWin();
Server* _Server();
AddOnMap fAddOnMap;
ProtocolMap fProtocolMap;

View File

@ -49,7 +49,7 @@ ProtocolSettings::Accounts() const
{
BObjectList<BString> list(true);
BPath path(AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature()));
BPath path = AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature());
if (path.InitCheck() != B_OK)
return list;
@ -93,7 +93,7 @@ ProtocolSettings::Load(const char* account, BMessage** settings)
status_t ret = B_ERROR;
// Find user's settings path
BPath path(AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature()));
BPath path = AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature());
if ((ret = path.InitCheck()) != B_OK)
return ret;
@ -127,7 +127,7 @@ status_t
ProtocolSettings::Save(const char* account, BMessage settings)
{
// Find user's settings path
BPath path(AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature()));
BPath path = AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature());
status_t ret;
if ((ret = path.InitCheck()) != B_OK)
@ -146,7 +146,7 @@ ProtocolSettings::Rename(const char* from, const char* to)
status_t ret = B_ERROR;
// Find user's settings path
BPath path(AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature()));
BPath path = AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature());
if ((ret = path.InitCheck()) != B_OK)
return ret;
@ -168,7 +168,7 @@ ProtocolSettings::Delete(const char* account)
status_t ret = B_ERROR;
// Find user's settings path
BPath path(AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature()));
BPath path = AccountPath(fAddOn->Signature(), fAddOn->ProtoSignature());
if ((ret = path.InitCheck()) != B_OK)
return ret;

View File

@ -28,7 +28,6 @@
#include "Account.h"
#include "AppMessages.h"
#include "AppPreferences.h"
#include "ChatOMatic.h"
#include "ChatProtocol.h"
#include "ConversationInfoWindow.h"
#include "ConversationView.h"
@ -49,6 +48,9 @@
#define B_TRANSLATION_CONTEXT "Server"
Server* Server::fInstance = NULL;
Server::Server()
:
BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE)
@ -83,6 +85,15 @@ Server::Server()
}
Server*
Server::Get()
{
if (fInstance == NULL)
fInstance = new Server();
return fInstance;
}
void
Server::Quit()
{
@ -628,7 +639,7 @@ Server::ImMessage(BMessage* msg)
// Join cached rooms
BEntry entry;
char fileName[B_FILE_NAME_LENGTH] = {'\0'};
BDirectory dir(RoomsCachePath(looper->Protocol()->GetName()));
BDirectory dir(RoomsCachePath(looper->Protocol()->GetName()).Path());
while (dir.GetNextEntry(&entry, true) == B_OK)
if (entry.GetName(fileName) == B_OK) {

View File

@ -1,6 +1,7 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _SERVER_H
@ -25,14 +26,10 @@ class RosterItem;
class ProtocolLooper;
typedef KeyMap<bigtime_t, ProtocolLooper*> ProtocolLoopers;
typedef KeyMap<BString, bigtime_t> AccountInstances;
typedef KeyMap<BString, bool> BoolMap;
class Server: public BMessageFilter, public Notifier {
public:
Server();
static Server* Get();
void Quit();
void LoginAll();
void Login(ProtocolLooper* looper);
@ -70,6 +67,9 @@ public:
BObjectList<BMessage> UserPopUpItems();
private:
typedef KeyMap<BString, bool> BoolMap;
typedef KeyMap<bigtime_t, ProtocolLooper*> ProtocolLoopers;
ProtocolLooper* _LooperFromMessage(BMessage* message);
Contact* _EnsureContact(BMessage* message);
@ -87,6 +87,8 @@ private:
void _ReplicantStatusNotify(UserStatus status);
static Server* fInstance;
ProtocolLoopers fLoopers;
AccountInstances fAccounts;
BoolMap fAccountEnabled;

View File

@ -11,10 +11,8 @@
#include <stdio.h>
#include "ChatProtocolMessages.h"
#include "MainWindow.h"
#include "NotifyMessage.h"
#include "Server.h"
#include "TheApp.h"
static StatusManager* fInstance = NULL;
@ -52,14 +50,12 @@ StatusManager::SetNickname(BString nick, int64 instance)
msg->AddString("user_name", nick);
// Send message
TheApp* theApp = reinterpret_cast<TheApp*>(be_app);
MainWindow* win = theApp->GetMainWindow();
if (instance > -1) {
msg->AddInt64("instance", instance);
win->GetServer()->SendProtocolMessage(msg);
Server::Get()->SendProtocolMessage(msg);
}
else
win->GetServer()->SendAllProtocolMessage(msg);
Server::Get()->SendAllProtocolMessage(msg);
}
@ -91,15 +87,12 @@ StatusManager::SetStatus(UserStatus status, const char* str, int64 instance)
msg->AddString("message", str);
// Send message
TheApp* theApp = reinterpret_cast<TheApp*>(be_app);
MainWindow* win = theApp->GetMainWindow();
if (instance > -1) {
msg->AddInt64("instance", instance);
win->GetServer()->SendProtocolMessage(msg);
Server::Get()->SendProtocolMessage(msg);
}
else
win->GetServer()->SendAllProtocolMessage(msg);
Server::Get()->SendAllProtocolMessage(msg);
// Notify status change
fStatus = status;

View File

@ -14,6 +14,7 @@
#include <stdio.h>
#include <AboutWindow.h>
#include <Alert.h>
#include <Catalog.h>
#include <Path.h>
@ -21,8 +22,6 @@
#include <librunview/Emoticor.h>
#include "AboutWindow.h"
#include "ChatOMatic.h"
#include "AppMessages.h"
#include "FilePanel.h"
#include "MainWindow.h"
@ -90,7 +89,7 @@ TheApp::ReadyToRun()
if (win == false) {
BString msg(B_TRANSLATE("No protocols found!\nPlease make sure %app% was installed correctly."));
msg.ReplaceAll("%app%", APP_NAME);
msg.ReplaceAll("%app%", B_TRANSLATE_SYSTEM_NAME(APP_NAME));
BAlert* alert = new BAlert("", msg.String(), B_TRANSLATE("Ouch!"));
alert->Go();
PostMessage(B_QUIT_REQUESTED);
@ -109,28 +108,21 @@ TheApp::AboutRequested()
"2009-2010 Andrea Anzani",
"2010-2015 Dario Casalinuovo",
"2009-2010 Pier Luigi Fiorini",
"2021 Jaidyn Levesque",
NULL
};
const char* authors[] = {
"Andrea Anzani",
"Dario Casalinuovo",
"Pier Luigi Fiorini",
"Jaidyn Levesque",
NULL
};
BString extraInfo(B_TRANSLATE("%app% is released under the MIT License.\n"
"Add-on and library licenses may vary.\n"
"Built: %buildDate%"));
extraInfo.ReplaceAll("%buildDate", BUILD_DATE);
extraInfo.ReplaceAll("%buildDate%", BUILD_DATE);
extraInfo.ReplaceAll("%app%", B_TRANSLATE_SYSTEM_NAME(APP_NAME));
AboutWindow* about = new AboutWindow(B_TRANSLATE_SYSTEM_NAME(APP_NAME),
holders, authors, extraInfo.String());
BAboutWindow* about = new BAboutWindow(B_TRANSLATE_SYSTEM_NAME(APP_NAME),
APP_SIGNATURE);
about->AddDescription(B_TRANSLATE("A multi-protocol chat program."));
about->AddCopyright(2021, "Jaidyn Levesque", holders);
about->AddExtraInfo(extraInfo);
about->Show();
delete about;
}

View File

@ -227,8 +227,7 @@ User::_EnsureCachePath()
{
if (fCachePath.InitCheck() == B_OK)
return;
fCachePath.SetTo(UserCachePath(fLooper->Protocol()->GetName(),
fID.String()));
fCachePath = UserCachePath(fLooper->Protocol()->GetName(), fID.String());
}

View File

@ -14,8 +14,7 @@
#include <Path.h>
#include <String.h>
#include <libsupport/KeyMap.h>
#include "Maps.h"
#include "Notifier.h"
#include "UserStatus.h"
@ -26,9 +25,6 @@ class ProtocolLooper;
class UserPopUp;
typedef KeyMap<BString, Conversation*> ChatMap;
class User : public Notifier {
public:
User(BString id, BMessenger msgn);

View File

@ -19,7 +19,6 @@
#include <kernel/fs_attr.h>
#include "ChatOMatic.h"
#include "Utils.h"
@ -151,88 +150,104 @@ AccountPath(const char* signature, const char* subsignature)
}
const char*
BPath
CachePath()
{
BPath path(SettingsPath());
if (path.InitCheck() != B_OK)
return NULL;
path.Append("Cache");
if (create_directory(path.Path(), 0755) != B_OK)
return NULL;
return path.Path();
BPath path = SettingsPath();
path.Append("Cache/");
create_directory(path.Path(), 0755);
return path;
}
const char*
BPath
AccountCachePath(const char* accountName)
{
BPath path(CachePath());
path.Append("Accounts");
if (path.InitCheck() != B_OK)
return NULL;
BPath path = CachePath();
path.Append("Accounts/");
path.Append(accountName);
if (create_directory(path.Path(), 0755) != B_OK)
return NULL;
return path.Path();
create_directory(path.Path(), 0755);
return path;
}
const char*
BPath
RoomsCachePath(const char* accountName)
{
BPath path(AccountCachePath(accountName));
if (path.InitCheck() != B_OK)
return NULL;
path.Append("Rooms");
if (create_directory(path.Path(), 0755) != B_OK)
return NULL;
return path.Path();
return RoomsCachePath(AccountCachePath(accountName));
}
const char*
BPath
RoomsCachePath(BPath accPath)
{
accPath.Append("Rooms/");
create_directory(accPath.Path(), 0755);
return accPath;
}
BPath
RoomCachePath(const char* accountName, const char* roomIdentifier)
{
BPath path(RoomsCachePath(accountName));
if (path.InitCheck() != B_OK)
return NULL;
path.Append(roomIdentifier);
return path.Path();
return RoomCachePath(AccountCachePath(accountName), roomIdentifier);
}
const char*
BPath
RoomCachePath(BPath accPath, const char* roomIdentifier)
{
BPath path = RoomsCachePath(accPath);
path.Append(roomIdentifier);
return path;
}
BPath
UserCachePath(const char* accountName, const char* userIdentifier)
{
BPath path(AccountCachePath(accountName));
if (path.InitCheck() != B_OK)
return NULL;
path.Append("Users");
if (create_directory(path.Path(), 0755) != B_OK)
return NULL;
path.Append(userIdentifier);
return path.Path();
return UserCachePath(AccountCachePath(accountName), userIdentifier);
}
const char*
BPath
UserCachePath(BPath accPath, const char* userIdentifier)
{
accPath.Append("Users/");
create_directory(accPath.Path(), 0755);
accPath.Append(userIdentifier);
return accPath;
}
BPath
ContactCachePath(const char* accountName, const char* userIdentifier)
{
BPath path(AccountCachePath(accountName));
if (path.InitCheck() != B_OK)
return NULL;
path.Append("Contacts");
return ContactCachePath(AccountCachePath(accountName), userIdentifier);
}
if (create_directory(path.Path(), 0755) != B_OK)
return NULL;
path.Append(userIdentifier);
return path.Path();
BPath
ContactCachePath(BPath accPath, const char* userIdentifier)
{
accPath.Append("Contacts/");
create_directory(accPath.Path(), 0755);
accPath.Append(userIdentifier);
return accPath;
}
BPath
AddOnCachePath(const char* signature)
{
BPath path = CachePath();
path.Append("Add-Ons/");
path.Append(signature);
create_directory(path.Path(), 0755);
return path;
}

View File

@ -14,7 +14,7 @@
#include <Resources.h>
#include "AppConstants.h"
#include "Server.h"
#include "UserStatus.h"
class BMenu;
@ -36,12 +36,17 @@ const char* SettingsPath();
const char* AccountsPath();
const char* AccountPath(const char* signature, const char* subsignature);
const char* CachePath();
const char* AccountCachePath(const char* accountName);
const char* RoomsCachePath(const char* accountName);
const char* RoomCachePath(const char* accountName, const char* roomIdentifier);
const char* UserCachePath(const char* accountName, const char* userIdentifier);
const char* ContactCachePath(const char* accountName, const char* userIdentifier);
BPath CachePath();
BPath AccountCachePath(const char* accountName);
BPath RoomsCachePath(const char* accountName);
BPath RoomsCachePath(BPath accPath);
BPath RoomCachePath(const char* accountName, const char* roomIdentifier);
BPath RoomCachePath(BPath accPath, const char* roomIdentifier);
BPath UserCachePath(const char* accountName, const char* userIdentifier);
BPath UserCachePath(BPath accPath, const char* userIdentifier);
BPath ContactCachePath(const char* accountName, const char* userIdentifier);
BPath ContactCachePath(BPath accPath, const char* userIdentifier);
BPath AddOnCachePath(const char* signature);
rgb_color TintColor(rgb_color color, int severity);
rgb_color ForegroundColor(rgb_color background);
@ -55,4 +60,3 @@ extern "C" status_t our_image(image_info& image);
#endif // _APP_UTILS_H

View File

@ -6,6 +6,8 @@
#include "AppPreferences.h"
#include <Path.h>
#include "Utils.h"
@ -44,6 +46,7 @@ AppPreferences::Load()
HideDeskbar = settings.GetBool("HideDeskbar", false);
DisableReplicant = settings.GetBool("DisableReplicant", true);
DisableQuitConfirm = settings.GetBool("DisableQuitConfirm", false);
MembershipUpdates = settings.GetBool("MembershipUpdates", true);
IgnoreEmoticons = settings.GetBool("IgnoreEmoticons", true);
HideOffline = settings.GetBool("HideOffline", false);
@ -56,6 +59,7 @@ AppPreferences::Load()
ChatViewVertSendWeight = settings.GetFloat("ChatViewVertSendWeight", 1);
MainWindowRect = settings.GetRect("MainWindowRect", BRect(0, 0, 600, 400));
RoomDirectoryRect = settings.GetRect("RoomDirectoryRect", BRect(0, 0, 630, 330));
}
@ -78,6 +82,7 @@ AppPreferences::Save()
settings.AddBool("DisableReplicant", DisableReplicant);
settings.AddBool("DisableQuitConfirm", DisableQuitConfirm);
settings.AddBool("IgnoreEmoticons", IgnoreEmoticons);
settings.AddBool("MembershipUpdates", MembershipUpdates);
settings.AddBool("HideOffline", HideOffline);
settings.AddFloat("MainWindowListWeight", MainWindowListWeight);
@ -89,6 +94,7 @@ AppPreferences::Save()
settings.AddFloat("ChatViewVertSendWeight", ChatViewVertSendWeight);
settings.AddRect("MainWindowRect", MainWindowRect);
settings.AddRect("RoomDirectoryRect", RoomDirectoryRect);
if (file.InitCheck() == B_OK)
settings.Flatten(&file);

View File

@ -1,7 +1,7 @@
/*
* Copyright 2010, Oliver Ruiz Dorantes. All rights reserved.
* Copyright 2012, Casalinuovo Dario. All rights reserved.
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* Copyright 2021-2022, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef _APP_PREFERENCES_H
@ -33,6 +33,7 @@ public:
bool DisableQuitConfirm;
bool IgnoreEmoticons;
bool MembershipUpdates;
bool HideOffline;
@ -45,6 +46,7 @@ public:
float ChatViewVertSendWeight;
BRect MainWindowRect;
BRect RoomDirectoryRect;
private:
const char* _PreferencesPath();

View File

@ -1,7 +1,7 @@
/*
* Copyright 2010, Oliver Ruiz Dorantes. All rights reserved.
* Copyright 2012, Dario Casalinuovo. All rights reserved.
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*/
@ -21,6 +21,7 @@
const uint32 kIgnoreEmoticons = 'CBhe';
const uint32 kMembershipUpdates = 'CBmu';
PreferencesChatWindow::PreferencesChatWindow()
@ -29,6 +30,9 @@ PreferencesChatWindow::PreferencesChatWindow()
BBox* chatBox = new BBox("chatBox");
chatBox->SetLabel(B_TRANSLATE("Chat settings"));
fMembershipUpdates = new BCheckBox("MembershipUpdates",
B_TRANSLATE("Show join/part messages"), new BMessage(kMembershipUpdates));
fIgnoreEmoticons = new BCheckBox("IgnoreEmoticons",
B_TRANSLATE("Ignore emoticons"), new BMessage(kIgnoreEmoticons));
fIgnoreEmoticons->SetEnabled(false); // No emoticon support currently
@ -38,6 +42,7 @@ PreferencesChatWindow::PreferencesChatWindow()
BLayoutBuilder::Group<>(chatBox, B_VERTICAL)
.SetInsets(spacing, spacing * 2, spacing, spacing)
.Add(fMembershipUpdates)
.Add(fIgnoreEmoticons)
.End();
@ -54,6 +59,8 @@ PreferencesChatWindow::AttachedToWindow()
{
fIgnoreEmoticons->SetTarget(this);
fIgnoreEmoticons->SetValue(AppPreferences::Get()->IgnoreEmoticons);
fMembershipUpdates->SetTarget(this);
fMembershipUpdates->SetValue(AppPreferences::Get()->MembershipUpdates);
}
@ -62,8 +69,10 @@ PreferencesChatWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case kIgnoreEmoticons:
AppPreferences::Get()->IgnoreEmoticons
= fIgnoreEmoticons->Value();
AppPreferences::Get()->IgnoreEmoticons = fIgnoreEmoticons->Value();
break;
case kMembershipUpdates:
AppPreferences::Get()->MembershipUpdates = fMembershipUpdates->Value();
break;
default:
BView::MessageReceived(message);

View File

@ -19,6 +19,7 @@ public:
private:
BCheckBox* fIgnoreEmoticons;
BCheckBox* fMembershipUpdates;
};
#endif // _PREFERENCES_BEHAVIOR_H

View File

@ -14,9 +14,7 @@
#include "AccountMenuItem.h"
#include "ImageCache.h"
#include "MainWindow.h"
#include "Server.h"
#include "TheApp.h"
#undef B_TRANSLATION_CONTEXT
@ -26,33 +24,23 @@
int64 AccountsMenu::fDefaultSelection = -1;
AccountsMenu::AccountsMenu(const char* name, BMessage msg, BMessage* allMsg,
Server* server)
AccountsMenu::AccountsMenu(const char* name, BMessage msg, BMessage* allMsg)
:
BPopUpMenu(name),
fAccountMessage(msg),
fAllMessage(allMsg),
fServer(server)
fAllMessage(allMsg)
{
_PopulateMenu();
SetRadioMode(true);
SetLabelFromMarked(true);
fServer->RegisterObserver(this);
}
AccountsMenu::AccountsMenu(const char* name, BMessage msg, BMessage* allMsg)
:
AccountsMenu(name, msg, allMsg,
((TheApp*)be_app)->GetMainWindow()->GetServer())
{
Server::Get()->RegisterObserver(this);
}
AccountsMenu::~AccountsMenu()
{
delete fAllMessage;
fServer->UnregisterObserver(this);
Server::Get()->UnregisterObserver(this);
}
@ -80,7 +68,7 @@ AccountsMenu::_PopulateMenu()
icon, 0, 0, false));
}
AccountInstances accounts = fServer->GetActiveAccounts();
AccountInstances accounts = Server::Get()->GetActiveAccounts();
// Add protocol item if not already in menu
for (int i = 0; i < accounts.CountItems(); i++) {
@ -98,7 +86,7 @@ AccountsMenu::_PopulateMenu()
if (FindItem(label.String()) != NULL)
continue;
ProtocolLooper* looper = fServer->GetProtocolLooper(instance);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(instance);
BBitmap* icon = _EnsureProtocolIcon(label.String(), looper);
BMessage* message = new BMessage(fAccountMessage);

View File

@ -10,13 +10,10 @@
#include "Observer.h"
class ProtocolLooper;
class Server;
class AccountsMenu : public BPopUpMenu, public Observer {
public:
AccountsMenu(const char* name, BMessage msg,
BMessage* allMsg, Server* server);
AccountsMenu(const char* name, BMessage msg,
BMessage* allMsg = NULL);
~AccountsMenu();
@ -24,7 +21,7 @@ public:
virtual void ObserveInteger(int32 what, int32 value);
void SetDefaultSelection(BMenuItem* item);
int64 GetDefaultSelection() { return fDefaultSelection; }
static int64 GetDefaultSelection() { return fDefaultSelection; }
private:
void _PopulateMenu();
@ -37,7 +34,6 @@ private:
BMessage fAccountMessage;
BMessage* fAllMessage;
static int64 fDefaultSelection;
Server* fServer;
};
#endif // _ACCOUNTS_MENU_H

View File

@ -71,7 +71,7 @@ ConversationItem::ObserveInteger(int32 what, int32 num)
case INT_NEW_MENTION:
fStatus |= kMentioned;
break;
case INT_WINDOW_FOCUSED:
case INT_CONV_VIEW_SELECTED:
fStatus = 0;
break;
}

View File

@ -15,10 +15,10 @@
#include "Conversation.h"
#include "ConversationAccountItem.h"
#include "ConversationItem.h"
#include "Flags.h"
#include "MainWindow.h"
#include "ProtocolLooper.h"
#include "Server.h"
#include "TheApp.h"
#undef B_TRANSLATION_CONTEXT
@ -167,8 +167,7 @@ ConversationListView::RemoveConversation(Conversation* chat)
void
ConversationListView::AddAccount(int64 instance)
{
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
ProtocolLooper* looper = server->GetProtocolLooper(instance);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(instance);
if (looper == NULL)
return;
AddItem(looper->GetListItem());
@ -190,7 +189,7 @@ ConversationListView::RemoveAccount(int64 instance)
}
}
if (CountItems() == 0)
((TheApp*)be_app)->GetMainWindow()->SetConversation(NULL);
((MainWindow*)Window())->SetConversation(NULL);
}
@ -215,7 +214,6 @@ ConversationListView::_ConversationPopUp()
Conversation* chat = item->GetConversation();
ProtocolLooper* looper = chat->GetProtocolLooper();
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
_AddDefaultItems(menu, chat);
BObjectList<BMessage> items = looper->Protocol()->ChatPopUpItems();
@ -302,7 +300,7 @@ ConversationListView::_BlankPopUp()
{
bool enabled = false;
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
Server* server = Server::Get();
if (server != NULL && server->GetAccounts().CountItems() > 0)
enabled = true;

View File

@ -1,6 +1,6 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@ -23,10 +23,11 @@
#include "AppMessages.h"
#include "AppPreferences.h"
#include "ChatOMatic.h"
#include "ChatProtocolMessages.h"
#include "Contact.h"
#include "Conversation.h"
#include "NotifyMessage.h"
#include "ProtocolLooper.h"
#include "ProtocolManager.h"
#include "RenderView.h"
#include "SendTextView.h"
@ -68,7 +69,15 @@ ConversationView::AttachedToWindow()
if (fSubjectTextView->Text() != fConversation->GetSubject())
fSubjectTextView->SetText(fConversation->GetSubject());
}
NotifyInteger(INT_WINDOW_FOCUSED, 0);
}
void
ConversationView::Show()
{
BView::Show();
NotifyInteger(INT_CONV_VIEW_SELECTED, 0);
fSendView->MakeFocus(true);
fSendView->Invalidate();
}
@ -125,7 +134,7 @@ ConversationView::ImMessage(BMessage* msg)
case IM_MESSAGE_RECEIVED:
{
_AppendOrEnqueueMessage(msg);
fReceiveView->ScrollToBottom();
_ScrollToBottom();
break;
}
case IM_MESSAGE_SENT:
@ -133,25 +142,12 @@ ConversationView::ImMessage(BMessage* msg)
{
_AppendOrEnqueueMessage(msg);
if (im_what == IM_MESSAGE_SENT)
fReceiveView->ScrollToBottom();
_ScrollToBottom();
break;
}
case IM_ROOM_JOINED:
{
BMessage msg;
msg.AddString("body", B_TRANSLATE("** You joined the room.\n"));
_AppendOrEnqueueMessage(&msg);
fReceiveView->ScrollToBottom();
}
case IM_ROOM_CREATED:
{
BMessage msg;
msg.AddString("body", B_TRANSLATE("** You created the room.\n"));
_AppendOrEnqueueMessage(&msg);
fReceiveView->ScrollToBottom();
}
case IM_ROOM_PARTICIPANT_JOINED:
{
if (AppPreferences::Get()->MembershipUpdates == true)
_UserMessage(B_TRANSLATE("%user% has joined the room.\n"),
B_TRANSLATE("%user% has joined the room (%body%).\n"),
msg);
@ -159,6 +155,7 @@ ConversationView::ImMessage(BMessage* msg)
}
case IM_ROOM_PARTICIPANT_LEFT:
{
if (AppPreferences::Get()->MembershipUpdates == true)
_UserMessage(B_TRANSLATE("%user% has left the room.\n"),
B_TRANSLATE("%user% has left the room (%body%).\n"),
msg);
@ -309,10 +306,10 @@ void
ConversationView::GetWeights(float* horizChat, float* horizList,
float* vertChat, float* vertSend)
{
*horizChat = fHorizSplit->ItemWeight(0);
*horizList = fHorizSplit->ItemWeight(1);
*vertChat = fVertSplit->ItemWeight(0);
*vertSend = fVertSplit->ItemWeight(1);
*horizChat = fHorizSplit->ItemWeight((int32)0);
*horizList = fHorizSplit->ItemWeight((int32)1);
*vertChat = fVertSplit->ItemWeight((int32)0);
*vertSend = fVertSplit->ItemWeight((int32)1);
}
@ -387,10 +384,27 @@ ConversationView::_InitInterface()
bool
ConversationView::_AppendOrEnqueueMessage(BMessage* msg)
{
// Fill the message with user information not provided by protocol
BString user_id = msg->FindString("user_id");
if (msg->FindString("user_id", &user_id) == B_OK) {
User* user = NULL;
if (fConversation != NULL)
user = fConversation->UserById(user_id);
if (user != NULL) {
if (msg->HasString("user_name") == false)
if (user->GetName().IsEmpty() == false)
msg->AddString("user_name", user->GetName());
msg->AddColor("user_color", user->fItemColor);
}
if (msg->HasString("user_name") == false)
msg->AddString("user_name", user_id);
}
// Fill the message with receive time if not provided
if (msg->HasInt64("when") == false)
msg->AddInt64("when", (int64)time(NULL));
// If not attached to the chat window, then re-handle this message
// If not attached to a chat window, then re-handle this message
// later [AttachedToWindow()], since you can't edit an unattached
// RenderView.
if (Window() == NULL) {
@ -423,31 +437,16 @@ ConversationView::_AppendMessage(BMessage* msg)
}
// Otherwise, it's message time!
int64 timeInt;
BString user_id;
int64 timeInt = msg->GetInt64("when", time(NULL));
BString user_name = msg->FindString("user_name");
rgb_color userColor = msg->GetColor("user_color", ui_color(B_PANEL_TEXT_COLOR));
BString body;
rgb_color userColor = ui_color(B_PANEL_TEXT_COLOR);
if (msg->FindString("body", &body) != B_OK)
return;
if (msg->FindInt64("when", &timeInt) != B_OK)
timeInt = (int64)time(NULL);
if (msg->FindString("user_id", &user_id) == B_OK) {
User* user = NULL;
if (fConversation != NULL
&& (user = fConversation->UserById(user_id)) != NULL) {
user_name = user->GetName();
userColor = user->fItemColor;
}
else if (user_name.IsEmpty() == true)
user_name = user_id;
}
if (user_name.IsEmpty() == true) {
fReceiveView->AppendGeneric(body);
fReceiveView->AppendGeneric(body, timeInt);
return;
}
@ -455,7 +454,7 @@ ConversationView::_AppendMessage(BMessage* msg)
BString meMsg = "** ";
meMsg << user_name.String() << " ";
meMsg << body.RemoveFirst("/me ");
fReceiveView->AppendGeneric(meMsg.String());
fReceiveView->AppendGeneric(meMsg.String(), timeInt);
return;
}
@ -516,6 +515,14 @@ ConversationView::_AppendMessage(BMessage* msg)
}
void
ConversationView::_ScrollToBottom()
{
if (IsHidden() == false)
fReceiveView->ScrollToBottom();
}
void
ConversationView::_EnableStartingFaces(BMessage* msg, int32 index, uint16* face,
UInt16IntMap* indices, int32* next)
@ -620,7 +627,7 @@ ConversationView::_UserMessage(const char* format, const char* bodyFormat,
BMessage newMsg;
newMsg.AddString("body", newBody);
_AppendOrEnqueueMessage(&newMsg);
fReceiveView->ScrollToBottom();
_ScrollToBottom();
}

View File

@ -1,6 +1,6 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _CHAT_VIEW_H
@ -26,14 +26,13 @@ class UserListView;
const uint32 kClearText = 'CVct';
typedef KeyMap<uint16, int32> UInt16IntMap;
class ConversationView : public BGroupView, public Observer, public Notifier {
public:
ConversationView(Conversation* chat = NULL);
virtual void AttachedToWindow();
void Show();
virtual void MessageReceived(BMessage* message);
void ImMessage(BMessage* msg);
@ -53,11 +52,15 @@ public:
float vertChat, float vertSend);
private:
typedef KeyMap<uint16, int32> UInt16IntMap;
void _InitInterface();
bool _AppendOrEnqueueMessage(BMessage* msg);
void _AppendMessage(BMessage* msg);
void _ScrollToBottom();
// Helper functions for _AppendFormattedMessage()
void _EnableStartingFaces(BMessage* msg, int32 index,
uint16* face, UInt16IntMap* indices, int32* next);

View File

@ -18,10 +18,10 @@ RenderView::RenderView(const char* name)
void
RenderView::AppendGeneric(const char* message)
RenderView::AppendGeneric(const char* message, int64 when)
{
if (BString(message).IsEmpty() == true) return;
AppendTimestamp(time(NULL));
AppendTimestamp(when);
Append(message, ui_color(B_PANEL_TEXT_COLOR), B_BOLD_FACE);
if (BString(message).EndsWith("\n") == false) Append("\n");
}

View File

@ -12,7 +12,7 @@ class RenderView : public RunView {
public:
RenderView(const char* name);
void AppendGeneric(const char* message);
void AppendGeneric(const char* message, int64 when);
void AppendUserstamp(const char* nick, rgb_color nameColor);
void AppendTimestamp(time_t time = 0);

View File

@ -29,7 +29,6 @@
#include "AppMessages.h"
#include "AppPreferences.h"
#include "ChatOMatic.h"
#include "ChatProtocolMessages.h"
#include "ReplicantMenuItem.h"
#include "Utils.h"

View File

@ -0,0 +1,35 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "RoomListRow.h"
#include <ColumnTypes.h>
RoomListRow::RoomListRow(BMessage* msg)
:
BRow(),
fMessage(new BMessage(*msg)),
fInstance(-1)
{
int64 proto = msg->FindInt64("instance");
BString id = msg->FindString("chat_id");
BString name = msg->GetString("chat_name", id);
BString desc = msg->FindString("subject");
BString category = msg->FindString("category");
int32 user_n = msg->GetInt32("user_count", -1);
SetField(new BStringField(name), kNameColumn);
SetField(new BStringField(desc), kDescColumn);
SetField(new BStringField(category), kCatColumn);
if (user_n > -1)
SetField(new BIntegerField(user_n), kUserColumn);
}
RoomListRow::~RoomListRow()
{
delete fMessage;
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef _ROOM_LIST_ROW_H
#define _ROOM_LIST_ROW_H
#include <ColumnListView.h>
enum {
kNameColumn,
kDescColumn,
kCatColumn,
kUserColumn
};
class RoomListRow : public BRow {
public:
RoomListRow(BMessage* msg);
~RoomListRow();
BMessage* Message() { return fMessage; }
int64 Instance() { return fInstance; }
private:
int64 fInstance;
BMessage* fMessage;
};
#endif // _ROOM_LIST_ROW_H

View File

@ -20,10 +20,10 @@
#include "AppMessages.h"
#include "AppPreferences.h"
#include "ChatOMatic.h"
#include "ChatProtocolMessages.h"
#include "RosterItem.h"
#include "RosterListView.h"
#include "Server.h"
#undef B_TRANSLATION_CONTEXT
@ -33,11 +33,10 @@
const uint32 kSearchContact = 'RWSC';
RosterView::RosterView(const char* title, Server* server, bigtime_t account)
RosterView::RosterView(const char* title, bigtime_t account)
:
BGroupView(title, B_VERTICAL, B_USE_DEFAULT_SPACING),
fAccount(-1),
fServer(server),
fManualItem(new BStringItem("")),
fManualStr("Select user %user%" B_UTF8_ELLIPSIS)
{
@ -122,7 +121,7 @@ RosterView::ImMessage(BMessage* msg)
|| user_id.IsEmpty() == true)
return;
Contact* contact = fServer->ContactById(user_id, instance);
Contact* contact = Server::Get()->ContactById(user_id, instance);
if (contact == NULL)
return;
@ -190,7 +189,7 @@ RosterView::ImMessage(BMessage* msg)
|| msg->FindInt64("instance", &instance) != B_OK
|| user_id.IsEmpty() == true)
return;
Contact* contact = fServer->ContactById(user_id, instance);
Contact* contact = Server::Get()->ContactById(user_id, instance);
if (contact == NULL)
return;
RosterItem* rosterItem = contact->GetRosterItem();
@ -209,7 +208,7 @@ RosterView::ImMessage(BMessage* msg)
|| user_id.IsEmpty() == true)
return;
Contact* contact = fServer->ContactById(user_id, instance);
Contact* contact = Server::Get()->ContactById(user_id, instance);
if (contact == NULL)
return;
@ -269,9 +268,9 @@ RosterView::_RosterMap()
{
RosterMap contacts;
if (fAccount < 0)
contacts = fServer->Contacts();
contacts = Server::Get()->Contacts();
else {
ProtocolLooper* looper = fServer->GetProtocolLooper(fAccount);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(fAccount);
contacts = looper->Contacts();
}
return contacts;

View File

@ -14,18 +14,17 @@
#include <GroupView.h>
#include "Server.h"
#include "Maps.h"
class BStringItem;
class BTextControl;
class RosterItem;
class RosterListView;
class Server;
class RosterView : public BGroupView {
public:
RosterView(const char* title, Server* server, bigtime_t account = -1);
RosterView(const char* title, bigtime_t account = -1);
void MessageReceived(BMessage* message);
void ImMessage(BMessage* msg);
@ -46,7 +45,6 @@ public:
private:
RosterMap _RosterMap();
Server* fServer;
RosterListView* fListView;
BTextControl* fSearchBox;
bigtime_t fAccount;

View File

@ -1,4 +1,3 @@
#include <iostream>
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
@ -10,9 +9,7 @@
#include <Window.h>
#include "AppMessages.h"
#include "MainWindow.h"
#include "Server.h"
#include "TheApp.h"
SendTextView::SendTextView(const char* name, ConversationView* convView)
@ -38,22 +35,27 @@ SendTextView::KeyDown(const char* bytes, int32 numBytes)
fCurrentIndex = 0;
fCurrentWord.SetTo("");
if ((bytes[0] == B_UP_ARROW) && (modifiers == 0)) {
for (int i = 0; i < numBytes; i++) {
if ((bytes[i] == B_UP_ARROW) && (modifiers == 0)) {
_UpHistory();
return;
}
else if ((bytes[0] == B_DOWN_ARROW) && (modifiers == 0)) {
else if ((bytes[i] == B_DOWN_ARROW) && (modifiers == 0)) {
_DownHistory();
return;
}
if ((bytes[0] == B_ENTER) && (modifiers & B_COMMAND_KEY))
if ((bytes[i] == B_ENTER)
&& ((modifiers & B_COMMAND_KEY) || (modifiers & B_SHIFT_KEY))) {
Insert("\n");
else if ((bytes[0] == B_ENTER) && (modifiers == 0)) {
return;
}
else if (bytes[i] == B_ENTER) {
_AppendHistory();
fChatView->MessageReceived(new BMessage(APP_CHAT));
return;
}
}
else
BTextView::KeyDown(bytes, numBytes);
}
@ -122,8 +124,7 @@ SendTextView::_CommandNames()
if (fCurrentIndex == 0) {
int64 instance = fChatView->GetConversation()->GetProtocolLooper()->GetInstance();
BStringList cmdNames;
CommandMap cmds =
((TheApp*)be_app)->GetMainWindow()->GetServer()->Commands(instance);
CommandMap cmds = Server::Get()->Commands(instance);
for (int i = 0; i < cmds.CountItems(); i++)
cmdNames.Add(cmds.KeyAt(i));

View File

@ -36,10 +36,9 @@ const int32 kSelectAccount = 'SVsa';
const int32 kSetNick = 'SVsn';
StatusView::StatusView(const char* name, Server* server)
StatusView::StatusView(const char* name)
:
BView(name, B_WILL_DRAW),
fServer(server),
fAccount(-1)
{
// Nick name
@ -85,7 +84,7 @@ StatusView::StatusView(const char* name, Server* server)
// Changing the account used
fAccountsMenu = new AccountsMenu("statusAccountsMenu",
BMessage(kSelectAccount), new BMessage(kSelectAccount), fServer);
BMessage(kSelectAccount), new BMessage(kSelectAccount));
fAccountsButton = new MenuButton("statusAccountsButton", "", new BMessage());
fAccountsButton->SetMenu(fAccountsMenu);
@ -195,9 +194,9 @@ StatusView::_SetToAccount()
{
int64 instance = fAccount;
if (instance == -1)
instance = fServer->GetActiveAccounts().ValueAt(0);
instance = Server::Get()->GetActiveAccounts().ValueAt(0);
ProtocolLooper* looper = fServer->GetProtocolLooper(instance);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(instance);
if (looper == NULL || looper->GetOwnContact() == NULL)
return;
Contact* contact = looper->GetOwnContact();

View File

@ -17,11 +17,10 @@ class AccountsMenu;
class BitmapView;
class EnterTextView;
class MenuButton;
class Server;
class StatusView : public BView, public Observer {
public:
StatusView(const char* name, Server* server);
StatusView(const char* name);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
@ -45,8 +44,6 @@ private:
MenuButton* fAccountsButton;
AccountsMenu* fAccountsMenu;
int64 fAccount;
Server* fServer;
};
#endif // _STATUS_VIEW_H

View File

@ -13,11 +13,9 @@
#include "AppMessages.h"
#include "ChatProtocolMessages.h"
#include "Conversation.h"
#include "MainWindow.h"
#include "ProtocolLooper.h"
#include "Role.h"
#include "Server.h"
#include "TheApp.h"
#include "User.h"
#include "UserInfoWindow.h"
#include "UserItem.h"
@ -108,8 +106,7 @@ UserListView::_UserPopUp()
Role* selected_role = fChat->GetRole(selected_user->GetId());
Server* server = ((TheApp*)be_app)->GetMainWindow()->GetServer();
BObjectList<BMessage> items = server->UserPopUpItems();
BObjectList<BMessage> items = Server::Get()->UserPopUpItems();
BObjectList<BMessage> protoItems = fChat->GetProtocolLooper()->Protocol()->UserPopUpItems();
items.AddList(&protoItems);

View File

@ -1,69 +0,0 @@
/*
* Copyright 2010-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2007-2009, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Ryan Leavengood, leavengood@gmail.com
*/
#include <Alert.h>
#include <Catalog.h>
#include <Font.h>
#include <String.h>
#include <TextView.h>
#include "AboutWindow.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "About window"
AboutWindow::AboutWindow(const char* appName, const char** holders,
const char** authors, const char* extraInfo)
{
fAppName = new BString(appName);
// Build the text to display
int32 i;
BString text(appName);
text << "\n\n";
for (i = 0; holders[i]; i++)
text << B_TRANSLATE("Copyright " B_UTF8_COPYRIGHT " ") << holders[i]
<< "\n";
text << B_TRANSLATE("\nWritten by:\n");
for (int32 i = 0; authors[i]; i++) {
text << " " << authors[i] << "\n";
}
// The extra information is optional
if (extraInfo != NULL)
text << "\n" << extraInfo << "\n";
fText = new BString(text);
}
AboutWindow::~AboutWindow()
{
delete fText;
delete fAppName;
}
void
AboutWindow::Show()
{
BAlert* alert = new BAlert(B_TRANSLATE("About" B_UTF8_ELLIPSIS),
fText->String(), B_TRANSLATE("Close"));
BTextView* view = alert->TextView();
BFont font;
view->SetStylable(true);
view->GetFont(&font);
font.SetFace(B_BOLD_FACE);
font.SetSize(font.Size() * 1.7f);
view->SetFontAndColor(0, fAppName->Length(), &font);
alert->Go();
}

View File

@ -1,24 +0,0 @@
/*
* Copyright 2010-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2007-2009, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _ABOUT_WINDOW_H
#define _ABOUT_WINDOW_H
#include <String.h>
class AboutWindow {
public:
AboutWindow(const char* appName, const char** holders,
const char** authors, const char* extraInfo = NULL);
virtual ~AboutWindow();
void Show();
private:
BString* fAppName;
BString* fText;
};
#endif // _ABOUT_WINDOW_H

View File

@ -27,9 +27,7 @@
#include "ChatProtocolMessages.h"
#include "ProtocolManager.h"
#include "ProtocolSettings.h"
#include "MainWindow.h"
#include "Server.h"
#include "TheApp.h"
#undef B_TRANSLATION_CONTEXT
@ -288,8 +286,7 @@ int64
AccountsWindow::_AccountInstance(const char* account)
{
bool found = false;
AccountInstances accs =
((TheApp*)be_app)->GetMainWindow()->GetServer()->GetAccounts();
AccountInstances accs = Server::Get()->GetAccounts();
int64 instance = accs.ValueFor(BString(account), &found);
if (found == false)

View File

@ -1,29 +1,34 @@
/*
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Copyright 2021-2022, 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
* Humdinger, humdingerb@gmail.com
*/
#include <Application.h>
#include <Alert.h>
#include <Beep.h>
#include <CardLayout.h>
#include <Catalog.h>
#include <LayoutBuilder.h>
#include <MenuBar.h>
#include <PathFinder.h>
#include <Roster.h>
#include <ScrollView.h>
#include <TranslationUtils.h>
#include <stdio.h>
#include "AccountDialog.h"
#include "AccountsWindow.h"
#include "AppMessages.h"
#include "AppPreferences.h"
#include "ChatOMatic.h"
#include "ChatProtocolAddOn.h"
#include "ChatProtocolMessages.h"
#include "ConversationItem.h"
@ -35,8 +40,10 @@
#include "ProtocolManager.h"
#include "ProtocolSettings.h"
#include "ReplicantStatusView.h"
#include "RoomListWindow.h"
#include "RosterEditWindow.h"
#include "RosterWindow.h"
#include "Server.h"
#include "StatusManager.h"
#include "StatusView.h"
#include "TemplateWindow.h"
@ -55,12 +62,10 @@ MainWindow::MainWindow()
B_TRANSLATE_SYSTEM_NAME(APP_NAME), B_TITLED_WINDOW, 0),
fWorkspaceChanged(false),
fConversation(NULL),
fRosterWindow(NULL),
fServer(NULL)
fRosterWindow(NULL)
{
// Filter messages using Server
fServer = new Server();
AddFilter(fServer);
AddFilter(Server::Get());
_InitInterface();
@ -80,7 +85,16 @@ MainWindow::Start()
MessageReceived(new BMessage(APP_SHOW_ACCOUNTS));
// Login all accounts
fServer->LoginAll();
Server::Get()->LoginAll();
}
void
MainWindow::Show()
{
if (fChatLayout->CountItems() == 0)
SetConversation(NULL);
BWindow::Show();
}
@ -98,12 +112,20 @@ MainWindow::QuitRequested()
button_index = alert->Go();
}
AppPreferences::Get()->MainWindowListWeight = fSplitView->ItemWeight(0);
AppPreferences::Get()->MainWindowChatWeight = fSplitView->ItemWeight(1);
AppPreferences::Get()->MainWindowRect = Frame();
_SaveWeights();
if (button_index == 0) {
fServer->Quit();
Server::Get()->Quit();
// ConversationViews will be removed by Server's deletion of Conversations,
// but some special views (protocol logs, the blank ConversationView)
// must be done manually
for (int i = fChatLayout->CountItems() - 1; i >= 0; i--)
fChatLayout->RemoveItem(i);
fChatLayout->RemoveSelf();
delete fChatLayout;
AppPreferences::Get()->Save();
ReplicantStatusView::RemoveReplicant();
be_app->PostMessage(B_QUIT_REQUESTED);
@ -160,7 +182,7 @@ MainWindow::MessageReceived(BMessage* message)
newMsg->AddInt32("im_what", IM_CREATE_CHAT);
fRosterWindow = new RosterWindow(B_TRANSLATE("Invite contact to "
"chat" B_UTF8_ELLIPSIS), newMsg, new BMessenger(this), fServer);
"chat" B_UTF8_ELLIPSIS), newMsg, new BMessenger(this));
fRosterWindow->Show();
break;
}
@ -170,7 +192,7 @@ MainWindow::MessageReceived(BMessage* message)
createMsg->AddInt32("im_what", IM_CREATE_ROOM);
TemplateWindow* win = new TemplateWindow(B_TRANSLATE("Create room"),
"create_room", createMsg, fServer);
"create_room", createMsg);
win->Show();
break;
}
@ -180,7 +202,7 @@ MainWindow::MessageReceived(BMessage* message)
joinMsg->AddInt32("im_what", IM_JOIN_ROOM);
TemplateWindow* win = new TemplateWindow(B_TRANSLATE("Join a room"),
"join_room", joinMsg, fServer);
"join_room", joinMsg);
win->Show();
break;
}
@ -197,15 +219,35 @@ MainWindow::MessageReceived(BMessage* message)
ProtocolLooper* plooper = fConversation->GetProtocolLooper();
BLooper* looper = (BLooper*)plooper;
fRosterWindow = new RosterWindow(B_TRANSLATE("Invite contact to "
"chat" B_UTF8_ELLIPSIS), invite, new BMessenger(looper), fServer,
"chat" B_UTF8_ELLIPSIS), invite, new BMessenger(looper),
plooper->GetInstance());
fRosterWindow->Show();
break;
}
case APP_ROOM_DIRECTORY:
{
RoomListWindow::Get()->Show();
break;
}
case APP_ROOM_SEARCH:
{
if (fConversation != NULL) {
entry_ref ref;
BEntry entry(fConversation->CachePath().Path());
if (entry.GetRef(&ref) != B_OK)
break;
BMessage msg(B_REFS_RECEIVED);
msg.AddRef("refs", &ref);
BRoster roster;
roster.Launch("application/x-vnd.Haiku.TextSearch", &msg);
}
break;
}
case APP_EDIT_ROSTER:
{
RosterEditWindow::Get(fServer)->Show();
RosterEditWindow::Get()->Show();
break;
}
case APP_MOVE_UP:
@ -255,6 +297,29 @@ MainWindow::MessageReceived(BMessage* message)
fListView->RemoveAccount(message->GetInt64("instance", -1));
break;
}
case APP_SHOW_HELP:
{
// Borrowed from HaikuArchives/Calendar's _ShowHelp()
BPathFinder pathFinder;
BStringList paths;
BPath path;
BEntry entry;
status_t error = pathFinder.FindPaths(B_FIND_PATH_DOCUMENTATION_DIRECTORY,
"packages/chat_o_matic", paths);
for (int i = 0; i < paths.CountStrings(); ++i) {
if (error == B_OK && path.SetTo(paths.StringAt(i)) == B_OK
&& path.Append("Documentation.html") == B_OK)
{
entry = path.Path();
entry_ref ref;
entry.GetRef(&ref);
be_roster->Launch(&ref);
}
}
break;
}
case IM_MESSAGE:
ImMessage(message);
break;
@ -276,7 +341,7 @@ MainWindow::ImMessage(BMessage* msg)
{
int64 instance;
if (msg->FindInt64("instance", &instance) == B_OK) {
ProtocolLooper* looper = fServer->GetProtocolLooper(instance);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(instance);
if (looper != NULL) {
Contact* contact = looper->GetOwnContact();
contact->RegisterObserver(fStatusView);
@ -311,7 +376,7 @@ MainWindow::ImMessage(BMessage* msg)
if (fRosterWindow != NULL)
fRosterWindow->PostMessage(msg);
if (RosterEditWindow::Check() == true)
RosterEditWindow::Get(fServer)->PostMessage(msg);
RosterEditWindow::Get()->PostMessage(msg);
break;
}
case IM_PROTOCOL_READY: {
@ -323,6 +388,10 @@ MainWindow::ImMessage(BMessage* msg)
fListView->AddAccount(msg->GetInt64("instance", -1));
break;
}
case IM_ROOM_DIRECTORY:
if (RoomListWindow::Check() == true)
RoomListWindow::Get()->PostMessage(msg);
break;
case IM_PROTOCOL_DISABLE:
fStatusView->MessageReceived(msg);
break;
@ -343,79 +412,52 @@ MainWindow::WorkspaceActivated(int32 workspace, bool active)
void
MainWindow::SetConversation(Conversation* chat)
{
fConversation = chat;
if (chat != NULL) {
SetConversationView(chat->GetView());
if (chat == NULL) {
SetTitle(APP_NAME);
SetConversationView(fBackupChatView);
return;
}
_EnsureConversationView(chat);
bool found = false;
BLayoutItem* item = fChatList.ValueFor(chat, &found);
if (found == false)
return;
BString title(chat->GetName());
title << "" << APP_NAME;
SetTitle(title.String());
}
else {
SetConversationView(fBackupChatView);
SetTitle(APP_NAME);
}
// Remove "Protocol" menu
BMenuItem* chatMenuItem = fMenuBar->FindItem(B_TRANSLATE("Chat"));
BMenuItem* protocolMenuItem = fMenuBar->FindItem(B_TRANSLATE("Protocol"));
if (protocolMenuItem != NULL)
fMenuBar->RemoveItem(protocolMenuItem);
// Populate new "Protocol" menu if need be
BMenu* protocolMenu = _CreateProtocolMenu();
if (protocolMenu != NULL)
fMenuBar->AddItem(protocolMenu, fMenuBar->IndexOf(chatMenuItem) + 1);
_SaveWeights();
fConversation = chat;
fChatLayout->SetVisibleItem(item);
_ApplyWeights();
}
void
MainWindow::SetConversationView(ConversationView* chatView)
MainWindow::SetConversationView(ConversationView* view)
{
// Save split weights
float weightChat = fRightView->ItemWeight((int32)0);
float weightSend = fRightView->ItemWeight((int32)1);
float horizChat, horizList, vertChat, vertSend;
fChatView->GetWeights(&horizChat, &horizList, &vertChat, &vertSend);
int32 index = fChatLayout->IndexOfView(view);
if (index < 0) {
BLayoutItem* item = fChatLayout->AddView(view);
fChatLayout->SetVisibleItem(item);
} else
fChatLayout->SetVisibleItem(index);
fRightView->RemoveChild(fRightView->FindView("chatView"));
fChatView = chatView;
fRightView->AddChild(fChatView, 9);
// Remove "Protocol" menu
BMenuItem* chatMenuItem = fMenuBar->FindItem("Protocol");
BMenu* chatMenu;
if (chatMenuItem != NULL && (chatMenu = chatMenuItem->Submenu()) != NULL)
fMenuBar->RemoveItem(chatMenu);
// Add and populate "Protocol" menu, if appropriate
if (fConversation != NULL) {
ProtocolLooper* looper = fConversation->GetProtocolLooper();
BObjectList<BMessage> menuItems = looper->Protocol()->MenuBarItems();
for (int i = 0; i < menuItems.CountItems(); i++) {
BMessage* itemMsg = menuItems.ItemAt(i);
BMessage* msg = new BMessage(*itemMsg);
BMessage toSend;
msg->FindMessage("_msg", &toSend);
toSend.AddString("chat_id", fConversation->GetId());
toSend.AddInt64("instance", looper->GetInstance());
msg->ReplaceMessage("_msg", &toSend);
BMenuItem* item = new BMenuItem(msg);
if (item == NULL)
continue;
if (msg->GetBool("x_to_protocol", true) == true)
item->SetTarget(looper);
else
item->SetTarget(this);
chatMenu->AddItem(item);
}
}
// Apply saved weights
fSplitView->SetItemWeight(0, AppPreferences::Get()->MainWindowListWeight, true);
fSplitView->SetItemWeight(1, AppPreferences::Get()->MainWindowChatWeight, true);
fChatView->SetWeights(horizChat, horizList, vertChat, vertSend);
if (weightChat * weightSend != 0) {
fRightView->SetItemWeight(0, weightChat, true);
fRightView->SetItemWeight(1, weightSend, true);
}
// Save ChatView weights to settings
AppPreferences::Get()->ChatViewHorizChatWeight = horizChat;
AppPreferences::Get()->ChatViewHorizListWeight = horizList;
AppPreferences::Get()->ChatViewVertChatWeight = vertChat;
AppPreferences::Get()->ChatViewVertSendWeight = vertSend;
_ApplyWeights();
}
@ -424,6 +466,13 @@ MainWindow::RemoveConversation(Conversation* chat)
{
SetConversation(NULL);
bool found = false;
BLayoutItem* item = fChatList.ValueFor(chat, &found);
if (item != NULL)
item->RemoveSelf();
if (found == true)
fChatList.RemoveItemFor(chat);
int32 index = fListView->IndexOf(chat->GetListItem());
if (index > 0)
index--;
@ -448,24 +497,15 @@ MainWindow::_InitInterface()
{
// Left side of window, Roomlist + Status
fListView = new ConversationListView("roomList");
fStatusView = new StatusView("statusView", fServer);
fStatusView = new StatusView("statusView");
fSplitView = new BSplitView(B_HORIZONTAL, 0);
BScrollView* listScroll = new BScrollView("roomListScroll", fListView,
true, false, B_NO_BORDER);
// Right-side of window, Chat + Textbox
fRightView = new BSplitView(B_VERTICAL, 0);
fChatLayout = new BCardLayout();
fBackupChatView = new ConversationView();
fChatView = fBackupChatView;
// Load weights from settings
float horizChat, horizList, vertChat, vertSend;
horizChat = AppPreferences::Get()->ChatViewHorizChatWeight;
horizList = AppPreferences::Get()->ChatViewHorizListWeight;
vertChat = AppPreferences::Get()->ChatViewVertChatWeight;
vertSend = AppPreferences::Get()->ChatViewVertSendWeight;
fChatView->SetWeights(horizChat, horizList, vertChat, vertSend);
BLayoutBuilder::Group<>(this, B_VERTICAL)
.Add((fMenuBar = _CreateMenuBar()))
@ -476,12 +516,11 @@ MainWindow::_InitInterface()
.Add(listScroll, 1)
.Add(fStatusView)
.End()
.Add(fRightView, 5)
.Add(fChatLayout, 5)
.End()
.End()
.End();
SetConversation(NULL);
_ToggleMenuItems();
}
@ -495,6 +534,8 @@ MainWindow::_CreateMenuBar()
BMenu* programMenu = new BMenu(B_TRANSLATE("Program"));
programMenu->AddItem(new BMenuItem(B_TRANSLATE("About" B_UTF8_ELLIPSIS),
new BMessage(B_ABOUT_REQUESTED)));
programMenu->AddItem(new BMenuItem(B_TRANSLATE("Help" B_UTF8_ELLIPSIS),
new BMessage(APP_SHOW_HELP)));
programMenu->AddItem(new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS),
new BMessage(APP_SHOW_SETTINGS), ',', B_COMMAND_KEY));
programMenu->AddItem(new BSeparatorItem());
@ -506,12 +547,17 @@ MainWindow::_CreateMenuBar()
BMenu* chatMenu = new BMenu(B_TRANSLATE("Chat"));
chatMenu->AddItem(new BMenuItem(B_TRANSLATE("Join room" B_UTF8_ELLIPSIS),
new BMessage(APP_JOIN_ROOM), 'J', B_COMMAND_KEY));
chatMenu->AddItem(new BMenuItem(B_TRANSLATE("Room directory" B_UTF8_ELLIPSIS),
new BMessage(APP_ROOM_DIRECTORY)));
chatMenu->SetTargetForItems(this);
chatMenu->AddSeparatorItem();
chatMenu->AddItem(new BMenuItem(B_TRANSLATE("New room" B_UTF8_ELLIPSIS),
new BMessage(APP_NEW_ROOM), 'N', B_COMMAND_KEY));
chatMenu->AddItem(new BMenuItem(B_TRANSLATE("New chat" B_UTF8_ELLIPSIS),
new BMessage(APP_NEW_CHAT), 'M', B_COMMAND_KEY));
chatMenu->SetTargetForItems(this);
chatMenu->AddSeparatorItem();
chatMenu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
new BMessage(APP_ROOM_SEARCH), 'F', B_COMMAND_KEY));
// Roster
BMenu* rosterMenu = new BMenu(B_TRANSLATE("Roster"));
@ -572,6 +618,40 @@ MainWindow::_RefreshAccountsMenu()
}
BMenu*
MainWindow::_CreateProtocolMenu()
{
if (fConversation == NULL)
return NULL;
ProtocolLooper* looper = fConversation->GetProtocolLooper();
BObjectList<BMessage> menuItems = looper->Protocol()->MenuBarItems();
if (menuItems.CountItems() == 0)
return NULL;
BMenu* menu = new BMenu(B_TRANSLATE("Protocol"));
for (int i = 0; i < menuItems.CountItems(); i++) {
BMessage* itemMsg = menuItems.ItemAt(i);
BMessage* msg = new BMessage(*itemMsg);
BMessage toSend;
msg->FindMessage("_msg", &toSend);
toSend.AddString("chat_id", fConversation->GetId());
toSend.AddInt64("instance", looper->GetInstance());
msg->ReplaceMessage("_msg", &toSend);
BMenuItem* item = new BMenuItem(msg);
if (item == NULL)
continue;
if (msg->GetBool("x_to_protocol", true) == true)
item->SetTarget(looper);
else
item->SetTarget(this);
menu->AddItem(item);
}
return menu;
}
void
MainWindow::_ToggleMenuItems()
{
@ -583,7 +663,8 @@ MainWindow::_ToggleMenuItems()
if (chatMenu == NULL || rosterMenu == NULL)
return;
bool enabled = (fServer != NULL && fServer->GetAccounts().CountItems() > 0);
Server* server = Server::Get();
bool enabled = (server != NULL && server->GetAccounts().CountItems() > 0);
for (int i = 0; i < chatMenu->CountItems(); i++)
chatMenu->ItemAt(i)->SetEnabled(enabled);
@ -603,13 +684,19 @@ MainWindow::_ToggleMenuItems()
ConversationItem*
MainWindow::_EnsureConversationItem(BMessage* msg)
{
ChatMap chats = fServer->Conversations();
BString chat_id = msg->FindString("chat_id");
Conversation* chat = fServer->ConversationById(chat_id, msg->FindInt64("instance"));
ConversationItem* item = chat->GetListItem();
int64 conversation_id = msg->FindInt64("instance");
Conversation* chat = Server::Get()->ConversationById(chat_id, conversation_id);
if (chat == NULL) {
printf("error: Conversation %" B_PRId64 " in '%s' not found!\n", conversation_id, chat_id);
return NULL;
}
if (chat != NULL) {
_EnsureConversationView(chat);
// Add conversation item if necessary, return it
ConversationItem* item;
if (chat != NULL && (item = chat->GetListItem()) != NULL) {
if (fListView->HasItem(item))
fListView->InvalidateItem(fListView->IndexOf(item));
else if (item != NULL) {
@ -625,6 +712,62 @@ MainWindow::_EnsureConversationItem(BMessage* msg)
}
void
MainWindow::_EnsureConversationView(Conversation* chat)
{
// Add conversation's view if necessary
bool added = false;
fChatList.ValueFor(chat, &added);
if (added == false) {
BLayoutItem* item = fChatLayout->AddView(chat->GetView());
if (item != NULL)
fChatList.AddItem(chat, item);
}
}
void
MainWindow::_ApplyWeights()
{
// Chat-list and chat-view splitter
fSplitView->SetItemWeight(0, AppPreferences::Get()->MainWindowListWeight, true);
fSplitView->SetItemWeight(1, AppPreferences::Get()->MainWindowChatWeight, true);
// Chat-view's weights
float horizChat, horizList, vertChat, vertSend;
horizChat = AppPreferences::Get()->ChatViewHorizChatWeight;
horizList = AppPreferences::Get()->ChatViewHorizListWeight;
vertChat = AppPreferences::Get()->ChatViewVertChatWeight;
vertSend = AppPreferences::Get()->ChatViewVertSendWeight;
BLayoutItem* item = fChatLayout->VisibleItem();
if (item != NULL)
((ConversationView*)item->View())->SetWeights(horizChat, horizList, vertChat, vertSend);
}
void
MainWindow::_SaveWeights()
{
// Chat-list and chat-view splitter
AppPreferences::Get()->MainWindowListWeight = fSplitView->ItemWeight((int32)0);
AppPreferences::Get()->MainWindowChatWeight = fSplitView->ItemWeight((int32)1);
// ChatView's weights
float horizChat, horizList, vertChat, vertSend;
BLayoutItem* item = fChatLayout->VisibleItem();
if (item == NULL)
return;
((ConversationView*)item->View())->GetWeights(&horizChat, &horizList, &vertChat, &vertSend);
AppPreferences::Get()->ChatViewHorizChatWeight = horizChat;
AppPreferences::Get()->ChatViewHorizListWeight = horizList;
AppPreferences::Get()->ChatViewVertChatWeight = vertChat;
AppPreferences::Get()->ChatViewVertSendWeight = vertSend;
}
bool
MainWindow::_PopulateWithAccounts(BMenu* menu, ProtocolSettings* settings)
{
@ -639,7 +782,7 @@ MainWindow::_PopulateWithAccounts(BMenu* menu, ProtocolSettings* settings)
BString toggleLabel = B_TRANSLATE("Enable");
bool isActive = false;
int64 instance = fServer->GetActiveAccounts().ValueFor(*account, &isActive);
int64 instance = Server::Get()->GetActiveAccounts().ValueFor(*account, &isActive);
if (isActive == true)
toggleLabel = B_TRANSLATE("Disable");

View File

@ -1,7 +1,7 @@
/*
* Copyright 2021, Jaidyn Levesque. All rights reserved.
* Copyright 2009-2011, Andrea Anzani. All rights reserved.
* Copyright 2009-2011, Pier Luigi Fiorini. All rights reserved.
* Copyright 2021-2022, Jaidyn Levesque. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _MAIN_WINDOW_H
@ -9,8 +9,10 @@
#include <Window.h>
#include "Server.h"
#include "libsupport/KeyMap.h"
class BCardLayout;
class BLayoutItem;
class BMenu;
class BSplitView;
class BTextView;
@ -21,9 +23,7 @@ class ConversationListView;
class ConversationView;
class ProtocolSettings;
class RosterItem;
class RosterEditWindow;
class RosterWindow;
class Server;
class StatusView;
@ -32,6 +32,7 @@ public:
MainWindow();
void Start();
void Show();
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* message);
@ -41,43 +42,45 @@ public:
bool active);
void SetConversation(Conversation* chat);
void SetConversationView(ConversationView* chatView);
void SetConversationView(ConversationView* view);
void RemoveConversation(Conversation* chat);
void SortConversation(Conversation* chat);
Server* GetServer() const { return fServer; }
private:
void _InitInterface();
BMenuBar* _CreateMenuBar();
BMenu* _CreateAccountsMenu();
void _RefreshAccountsMenu();
BMenu* _CreateProtocolMenu();
void _ToggleMenuItems();
ConversationItem*
_EnsureConversationItem(BMessage* msg);
void _EnsureConversationView(Conversation* chat);
void _ApplyWeights();
void _SaveWeights();
bool _PopulateWithAccounts(BMenu* menu,
ProtocolSettings* settings);
void _ReplaceMenu(const char* name, BMenu* newMenu);
Server* fServer;
RosterWindow* fRosterWindow;
RosterEditWindow* fRosterEditWindow;
bool fWorkspaceChanged;
BMenuBar* fMenuBar;
KeyMap<Conversation*, BLayoutItem*> fChatList;
// Left panel, chat list
ConversationListView* fListView;
StatusView* fStatusView;
BSplitView* fSplitView;
// Right panel, chat
BSplitView* fRightView;
BCardLayout* fChatLayout;
Conversation* fConversation;
ConversationView* fChatView;
ConversationView* fBackupChatView;
};

View File

@ -0,0 +1,207 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "RoomListWindow.h"
#include <Button.h>
#include <Catalog.h>
#include <ColumnListView.h>
#include <ColumnTypes.h>
#include <LayoutBuilder.h>
#include <StringList.h>
#include "AccountsMenu.h"
#include "AppPreferences.h"
#include "ChatProtocolMessages.h"
#include "RoomListRow.h"
#include "Server.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Room directory"
const uint32 kSelectAcc = 'rlse';
const uint32 kSelectAll = 'rlsa';
const uint32 kJoinRoom = 'join';
RoomListWindow* RoomListWindow::fInstance = NULL;
RoomListWindow::RoomListWindow()
:
BWindow(AppPreferences::Get()->RoomDirectoryRect,
B_TRANSLATE("Room directory"), B_FLOATING_WINDOW,
B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
fAccount(-1)
{
_InitInterface();
CenterOnScreen();
BMessage* request = new BMessage(IM_MESSAGE);
request->AddInt32("im_what", IM_GET_ROOM_DIRECTORY);
Server::Get()->SendAllProtocolMessage(request);
}
RoomListWindow::~RoomListWindow()
{
fInstance = NULL;
AppPreferences::Get()->RoomDirectoryRect = Bounds();
_EmptyList();
for (int i = 0; i < fRows.CountItems(); i++) {
BObjectList<RoomListRow>* list = fRows.ValueAt(i);
if (list != NULL)
delete list;
}
}
RoomListWindow*
RoomListWindow::Get()
{
if (fInstance == NULL)
fInstance = new RoomListWindow();
return fInstance;
}
bool
RoomListWindow::Check()
{
return (fInstance != NULL);
}
void
RoomListWindow::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case IM_MESSAGE:
{
if (msg->GetInt32("im_what", -1) != IM_ROOM_DIRECTORY)
break;
int64 instance;
BString id;
if (msg->FindInt64("instance", &instance) == B_OK
&& msg->FindString("chat_id", &id) == B_OK) {
RoomListRow* row = new RoomListRow(msg);
bool fnd = false;
BObjectList<RoomListRow>* list = fRows.ValueFor(instance, &fnd);
if (fnd == false || list == NULL) {
list = new BObjectList<RoomListRow>(20, true);
fRows.AddItem(instance, list);
}
list->AddItem(row);
if (fAccount == -1 || instance == fAccount)
fListView->AddRow(row);
}
break;
}
case kSelectAll:
{
_EmptyList();
for (int i = 0; i < fRows.CountItems(); i++) {
BObjectList<RoomListRow>* list = fRows.ValueAt(i);
if (list != NULL)
for (int j = 0; j < list->CountItems(); j++) {
RoomListRow* row = list->ItemAt(j);
if (row != NULL)
fListView->AddRow(row);
}
}
fAccount = -1;
break;
}
case kSelectAcc:
{
msg->PrintToStream();
int64 instance;
if (msg->FindInt64("instance", &instance) == B_OK) {
bool fnd = false;
BObjectList<RoomListRow>* list = fRows.ValueFor(instance, &fnd);
if (fnd == false || list == NULL)
break;
_EmptyList();
for (int i = 0; i < list->CountItems(); i++) {
RoomListRow* row = list->ItemAt(i);
if (row != NULL)
fListView->AddRow(row);
}
fAccount = instance;
}
break;
}
case kJoinRoom:
{
RoomListRow* row =
(RoomListRow*)fListView->CurrentSelection();
if (row != NULL) {
BMessage* joinMsg = row->Message();
joinMsg->ReplaceInt32("im_what", IM_JOIN_ROOM);
Server::Get()->SendProtocolMessage(joinMsg);
Quit();
}
break;
}
default:
BWindow::MessageReceived(msg);
}
}
void
RoomListWindow::_InitInterface()
{
float size = BFont().Size();
BStringColumn* name = new BStringColumn(B_TRANSLATE("Name"), 130, 50, 300,
B_TRUNCATE_END);
BStringColumn* desc = new BStringColumn(B_TRANSLATE("Description"), 270,
50, 5000, B_TRUNCATE_END);
BStringColumn* category = new BStringColumn("Category", 90, 50, 300,
B_TRUNCATE_END);
BIntegerColumn* users = new BIntegerColumn("Users", 70, 10, 300);
fListView = new BColumnListView("roomList", B_NAVIGABLE, B_PLAIN_BORDER);
fListView->SetInvocationMessage(new BMessage(kJoinRoom));
fListView->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fListView->AddColumn(name, kNameColumn);
fListView->AddColumn(desc, kDescColumn);
fListView->AddColumn(category, kCatColumn);
fListView->AddColumn(users, kUserColumn);
AccountsMenu* accsMenu = new AccountsMenu("accounts", BMessage(kSelectAcc),
new BMessage(kSelectAll));
BMenuField* accsField = new BMenuField(NULL, accsMenu);
fJoinButton = new BButton("joinRoom", B_TRANSLATE("Join"),
new BMessage(kJoinRoom));
BLayoutBuilder::Group<>(this, B_VERTICAL)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(fListView)
.AddGroup(B_HORIZONTAL)
.Add(accsField)
.AddGlue()
.Add(fJoinButton)
.End()
.End();
}
void
RoomListWindow::_EmptyList()
{
BRow* row = fListView->RowAt((int32)0, NULL);
while (row != NULL) {
fListView->RemoveRow(row);
row = fListView->RowAt((int32)0, NULL);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef _ROOM_LIST_WINDOW_H
#define _ROOM_LIST_WINDOW_H
#include <ObjectList.h>
#include <Window.h>
#include <libsupport/KeyMap.h>
class BButton;
class BColumnListView;
class RoomListRow;
typedef KeyMap<int64, BObjectList<RoomListRow>*> RowMap;
class RoomListWindow : public BWindow {
public:
RoomListWindow();
~RoomListWindow();
static RoomListWindow* Get();
static bool Check();
virtual void MessageReceived(BMessage* msg);
private:
void _InitInterface();
void _EmptyList();
BButton* fJoinButton;
BColumnListView* fListView;
RowMap fRows;
int64 fAccount;
static RoomListWindow* fInstance;
};
#endif // _ROOM_LIST_WINDOW_H

View File

@ -24,9 +24,12 @@
#include "AppMessages.h"
#include "AppPreferences.h"
#include "ChatProtocolMessages.h"
#include "Maps.h"
#include "ProtocolLooper.h"
#include "RosterItem.h"
#include "RosterListView.h"
#include "RosterView.h"
#include "Server.h"
#include "TemplateWindow.h"
@ -47,14 +50,13 @@ const char* kEditTitle = B_TRANSLATE("Editing contact");
RosterEditWindow* RosterEditWindow::fInstance = NULL;
RosterEditWindow::RosterEditWindow(Server* server)
RosterEditWindow::RosterEditWindow()
:
BWindow(BRect(0, 0, 300, 400), B_TRANSLATE("Roster"), B_FLOATING_WINDOW,
B_AUTO_UPDATE_SIZE_LIMITS),
fServer(server),
fEditingWindow(NULL)
{
fRosterView = new RosterView("buddyView", server);
fRosterView = new RosterView("buddyView");
fRosterView->SetInvocationMessage(new BMessage(kEditMember));
fRosterView->SetManualString(BString("Add %user% as contact"
B_UTF8_ELLIPSIS));
@ -104,10 +106,10 @@ RosterEditWindow::~RosterEditWindow()
RosterEditWindow*
RosterEditWindow::Get(Server* server)
RosterEditWindow::Get()
{
if (fInstance == NULL) {
fInstance = new RosterEditWindow(server);
fInstance = new RosterEditWindow();
}
return fInstance;
}
@ -172,7 +174,7 @@ RosterEditWindow::MessageReceived(BMessage* message)
fEditingWindow =
new TemplateWindow(title, "roster",
edit, fServer, instance);
edit, instance);
fEditingWindow->Show();
if (ritem == NULL) {
@ -188,7 +190,7 @@ RosterEditWindow::MessageReceived(BMessage* message)
add->AddInt32("im_what", IM_ROSTER_ADD_CONTACT);
TemplateWindow* win =
new TemplateWindow(B_TRANSLATE(kAddTitle), "roster",
add, fServer);
add);
win->Show();
break;
}
@ -216,7 +218,7 @@ RosterEditWindow::MessageReceived(BMessage* message)
}
case kSelAccount:
{
AccountInstances accounts = fServer->GetActiveAccounts();
AccountInstances accounts = Server::Get()->GetActiveAccounts();
int index = message->FindInt32("index") - 1;
if (index < 0 || index > (accounts.CountItems() - 1))

View File

@ -14,8 +14,6 @@
#include <Window.h>
#include "Server.h"
class BMenuField;
class RosterItem;
class RosterView;
@ -26,9 +24,9 @@ class TemplateWindow;
the server with contact info, once a contact is selected. */
class RosterEditWindow : public BWindow {
public:
RosterEditWindow(Server* server);
RosterEditWindow();
~RosterEditWindow();
static RosterEditWindow* Get(Server* server);
static RosterEditWindow* Get();
static bool Check();
void MessageReceived(BMessage* message);
@ -40,7 +38,6 @@ private:
BString fEditingUser;
TemplateWindow* fEditingWindow;
Server* fServer;
RosterView* fRosterView;
static RosterEditWindow* fInstance;

View File

@ -25,6 +25,7 @@
#include "RosterItem.h"
#include "RosterListView.h"
#include "RosterView.h"
#include "Server.h"
const uint32 kSendMessage = 'RWSM';
@ -33,20 +34,19 @@ const uint32 kSelNoAccount = 'RWNA';
RosterWindow::RosterWindow(const char* title, BMessage* selectMsg,
BMessenger* messenger, Server* server, bigtime_t instance)
BMessenger* messenger, bigtime_t instance)
:
BWindow(BRect(0, 0, 300, 400), title, B_FLOATING_WINDOW,
B_AUTO_UPDATE_SIZE_LIMITS),
fTarget(messenger),
fMessage(selectMsg),
fServer(server)
fMessage(selectMsg)
{
fRosterView = new RosterView("buddyView", server, instance),
fRosterView = new RosterView("buddyView", instance);
fRosterView->SetInvocationMessage(new BMessage(kSendMessage));
fOkButton = new BButton("OK", new BMessage(kSendMessage));
AccountInstances accounts = fServer->GetActiveAccounts();
AccountInstances accounts = Server::Get()->GetActiveAccounts();
// If a specific instance is given, disallow selecting other accounts
// In fact, don't even bother populating with them
@ -119,7 +119,7 @@ RosterWindow::MessageReceived(BMessage* message)
}
case kSelAccount:
{
AccountInstances accounts = fServer->GetActiveAccounts();
AccountInstances accounts = Server::Get()->GetActiveAccounts();
int index = message->FindInt32("index") - 1;
if (index < 0 || index > (accounts.CountItems() - 1))

View File

@ -14,8 +14,6 @@
#include <Window.h>
#include "Server.h"
class BMenuField;
class RosterItem;
class RosterView;
@ -26,7 +24,7 @@ class RosterView;
class RosterWindow : public BWindow {
public:
RosterWindow(const char* title, BMessage* selectMsg, BMessenger* messenger,
Server* server, bigtime_t instance = -1);
bigtime_t instance = -1);
void MessageReceived(BMessage* message);
@ -36,8 +34,6 @@ private:
BButton* fOkButton;
BMenuField* fAccountField;
Server* fServer;
RosterView* fRosterView;
BMessenger* fTarget;
BMessage* fMessage;

View File

@ -20,6 +20,7 @@
#include "AccountsMenu.h"
#include "ChatProtocolMessages.h"
#include "Server.h"
#include "TemplateView.h"
@ -33,11 +34,10 @@ const uint32 kAccSelected = 'JWas';
TemplateWindow::TemplateWindow(const char* title, const char* templateType,
BMessage* msg, Server* server, bigtime_t instance)
BMessage* msg, 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),
fSelectedAcc(instance),
fTemplate(NULL),
fTemplateType(templateType),
@ -51,11 +51,10 @@ TemplateWindow::TemplateWindow(const char* title, const char* templateType,
TemplateWindow::TemplateWindow(const char* title, ProtocolTemplate* temp,
BMessage* msg, Server* server, bigtime_t instance)
BMessage* msg, 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),
fSelectedAcc(-1),
fTemplate(temp),
fMessage(msg)
@ -99,7 +98,7 @@ TemplateWindow::MessageReceived(BMessage* msg)
break;
}
ProtocolLooper* looper = fServer->GetProtocolLooper(fSelectedAcc);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(fSelectedAcc);
if (looper == NULL)
break;
looper->PostMessage(settings);
@ -131,7 +130,7 @@ void
TemplateWindow::_InitInterface(bigtime_t instance)
{
fTemplateView = new TemplateView("template");
AccountInstances accounts = fServer->GetActiveAccounts();
AccountInstances accounts = Server::Get()->GetActiveAccounts();
if (instance > -1) {
BMenu* accountMenu = new BMenu("accountMenu");
@ -185,7 +184,7 @@ TemplateWindow::_LoadTemplate()
if (fTemplateType.IsEmpty() == true)
return;
ProtocolLooper* looper = fServer->GetProtocolLooper(fSelectedAcc);
ProtocolLooper* looper = Server::Get()->GetProtocolLooper(fSelectedAcc);
if (looper == NULL)
return;

View File

@ -10,7 +10,6 @@
#include <Window.h>
#include "ProtocolTemplate.h"
#include "Server.h"
class BAlert;
class BMenu;
@ -26,12 +25,12 @@ public:
* via ChatProtocol::SettingsTemplate() */
TemplateWindow(const char* title,
const char* templateType, BMessage* msg,
Server* server, bigtime_t instance = -1);
bigtime_t instance = -1);
/*! Use only the given template. */
TemplateWindow(const char* title,
ProtocolTemplate* temp, BMessage* msg,
Server* server, bigtime_t instance = -1);
bigtime_t instance = -1);
virtual void MessageReceived(BMessage* msg);
@ -41,7 +40,6 @@ private:
void _InitInterface(bigtime_t instance);
void _LoadTemplate();
Server* fServer;
int64 fSelectedAcc;
BMenuField* fMenuField;

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,19 +1,16 @@
resource vector_icon array {
$"6E6369660E05000200060237E670B8880E39469D39AE144A52234B0D2500C6D7"
$"F5FF6B94DD03EC6666020006023B2B47BB18653D0FA53D225148297046CA1900"
$"FFEC4BFFF0A506020006023B3049396B0ABA90833C646E4A101543299500FFFF"
$"FFFFFFF289020006023C71E33A0C78BA15E43C7D2149055549455700E3EDFFFF"
$"9EC2FF03FFACAC020006023A1DA6393F04BBB5BC3C6B074AEA3648091100F99B"
$"05FFFCB23D03003CB00200060230B31E3A09B9BB024238A12F4BAB534AFF0B00"
$"A3043CFFFFDCE603CD4D4D030D296402000602BD498B3E1159BF219BBE7D2F4B"
$"E71F4AB31300C13E3EFFE27A7A0401740D0A06322E323E42464C3C4C2C3D260A"
$"04322E323E424642350A04322E42354C2C3D260A04423542464C3C4C2C0A0338"
$"423C4D3C440A08335C395C41544F5C555C6051594D3E510A0422422252325A32"
$"490A04224232493C402D3A0A043249325A3C503C400A043E42C35DC27AC751BE"
$"F3493A0A04C35DC27A4E495840C751BEF30A05424BC08BC7B74E5A4E49C35DC2"
$"7A0A053E423E52C08BC7B7424BC35DC27A100A0D0105000A0001003010340117"
$"8400040A0101012010340A0801032010340A0B01042020210A010107000A0001"
$"00302C3401178400040A02010B000A0A010C000A090103202C340A06010A000A"
$"0C0109000A0001001001178400040A030101000A040102000A07010300"
$"6E636966040500020106033D835C3C19B2BA8B0B3C20794769624A510E00FFFF"
$"FFB4FFE405FFFFA405020106023D835C3C19B2BA8B0B3C20794769624A510E00"
$"FFE405FFFFA405020106033D429E3C5148BB5ADA3C5C1B4A23AA46EC1800FFFF"
$"FFB47FE583FF04B10C0902093F404644383C273F2E3A244122482245224D2755"
$"245126572256245725582A5828592C5A315C2F5B345D3C5E385E425E4957475C"
$"4D4E02043F4044433D3FBC95BE953B3D333D3D493844424E4A5148534A4A0403"
$"3B2F4E2F4E2D4E2A4D2F532D522F530003334F334F364F345337533755325636"
$"59325602085645C8FEC3AFCA30C0E55E3A5E405EBA4D52B58359B71B4AB38537"
$"B51D3EB31FBA16B69933353130363E4A4641444D475346C657C27B554BCB01C6"
$"35584F5A4F080239BA0539BA6B0802BD1CBC1DBD2FBCA908023E37423904032E"
$"4535473C4739473F4540070A000100123FFFFE2FDACEAFDACE3FFFFE3AB6A5B8"
$"4CC101178400040A010100000A020101000A000202031001178210040A000104"
$"1001178400040A030104000A000407050608100117821004"
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,25 @@
resource vector_icon {
$"6E63696601050003020E2040204020C400206020C840216023602260B54C6026"
$"CC3C26CC7926CC05B491CBF1B548CBFBB449CBEDB3BBCBE7B402CBEAB3BBC7DA"
$"B3BB40B3BBC3CDB3BBBBB3B3BBB399B3BBB7A6B402B395B491B38EB449B392B5"
$"48B38426B34426B37A26B3062320B54C2022202020212020B740204020BB8020"
$"40204020402040020E5AB3445AB3445AB37ACAF2B38ECA37B384CB38B392CBC5"
$"B399CB7EB395CBC5B7A6CBC540CBC5BBB3CBC5C3CDCBC5CBE7CBC5C7DACB7ECB"
$"EACAF2CBF1CB38CBEDCA37CBFB5ACC3C5ACC055ACC795D60CA33605E6060605F"
$"6060C840604060C40060BB80602060B7405F205D205E20CA33205AB3445AB306"
$"5AB3445AB3445AB3445AB3440222BC52BB80BC52BB80BBF3BBB9BB7937BB8DBC"
$"04BB35BC93BB1ABBD5BB1ABC7BBB1ABBA2BB1ABB3CBB1ABB6FBAB9BB3CB9F9BB"
$"3CBA59BB3CB998BB3CB8D8BB3CB938BB3CB8D8BCC3B8D8BFD1B8D8BE4AB8D844"
$"B8D8C466B8D8C2DFB943C466BA1BC466B9AFC466BA86C46635C466BAF2C46635"
$"C35D35C14D35C25535BE7DBB9EBDD6BB64BE32BBFABD4EBD1BBD18BC60BD1BBE"
$"68BD18BE8AC1A6BE8ABD7EBE8BC2913DC469BE8CC37DBEF7C466BFCAC45FBF60"
$"C462C032C45BC103C455C09AC458C108C34AC114C136C10EC240C11EBF0FC151"
$"BDCCC132BDFFC198BD4EC2BDBD18C205BD18C34BBD184ABD69C37BBD29C42CBD"
$"E4C444C1A6C4403DC444C291C444C469C444C37DC4ADC466C580C45FC516C462"
$"C5E8C45BC6B9C455C650C458C6BCC361C6C3C17AC6BFC26DC6CDBE09C62ABC3E"
$"C6AEBCFCC59EBB7CC3A7BB20C4F1BB2EC26EBB13C114BC0BC1D9BB49C0EFBC2F"
$"C0A7BC78C0CBBC53C089BC52C04EBC08C06CBC2DBFD1BB68BDF5BB1ABF12BB1A"
$"BD29BB1ABC52BB80BCEBBB2BBC52BB80BC52BB80BC52BB80BC52BB80030A0001"
$"00000A000101000A00010200"
};

View File

@ -0,0 +1,301 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Chat-O-Matic Documentation</title>
<link href='style.css' rel='stylesheet' type='text/css'>
</head>
<body>
<div align="center">
<h1>
<img width=32px src="img/AppIcon.png" />
Chat-O-Matic Documentation
</h1>
</div>
<p>Chat-O-Matic is a multi-protocol chat program based on
<a href="https://github.com/Numerio/Caya">Caya</a>, which has its own roots in the
<a href="http://www.eiman.tv/imkit/">IM kit</a> project. Several protocols are supported,
including <abbr title="Internet Relay Chat">IRC</abbr>,
<abbr title="Extensible Messaging and Presence Protocol">XMPP</abbr>, and more through
libpurple.
<nav>
<b>Contents</b>
<ul>
<li><a href="#window">Main window</a></li>
<ul>
<li><a href="#room_list">Room list</a></li>
<ul>
<li><a href="#room_flags">Room options</a></li>
</ul>
<li><a href="#room_header">Room header</a></li>
<li><a href="#user_list">User list</a></li>
<ul>
<li><a href="#moderation">Moderation</a></li>
</ul>
<li><a href="#chat_input">Chat input</a></li>
<ul>
<li><a href="#commands">Commands</a></li>
</ul>
<li><a href="#status">Status area</a></li>
<li><a href="#menu_bar">Menu-bar</a></li>
</ul>
<li><a href="#preferences">Preferences window</a></li>
<li><a href="#roster">Roster windows</a></li>
<li><a href="#rooms">Room creation/join windows</a></li>
<li><a href="#room_dir">Room directory</a></li>
</ul>
</nav>
<section id="window">
<h2>Main window</h2>
<img src="img/window.png" />
<section id="room_list">
<h3>Room list</h3>
<img src="img/chat-list.png" />
<p>Lists all joined chats that are attached to the current window, ordered by account.
Clicking a chat will select it, changing the text buffer,
<a href="#user_list">user-list</a>, and <a href="#room_header">room header</a>. If you
click on an <i>account's</i> list item, then that account's logs will be shown instead: This
might include anything from <abbr title="Message of the Day">MOTDs</abbr> to error messages,
depending on the protocol.</p>
<section id="room_flags">
<h4>Room options</h4>
<p>If you right-click on a room's list item, you can toggle several per-room options,
including:</p>
<table>
<tr><th>Flag</th><th>Description</th><th>Default</th></tr>
<tr><td>Auto-join</td>
<td>If the room will be joined after every start-up</td><td></td>
</tr>
<tr><td>Log messages</td>
<td>Whether or not messages will be logged to disk</td><td></td>
</tr>
<tr><td>Notify on every message</td>
<td>Notify if any message is sent to the room</td><td></td></tr>
<tr><td>Notify on direct-messages</td>
<td>Notify if the user is mentioned, or if message received in one-on-one
chat</td><td></td></tr>
</table>
</section>
</section>
<section id="room_header">
<h3>Room header</h3>
<img src="img/chat-header.png" />
<p>This little section displays a room's name, topic, and icon.</p>
<p>If you have permission to, you can also change a room's topic by clicking on the topic
text, typing what you'd like it to be, and hitting <span class="key">ENTER</span>. After
hitting <span class="key">ENTER</span>, the text will change back to the previous topic,
pending the protocol or server— after which it will update to the new topic.</p>
<p>Here, the room name is “#haiku” and the topic “Open-source operating system […].”</p>
</section>
<section>
<h3 id="user_list">User list</h3>
<img src="img/user-list.png" />
<p>This area has a calm, undemanding job— show a list of users in the room. On
right-clicking a user item, you'll see a pop-up menu with at least <i>User info…</i>.
<section>
<h4 id="moderation">Moderation</h4>
<p>Depending on your user's permissions, you might see more than <i>User info…</i> in the
user-list pop-up menus: <i>Ban user</i>, <i>Kick user</i>, <i>Mute user</i>,
<i>Deafen user</i>, and their reciprocals (<i>Unmute user</i>, <i>Undeafen user</i>).</p>
<p>By moderating through this right-click menu, though, you can't attach a message—
sometimes, for example, it's useful to give the victim a reason that you've kicked them
in the shins. For that, you can moderate through <a href="#commands">commands</a> as well.</p>
</section>
<section>
<h3 id="chat_input">Chat input</h3>
<img src="img/chat-input.png" />
<p>With the text-box, you can send messages and run commands. Type what you like, and hit
<span class="key">ENTER</span> to send. If you're typing someone's username or nickname,
hitting <span class="key">TAB</span> can auto-complete it.</p>
<p>Messages don't necessarily have to be only one line long— hence why you can resize the
text-box. <span class="key">ALT</span><span class="key">ENTER</span> starts a new line. If
you get lost, <span class="key">Page↑</span> and <span class="key">Page↓</span> are your
friends.</p>
<section>
<h4 id="commands">Commands</h4>
<p><i>Commands</i> let you perform some special action— they vary by protocol, but there are
some standard ones you can expect to work:</p>
<table>
<tr><th>Command</th><th>Description</th></tr>
<tr><td>/help</td><td>List all commands for the current protocol</td>
<tr><td>/invite <i>USER</i></td><td>Invite a user to the current room</td>
<tr><td>/kick <i>USER MESSAGE</i></td><td>Disconnect a user from the current room</td>
<tr><td>/ban <i>USER MESSAGE</i></td><td>Disconnect a user and prevent them from re-joining</td>
<tr><td>/unban <i>USER</i></td><td>Undo a user's ban</td>
<tr><td>/deafen <i>USER</i></td><td>Prevent a user from receiving any messages</td>
<tr><td>/undeafen <i>USER</i></td><td>Undo a user's deafening</td>
<tr><td>/mute <i>USER</i></td><td>Prevent a user from sending messages</td>
<tr><td>/unmute <i>USER</i></td><td>Undo a user's muting</td>
</table>
<p>As you might be able to tell, most of these are just aliases for
<a href="#moderation">moderation</a> through the <a href="#user_list">user list</a>.</p>
</section>
</section>
<section>
<h3 id="status">Status area</h3>
<img src="img/status-area.png" />
<p>In the status area, you can see your avatar (how cute!), change your nickname, and update
your current status.</p>
<p>Clicking on the nickname text-box (here it's “jaidedim”), you can then edit your nickname,
hitting <span class="key">ENTER</span> to update. After hitting
<span class="key">ENTER</span>, the text will change back to your previous nickname, pending
the protocol or server— after which it will update to the new nickname.</p>
<p>The status drop-down menu lets you change the status for the currently selected account
(here it's “Available”).</p>
<p>There is, next to the status menu, an account menu. By default “All” (the asterisk) is
selected, meaning that any changes to status or nickname will apply to all accounts. If
you'd like to change the nick or status of only a single account, you can select it from
this menu.</p>
</section>
<section>
<h3 id="menu_bar">Menu-bar</h3>
<img src="img/menu-bar.png" />
<p>In the <i>Program</i> menu, you'll find:
<ul>
<li>About…</li>
<li><a href="#preferences">Preferences…</a></li>
<li>Quit</li>
</ul>
</p>
<p>In the <i>Accounts</i> menu, you'll find a list of your accounts for
easy management. Through this menu, you can also select the <i>Accounts→Manage accounts…</i>
item to see the account management window, which lets you add and remove accounts.</p>
<p>In the <i>Chat</i> menu, you'll find:
<ul>
<li>Join room…</li>
<li><a href="#room_dir">Room directory…</a></li>
<li>New room…</li>
<li>New chat…</li>
<li>Find…</li>
</ul>
<i>Chat→Find…</i> (<span class="key">ALT</span><span class="key">F</span>) will open a
<b>TextSearch</b> window for searching the current room's logs (so long as logging
<a href="#room_flags">is enabled</a>). The other items are related to
<a href="#rooms">room creation/joining</a>.
</p>
<p>In the <i>Roster</i> menu, you'll find:
<ul>
<li>Edit roster…</li>
<li>Invite user…</li>
</ul>
<p>In the <i>Window</i> menu, you'll find two items that make switching between rooms
faster— <i>Window→Up</i> (<span class="key">ALT</span><span class="key"></span>) and
<i>Window→Down</i> (<span class="key">ALT</span><span class="key"></span>).</p>
</section>
</section>
<section>
<h2 id="preferences">Preferences window</h4>
<img src="img/pref-notifications.png" />
<p>Selecting <i>Program→Preferences</i> through the menu-bar (<span class="key">ALT</span>
<span class="key">,</span>), you'll see the Preferences window. Most of the options should
be self-explanatory, but there a couple that are noteworthy:<p>
<p>The <i>Notifications</i> tab lets you toggle whether or not you receive message-related
notifications and whether or not you'll be notified on a connection/disconnection.</p>
<p>You can also attach specific sounds to notifications through Haiku's <b>Sounds</b>
preferences, brought up through the <i>Edit sounds…</i> button. The beeps configurable
through <b>Sounds</b> are <i>Chat-O-Matic mention</i> and <i>Chat-O-Matic message</i>.</p>
</section>
<section>
<h2 id="roster">Roster/user windows</h2>
<img src="img/select-user.png" />
<p>Roster and user-selection windows (as in <i>Roster→Edit roster…</i>,
<i>Roster→Invite user…</i> and <i>Chat→New chat…</i> from the
<a href="#menu_bar">menu-bar</a>) display a list of your contacts,
allowing you to select one.</p>
<p>Through the account drop-down menu in the
bottom-left corner, you can filter the list, showing only contacts
of a specific account. In this example, “jaidedim-xmpp“, an XMPP
account, is selected.</p>
<p>By typing into the text-box at the top of the window, you can search
through the shown contacts— here, all contacts matching “Ash” are
shown. If you have an account selected, you can also use this text-box
to manually type in a username for selection. In this window, since
a specific account is selected, “Select user Ash…” is an item in the
list.</p>
</section>
<section>
<h2 id="rooms">Room creation/join windows</h2>
<img src="img/room-creation.png" />
<p>With <i>Chat→Join room…</i> and <i>Chat→New room…</i> in the
<a href="#menu_bar">menu-bar</a> (<span class="key">ALT</span><span class="key">J</span> and
<span class="key">ALT</span><span class="key">N</span>, respectively), you'll see a window
like the one above. For each protocol, the options and text-boxes shown will differ— for
<abbr title="Internet Relay Chat">IRC</abbr>, as in this image, the only option is the
channel-name.</p>
<p>You can select the account to join/create from through the account drop-down menu in the
bottom-left corner. Here, the account “oftc-irc” is selected, an
<abbr title="Internet Relay Chat">IRC</abbr> account.</p>
<p><i>Chat→Create chat…</i> (<span class="key">ALT</span><span class="key">M</span>), in
contrast, is used for creating one-on-one chats, and opens a
<a href="#roster">user-selection window</a> instead.</p>
</section>
<section>
<h2 id="room_dir">Room directory</h2>
<img src="img/room-directory.png" />
<p>The room directory (accessible though <i>Chat→Room directory…</i> in the
<a href="#menu_bar">menu-bar</a>) lets you browse and join publically listed chatrooms— a
feature used extensively for <abbr title="Internet Relay Chat">IRC</abbr>,
<abbr title="Extensible Messaging and Presence Protocol">XMPP</abbr>, and some libpurple
add-ons.</p>
<p>Like other dialogues in Chat-O-Matic, you can select a specific account to act on though
the bottom-left account dropdown menu. In this image, “All” accounts is selected, so rooms
available to all the user's accounts are shown.</p>
</section>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

22
documentation/style.css Normal file
View File

@ -0,0 +1,22 @@
/* SPCSS theme by Susam Pal, under the MIT license
* https://github.com/susam/spcss */
body{color:#333;font-family:helvetica,arial,sans-serif;line-height:1.5;margin:0 auto;max-width:40em;padding:0 1em}h1,h2,h3,h4,h5,h6{margin:1.25em 0 .5em;line-height:1.2}a:link{color:#00e}a:visited{color:#518}a:focus,a:hover{color:#03f}a:active{color:#e00}h1 a:empty:before,h2 a:empty:before,h3 a:empty:before,h4 a:empty:before,h5 a:empty:before,h6 a:empty:before{content:"#"}h1 a:empty,h2 a:empty,h3 a:empty,h4 a:empty,h5 a:empty,h6 a:empty{visibility:hidden;padding-left:.25em}h1:hover a:empty,h2:hover a:empty,h3:hover a:empty,h4:hover a:empty,h5:hover a:empty,h6:hover a:empty{visibility:visible}img{max-width:100%}figure{margin:1em 0;text-align:center}figcaption{font-size:small}code,kbd,pre,samp{color:#009;font-family:monospace,monospace}pre kbd{color:#060}blockquote,pre{background:#eee;padding:.5em}pre{overflow:auto}blockquote{border-left:medium solid #ccc;margin:1em 0}blockquote :first-child{margin-top:0}blockquote :last-child{margin-bottom:0}table{border-collapse:collapse}td,th{border:thin solid #999;padding:.3em .4em;text-align:left}@media (prefers-color-scheme:dark){body{background:#111;color:#bbb}a:link{color:#9bf}a:visited{color:#caf}a:focus,a:hover{color:#9cf}a:active{color:#faa}code,kbd,pre,samp{color:#6cf}pre kbd{color:#9c6}blockquote,pre{background:#000}blockquote{border-color:#333}td,th{border-color:#666}}
/*
* Copyright 2008-2017, Haiku. All rights reserved.
* Distributed under the terms of the MIT License.
*/
.key { /* Shortcut (separate with */
-webkit-border-radius: 3px;
-khtml-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
border-color: #c7c7c7;
border-style: solid;
border-width: 1px;
padding: 0px 2px 0px 2px;
background-color: #e8e8e8;
font-family: serif;
font-variant: small-caps;
font-size: 0.8em;
}

View File

@ -1,3 +1,5 @@
include Make.pre
# Haiku Generic Makefile v2.6 ##
## Fill in this file to specify the project being created, and the referenced
@ -140,4 +142,4 @@ DEVEL_DIRECTORY := \
$(shell findpaths -r "makefile_engine" B_FIND_PATH_DEVELOP_DIRECTORY)
include $(DEVEL_DIRECTORY)/etc/makefile-engine
include Makefile.common
include Make.post

View File

@ -1,3 +1,5 @@
include Make.pre
## Haiku Generic Makefile v2.6 ##
## Fill in this file to specify the project being created, and the referenced
@ -134,4 +136,4 @@ DEVEL_DIRECTORY := \
$(shell findpaths -r "makefile_engine" B_FIND_PATH_DEVELOP_DIRECTORY)
include $(DEVEL_DIRECTORY)/etc/makefile-engine
include Makefile.common
include Make.post

View File

@ -17,6 +17,7 @@ public:
void AddItem(KEY k, TYPE t);
TYPE ValueFor(KEY, bool* found = NULL) const;
KEY KeyFor(TYPE, bool* found = NULL) const;
TYPE RemoveItemAt(int32 position);
TYPE RemoveItemFor(KEY);
@ -63,11 +64,25 @@ KeyMap<KEY, TYPE>::ValueFor(KEY k, bool* found) const
}
if (i == fMap.end())
return NULL;
return 0;
return i->second;
}
template<class KEY, class TYPE>
inline KEY
KeyMap<KEY, TYPE>::KeyFor(TYPE v, bool* found) const
{
*found = false;
for (int32 i = 0; i < CountItems(); i++)
if (ValueAt(i) == v) {
*found = true;
return KeyAt(i);
}
return NULL;
}
template<class KEY, class TYPE>
inline TYPE
KeyMap<KEY, TYPE>::RemoveItemAt(int32 position)

View File

@ -1,3 +1,5 @@
include Make.pre
## Haiku Generic Makefile v2.6 ##
## Fill in this file to specify the project being created, and the referenced
@ -134,4 +136,4 @@ DEVEL_DIRECTORY := \
$(shell findpaths -r "makefile_engine" B_FIND_PATH_DEVELOP_DIRECTORY)
include $(DEVEL_DIRECTORY)/etc/makefile-engine
include Makefile.common
include Make.post

View File

@ -1,108 +1,133 @@
1 English application/x-vnd.cardie 1762272810
Disable deskbar replicant PreferencesReplicant Disable deskbar replicant
1 English application/x-vnd.chat-o-matic 244317784
No accounts enabled, no joy. ConversationView ― Startup messages No accounts enabled, no joy.
Description Room directory Description
%user% is now offline! Server %user% is now offline!
New mention Conversation ― Notifications New mention
{0, plural,=1{One lonely user}=2{Two partners}other{# members}} ConversationInfoWindow {0, plural,=1{One lonely user}=2{Two partners}other{# members}}
Enable contact status notifications PreferencesNotifications Enable contact status notifications
Edit sounds… PreferencesNotifications Edit sounds…
Auto-raise when a message is received PreferencesBehavior Auto-raise when a message is received
On incoming… PreferencesBehavior On incoming…
Account name: AccountDialog Account name:
Connected Server Connected
User information UserInfoWindow User information
Account now online! Server Account now online!
Del AccountsWindow Short for 'delete' Del
%name% is offline! RosterView %name% is offline!
Preferences… MainWindow Preferences…
** You created the room.\n ConversationView ** You created the room.\n
Room directory Room directory Room directory
Adding contact RosterEditWindow Adding contact
Deskbar replicant PreferencesReplicant Deskbar replicant
Add account PreferencesAccounts Add account
It looks like you don't have any accounts enabled. ConversationView ― Startup messages It looks like you don't have any accounts enabled.
Start a chat RosterListView Start a chat
Enable PreferencesAccounts Enable
%app% is released under the GNU GPL License.\nSome parts of %app% are available under MIT license.\nBuilt: %buildDate% TheApp %app% is released under the GNU GPL License.\nSome parts of %app% are available under MIT license.\nBuilt: %buildDate%
Log messages ConversationListView Log messages
Edit… AccountsWindow Edit…
%app% setup ConversationView ― Startup messages %app% setup
Manage accounts… MainWindow Manage accounts…
Create room MainWindow Create room
** The subject is now: %subject% ConversationView ** The subject is now: %subject%
New chat… ConversationListView New chat…
Add AccountsWindow Add
Chat view PreferencesChatWindow Chat view
Hide field in Deskbar PreferencesReplicant Hide field in Deskbar
Edit roster… MainWindow Edit roster…
Room information ConversationInfoWindow Room information
Ouch! TheApp Ouch!
Notifications PreferencesNotifications Notifications
Disconnected Server Disconnected
Join a room MainWindow Join a room
Room info… ConversationListView Room info…
OK PreferencesWindow OK
Enable protocol status notifications PreferencesBehavior Enable protocol status notifications
Accounts ConversationView ― Startup messages Accounts
Send a file… RosterListView Send a file…
Are you sure you want to quit? MainWindow Are you sure you want to quit?
Del PreferencesAccounts Short for 'delete' Del
Manage accounts through the %menu% menu to get started. ConversationView ― Startup messages Manage accounts through the %menu% menu to get started.
%name% is available! RosterView %name% is available!
New message Conversation ― Notifications New message
Some items are empty. Please make sure to fill out every item. TemplateWindow Some items are empty. Please make sure to fill out every item.
Enable message notifications PreferencesBehavior Enable message notifications
Program MainWindow Program
Cancel AccountDialog Cancel
Leave room ConversationListView Leave room
Behavior PreferencesBehavior Behavior
Replicant PreferencesReplicant Replicant
Play sound event PreferencesBehavior Play sound event
Find… MainWindow Find…
Enable MainWindow Enable
You've been summoned from %source%. Conversation ― Notifications You've been summoned from %source%.
Notify on every message ConversationListView Notify on every message
Disabled Server Disabled
That isn't a valid command. Try /help for a list. Conversation ― Command info That isn't a valid command. Try /help for a list.
** You joined the room.\n ConversationView ** You joined the room.\n
-- That command doesn't exist. Try '/help' for a list.\n Server -- That command doesn't exist. Try '/help' for a list.\n
OK TemplateWindow OK
Reject InviteDialogue Reject
\nWritten by:\n About window \nWritten by:\n
Disable PreferencesAccounts Disable
All AccountsMenu All
Offline Utils ― Status names Offline
%user% has been disabled! Server %user% has been disabled!
You've been invited to %room%. Server You've been invited to %room%.
Invite user… UserListView Invite user…
Add PreferencesAccounts Add
Sound when mentioned PreferencesNotifications Sound when mentioned
Preferences PreferencesWindow Preferences
Deskbar notifications PreferencesBehavior Deskbar notifications
Connecting Server Connecting
Name Room directory Name
General PreferencesBehavior General
%user% is readying… Server %user% is readying…
Enable protocol status notifications PreferencesNotifications Enable protocol status notifications
Presence RosterView Presence
Cancel TemplateWindow Cancel
Hide offline contacts PreferencesBehavior Hide offline contacts
%user% has joined the room.\n ConversationView %user% has joined the room.\n
Busy Utils ― Status names Busy
Roster RosterEditWindow Roster
Auto-join room ConversationListView Auto-join room
** %old% has changed their nick to %new%. Conversation ― Command info ** %old% has changed their nick to %new%.
Don't ask confirmation at Quit PreferencesBehavior Don't ask confirmation at Quit
Closing MainWindow Closing
Sound on message received PreferencesNotifications Sound on message received
Available Utils ― Status names Available
Up MainWindow Up
Invite user… MainWindow Invite user…
Room directory… MainWindow Room directory…
Custom Status Utils ― Status names Custom Status
Can't find smileys settings in:\n\n%path% TheApp Can't find smileys settings in:\n\n%path%
Connection failed Server Connection failed
Mark unread window chat PreferencesBehavior Mark unread window chat
%user% has left the room.\n ConversationView %user% has left the room.\n
Join Room directory Join
Join room… MainWindow Join room…
Edit account AccountsWindow Edit account
Preferences ReplicantStatusView Preferences
Quit MainWindow Quit
Chat MainWindow Chat
No MainWindow No
Chat settings PreferencesChatWindow Chat settings
Cancel InviteDialogue Cancel
Mark unread the Deskbar Replicant PreferencesBehavior Mark unread the Deskbar Replicant
Sounds PreferencesNotifications Sounds
Add account AccountsWindow Add account
OK Server OK
Enable AccountsWindow Enable
Exit ReplicantStatusView Exit
Disable AccountsWindow Disable
User info… RosterListView User info…
Move window to current workspace PreferencesBehavior Move window to current workspace
Away Utils ― Status names Away
OK AccountDialog OK
Notify on direct-messages ConversationListView Notify on direct-messages
Accounts MainWindow Accounts
%user% has been banned.\n ConversationView %user% has been banned.\n
Editing contact RosterEditWindow Editing contact
%user% isn't a member of this room. ChatCommand %user% isn't a member of this room.
Edit account PreferencesAccounts Edit account
Account now offline and disconnected. Server Account now offline and disconnected.
Down MainWindow Down
** Commands: Server ** Commands:
Cardie System name Cardie
I'm rearing to go! Conversation ― Command info I'm rearing to go!
About… About window About…
Ignore emoticons PreferencesChatWindow Ignore emoticons
Enable message notifications PreferencesNotifications Enable message notifications
Invitation received Server Invitation received
Enable contact status notifications PreferencesBehavior Enable contact status notifications
Auto-raise when user is typing PreferencesBehavior Auto-raise when user is typing
Ignore emoticons PreferencesChatWindow Ignore emoticons
Error Server Error
Modify account… MainWindow Modify account…
Copyright © About window Copyright ©
Roster MainWindow Roster
Accept InviteDialogue Accept
%user% was kicked (%body%).\n ConversationView %user% was kicked (%body%).\n
An error is occurred renaming the account to %name%! AccountDialog An error is occurred renaming the account to %name%!
%user% has invited you to %room%. Server %user% has invited you to %room%.
Permanent deskbar replicant PreferencesReplicant Permanent deskbar replicant
Default User role Default
%user% has been banned (%body%).\n ConversationView %user% has been banned (%body%).\n
Invite contact to chat… MainWindow Invite contact to chat…
@ -110,14 +135,22 @@ Invite contact to chat… MainWindow Invite contact to chat…
Window MainWindow Window
Invisible Utils ― Status names Invisible
{0, plural,=1{You've got a new message from %source%.}other{You've got # new messages from %source%.}} Conversation ― Notifications {0, plural,=1{You've got a new message from %source%.}other{You've got # new messages from %source%.}}
Afterward, you can join a room or start a chat through the %menu% menu. :-) ConversationView ― Startup messages Afterward, you can join a room or start a chat through the %menu% menu. :-)
%user% has joined the room (%body%).\n ConversationView %user% has joined the room (%body%).\n
%app% is released under the MIT License.\nAdd-on and library licenses may vary.\nBuilt: %buildDate% TheApp %app% is released under the MIT License.\nAdd-on and library licenses may vary.\nBuilt: %buildDate%
Chat-O-Matic System name Chat-O-Matic
Yes MainWindow Yes
An error has occured saving the settings.\nCheck if your disk has enough space. AccountDialog An error has occured saving the settings.\nCheck if your disk has enough space.
%user% has been temporarily disabled. Server %user% has been temporarily disabled.
Disable MainWindow Disable
Close About window Close
No protocols found!\nPlease make sure %app% was installed correctly. TheApp No protocols found!\nPlease make sure %app% was installed correctly.
Accounts AccountsWindow Accounts
%user% has left the room (%body%).\n ConversationView %user% has left the room (%body%).\n
New chat… MainWindow New chat…
New room… MainWindow New room…
About… MainWindow About…
Edit… PreferencesAccounts Edit…
Master Foo ConversationView ― Startup messages Master Foo
Chat ConversationView ― Startup messages Chat
%user% was kicked.\n ConversationView %user% was kicked.\n
You aren't contacts with and have no chats in common with %user%. Shame. ChatCommand You aren't contacts with and have no chats in common with %user%. Shame.

View File

@ -1,4 +1,4 @@
1 English application/x-vnd.cardie.purple 2993124882
1 English application/x-vnd.chat-o-matic.purple 3277859582
Connection error PurpleApp ― Connection errors Connection error
** You can't undo what was once done… at least with this protocol.\n PurpleApp ― Room moderation ** You can't undo what was once done… at least with this protocol.\n
** Command isn't useful in this chat %err%\n PurpleApp ― Command errors ** Command isn't useful in this chat %err%\n
@ -6,8 +6,8 @@ Self-signed certificate PurpleApp ― Connection errors Self-signed certificate
Username: PurpleApp ― Account template Username:
No SSL certificate provided PurpleApp ― Connection errors No SSL certificate provided
SSL unsupported PurpleApp ― Connection errors SSL unsupported
** Command error %err%\n PurpleApp ― Command errors ** Command error %err%\n
Authentication failed PurpleApp ― Connection errors Authentication failed
** Command error %err%\n PurpleApp ― Command errors ** Command error %err%\n
You can't friend someone without a nick. PurpleProtocol ― Roster template You can't friend someone without a nick.
** Command failed %err%\n PurpleApp ― Command errors ** Command failed %err%\n
Certificate error PurpleApp ― Connection errors Certificate error
@ -20,10 +20,12 @@ Certifcate and fingerprint conflict PurpleApp ― Connection errors Certifcate
Password: PurpleApp ― Account template Password:
Invalid username PurpleApp ― Connection errors Invalid username
Username in use PurpleApp ― Connection errors Username in use
User changed name to %nick% PurpleApp ― Room template User changed name to %nick%
Encryption error PurpleApp ― Connection errors Encryption error
A username needs to be specified! PurpleApp ― Account template A username needs to be specified!
Settings invalid PurpleApp ― Connection errors Settings invalid
Moderator PurpleApp ― User roles Moderator
OK PurpleApp ― Room template OK
Alias: PurpleProtocol ― Roster template Alias:
Untrusted SSL certificate PurpleApp ― Connection errors Untrusted SSL certificate
Operator PurpleApp ― User roles Operator
@ -33,10 +35,12 @@ Founder PurpleApp ― User roles Founder
Certificate and hostname conflict PurpleApp ― Connection errors Certificate and hostname conflict
** Invalid arguments to command %err%\n PurpleApp ― Command errors ** Invalid arguments to command %err%\n
** Command not found %err%\n PurpleApp ― Command errors ** Command not found %err%\n
Couldn't set your nick:\n%error% PurpleApp ― Room template Couldn't set your nick:\n%error%
Expired SSL certificate PurpleApp ― Connection errors Expired SSL certificate
** Banning won't work with this protocol. Try being mean instead.\n PurpleApp ― Room moderation ** Banning won't work with this protocol. Try being mean instead.\n
Room ID PurpleApp ― Room template Room ID
** This protocol is particularly self-concious, and prefers that this person not see its chats.\n PurpleApp ― Room moderation ** This protocol is particularly self-concious, and prefers that this person not see its chats.\n
Failed to set nickname PurpleApp ― Room template Failed to set nickname
Authentication impossible PurpleApp ― Connection errors Authentication impossible
Username: PurpleProtocol ― Roster template Username:
** This protocol doesn't support deafening, but spamming the chat should be a good substitute. :^)\n PurpleApp ― Room moderation ** This protocol doesn't support deafening, but spamming the chat should be a good substitute. :^)\n

Some files were not shown because too many files have changed in this diff Show More