Compare commits
10 Commits
0752aab1d4
...
2ddca713c6
Author | SHA1 | Date |
---|---|---|
Pascal Abresch | 2ddca713c6 | |
Pascal Abresch | a3db234a9f | |
Pascal Abresch | 3609bbea28 | |
Pascal Abresch | 61907c08dd | |
Pascal Abresch | f0c6faa193 | |
Adrien Destugues | 447f91d80d | |
Adrien Destugues | ba54032d17 | |
Adrien Destugues | 408acef105 | |
Adrien Destugues | cd64eff494 | |
Adrien Destugues | 6cd7d711be |
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required (VERSION 2.8)
|
||||
cmake_minimum_required (VERSION 3.20)
|
||||
|
||||
# projectname is the same as the main-executable
|
||||
project(Renga)
|
||||
|
|
|
@ -45,7 +45,7 @@ int RosterView::ListComparison(const void *a, const void *b) {
|
|||
}
|
||||
|
||||
void RosterView::AttachedToWindow() {
|
||||
// superclass call
|
||||
// superclass call
|
||||
BOutlineListView::AttachedToWindow();
|
||||
|
||||
// on double-click
|
||||
|
@ -58,11 +58,10 @@ void RosterView::AttachedToWindow() {
|
|||
_message_item = new BMenuItem("Send Message" B_UTF8_ELLIPSIS, new BMessage(JAB_OPEN_MESSAGE));
|
||||
_change_user_item = new BMenuItem("Edit Buddy", new BMessage(JAB_OPEN_EDIT_BUDDY_WINDOW));
|
||||
_remove_user_item = new BMenuItem("Remove Buddy", new BMessage(JAB_REMOVE_BUDDY));
|
||||
_user_info_item = new BMenuItem("Get User Info", new BMessage(JAB_USER_INFO));
|
||||
_user_chatlog_item = new BMenuItem("Show Chat Log", new BMessage(JAB_SHOW_CHATLOG));
|
||||
_user_info_item = new BMenuItem("Get Buddy Info", new BMessage(JAB_USER_INFO));
|
||||
|
||||
_presence = new BMenu("Presence");
|
||||
|
||||
|
||||
_subscribe_presence = new BMenuItem("Subscribe", new BMessage(JAB_SUBSCRIBE_PRESENCE));
|
||||
_unsubscribe_presence = new BMenuItem("Unsubscribe", new BMessage(JAB_UNSUBSCRIBE_PRESENCE));
|
||||
|
||||
|
@ -77,8 +76,6 @@ void RosterView::AttachedToWindow() {
|
|||
_popup->AddSeparatorItem();
|
||||
_popup->AddItem(_user_info_item);
|
||||
_popup->AddSeparatorItem();
|
||||
_popup->AddItem(_user_chatlog_item);
|
||||
_popup->AddSeparatorItem();
|
||||
_popup->AddItem(_presence);
|
||||
|
||||
// create top level lists
|
||||
|
@ -88,7 +85,7 @@ void RosterView::AttachedToWindow() {
|
|||
AddItem(_offline = new RosterSuperitem("Offline"));
|
||||
AddItem(_transports = new RosterSuperitem("Live Transports"));
|
||||
AddItem(_bookmarks = new RosterSuperitem("Group chats"));
|
||||
|
||||
|
||||
// make maps (BUGBUG better way to do two-way map?)
|
||||
_item_to_status_map[_offline] = UserID::OFFLINE;
|
||||
_item_to_status_map[_online] = UserID::ONLINE;
|
||||
|
@ -108,7 +105,7 @@ void RosterView::AttachedToWindow() {
|
|||
_status_to_item_map[UserID::UNKNOWN] = _unknown;
|
||||
_status_to_item_map[UserID::TRANSPORT_ONLINE] = _transports;
|
||||
_status_to_item_map[UserID::UNACCEPTED] = _unaccepted;
|
||||
|
||||
|
||||
// BUGBUG events
|
||||
_presence->SetTargetForItems(Window());
|
||||
_popup->SetTargetForItems(Window());
|
||||
|
@ -120,7 +117,7 @@ void RosterView::AttachedToWindow() {
|
|||
|
||||
RosterItem *RosterView::CurrentItemSelection() {
|
||||
int32 index = CurrentSelection();
|
||||
|
||||
|
||||
if (index >= 0) {
|
||||
return dynamic_cast<RosterItem *>(ItemAt(index));
|
||||
} else {
|
||||
|
@ -131,7 +128,7 @@ RosterItem *RosterView::CurrentItemSelection() {
|
|||
BookmarkItem* RosterView::CurrentBookmarkSelection()
|
||||
{
|
||||
int32 index = CurrentSelection();
|
||||
|
||||
|
||||
if (index >= 0) {
|
||||
return dynamic_cast<BookmarkItem *>(ItemAt(index));
|
||||
} else {
|
||||
|
@ -153,10 +150,10 @@ void RosterView::MouseDown(BPoint point) {
|
|||
if (buttons & B_SECONDARY_MOUSE_BUTTON) {
|
||||
// update menu before presentation
|
||||
UpdatePopUpMenu();
|
||||
|
||||
|
||||
BPoint screen_point(point);
|
||||
ConvertToScreen(&screen_point);
|
||||
|
||||
|
||||
BRect r(screen_point.x - 4, screen_point.y - 20, screen_point.x + 24, screen_point.y + 4);
|
||||
_popup->Go(screen_point, true, true, r, false);
|
||||
//_popup->Go(screen_point, true, true, false);
|
||||
|
@ -168,12 +165,12 @@ void RosterView::RemoveSelected() {
|
|||
// numeric and object based selections
|
||||
int32 selected = CurrentSelection();
|
||||
RosterItem *item = CurrentItemSelection();
|
||||
|
||||
|
||||
if (item == NULL) {
|
||||
// not a roster item, won't remove
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// remove item from view
|
||||
RemoveItem(CurrentSelection());
|
||||
|
||||
|
@ -271,41 +268,64 @@ void RosterView::LinkTransport(const UserID *added_transport) {
|
|||
AddUnder(new TransportItem(added_transport), _transports);
|
||||
}
|
||||
|
||||
|
||||
static int compareStrings(const char* a, const char* b)
|
||||
{
|
||||
// FIXME use ICU locale aware comparison instead
|
||||
int icompare = strcasecmp(a, b);
|
||||
if (icompare != 0)
|
||||
return icompare;
|
||||
|
||||
// In case the names are case-insensitive-equal, still sort them in a
|
||||
// predictible way
|
||||
return strcmp(a, b);
|
||||
}
|
||||
|
||||
|
||||
int32 CompareBookmarks(const BListItem* first, const BListItem* second) {
|
||||
gloox::JID firstBookmark = dynamic_cast<const BookmarkItem*>(first)->GetUserID();
|
||||
gloox::JID secondBookmark = dynamic_cast<const BookmarkItem*>(second)->GetUserID();
|
||||
return compareStrings(firstBookmark.full().c_str(), secondBookmark.full().c_str());
|
||||
}
|
||||
|
||||
|
||||
void RosterView::LinkBookmark(const gloox::JID& added_bookmark, BString name) {
|
||||
int32 index = FindBookmark(added_bookmark);
|
||||
if (index < 0)
|
||||
if (index < 0) {
|
||||
AddUnder(new BookmarkItem(added_bookmark, name), _bookmarks);
|
||||
else {
|
||||
SortItemsUnder(_bookmarks, true, &CompareBookmarks);
|
||||
} else {
|
||||
BookmarkItem* item = dynamic_cast<BookmarkItem*>(FullListItemAt(index));
|
||||
item->Update(this, be_plain_font);
|
||||
Invalidate(ItemFrame(index));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RosterView::UnlinkUser(const gloox::JID& removed_user) {
|
||||
// does user exist
|
||||
int32 index = FindUser(removed_user);
|
||||
|
||||
|
||||
if (index >= 0) {
|
||||
RemoveItem(index);
|
||||
RemoveItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
void RosterView::UnlinkTransport(const gloox::JID& removed_transport) {
|
||||
// does transport exist
|
||||
int32 index = FindTransport(removed_transport);
|
||||
|
||||
|
||||
if (index >= 0) {
|
||||
RemoveItem(index);
|
||||
RemoveItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
void RosterView::UnlinkBookmark(const gloox::JID& removed_bookmark) {
|
||||
// does transport exist
|
||||
int32 index = FindBookmark(removed_bookmark);
|
||||
|
||||
|
||||
if (index >= 0) {
|
||||
RemoveItem(index);
|
||||
RemoveItem(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +337,7 @@ int32 RosterView::FindUser(const gloox::JID& compare_user) {
|
|||
if (item == NULL || item->StalePointer()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// compare against RosterView
|
||||
if (item->GetUserID()->JID().bare() == compare_user.bare()) {
|
||||
return i;
|
||||
|
@ -336,7 +356,7 @@ int32 RosterView::FindTransport(const gloox::JID& compare_transport) {
|
|||
if (item == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// compare against RosterView
|
||||
if (item->GetUserID()->JID() == compare_transport) {
|
||||
return i;
|
||||
|
@ -357,7 +377,7 @@ int32 RosterView::FindBookmark(const gloox::JID& compare_jid)
|
|||
if (item == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// compare against RosterView
|
||||
if (item->GetUserID() == compare_jid) {
|
||||
return i;
|
||||
|
@ -390,7 +410,6 @@ void RosterView::UpdatePopUpMenu() {
|
|||
_remove_user_item->SetEnabled(true);
|
||||
|
||||
_user_info_item->SetEnabled(true);
|
||||
_user_chatlog_item->SetEnabled(BlabberSettings::Instance()->Tag("autoopen-chatlog"));
|
||||
|
||||
_presence->SetEnabled(true);
|
||||
|
||||
|
@ -415,7 +434,6 @@ void RosterView::UpdatePopUpMenu() {
|
|||
_remove_user_item->SetEnabled(true);
|
||||
|
||||
_user_info_item->SetEnabled(false);
|
||||
_user_chatlog_item->SetEnabled(false);
|
||||
|
||||
_presence->SetEnabled(false);
|
||||
} else {
|
||||
|
@ -432,7 +450,6 @@ void RosterView::UpdatePopUpMenu() {
|
|||
_remove_user_item->SetEnabled(false);
|
||||
|
||||
_user_info_item->SetEnabled(false);
|
||||
_user_chatlog_item->SetEnabled(false);
|
||||
|
||||
_presence->SetEnabled(false);
|
||||
}
|
||||
|
@ -458,7 +475,7 @@ void RosterView::UpdateRoster() {
|
|||
for (int i = 0; i < FullListCountItems(); ++i) {
|
||||
RosterItem *item = dynamic_cast<RosterItem *>(FullListItemAt(i));
|
||||
TransportItem *transport_item = dynamic_cast<TransportItem *>(FullListItemAt(i));
|
||||
|
||||
|
||||
// skip illegal entries
|
||||
if (item == NULL && transport_item == NULL) {
|
||||
continue;
|
||||
|
@ -472,14 +489,14 @@ void RosterView::UpdateRoster() {
|
|||
|
||||
goto RESET;
|
||||
}
|
||||
|
||||
|
||||
// change of statuses
|
||||
if (item->GetUserID()->OnlineStatus() != _item_to_status_map[Superitem(item)]) {
|
||||
UserID::online_status old_status = _item_to_status_map[Superitem(item)];
|
||||
|
||||
|
||||
// remove the item from the current superitem...
|
||||
RemoveItem(i);
|
||||
|
||||
|
||||
// and add it to the appropriate one
|
||||
AddUnder(item, _status_to_item_map[item->GetUserID()->OnlineStatus()]);
|
||||
|
||||
|
@ -489,9 +506,9 @@ void RosterView::UpdateRoster() {
|
|||
} else if (item->GetUserID()->OnlineStatus() == UserID::OFFLINE && item->GetUserID()->IsUser() && old_status == UserID::ONLINE) {
|
||||
SoundSystem::Instance()->PlayUserOfflineSound();
|
||||
}
|
||||
|
||||
|
||||
goto RESET;
|
||||
}
|
||||
}
|
||||
|
||||
// clean it
|
||||
InvalidateItem(i);
|
||||
|
@ -507,7 +524,7 @@ void RosterView::UpdateRoster() {
|
|||
if (transport_item->GetUserID()->OnlineStatus() != _item_to_status_map[Superitem(transport_item)]) {
|
||||
// remove the item from the current superitem...
|
||||
RemoveItem(i);
|
||||
|
||||
|
||||
// and add it to the appropriate one
|
||||
if (transport_item->GetUserID()->OnlineStatus() == UserID::TRANSPORT_ONLINE) {
|
||||
AddUnder(transport_item, _status_to_item_map[transport_item->GetUserID()->OnlineStatus()]);
|
||||
|
@ -519,9 +536,9 @@ void RosterView::UpdateRoster() {
|
|||
} else if (transport_item->GetUserID()->OnlineStatus() == UserID::OFFLINE) {
|
||||
SoundSystem::Instance()->PlayUserOfflineSound();
|
||||
}
|
||||
|
||||
|
||||
goto RESET;
|
||||
}
|
||||
}
|
||||
|
||||
// clean it
|
||||
InvalidateItem(i);
|
||||
|
|
|
@ -62,7 +62,6 @@ private:
|
|||
BMenuItem *_change_user_item;
|
||||
BMenuItem *_remove_user_item;
|
||||
BMenuItem *_user_info_item;
|
||||
BMenuItem *_user_chatlog_item;
|
||||
|
||||
BMenu *_presence;
|
||||
BMenuItem *_subscribe_presence;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <File.h>
|
||||
#include <FindDirectory.h>
|
||||
#include <Notification.h>
|
||||
#include <Path.h>
|
||||
#include <interface/Window.h>
|
||||
|
||||
|
@ -456,10 +457,7 @@ TalkManager::handleMUCMessage(gloox::MUCRoom *room,
|
|||
{
|
||||
TalkView *window = NULL;
|
||||
|
||||
string group_username;
|
||||
|
||||
// clear out text
|
||||
group_username = msg.from().resource();
|
||||
string group_username = msg.from().resource();
|
||||
|
||||
window = fGroupMap.at(room);
|
||||
// submit the chat
|
||||
|
@ -469,7 +467,28 @@ TalkManager::handleMUCMessage(gloox::MUCRoom *room,
|
|||
if (group_username.empty()) {
|
||||
window->AddToTalk("System:", msg.body(), TalkView::OTHER);
|
||||
} else {
|
||||
window->NewMessage(group_username, msg.body());
|
||||
bool highlight;
|
||||
// Highlight messages when they mention the nickname
|
||||
// TODO also use metadata in the message that may indicate an highlight
|
||||
if (BString(msg.body().c_str()).IFindFirst(room->nick().c_str()) != B_ERROR) {
|
||||
// NOTE: This will erronously pop up for backlog messages aswell, can be improved
|
||||
// once we have MAM
|
||||
BNotification notification(B_INFORMATION_NOTIFICATION);
|
||||
notification.SetGroup(room->name().c_str());
|
||||
notification.SetTitle(group_username.c_str());
|
||||
notification.SetContent(msg.body().c_str());
|
||||
notification.Send();
|
||||
|
||||
BlabberMainWindow::Instance()->FlagBookmarkItem(msg.from().bare(),
|
||||
BookmarkItem::NICKNAME_HIGHLIGHT);
|
||||
highlight = true;
|
||||
} else {
|
||||
BlabberMainWindow::Instance()->FlagBookmarkItem(msg.from().bare(),
|
||||
BookmarkItem::ACTIVITY);
|
||||
highlight = false;
|
||||
}
|
||||
|
||||
window->NewMessage(group_username, msg.body(), highlight);
|
||||
}
|
||||
window->UnlockLooper();
|
||||
}
|
||||
|
@ -497,7 +516,7 @@ TalkManager::handleMUCSubject(gloox::MUCRoom *room,
|
|||
window->LockLooper();
|
||||
window->SetStatus(subject);
|
||||
if (!nick.empty()) {
|
||||
// Do it only for topic chnages (not for the initial topic setting)
|
||||
// Do it only for topic changes (not for the initial topic setting)
|
||||
window->AddToTalk(nick, topic.String(), TalkView::OTHER);
|
||||
}
|
||||
window->UnlockLooper();
|
||||
|
|
Before Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 8.4 KiB |
|
@ -39,36 +39,41 @@ using this client as natural as any other you're used to.
|
|||
As such, let's look at Renga!
|
||||
|
||||
<a NAME="logging">
|
||||
<font FACE="Courier New"><b><u><h1 ALIGN=CENTER>Logging In</h1></u></b></font>
|
||||
<center><font SIZE=-2><a HREF="#top">top</a></font></center>
|
||||
<h1 ALIGN=CENTER>Logging In</h1>
|
||||
<a href="#top">top</a>
|
||||
|
||||
<p>When you first run Renga, you will be presented with a window like this:
|
||||
<p>When you first run Renga, you will be presented with a window like this:</p>
|
||||
|
||||
<p ALIGN=CENTER><img SRC="login-pane.png">
|
||||
<div ALIGN=CENTER><img SRC="login-pane.png"></div>
|
||||
|
||||
<p>If you've used XMPP in the past then this should look familiar to you. Just
|
||||
enter the login information you've always used. If you haven't seen the nickname field
|
||||
before, this is used as a friendly name only for yourself in chat windows. Nobody else will see this
|
||||
information so it's not critical. As an expert, you'll probably want to check the Auto-login box as well.
|
||||
information so it's not critical. As an expert, you'll probably want to check the Auto-login box as well.</p>
|
||||
|
||||
<p>If you're new to XMPP, this is an important step in getting online. You'll want
|
||||
to decide on your official username! Here is what all the fields mean:
|
||||
<p>If you're new to XMPP, you need to create an account. This is an important step in getting online.
|
||||
You'll want to decide on your official username! Here is what all the fields mean:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Nickname:</b> This is a friendly name for yourself. It'll only be used in your chat windows and no one else will see it, so feel free to change it to fit your mood.
|
||||
<li><b>Username:</b> This is the username you will be officially referred to with.
|
||||
<li><b>Password:</b> Your password.
|
||||
<li><b>Create This Account:</b> If this is a new account for you, check this box and the account will be created (if possible) before you log in. In the future, when you login with the same username, you want to keep this box unchecked.
|
||||
<li><b>Auto-login:</b> This is a handy option if you just want Renga to log you in with your last entered username/password automatically. It's quicker than entering in your information all the time, but also less secure. If you're on a single-user machine, this is a recommended option.
|
||||
<li><b>Nickname:</b> This is a friendly name for yourself. Other people will see it, and you can change it each time you connect.</li>
|
||||
<li><b>Username:</b> This is the username you will be officially referred to with. You cannot change this once you picked one.</li>
|
||||
<li><b>Password:</b> Your password.</li>
|
||||
<li><b>Auto-login:</b> This is a handy option if you just want Renga to log you in with your
|
||||
last entered username/password automatically. It's quicker than entering in your
|
||||
information all the time, but also less secure. If you're on a single-user machine, this
|
||||
is a recommended option.</li>
|
||||
</ul>
|
||||
|
||||
<p>There are also some rules you should know about how Auto-login works in specific situations. Here they are:
|
||||
<p>To create your account, use the "Create an account" button and follow the instructions. You will
|
||||
need to pick a server to host your account, or you can let Renga select one for you.</p>
|
||||
|
||||
<p>There are also some rules you should know about how Auto-login works in specific situations. Here they are:</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Normal login:</b> In the "normal" situation, running Renga will cause the login process to initiate for the last username/password you tried logging in with.
|
||||
<li><b>Failed login:</b> If Auto-login fails to log you in because your last username/password is, or is no longer, valid then the login screen will appear and allow you to correct any mistakes.
|
||||
<li><b>Multiple logins:</b> If you already have another instance of Renga running on the machine, Auto-login will not run for that second instance. The reason is that logging you in with the same name as the one already running will cause the first to disconnect. Not convenient!
|
||||
<li><b>Change login:</b> If you use Auto-login, but you want to change your username and log in under another address (stalkers again?), simply logout by selecting "Log Off" from the File menu.
|
||||
<li><b>Normal login:</b> In the "normal" situation, running Renga will cause the login process to initiate for the last username/password you tried logging in with.</li>
|
||||
<li><b>Failed login:</b> If Auto-login fails to log you in because your last username/password is, or is no longer, valid then the login screen will appear and allow you to correct any mistakes.</li>
|
||||
<li><b>Multiple logins:</b> If you already have another instance of Renga running on the machine, Auto-login will not run for that second instance. Having two copies of the application running for the same account on the same machine would just be confusing.</li>
|
||||
<li><b>Change login:</b> If you use Auto-login, but you want to change your username and log in under another address (stalkers again?), simply logout by selecting "Log Off" from the File menu. </li>
|
||||
</ul>
|
||||
|
||||
<a NAME="im">
|
||||
|
@ -77,9 +82,9 @@ to decide on your official username! Here is what all the fields mean:
|
|||
|
||||
<p>Congratulations, you're logged in! Your login window has now transformed and looks like:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="logged-in.png" HSPACE=5><img SRC="logged-in-with-friends.png">
|
||||
<p ALIGN=CENTER><img SRC="logged-in-with-friends.png"></p>
|
||||
|
||||
<p>This is your buddy list, or "roster" as the XMPP group likes to call it. This is where you'll see your friends and family, their online status and more. If you just created your account, it will appear empty. So let's fill it up!
|
||||
<p>On the left is your buddy list, or "roster" as the XMPP group likes to call it. This is where you'll see your friends and family, their online status and more. If you just created your account, it will appear empty. So let's fill it up!
|
||||
|
||||
<p>There are two basic concepts when dealing with this list: <i>roster</i> and <i>presence</i>. While many external chat systems
|
||||
treat these concepts as one entity, XMPP keeps them separate. What you need to rememeber is that even if you add a friend to your list (<i>roster</i>), you don't automatically begin seeing their online status (<i>presence</i>). You need to ask for it.
|
||||
|
@ -105,56 +110,17 @@ Next, you want to add a nickname, a casual name that tends to be less cryptic th
|
|||
If you've gotten acceptance, you've probably already noticed that the user is now either in the online or offline list and their color is green (dark or light) or red. Bet you can't guess what this means? ;-)
|
||||
Right! The greens are for online, red is for offline. The <b><font COLOR=005500>darker shade of green</font></b> means the user is online, but is either away from their computer or does not want to be distrubed. <b><font COLOR=00CC00>The lighter shade of green</font></b> signifies their open for chat. Usually, after being granted presence, the user is online because, well, how else could they have accepted your presence?
|
||||
|
||||
<p>Great! So the user is online. Now what? You want to chat is what! Isn't that why you downloaded this program? :-) There are two options for chatting:
|
||||
<p>Great! So the user is online. Now what? You want to chat is what! Isn't that why you downloaded this program? :-)</p>
|
||||
|
||||
<ul>
|
||||
<li><b>Message-based or ICQ-style chat:</b> ICQ users will be familiar with this, and frankly no one else. Message-based chat lets you send a single message and then your window will go away. It's quick, it's easy and it can be annoying for the receiving user. :-) ICQ users should also note one difference in implementation: XMPP will queue multiple, unanswered messages in the same window alleviating the pain of a billion windows to read through and answer separately! The ICQ-style of messages can be handy for quick messages that require no, or little response. They can be handy because the window goes away when you respond to one.
|
||||
<li><b>Scrolling chat or AOL-style chat:</b> This is what most users are familiar with as it's used by AOL, and uncommonly ICQ. It's a one-on-one chat, you and one other user, with messages scrolling on the top of the window and an area below to enter your next message. This is the most pleasant way to communicate long conversations.
|
||||
</ul>
|
||||
<p>The chatting happens on the right part of the main window. At the bottom is a text input where you can send your messages. Above that is a list of previous messages for the current conversation. To chat with different people, simple select them on the left panel.</p>
|
||||
|
||||
<p>While the messaging and chat paradigms are different, the window will look mostly the same:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="chat-screen.png">
|
||||
|
||||
<p>Make sure you understand that little note on the lower left. When newlines are allowed, simply hitting <b>Enter</b> inserts a raw newline into your message, otherwise it sends the message. <b>Command-Enter</b> always performs the opposite operation.
|
||||
<p>If you want to send a long message with multiple lines, remember to use <b>Command-Enter</b> to insert a newline (since pressing <b>Enter</b> will send the current message to the other user). If you are not happy with that, you can exchange the two in the preferences window.</p>
|
||||
|
||||
<a NAME="chatfeatures">
|
||||
<font FACE="Courier New"><b><u><h1 ALIGN=CENTER>Chat Features</h1></u></b></font>
|
||||
<center><font SIZE=-2><a HREF="#top">top</a></font></center>
|
||||
|
||||
|
||||
<p>There are several fun chat features available in Renga. The first you might notice are <i>Canned Quips</i>. These are pre-generated messages that you define. You usually want to type phrases and exclamations you say a lot. As an example, these are my usual quips:
|
||||
|
||||
<ul>
|
||||
<li>For more information about Rapture In Venice, go to http://www.users.uswest.net/~jblanco.
|
||||
<li>Sup.
|
||||
<li>What's that? You say Renga still hasn't crashed yet? Not surprising.
|
||||
<li>blue screen of death
|
||||
</ul>
|
||||
|
||||
<p>I've included four messages, but you can use up to nine. Each message is associated with a <b>Command-number</b> key, so <i>"Message #4"</i> is triggered with <b>Command-4</b>. So easy. :-)
|
||||
You can edit your canned quips in the preferences panel under the <i>"Messaging"</i> tab.
|
||||
|
||||
<p>You'll notice that next to each message there is a checkbox as seen below:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="preferences-panel.png">
|
||||
|
||||
<p>This is the <i>quick-fire</i> option and it affects how messages are included in your chat session:
|
||||
|
||||
<ul>
|
||||
<li><b>When checked:</b> This means the message will be <i>"quick-fired."</i> Basically, when you hit the hotkey the message will be sent with no regard to what you already have in your outgoing message box. It's great for when you need to send the same message many times but don't want to cut-and-paste your current message to send it.
|
||||
<li><b>When unchecked:</b> This means the message will be pasted in your outgoing message box wherever your cursor currently is.
|
||||
</ul>
|
||||
|
||||
<p>As an example, I'm always having to tell people where my website is (whether they ask or not...heh heh)! I've set up my first message for quick-fire. So, if I'm in the middle of a sentence and I see the need to send the information out immediately, Command-1 sends it. Notice how my current message is unaffected:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="quick-fired-message.png"><br><font SIZE=-2>- I've just pressed <b>Command-1</b> -</font>
|
||||
|
||||
<p>On the other hand, my fourth quip, "blue screen of death," is not a standalone message. The problem is I always have to type this phrase when explaining to strangers how much better <font COLOR=BLUE>B</font><font COLOR=RED>e</font>OS is than Windows! Seeing that I've had carpal-tunnel problems in the past typing this exact phrase thousands of times a day, I did't make it a quick-fire and instead include it in messages that are part of larger sentences as shown:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="non-quick-fired-message.png"><br><font SIZE=-2>- I've just pressed <b>Command-4</b> -</font>
|
||||
|
||||
<p>Renga also supports what's known as <i>"/me syntax"</i>. This is when you want to type a message but want to make it as part of an action. It's almost always used for humorous purposes. Here's an example:
|
||||
<p>Renga supports what's known as <i>"/me syntax"</i>. This is when you want to type a message but want to make it as part of an action. It's almost always used for humorous purposes. Here's an example:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="me-syntax.png"><br><font SIZE=-2>- Note how /me makes my message an action -</font>
|
||||
|
||||
|
@ -162,18 +128,9 @@ You can edit your canned quips in the preferences panel under the <i>"Messaging"
|
|||
|
||||
<ul><tt>/me <i>your action</i></tt></ul>
|
||||
|
||||
<p>Here's exactly how I generated the previous example:
|
||||
<p>Here's exactly how I generated the previous example:
|
||||
|
||||
<ul><tt>/me is saddened by your remark. :(</tt></ul>
|
||||
|
||||
<p>In addition to <i>"/me syntax"</i>, Renga supports other IRC-like and not so IRC-like <i>"slash"</i> commands:
|
||||
|
||||
<ul>
|
||||
<li><b>/me message</b> - As stated above, instead of sending a message you convey an action you are fictionally or non-fictionally performing.
|
||||
<li><b>/alert message</b> - When you have an urgent need to get your recipient's attention, precede your usual message with /alert and the window will be activated on the receiver's side as well as a sound played.
|
||||
</ul>
|
||||
|
||||
<p>For some, <b>/alert</b> can be annoying as it can easily be abused. This feature can be turned off in the <i>"Chat Rules"</i> section of the preferences panel.
|
||||
<ul><tt>/me does a barrel roll</tt></ul>
|
||||
|
||||
<p>A feature that is new with Jabber for <font COLOR=RED>B</font><font COLOR=BLUE>e</font>OS v1.2 is chat history. In any chat window, hold down the Command key and use the up and down arrows to scroll through the history of your last 50 messages. This is handy when your buddy misses a message or you just want to accentuate it! ;-)
|
||||
|
||||
|
@ -215,24 +172,20 @@ You can edit your canned quips in the preferences panel under the <i>"Messaging"
|
|||
</ul>
|
||||
|
||||
<a NAME="transports">
|
||||
<font FACE="Courier New"><b><u><h1 ALIGN=CENTER>XMPP Transports: Gateways to External Chat Systems</h1></u></b></font>
|
||||
<center><font SIZE=-2><a HREF="#top">top</a></font></center>
|
||||
<h1 ALIGN=CENTER>XMPP Transports: Gateways to External Chat Systems</h1>
|
||||
<a HREF="#top">top</a>
|
||||
|
||||
<p>If XMPP was just another new chat system, then everyone would just be a little more ill. :-) But, in fact, XMPP is not. While it does support it's own network of users who can communicate with each other along with other things like sending files, XMPP allows it's users to talk directly with users of other chat clients! A little discussion about how it's done is in order.
|
||||
<p>If XMPP was just another new chat system, then everyone would just be a little more ill. :-)
|
||||
But, in fact, XMPP is not. While it does support it's own network of users who can communicate
|
||||
with each other along with other things like sending files, XMPP allows it's users to talk directly
|
||||
with users of other chat clients! A little discussion about how it's done is in order.</p>
|
||||
|
||||
<p>If you're familiar with other <font COLOR=BLUE>B</font><font COLOR=RED>e</font>OS apps such as GimICQ and BeAIM, you know that getting unofficial chat clients for popular chat systems is not a new thing. Anything on the Internet is hackable, so there's no reason to believe AOL and ICQ have secured their networks better than any hackable e-commerce system.
|
||||
Well, these unofficial clients are just that -- clients. GimICQ only lets you talk to other ICQ users, BeAIM the same with AOL users. However, XMPP lets you talk to both of these systems and more!
|
||||
|
||||
<p>As with GimICQ and BeAIM, the idea is that you have a login to the external system already. What you do is <i>register</i> your login information with the XMPP transport that's responsible for talking with that system. Once complete, you can chat with AOL users just as easily as you can with XMPP users themselves.
|
||||
|
||||
<p>When you've registered with a transport, Renga has now become just like a login to another chat service. If you registered as "BeMan" under AOL, then anyone on AOL, or XMPP, who sends a message to your AOL address will reach you on your Renga client. Basically, you are using one client with one main login (your Jabber ID) and extra virtual logins (your AOL, etc... logins). Everything is completely transparent from now on.
|
||||
|
||||
<p>Lets try it. Go to your preferences panel and click the <i>"Transports"</i> tab. Let's pretend you want to register with the AOL transport (Feel free to go ahead and register with whichever transport you'd like to right now).
|
||||
Assuming your username and password is <i>"BjorkLover"</i> and <i>"fulloflove"</i>, select the AOL transport and enter the information in as above:
|
||||
|
||||
<p ALIGN=CENTER><img SRC="aol-registering.png">
|
||||
|
||||
<p>Hit "Register".
|
||||
<p>If you're familiar with other <font COLOR=BLUE>B</font><font COLOR=RED>e</font>OS apps such as
|
||||
GimICQ and BeAIM, you know that getting unofficial chat clients for popular chat systems is not a
|
||||
new thing. Anything on the Internet is hackable, so there's no reason to believe AOL and ICQ have
|
||||
secured their networks better than any hackable e-commerce system.
|
||||
Well, these unofficial clients are just that -- clients. GimICQ only lets you talk to other ICQ
|
||||
users, BeAIM the same with AOL users. However, XMPP lets you talk to both of these systems and more!</p>
|
||||
|
||||
<p>Here's where I have to give my disclaimers. You may remember the very public chat wars that went on between AOL Instant Messenger and MSN Messenger a while back. Basically, Microsoft was trying to hack the AOL chat protocol so that MSN Messenger users could talk to AOL users and vice-versa. AOL would have none of it and in what was a hilarious coding war, the cycle began:
|
||||
|
||||
|
@ -255,24 +208,22 @@ if a transport doesn't work with Renga, it's probabably not the client's fault.
|
|||
|
||||
<p>Given a happy world where all transports work, you'll also want to add these external users to your roster. This is done the same as when adding XMPP users, except you want to change the menu to reflect what chat system they're using. Notice that the instructions on the "Add New Buddy" screen will change as you flip through the systems.
|
||||
|
||||
<p>The most common use of these bridging technologies is to access IRC discussion channels. Since
|
||||
the IRC protocol does not change a lot, this seems to work better than bridging with other places.</p>
|
||||
|
||||
<p>To access an IRC channel, you need to mangle its name a bit. For example to join the #haiku
|
||||
channel on irc.oftc.net you would use the address #haiku%irc.oftc.net@irc.jabberfr.org. Once joined,
|
||||
this works like any other XMPP group chat.</p>
|
||||
|
||||
<a NAME="further">
|
||||
<font FACE="Courier New"><b><u><h1 ALIGN=CENTER>Further Exploration</h1></u></b></font>
|
||||
<center><font SIZE=-2><a HREF="#top">top</a></font></center>
|
||||
|
||||
<p>There are several other features that XMPP offers, as well as many nuances that are specific to Renga. The big one being changing your own online status. But I'll leave these all for you to explore! :-) If it's not easy enough to figure out on your own, then Rapture In Venice has failed you as a developer. So be confident!
|
||||
|
||||
<p>If you're a registered user, feel free to ask me for help. Rapture In Venice's contact information can be found below, but since this is a chat client the most fun way to get in touch is through XMPP! As I said before, my Jabber ID is rapture@jabber.org. I hope this user manual is a friendly springboard to the world of XMPP and Renga. Have fun!
|
||||
<p>If you run into problems (or just want to say hi), you can find the Renga developers on our XMPP chat room renga@chat.jabberfr.org.</p>
|
||||
|
||||
<a NAME="cause">
|
||||
<font FACE="Courier New"><b><u><h1 ALIGN=CENTER>The Rapture In Venice Cause</h1></u></b></font>
|
||||
<center><font SIZE=-2><a HREF="#top">top</a></font></center>
|
||||
|
||||
<p>Rapture In Venice was born to help <font COLOR=BLUE>B</font><font COLOR=RED>e</font>OS become the #1 desktop OS on the consumer market. A lofty goal, but Be, Inc. started it. :-) As such, Rapture In Venice needs your support. If we're going to be able to create top quality applications that help bolster the existing <font COLOR=BLUE>B</font><font COLOR=RED>e</font>OS library, we need you to support us back.
|
||||
Below is how you can get in contact with Rapture In Venice with question, tips, business proposals and whatever else you may need. As always, thank you for choosing Rapture In Venice.
|
||||
|
||||
<ul>
|
||||
<p>Rapture In Venice<br>2663 S. Moore Dr. #B<br>Lakewood CO 80227<br>(720) 962-0898<br><a HREF="mailto:jblanco@uswest.net">jblanco@uswest.net</a><br><a HREF="http://www.users.uswest.net/~jblanco">http://www.users.uswest.net/~jblanco</a>
|
||||
</ul>
|
||||
<p>I hope this user manual is a friendly springboard to the world of XMPP and Renga. Have fun!
|
||||
|
||||
</body>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Adrien Destugues <pulkomandy@pulkomandy.tk>
|
||||
* Copyright 2019-2021 Adrien Destugues <pulkomandy@pulkomandy.tk>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
@ -17,18 +17,23 @@
|
|||
|
||||
#include "ui/HVIFUtil.h"
|
||||
|
||||
|
||||
BBitmap *BookmarkItem::_online_icon = NULL;
|
||||
BBitmap *BookmarkItem::_unknown_icon = NULL;
|
||||
|
||||
|
||||
BookmarkItem::BookmarkItem(const gloox::JID& userid, BString name)
|
||||
: BStringItem(name)
|
||||
, _userid(userid)
|
||||
, fFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BookmarkItem::~BookmarkItem() {
|
||||
}
|
||||
|
||||
|
||||
void BookmarkItem::DrawItem(BView *owner, BRect frame, __attribute__((unused)) bool complete)
|
||||
{
|
||||
// get online status
|
||||
|
@ -42,7 +47,7 @@ void BookmarkItem::DrawItem(BView *owner, BRect frame, __attribute__((unused)) b
|
|||
BRect selectionFrame = frame;
|
||||
selectionFrame.left = 0;
|
||||
if (IsSelected()) {
|
||||
owner->SetHighUIColor(B_LIST_SELECTED_BACKGROUND_COLOR);
|
||||
owner->SetHighUIColor(B_LIST_SELECTED_BACKGROUND_COLOR);
|
||||
} else {
|
||||
owner->SetHighColor(owner->ViewColor());
|
||||
}
|
||||
|
@ -67,12 +72,16 @@ void BookmarkItem::DrawItem(BView *owner, BRect frame, __attribute__((unused)) b
|
|||
if (name.IsEmpty())
|
||||
name = _userid.full().c_str();
|
||||
|
||||
// TODO: set font color based on recent activity
|
||||
if (IsSelected()) {
|
||||
owner->SetHighUIColor(B_LIST_SELECTED_ITEM_TEXT_COLOR);
|
||||
} else {
|
||||
// When the room is active, we can clear the flags
|
||||
fFlags = 0;
|
||||
} else if (fFlags & NICKNAME_HIGHLIGHT)
|
||||
owner->SetHighUIColor(B_FAILURE_COLOR);
|
||||
else if (fFlags & ACTIVITY)
|
||||
owner->SetHighUIColor(B_SUCCESS_COLOR);
|
||||
else
|
||||
owner->SetHighUIColor(B_LIST_ITEM_TEXT_COLOR);
|
||||
}
|
||||
|
||||
// construct text positioning, keeping space for the icon on the left
|
||||
font_height fh;
|
||||
|
@ -82,6 +91,7 @@ void BookmarkItem::DrawItem(BView *owner, BRect frame, __attribute__((unused)) b
|
|||
owner->DrawString(name, BPoint(frame.left + height, frame.bottom - fh.descent));
|
||||
}
|
||||
|
||||
|
||||
void BookmarkItem::Update(BView *owner, const BFont *font)
|
||||
{
|
||||
BListItem::Update(owner, font);
|
||||
|
@ -107,6 +117,7 @@ void BookmarkItem::Update(BView *owner, const BFont *font)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const gloox::JID& BookmarkItem::GetUserID() const {
|
||||
return _userid;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
/*
|
||||
* BookmarkkItem.h
|
||||
* Copyright (C) 2019 Adrien Destugues <pulkomandy@pulkomandy.tk>
|
||||
* Copyright 2019-2021 Adrien Destugues <pulkomandy@pulkomandy.tk>
|
||||
*
|
||||
* Distributed under terms of the MIT license.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
* Bookmark (group chat) entries of the RosterView widget
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Bitmap.h>
|
||||
|
@ -18,19 +14,29 @@
|
|||
|
||||
#include "../jabber/UserID.h"
|
||||
|
||||
|
||||
class BookmarkItem : public BStringItem {
|
||||
public:
|
||||
BookmarkItem(const gloox::JID& userid, BString name);
|
||||
~BookmarkItem();
|
||||
BookmarkItem(const gloox::JID& userid, BString name);
|
||||
~BookmarkItem();
|
||||
|
||||
void DrawItem(BView *owner, BRect frame, bool complete = false);
|
||||
virtual void Update(BView *owner, const BFont *font);
|
||||
|
||||
const gloox::JID& GetUserID() const;
|
||||
void DrawItem(BView *owner, BRect frame, bool complete = false);
|
||||
virtual void Update(BView *owner, const BFont *font);
|
||||
|
||||
const gloox::JID& GetUserID() const;
|
||||
|
||||
enum Flags {
|
||||
NICKNAME_HIGHLIGHT = 1,
|
||||
ACTIVITY = 2
|
||||
};
|
||||
|
||||
void SetFlag(uint32 flag) { fFlags |= flag; }
|
||||
|
||||
private:
|
||||
const gloox::JID _userid;
|
||||
|
||||
static BBitmap *_online_icon;
|
||||
static BBitmap *_unknown_icon;
|
||||
const gloox::JID _userid;
|
||||
|
||||
int32 fFlags;
|
||||
|
||||
static BBitmap* _online_icon;
|
||||
static BBitmap* _unknown_icon;
|
||||
};
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "../jabber/Messages.h"
|
||||
|
||||
BuddyInfoWindow::BuddyInfoWindow(UserID *querying_user)
|
||||
: BWindow(BRect(0, 0, 0, 0), "User Information", B_TITLED_WINDOW,
|
||||
: BWindow(BRect(0, 0, 0, 0), "Buddy Information", B_TITLED_WINDOW,
|
||||
B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS)
|
||||
, fJID(querying_user->Handle().c_str())
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "../jabber/TalkManager.h"
|
||||
|
||||
ChangeNameWindow::ChangeNameWindow(const gloox::JID& changing_user, BString oldName)
|
||||
: BWindow(BRect(0, 0, 100, 100), "Changing User Name", B_TITLED_WINDOW,
|
||||
: BWindow(BRect(0, 0, 100, 100), "Change Buddy Name", B_TITLED_WINDOW,
|
||||
B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS),
|
||||
_changing_user(changing_user)
|
||||
{
|
||||
|
@ -29,15 +29,15 @@ ChangeNameWindow::ChangeNameWindow(const gloox::JID& changing_user, BString oldN
|
|||
AddChild(full_view);
|
||||
|
||||
BStringView *query = new BStringView(NULL, "Specify the new \"Nickname\" you'd like to use:");
|
||||
|
||||
|
||||
_handle = new BTextControl(NULL, NULL, "", NULL);
|
||||
|
||||
|
||||
if (BlabberSettings::Instance()->Data("last-talk-sent-to")) {
|
||||
_handle->SetText(BlabberSettings::Instance()->Data("last-talk-sent-to"));
|
||||
} else {
|
||||
_handle->SetText("somebody@jabber.org");
|
||||
}
|
||||
|
||||
|
||||
BButton *cancel = new BButton("cancel", "Nevermind", new BMessage(JAB_CANCEL));
|
||||
cancel->SetTarget(this);
|
||||
|
||||
|
@ -77,7 +77,7 @@ void ChangeNameWindow::MessageReceived(BMessage *msg) {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// re-add to roster
|
||||
JabberSpeak::Instance()->SetFriendlyName(_changing_user, _handle->Text());
|
||||
|
||||
|
|
|
@ -1057,6 +1057,26 @@ void BlabberMainWindow::SetCustomStatus(string status) {
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
BlabberMainWindow::FlagBookmarkItem(const gloox::JID& room, uint32 flags)
|
||||
{
|
||||
int32 pos = _roster->FindBookmark(room);
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
auto* item = dynamic_cast<BookmarkItem*>(_roster->FullListItemAt(pos));
|
||||
item->SetFlag(flags);
|
||||
|
||||
// Need to redo a FindItem because there is no FullListInvalidateItem, so we need to convert the
|
||||
// FullList index returned by FindBookmark to a BListView index...
|
||||
int32 listIndex = _roster->IndexOf(item);
|
||||
// ... and this can fail if the item is in a collapsed part of the list
|
||||
if (listIndex < 0)
|
||||
return;
|
||||
_roster->InvalidateItem(listIndex);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BlabberMainWindow::AddTalkView(TalkView* view)
|
||||
{
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <TextControl.h>
|
||||
#include <Window.h>
|
||||
|
||||
#include <gloox/instantmucroom.h>
|
||||
|
||||
#include "ui/PictureView.h"
|
||||
|
||||
#include "jabber/RosterView.h"
|
||||
|
@ -41,6 +43,7 @@ public:
|
|||
void ShowLogin();
|
||||
void SetCustomStatus(std::string status);
|
||||
|
||||
void FlagBookmarkItem(const gloox::JID& room, uint32 flags);
|
||||
|
||||
protected:
|
||||
BlabberMainWindow(BRect frame);
|
||||
|
|
119
ui/TalkView.cpp
|
@ -14,7 +14,6 @@
|
|||
#include <GroupLayout.h>
|
||||
#include <GroupView.h>
|
||||
#include <LayoutBuilder.h>
|
||||
#include <Notification.h>
|
||||
#include <Roster.h>
|
||||
#include <storage/Path.h>
|
||||
#include <SplitView.h>
|
||||
|
@ -65,31 +64,31 @@ TalkView::TalkView(const gloox::JID *user, string group_room,
|
|||
_status_view = new StatusView();
|
||||
_status_view->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
||||
|
||||
_chat = new ChatTextView("chat", B_WILL_DRAW | B_FRAME_EVENTS);
|
||||
_chat_scroller = new BScrollView("chat_scroller", _chat, B_WILL_DRAW, false, true);
|
||||
_chat->TargetedByScrollView(_chat_scroller);
|
||||
_chat->SetFontSize(12.0);
|
||||
_chat->SetWordWrap(true);
|
||||
_chat->SetStylable(true);
|
||||
_chat->MakeEditable(false);
|
||||
timeline = new ChatTextView("timeline", B_WILL_DRAW | B_FRAME_EVENTS);
|
||||
timelineScroller = new BScrollView("timelineScroller", timeline, B_WILL_DRAW, false, true);
|
||||
timeline->TargetedByScrollView(timelineScroller);
|
||||
timeline->SetFontSize(12.0);
|
||||
timeline->SetWordWrap(true);
|
||||
timeline->SetStylable(true);
|
||||
timeline->MakeEditable(false);
|
||||
|
||||
// message control
|
||||
rgb_color text_color = ui_color(B_PANEL_TEXT_COLOR);
|
||||
BFont text_font(be_plain_font);
|
||||
|
||||
_message = new BTextView("message", &text_font, &text_color, B_WILL_DRAW);
|
||||
_message_scroller = new BScrollView("message_scroller", _message, B_WILL_DRAW, false, false);
|
||||
messageInput = new BTextView("messageInput", &text_font, &text_color, B_WILL_DRAW);
|
||||
messageInputScroller = new BScrollView("messageInputScroller", messageInput, B_WILL_DRAW, false, false);
|
||||
|
||||
_message->TargetedByScrollView(_message_scroller);
|
||||
_message->SetWordWrap(true);
|
||||
messageInput->TargetedByScrollView(messageInputScroller);
|
||||
messageInput->SetWordWrap(true);
|
||||
|
||||
// editing filter for messaging
|
||||
_message->AddFilter(new EditingFilter(_message, this));
|
||||
messageInput->AddFilter(new EditingFilter(messageInput, this));
|
||||
|
||||
// handle splits
|
||||
BSplitView* _split_talk = new BSplitView(B_VERTICAL);
|
||||
_split_talk->AddChild(_chat_scroller);
|
||||
_split_talk->AddChild(_message_scroller);
|
||||
_split_talk->AddChild(timelineScroller);
|
||||
_split_talk->AddChild(messageInputScroller);
|
||||
_split_talk->SetItemWeight(0, 12, false);
|
||||
_split_talk->SetItemWeight(1, 1, false);
|
||||
_split_talk->SetSpacing(0);
|
||||
|
@ -119,7 +118,7 @@ TalkView::TalkView(const gloox::JID *user, string group_room,
|
|||
AddChild(_split_group_people);
|
||||
AddChild(_status_view);
|
||||
|
||||
_message->MakeFocus(true);
|
||||
messageInput->MakeFocus(true);
|
||||
|
||||
// generate window title
|
||||
char buffer[1024];
|
||||
|
@ -185,8 +184,8 @@ void TalkView::FrameResized(float width, float height)
|
|||
{
|
||||
BView::FrameResized(width, height);
|
||||
|
||||
BRect chat_rect = _chat->Frame();
|
||||
BRect message_rect = _message->Frame();
|
||||
BRect chat_rect = timeline->Frame();
|
||||
BRect message_rect = messageInput->Frame();
|
||||
|
||||
chat_rect.OffsetTo(B_ORIGIN);
|
||||
message_rect.OffsetTo(B_ORIGIN);
|
||||
|
@ -194,11 +193,11 @@ void TalkView::FrameResized(float width, float height)
|
|||
chat_rect.InsetBy(2.0, 2.0);
|
||||
message_rect.InsetBy(2.0, 2.0);
|
||||
|
||||
_chat->SetTextRect(chat_rect);
|
||||
_message->SetTextRect(message_rect);
|
||||
timeline->SetTextRect(chat_rect);
|
||||
messageInput->SetTextRect(message_rect);
|
||||
|
||||
_chat->Invalidate();
|
||||
_chat_scroller->Invalidate();
|
||||
timeline->Invalidate();
|
||||
timelineScroller->Invalidate();
|
||||
}
|
||||
|
||||
|
||||
|
@ -281,7 +280,7 @@ void TalkView::MessageReceived(BMessage *msg) {
|
|||
}
|
||||
|
||||
case JAB_CHAT_SENT: {
|
||||
string message = _message->Text();
|
||||
string message = messageInput->Text();
|
||||
|
||||
// eliminate empty messages
|
||||
if (message.empty())
|
||||
|
@ -292,17 +291,17 @@ void TalkView::MessageReceived(BMessage *msg) {
|
|||
// we go through main app?
|
||||
gloox::MUCRoom* room = (gloox::MUCRoom*)TalkManager::Instance()
|
||||
->IsExistingWindowToGroup(GetGroupRoom());
|
||||
room->send(_message->Text());
|
||||
room->send(messageInput->Text());
|
||||
} else
|
||||
_session->send(_message->Text());
|
||||
_session->send(messageInput->Text());
|
||||
|
||||
// user part
|
||||
NewMessage(message);
|
||||
|
||||
// GUI
|
||||
_message->ScrollToOffset(0);
|
||||
_message->SetText("");
|
||||
_message->MakeFocus(true);
|
||||
messageInput->ScrollToOffset(0);
|
||||
messageInput->SetText("");
|
||||
messageInput->MakeFocus(true);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -339,7 +338,7 @@ string TalkView::OurRepresentation() {
|
|||
}
|
||||
|
||||
|
||||
void TalkView::AddToTalk(string username, string message, user_type type) {
|
||||
void TalkView::AddToTalk(string username, string message, user_type type, bool highlight) {
|
||||
// transform local identity
|
||||
if (IsGroupChat() && type == LOCAL)
|
||||
username = _group_username;
|
||||
|
@ -373,24 +372,24 @@ void TalkView::AddToTalk(string username, string message, user_type type) {
|
|||
rgb_color blue = {0, 0, 255, 255};
|
||||
rgb_color red = {255, 0, 0, 255};
|
||||
rgb_color orange = {205, 113, 57, 255};
|
||||
rgb_color message_color = ui_color(B_PANEL_TEXT_COLOR);
|
||||
rgb_color bg_color = ui_color(B_PANEL_BACKGROUND_COLOR);
|
||||
rgb_color highlight = mix_color(message_color, orange, 200);
|
||||
rgb_color messageColor = ui_color(B_DOCUMENT_TEXT_COLOR);
|
||||
rgb_color backgroundColor = ui_color(B_DOCUMENT_BACKGROUND_COLOR);
|
||||
rgb_color highlightColor = mix_color(messageColor, orange, 200);
|
||||
|
||||
// TODO figure out a goood threshold here, this seems to work for me,
|
||||
// but it may not for others
|
||||
if (abs(blue.Brightness() - bg_color.Brightness()) < 45)
|
||||
if (abs(blue.Brightness() - backgroundColor.Brightness()) < 45)
|
||||
blue = { 128, 137, 252, 255 };
|
||||
|
||||
if (abs(red.Brightness() - bg_color.Brightness()) < 45)
|
||||
if (abs(red.Brightness() - backgroundColor.Brightness()) < 45)
|
||||
red = { 249, 84, 87, 255 };
|
||||
|
||||
// some runs to play with
|
||||
text_run tr_thick_blue = {0, thick, blue};
|
||||
text_run tr_thick_red = {0, thick, red};
|
||||
text_run tr_thick_black = {0, thick, message_color};
|
||||
text_run tr_thick_highlight = {0, thick, highlight};
|
||||
text_run tr_thin_black = {0, thin, message_color};
|
||||
text_run tr_thick_black = {0, thick, messageColor};
|
||||
text_run tr_thick_highlight = {0, thick, highlightColor};
|
||||
text_run tr_thin_black = {0, thin, messageColor};
|
||||
|
||||
// some run array to play with (simple)
|
||||
text_run_array tra_thick_blue = {1, {tr_thick_blue}};
|
||||
|
@ -409,18 +408,18 @@ void TalkView::AddToTalk(string username, string message, user_type type) {
|
|||
BString messageString = BString(message.c_str());
|
||||
|
||||
if (BlabberSettings::Instance()->Tag("show-timestamp"))
|
||||
_chat->Insert(_chat->TextLength(), time_stamp.c_str(), time_stamp.size(), &tra_thin_black);
|
||||
timeline->Insert(timeline->TextLength(), time_stamp.c_str(), time_stamp.size(), &tra_thin_black);
|
||||
|
||||
if (messageString.StartsWith("/me ")) {
|
||||
messageString.ReplaceFirst("/me", username.c_str());
|
||||
if (type == MAIN_RECIPIENT)
|
||||
_chat->Insert(_chat->TextLength(), messageString, messageString.Length(), &tra_thick_blue);
|
||||
timeline->Insert(timeline->TextLength(), messageString, messageString.Length(), &tra_thick_blue);
|
||||
else
|
||||
_chat->Insert(_chat->TextLength(), messageString, messageString.Length(), &tra_thick_red);
|
||||
timeline->Insert(timeline->TextLength(), messageString, messageString.Length(), &tra_thick_red);
|
||||
|
||||
_chat->Insert(_chat->TextLength(), "\n", 1, &tra_thin_black);
|
||||
timeline->Insert(timeline->TextLength(), "\n", 1, &tra_thin_black);
|
||||
if (type == LOCAL)
|
||||
_chat->ScrollTo(0.0, _chat->Bounds().bottom);
|
||||
timeline->ScrollTo(0.0, timeline->Bounds().bottom);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -429,35 +428,29 @@ void TalkView::AddToTalk(string username, string message, user_type type) {
|
|||
if (!IsGroupChat() || !BlabberSettings::Instance()->Tag("exclude-groupchat-sounds"))
|
||||
SoundSystem::Instance()->PlayMessageSound();
|
||||
|
||||
_chat->Insert(_chat->TextLength(), username.c_str(), username.size(), &tra_thick_blue);
|
||||
_chat->Insert(_chat->TextLength(), ": ", 2, &tra_thin_black);
|
||||
timeline->Insert(timeline->TextLength(), username.c_str(), username.size(), &tra_thick_blue);
|
||||
timeline->Insert(timeline->TextLength(), ": ", 2, &tra_thin_black);
|
||||
|
||||
// Highlight messages when they mention the nickname
|
||||
if (messageString.IFindFirst(_group_username.c_str()) != B_ERROR) {
|
||||
// NOTE: This will erronously pop up for backlog messages aswell, can be improved once we have MAM
|
||||
BNotification notification(B_INFORMATION_NOTIFICATION);
|
||||
notification.SetGroup(GetGroupRoom().c_str());
|
||||
notification.SetTitle(username.c_str());
|
||||
notification.SetContent(message.c_str());
|
||||
notification.Send();
|
||||
if (highlight) {
|
||||
GenerateHyperlinkText(message, tr_thick_highlight, &this_array);
|
||||
} else {
|
||||
GenerateHyperlinkText(message, tr_thin_black, &this_array);
|
||||
}
|
||||
} else if (type == LOCAL) {
|
||||
_chat->Insert(_chat->TextLength(), username.c_str(), username.size(), &tra_thick_red);
|
||||
_chat->Insert(_chat->TextLength(), ": ", 2, &tra_thin_black);
|
||||
timeline->Insert(timeline->TextLength(), username.c_str(), username.size(), &tra_thick_red);
|
||||
timeline->Insert(timeline->TextLength(), ": ", 2, &tra_thin_black);
|
||||
|
||||
GenerateHyperlinkText(message, tr_thin_black, &this_array);
|
||||
} else { // SYSTEM messages
|
||||
GenerateHyperlinkText(message, tr_thick_black, &this_array);
|
||||
}
|
||||
_chat->Insert(_chat->TextLength(), message.c_str(), message.size(), this_array);
|
||||
timeline->Insert(timeline->TextLength(), message.c_str(), message.size(), this_array);
|
||||
free(this_array);
|
||||
|
||||
_chat->Insert(_chat->TextLength(), "\n", 1, &tra_thin_black);
|
||||
timeline->Insert(timeline->TextLength(), "\n", 1, &tra_thin_black);
|
||||
if (type == LOCAL)
|
||||
_chat->ScrollTo(0.0, _chat->Bounds().bottom);
|
||||
timeline->ScrollTo(0.0, timeline->Bounds().bottom);
|
||||
}
|
||||
|
||||
|
||||
|
@ -475,11 +468,11 @@ void TalkView::NewMessage(string new_message) {
|
|||
}
|
||||
|
||||
|
||||
void TalkView::NewMessage(string username, string new_message) {
|
||||
void TalkView::NewMessage(string username, string new_message, bool highlight) {
|
||||
if (username == _group_username)
|
||||
AddToTalk(username.c_str(), new_message, LOCAL);
|
||||
AddToTalk(username.c_str(), new_message, LOCAL, highlight);
|
||||
else
|
||||
AddToTalk(username.c_str(), new_message, MAIN_RECIPIENT);
|
||||
AddToTalk(username.c_str(), new_message, MAIN_RECIPIENT, highlight);
|
||||
}
|
||||
|
||||
|
||||
|
@ -798,13 +791,13 @@ void TalkView::RevealPreviousHistory() {
|
|||
return;
|
||||
|
||||
if (_chat_index == -1)
|
||||
_chat_buffer = _message->Text();
|
||||
_chat_buffer = messageInput->Text();
|
||||
|
||||
// go back
|
||||
++_chat_index;
|
||||
|
||||
// update text
|
||||
_message->SetText(_chat_history[_chat_index].c_str());
|
||||
messageInput->SetText(_chat_history[_chat_index].c_str());
|
||||
}
|
||||
|
||||
|
||||
|
@ -818,10 +811,10 @@ void TalkView::RevealNextHistory() {
|
|||
|
||||
// last buffer
|
||||
if (_chat_index == -1) {
|
||||
_message->SetText(_chat_buffer.c_str());
|
||||
messageInput->SetText(_chat_buffer.c_str());
|
||||
} else {
|
||||
// update text
|
||||
_message->SetText(_chat_history[_chat_index].c_str());
|
||||
messageInput->SetText(_chat_history[_chat_index].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,9 @@ public:
|
|||
void MessageReceived(BMessage *msg) override;
|
||||
|
||||
std::string OurRepresentation();
|
||||
void AddToTalk(std::string username, std::string message, user_type type);
|
||||
void AddToTalk(std::string username, std::string message, user_type type, bool highlight = false);
|
||||
void NewMessage(std::string new_message);
|
||||
void NewMessage(std::string username, std::string new_message);
|
||||
void NewMessage(std::string username, std::string new_message, bool highlight);
|
||||
|
||||
bool NewlinesAllowed();
|
||||
|
||||
|
@ -89,10 +89,10 @@ private:
|
|||
// GUI
|
||||
StatusView *_status_view;
|
||||
|
||||
BScrollView *_chat_scroller;
|
||||
BScrollView *_message_scroller;
|
||||
ChatTextView *_chat;
|
||||
BTextView *_message;
|
||||
BScrollView *timelineScroller;
|
||||
BScrollView *messageInputScroller;
|
||||
ChatTextView *timeline;
|
||||
BTextView *messageInput;
|
||||
|
||||
BListView *_people;
|
||||
|
||||
|
|