/******************************************************************** Copyright (C) 2000 Bassoukos Tassos 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; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *********************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "connection.h" #include "protocol.h" #include "messages.h" #include "transaction.h" #include "guiprefs.h" #include "smalltrans.h" #include "userlist.h" #include "login.h" #include "main.h" #include "users.h" #include "hldat.h" #include "chat.h" #include "pixmap.h" /* ================================= */ #define MESSAGES "Messages" typedef struct { Connection *c; GtkWidget *w; char *message; gboolean is_error; } UnkMessage; typedef struct { Connection *c; GList *windows; GList *chats; } MessageData; typedef struct { MessageData *md; ChatWindowWidgets cww; int socket; } MessageChat; typedef struct { MessageData *md; GtkWidget *toplevel; GtkWidget *text_widget; gpointer data; int socket; char *text; char *quote; } MessageWindow; static void message_chat_add(MessageChat *mc,int socket,char *message); /* =============================== */ static void message_destroy_event(GtkWidget *w,gpointer data){ MessageWindow *m=(MessageWindow *)data; m->md->windows=g_list_remove(m->md->windows,m); if(m->quote!=NULL) free(m->quote); if(m->text!=NULL) free(m->text); free(m); } static void message_chat_close(GtkWidget *w,MessageChat *mc){ mc->md->chats=g_list_remove(mc->md->chats,mc); gutils_nbpage_destroy(mc->cww.nb); free(mc); } static void message_destroy_all(Connection *c,gpointer data){ MessageData *md=(MessageData *)connection_get_data(c,MESSAGES); connection_set_data(c,MESSAGES,NULL); while(md->windows!=NULL) gtk_widget_destroy(((MessageWindow *)md->windows->data)->toplevel); while(md->chats!=NULL) message_chat_close(NULL,md->chats->data); free(md); } void check_messages(Connection *c){ MessageData *md; if(connection_get_data(c,MESSAGES)!=NULL) return; md=(MessageData *)calloc(sizeof(MessageData),1); md->c=c; md->windows=NULL; connection_set_data(c,MESSAGES,md); gtk_signal_connect(GTK_OBJECT(c),"destroy",GTK_SIGNAL_FUNC(message_destroy_all), NULL); } static MessageWindow *message_create_messagewindow(Connection *c,int socket){ MessageWindow *m=(MessageWindow *)calloc(sizeof(MessageWindow),1); MessageData *md=(MessageData *)connection_get_data(c,MESSAGES); m->md=md; m->toplevel=NULL; m->socket=socket; m->toplevel=NULL; m->text_widget=NULL; m->text=NULL; m->quote=NULL; md->windows=g_list_prepend(md->windows,m); return m; } void message_display_text(Connection *c,char *string){ MessageWindow *m=message_create_messagewindow(c,0); GtkWidget *label,*d; label=gtk_label_new(string); d=gnome_dialog_new(_("User Info"),GNOME_STOCK_BUTTON_CLOSE,NULL); if(c!=NULL && c->gui!=NULL && c->gui->main_window!=NULL) gnome_dialog_set_parent(GNOME_DIALOG(d),GTK_WINDOW(c->gui->main_window)); gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_LEFT); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(d)->vbox),label,TRUE,TRUE,0); m->toplevel=d; gtk_signal_connect(GTK_OBJECT(d),"destroy", GTK_SIGNAL_FUNC(message_destroy_event), (gpointer)m); gnome_dialog_button_connect_object(GNOME_DIALOG(d),0, GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(m->toplevel)); gtk_widget_show_all(GTK_WIDGET(d)); } static void message_start_send(GtkButton *b,MessageWindow *m){ Transaction *t; HLObject *o; if(m->toplevel==NULL || m->text_widget==NULL || m->text!=NULL) return; m->text=gtk_editable_get_chars(GTK_EDITABLE(m->text_widget),0,-1); t=transaction_new(m->md->c,HLCT_SENDPRIVM); message_add_object(t->request,create_number(HLO_SOCKET,m->socket)); o=create_string(HLO_MESSAGE,m->text); string_unix_to_net(o); message_add_object(t->request,o); if(m->quote!=NULL){ o=create_string(HLO_QUOTE,m->quote); string_unix_to_net(o); message_add_object(t->request,o); } gtk_signal_connect_object(GTK_OBJECT(t),"destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(m->toplevel)); transaction_set_error_check(t,strdup(_("Could not send Message:"))); transaction_start(t); } static void message_send_from_chat(ChatWindowWidgets *cww, gpointer data, int emote){ MessageChat *mc=data; HLObject *o; Transaction *t; char *text; text=gtk_editable_get_chars(GTK_EDITABLE(cww->message),0,-1); if(strlen(text)){ t=transaction_new(mc->md->c,HLCT_SENDPRIVM); message_add_object(t->request,create_number(HLO_SOCKET,mc->socket)); gtk_editable_delete_text(GTK_EDITABLE(cww->message),0,-1); message_chat_add(mc, mc->socket, text); o=create_string(HLO_MESSAGE,text); string_unix_to_net(o); message_add_object(t->request,o); transaction_set_error_check(t,strdup(_("Could not send Message:"))); transaction_start(t); } free(text); } static GtkWidget *get_text_with_label(char *label,char *text,GtkWidget **textwidget){ GtkWidget *vbox,*labelw,*textw,*sl; vbox=gtk_vbox_new(FALSE,0); if(label!=NULL){ labelw=gtk_label_new(label); gtk_box_pack_start(GTK_BOX(vbox),labelw,FALSE,FALSE,0); gtk_misc_set_alignment(GTK_MISC(labelw),0,0.5); } textw=gtk_text_new(NULL,NULL); if(text!=NULL) gtk_text_insert(GTK_TEXT(textw),NULL,NULL,NULL,text,-1); gtk_text_set_editable(GTK_TEXT(textw),FALSE); sl=gtk_scrolled_window_new(NULL,NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sl), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(sl),textw); gtk_box_pack_start(GTK_BOX(vbox),sl,TRUE,TRUE,0); if(textwidget!=NULL) *textwidget=textw; return vbox; } static GtkWidget *make_textboxes(MessageWindow *m,GtkWidget **msg){ char buf[256]; GtkWidget *hdr=NULL; UserlistEntry *ue; char *uname=NULL; if(m==NULL){ if(msg!=NULL) *msg=NULL; return NULL; } ue=get_user_by_socket(m->md->c,m->socket); if(ue!=NULL) uname=ue->name; if(m->quote!=NULL){ hdr=get_text_with_label(_("You wrote:"),m->quote,NULL); if(uname!=NULL) sprintf(buf,_("%s replied:"),uname); else sprintf(buf,_("Socket %d replied:"),m->socket); } else { if(uname!=NULL) sprintf(buf,_("%s writes:"),uname); else sprintf(buf,_("Socket %d writes:"),m->socket); } *msg=get_text_with_label(buf,m->text,NULL); return hdr; } gpointer message_create_simple(Connection *c, char *message, int socket, void *dummy){ MessageWindow *m=message_create_messagewindow(c,socket); MessageWindow *orig=dummy; GtkWidget *d,*text,*vbox,*label; UserlistEntry *ue=get_user_by_socket(c,socket); char buf[256]; if(orig==NULL){ if(ue==NULL) strcpy(buf,_("Message")); else sprintf(buf,_("Message to %s"),ue->name); } else { if(orig->text!=NULL) m->quote=strdup(orig->text); if(ue==NULL) strcpy(buf,_("Reply")); else sprintf(buf,_("Reply to %s"),ue->name); } d=gnome_dialog_new(buf,GNOME_STOCK_BUTTON_CANCEL,"Send",NULL); m->toplevel=d; gnome_dialog_button_connect_object(GNOME_DIALOG(d),0, GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(m->toplevel)); gnome_dialog_button_connect(GNOME_DIALOG(d),1, GTK_SIGNAL_FUNC(message_start_send), (gpointer)m); vbox=gtk_vbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(d)->vbox),vbox,TRUE,TRUE,0); label=make_textboxes(orig,&text); if(label!=NULL) gtk_box_pack_start(GTK_BOX(vbox),label,TRUE,TRUE,0); if(text!=NULL) gtk_box_pack_start(GTK_BOX(vbox),text,TRUE,TRUE,0); label=get_text_with_label(orig==NULL?NULL:_("Your reply:"),message,&text); gtk_text_set_editable(GTK_TEXT(text),TRUE); gtk_box_pack_start(GTK_BOX(vbox),label,TRUE,TRUE,0); if(message!=NULL) gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,"\n\n",1); m->text_widget=text; if(m->md->c->gui->main_window!=NULL) gnome_dialog_set_parent(GNOME_DIALOG(d), GTK_WINDOW(m->md->c->gui->main_window)); gtk_window_set_focus(GTK_WINDOW(d),m->text_widget); gtk_window_set_modal(GTK_WINDOW(d),FALSE); gtk_signal_connect(GTK_OBJECT(text),"activate", GTK_SIGNAL_FUNC(message_start_send), (gpointer)m); gtk_signal_connect(GTK_OBJECT(d),"destroy", GTK_SIGNAL_FUNC(message_destroy_event), (gpointer)m); guiprefs_add_window(GTK_WINDOW(d),"Server/Messages/ReplyDialogSize"); gtk_window_set_policy(GTK_WINDOW(d), FALSE, TRUE, FALSE); gtk_widget_show_all(d); return m; } static void message_clear_window(GtkButton *b,gpointer data){ MessageWindow *m=(MessageWindow *)data; if(m->text_widget!=NULL) gtk_editable_delete_text(GTK_EDITABLE(m->text_widget),0,-1); } static void message_open_reply(GtkButton *b,gpointer data){ MessageWindow *m=(MessageWindow *)data; message_create_simple(m->md->c,NULL,m->socket,m); gtk_widget_destroy(m->toplevel); } static void message_quote_and_fill(char *msg, GtkWidget *text){ char **v=g_strsplit(msg,"\n",0); int i; gtk_text_freeze(GTK_TEXT(text)); if(v!=NULL) for(i=0;v[i]!=NULL;i++){ gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,"> ",2); gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,v[i],-1); gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,"\n",1); } gtk_text_insert(GTK_TEXT(text),NULL,NULL,NULL,"\n",1); g_strfreev(v); gtk_text_thaw(GTK_TEXT(text)); } static void message_open_reply_quoted(GtkButton *t,gpointer data){ MessageWindow *m=(MessageWindow *)data,*b; b=(MessageWindow *)message_create_simple(m->md->c,NULL,m->socket,m); message_quote_and_fill(m->text,b->text_widget); gtk_widget_destroy(m->toplevel); } static void message_chat_add(MessageChat *mc,int socket,char *message){ UserlistEntry *ue=get_user_by_socket(mc->md->c,socket); char buf[14]; char **arr,*full,*name; int i; if(ue==NULL){ sprintf(buf,_("Socket %d"),socket); name=buf; } else { name=ue->name; } arr=g_strsplit(message,"\r",0); for(i=0;arr[i]!=NULL;i++){ full=g_strdup_printf("%s: %s",name,arr[i]); chat_pane_add_text(&mc->cww,full); free(full); } g_strfreev(arr); } static gint message_check_single_chat(const MessageChat *mc, const int *socket){ return mc->socket-*socket; } static MessageChat *message_check_chat(Connection *c,int socket){ MessageData *md=(MessageData *)connection_get_data(c,MESSAGES); GList *gl; gl=g_list_find_custom(md->chats,&socket,(GCompareFunc)message_check_single_chat); return gl?gl->data:NULL; } static void message_start_chat(GtkButton *c,MessageWindow *m){ static GnomeUIInfo chat_toolbar[] = { GNOMEUIINFO_ITEM_STOCK("Close",N_("Close chat"), message_chat_close, HL_STOCK_PIXMAP_CLOSE_PAGE), GNOMEUIINFO_END }; char buf[32],*name; UserlistEntry *ue=get_user_by_socket(m->md->c,m->socket); MessageChat *mc=malloc(sizeof(MessageChat)); if(ue==NULL){ sprintf(buf,_("Socket %d"),m->socket); name=buf; } else { name=ue->name; } mc->md=m->md; mc->socket=m->socket; mc->md->chats=g_list_prepend(mc->md->chats,mc); mc->cww.nb=gutils_nbpage_new("MessageChat",chat_toolbar, mc->md->c,name,"announce",mc); mc->cww.sendmsg=message_send_from_chat; mc->cww.user_data=mc; chat_pane_create(mc->cww.nb,&mc->cww,FALSE); gtk_widget_hide(mc->cww.emote); userwidget_add(mc->cww.userwidget,mc->socket); if(mc->md->c->server_socket_no!=-1) userwidget_add(mc->cww.userwidget,mc->md->c->server_socket_no); gtk_widget_destroy(m->toplevel); } static MessageWindow *message_handle_privmsg(Connection *c,Message *mess, int socket,char *nick, char *origmsg,char *quote){ MessageWindow *m; char buf[256]; GtkWidget *vbox,*label,*msg; GtkWidget *text_message=NULL,*paned=NULL; int button=0; UserlistEntry *ue; char *uname; MessageChat *mc; if(userlist_get_ignored(c,socket)==TRUE) return NULL; if((mc=message_check_chat(c,socket))!=NULL) { message_chat_add(mc,socket,origmsg); return NULL; } m=message_create_messagewindow(c,socket); if(quote!=NULL) m->quote=strdup(quote); if(origmsg!=NULL) m->text=strdup(origmsg); ue=get_user_by_socket(c,socket); uname=(ue!=NULL && ue->name!=NULL)?ue->name:nick; if(uname!=NULL) sprintf(buf,_("Message from %s - %s"),uname, C_NAME(c)); else sprintf(buf,_("Message from socket %d - %s"),socket, C_NAME(c)); m->toplevel=gnome_dialog_new(buf,NULL); vbox=GNOME_DIALOG(m->toplevel)->vbox; if(prefs_messages_show_with_reply==TRUE){ gnome_dialog_append_button(GNOME_DIALOG(m->toplevel),_("Clear")); gnome_dialog_button_connect(GNOME_DIALOG(m->toplevel),button++, GTK_SIGNAL_FUNC(message_clear_window), (gpointer)m); gnome_dialog_append_button(GNOME_DIALOG(m->toplevel),_("Send")); gnome_dialog_button_connect(GNOME_DIALOG(m->toplevel),button++, GTK_SIGNAL_FUNC(message_start_send), (gpointer)m); paned=gtk_vpaned_new(); gtk_box_pack_end(GTK_BOX(vbox),paned,TRUE,TRUE,2); label=get_text_with_label(_("Your reply:"),NULL,&text_message); gtk_text_set_editable(GTK_TEXT(text_message),TRUE); gtk_paned_pack2(GTK_PANED(paned),label,TRUE,FALSE); if(prefs_messages_reply_quoted==TRUE) message_quote_and_fill(origmsg,text_message); gtk_window_set_focus(GTK_WINDOW(m->toplevel),text_message); guiprefs_add_window(GTK_WINDOW(m->toplevel),"Server/Messages/MessageReplySize"); } else { if(prefs_messages_reply_quoted==FALSE){ gnome_dialog_append_button(GNOME_DIALOG(m->toplevel),_("Reply Quoted")); gnome_dialog_button_connect(GNOME_DIALOG(m->toplevel),button++, GTK_SIGNAL_FUNC(message_open_reply_quoted), (gpointer)m); } gnome_dialog_append_button(GNOME_DIALOG(m->toplevel),_("Reply")); gnome_dialog_button_connect(GNOME_DIALOG(m->toplevel),button++, GTK_SIGNAL_FUNC(message_open_reply), (gpointer)m); guiprefs_add_window(GTK_WINDOW(m->toplevel),"Server/Messages/MessageDialogSize"); } gnome_dialog_append_button(GNOME_DIALOG(m->toplevel),_("Chat")); gnome_dialog_button_connect(GNOME_DIALOG(m->toplevel),button++, GTK_SIGNAL_FUNC(message_start_chat),m); gnome_dialog_append_button(GNOME_DIALOG(m->toplevel),GNOME_STOCK_BUTTON_CLOSE); gnome_dialog_button_connect_object(GNOME_DIALOG(m->toplevel),button++, GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(m->toplevel)); label=make_textboxes(m,&msg); if(label!=NULL) gtk_box_pack_start(GTK_BOX(vbox),label,TRUE,TRUE,0); if(paned!=NULL){ gtk_paned_pack1(GTK_PANED(paned),msg,TRUE,FALSE); } else { gtk_box_pack_start(GTK_BOX(vbox),msg,TRUE,TRUE,0); } if(prefs_messages_show_with_reply==TRUE){ free(m->quote); m->quote=m->text; m->text=NULL; } if(m->text_widget==NULL) m->text_widget=text_message; if(m->text_widget!=NULL) gtk_signal_connect(GTK_OBJECT(m->text_widget),"activate", GTK_SIGNAL_FUNC(message_start_send), (gpointer)m); gtk_window_set_policy(GTK_WINDOW(m->toplevel), FALSE, TRUE, FALSE); gtk_window_set_modal(GTK_WINDOW(m->toplevel),FALSE); return m; } static MessageWindow *message_handle_error(Connection *c,char *message){ MessageWindow *m=message_create_messagewindow(c,0); GtkWidget *parent=main_app; if(c!=NULL && c->gui!=NULL && c->gui->main_window!=NULL) parent=c->gui->main_window; m->toplevel=gnome_error_dialog_parented(message,GTK_WINDOW(parent)); play_sound(HL_SOUND_ERROR); return m; } static MessageWindow *message_handle_broadcast(Connection *c,char *message){ MessageWindow *m=message_create_messagewindow(c,0); GtkWidget *parent=main_app; if(c!=NULL && c->gui!=NULL && c->gui->main_window!=NULL) parent=c->gui->main_window; m->toplevel=gnome_ok_dialog_parented(message,GTK_WINDOW(parent)); play_sound(HL_SOUND_SERVMSG); return m; } void handle_server_message(Connection *c,Message *mess,gpointer data){ HLObject *message=message_find_object(mess,HLO_MESSAGE); HLObject *sock=message_find_object(mess,HLO_SOCKET); HLObject *nick=message_find_object(mess,HLO_NICK); HLObject *quote=message_find_object(mess,HLO_QUOTE); MessageWindow *m=NULL; if(message!=NULL){ string_net_to_unix(message); if(quote!=NULL) string_net_to_unix(quote); if(message_find_object(mess,HLO_PARAMETER)!=NULL) m=message_handle_error(c,message->data.string); else { if(sock==NULL) m=message_handle_broadcast(c,message->data.string); else m=message_handle_privmsg(c, mess, sock->data.number, nick?nick->data.string:NULL, message->data.string, quote?quote->data.string:NULL); } if(m!=NULL && m->toplevel!=NULL){ gtk_signal_connect(GTK_OBJECT(m->toplevel),"destroy", GTK_SIGNAL_FUNC(message_destroy_event), (gpointer)m); gtk_widget_show_all(m->toplevel); } } message_unref(mess); } /* ================================================ */ void show_message_error(Connection *c,char *text){ MessageWindow *m=message_handle_error(c,text); gtk_signal_connect(GTK_OBJECT(m->toplevel),"destroy", GTK_SIGNAL_FUNC(message_destroy_event), (gpointer)m); gtk_widget_show_all(m->toplevel); } static gboolean show_error_idle(gpointer data){ UnkMessage *um=data;; if(um==NULL) return FALSE; if(um->c!=NULL && um->c->gui!=NULL && um->c->gui->main_window!=NULL){ show_message_error(um->c,um->message); } else { GtkWidget *dialog,*parent; /* FIXME: is this neccesary? */ parent=um->w; if(um->w==NULL) parent=main_app; dialog=gnome_error_dialog_parented(um->message,GTK_WINDOW(parent)); gnome_dialog_close_hides(GNOME_DIALOG(dialog),FALSE); gtk_widget_show_all(dialog); } free(um->message); free(um); return FALSE; } static void show_error_ap(Connection *c, GtkWidget *w, const char *fmt, va_list ap){ UnkMessage *um = malloc(sizeof(UnkMessage)); um->c = c; um->w = w; um->message=g_strdup_vprintf(fmt, ap); gtk_idle_add(show_error_idle, um); } void show_error(Connection *c, GtkWidget *w, const char *fmt, ...){ va_list ap; va_start(ap, fmt); show_error_ap(c, w, fmt, ap); va_end(ap); } void show_error_errno(Connection *c, GtkWidget *w, const char *fmt, ...){ va_list ap; char *fmt2; va_start(ap, fmt); fmt2=g_strdup_printf("%s\n%s", fmt, strerror(errno)); show_error_ap(c, w, fmt2, ap); free(fmt2); va_end(ap); } /* ========================================= */ void handle_server_disconnect(Connection *c,Message *m, gpointer data) { char buffer[512]; HLObject *o; o = message_find_object(m, HLO_MESSAGE); snprintf(buffer, 512, "Disconnected: %s", o ? o->data.string : "(no reason)"); message_unref(m); show_message_error(NULL, buffer); connection_cancel(c); } void handle_banner(Connection *c,Message *m,gpointer data){ #ifdef DEBUG HLObject *bannerurl=message_find_object(m,HLO_BANNERURL); HLObject *bannertype=message_find_object(m,HLO_BANNERTYPE); printf("Banner type=%s at %s\n", bannertype?bannertype->data.string:"(none)", bannerurl?bannerurl->data.string:"(none)"); #endif message_unref(m); }