824 lines
22 KiB
C++
824 lines
22 KiB
C++
//////////////////////////////////////////////////
|
|
// Blabber [TalkView.cpp]
|
|
//////////////////////////////////////////////////
|
|
|
|
#include <cstdio>
|
|
#include <ctime>
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <Box.h>
|
|
#include <be_apps/NetPositive/NetPositive.h>
|
|
#include <FindDirectory.h>
|
|
#include <GridView.h>
|
|
#include <GroupLayout.h>
|
|
#include <GroupView.h>
|
|
#include <LayoutBuilder.h>
|
|
#include <Roster.h>
|
|
#include <storage/Path.h>
|
|
#include <SplitView.h>
|
|
|
|
#include "support/AppLocation.h"
|
|
|
|
#include "jabber/BlabberSettings.h"
|
|
#include "jabber/GenericFunctions.h"
|
|
#include "jabber/JabberSpeak.h"
|
|
#include "jabber/MessageRepeater.h"
|
|
#include "jabber/Messages.h"
|
|
#include "jabber/PreferencesWindow.h"
|
|
#include "jabber/SoundSystem.h"
|
|
#include "jabber/TalkListItem.h"
|
|
#include "jabber/TalkManager.h"
|
|
|
|
#include "ui/PeopleListItem.h"
|
|
#include "ui/RotateChatFilter.h"
|
|
|
|
#include "TalkView.h"
|
|
|
|
#include "gloox/mucroom.h"
|
|
#include "gloox/rostermanager.h"
|
|
|
|
#define NOTIFICATION_CHAR "√"
|
|
|
|
|
|
TalkView::TalkView(const gloox::JID *user, string group_room,
|
|
string group_username, gloox::MessageSession* session)
|
|
: BGroupView("<talk window>", B_VERTICAL)
|
|
, _session(session)
|
|
{
|
|
_chat_index = -1;
|
|
|
|
UserID* uid = NULL;
|
|
if (user) {
|
|
uid = JRoster::Instance()->FindUser(*user);
|
|
}
|
|
_group_room = group_room;
|
|
_group_username = group_username;
|
|
|
|
if (!IsGroupChat() && uid) {
|
|
_current_status = uid->OnlineStatus();
|
|
}
|
|
|
|
// FILE MENU
|
|
// status bar
|
|
_status_view = new StatusView();
|
|
_status_view->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
|
|
|
fTimeline = new ChatTextView("Timeline", B_WILL_DRAW | B_FRAME_EVENTS);
|
|
fTimelineScroller = new BScrollView("Timeline Scroller", fTimeline, B_WILL_DRAW, false, true);
|
|
fTimeline->TargetedByScrollView(fTimelineScroller);
|
|
fTimeline->SetFontSize(12.0);
|
|
fTimeline->SetWordWrap(true);
|
|
fTimeline->SetStylable(true);
|
|
fTimeline->MakeEditable(false);
|
|
|
|
// message control
|
|
rgb_color text_color = ui_color(B_PANEL_TEXT_COLOR);
|
|
BFont text_font(be_plain_font);
|
|
|
|
fMessageInput = new BTextView("Message Input", &text_font, &text_color, B_WILL_DRAW);
|
|
fMessageInputScroller = new BScrollView("Message Input Scroller", fMessageInput, B_WILL_DRAW, false, false);
|
|
|
|
fMessageInput->TargetedByScrollView(fMessageInputScroller);
|
|
fMessageInput->SetWordWrap(true);
|
|
|
|
// editing filter for messaging
|
|
fMessageInput->AddFilter(new EditingFilter(fMessageInput, this));
|
|
|
|
// handle splits
|
|
BSplitView* _split_talk = new BSplitView(B_VERTICAL);
|
|
_split_talk->AddChild(fTimelineScroller);
|
|
_split_talk->AddChild(fMessageInputScroller);
|
|
_split_talk->SetItemWeight(0, 12, false);
|
|
_split_talk->SetItemWeight(1, 1, false);
|
|
_split_talk->SetSpacing(0);
|
|
_split_talk->SetCollapsible(false);
|
|
|
|
_people = new BListView(NULL, B_SINGLE_SELECTION_LIST);
|
|
_people->SetExplicitMinSize(BSize(StringWidth("Firstname M. Lastname"), B_SIZE_UNSET));
|
|
_scrolled_people_pane = new BScrollView(NULL, _people, 0, false, true, B_PLAIN_BORDER);
|
|
|
|
BSplitView* _split_group_people = new BSplitView(B_HORIZONTAL);
|
|
_split_group_people->AddChild(_split_talk);
|
|
_split_group_people->AddChild(_scrolled_people_pane);
|
|
_split_group_people->SetItemWeight(0, 5, false);
|
|
_split_group_people->SetItemWeight(1, 1, false);
|
|
_split_group_people->SetSpacing(0);
|
|
|
|
if (!IsGroupChat()) {
|
|
_split_group_people->SetItemCollapsed(1, true);
|
|
_split_group_people->SetSplitterSize(0);
|
|
}
|
|
|
|
// add GUI components to BView
|
|
BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
|
|
SetLayout(layout);
|
|
layout->SetSpacing(0);
|
|
|
|
AddChild(_split_group_people);
|
|
AddChild(_status_view);
|
|
|
|
fMessageInput->MakeFocus(true);
|
|
|
|
// generate window title
|
|
char buffer[1024];
|
|
string user_representation;
|
|
|
|
if (!IsGroupChat()) {
|
|
// identify the user
|
|
sprintf(buffer, "your identity is %s", _group_username.c_str());
|
|
_status_view->SetMessage(buffer);
|
|
} else if (!uid || uid->UserType() == UserID::JABBER) {
|
|
// identify the user
|
|
sprintf(buffer, "your identity is %s", JabberSpeak::Instance()->CurrentLogin().c_str());
|
|
_status_view->SetMessage(buffer);
|
|
} else {
|
|
user_representation = uid->FriendlyName();
|
|
|
|
if (user_representation.empty())
|
|
user_representation = uid->JabberUsername();
|
|
|
|
}
|
|
|
|
if (!IsGroupChat() && user_representation.empty()) {
|
|
if (uid)
|
|
user_representation = uid->FriendlyName();
|
|
else
|
|
user_representation = _session->target().bare();
|
|
}
|
|
|
|
// put Session started message
|
|
// construct timestamp
|
|
string message;
|
|
message.resize(128);
|
|
time_t now = time(NULL);
|
|
struct tm *time_struct = localtime(&now);
|
|
strftime(&message[0], message.size()-1, "Session started %e %b %y [%R:%S]", time_struct);
|
|
AddToTalk("", message.c_str(), OTHER);
|
|
|
|
TalkManager::Instance()->StartWatchingAll(this);
|
|
}
|
|
|
|
|
|
TalkView::~TalkView() {
|
|
string message;
|
|
message.resize(128);
|
|
time_t now = time(NULL);
|
|
struct tm *time_struct = localtime(&now);
|
|
strftime(&message[0], message.size()-1, "Session finished %e %b %y [%R:%S]\n---", time_struct);
|
|
AddToTalk("", message.c_str(), OTHER);
|
|
|
|
if (IsGroupChat())
|
|
JabberSpeak::Instance()->SendGroupUnvitation(_group_room, _group_username);
|
|
|
|
TalkManager::Instance()->RemoveWindow(this);
|
|
}
|
|
|
|
|
|
void TalkView::AttachedToWindow()
|
|
{
|
|
}
|
|
|
|
|
|
void TalkView::FrameResized(float width, float height)
|
|
{
|
|
BView::FrameResized(width, height);
|
|
|
|
BRect chat_rect = fTimeline->Frame();
|
|
BRect message_rect = fMessageInput->Frame();
|
|
|
|
chat_rect.OffsetTo(B_ORIGIN);
|
|
message_rect.OffsetTo(B_ORIGIN);
|
|
|
|
chat_rect.InsetBy(2.0, 2.0);
|
|
message_rect.InsetBy(2.0, 2.0);
|
|
|
|
fTimeline->SetTextRect(chat_rect);
|
|
fMessageInput->SetTextRect(message_rect);
|
|
|
|
fTimeline->Invalidate();
|
|
fTimelineScroller->Invalidate();
|
|
}
|
|
|
|
|
|
void TalkView::MessageReceived(BMessage *msg) {
|
|
switch(msg->what) {
|
|
case JAB_CLOSE_TALKS:
|
|
{
|
|
RemoveSelf();
|
|
delete this;
|
|
break;
|
|
}
|
|
|
|
case B_OBSERVER_NOTICE_CHANGE:
|
|
{
|
|
// only for groupchat
|
|
if (!IsGroupChat())
|
|
break;
|
|
|
|
switch(msg->FindInt32("be:observe_orig_what"))
|
|
{
|
|
case JAB_GROUP_CHATTER_ONLINE:
|
|
{
|
|
if (GetGroupRoom() == msg->FindString("room")) {
|
|
AddGroupChatter(msg->FindString("username"),
|
|
(gloox::MUCRoomAffiliation)msg->FindInt32("affiliation"));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case JAB_GROUP_CHATTER_OFFLINE: {
|
|
RemoveGroupChatter(msg->FindString("username"));
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
case JAB_SHOW_CHATLOG: {
|
|
// just forward to main blabber window...
|
|
BMessage *msgForward = new BMessage(*msg);
|
|
BlabberMainWindow::Instance()->PostMessage(msgForward);
|
|
break;
|
|
}
|
|
|
|
|
|
case BLAB_UPDATE_ROSTER: {
|
|
// doesn't apply to groupchat
|
|
if (!IsGroupChat())
|
|
break;
|
|
|
|
// get new status
|
|
JRoster::Instance()->Lock();
|
|
UserID* user = JRoster::Instance()->FindUser(_session->target());
|
|
if (!user) {
|
|
JRoster::Instance()->Unlock();
|
|
break;
|
|
}
|
|
UserID::online_status new_status = user->OnlineStatus();
|
|
|
|
// if we have one, check their presence
|
|
if (_current_status != new_status) {
|
|
char buffer[2048];
|
|
|
|
if (_current_status == UserID::ONLINE && new_status == UserID::OFFLINE) {
|
|
sprintf(buffer, "This user is now offline.");
|
|
AddToTalk("", buffer, OTHER);
|
|
} else if (_current_status == UserID::OFFLINE && new_status == UserID::ONLINE) {
|
|
sprintf(buffer, "This user is now online.");
|
|
AddToTalk("", buffer, OTHER);
|
|
}
|
|
}
|
|
|
|
_current_status = new_status;
|
|
|
|
JRoster::Instance()->Unlock();
|
|
|
|
break;
|
|
}
|
|
|
|
case JAB_CHAT_SENT: {
|
|
string message = fMessageInput->Text();
|
|
|
|
// eliminate empty messages
|
|
if (message.empty())
|
|
break;
|
|
|
|
if (_session == NULL) {
|
|
// FIXME is it ok to do this from window thread? Or should
|
|
// we go through main app?
|
|
gloox::MUCRoom* room = (gloox::MUCRoom*)TalkManager::Instance()
|
|
->IsExistingWindowToGroup(GetGroupRoom());
|
|
room->send(message);
|
|
} else {
|
|
_session->send(message);
|
|
AddToTalk(OurRepresentation(), message, LOCAL);
|
|
}
|
|
|
|
// Reset message input box
|
|
fMessageInput->ScrollToOffset(0);
|
|
fMessageInput->SetText("");
|
|
fMessageInput->MakeFocus(true);
|
|
|
|
break;
|
|
}
|
|
|
|
case JAB_CLOSE_CHAT: {
|
|
RemoveSelf();
|
|
delete this;
|
|
break;
|
|
}
|
|
|
|
case JAB_FOCUS_BUDDY: {
|
|
BlabberMainWindow::Instance()->Activate();
|
|
break;
|
|
}
|
|
|
|
case kIncomingMessage:
|
|
{
|
|
gloox::RosterManager* rm = JabberSpeak::Instance()->GlooxClient()->rosterManager();
|
|
gloox::RosterItem* item = rm->getRosterItem(_session->target());
|
|
if (item && !item->name().empty()) {
|
|
AddToTalk(item->name().c_str(), msg->FindString("content"), MAIN_RECIPIENT);
|
|
} else {
|
|
AddToTalk(_session->target().bare().c_str(), msg->FindString("content"), MAIN_RECIPIENT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
string TalkView::OurRepresentation() {
|
|
// use friendly name if you have it
|
|
string user = JabberSpeak::Instance()->CurrentRealName();
|
|
|
|
// and if not :)
|
|
if (user.empty())
|
|
user = JabberSpeak::Instance()->CurrentLogin();
|
|
|
|
return user;
|
|
}
|
|
|
|
|
|
void TalkView::AddToTalk(string username, string message, user_type type, bool highlight) {
|
|
// transform local identity
|
|
if (IsGroupChat() && type == LOCAL)
|
|
username = _group_username;
|
|
|
|
// history
|
|
if (type == LOCAL) {
|
|
// reset chat history index
|
|
_chat_index = -1;
|
|
|
|
// add latest
|
|
_chat_history.push_front(message);
|
|
|
|
// prune end
|
|
if (_chat_history.size() > 50)
|
|
_chat_history.pop_back();
|
|
}
|
|
|
|
// ignore empty messages
|
|
if (message.empty())
|
|
return;
|
|
|
|
// Figure out if the view is scrolled down before modifying it
|
|
BRect bounds = fTimeline->Bounds();
|
|
BRect textRect = fTimeline->TextRect();
|
|
bool scrolledDown = bounds.bottom == -1 ||
|
|
(bounds.bottom > textRect.bottom && bounds.top < textRect.bottom);
|
|
|
|
// prune trailing whitespace
|
|
while (!message.empty() && isspace(message[message.size() - 1]))
|
|
message.erase(message.size() - 1);
|
|
|
|
// create the thin (plain) and thick (bold) font
|
|
BFont thin(be_plain_font);
|
|
BFont thick(be_bold_font);
|
|
|
|
// some colors to play with
|
|
rgb_color blue = {0, 0, 255, 255};
|
|
rgb_color red = {255, 0, 0, 255};
|
|
rgb_color orange = {205, 113, 57, 255};
|
|
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() - backgroundColor.Brightness()) < 45)
|
|
blue = { 128, 137, 252, 255 };
|
|
|
|
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, 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}};
|
|
text_run_array tra_thick_red = {1, {tr_thick_red}};
|
|
text_run_array tra_thin_black = {1, {tr_thin_black}};
|
|
|
|
// construct timestamp
|
|
char timestamp[64];
|
|
time_t now = time(NULL);
|
|
struct tm *time_struct = localtime(&now);
|
|
|
|
strftime(timestamp, 63, "[%R:%S] ", time_struct);
|
|
|
|
string time_stamp = timestamp;
|
|
|
|
BString messageString = BString(message.c_str());
|
|
|
|
if (BlabberSettings::Instance()->Tag("show-timestamp"))
|
|
fTimeline->Insert(fTimeline->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)
|
|
fTimeline->Insert(fTimeline->TextLength(), messageString, messageString.Length(), &tra_thick_blue);
|
|
else
|
|
fTimeline->Insert(fTimeline->TextLength(), messageString, messageString.Length(), &tra_thick_red);
|
|
|
|
fTimeline->Insert(fTimeline->TextLength(), "\n", 1, &tra_thin_black);
|
|
if (scrolledDown)
|
|
fTimeline->ScrollTo(0.0, fTimeline->Bounds().bottom);
|
|
|
|
return;
|
|
}
|
|
|
|
text_run_array *this_array;
|
|
if (type == MAIN_RECIPIENT) {
|
|
if (!IsGroupChat() || !BlabberSettings::Instance()->Tag("exclude-groupchat-sounds"))
|
|
SoundSystem::Instance()->PlayMessageSound();
|
|
|
|
fTimeline->Insert(fTimeline->TextLength(), username.c_str(), username.size(), &tra_thick_blue);
|
|
fTimeline->Insert(fTimeline->TextLength(), ": ", 2, &tra_thin_black);
|
|
|
|
// Highlight messages when they mention the nickname
|
|
if (highlight) {
|
|
GenerateHyperlinkText(message, tr_thick_highlight, &this_array);
|
|
} else {
|
|
GenerateHyperlinkText(message, tr_thin_black, &this_array);
|
|
}
|
|
} else if (type == LOCAL) {
|
|
fTimeline->Insert(fTimeline->TextLength(), username.c_str(), username.size(), &tra_thick_red);
|
|
fTimeline->Insert(fTimeline->TextLength(), ": ", 2, &tra_thin_black);
|
|
|
|
GenerateHyperlinkText(message, tr_thin_black, &this_array);
|
|
} else { // SYSTEM messages
|
|
GenerateHyperlinkText(message, tr_thick_black, &this_array);
|
|
}
|
|
fTimeline->Insert(fTimeline->TextLength(), message.c_str(), message.size(), this_array);
|
|
free(this_array);
|
|
|
|
|
|
fTimeline->Insert(fTimeline->TextLength(), "\n", 1, &tra_thin_black);
|
|
if (scrolledDown)
|
|
fTimeline->ScrollTo(0.0, fTimeline->Bounds().bottom);
|
|
}
|
|
|
|
|
|
const gloox::JID& TalkView::GetUserID() {
|
|
if (_session == NULL)
|
|
debugger("Getting user ID not possible for group chat");
|
|
|
|
return _session->target();
|
|
}
|
|
|
|
|
|
string TalkView::GetGroupRoom() {
|
|
return _group_room;
|
|
}
|
|
|
|
|
|
string TalkView::GetGroupUsername() {
|
|
return _group_username;
|
|
}
|
|
|
|
|
|
bool TalkView::NewlinesAllowed() {
|
|
return false;
|
|
}
|
|
|
|
|
|
int TalkView::CountHyperlinks(string message) {
|
|
string::size_type curr_pos = 0, link_start, link_end;
|
|
string::size_type find1, find2, find3;
|
|
|
|
// keep count
|
|
int link_count = 0;
|
|
|
|
// find next link
|
|
link_start = message.find("http://", curr_pos);
|
|
|
|
find1 = message.find("ftp://", curr_pos);
|
|
if (find1 != string::npos && (link_start == string::npos || find1 < link_start)) {
|
|
link_start = find1;
|
|
}
|
|
|
|
find2 = message.find("www.", curr_pos);
|
|
if (find2 != string::npos && (link_start == string::npos || find2 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find2 > 0 && isalnum(message[find2 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find2 + 4]) || message[find2 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find2;
|
|
}
|
|
}
|
|
|
|
find3 = message.find("ftp.", curr_pos);
|
|
if (find3 != string::npos && (link_start == string::npos || find3 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find3 > 0 && isalnum(message[find3 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find3 + 4]) || message[find3 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find3;
|
|
}
|
|
}
|
|
|
|
while (link_start != string::npos) {
|
|
// find whitespace or end
|
|
link_end = message.find_first_of(" \t\r\n", link_start);
|
|
|
|
if (link_end == string::npos)
|
|
link_end = message.size() - 1;
|
|
|
|
// prune punctuation
|
|
while (link_start < link_end) {
|
|
if (message[link_end] == ',' || message[link_end] == '!' || message[link_end] == '.' || message[link_end] == ')' || message[link_end] == ';' || message[link_end] == ']' || message[link_end] == '>' || message[link_end] == '\'' || message[link_end] == '"') {
|
|
--link_end;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (link_start < link_end) {
|
|
++link_count;
|
|
}
|
|
|
|
curr_pos = link_end + 1;
|
|
|
|
// find next link
|
|
link_start = message.find("http://", curr_pos);
|
|
|
|
find1 = message.find("ftp://", curr_pos);
|
|
if (find1 != string::npos && (link_start == string::npos || find1 < link_start))
|
|
link_start = find1;
|
|
|
|
find2 = message.find("www.", curr_pos);
|
|
if (find2 != string::npos && (link_start == string::npos || find2 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find2 > 0 && isalnum(message[find2 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find2 + 4]) || message[find2 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find2;
|
|
}
|
|
}
|
|
|
|
find3 = message.find("ftp.", curr_pos);
|
|
if (find3 != string::npos && (link_start == string::npos || find3 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find3 > 0 && isalnum(message[find3 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find3 + 4]) || message[find3 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find3;
|
|
}
|
|
}
|
|
}
|
|
|
|
return link_count;
|
|
}
|
|
|
|
|
|
void TalkView::GenerateHyperlinkText(string message, text_run standard, text_run_array **tra) {
|
|
int link_count = CountHyperlinks(message);
|
|
string::size_type find1, find2, find3;
|
|
int link_index = 0;
|
|
|
|
// no links?
|
|
if (link_count == 0) {
|
|
*tra = (text_run_array *)malloc(sizeof(text_run_array));
|
|
|
|
(*tra)->count = 1;
|
|
(*tra)->runs[0].offset = standard.offset;
|
|
(*tra)->runs[0].font = standard.font;
|
|
(*tra)->runs[0].color = standard.color;
|
|
|
|
return;
|
|
}
|
|
|
|
*tra = (text_run_array *)malloc(sizeof(text_run_array) + (sizeof(text_run) * (link_count * 2 - 1)));
|
|
(*tra)->count = link_count * 2;
|
|
|
|
string::size_type curr_pos = 0, link_start, link_end;
|
|
|
|
// find next link
|
|
link_start = message.find("http://", curr_pos);
|
|
|
|
find1 = message.find("ftp://", curr_pos);
|
|
if (find1 != string::npos && (link_start == string::npos || find1 < link_start))
|
|
link_start = find1;
|
|
|
|
find2 = message.find("www.", curr_pos);
|
|
if (find2 != string::npos && (link_start == string::npos || find2 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find2 > 0 && isalnum(message[find2 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find2 + 4]) || message[find2 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find2;
|
|
}
|
|
}
|
|
|
|
find3 = message.find("ftp.", curr_pos);
|
|
if (find3 != string::npos && (link_start == string::npos || find3 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find3 > 0 && isalnum(message[find3 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find3 + 4]) || message[find3 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find3;
|
|
}
|
|
}
|
|
|
|
while (link_start != string::npos) {
|
|
// find whitespace or end
|
|
link_end = message.find_first_of(" \t\r\n", link_start);
|
|
|
|
if (link_end == string::npos) {
|
|
link_end = message.size() - 1;
|
|
}
|
|
|
|
// prune punctuation
|
|
while (link_start < link_end) {
|
|
if (message[link_end] == ',' || message[link_end] == '!' || message[link_end] == '.' || message[link_end] == ')' || message[link_end] == ';' || message[link_end] == ']' || message[link_end] == '>' || message[link_end] == '?' || message[link_end] == '\'' || message[link_end] == '"') {
|
|
--link_end;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// add hyperlink
|
|
if (link_start < link_end) {
|
|
BFont thin(be_plain_font);
|
|
rgb_color purple = {192, 0, 192, 255};
|
|
|
|
(*tra)->runs[link_index].offset = link_start;
|
|
(*tra)->runs[link_index].font = thin;
|
|
(*tra)->runs[link_index].color = purple;
|
|
|
|
(*tra)->runs[link_index + 1].offset = link_end + 1;
|
|
(*tra)->runs[link_index + 1].font = standard.font;
|
|
(*tra)->runs[link_index + 1].color = standard.color;
|
|
}
|
|
|
|
curr_pos = link_end + 1;
|
|
|
|
if (curr_pos >= message.size()) {
|
|
break;
|
|
}
|
|
|
|
// find next link
|
|
link_start = message.find("http://", curr_pos);
|
|
|
|
find1 = message.find("ftp://", curr_pos);
|
|
if (find1 != string::npos && (link_start == string::npos || find1 < link_start)) {
|
|
link_start = find1;
|
|
}
|
|
|
|
find2 = message.find("www.", curr_pos);
|
|
if (find2 != string::npos && (link_start == string::npos || find2 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find2 > 0 && isalnum(message[find2 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find2 + 4]) || message[find2 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find2;
|
|
}
|
|
}
|
|
|
|
find3 = message.find("ftp.", curr_pos);
|
|
if (find3 != string::npos && (link_start == string::npos || find3 < link_start)) {
|
|
// ignore if it's not at the beginning or has no whitespace
|
|
if (find3 > 0 && isalnum(message[find3 - 1])) {
|
|
// do nothing
|
|
} else if (isspace(message[find3 + 4]) || message[find3 + 4] == '.') {
|
|
// do nothing
|
|
} else {
|
|
link_start = find3;
|
|
}
|
|
}
|
|
|
|
link_index += 2;
|
|
}
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void TalkView::AddGroupChatter(string user, gloox::MUCRoomAffiliation affiliation) {
|
|
int i;
|
|
|
|
// create a new entry
|
|
PeopleListItem *people_item = new PeopleListItem(user, affiliation);
|
|
|
|
// exception
|
|
if (_people->CountItems() == 0) {
|
|
// add the new user
|
|
_people->AddItem(people_item);
|
|
return;
|
|
}
|
|
|
|
// add it to the list
|
|
// FIXME we should binary search for the correct position
|
|
for (i=0; i < _people->CountItems(); ++i) {
|
|
PeopleListItem *iterating_item = dynamic_cast<PeopleListItem *>(_people->ItemAt(i));
|
|
|
|
int compare = compareStrings(iterating_item->User().c_str(), user.c_str());
|
|
|
|
if (compare == 0) {
|
|
// Update existing user
|
|
// FIXME affiliation might have changed, refresh it
|
|
_people->InvalidateItem(i);
|
|
} else if (compare > 0) {
|
|
// add the new user in the middle
|
|
_people->AddItem(people_item, i);
|
|
} else if (i == (_people->CountItems() - 1)) {
|
|
// add the new user at the end
|
|
_people->AddItem(people_item);
|
|
} else {
|
|
// continue searching for the correct place
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void TalkView::RemoveGroupChatter(string username) {
|
|
// remove user
|
|
for (int i=0; i < _people->CountItems(); ++i) {
|
|
if (dynamic_cast<PeopleListItem *>(_people->ItemAt(i))->User() == username)
|
|
_people->RemoveItem(i);
|
|
}
|
|
}
|
|
|
|
|
|
void TalkView::RevealPreviousHistory() {
|
|
// boundary
|
|
if (_chat_index == 49 || _chat_index == ((int)_chat_history.size() - 1))
|
|
return;
|
|
|
|
if (_chat_index == -1)
|
|
_chat_buffer = fMessageInput->Text();
|
|
|
|
// go back
|
|
++_chat_index;
|
|
|
|
// update text
|
|
fMessageInput->SetText(_chat_history[_chat_index].c_str());
|
|
}
|
|
|
|
|
|
void TalkView::RevealNextHistory() {
|
|
// boundary
|
|
if (_chat_index == -1)
|
|
return;
|
|
|
|
// go forward
|
|
--_chat_index;
|
|
|
|
// last buffer
|
|
if (_chat_index == -1) {
|
|
fMessageInput->SetText(_chat_buffer.c_str());
|
|
} else {
|
|
// update text
|
|
fMessageInput->SetText(_chat_history[_chat_index].c_str());
|
|
}
|
|
}
|
|
|
|
|
|
bool
|
|
TalkView::IsGroupChat()
|
|
{
|
|
return !_group_room.empty();
|
|
}
|
|
|
|
|
|
void
|
|
TalkView::SetStatus(std::string message)
|
|
{
|
|
_status_view->SetMessage(message);
|
|
}
|