/* * 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 * mats_peterson@swipnet.se (Mats Peterson) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, ""); } } .