/*
* XmNap A Motif napster client
*
* Copyright (C) 2000 Mats Peterson
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Please send any comments/bug reports to
*
[email protected] (Mats Peterson)
*/
#include <Xm/Xm.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/Frame.h>
#include <Xm/Form.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/List.h>
#include <Xm/RowColumn.h>
#include <Xm/Protocols.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include "main.h"
#include "connect.h"
#include "command.h"
#include "chat.h"
#include "message.h"
#include "info.h"
#include "msgbox.h"
#include "input.h"
#include "util.h"
#ifdef USE_SOUND
#include "sound.h"
#endif
CHAN *channels = NULL;
PRIV *privs = NULL;
PING *pings = NULL;
static PRIV* PrivWindow(String nick);
static void ClosePriv(String nick);
static void ChatFocusCB(Widget w, XtPointer clientData, XtPointer callData)
{
curWin = curFocus = XtParent(w);
curWinType = 0;
if (! strcmp((String)clientData, "privWin"))
curWinType = 1;
}
static void TextFocusCB(Widget w, XtPointer clientData,
XmAnyCallbackStruct *cbs) {
XtVaSetValues(w, XmNcursorPositionVisible, True, NULL);
}
static void TextLosingFocusCB(Widget w, XtPointer clientData,
XmTextVerifyCallbackStruct *cbs) {
XtVaSetValues(w, XmNcursorPositionVisible, False, NULL);
}
static void ChatClearCB(Widget w, XtPointer clientData,
XmPushButtonCallbackStruct *cbs)
{
Widget text = (Widget)clientData;
XmTextSetString(text, "");
}
static void InsertText(Widget w, String text)
{
String s;
XmTextPosition pos;
if ((pos = XmTextGetLastPosition(w)))
XmTextInsert(w, pos++, "\n");
XmTextInsert(w, pos, text);
pos += strlen(text);
if (pos > 10000) {
s = XmTextGetString(w);
XmTextSetString(w, s + 1000);
XtFree(s);
}
XmTextSetInsertionPosition(w, XmTextGetLastPosition(w));
}
static void ModifyVerifyCB(Widget w, XtPointer clientData,
XmTextVerifyCallbackStruct *cbs)
{
String text = cbs->text->ptr, p1, p2;
String tmp;
int msgType = curWinType ? MSG_CLIENT_PRIVMSG : MSG_CLIENT_PUBLIC;
if (! text)
return;
if (strchr(text, '\n')) {
tmp = XtMalloc(8192);
cbs->doit = False;
for (p1 = p2 = text; *p2; p2++) {
if (*p2 == '\n') {
*p2 = '\0';
sprintf(tmp, "%s %s", (String)clientData, p1);
if (SendMsg(msgType, tmp)) {
Disconnect(strerror(errno));
goto end;
}
if (msgType == MSG_CLIENT_PRIVMSG)
InsertText(XtNameToWidget(XtParent(w), "*privText"), p1);
p1 = p2 + 1;
}
}
sprintf(tmp, "%s %s", (String)clientData, p1);
if (SendMsg(msgType, tmp)) {
Disconnect(strerror(errno));
goto end;
}
if (msgType == MSG_CLIENT_PRIVMSG)
InsertText(XtNameToWidget(XtParent(w), "*privText"), p1);
end:
XtFree(tmp);
}
}
void Ping(String nick)
{
PING *newPing, *ping, *prevPing = NULL;
for (ping = pings; ping; ping = ping->next) {
if (! strcasecmp(ping->nick, nick))
break;
prevPing = ping;
}
if (ping) {
if (prevPing) {
prevPing->next = ping->next;
} else {
pings = pings->next;
}
XtFree(ping->nick);
XtFree((char*)ping);
}
newPing = XtNew(PING);
newPing->nick = XtNewString(nick);
newPing->start = time(NULL);
newPing->next = NULL;
if (! pings)
pings = newPing;
else {
for (ping = pings; ping->next; ping = ping->next);
ping->next = newPing;
}
if (SendMsg(MSG_CLIENT_PING, newPing->nick))
Disconnect(strerror(errno));
}
static String GetPingTime(String nick)
{
String pingStr;
PING *ping, *prevPing = NULL;
int pingTime;
for (ping = pings; ping; ping = ping->next) {
if (! strcasecmp(nick, ping->nick))
break;
prevPing = ping;
}
if (! ping)
return NULL;
if (prevPing) {
prevPing->next = ping->next;
} else {
pings = pings->next;
}
pingTime = time(NULL) - ping->start;
pingStr = XtMalloc(80);
if (pingTime != 1) {
sprintf(pingStr, "%d seconds", pingTime);
} else {
sprintf(pingStr, "%d second", pingTime);
}
XtFree(ping->nick);
XtFree((char*)ping);
return pingStr;
}
void RcvGlobal(int type, String data)
{
Widget textBox = NULL;
String nick, text, ptr, tmp = XtMalloc(8192);
CHAN *c;
PRIV *p;
String pingStr;
ptr = strtok(data, " ");
nick = XtNewString(ptr);
if ((ptr = strtok(NULL, "\0")))
text = XtNewString(ptr);
else
text = XtNewString("");
if ((type == MSG_CLIENT_PRIVMSG)) {
if ((p = FindPrivByNick(nick)))
textBox = p->text;
} else {
if (curWinType) {
if ((p = FindPrivByWin(curWin)))
textBox = p->text;
} else {
if ((c = FindChanByWin(curWin)))
textBox = c->text;
}
}
if (! textBox) {
switch (type) {
case MSG_CLIENT_PRIVMSG:
p = PrivWindow(nick);
sprintf(tmp, "> %s", text);
InsertText(p->text, tmp);
#ifdef USE_SOUND
if (strcmp(nick, userInfo.userName))
PlaySound(sound[PRIVMSG_SOUND]);
#endif
sprintf(tmp, "Private message from %s. Accept?", nick);
if (YesNoMsg(tmp, "OK")) {
XtPopup(p->w, XtGrabNone);
p->visible = True;
} else
ClosePriv(nick);
goto end;
case MSG_SERVER_WALLOP:
sprintf(tmp, "OPERATOR MESSAGE from %s: %s",
nick, text);
ShowMiscInfo(tmp, 0);
goto end;
case MSG_SERVER_ANNOUNCE:
sprintf(tmp, "GLOBAL MESSAGE from %s: %s",
nick, text);
ShowMiscInfo(tmp, 0);
goto end;
case MSG_SERVER_PING:
strcpy(tmp, nick);
if (SendMsg(MSG_CLIENT_PONG, tmp))
Disconnect(strerror(errno));
goto end;
case MSG_SERVER_PONG:
if (! (pingStr = GetPingTime(nick)))
goto end;
sprintf(tmp, "PONG received from %s: %s",
nick, pingStr);
ShowMiscInfo(tmp, 0);
XtFree(pingStr);
goto end;
}
}
switch (type) {
case MSG_CLIENT_PRIVMSG:
sprintf(tmp, "> %s", text);
InsertText(textBox, tmp);
#ifdef USE_SOUND
if (p->visible && strcmp(nick, userInfo.userName))
PlaySound(sound[PRIVMSG_SOUND]);
#endif
break;
case MSG_SERVER_WALLOP:
sprintf(tmp, "*** OPERATOR MESSAGE from %s: %s",
nick, text);
InsertText(textBox, tmp);
break;
case MSG_SERVER_ANNOUNCE:
sprintf(tmp, "*** GLOBAL MESSAGE from %s: %s",
nick, text);
InsertText(textBox, tmp);
break;
case MSG_SERVER_PING:
sprintf(tmp, "*** %s PING", nick);
InsertText(textBox, tmp);
strcpy(tmp, nick);
if (SendMsg(MSG_CLIENT_PONG, tmp))
Disconnect(strerror(errno));
break;
case MSG_SERVER_PONG:
if (! (pingStr = GetPingTime(nick)))
goto end;
sprintf(tmp, "*** PONG received from %s: %s",
nick, pingStr);
InsertText(textBox, tmp);
XtFree(pingStr);
break;
}
end:
XtFree(tmp);
XtFree(nick);
XtFree(text);
}
void RcvChannel(int type, String data)
{
String topic, channel, nick, text, empty = "";
String tmp = XtMalloc(8192);
CHAN *c;
channel = strtok(data, " ");
c = FindChanByName(channel);
if ((! c) || (! XtIsRealized(c->w)))
goto end;
switch (type) {
case MSG_SERVER_PUBLIC:
nick = strtok(NULL, " ");
if (! (text = strtok(NULL, "\0")))
text = empty;
sprintf(tmp, "<%s> %s", nick, text);
InsertText(c->text, tmp);
#ifdef USE_SOUND
if (strcmp(nick, userInfo.userName))
PlaySound(sound[CHANMSG_SOUND]);
#endif
break;
case MSG_SERVER_TOPIC:
if ((topic = strtok(NULL, "\0")))
sprintf(tmp, "%s %s", channel, topic);
else
strcpy(tmp, channel);
XtVaSetValues(c->w, XmNtitle, tmp, NULL);
break;
case MSG_CLIENT_EMOTE:
nick = strtok(NULL, " ");
if (! (text = strtok(NULL, "\"")))
text = empty;
sprintf(tmp, "* %s %s", nick, text);
InsertText(c->text, tmp);
break;
}
end:
XtFree(tmp);
}
static void ShowQuickInfo(CHAN *c, String nick)
{
String values[3];
enum {SHARED, LINK, END};
ULIST *ul;
char shared[20], link[20];
for(ul = c->users; ul; ul = ul->next) {
if (! strcmp(ul->nick, nick))
break;
}
sprintf(shared, "%d", ul->shared);
values[SHARED] = shared;
sprintf(link, "%s", linkStr[ul->link]);
values[LINK] = link;
values[END] = NULL;
ShowInfo("quickInfo", values, 15);
}
static void UserMenuCB(Widget w , XtPointer clientData,
XmPopupHandlerCallbackStruct *cbs)
{
XmString *items;
String nick, reason, msg, tmp = XtMalloc(8192);
CHAN *c;
int itemCount;
enum {MESSAGE, QINFO, WHOIS, BROWSE, PING, OP, DEOP,
MUZZLE, UNMUZZLE, KICK, KILL, BAN};
XtVaGetValues(XtParent(XtParent(XtParent(w))),
XmNselectedItemCount, &itemCount, NULL);
if (! itemCount)
return;
XtVaGetValues(XtParent(XtParent(XtParent(w))),
XmNselectedItems, &items, NULL);
XmStringGetLtoR(items[0], XmFONTLIST_DEFAULT_TAG, &nick);
c = FindChanByWin(curWin);
switch ((int)clientData) {
case MESSAGE:
msg = GetInput("Message", "", 40);
if (! strlen(msg))
goto end;
sprintf(tmp, "/msg %s %s", nick, msg);
break;
case QINFO:
ShowQuickInfo(c, nick);
goto end;
case WHOIS:
sprintf(tmp, "/whois %s", nick);
break;
case BROWSE:
sprintf(tmp, "/browse %s", nick);
break;
case PING:
sprintf(tmp, "/ping %s", nick);
break;
case OP:
sprintf(tmp, "/op %s %s", c->name, nick);
break;
case DEOP:
sprintf(tmp, "/deop %s %s", c->name, nick);
break;
case MUZZLE:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/muzzle %s %s", nick, reason);
break;
case UNMUZZLE:
sprintf(tmp, "/unmuzzle %s", nick);
break;
case KICK:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/kick %s %s %s", c->name, nick, reason);
break;
case KILL:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/kill %s %s", nick, reason);
break;
case BAN:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/ban %s %s", nick, reason);
break;
}
CmdParse(tmp);
end:
XtFree(nick);
XtFree(tmp);
}
static void ChnEntryCB(Widget w, XtPointer clientData,
XmAnyCallbackStruct *cbs)
{
String c, s, tmp = XtMalloc(8192);
c = (String)clientData;
s = XmTextFieldGetString(w);
XmTextFieldSetString(w, "");
if (s[0] == '/')
CmdParse(s);
else {
sprintf(tmp, "%s %s", c, s);
if (SendMsg(MSG_CLIENT_PUBLIC, tmp))
Disconnect(strerror(errno));
}
XtFree(s);
XtFree(tmp);
}
static void PartChannelCB(Widget w, XtPointer clientData,
XmPushButtonCallbackStruct *cbs)
{
PartChannel((String)clientData);
}
static void ChannelWindow(CHAN *c)
{
Widget chanWin, chanForm, chanText, chanEntry, userList,
userMenu, partBtn, clearBtn;
Arg args[20];
int n;
Dimension w1, w2;
chanWin = XtVaCreatePopupShell("chanWin",
topLevelShellWidgetClass, topLevel,
XmNtitle, c->name,
XmNiconPixmap, napPix,
XmNiconName, c->name,
NULL);
c->w = chanWin;
chanForm = XtVaCreateManagedWidget("chanForm",
xmFormWidgetClass, chanWin,
XmNmarginWidth, 6,
XmNmarginHeight, 6,
NULL);
partBtn = XtVaCreateManagedWidget("partBtn",
xmPushButtonWidgetClass, chanForm,
XmNleftAttachment, XmATTACH_FORM,
XmNleftOffset, 20,
XmNbottomAttachment, XmATTACH_FORM,
XmNbottomOffset, 8,
NULL);
clearBtn = XtVaCreateManagedWidget("clearBtn",
xmPushButtonWidgetClass, chanForm,
XmNleftAttachment, XmATTACH_WIDGET,
XmNleftWidget, partBtn,
XmNleftOffset, 10,
XmNbottomAttachment, XmATTACH_FORM,
XmNbottomOffset, 8,
NULL);
XtVaGetValues(partBtn, XmNwidth, &w1, NULL);
XtVaGetValues(clearBtn, XmNwidth, &w2, NULL);
if (w1 > w2)
XtVaSetValues(clearBtn, XmNwidth, w1, NULL);
else
XtVaSetValues(partBtn, XmNwidth, w2, NULL);
chanEntry = XtVaCreateManagedWidget("chanEntry",
xmTextFieldWidgetClass, chanForm,
XmNleftAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_WIDGET,
XmNbottomWidget, partBtn,
XmNbottomOffset, 8,
NULL);
c->entry = chanEntry;
n = 0;
XtSetArg(args[n], XmNselectionPolicy, XmBROWSE_SELECT); n++;
XtSetArg(args[n], XmNscrollBarDisplayPolicy, XmSTATIC); n++;
XtSetArg(args[n], XmNlistSizePolicy, XmCONSTANT); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, chanEntry); n++;
XtSetArg(args[n], XmNbottomOffset, 10); n++;
userList = XmCreateScrolledList(chanForm, "userList", args, n);
c->list = userList;
XtManageChild(userList);
n = 0;
XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
XtSetArg(args[n], XmNeditable, False); n++;
XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
XtSetArg(args[n], XmNscrollHorizontal, False); n++;
XtSetArg(args[n], XmNwordWrap, True); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNrightWidget, userList); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, chanEntry); n++;
XtSetArg(args[n], XmNbottomOffset, 10); n++;
chanText = XmCreateScrolledText(chanForm, "chanText", args, n);
c->text = chanText;
XtManageChild(chanText);
userMenu = XmVaCreateSimplePopupMenu(userList, "userMenu",
(XtCallbackProc)UserMenuCB,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmNpopupEnabled, XmPOPUP_AUTOMATIC,
NULL);
n = 0;
XtSetArg(args[n], XmNinitialFocus, chanEntry); n++;
XtSetValues(chanForm, args, n);
XtAddCallback(chanForm, XmNfocusCallback,
(XtCallbackProc)ChatFocusCB, (XtPointer)"chanWin"),
XtAddCallback(chanEntry, XmNactivateCallback,
(XtCallbackProc)ChnEntryCB, (XtPointer)c->name);
XtAddCallback(chanEntry, XmNmodifyVerifyCallback,
(XtCallbackProc)ModifyVerifyCB, (XtPointer)c->name);
XtAddCallback(partBtn, XmNactivateCallback,
(XtCallbackProc)PartChannelCB, (XtPointer)c->name);
XtAddCallback(clearBtn, XmNactivateCallback,
(XtCallbackProc)ChatClearCB, (XtPointer)chanText);
XtAddCallback(chanText, XmNfocusCallback,
(XtCallbackProc)TextFocusCB, NULL);
XtAddCallback(chanText, XmNlosingFocusCallback,
(XtCallbackProc)TextLosingFocusCB, NULL);
XtAddCallback(chanEntry, XmNfocusCallback,
(XtCallbackProc)TextFocusCB, NULL);
XtAddCallback(chanEntry, XmNlosingFocusCallback,
(XtCallbackProc)TextLosingFocusCB, NULL);
XmAddWMProtocolCallback(chanWin,
XmInternAtom(XtDisplay(chanWin), "WM_DELETE_WINDOW", False),
(XtCallbackProc)PartChannelCB, (XtPointer)c->name);
XtPopup(chanWin, XtGrabNone);
}
void JoinChannel(String name)
{
if (SendMsg(MSG_CLIENT_JOIN, name))
Disconnect(strerror(errno));
}
void DoJoinChannel(String name)
{
CHAN *ptr, *newChn;
newChn = XtNew(CHAN);
newChn->name = XtNewString(name);
newChn->users = NULL;
newChn->next = NULL;
if (! channels)
channels = newChn;
else {
for (ptr = channels; ptr->next; ptr = ptr->next);
ptr->next = newChn;
}
ChannelWindow(newChn);
}
void PartChannel(String name)
{
if (SendMsg(MSG_CLIENT_PART, name))
Disconnect(strerror(errno));
}
void DoPartChannel(String name)
{
CHAN *ptr, *prevPtr = NULL;
String p;
for (ptr = channels; ptr; ptr = ptr->next) {
if (! strcmp(ptr->name, name))
break;
prevPtr = ptr;
}
if (! ptr) {
ErrMsg("DoPartChannel: no such channel");
return;
}
while (ptr->users) {
XtFree(ptr->users->nick);
p = (String)ptr->users;
ptr->users = ptr->users->next;
XtFree(p);
}
if (prevPtr) {
prevPtr->next = ptr->next;
} else {
channels = channels->next;
}
DestroyWin(ptr->w);
XtFree(ptr->name);
XtFree((char*)ptr);
}
void PartAllChannels(void)
{
while (channels)
DoPartChannel(channels->name);
}
static void PrivMenuCB(Widget w , XtPointer clientData,
XmPopupHandlerCallbackStruct *cbs)
{
String reason, tmp = XtMalloc(8192);
PRIV *priv;
Widget topLevel;
enum {WHOIS, BROWSE, PING, MUZZLE, UNMUZZLE, KILL, BAN};
for(topLevel = w; !XtIsTopLevelShell(topLevel);
topLevel = XtParent(topLevel));
priv = FindPrivByWin(topLevel);
switch ((int)clientData) {
case WHOIS:
sprintf(tmp, "/whois %s", priv->nick);
break;
case BROWSE:
sprintf(tmp, "/browse %s", priv->nick);
break;
case PING:
sprintf(tmp, "/ping %s", priv->nick);
break;
case MUZZLE:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/muzzle %s %s", priv->nick, reason);
break;
case UNMUZZLE:
sprintf(tmp, "/unmuzzle %s", priv->nick);
break;
case KILL:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/kill %s %s", priv->nick, reason);
break;
case BAN:
reason = GetInput("Reason", "", 40);
sprintf(tmp, "/ban %s %s", priv->nick, reason);
break;
}
CmdParse(tmp);
XtFree(tmp);
}
static void PrivEntryCB(Widget w, XtPointer clientData,
XmAnyCallbackStruct *cbs)
{
String nick, s, tmp;
PRIV *p;
nick = (String)clientData;
s = XmTextFieldGetString(w);
XmTextFieldSetString(w, "");
if (s[0] == '/')
CmdParse(s);
else {
tmp = (String)XtMalloc(strlen(nick) + strlen(s) + 4);
sprintf(tmp, "%s %s", nick, s);
if (SendMsg(MSG_CLIENT_PRIVMSG, tmp))
Disconnect(strerror(errno));
if (! (p = FindPrivByNick(nick))) {
ErrMsg("PrivEntryCB: no such window");
} else
InsertText(p->text, s);
XtFree(tmp);
}
XtFree(s);
}
static void ClosePriv(String nick)
{
PRIV *ptr, *prevPtr = NULL;
for (ptr = privs; ptr; ptr = ptr->next) {
if (! strcmp(ptr->nick, nick))
break;
prevPtr = ptr;
}
if (! ptr) {
ErrMsg("ClosePrivWindow: no such nick");
return;
}
if (prevPtr) {
prevPtr->next = ptr->next;
} else {
privs = privs->next;
}
DestroyWin(ptr->w);
XtFree(ptr->nick);
XtFree((char*)ptr);
}
void CloseAllPrivs(void)
{
while (privs)
ClosePriv(privs->nick);
}
static void ClosePrivCB(Widget w, XtPointer clientData,
XmPushButtonCallbackStruct *cbs)
{
ClosePriv((String)clientData);
}
static PRIV* PrivWindow(String nick)
{
Widget privWin, privForm, privText, privEntry, privMenu;
Widget closeBtn, clearBtn;
Arg args[20];
int n;
PRIV *ptr, *newPriv;
Dimension w1, w2;
newPriv = XtNew(PRIV);
newPriv->nick = XtNewString(nick);
newPriv->visible = False;
newPriv->next = NULL;
if (! privs)
privs = newPriv;
else {
for (ptr = privs; ptr->next; ptr = ptr->next);
ptr->next = newPriv;
}
privWin = XtVaCreatePopupShell("privWin",
topLevelShellWidgetClass, topLevel,
XmNtitle, newPriv->nick,
XmNiconPixmap, napPix,
XmNiconName, newPriv->nick,
NULL);
newPriv->w = privWin;
privForm = XtVaCreateManagedWidget("privForm",
xmFormWidgetClass, privWin,
XmNmarginWidth, 6,
XmNmarginHeight, 6,
NULL);
closeBtn = XtVaCreateManagedWidget("closeBtn",
xmPushButtonWidgetClass, privForm,
XmNleftAttachment, XmATTACH_FORM,
XmNleftOffset, 20,
XmNbottomAttachment, XmATTACH_FORM,
XmNbottomOffset, 8,
NULL);
clearBtn = XtVaCreateManagedWidget("clearBtn",
xmPushButtonWidgetClass, privForm,
XmNleftAttachment, XmATTACH_WIDGET,
XmNleftWidget, closeBtn,
XmNleftOffset, 10,
XmNbottomAttachment, XmATTACH_FORM,
XmNbottomOffset, 8,
NULL);
XtVaGetValues(closeBtn, XmNwidth, &w1, NULL);
XtVaGetValues(clearBtn, XmNwidth, &w2, NULL);
if (w1 > w2)
XtVaSetValues(clearBtn, XmNwidth, w1, NULL);
else
XtVaSetValues(closeBtn, XmNwidth, w2, NULL);
privEntry = XtVaCreateManagedWidget("privEntry",
xmTextFieldWidgetClass, privForm,
XmNleftAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_WIDGET,
XmNbottomWidget, closeBtn,
XmNbottomOffset, 8,
NULL);
newPriv->entry = privEntry;
n = 0;
XtSetArg(args[n], XmNeditMode, XmMULTI_LINE_EDIT); n++;
XtSetArg(args[n], XmNeditable, False); n++;
XtSetArg(args[n], XmNcursorPositionVisible, False); n++;
XtSetArg(args[n], XmNscrollHorizontal, False); n++;
XtSetArg(args[n], XmNwordWrap, True); n++;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNbottomWidget, privEntry); n++;
XtSetArg(args[n], XmNbottomOffset, 10); n++;
privText = XmCreateScrolledText(privForm, "privText", args, n);
newPriv->text = privText;
XtManageChild(privText);
privMenu = XmVaCreateSimplePopupMenu(privText, "privMenu",
(XtCallbackProc)PrivMenuCB,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmVaPUSHBUTTON, NULL, NULL, NULL, NULL,
XmNpopupEnabled, XmPOPUP_AUTOMATIC,
NULL);
n = 0;
XtSetArg(args[n], XmNinitialFocus, privEntry); n++;
XtSetValues(privForm, args, n);
XtAddCallback(privForm, XmNfocusCallback,
(XtCallbackProc)ChatFocusCB, (XtPointer)"privWin");
XtAddCallback(privEntry, XmNactivateCallback,
(XtCallbackProc)PrivEntryCB, (XtPointer)newPriv->nick);
XtAddCallback(privEntry, XmNmodifyVerifyCallback,
(XtCallbackProc)ModifyVerifyCB, (XtPointer)newPriv->nick);
XtAddCallback(closeBtn, XmNactivateCallback,
(XtCallbackProc)ClosePrivCB, (XtPointer)newPriv->nick);
XtAddCallback(clearBtn, XmNactivateCallback,
(XtCallbackProc)ChatClearCB, (XtPointer)privText);
XtAddCallback(privText, XmNfocusCallback,
(XtCallbackProc)TextFocusCB, NULL);
XtAddCallback(privText, XmNlosingFocusCallback,
(XtCallbackProc)TextLosingFocusCB, NULL);
XtAddCallback(privEntry, XmNfocusCallback,
(XtCallbackProc)TextFocusCB, NULL);
XtAddCallback(privEntry, XmNlosingFocusCallback,
(XtCallbackProc)TextLosingFocusCB, NULL);
XmAddWMProtocolCallback(privWin,
XInternAtom(XtDisplay(privWin), "WM_DELETE_WINDOW", False),
(XtCallbackProc)ClosePrivCB, (XtPointer)newPriv->nick);
return newPriv;
}
void UpdateUserList(int type, String data)
{
String channel, nick;
CHAN *ptr;
ULIST *newUser, *userPtr, *prevPtr = NULL;
XmString xms;
char tmp[256];
int n, shared, link;
channel = strtok(data, " ");
nick = strtok(NULL, " ");
shared = atoi(strtok(NULL, " "));
link = atoi(strtok(NULL, " "));
if (! (ptr = FindChanByName(channel))) {
ErrMsg("UpdateUserList: channel not found");
return;
}
if ((type == MSG_SERVER_JOIN) ||
(type == MSG_SERVER_CHANNEL_USER_LIST)) {
newUser = XtNew(ULIST);
newUser->nick = XtNewString(nick);
newUser->shared = shared;
newUser->link = link;
if (! ptr->users) {
ptr->users = newUser;
newUser->next = NULL;
n = 1;
} else {
for (userPtr = ptr->users, n = 1; userPtr;
userPtr = userPtr->next, n++) {
if (strcasecmp(userPtr->nick, nick) > 0)
break;
prevPtr = userPtr;
}
if (prevPtr) {
newUser->next = prevPtr->next;
prevPtr->next = newUser;
} else {
newUser->next = ptr->users;
ptr->users = newUser;
}
}
xms = XmStringCreateLocalized(nick);
XmListAddItems(ptr->list, &xms, 1, n);
XmStringFree(xms);
if (type == MSG_SERVER_JOIN) {
sprintf(tmp, "*** %s has joined channel %s", nick, channel);
InsertText(ptr->text, tmp);
#ifdef USE_SOUND
PlaySound(sound[JOIN_SOUND]);
#endif
}
} else {
for (userPtr = ptr->users; userPtr; userPtr = userPtr->next) {
if (! strcmp(userPtr->nick, nick))
break;
prevPtr = userPtr;
}
if (! userPtr) {
ErrMsg("UpdateUserList: no such nick");
return;
}
if (prevPtr) {
prevPtr->next = userPtr->next;
} else {
ptr->users = ptr->users->next;
}
XtFree(userPtr->nick);
XtFree((char*)userPtr);
xms = XmStringCreateLocalized(nick);
XmListDeleteItems(ptr->list, &xms, 1);
XmStringFree(xms);
sprintf(tmp, "*** %s has left channel %s", nick, channel);
InsertText(ptr->text, tmp);
#ifdef USE_SOUND
PlaySound(sound[PART_SOUND]);
#endif
}
}
void ChatActions(Widget w, XEvent *ev, String *params, int *numParams)
{
CHAN* c;
PRIV* p;
Widget topLevel;
for (topLevel = w; ! XtIsTopLevelShell(topLevel);
topLevel = XtParent(topLevel));
if (! strcmp(params[0], "partChan")) {
c = FindChanByWin(topLevel);
PartChannel(c->name);
} else if (! strcmp(params[0], "clearChan")) {
c = FindChanByWin(topLevel);
XmTextSetString(c->text, "");
} else if (! strcmp(params[0], "closePriv")) {
p = FindPrivByWin(topLevel);
ClosePriv(p->nick);
} else if (! strcmp(params[0], "clearPriv")) {
p = FindPrivByWin(topLevel);
XmTextSetString(p->text, "");
}
}