Yattm - unified GTK instant-messaging client logo
   [Generated for version 0.2-17 - Mon Jan 6 19:01:23 GMT+1 2003]

Home - Main Page - Data Structures - File List - Data Fields - Globals

chat_window.c

Go to the documentation of this file.
00001 /*
00002  * Yattm
00003  *
00004  * Copyright (C) 1999, Torrey Searle <tsearle@uci.edu>
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 /*
00023  * chat_window.c
00024  * implementation for the conversation window
00025  * This is the window where you will be doing most of your talking :)
00026  *
00027  */
00028 
00029 #ifdef HAVE_CONFIG_H
00030 #  include <config.h>
00031 #endif
00032 
00033 #include "intl.h"
00034 #include <string.h>
00035 #include <stdlib.h>
00036 #include <gdk/gdkkeysyms.h>
00037 #include <ctype.h>
00038 #include <assert.h>
00039 
00040 #include "chat_window.h"
00041 #include "util.h"
00042 #include "gtkspell.h"
00043 #include "add_contact_window.h"
00044 #include "sound.h"
00045 #include "dialog.h"
00046 #include "globals.h"
00047 #include "status.h"
00048 #include "away_window.h"
00049 #include "message_parse.h"
00050 #include "gtk_eb_html.h"
00051 #include "plugin.h"
00052 #include "contact_actions.h"
00053 #include "smileys.h"
00054 #include "prefs.h"
00055 
00056 #include "pixmaps/tb_book_red.xpm"
00057 #include "pixmaps/tb_open.xpm"
00058 #include "pixmaps/tb_volume.xpm"
00059 #include "pixmaps/tb_edit.xpm"
00060 #include "pixmaps/tb_search.xpm"
00061 #include "pixmaps/tb_no.xpm"
00062 #include "pixmaps/tb_mail_send.xpm"
00063 #include "pixmaps/cancel.xpm"
00064 #include "pixmaps/smiley_button.xpm"
00065 
00066 #define BUF_SIZE 1024  /* Maximum message length */
00067 #ifndef NAME_MAX
00068 #define NAME_MAX 4096
00069 #endif
00070 
00071 
00072 #ifdef HAVE_ICONV_H
00073 #include <iconv.h>
00074 #include <errno.h>
00075 
00076 /*
00077     Recodes source text with iconv() and puts it in translated_text
00078     returns pointer to recoded text
00079 */
00080 
00081 #define RECODE_TO_REMOTE    1
00082 #define RECODE_TO_LOCAL     0
00083 
00084 /* forward declaration */
00085 static chat_window * find_tabbed_chat_window_index (int current_page);
00086 char *
00087 recode_if_needed(char * source_text, char * recoded_text, int direction)
00088 {
00089   size_t inleft;
00090   size_t outleft;
00091   char * ptr_src_text = source_text;
00092   char * ptr_recoded_text = recoded_text;
00093   iconv_t conv_desc;
00094   int tries;
00095 
00096   if( use_recoding == 1 )
00097     {
00098       if( direction == RECODE_TO_REMOTE )
00099     {
00100       conv_desc = iconv_open( cGetLocalPref("remote_encoding"), cGetLocalPref("local_encoding") );
00101     }
00102       else
00103     {
00104       conv_desc = iconv_open( cGetLocalPref("local_encoding"), cGetLocalPref("remote_encoding") );
00105     }
00106 
00107       if( conv_desc != (iconv_t)(-1) )
00108     {
00109       ptr_src_text = source_text;
00110       inleft = strlen(source_text) + 1;
00111       /* 'inleft*2' e.g. for 'Latin-x' to 'UTF-8', 
00112          which is 1-byte to 2-byte recoding */
00113       outleft = inleft * 2 + 1;
00114         
00115       for(tries = 0; tries < 4; tries++ )
00116         {
00117           if( iconv(conv_desc, &ptr_src_text, &inleft,
00118             &ptr_recoded_text, &outleft) == (size_t)(-1) )
00119         {
00120           if( inleft && errno == EILSEQ )
00121             {
00122             
00123               /* errno == EILSEQ, ptr_sec_text -> offending symbol
00124              errno == EIBVAL, ptr_sec_text -> beginning of incomplete mb seq
00125              errno == E2BIG (output buf is too small)
00126               */
00127         
00128               if( tries == 3 )
00129             {
00130               strcpy( ptr_recoded_text, ptr_src_text );
00131               fprintf( stderr, "Yattm: recoding broke 3 times,"
00132                    " leaving the rest of the line as it is...\n");
00133               break;
00134             } else
00135               {
00136                 /* trying to skip offending character
00137                    this may break input stream if it's multibyte
00138                 */
00139                 *ptr_recoded_text = '.'; /*  *ptr_src_text;  */
00140                 ptr_recoded_text++;
00141                 ptr_src_text++;
00142                 inleft--;
00143                 outleft--;
00144                 fprintf( stderr, "Yattm: charachter cannot be recoded, "
00145                      "trying to skip it...\n");
00146                 continue;
00147               }
00148             }
00149           else if (errno == EINVAL )
00150             {
00151               fprintf( stderr, "Yattm: recoding broke - "
00152                    "incomplete multibyte sequence at the end of the line.\n");
00153               break;
00154             }
00155           else if (errno == E2BIG )
00156             {
00157               fprintf( stderr, "Yattm: recoding buffer too small?!! Oops! :(\n");
00158               break;
00159             }
00160           else
00161             {
00162               fprintf( stderr, "Yattm: unknown recoding error.\n");
00163               break;
00164             }
00165         }
00166         }
00167       *(ptr_recoded_text + strlen(ptr_src_text)) = '\0'; // just in case :)
00168 
00169       iconv_close(conv_desc);
00170       return recoded_text;
00171     }
00172       else
00173     {
00174       fprintf( stderr, "Yattm: recoding from %s to %s is not valid, sorry!\n"
00175            "Turning recoding off.\n",
00176            cGetLocalPref("local_encoding"), cGetLocalPref("remote_encoding"));
00177       use_recoding = 0;
00178       return source_text;
00179     }
00180     }
00181 
00182   return source_text;
00183 }
00184 
00185 #endif  /* HAVE_ICONV_H */
00186 
00187 
00188 /* declare functions */
00189 void eb_update_window_title(chat_window * cw, gboolean new_message);
00190 void eb_update_window_title_to_tab(int tab, gboolean new_message);
00191 
00192 GList *outgoing_message_filters=NULL;
00193 GList *incoming_message_filters=NULL;
00194 
00195 static void set_tab_red(GtkNotebook *notebook, GtkWidget *child)
00196 {
00197   GtkStyle *style;
00198   GdkColor color;
00199 
00200   GtkWidget *label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), child);
00201 
00202   gtk_widget_realize(label);
00203     
00204   color.red = 65000;
00205   color.green = 0;
00206   color.blue = 0;
00207 
00208 
00209   style = gtk_style_new();
00210   gdk_font_unref(style->font);
00211   style->font = gdk_font_ref(label->style->font);
00212   style->fg[0] = color;
00213 
00214   gtk_widget_set_style(label, style);
00215 
00216 
00217 }
00218 
00219 static void set_tab_normal(GtkNotebook *notebook, GtkWidget *child)
00220 {
00221   GtkStyle *style;
00222   GdkColor color;
00223 
00224   GtkWidget *label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), child);
00225   gtk_widget_realize(label);
00226 
00227   color.red = 0;
00228   color.green = 0;
00229   color.blue = 0;
00230 
00231   style = gtk_style_new();
00232   gdk_font_unref(style->font);
00233   style->font = gdk_font_ref(label->style->font);
00234   style->fg[0] = GTK_WIDGET(notebook)->style->fg[0];
00235 
00236   gtk_widget_set_style(label, style);
00237   gtk_style_unref(style);
00238 }
00239 
00240 
00241 static void end_conversation(chat_window* cw)
00242 {
00243   GList * node;
00244 
00245   /* cw->window=NULL; */
00246 
00247   /* will this fix the weird tabbed chat segfault? */
00248   if (cw->contact != NULL)
00249     {
00250       cw->contact->chatwindow = NULL;
00251     }
00252 
00253   /*
00254    * Some protocols like MSN and jabber require that something
00255    * needs to be done when you stop a conversation
00256    * for every protocol that requires it we call their terminate
00257    * method
00258    */
00259 
00260   for(node = cw->contact->accounts; node; node = node->next)
00261     {
00262       eb_account *ea = (eb_account*)(node->data);
00263 
00264       if(eb_services[ea->service_id].sc->terminate_chat)
00265     {
00266       RUN_SERVICE(ea)->terminate_chat(ea);
00267     }
00268     }
00269 
00270   for(node=cw->history; node!=NULL; node=node->next)
00271   {
00272     free(node->data);
00273     node->data=NULL;
00274   }
00275 
00276   g_list_free(cw->history);
00277 
00278   /*
00279    * if we are logging conversations, time stamp when the conversation
00280    * has ended
00281    */
00282 
00283   if(do_logging)
00284     {
00285       time_t my_time = time(NULL);
00286 
00287       fprintf(cw->fp, _("%sConversation ended on %s %s\n"),
00288           (do_strip_html ? "" : "<P ALIGN=\"CENTER\"><B>"),
00289           g_strchomp(asctime(localtime(&my_time))),
00290           (do_strip_html ? "" : "</B></P>"));
00291     }
00292 
00293   /*
00294    * close the log file
00295    */
00296 
00297   if ((cw->fp != NULL )) fclose(cw->fp);
00298 
00299   /*
00300    * and free the memory we allocated to the chat window
00301    * NOTE: a memset is done to flush out bugs related to the
00302    * tabbed chat window
00303    */
00304 
00305   memset(cw, 0, sizeof(chat_window));
00306   g_free(cw);
00307 }
00308 
00309 /*
00310  * They guy closed the chat window, so we need to clean up
00311  */
00312 
00313 static void destroy_event(GtkWidget *widget, gpointer userdata)
00314 {
00315   chat_window* cw = (chat_window*)userdata;
00316   /* gotta clean up all of the people we're talking with */
00317 
00318   end_conversation(cw);
00319 }
00320 
00321 static void close_tab_callback(GtkWidget *button, gpointer userdata)
00322 {
00323   chat_window *cw = (chat_window*)userdata;
00324   GtkWidget *window = cw->window;
00325   GtkWidget *notebook = cw->notebook;
00326   gint tab_number;
00327 
00328   /* we got called to kill the tab, so find out which tab number
00329      we need to kill */
00330   tab_number = gtk_notebook_page_num(GTK_NOTEBOOK(notebook),
00331                      cw->notebook_child);
00332 
00333   gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), tab_number);
00334 
00335   /*
00336    * WARNING: AT THIS POINT IN TIME cw IS NO LONGER AVAILABLE
00337    * end_conversation calls a g_free on cw
00338    * gtk_notebook_remove_page triggers the destroy signal on the vbox
00339    * (which calls end_conversation via destroy_event)
00340    */
00341 
00342   /*
00343    * Question: Now, as soon as the last page from the notebook gets
00344    * removed the window disappears, and I didn't find a call to destroy
00345    * it so... i've left in this destroy.. but I really don't know if it
00346    * is needed...
00347    */
00348 
00349   if (gtk_notebook_get_number_pages(GTK_NOTEBOOK(notebook)) == 0) {
00350     // we just delete the last conversation, no point in having the window
00351     // around... note that this also causes end_conversation to be called...
00352     gtk_widget_destroy(window);
00353   }
00354 }
00355 
00356 static void add_unknown_callback(GtkWidget * add_button, gpointer userdata)
00357 {
00358   chat_window * cw = userdata;
00359 
00360   /* if something wierd is going on and the unknown contact has multiple
00361    * accounts, find use the perfered account
00362    */
00363 
00364   cw->perfered = find_suitable_remote_account(cw->perfered, cw->contact);
00365 
00366   /* if in the weird case that the unknown user has gone offline
00367    * just use the first account you see
00368    */
00369      
00370   if(!cw->perfered)
00371     cw->perfered = cw->contact->accounts->data;
00372 
00373   /* if that fails, something is seriously wrong
00374    * bail out while you can
00375    */
00376 
00377   if(!cw->perfered)
00378     return;
00379 
00380   /* now that we have a valid account, pop up the window already :) */
00381 
00382   add_unknown_account_window_new(cw->perfered);
00383 }
00384 
00385 #define GET_CHAT_WINDOW(cur_cw) {\
00386   if(do_tabbed_chat) { \
00387     chat_window *bck = cur_cw; \
00388     if (cur_cw->notebook) \
00389         cur_cw = find_tabbed_chat_window_index(gtk_notebook_get_current_page(GTK_NOTEBOOK(cur_cw->notebook))); \
00390     if (cur_cw == NULL) \
00391         cur_cw = bck; \
00392   } \
00393 }
00394 
00395 void send_message(GtkWidget *widget, gpointer d)
00396 {
00397   chat_window * data = (chat_window*)d;
00398   gchar buff[BUF_SIZE];
00399   gchar buff2[BUF_SIZE];
00400   gchar * text, *o_text;
00401   gchar * link_message;
00402   gchar * temp_message;
00403   struct tm * cur_time;
00404   time_t t;
00405 
00406   GList * filter_walk;
00407 
00408 #ifdef HAVE_ICONV_H
00409   /* 'BUF_SIZE*2' e.g. for 'Latin-x' to 'UTF-8',
00410      which is 1-byte to 2-byte recoding */
00411   char recode_buff[BUF_SIZE*2 + 1];
00412 #endif
00413   GET_CHAT_WINDOW(data);
00414 
00415   /*determine what is the best account to send to*/
00416   data->perfered= find_suitable_remote_account(data->perfered, data->contact);
00417   
00418 
00419   if(!data->perfered)
00420     {
00421       /*Eventually this will need to become a dialog box that pops up*/
00422 
00423       if(data->contact->send_offline && can_offline_message(data->contact))
00424     {
00425       data->perfered = can_offline_message(data->contact);
00426     }
00427       else
00428     {
00429       gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<hr>", 0,0,0);
00430       gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<b>Cannot send message - user is offline.</b>", 0, 0, 0);
00431       gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<hr>", 0,0,0);
00432       return;
00433     }
00434     }
00435 
00436   if(data->local_user && data->local_user->service_id != data->perfered->service_id)
00437     {
00438       data->local_user = NULL;
00439     }
00440 
00441   if(data->local_user && !data->local_user->connected)
00442     {
00443       data->local_user = NULL;
00444     }
00445 
00446   /*determine what is the best local account to use*/
00447 
00448   if(!data->local_user)
00449   {
00450     data->local_user =
00451                 find_suitable_local_account(data->local_user, data->perfered->service_id); 
00452   }
00453 
00454   if(!data->local_user) {
00455     return;
00456   }
00457 
00458   eb_update_window_title(data, FALSE);
00459 
00460   text = gtk_editable_get_chars(GTK_EDITABLE (data->entry), 0, -1);
00461 
00462   if(strlen(text) == 0)
00463     return;
00464 
00465   if(data->this_msg_in_history)
00466   {
00467     GList * node=NULL;
00468     GList * node2=NULL;
00469 
00470     for(node=data->history; node!=NULL ; node=node->next)
00471     {
00472       node2=node;
00473     }
00474     free(node2->data);
00475     node2->data=strdup(text);
00476     data->this_msg_in_history=0;
00477   } else {
00478     data->history=g_list_append(data->history, strdup(text));
00479     data->hist_pos=NULL;
00480   }
00481 
00482   temp_message = eb_smilify(strdup(text), RUN_SERVICE(data->local_user)->get_smileys());
00483   link_message = linkify(temp_message);
00484   g_free(temp_message);
00485 
00486   eb_update_window_title(data, FALSE);
00487 
00488   // Outbound filters here - Meredydd
00489   filter_walk=outgoing_message_filters;
00490 
00491   eb_debug(DBG_CORE, "Starting to run outgoing filters\n");
00492 
00493   while(filter_walk!=NULL)
00494   {
00495     gchar * (*ifilter)(eb_local_account *, eb_account *, struct contact *, gchar *);
00496 
00497     eb_debug(DBG_CORE, "Running an outgoing filter\n");
00498 
00499     ifilter=(gchar *(*)(eb_local_account *, eb_account *, struct contact *, gchar *))filter_walk->data;
00500 
00501     text=ifilter(data->local_user, data->perfered, data->contact, text);
00502     
00503     if(text==NULL) { return; } // Urgh, no cleanup, but it does it on strlen(text)==0 too
00504 
00505     filter_walk=g_list_next(filter_walk);
00506   }
00507 
00508   eb_debug(DBG_CORE, "Finished outgoing filters\n");
00509 
00510   // end outbound filters
00511   
00512   o_text = text;
00513   text = convert_eol(text);
00514   g_free(o_text);
00515   
00516 #ifdef HAVE_ICONV_H
00517     if(!eb_services[data->perfered->service_id].can_iconvert)
00518         {
00519         RUN_SERVICE(data->local_user)->send_im(
00520                             data->local_user,
00521                             data->perfered,
00522                             text);
00523         }
00524         else
00525         {
00526         RUN_SERVICE(data->local_user)->send_im(
00527                             data->local_user,
00528                             data->perfered,
00529                      recode_if_needed(text, recode_buff, RECODE_TO_REMOTE) );
00530         }
00531   /* seems like variable 'text' is not used any more down
00532      the function, so we don't have to assign it (BTW it's freed in the end)*/
00533 #else
00534   RUN_SERVICE(data->local_user)->send_im(
00535                      data->local_user,
00536                      data->perfered,
00537                      text);
00538 #endif
00539   serv_touch_idle();
00540 
00541   if(data->sound_enabled && data->send_enabled)
00542     play_sound(SEND);
00543 
00544   if (do_convo_timestamp)
00545     {
00546       time(&t);
00547       cur_time = localtime(&t);
00548       g_snprintf(buff2, BUF_SIZE, "%d:%.2d:%.2d %s", cur_time->tm_hour,
00549          cur_time->tm_min, cur_time->tm_sec,
00550          data->local_user->alias);
00551     }
00552   else
00553     {
00554       g_snprintf(buff2, BUF_SIZE, "%s", data->local_user->alias);
00555     }
00556 
00557   g_snprintf(buff, BUF_SIZE, "<FONT COLOR=\"#0000ff\"><B>%s: </B></FONT>", buff2);
00558 
00559   gtk_eb_html_add(EXT_GTK_TEXT(data->chat), buff, 1, 0, 0);
00560   gtk_eb_html_add(EXT_GTK_TEXT(data->chat), link_message, do_ignore_back, do_ignore_fore, do_ignore_font);
00561   gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<br>", 0, 0, 0);
00562 
00563   /* If an away message had been sent to this person, reset the away message tracker */
00564   /* It's probably faster to just do the assignment all the time--the test
00565      is there for code clarity. */
00566 
00567   if (data->away_msg_sent)
00568     {
00569       data->away_msg_sent = FALSE;
00570     }
00571 
00572   /* Log the message */
00573 
00574   if(do_logging) eb_log_message(data->fp, buff, link_message);
00575 
00576   gtk_editable_delete_text(GTK_EDITABLE (data->entry), 0, -1);
00577   g_free(link_message);
00578   g_free(text);
00579 
00580 
00581   /* if using tabs, then turn off the chat icon */
00582   if (data->notebook != NULL) {
00583     /* no more icons in the tabs */
00584     /* gtk_widget_hide(data->talk_pixmap); */
00585     /* printf("chat icon is off... \n"); */
00586     set_tab_normal(GTK_NOTEBOOK(data->notebook), data->notebook_child);
00587   }
00588 }
00589 
00590 /*These are the action handlers for the buttons*/
00591 
00592 /*** MIZHI
00593  * callback function for viewing the log
00594  */
00595 static void view_log_callback(GtkWidget *widget, gpointer d)
00596 {
00597     chat_window* data = (chat_window*)d;
00598     GET_CHAT_WINDOW(data);
00599     eb_view_log(data->contact);
00600 }
00601 
00602 static void insert_smiley (GtkWidget * widget, chat_window * cw) 
00603 {
00604     gint pos=0;
00605     GET_CHAT_WINDOW(cw);
00606     if(GTK_EDITABLE(cw->entry) && GTK_EDITABLE(cw->entry)->current_pos)
00607         pos=GTK_EDITABLE(cw->entry)->current_pos;
00608         gtk_editable_insert_text(GTK_EDITABLE (cw->entry), 
00609                 gtk_widget_get_name(widget),
00610                 strlen(gtk_widget_get_name(widget)),
00611                 &pos);
00612     gtk_widget_destroy(cw->smiley_window);
00613     cw->smiley_window = NULL;
00614 }
00615 
00616 static void show_smileys_callback(GtkWidget *widget, gpointer d)
00617 {
00618   chat_window *data = (chat_window*) d;
00619   GList *smileys = NULL;
00620   protocol_smiley * msmiley = NULL;
00621   GtkWidget * smileys_table = NULL;
00622   GtkWidget * button = NULL;
00623   GtkWidget *iconwid;
00624   GdkPixmap *icon;
00625   GdkBitmap *mask;
00626   GList     *done = NULL;
00627   int len = 0, real_len = 0, x=-1, y=0;
00628   int win_w=0, win_h=0, w, h;
00629   gint win_x, win_y;
00630   if(data->smiley_window != NULL) {
00631     /* close popup */
00632     gtk_widget_destroy(data->smiley_window);
00633     data->smiley_window = NULL;
00634     return;
00635   }
00636 
00637   if (data && data->local_user && RUN_SERVICE(data->local_user))
00638     smileys = RUN_SERVICE(data->local_user)->get_smileys();
00639   else 
00640     return;
00641   len = g_list_length(smileys);
00642   smileys_table = gtk_table_new(5,len/5 +((len%5==0)?1:2),TRUE);
00643   for(;smileys;smileys=g_list_next(smileys)) {
00644       GList * l;
00645       gboolean already_done = FALSE;
00646       smiley * dsmile = NULL;
00647       msmiley = (smileys->data);
00648       for(l=done; l; l=g_list_next(l)) {
00649           if(!strcmp((char*)l->data, msmiley->name)) {
00650          already_done = TRUE;
00651          break;
00652       }
00653       }
00654       
00655       if(already_done)
00656           continue;
00657       
00658       done = g_list_append(done, msmiley->name);
00659       
00660       dsmile = get_smiley_by_name(msmiley->name);
00661       if(dsmile != NULL) {
00662           icon = gdk_pixmap_create_from_xpm_d(data->window->window, &mask, NULL, dsmile->pixmap);
00663           iconwid = gtk_pixmap_new(icon, mask);
00664       sscanf (dsmile->pixmap [0], "%d %d", &w, &h);
00665           if(x<5) {
00666          x++;
00667          if(y==0) win_h+=h+2;
00668       }
00669       if(x==5) {
00670              y++;
00671          x=0;
00672          if(x==0) win_w+=w+2;
00673       }
00674           gtk_widget_show(iconwid);
00675       button = gtk_button_new();
00676       gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
00677           gtk_container_add(GTK_CONTAINER(button), iconwid);
00678       gtk_widget_show(button);
00679       gtk_widget_set_name(button,msmiley->text);
00680       gtk_signal_connect (GTK_OBJECT (button), "clicked",
00681                              GTK_SIGNAL_FUNC (insert_smiley), data);
00682       gtk_table_attach(GTK_TABLE(smileys_table),
00683                button,
00684                y,y+1,
00685                x,x+1,
00686                GTK_FILL, GTK_FILL, 0, 0);
00687       real_len++;
00688       }
00689   }
00690   
00691   g_list_free(done);
00692   done = NULL;
00693   gtk_table_resize(GTK_TABLE(smileys_table), 5,real_len/5 +((real_len%5==0)?0:1));
00694   
00695   data->smiley_window = gtk_window_new(GTK_WINDOW_DIALOG);
00696   gtk_window_set_modal(GTK_WINDOW(data->smiley_window), FALSE);
00697   gtk_window_set_wmclass(GTK_WINDOW(data->smiley_window), "yattm-chat", "Yattm");
00698   gtk_window_set_title(GTK_WINDOW(data->smiley_window), "Smileys");
00699   gtk_window_set_policy(GTK_WINDOW(data->smiley_window), FALSE, FALSE, FALSE);
00700   gtk_widget_realize(data->smiley_window);
00701 
00702   gtk_widget_show(data->smiley_window);
00703 
00704   gtk_container_add(GTK_CONTAINER(data->smiley_window), smileys_table);
00705   gtk_widget_show(smileys_table);
00706   
00707   /* move the window a bit after the cursor and in the screen */
00708   gdk_window_get_pointer (NULL, &win_x, &win_y, NULL);
00709   win_x += 5; win_y += 5;
00710   while ((win_x)+win_w > gdk_screen_width() - 30)
00711     win_x -= 20;
00712   while ((win_y)+win_h > gdk_screen_height() - 30)
00713     win_y -= 20;
00714   gdk_window_move_resize(data->smiley_window->window, win_x, win_y, win_w, win_h);
00715   
00716 }
00717 
00718 /*This is the callback for ignoring a user*/
00719 
00720 static void ignore_dialog_callback (GtkWidget *widget, gpointer userdata)
00721 {
00722   struct contact *c = (struct contact *)userdata;
00723   int response = (int)gtk_object_get_user_data(GTK_OBJECT(widget));
00724 
00725   if (response) 
00726     {
00727       move_contact(_("Ignore"), c);
00728       update_contact_list ();
00729       write_contact_list();
00730     }
00731 
00732 }
00733 
00734 static void ignore_callback (GtkWidget *ignore_button, gpointer userdata)
00735 {
00736   chat_window * cw = (chat_window *)userdata;
00737   gchar *buff = (gchar *)g_new0(gchar *, BUF_SIZE);
00738   GET_CHAT_WINDOW(cw);
00739     
00740   g_snprintf(buff, BUF_SIZE, _("Do you really want to ignore %s?\n"), cw->contact->nick);
00741 
00742   do_dialog(buff, _("Ignore Contact"), ignore_dialog_callback, cw->contact);    
00743   g_free(buff);
00744 }
00745 
00746 /*This is the callback for file x-fer*/
00747 static void send_file (GtkWidget * sendf_button, gpointer userdata)
00748 {
00749   eb_account *ea;
00750   chat_window *data  = (chat_window*)userdata;
00751   GET_CHAT_WINDOW(data);
00752   
00753   if ( data->contact->online == 0 )
00754     {
00755       gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<hr>", 0,0,0);
00756       gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<b>Cannot send message - user is offline.</b>", 0, 0, 0);
00757       gtk_eb_html_add(EXT_GTK_TEXT(data->chat), "<hr>", 0,0,0);
00758       return;
00759     }
00760 
00761   ea = find_suitable_remote_account(data->perfered,
00762                     data->contact);
00763 
00764   eb_do_send_file(ea);
00765 }
00766 
00767 /*These are the callback for setting the sound*/
00768 
00769 static void set_sound_on_toggle(GtkWidget * sound_button, gpointer userdata)
00770 {
00771   chat_window * cw = (chat_window *)userdata;
00772    
00773   /*Set the sound_enable variable depending on the toggle button*/
00774    
00775   if (GTK_TOGGLE_BUTTON (sound_button)->active)
00776     cw->sound_enabled = TRUE;
00777   else
00778     cw->sound_enabled = FALSE;
00779 }
00780 
00781 static void set_sound_on_click(GtkWidget * button, gpointer userdata)
00782 {
00783   chat_window * cw = (chat_window *)userdata;
00784   GET_CHAT_WINDOW(cw);
00785    
00786   if (cw->sound_enabled)
00787     {
00788       cw->sound_enabled = FALSE;
00789       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->sound_button), FALSE);
00790     }
00791 
00792   else
00793     {
00794       cw->sound_enabled = TRUE;
00795       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->sound_button), TRUE);
00796     }
00797 }
00798 
00799 /*This is the callback for closing the window*/
00800 
00801 static void close_win (GtkWidget * close_button, gpointer userdata)
00802 {
00803   chat_window * cw = (chat_window *)userdata;
00804   if(cw->smiley_window != NULL && cw->smiley_window->window != NULL) {
00805     /* close smileys popup */
00806     gtk_widget_destroy(cw->smiley_window);
00807     cw->smiley_window = NULL;
00808   }
00809   gtk_widget_destroy(cw->window);
00810 }
00811 
00812 static void allow_offline_on_toggle (GtkWidget *allow_button, gpointer userdata)
00813 { 
00814   chat_window * cw = (chat_window *)userdata;
00815 
00816   /*set send_offline based upon toggle button*/
00817 
00818   if (GTK_TOGGLE_BUTTON (allow_button)->active)
00819     {
00820       cw->contact->send_offline = TRUE;
00821     }
00822   else
00823     {
00824       cw->contact->send_offline = FALSE;
00825     }
00826 }
00827 
00828 static void allow_offline_on_click (GtkWidget * button, gpointer userdata)
00829 {
00830   chat_window * cw = (chat_window *)userdata;
00831   GET_CHAT_WINDOW(cw);
00832 
00833   if (cw->contact->send_offline)
00834     {
00835       cw->contact->send_offline = FALSE;
00836       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->allow_button), FALSE);
00837     }
00838   else
00839     {
00840       cw->contact->send_offline = TRUE;
00841       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->allow_button), TRUE);      
00842     }
00843 }
00844 
00845 static void change_local_account_on_click (GtkWidget * button, gpointer userdata)
00846 {
00847   GtkLabel *label= GTK_LABEL (GTK_BIN (button)->child);
00848   chat_window_account *cwa = (chat_window_account *)userdata;
00849   chat_window * cw;
00850   gchar *account;
00851   eb_local_account *ela=NULL;
00852 
00853   /* Should never happen */
00854   if(!cwa)
00855       return;
00856    cw=cwa->cw;
00857    ela=(eb_local_account *)cwa->data;
00858    cw->local_user=ela;
00859    /* don't free it */
00860    gtk_label_get(label, &account);
00861    eb_debug(DBG_CORE, "change_local_account_on_click: %s\n", account);
00862 }
00863 
00864 GtkWidget *get_local_accounts(chat_window *cw)
00865 {
00866       GtkWidget *submenu, *label, *button;
00867       char *handle=NULL, buff[256];
00868       eb_local_account *first_act=NULL, *subsequent_act=NULL;
00869       chat_window_account *cwa=NULL;
00870     
00871      /* Do we have a preferred remote account, no, get one */ 
00872      if(!cw->perfered) {
00873         cw->perfered = find_suitable_remote_account(NULL, cw->contact);
00874         if(!cw->perfered) /* The remote user is not online */
00875             return(NULL);
00876       }
00877       handle=cw->perfered->handle;
00878       eb_debug(DBG_CORE, "Setting menu item with label: %s\n", handle);
00879       /* Check to see if we have at least 2 local accounts suitable for the remote account */
00880       first_act = find_local_account_for_remote(cw->perfered, TRUE);
00881       subsequent_act = find_local_account_for_remote(NULL, TRUE);
00882       if(!first_act || !subsequent_act)
00883           return(NULL);
00884 
00885       first_act = find_local_account_for_remote(cw->perfered, TRUE);
00886 
00887       /* Start building the menu */
00888       label = gtk_menu_item_new_with_label(_("Change Local Account"));
00889       submenu = gtk_menu_new();
00890       gtk_menu_item_set_submenu(GTK_MENU_ITEM(label), submenu);
00891 
00892       subsequent_act = first_act;
00893       do {
00894       sprintf(buff, "%s:%s", get_service_name(subsequent_act->service_id), subsequent_act->alias);
00895           button = gtk_menu_item_new_with_label(buff);
00896           gtk_menu_append(GTK_MENU(submenu), button);
00897       cwa = g_new0(chat_window_account, 1);
00898       cwa->cw=cw;
00899       cwa->data=subsequent_act;
00900           gtk_signal_connect(GTK_OBJECT(button), "activate",
00901                      GTK_SIGNAL_FUNC(change_local_account_on_click), cwa);
00902           gtk_widget_show(button);
00903       } while( (subsequent_act = find_local_account_for_remote(NULL, TRUE)));
00904       gtk_widget_show(label);
00905       gtk_widget_show(submenu);
00906 
00907       return(label);
00908 }
00909 
00910 static void handle_focus(GtkWidget *widget, GdkEventFocus * event, 
00911              gpointer userdata)
00912 {
00913     chat_window * cw = (chat_window *)userdata;
00914     eb_update_window_title(cw, FALSE);
00915 
00916 }
00917 
00918 /*This handles the right mouse button clicks*/
00919 
00920 static void handle_click(GtkWidget *widget, GdkEventButton * event, 
00921              gpointer userdata)
00922 {
00923   chat_window * cw = (chat_window*)userdata;
00924 
00925   if (event->type == GDK_BUTTON_PRESS && event->button == 3)
00926     {
00927       GtkWidget *menu;
00928       GtkWidget *button;
00929       menu_data *md=NULL;
00930       menu_item_data *mid=NULL;
00931       ebmContactData *ecd=NULL;
00932       GList *list=NULL;
00933 
00934       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
00935                        "button_press_event");
00936       menu = gtk_menu_new();
00937 
00938       /*Add Contact Selection*/
00939 
00940       if(!strcmp(cw->contact->group->name, _("Unknown")))
00941     {
00942       button = gtk_menu_item_new_with_label(_("Add Contact"));
00943       gtk_signal_connect(GTK_OBJECT(button), "activate",
00944                  GTK_SIGNAL_FUNC(add_unknown_callback), cw);
00945       gtk_menu_append(GTK_MENU(menu), button);
00946       gtk_widget_show(button);
00947     }
00948 
00949       /*Allow Offline Messaging Selection*/
00950 
00951       if(can_offline_message(cw->contact))
00952     {   
00953       button = gtk_menu_item_new_with_label(_("Offline Messaging"));
00954       gtk_signal_connect(GTK_OBJECT(button), "activate",
00955                  GTK_SIGNAL_FUNC(allow_offline_on_click), cw);
00956       gtk_menu_append(GTK_MENU(menu), button);
00957       gtk_widget_show(button);
00958     }
00959 
00960       /*Allow account selection when there are multiple accounts for the same protocl */
00961       button = get_local_accounts(cw);
00962       if(button)
00963         gtk_menu_append(GTK_MENU(menu), button);
00964 
00965       /*Sound Selection*/
00966 
00967       if (cw->sound_enabled)
00968     button = gtk_menu_item_new_with_label(_("Disable Sounds"));
00969       else
00970     button = gtk_menu_item_new_with_label(_("Enable Sounds"));
00971 
00972       gtk_signal_connect(GTK_OBJECT(button), "activate",
00973              GTK_SIGNAL_FUNC(set_sound_on_click), cw);
00974       gtk_menu_append(GTK_MENU(menu), button);
00975       gtk_widget_show(button);
00976 
00977       /*View log selection*/
00978 
00979       button = gtk_menu_item_new_with_label(_("View Log"));
00980       gtk_signal_connect(GTK_OBJECT(button), "activate",
00981              GTK_SIGNAL_FUNC(view_log_callback), cw);
00982       gtk_menu_append(GTK_MENU(menu), button);
00983       gtk_widget_show(button);
00984         
00985       /*Send File Selection*/
00986 
00987       button = gtk_menu_item_new_with_label(_("Send File"));
00988       gtk_signal_connect(GTK_OBJECT(button), "activate",
00989              GTK_SIGNAL_FUNC(send_file), cw);
00990       gtk_menu_append(GTK_MENU(menu), button);
00991       gtk_widget_show(button);
00992 
00993       /*Ignore Section*/
00994 
00995       button = gtk_menu_item_new_with_label(_("Ignore Contact"));
00996       gtk_signal_connect(GTK_OBJECT(button), "activate",
00997              GTK_SIGNAL_FUNC(ignore_callback), cw);
00998       gtk_menu_append(GTK_MENU(menu), button);
00999       gtk_widget_show(button);
01000 
01001       /*Send message Section*/
01002 
01003       button = gtk_menu_item_new_with_label(_("Send Message"));
01004       gtk_signal_connect(GTK_OBJECT(button), "activate",
01005              GTK_SIGNAL_FUNC(send_message), cw);
01006       gtk_menu_append(GTK_MENU(menu), button);
01007       gtk_widget_show(button);  
01008 
01009       /*Add Plugin Menus*/
01010     md = GetPref(EB_CHAT_WINDOW_MENU);
01011     if(md) {
01012         for(list = md->menu_items; list; list  = g_list_next(list) ) {
01013             ecd=ebmContactData_new();
01014             ecd->contact=cw->contact->nick;
01015             mid=(menu_item_data *)list->data;
01016             mid->data=(ebmCallbackData *)ecd;
01017             eb_debug(DBG_CORE, "adding chat window item: %s\n", mid->label);
01018             button = gtk_menu_item_new_with_label(mid->label);
01019             gtk_menu_append(GTK_MENU(menu), button);
01020             gtk_signal_connect(GTK_OBJECT(button), "activate",
01021                     eb_generic_menu_function, mid);
01022             gtk_widget_show(button);
01023         }
01024     }
01025 
01026 
01027       /*Close Selection*/
01028 
01029       button = gtk_menu_item_new_with_label(_("Close"));
01030 
01031       if (do_tabbed_chat)
01032     {
01033       gtk_signal_connect(GTK_OBJECT(button), "activate",
01034                  GTK_SIGNAL_FUNC(close_tab_callback), cw);
01035     } else {
01036       gtk_signal_connect(GTK_OBJECT(button), "activate",
01037                  GTK_SIGNAL_FUNC(close_win), cw);
01038     }
01039       gtk_menu_append(GTK_MENU(menu), button);
01040       gtk_widget_show(button);
01041 
01042       gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
01043              event->button, event->time );
01044     }
01045 }
01046 
01047 static void send_typing_status(chat_window *cw)
01048 {
01049   /* typing send code */
01050   time_t now=time(NULL);
01051 
01052   if(!do_send_typing_notify)
01053     return;
01054 
01055   if(now>=cw->next_typing_send)
01056   {
01057     if(!cw->perfered) {
01058        if(!cw->contact)
01059           return;
01060     cw->perfered = find_suitable_remote_account(NULL, cw->contact);
01061     if(!cw->perfered) /* The remote user is not online */
01062        return;
01063     }
01064     cw->local_user=find_suitable_local_account(cw->local_user, cw->perfered->service_id);
01065     if(cw->local_user==NULL) { return; }
01066 
01067     if(RUN_SERVICE(cw->local_user)->send_typing!=NULL)
01068     {
01069       cw->next_typing_send=now + RUN_SERVICE(cw->local_user)->send_typing(cw->local_user, cw->perfered);
01070     }
01071   }
01072 }
01073 
01074 static gboolean chat_singleline_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
01075 {
01076   chat_window *cw = (chat_window *)data;
01077   if(!(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_MOD4_MASK)))
01078   {
01079     send_typing_status(cw);
01080   }
01081   return gtk_true();
01082 }
01083 
01084 static gboolean chat_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
01085 {
01086   chat_window *cw = (chat_window *)data;
01087   GdkModifierType modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_MOD4_MASK);
01088 
01089   eb_update_window_title(cw, FALSE);
01090 
01091   if (event->keyval == GDK_Return)
01092     {
01093       /* Just print a newline on Shift-Return */
01094       if (event->state & GDK_SHIFT_MASK)
01095     {
01096       event->state = 0;
01097     }
01098       else if (do_enter_send)
01099     {
01100       /*Prevents a newline from being printed*/
01101       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
01102 
01103       send_message(NULL, cw);
01104       return gtk_true();
01105         }
01106     }
01107   else if (event->keyval == GDK_Up)
01108     {
01109       gint p=0;
01110 
01111       if(cw->history==NULL) { return gtk_true(); }
01112 
01113       if(cw->hist_pos==NULL)
01114       {
01115         char * s;
01116         GList * node;
01117 
01118         s= gtk_editable_get_chars(GTK_EDITABLE (cw->entry), 0, -1);
01119 
01120         for(node=cw->history; node!=NULL ; node=node->next)
01121         {
01122           cw->hist_pos=node;
01123         }
01124 
01125         if(strlen(s)>0)
01126         {
01127           cw->history=g_list_append(cw->history, strdup(s));
01128           //cw->hist_pos=cw->history->next;
01129           g_free(s); // that strdup() followed by g_free() looks stupid, but it isn't
01130                         // - strdup() uses vanilla malloc(), and the destroy code uses
01131                         // vanilla free(), so we can't use something that needs to be
01132                         // g_free()ed (apparently glib uses an incompatible allocation
01133                         //system
01134           cw->this_msg_in_history=1;
01135         }
01136 
01137       } else {
01138         cw->hist_pos=cw->hist_pos->prev;
01139         if(cw->hist_pos==NULL)
01140         {
01141           GList * node;
01142           printf("Wrapped!\n");
01143           for(node=cw->history; node!=NULL ; node=node->next)
01144           {
01145             cw->hist_pos=node;
01146           }
01147         }
01148       }
01149 
01150       gtk_editable_delete_text(GTK_EDITABLE (cw->entry), 0, -1);
01151       gtk_editable_insert_text(GTK_EDITABLE (cw->entry), cw->hist_pos->data, strlen(cw->hist_pos->data), &p);
01152     }
01153   else if (event->keyval == GDK_Down)
01154     {
01155       gint p=0;
01156 
01157       if(cw->history==NULL || cw->hist_pos==NULL) { return gtk_true(); }
01158       cw->hist_pos=cw->hist_pos->next;
01159       if(cw->hist_pos==NULL)
01160       {
01161         gtk_editable_delete_text(GTK_EDITABLE (cw->entry), 0, -1);
01162       } else {
01163         gtk_editable_delete_text(GTK_EDITABLE (cw->entry), 0, -1);
01164         gtk_editable_insert_text(GTK_EDITABLE (cw->entry), cw->hist_pos->data, strlen(cw->hist_pos->data), &p);
01165       }
01166     }
01167   else if (cw->notebook != NULL)  /* only change tabs if this window is tabbed */
01168     {
01169       /*
01170        * this does not allow for using the same keyval for both prev and next
01171        * when next contains every modifier that previous has (with some more)
01172        * but i really don't think that will be a huge problem =)
01173        */
01174       if ((modifiers == accel_prev_tab.modifiers) && (event->keyval == accel_prev_tab.keyval))
01175     {
01176       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
01177       gtk_notebook_prev_page( GTK_NOTEBOOK(cw->notebook) );
01178       return gtk_true();
01179     }
01180       else if ((modifiers == accel_next_tab.modifiers) && (event->keyval == accel_next_tab.keyval))
01181     {
01182       gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
01183       gtk_notebook_next_page( GTK_NOTEBOOK(cw->notebook) );
01184       return gtk_true();
01185     }
01186     }
01187 
01188   if(cw->perfered==NULL || modifiers)
01189   { return gtk_false(); }
01190 
01191   if(!modifiers)
01192   {
01193     send_typing_status(cw);
01194   }
01195 
01196   return gtk_false();
01197 }
01198 
01199 static void chat_notebook_switch_callback(GtkNotebook *notebook, GtkNotebookPage *page, gint page_num, gpointer user_data)
01200 {
01201   /* find the contact for the page we just switched to and turn off their talking penguin icon */
01202   GList * l1;
01203   GList * l2;
01204   struct contact* c;
01205 
01206   for(l1 = groups; l1; l1=l1->next )
01207     {
01208       for(l2 = ((grouplist*)l1->data)->members; l2; l2=l2->next )
01209     {
01210       // if this contact's chatwindow is equal to this one,
01211       // then we need to end the conversation and remove the
01212       // notebook tab...
01213       c = (struct contact*)l2->data;
01214       if (c->chatwindow != NULL) {
01215         if (c->chatwindow->notebook_child == page->child) {
01216           /* get rid of this pixmap .. no more icons in the tabs */
01217           /* gtk_widget_hide(c->chatwindow->talk_pixmap); */
01218           set_tab_normal(GTK_NOTEBOOK(c->chatwindow->notebook), c->chatwindow->notebook_child);
01219           gtk_widget_grab_focus(c->chatwindow->entry);
01220           eb_update_window_title_to_tab (page_num, FALSE);
01221         }
01222       }
01223     }
01224     }
01225 }
01226 
01227 
01228 static chat_window* find_tabbed_chat_window()
01229 {
01230   GList * l1;
01231   GList * l2;
01232   struct contact* c;
01233   
01234   for(l1 = groups; l1; l1=l1->next )
01235     {
01236       for(l2 = ((grouplist*)l1->data)->members; l2; l2=l2->next )
01237         {
01238       c = (struct contact*)l2->data;
01239       if (c->chatwindow != NULL) {
01240         if (c->chatwindow->notebook != NULL) {
01241           return c->chatwindow;
01242         }
01243       }
01244     }
01245     }
01246 
01247   return NULL;
01248 }
01249 
01250 static chat_window *
01251 find_tabbed_chat_window_index (int current_page)
01252 {
01253   GList *l1;
01254   GList *l2;
01255   struct contact *c;
01256 
01257   chat_window *notebook_window = find_tabbed_chat_window ();
01258   if (notebook_window == NULL || notebook_window->notebook == NULL)
01259     {
01260       return NULL;
01261     }
01262 
01263 
01264   for (l1 = groups; l1; l1 = l1->next)
01265     {
01266       for (l2 = ((grouplist *) l1->data)->members; l2; l2 = l2->next)
01267     {
01268       c = (struct contact *) l2->data;
01269       if (c->chatwindow != NULL)
01270         {
01271           if (gtk_notebook_page_num
01272           (GTK_NOTEBOOK (notebook_window->notebook),
01273            c->chatwindow->notebook_child) == current_page)
01274         {
01275           return c->chatwindow;
01276         }
01277         }
01278     }
01279     }
01280 
01281   return NULL;
01282 }
01283 static chat_window *
01284 find_tabbed_current_chat_window ()
01285 {
01286   GList *l1;
01287   GList *l2;
01288   struct contact *c;
01289   int current_page = 0;
01290 
01291   chat_window *notebook_window = find_tabbed_chat_window ();
01292   if (notebook_window == NULL || notebook_window->notebook == NULL)
01293     {
01294       return NULL;
01295     }
01296 
01297   current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook_window->notebook));
01298 
01299 
01300   for (l1 = groups; l1; l1 = l1->next)
01301     {
01302       for (l2 = ((grouplist *) l1->data)->members; l2; l2 = l2->next)
01303     {
01304       c = (struct contact *) l2->data;
01305       if (c->chatwindow != NULL)
01306         {
01307           if (gtk_notebook_page_num
01308           (GTK_NOTEBOOK (notebook_window->notebook),
01309            c->chatwindow->notebook_child) == current_page)
01310         {
01311           return c->chatwindow;
01312         }
01313         }
01314     }
01315     }
01316 
01317   return NULL;
01318 }
01319 
01320 chat_window * eb_chat_window_new(eb_local_account * local, struct contact * remote)
01321 {
01322   GtkWidget *vbox;
01323   GtkWidget *hbox;
01324   GtkWidget *scrollwindow;
01325   GtkWidget *toolbar;
01326   GtkWidget *add_button;
01327   GtkWidget *sendf_button;
01328   GtkWidget *send_button;
01329   GtkWidget *view_log_button;
01330   GtkWidget *close_button;
01331   GtkWidget *ignore_button;
01332   GtkWidget *iconwid;
01333   GdkPixmap *icon;
01334   GdkBitmap *mask;
01335   GtkAccelGroup *accel_group;
01336   GtkWidget *menu;
01337   GtkWidget *button;
01338   GtkWidget *separator;
01339   GtkWidget *resize_bar;
01340   gchar buff[NAME_MAX];
01341   chat_window *cw;
01342   chat_window *tab_cw;
01343   GtkPositionType pos;
01344   GtkWidget *contact_label;
01345 
01346   if (do_ignore_unknown)
01347     {
01348       if (!strcmp(_("Unknown"), remote->group->name))
01349     return NULL;
01350     }
01351 
01352   /* first we allocate room for the new chat window */
01353   cw = g_new0(chat_window,1);
01354   cw->contact = remote;
01355   cw->away_msg_sent = 0;
01356   cw->perfered = NULL;
01357   cw->local_user = NULL;
01358 
01359   vbox = gtk_vbox_new(FALSE,0); 
01360            
01361   /* we're doing a tabbed chat */
01362   if (do_tabbed_chat) {
01363     /* look for an already open tabbed chat window */
01364     tab_cw = find_tabbed_chat_window();
01365     if (tab_cw == NULL) {
01366       /* none exists, create one */
01367       cw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
01368       gtk_window_set_wmclass(GTK_WINDOW(cw->window), "yattm-chat", "Yattm");
01369       gtk_window_set_policy(GTK_WINDOW(cw->window), TRUE, TRUE, TRUE);
01370       gtk_widget_realize(cw->window);
01371 
01372       cw->notebook = gtk_notebook_new();
01373         
01374       /* Set tab orientation.... */
01375       pos = GTK_POS_BOTTOM;
01376       switch (do_tabbed_chat_orient)
01377     {
01378     case 1:
01379       pos = GTK_POS_TOP;
01380       break;;
01381         
01382     case 2:
01383       pos = GTK_POS_LEFT;
01384       break;;
01385 
01386     case 3:
01387       pos = GTK_POS_RIGHT;
01388       break;;
01389 
01390     case 0:
01391     default:
01392       pos = GTK_POS_BOTTOM;
01393       break;;
01394     }
01395       gtk_notebook_set_tab_pos(GTK_NOTEBOOK(cw->notebook), pos);
01396       /* End tab orientation */
01397         
01398       gtk_notebook_set_scrollable(GTK_NOTEBOOK(cw->notebook), TRUE);
01399       gtk_container_add(GTK_CONTAINER(cw->window), cw->notebook);
01400 
01401       /* setup a signal handler for the notebook to handle page switches */
01402       gtk_signal_connect(GTK_OBJECT(cw->notebook), "switch-page",
01403              GTK_SIGNAL_FUNC(chat_notebook_switch_callback),
01404              cw);
01405         
01406 
01407       gtk_widget_show(cw->notebook);
01408     } else {
01409       cw->window = tab_cw->window;
01410       cw->notebook = tab_cw->notebook;
01411     }
01412 
01413     /* set up the text and close button */
01414     contact_label = gtk_label_new(remote->nick);
01415     gtk_widget_show(contact_label);
01416 
01417     /* we use vbox as our child. */
01418     gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
01419     cw->notebook_child = vbox;
01420     gtk_notebook_append_page(GTK_NOTEBOOK(cw->notebook), cw->notebook_child, contact_label);
01421     gtk_widget_show(cw->notebook_child);
01422   } else {
01423     /* setup like normal */
01424     cw->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
01425 
01426     gtk_window_set_wmclass(GTK_WINDOW(cw->window), "yattm-chat", "Yattm");
01427     gtk_window_set_policy(GTK_WINDOW(cw->window), TRUE, TRUE, TRUE);
01428     gtk_widget_realize(cw->window);
01429 
01430     cw->notebook = NULL;
01431     cw->notebook_child = NULL;
01432     gtk_container_add(GTK_CONTAINER(cw->window), vbox);
01433     gtk_widget_show(vbox);
01434   }
01435 
01436   /* Next line allows making window smaller than orig. size */
01437   cw->chat = ext_gtk_text_new(NULL,NULL);
01438   gtk_eb_html_init(EXT_GTK_TEXT(cw->chat));
01439   scrollwindow = gtk_scrolled_window_new(NULL, EXT_GTK_TEXT(cw->chat)->vadj);
01440   /*    gtk_widget_realize(cw->window); */
01441 
01442   gtk_window_set_title(GTK_WINDOW(cw->window), remote->nick);    
01443     
01444   eb_icon(cw->window->window);  
01445   gtk_widget_set_usize(scrollwindow, 375, 150);
01446   gtk_container_add(GTK_CONTAINER(scrollwindow), cw->chat);
01447   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwindow), 
01448                  GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
01449   gtk_widget_show(scrollwindow);
01450 
01451   /* Create the bar for resizing chat/window box */
01452 
01453   /*Add stuff for multi-line*/
01454    
01455   if(do_multi_line)
01456     {
01457       resize_bar = gtk_vpaned_new();
01458       gtk_paned_set_gutter_size(GTK_PANED(resize_bar), 20);
01459       gtk_paned_pack1(GTK_PANED(resize_bar), scrollwindow, TRUE, TRUE);
01460       gtk_widget_show(scrollwindow);
01461 
01462       scrollwindow = gtk_scrolled_window_new(NULL, NULL);
01463       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrollwindow), 
01464                      GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
01465         
01466       cw->entry = gtk_text_new(NULL, NULL);
01467      
01468       gtk_widget_set_usize(scrollwindow, 375, 50);
01469       gtk_container_add(GTK_CONTAINER(scrollwindow), cw->entry);
01470       
01471       gtk_text_set_editable(GTK_TEXT(cw->entry), TRUE);
01472       gtk_text_set_word_wrap(GTK_TEXT(cw->entry), TRUE);
01473       gtk_text_set_line_wrap(GTK_TEXT(cw->entry), TRUE);
01474 
01475 #ifdef HAVE_ISPELL
01476       if(do_spell_checking)
01477     {
01478       if(!gtkspell_running())
01479         {
01480           gchar *ispell_cmd[] = { "ispell", "-a", NULL };
01481           gtkspell_start(NULL, ispell_cmd);
01482         }
01483       gtkspell_attach(GTK_TEXT(cw->entry));
01484     }
01485 #endif
01486 
01487       gtk_signal_connect(GTK_OBJECT(cw->entry), "key_press_event",
01488              GTK_SIGNAL_FUNC(chat_key_press),
01489              cw);
01490 
01491       gtk_paned_pack2(GTK_PANED(resize_bar), scrollwindow, FALSE, FALSE);
01492       gtk_widget_show(scrollwindow);
01493       gtk_box_pack_start(GTK_BOX(vbox),resize_bar, TRUE, TRUE, 5);
01494       gtk_widget_show(resize_bar);
01495     }
01496 
01497   /*Or not multi-line*/
01498 
01499   else
01500     {
01501       cw->entry = gtk_entry_new();
01502       gtk_signal_connect(GTK_OBJECT(cw->entry), "key_press_event",
01503              GTK_SIGNAL_FUNC(chat_singleline_key_press),
01504              cw);
01505 
01506       gtk_box_pack_start(GTK_BOX(vbox), scrollwindow, TRUE,TRUE, 5);
01507       gtk_box_pack_start(GTK_BOX(vbox), cw->entry, FALSE,FALSE, 5);
01508     }
01509     
01510   gtk_container_set_border_width(GTK_CONTAINER(cw->window), do_tabbed_chat ? 2 : 5);
01511   /*    gtk_container_add(GTK_CONTAINER(cw->window), vbox); */
01512 
01513   gtk_signal_connect(GTK_OBJECT(cw->entry), "activate",
01514              GTK_SIGNAL_FUNC(send_message), cw);
01515 
01516   gtk_signal_connect(GTK_OBJECT(vbox), "destroy",
01517              GTK_SIGNAL_FUNC(destroy_event), cw);   
01518 
01519   gtk_signal_connect(GTK_OBJECT(cw->chat), "button_press_event",
01520              GTK_SIGNAL_FUNC(handle_click), cw);                        
01521   
01522   gtk_signal_connect(GTK_OBJECT(cw->window), "focus_in_event",
01523              GTK_SIGNAL_FUNC(handle_focus), cw);
01524 
01525   hbox = gtk_hbox_new(FALSE, 0);
01526   gtk_widget_set_usize(hbox, 200, 25);
01527    
01528   toolbar = gtk_toolbar_new(GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_ICONS);
01529   gtk_toolbar_set_button_relief(GTK_TOOLBAR(toolbar), GTK_RELIEF_NONE);
01530   gtk_container_set_border_width(GTK_CONTAINER(toolbar), 0);
01531   gtk_toolbar_set_space_size(GTK_TOOLBAR(toolbar), 5);
01532   
01533   /* Adding accelerators to windows*/
01534     
01535   accel_group = gtk_accel_group_new();
01536   gtk_window_add_accel_group(GTK_WINDOW(cw->window), accel_group);
01537   menu = gtk_menu_new();
01538   
01539   /* This is the same as handle_clicks, without showing the menu for
01540      accelerators to hopefully work better now. 
01541   */
01542 
01543   /*Add Contact Selection*/
01544    
01545   if(!strcmp(cw->contact->group->name, _("Unknown")))
01546     {
01547       button = gtk_menu_item_new_with_label(_("Add Contact"));
01548       gtk_signal_connect(GTK_OBJECT(button), "activate",
01549              GTK_SIGNAL_FUNC(add_unknown_callback),
01550              cw);
01551       gtk_menu_append(GTK_MENU(menu), button);
01552     }
01553       
01554   /*Allow Offline Messaging Selection*/
01555    
01556   if(can_offline_message(cw->contact))
01557     {   
01558       button = gtk_menu_item_new_with_label(_("Offline Messaging"));
01559       gtk_signal_connect(GTK_OBJECT(button), "activate",
01560              GTK_SIGNAL_FUNC(allow_offline_on_click),
01561              cw);
01562       gtk_widget_add_accelerator(button, "activate", accel_group, 
01563                  GDK_o, GDK_CONTROL_MASK,
01564                  GTK_ACCEL_VISIBLE);
01565       gtk_menu_append(GTK_MENU(menu), button);
01566     }
01567 
01568   /*Sound Selection*/
01569 
01570   if (cw->sound_enabled)
01571     {
01572       button = gtk_menu_item_new_with_label(_("Disable Sounds"));
01573     }
01574   else
01575     {
01576       button = gtk_menu_item_new_with_label(_("Enable Sounds"));
01577     }
01578        
01579   gtk_signal_connect(GTK_OBJECT(button), "activate",
01580              GTK_SIGNAL_FUNC(set_sound_on_click),
01581              cw); 
01582   gtk_widget_add_accelerator(button, "activate", accel_group, 
01583                  GDK_s, GDK_CONTROL_MASK,
01584                  GTK_ACCEL_VISIBLE);
01585   gtk_menu_append(GTK_MENU(menu), button);
01586 
01587   /* Ignore button section */
01588 
01589   button = gtk_menu_item_new_with_label(_("Ignore Contact"));
01590   gtk_signal_connect(GTK_OBJECT(button), "activate",
01591              GTK_SIGNAL_FUNC(ignore_callback), cw);
01592   gtk_widget_add_accelerator(button, "activate", accel_group,
01593                  GDK_g, GDK_CONTROL_MASK,
01594                  GTK_ACCEL_VISIBLE);
01595   gtk_menu_append(GTK_MENU(menu), button);
01596 
01597   /*Send File Selection*/ 
01598 
01599   button = gtk_menu_item_new_with_label(_("Send File"));
01600   gtk_signal_connect(GTK_OBJECT(button), "activate",
01601              GTK_SIGNAL_FUNC(send_file),
01602              cw);
01603   gtk_widget_add_accelerator(button, "activate", accel_group, 
01604                  GDK_t, GDK_CONTROL_MASK,
01605                  GTK_ACCEL_VISIBLE);
01606   gtk_menu_append(GTK_MENU(menu), button);
01607     
01608   /*Send Selection*/
01609       
01610   button = gtk_menu_item_new_with_label(_("Send Message"));
01611   gtk_signal_connect(GTK_OBJECT(button), "activate",
01612              GTK_SIGNAL_FUNC(send_message),
01613              cw);
01614   gtk_widget_add_accelerator(button, "activate", accel_group, 
01615                  GDK_r, GDK_CONTROL_MASK,
01616                  GTK_ACCEL_VISIBLE);
01617   gtk_menu_append(GTK_MENU(menu), button);
01618 
01619   /*** MIZHI
01620    * view log file selection
01621    */
01622 
01623   button = gtk_menu_item_new_with_label(_("View log"));
01624   gtk_signal_connect(GTK_OBJECT(button), "activate",
01625              GTK_SIGNAL_FUNC(view_log_callback),
01626              cw);
01627   gtk_widget_add_accelerator(button, "activate", accel_group, 
01628                  GDK_l, GDK_CONTROL_MASK,
01629                  GTK_ACCEL_VISIBLE);
01630   gtk_menu_append(GTK_MENU(menu), button);
01631     
01632   /*Close Selection*/
01633 
01634   button = gtk_menu_item_new_with_label(_("Close"));
01635 
01636   gtk_signal_connect(GTK_OBJECT(button), "activate",
01637              GTK_SIGNAL_FUNC(close_win),
01638              cw);
01639 
01640   gtk_widget_add_accelerator(button, "activate", accel_group,
01641                  GDK_q, GDK_CONTROL_MASK,
01642                  GTK_ACCEL_VISIBLE);
01643   gtk_menu_append(GTK_MENU(menu), button);  
01644 
01645   /*This is we decide whether or not the add button should be displayed*/
01646    
01647   if (!strcmp(cw->contact->group->name, _("Unknown")))
01648     {
01649       /*This is the add button*/
01650       icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_book_red_xpm);
01651       iconwid = gtk_pixmap_new(icon , mask);
01652       gtk_widget_show(iconwid);
01653       add_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01654                        _("Add"),
01655                        _("Add Contact"),
01656                        _("Add"),
01657                        iconwid,
01658                        GTK_SIGNAL_FUNC(add_unknown_callback),
01659                        cw);
01660       gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01661     }
01662 
01663   /*Decide whether the offline messaging button should be displayed*/
01664 
01665   if(can_offline_message(remote))
01666     {
01667       icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_edit_xpm);
01668       iconwid = gtk_pixmap_new(icon , mask);
01669       gtk_widget_show(iconwid);
01670       cw->allow_button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
01671                             GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
01672                             NULL,
01673                             _("Allow"),
01674                             _("Allow Offline Messaging CTRL+O"),
01675                             _("Allow"),
01676                             iconwid,
01677                             GTK_SIGNAL_FUNC(allow_offline_on_toggle),
01678                             cw);
01679       gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01680       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->allow_button), FALSE);
01681     }
01682    
01683   /* smileys */
01684   if(do_smiley == 1) {
01685       icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, smiley_button_xpm);
01686       iconwid = gtk_pixmap_new(icon, mask);
01687       gtk_widget_show(iconwid);
01688       cw->smiley_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01689                             _("Smiley"),
01690                             _("Insert Smiley"),
01691                             _("Smiley"),
01692                             iconwid,
01693                             GTK_SIGNAL_FUNC(show_smileys_callback),
01694                             cw);
01695       /*Create the separator for the toolbar*/
01696 
01697       gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01698       separator = gtk_vseparator_new();
01699       gtk_widget_set_usize(GTK_WIDGET(separator), 0, 20);
01700       gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), separator, NULL, NULL);
01701       gtk_widget_show(separator);
01702       gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01703   }
01704   /*This is the sound toggle button*/
01705 
01706   icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_volume_xpm);
01707   iconwid = gtk_pixmap_new(icon, mask);
01708   gtk_widget_show(iconwid);
01709   cw->sound_button = gtk_toolbar_append_element(GTK_TOOLBAR(toolbar),
01710                         GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
01711                         NULL,
01712                         _("Sound"),
01713                         _("Enable Sounds CTRL+S"),
01714                         _("Sound"),
01715                         iconwid,
01716                         GTK_SIGNAL_FUNC(set_sound_on_toggle),
01717                         cw);
01718   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01719    
01720   /*Toggle the sound button based on preferences*/
01721 
01722   if (do_play_send)
01723     cw->send_enabled=TRUE;
01724 
01725   if (do_play_receive)
01726     cw->receive_enabled = TRUE;
01727 
01728   if (do_play_first)
01729     cw->first_enabled = TRUE;
01730 
01731   if (do_play_send + do_play_receive + do_play_first > 0)
01732     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->sound_button), TRUE);
01733   else
01734     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(cw->sound_button), FALSE);
01735 
01736   /*Create the separator for the toolbar*/
01737 
01738   separator = gtk_vseparator_new();
01739   gtk_widget_set_usize(GTK_WIDGET(separator), 0, 20);
01740   gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), separator, NULL, NULL);
01741   gtk_widget_show(separator);
01742   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01743    
01744   /*** MIZHI
01745    * create the button for the log viewing functions
01746    */
01747 
01748   icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_search_xpm);
01749   iconwid = gtk_pixmap_new(icon, mask);
01750   gtk_widget_show(iconwid);
01751   view_log_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01752                         "View",
01753                         _("View Log CTRL+L"),
01754                         "View",
01755                         iconwid,
01756                         GTK_SIGNAL_FUNC(view_log_callback),
01757                         cw);
01758   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar)); 
01759 
01760   /*This is the send file button*/
01761 
01762   icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_open_xpm);
01763   iconwid = gtk_pixmap_new(icon, mask);
01764   gtk_widget_show(iconwid);
01765   sendf_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01766                      "sendf",
01767                      _("Send File CTRL+T"),
01768                      "sendf",
01769                      iconwid,
01770                      GTK_SIGNAL_FUNC(send_file),
01771                      cw);
01772   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar)); 
01773   
01774   /*This is the ignore button*/
01775 
01776   icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_no_xpm);
01777   iconwid = gtk_pixmap_new(icon, mask);
01778   gtk_widget_show(iconwid); 
01779   ignore_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01780                       "ignore",
01781                       _("Ignore CTRL+G"), 
01782                       "ignore",
01783                       iconwid, 
01784                       GTK_SIGNAL_FUNC(ignore_callback), 
01785                       cw);
01786   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar)); 
01787 
01788   /*This is the send button*/
01789 
01790   icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, tb_mail_send_xpm);
01791   iconwid = gtk_pixmap_new(icon, mask);
01792   gtk_widget_show(iconwid);
01793   send_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01794                     "send", 
01795                     _("Send Message CTRL+R"),
01796                     "send",
01797                     iconwid, 
01798                     GTK_SIGNAL_FUNC(send_message),
01799                     cw);
01800   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar)); 
01801 
01802   /* Vertical separator */
01803 
01804   separator = gtk_vseparator_new();
01805   gtk_widget_set_usize(GTK_WIDGET(separator), 0, 20);
01806   gtk_toolbar_append_widget(GTK_TOOLBAR(toolbar), separator, NULL, NULL);
01807   gtk_widget_show(separator);
01808   gtk_toolbar_append_space(GTK_TOOLBAR(toolbar));
01809     
01810   /*This is the close button*/
01811 
01812   icon = gdk_pixmap_create_from_xpm_d(cw->window->window, &mask, NULL, cancel_xpm);
01813   iconwid = gtk_pixmap_new(icon, mask);
01814   gtk_widget_show(iconwid);
01815 
01816   if (do_tabbed_chat)
01817     {
01818       close_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01819                          "close",
01820                          _("Close CTRL+Q"),
01821                          "close",
01822                          iconwid,
01823                          GTK_SIGNAL_FUNC(close_tab_callback),
01824                          cw);
01825     } else {
01826 
01827       close_button = gtk_toolbar_append_item(GTK_TOOLBAR(toolbar),
01828                          "close",
01829                          _("Close CTRL+Q"),
01830                          "close",
01831                          iconwid,
01832                          GTK_SIGNAL_FUNC(close_win),
01833                          cw);
01834     }       
01835     
01836   cw->status_label = gtk_label_new(" ");
01837   gtk_box_pack_start(GTK_BOX(hbox), cw->status_label, FALSE, FALSE, 0);
01838   gtk_widget_show(cw->status_label);
01839 
01840   gtk_box_pack_end(GTK_BOX(hbox), toolbar, FALSE, FALSE, 0);
01841   gtk_widget_show(toolbar);
01842    
01843   gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
01844   gtk_widget_show(hbox);
01845 
01846   make_safe_filename(buff, remote->nick);
01847 
01848   if ((cw->fp = fopen(buff, "a")) == NULL) 
01849     {
01850       perror(buff);
01851       do_logging=0;
01852     }
01853 
01854   if(do_logging)
01855     {
01856       time_t my_time = time(NULL);
01857       fprintf(cw->fp, _("%sConversation started on %s %s\n"),
01858           (do_strip_html ? "" : "<HR WIDTH=\"100%%\"><P ALIGN=\"CENTER\"><B>"),
01859           g_strchomp(asctime(localtime(&my_time))), (do_strip_html?"":"</B></P>"));
01860       fflush(cw->fp);
01861     }
01862 
01863   gtk_widget_show(cw->chat);
01864   gtk_widget_show(cw->entry);
01865   /*    gtk_widget_show(vbox);
01866     gtk_widget_show(cw->notebook); */
01867 
01868   return cw;
01869 }
01870 
01871 void eb_chat_window_display_error(eb_account * remote, gchar * message)
01872 {
01873   struct contact * remote_contact = remote->account_contact;
01874 
01875   if(remote_contact->chatwindow)
01876     {
01877       gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat),
01878               _("<b>Error: </b>"), 0,0,0);
01879       gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), message, 0,0,0);
01880       gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), "<br>", 0,0,0);
01881     }
01882 }
01883 
01884 void eb_chat_window_do_timestamp(struct contact * c, gboolean online)
01885 {
01886   gchar buff[BUF_SIZE];
01887   time_t my_time = time(NULL);
01888   if(!c || !c->chatwindow)
01889     return;
01890 
01891   if(!do_timestamp)
01892     {
01893       return;
01894     }
01895 
01896   gtk_eb_html_add(EXT_GTK_TEXT(c->chatwindow->chat), "<hr>", 0,0,0);
01897   g_snprintf(buff, BUF_SIZE,_("<b>%s is logged %s @ %s.</b>"),
01898          c->nick, (online?_("in"):_("out")), g_strchomp(asctime(localtime(&my_time))));
01899   gtk_eb_html_add(EXT_GTK_TEXT(c->chatwindow->chat), buff, 0,0,0);
01900   gtk_eb_html_add(EXT_GTK_TEXT(c->chatwindow->chat), "<hr>", 0,0,0);
01901 }
01902 
01903 
01904 void eb_chat_window_display_remote_message(eb_local_account * account,
01905                        eb_account * remote,
01906                                            struct service * serv,
01907                        gchar * o_message)
01908 {
01909   struct contact * remote_contact = remote->account_contact;
01910   gchar buff[BUF_SIZE];
01911   gchar buff2[BUF_SIZE];
01912   struct tm * cur_time;
01913   time_t t;
01914   GList * filter_walk;
01915   gchar * message;
01916   gboolean firstmsg = FALSE; /* init to false so only play if
01917                   * first msg is one received rather
01918                   * than sent */
01919 #ifdef HAVE_ICONV_H
01920   /* 'BUF_SIZE*2' e.g. for 'Latin-x' to 'UTF-8',
01921      which is 1-byte to 2-byte recoding */
01922   char recode_buff[BUF_SIZE*2 + 1];
01923 #endif
01924 
01925 
01926   /* do we need to ignore this user? If so, do it BEFORE filters so they can't DoS us */
01927 
01928   if(!strcasecmp(remote_contact->group->name, _("Ignore") ))
01929     return;
01930 
01931   // Inbound filters here - Meredydd
01932   filter_walk=incoming_message_filters;
01933 
01934   eb_debug(DBG_CORE, "Starting to run incoming filters\n");
01935 
01936   message=strdup(o_message);
01937 
01938   while(filter_walk!=NULL)
01939   {
01940     gchar * (*ofilter)(eb_local_account *, eb_account *, struct contact *, gchar *);
01941 
01942     eb_debug(DBG_CORE, "Running an incoming filter\n");
01943     ofilter=(gchar *(*)(eb_local_account *, eb_account *, struct contact *, gchar *))filter_walk->data;
01944 
01945     message=ofilter(account, remote, remote_contact, message);
01946     if(message==NULL) { return; } // Nothing to clean up (I think...)
01947 
01948     filter_walk=g_list_next(filter_walk);
01949   }
01950 
01951   eb_debug(DBG_CORE, "Finished incoming filters\n");
01952 
01953   // end inbound filters
01954 
01955   
01956   message = eb_smilify(message, serv->sc->get_smileys());
01957 
01958   if(!remote_contact->chatwindow || !remote_contact->chatwindow->window)
01959     {
01960       if(remote_contact->chatwindow)
01961     g_free(remote_contact->chatwindow);
01962 
01963       remote_contact->chatwindow = eb_chat_window_new(account, remote_contact);
01964 
01965       if (!remote_contact->chatwindow)
01966     /* this message is ignored */
01967     return;
01968 
01969       gtk_widget_show(remote_contact->chatwindow->window);
01970       firstmsg = TRUE;        /* chat window created by
01971                    * receive msg, so set to true */
01972       if (do_restore_last_conv){
01973     gchar buff[NAME_MAX];
01974     make_safe_filename(buff, remote_contact->nick);
01975     eb_restore_last_conv(buff,remote_contact->chatwindow);
01976       }
01977     }
01978 
01979   /*for now we will assume the identity that the person in question talked
01980     to us last as */
01981   remote_contact->chatwindow->local_user = account;
01982 
01983   /*also specify that if possible, try to use the same account they used
01984     to last talk to us with */
01985   remote_contact->chatwindow->perfered = remote;
01986 
01987   if (remote_contact->chatwindow->notebook != NULL) {
01988     /* no more icons in the tabs */
01989     /* gtk_widget_show(remote_contact->chatwindow->talk_pixmap); */
01990 
01991     if (gtk_notebook_page_num(GTK_NOTEBOOK(remote_contact->chatwindow->notebook),
01992                   remote_contact->chatwindow->notebook_child) != gtk_notebook_get_current_page(
01993                                                        GTK_NOTEBOOK(remote_contact->chatwindow->notebook)))
01994       {
01995     set_tab_red(GTK_NOTEBOOK(remote_contact->chatwindow->notebook), remote_contact->chatwindow->notebook_child);
01996       }
01997 
01998   } else {
01999     gdk_window_raise(remote_contact->chatwindow->window->window);     
02000     gtk_window_set_focus(GTK_WINDOW(remote_contact->chatwindow->window),
02001              remote_contact->chatwindow->entry);
02002   }
02003 
02004   eb_update_window_title(remote_contact->chatwindow, TRUE);
02005 
02006   if(remote_contact->chatwindow->sound_enabled)
02007     {
02008       if (firstmsg)
02009     {
02010       if (remote_contact->chatwindow->first_enabled)
02011         {
02012           play_sound(FIRSTMSG);
02013           firstmsg = FALSE;
02014         }
02015       else
02016         {
02017           play_sound(RECEIVE);
02018           firstmsg = FALSE;
02019         }
02020     }
02021       else if (remote_contact->chatwindow->receive_enabled)
02022     play_sound(RECEIVE);
02023     }
02024     
02025   /*for grab the focus*/
02026     
02027   if(do_raise_window)
02028     {
02029       gdk_window_raise(remote_contact->chatwindow->window->window);
02030       //    gtk_widget_grab_focus(remote_contact->chatwindow->entry);
02031     }
02032 
02033   if (do_convo_timestamp)
02034     {
02035       gchar * color;
02036 
02037       color=serv->sc->get_color(); // note do not free() afterwards, may be static
02038 
02039       time(&t);
02040       cur_time = localtime(&t);
02041       g_snprintf(buff2, BUF_SIZE, "<FONT COLOR=\"#ff0000\">%d:%.2d:%.2d</FONT> <FONT COLOR=\"%s\">%s:</FONT>",
02042          cur_time->tm_hour, cur_time->tm_min,
02043          cur_time->tm_sec, color, remote_contact->nick);
02044     }
02045   else
02046     {
02047       g_snprintf(buff2, BUF_SIZE, "%s", remote_contact->nick);
02048     }
02049 
02050 #ifdef HAVE_ICONV_H
02051 message = recode_if_needed(message, recode_buff, RECODE_TO_LOCAL);
02052 #endif
02053 
02054   g_snprintf(buff, BUF_SIZE, "<B>%s </B>",buff2);
02055 
02056   gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), buff,0,0,0);
02057   gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), message,do_ignore_back,do_ignore_fore,do_ignore_font);
02058   gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), "<BR>",0,0,0);
02059 
02060   /* Log the message */
02061 
02062   if(do_logging) eb_log_message(remote_contact->chatwindow->fp, buff, message);
02063 
02064   /* If user's away and hasn't yet sent the away message in the last 5 minutes,
02065      send, display, & log his away message.
02066      We might want to give the option of whether or not to always send the message.*/
02067 
02068   if(is_away && (time(NULL) - remote_contact->chatwindow->away_msg_sent) > 300)
02069     {
02070       send_message(NULL, remote_contact->chatwindow);
02071       RUN_SERVICE(account)->send_im(
02072                     account,
02073                     remote,
02074                     gtk_entry_get_text(GTK_ENTRY(away_message)));
02075       time(&t);
02076       cur_time = localtime(&t);
02077       g_snprintf(buff, BUF_SIZE, "<FONT COLOR=\"#0000ff\"><B>%d:%.2d:%.2d %s: </B></FONT>",
02078          cur_time->tm_hour, cur_time->tm_min, cur_time->tm_sec,
02079          account->alias);
02080       gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), buff,0,0,0);
02081       gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), 
02082               gtk_entry_get_text(GTK_ENTRY(away_message)),do_ignore_back,do_ignore_fore,do_ignore_font);
02083       gtk_eb_html_add(EXT_GTK_TEXT(remote_contact->chatwindow->chat), "<br>", 0,0,0);
02084 
02085       /* Note that the time the last away message has been sent */
02086 
02087       remote_contact->chatwindow->away_msg_sent = time(NULL);
02088 
02089       /* Log it */
02090 
02091       if(do_logging) eb_log_message(remote_contact->chatwindow->fp, buff,
02092                     gtk_entry_get_text(GTK_ENTRY(away_message)));
02093     }
02094     free(message);
02095 }
02096 
02097 void eb_chat_window_display_contact(struct contact * remote_contact)
02098 {
02099   eb_account *remote_account =
02100     find_suitable_remote_account (NULL, remote_contact);
02101   eb_local_account *account = NULL;
02102 
02103   if (remote_account)
02104     account = find_suitable_local_account (NULL, remote_account->service_id);
02105 
02106   if (!remote_contact->chatwindow || !remote_contact->chatwindow->window)
02107   {
02108     if (remote_contact->chatwindow)
02109     {
02110       g_free (remote_contact->chatwindow);
02111     }
02112 
02113     remote_contact->chatwindow = eb_chat_window_new (account, remote_contact);
02114 
02115     if (remote_contact->chatwindow)
02116     {
02117       gtk_widget_show (remote_contact->chatwindow->window);
02118       if (do_restore_last_conv)
02119       {
02120     gchar buff[NAME_MAX];
02121     make_safe_filename (buff, remote_contact->nick);
02122     eb_restore_last_conv (buff, remote_contact->chatwindow);
02123       }
02124       gdk_window_raise(remote_contact->chatwindow->window->window);
02125       gtk_window_set_focus (GTK_WINDOW
02126                 (remote_contact->chatwindow->window),
02127                 remote_contact->chatwindow->entry);
02128     }
02129 
02130   } else {
02131     gdk_window_raise(remote_contact->chatwindow->window->window);
02132     gtk_window_set_focus (GTK_WINDOW
02133                 (remote_contact->chatwindow->window),
02134                 remote_contact->chatwindow->entry);
02135   
02136   }
02137 
02138   if (remote_contact->chatwindow->notebook != NULL)
02139   {
02140     int page_num =
02141       gtk_notebook_page_num (GTK_NOTEBOOK
02142                  (remote_contact->chatwindow->notebook),
02143                  remote_contact->chatwindow->notebook_child);
02144     /* no more icons in the tabs */
02145     /* gtk_widget_hide(remote_contact->chatwindow->talk_pixmap); */
02146 
02147     set_tab_normal (GTK_NOTEBOOK (remote_contact->chatwindow->notebook),
02148             remote_contact->chatwindow->notebook_child);
02149 
02150     gtk_notebook_set_page (GTK_NOTEBOOK
02151                (remote_contact->chatwindow->notebook), page_num);
02152   }
02153 }
02154 
02155 void eb_chat_window_display_account(eb_account * remote_account)
02156 {
02157   struct contact * remote_contact  = remote_account->account_contact;
02158   eb_local_account * account =find_suitable_local_account(NULL, remote_account->service_id);
02159 
02160   if(!remote_contact->chatwindow || !remote_contact->chatwindow->window)
02161     {
02162       if(remote_contact->chatwindow)
02163     {
02164       g_free(remote_contact->chatwindow);
02165     }
02166 
02167       remote_contact->chatwindow = eb_chat_window_new(account, remote_contact);
02168 
02169       if(remote_contact->chatwindow)
02170     {
02171       gtk_widget_show(remote_contact->chatwindow->window);
02172       if (do_restore_last_conv) 
02173         {
02174           gchar buff[NAME_MAX];
02175           make_safe_filename(buff, remote_contact->nick);
02176           eb_restore_last_conv(buff,remote_contact->chatwindow);    
02177         }
02178           gdk_window_raise(remote_contact->chatwindow->window->window);
02179       gtk_window_set_focus (GTK_WINDOW
02180                 (remote_contact->chatwindow->window),
02181                              remote_contact->chatwindow->entry);
02182     }
02183       else /* Did they get denied because they're in the Unknown group? */
02184     return;
02185     }
02186   else if (remote_contact->chatwindow && remote_contact->chatwindow->window)
02187     {
02188           gdk_window_raise(remote_contact->chatwindow->window->window);
02189       gtk_window_set_focus (GTK_WINDOW
02190                 (remote_contact->chatwindow->window),
02191                              remote_contact->chatwindow->entry);
02192     }       
02193     
02194   eb_update_window_title(remote_contact->chatwindow, FALSE);
02195     
02196   if (remote_contact->chatwindow->notebook != NULL) 
02197   {
02198     int page_num = gtk_notebook_page_num(GTK_NOTEBOOK(remote_contact->chatwindow->notebook),
02199                                      remote_contact->chatwindow->notebook_child);
02200     /* no more icons in the tabs */
02201     /* gtk_widget_hide(remote_contact->chatwindow->talk_pixmap); */
02202     set_tab_normal(GTK_NOTEBOOK(remote_contact->chatwindow->notebook), 
02203            remote_contact->chatwindow->notebook_child);
02204     gtk_notebook_set_page(GTK_NOTEBOOK(remote_contact->chatwindow->notebook), 
02205               page_num);
02206         
02207   } else {
02208     gdk_window_raise(remote_contact->chatwindow->window->window);     
02209     gtk_window_set_focus(GTK_WINDOW(remote_contact->chatwindow->window),
02210              remote_contact->chatwindow->entry);
02211   }
02212     
02213   remote_contact->chatwindow->perfered = remote_account;
02214 }
02215     
02216 void eb_log_status_changed(eb_account *ea, gchar *status)
02217 {
02218     if(ea == NULL || ea->account_contact == NULL || 
02219             ea->account_contact->chatwindow == NULL ||
02220             ea->account_contact->chatwindow->fp == NULL)
02221         return;
02222 
02223     eb_log_message(ea->account_contact->chatwindow->fp, ea->handle, 
02224             ((status && status[0])?status:"Available"));
02225 }
02226 
02227 void eb_log_message(FILE *log_file, gchar buff[], gchar *message)
02228 {
02229   gchar * my_name = strdup(buff);
02230   gchar * my_message = strdup(message);
02231 
02232   /* Get rid of the HTML if the user doesn't want it */
02233 
02234   if(do_strip_html)
02235     {
02236       strip_html(my_message);
02237       strip_html(my_name);
02238     }
02239 
02240   /* Log the message, using the appropriate formatting */
02241 
02242   fprintf(log_file, "%s %s %s %s\n",
02243       (do_strip_html ? "" : "<P>"), my_name, my_message, (do_strip_html ? "" : "</P>"));
02244   fflush(log_file);
02245 
02246   free(my_message);
02247   free(my_name);
02248 }
02249 
02250 void eb_restore_last_conv(gchar *file_name, chat_window* cw)
02251 {
02252   FILE * fp;
02253   gchar buff[1024];
02254   gchar buff2[1024];
02255   gchar *buff3;
02256   gchar color[8];
02257   gchar name[512];
02258   gchar *token;
02259   long location = -1;
02260   long lastlocation = -1;
02261   long beforeget;
02262 
02263   if ( (fp = fopen(file_name, "r")) == NULL)
02264     {
02265       //there must not be a list logfile...
02266       return;
02267     }
02268 
02269 
02270   /*find last conversation */
02271   while(!feof(fp))
02272     {
02273       beforeget = ftell(fp);
02274       fgets(buff, 1024, fp);
02275       if(feof(fp))
02276     {
02277       break;
02278     }
02279       g_strchomp(buff);
02280       if(!strncmp(buff,_("Conversation started"),strlen(_("Conversation started")))
02281      || !strncmp(buff,_("<HR WIDTH=\"100%\"><P ALIGN=\"CENTER\"><B>Conversation started"),strlen(_("<HR WIDTH=\"100%\"><P ALIGN=\"CENTER\"><B>Conversation started"))))
02282     {
02283       lastlocation = location;
02284       location = beforeget;
02285     }
02286     }
02287 
02288   if(lastlocation == -1)
02289     {
02290       if(location == -1 || location == 0)
02291     {
02292       fclose(fp);
02293       return;
02294     }
02295       lastlocation = location;
02296     }
02297   fseek(fp,lastlocation, SEEK_SET);
02298 
02299   /* now we display the log */
02300   while(!feof(fp))
02301     {
02302       fgets(buff,1024,fp);
02303       if(feof(fp))
02304     {
02305       break;
02306     }
02307       g_strchomp(buff);
02308 
02309       if(buff[0] == '<') /*this is html*/
02310     {
02311 
02312       if(!strncmp(buff,"<HR WIDTH=\"100%\">",
02313               strlen("<HR WIDTH=\"100%\">")))
02314         {
02315           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), buff+strlen("<HR WIDTH=\"100%\">"),0,0,0);
02316         }
02317       else
02318         {
02319           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), buff, 0,0,0);
02320         }
02321 
02322 
02323       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<br>",0,0,0);
02324 
02325       if(strlen(buff) > 34 && !strncmp(buff+34,_("ended on"),8))
02326         {
02327           break;
02328         }
02329     }
02330       else if(!strncmp(buff,_("Conversation started"),strlen(_("Conversation started"))))
02331     {
02332       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<hr>", 0,0,0);
02333       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), buff, 0,0,0);
02334       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<br>",0,0,0);
02335 
02336     }
02337       else if(!strncmp(buff,_("Conversation ended"),strlen(_("Conversation ended"))))
02338     {
02339 
02340       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), buff,0,0,0);
02341       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<br>",0,0,0);
02342       gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<hr>",0,0,0);
02343       break;
02344     }
02345       else
02346     {
02347           strip_html(buff); /*better safe than sorry */
02348           strcpy(buff2, buff);
02349 
02350           token = strtok(buff2,":");
02351 
02352           if(token && (strcmp(buff,token) != 0))
02353         {
02354           /* not happy with this if statement at all! */
02355           if(((strlen(token)==3)&&isdigit((int)token[1])&&isdigit(token[2]))
02356          || ((strlen(token)==2) && isdigit((int)token[1])))
02357             {
02358                 /* we must have time stamps */
02359           /* allready have hours */
02360           token = strtok(NULL,":"); /*minutes*/
02361           if(token == NULL) /* we were wrong, this isn't a time stamp */
02362             {
02363               break; /* need to test this */
02364             }
02365           token = strtok(NULL,":"); /*seconds + name*/
02366 
02367           if(token == NULL) /* we were wrong, this isn't a time stamp */
02368             break; /* need to test this */
02369           buff3 = token + strlen(token)+1; /* should be the end
02370                               of the screen name */
02371           token+=3;
02372             }
02373           else
02374         {
02375           /* no time stamps */
02376           buff3 = buff2+strlen(token)+1;
02377           token++;
02378         }
02379           if( !strncmp(token,cw->contact->nick,strlen(cw->contact->nick)))
02380             {
02381                 /* this is the other person */
02382 
02383           strcpy(color,"#ff0000");
02384         }
02385           else
02386         {
02387           /* must be me */
02388           strcpy(color,"#0000ff");
02389         }
02390           strncpy(name,buff,buff3-buff2);
02391           name[buff3-buff2] = '\0';
02392           g_snprintf(buff, BUF_SIZE, "<FONT COLOR=\"%s\"><B>%s </B></FONT>",color, name);
02393           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), buff,0,0,0);
02394           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), buff3,0,0,0);
02395           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<br>",0,0,0);
02396         }
02397       else
02398         {
02399           /* hmm, no ':' must be a non start/blank line */
02400           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat),buff2,0,0,0);
02401           gtk_eb_html_add(EXT_GTK_TEXT(cw->chat), "<br>",0,0,0);
02402         }
02403     }
02404     }
02405   fclose(fp);
02406 
02407 }
02408 
02409 void eb_chat_window_display_status(eb_account * remote,
02410                    gchar * message)
02411 {
02412   struct contact * remote_contact = find_contact_by_handle(remote->handle);
02413   char * tmp = NULL;
02414 
02415   /* trim @.* part for User is typing */
02416   if (remote_contact == NULL || remote_contact->chatwindow == NULL) {
02417      gchar ** tmp_ct = NULL;
02418      if (strchr (remote->handle,'@') ) {
02419      tmp_ct = g_strsplit (remote->handle,"@",2);
02420      remote_contact = find_contact_by_handle(tmp_ct[0]);
02421      g_strfreev (tmp_ct);
02422      }
02423   }
02424 
02425   if (remote_contact == NULL || remote_contact->chatwindow == NULL)
02426      return;
02427   
02428   if (message != NULL && strlen(message) > 0)
02429      tmp = g_strdup_printf("%s", message);
02430   else
02431      tmp = g_strdup_printf(" ");
02432 
02433   gtk_label_set_text( GTK_LABEL(remote_contact->chatwindow->status_label), tmp );
02434   g_free(tmp);
02435 }
02436 
02437 void
02438 eb_update_window_title_to_tab (int tab, gboolean new_message)
02439 {
02440   char buff[BUF_SIZE];
02441   chat_window * cw = NULL;
02442 
02443       cw = find_tabbed_chat_window_index(tab);
02444       if (cw && cw->notebook != NULL
02445       && cw->contact != NULL
02446       && cw->local_user != NULL
02447           && cw->perfered != NULL
02448       && GET_SERVICE (cw->local_user).name != NULL)
02449     {
02450       g_snprintf (buff, BUF_SIZE, "%s%s (%s <=> %s via %s)",
02451               new_message == TRUE ? "* " : "",
02452               cw->contact->nick, cw->local_user->alias,
02453               cw->perfered->handle,
02454               GET_SERVICE (cw->local_user).name);
02455 
02456         gtk_window_set_title (GTK_WINDOW (cw->contact->chatwindow->window),
02457                 buff);
02458 
02459 
02460     }
02461         else if(cw && cw->contact != NULL)
02462     {
02463             gtk_window_set_title (GTK_WINDOW (cw->contact->chatwindow->window),
02464                 cw->contact->nick);
02465     }
02466         
02467 }
02468 
02469 void
02470 eb_update_window_title (chat_window * cw, gboolean new_message)
02471 {
02472   char buff[BUF_SIZE];
02473 
02474   if (!do_tabbed_chat
02475       && cw != NULL
02476       && cw->contact != NULL
02477       && cw->perfered != NULL
02478       && cw->local_user != NULL && GET_SERVICE (cw->local_user).name != NULL)
02479     {
02480       g_snprintf (buff, BUF_SIZE, "%s%s (%s <=> %s via %s)",
02481           new_message == TRUE ? "* " : "",
02482           cw->contact->nick, cw->local_user->alias,
02483           cw->perfered->handle, GET_SERVICE (cw->local_user).name);
02484 
02485       gtk_window_set_title (GTK_WINDOW (cw->contact->chatwindow->window),
02486                 buff);
02487     }
02488   else if (do_tabbed_chat)
02489     {
02490       if (!new_message)       
02491           cw = find_tabbed_current_chat_window ();
02492       if (cw && cw->notebook != NULL
02493       && cw->contact != NULL
02494       && cw->local_user != NULL
02495           && cw->perfered != NULL
02496       && GET_SERVICE (cw->local_user).name != NULL)
02497     {
02498       g_snprintf (buff, BUF_SIZE, "%s%s (%s <=> %s via %s)",
02499               new_message == TRUE ? "* " : "",
02500               cw->contact->nick, cw->local_user->alias,
02501               cw->perfered->handle,
02502               GET_SERVICE (cw->local_user).name);
02503 
02504         gtk_window_set_title (GTK_WINDOW (cw->contact->chatwindow->window),
02505                 buff);
02506 
02507 
02508     }
02509     }
02510 }

Contact: Andy Maloney     [Documentation generated by doxygen]