| 1 /** |
|
| 2 * @file gtkconv.c GTK+ Conversation API |
|
| 3 * @ingroup gtkui |
|
| 4 * |
|
| 5 * gaim |
|
| 6 * |
|
| 7 * Gaim is the legal property of its developers, whose names are too numerous |
|
| 8 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 9 * source distribution. |
|
| 10 * |
|
| 11 * This program is free software; you can redistribute it and/or modify |
|
| 12 * it under the terms of the GNU General Public License as published by |
|
| 13 * the Free Software Foundation; either version 2 of the License, or |
|
| 14 * (at your option) any later version. |
|
| 15 * |
|
| 16 * This program is distributed in the hope that it will be useful, |
|
| 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 19 * GNU General Public License for more details. |
|
| 20 * |
|
| 21 * You should have received a copy of the GNU General Public License |
|
| 22 * along with this program; if not, write to the Free Software |
|
| 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 24 * |
|
| 25 */ |
|
| 26 #include "internal.h" |
|
| 27 #include "gtkgaim.h" |
|
| 28 |
|
| 29 #ifndef _WIN32 |
|
| 30 # include <X11/Xlib.h> |
|
| 31 #endif |
|
| 32 |
|
| 33 #ifdef USE_GTKSPELL |
|
| 34 # include <gtkspell/gtkspell.h> |
|
| 35 # ifdef _WIN32 |
|
| 36 # include "wspell.h" |
|
| 37 # endif |
|
| 38 #endif |
|
| 39 |
|
| 40 #include <gdk/gdkkeysyms.h> |
|
| 41 |
|
| 42 #include "account.h" |
|
| 43 #include "cmds.h" |
|
| 44 #include "debug.h" |
|
| 45 #include "idle.h" |
|
| 46 #include "imgstore.h" |
|
| 47 #include "log.h" |
|
| 48 #include "notify.h" |
|
| 49 #include "prpl.h" |
|
| 50 #include "request.h" |
|
| 51 #include "util.h" |
|
| 52 |
|
| 53 #include "gtkdnd-hints.h" |
|
| 54 #include "gtkblist.h" |
|
| 55 #include "gtkconv.h" |
|
| 56 #include "gtkconvwin.h" |
|
| 57 #include "gtkdialogs.h" |
|
| 58 #include "gtkimhtml.h" |
|
| 59 #include "gtkimhtmltoolbar.h" |
|
| 60 #include "gtklog.h" |
|
| 61 #include "gtkmenutray.h" |
|
| 62 #include "gtkpounce.h" |
|
| 63 #include "gtkprefs.h" |
|
| 64 #include "gtkprivacy.h" |
|
| 65 #include "gtkthemes.h" |
|
| 66 #include "gtkutils.h" |
|
| 67 #include "gtkstock.h" |
|
| 68 |
|
| 69 #include "gtknickcolors.h" |
|
| 70 |
|
| 71 #define AUTO_RESPONSE "<AUTO-REPLY> : " |
|
| 72 |
|
| 73 typedef enum |
|
| 74 { |
|
| 75 GAIM_GTKCONV_SET_TITLE = 1 << 0, |
|
| 76 GAIM_GTKCONV_BUDDY_ICON = 1 << 1, |
|
| 77 GAIM_GTKCONV_MENU = 1 << 2, |
|
| 78 GAIM_GTKCONV_TAB_ICON = 1 << 3, |
|
| 79 GAIM_GTKCONV_TOPIC = 1 << 4, |
|
| 80 GAIM_GTKCONV_SMILEY_THEME = 1 << 5, |
|
| 81 GAIM_GTKCONV_COLORIZE_TITLE = 1 << 6 |
|
| 82 }GaimGtkConvFields; |
|
| 83 |
|
| 84 #define GAIM_GTKCONV_ALL ((1 << 7) - 1) |
|
| 85 |
|
| 86 #define SEND_COLOR "#204a87" |
|
| 87 #define RECV_COLOR "#cc0000" |
|
| 88 #define HIGHLIGHT_COLOR "#AF7F00" |
|
| 89 |
|
| 90 /* Undef this to turn off "custom-smiley" debug messages */ |
|
| 91 #define DEBUG_CUSTOM_SMILEY |
|
| 92 |
|
| 93 #define LUMINANCE(c) (float)((0.3*(c.red))+(0.59*(c.green))+(0.11*(c.blue))) |
|
| 94 |
|
| 95 #if 0 |
|
| 96 /* These colors come from the default GNOME palette */ |
|
| 97 static GdkColor nick_colors[] = { |
|
| 98 {0, 47616, 46336, 43776}, /* Basic 3D Medium */ |
|
| 99 {0, 32768, 32000, 29696}, /* Basic 3D Dark */ |
|
| 100 {0, 22016, 20992, 18432}, /* 3D Shadow */ |
|
| 101 {0, 33536, 42496, 32512}, /* Green Medium */ |
|
| 102 {0, 23808, 29952, 21760}, /* Green Dark */ |
|
| 103 {0, 17408, 22016, 12800}, /* Green Shadow */ |
|
| 104 {0, 57344, 46592, 44800}, /* Red Hilight */ |
|
| 105 {0, 49408, 26112, 23040}, /* Red Medium */ |
|
| 106 {0, 34816, 17920, 12544}, /* Red Dark */ |
|
| 107 {0, 49408, 14336, 8704}, /* Red Shadow */ |
|
| 108 {0, 34816, 32512, 41728}, /* Purple Medium */ |
|
| 109 {0, 25088, 23296, 33024}, /* Purple Dark */ |
|
| 110 {0, 18688, 16384, 26112}, /* Purple Shadow */ |
|
| 111 {0, 40192, 47104, 53760}, /* Blue Hilight */ |
|
| 112 {0, 29952, 36864, 44544}, /* Blue Medium */ |
|
| 113 {0, 57344, 49920, 40448}, /* Face Skin Medium */ |
|
| 114 {0, 45824, 37120, 26880}, /* Face skin Dark */ |
|
| 115 {0, 33280, 26112, 18176}, /* Face Skin Shadow */ |
|
| 116 {0, 57088, 16896, 7680}, /* Accent Red */ |
|
| 117 {0, 39168, 0, 0}, /* Accent Red Dark */ |
|
| 118 {0, 17920, 40960, 17920}, /* Accent Green */ |
|
| 119 {0, 9728, 50944, 9728} /* Accent Green Dark */ |
|
| 120 }; |
|
| 121 |
|
| 122 #define NUM_NICK_COLORS (sizeof(nick_colors) / sizeof(*nick_colors)) |
|
| 123 #endif |
|
| 124 |
|
| 125 /* From http://www.w3.org/TR/AERT#color-contrast */ |
|
| 126 #define MIN_BRIGHTNESS_CONTRAST 75 |
|
| 127 #define MIN_COLOR_CONTRAST 200 |
|
| 128 |
|
| 129 #define NUM_NICK_COLORS 220 |
|
| 130 static GdkColor *nick_colors = NULL; |
|
| 131 static guint nbr_nick_colors; |
|
| 132 |
|
| 133 typedef struct { |
|
| 134 GtkWidget *window; |
|
| 135 |
|
| 136 GtkWidget *entry; |
|
| 137 GtkWidget *message; |
|
| 138 |
|
| 139 GaimConversation *conv; |
|
| 140 |
|
| 141 } InviteBuddyInfo; |
|
| 142 |
|
| 143 static GtkWidget *invite_dialog = NULL; |
|
| 144 static GtkWidget *warn_close_dialog = NULL; |
|
| 145 |
|
| 146 static GaimGtkWindow *hidden_convwin = NULL; |
|
| 147 static GList *window_list = NULL; |
|
| 148 |
|
| 149 |
|
| 150 static gboolean update_send_to_selection(GaimGtkWindow *win); |
|
| 151 static void generate_send_to_items(GaimGtkWindow *win); |
|
| 152 |
|
| 153 /* Prototypes. <-- because Paco-Paco hates this comment. */ |
|
| 154 static void got_typing_keypress(GaimGtkConversation *gtkconv, gboolean first); |
|
| 155 static void gray_stuff_out(GaimGtkConversation *gtkconv); |
|
| 156 static GList *generate_invite_user_names(GaimConnection *gc); |
|
| 157 static void add_chat_buddy_common(GaimConversation *conv, const char *name, |
|
| 158 GaimConvChatBuddyFlags flags, const char *alias, const char *old_name); |
|
| 159 static gboolean tab_complete(GaimConversation *conv); |
|
| 160 static void gaim_gtkconv_updated(GaimConversation *conv, GaimConvUpdateType type); |
|
| 161 static void gtkconv_set_unseen(GaimGtkConversation *gtkconv, GaimUnseenState state); |
|
| 162 static void update_typing_icon(GaimGtkConversation *gtkconv); |
|
| 163 static char *item_factory_translate_func (const char *path, gpointer func_data); |
|
| 164 gboolean gaim_gtkconv_has_focus(GaimConversation *conv); |
|
| 165 static void gaim_gtkconv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data); |
|
| 166 static void gaim_gtkconv_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data); |
|
| 167 static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background); |
|
| 168 static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast); |
|
| 169 static void gaim_gtkconv_update_fields(GaimConversation *conv, GaimGtkConvFields fields); |
|
| 170 |
|
| 171 static GdkColor *get_nick_color(GaimGtkConversation *gtkconv, const char *name) { |
|
| 172 static GdkColor col; |
|
| 173 GtkStyle *style = gtk_widget_get_style(gtkconv->imhtml); |
|
| 174 float scale; |
|
| 175 |
|
| 176 col = nick_colors[g_str_hash(name) % nbr_nick_colors]; |
|
| 177 scale = ((1-(LUMINANCE(style->base[GTK_STATE_NORMAL]) / LUMINANCE(style->white))) * |
|
| 178 (LUMINANCE(style->white)/MAX(MAX(col.red, col.blue), col.green))); |
|
| 179 |
|
| 180 /* The colors are chosen to look fine on white; we should never have to darken */ |
|
| 181 if (scale > 1) { |
|
| 182 col.red *= scale; |
|
| 183 col.green *= scale; |
|
| 184 col.blue *= scale; |
|
| 185 } |
|
| 186 |
|
| 187 return &col; |
|
| 188 } |
|
| 189 |
|
| 190 /************************************************************************** |
|
| 191 * Callbacks |
|
| 192 **************************************************************************/ |
|
| 193 |
|
| 194 static gint |
|
| 195 close_conv_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 196 { |
|
| 197 GList *list = g_list_copy(gtkconv->convs), *l; |
|
| 198 |
|
| 199 l = list; |
|
| 200 while (l) { |
|
| 201 GaimConversation *conv = l->data; |
|
| 202 gaim_conversation_destroy(conv); |
|
| 203 l = l->next; |
|
| 204 } |
|
| 205 |
|
| 206 g_list_free(list); |
|
| 207 |
|
| 208 return TRUE; |
|
| 209 } |
|
| 210 |
|
| 211 static gboolean |
|
| 212 size_allocate_cb(GtkWidget *w, GtkAllocation *allocation, GaimGtkConversation *gtkconv) |
|
| 213 { |
|
| 214 GaimConversation *conv = gtkconv->active_conv; |
|
| 215 |
|
| 216 if (!GTK_WIDGET_VISIBLE(w)) |
|
| 217 return FALSE; |
|
| 218 |
|
| 219 if (!GAIM_IS_GTK_CONVERSATION(conv)) |
|
| 220 return FALSE; |
|
| 221 |
|
| 222 /* I find that I resize the window when it has a bunch of conversations in it, mostly so that the |
|
| 223 * tab bar will fit, but then I don't want new windows taking up the entire screen. I check to see |
|
| 224 * if there is only one conversation in the window. This way we'll be setting new windows to the |
|
| 225 * size of the last resized new window. */ |
|
| 226 /* I think that the above justification is not the majority, and that the new tab resizing should |
|
| 227 * negate it anyway. --luke */ |
|
| 228 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 229 { |
|
| 230 if (w == gtkconv->imhtml) { |
|
| 231 gaim_prefs_set_int("/gaim/gtk/conversations/im/default_width", allocation->width); |
|
| 232 gaim_prefs_set_int("/gaim/gtk/conversations/im/default_height", allocation->height); |
|
| 233 } |
|
| 234 if (w == gtkconv->entry) |
|
| 235 gaim_prefs_set_int("/gaim/gtk/conversations/im/entry_height", allocation->height); |
|
| 236 } |
|
| 237 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 238 { |
|
| 239 if (w == gtkconv->imhtml) { |
|
| 240 gaim_prefs_set_int("/gaim/gtk/conversations/chat/default_width", allocation->width); |
|
| 241 gaim_prefs_set_int("/gaim/gtk/conversations/chat/default_height", allocation->height); |
|
| 242 } |
|
| 243 if (w == gtkconv->entry) |
|
| 244 gaim_prefs_set_int("/gaim/gtk/conversations/chat/entry_height", allocation->height); |
|
| 245 } |
|
| 246 |
|
| 247 return FALSE; |
|
| 248 } |
|
| 249 |
|
| 250 static void |
|
| 251 default_formatize(GaimGtkConversation *c) |
|
| 252 { |
|
| 253 GaimConversation *conv = c->active_conv; |
|
| 254 |
|
| 255 if (conv->features & GAIM_CONNECTION_HTML) |
|
| 256 { |
|
| 257 char *color; |
|
| 258 GdkColor fg_color, bg_color; |
|
| 259 |
|
| 260 if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_bold") != GTK_IMHTML(c->entry)->edit.bold) |
|
| 261 gtk_imhtml_toggle_bold(GTK_IMHTML(c->entry)); |
|
| 262 |
|
| 263 if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_italic") != GTK_IMHTML(c->entry)->edit.italic) |
|
| 264 gtk_imhtml_toggle_italic(GTK_IMHTML(c->entry)); |
|
| 265 |
|
| 266 if (gaim_prefs_get_bool("/gaim/gtk/conversations/send_underline") != GTK_IMHTML(c->entry)->edit.underline) |
|
| 267 gtk_imhtml_toggle_underline(GTK_IMHTML(c->entry)); |
|
| 268 |
|
| 269 gtk_imhtml_toggle_fontface(GTK_IMHTML(c->entry), |
|
| 270 gaim_prefs_get_string("/gaim/gtk/conversations/font_face")); |
|
| 271 |
|
| 272 if (!(conv->features & GAIM_CONNECTION_NO_FONTSIZE)) |
|
| 273 gtk_imhtml_font_set_size(GTK_IMHTML(c->entry), |
|
| 274 gaim_prefs_get_int("/gaim/gtk/conversations/font_size")); |
|
| 275 |
|
| 276 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/fgcolor"), "") != 0) |
|
| 277 { |
|
| 278 gdk_color_parse(gaim_prefs_get_string("/gaim/gtk/conversations/fgcolor"), |
|
| 279 &fg_color); |
|
| 280 color = g_strdup_printf("#%02x%02x%02x", |
|
| 281 fg_color.red / 256, |
|
| 282 fg_color.green / 256, |
|
| 283 fg_color.blue / 256); |
|
| 284 } |
|
| 285 else |
|
| 286 color = g_strdup(""); |
|
| 287 |
|
| 288 gtk_imhtml_toggle_forecolor(GTK_IMHTML(c->entry), color); |
|
| 289 g_free(color); |
|
| 290 |
|
| 291 if(!(conv->features & GAIM_CONNECTION_NO_BGCOLOR) && |
|
| 292 strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/bgcolor"), "") != 0) |
|
| 293 { |
|
| 294 gdk_color_parse(gaim_prefs_get_string("/gaim/gtk/conversations/bgcolor"), |
|
| 295 &bg_color); |
|
| 296 color = g_strdup_printf("#%02x%02x%02x", |
|
| 297 bg_color.red / 256, |
|
| 298 bg_color.green / 256, |
|
| 299 bg_color.blue / 256); |
|
| 300 } |
|
| 301 else |
|
| 302 color = g_strdup(""); |
|
| 303 |
|
| 304 gtk_imhtml_toggle_background(GTK_IMHTML(c->entry), color); |
|
| 305 g_free(color); |
|
| 306 |
|
| 307 |
|
| 308 if (conv->features & GAIM_CONNECTION_FORMATTING_WBFO) |
|
| 309 gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), TRUE); |
|
| 310 else |
|
| 311 gtk_imhtml_set_whole_buffer_formatting_only(GTK_IMHTML(c->entry), FALSE); |
|
| 312 } |
|
| 313 } |
|
| 314 |
|
| 315 static void |
|
| 316 clear_formatting_cb(GtkIMHtml *imhtml, GaimGtkConversation *gtkconv) |
|
| 317 { |
|
| 318 default_formatize(gtkconv); |
|
| 319 } |
|
| 320 |
|
| 321 static const char * |
|
| 322 gaim_gtk_get_cmd_prefix(void) |
|
| 323 { |
|
| 324 return "/"; |
|
| 325 } |
|
| 326 |
|
| 327 static GaimCmdRet |
|
| 328 say_command_cb(GaimConversation *conv, |
|
| 329 const char *cmd, char **args, char **error, void *data) |
|
| 330 { |
|
| 331 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 332 gaim_conv_im_send(GAIM_CONV_IM(conv), args[0]); |
|
| 333 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 334 gaim_conv_chat_send(GAIM_CONV_CHAT(conv), args[0]); |
|
| 335 |
|
| 336 return GAIM_CMD_RET_OK; |
|
| 337 } |
|
| 338 |
|
| 339 static GaimCmdRet |
|
| 340 me_command_cb(GaimConversation *conv, |
|
| 341 const char *cmd, char **args, char **error, void *data) |
|
| 342 { |
|
| 343 char *tmp; |
|
| 344 |
|
| 345 tmp = g_strdup_printf("/me %s", args[0]); |
|
| 346 |
|
| 347 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 348 gaim_conv_im_send(GAIM_CONV_IM(conv), tmp); |
|
| 349 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 350 gaim_conv_chat_send(GAIM_CONV_CHAT(conv), tmp); |
|
| 351 |
|
| 352 g_free(tmp); |
|
| 353 return GAIM_CMD_RET_OK; |
|
| 354 } |
|
| 355 |
|
| 356 static GaimCmdRet |
|
| 357 debug_command_cb(GaimConversation *conv, |
|
| 358 const char *cmd, char **args, char **error, void *data) |
|
| 359 { |
|
| 360 char *tmp, *markup; |
|
| 361 GaimCmdStatus status; |
|
| 362 |
|
| 363 if (!g_ascii_strcasecmp(args[0], "version")) { |
|
| 364 tmp = g_strdup_printf(_("me is using Gaim v%s."), VERSION); |
|
| 365 markup = g_markup_escape_text(tmp, -1); |
|
| 366 |
|
| 367 status = gaim_cmd_do_command(conv, tmp, markup, error); |
|
| 368 |
|
| 369 g_free(tmp); |
|
| 370 g_free(markup); |
|
| 371 return status; |
|
| 372 } else { |
|
| 373 gaim_conversation_write(conv, NULL, _("Supported debug options are: version"), |
|
| 374 GAIM_MESSAGE_NO_LOG|GAIM_MESSAGE_ERROR, time(NULL)); |
|
| 375 return GAIM_CMD_STATUS_OK; |
|
| 376 } |
|
| 377 } |
|
| 378 |
|
| 379 static GaimCmdRet |
|
| 380 clear_command_cb(GaimConversation *conv, |
|
| 381 const char *cmd, char **args, char **error, void *data) |
|
| 382 { |
|
| 383 GaimGtkConversation *gtkconv = NULL; |
|
| 384 |
|
| 385 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 386 |
|
| 387 gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); |
|
| 388 return GAIM_CMD_STATUS_OK; |
|
| 389 } |
|
| 390 |
|
| 391 static GaimCmdRet |
|
| 392 help_command_cb(GaimConversation *conv, |
|
| 393 const char *cmd, char **args, char **error, void *data) |
|
| 394 { |
|
| 395 GList *l, *text; |
|
| 396 GString *s; |
|
| 397 |
|
| 398 if (args[0] != NULL) { |
|
| 399 s = g_string_new(""); |
|
| 400 text = gaim_cmd_help(conv, args[0]); |
|
| 401 |
|
| 402 if (text) { |
|
| 403 for (l = text; l; l = l->next) |
|
| 404 if (l->next) |
|
| 405 g_string_append_printf(s, "%s\n", (char *)l->data); |
|
| 406 else |
|
| 407 g_string_append_printf(s, "%s", (char *)l->data); |
|
| 408 } else { |
|
| 409 g_string_append(s, _("No such command (in this context).")); |
|
| 410 } |
|
| 411 } else { |
|
| 412 s = g_string_new(_("Use \"/help <command>\" for help on a specific command.\n" |
|
| 413 "The following commands are available in this context:\n")); |
|
| 414 |
|
| 415 text = gaim_cmd_list(conv); |
|
| 416 for (l = text; l; l = l->next) |
|
| 417 if (l->next) |
|
| 418 g_string_append_printf(s, "%s, ", (char *)l->data); |
|
| 419 else |
|
| 420 g_string_append_printf(s, "%s.", (char *)l->data); |
|
| 421 } |
|
| 422 |
|
| 423 gaim_conversation_write(conv, NULL, s->str, GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 424 g_string_free(s, TRUE); |
|
| 425 |
|
| 426 return GAIM_CMD_STATUS_OK; |
|
| 427 } |
|
| 428 |
|
| 429 static void |
|
| 430 send_history_add(GaimConversation *conv, const char *message) |
|
| 431 { |
|
| 432 GList *first; |
|
| 433 |
|
| 434 first = g_list_first(conv->send_history); |
|
| 435 |
|
| 436 if (first->data) |
|
| 437 g_free(first->data); |
|
| 438 |
|
| 439 first->data = g_strdup(message); |
|
| 440 |
|
| 441 conv->send_history = g_list_prepend(first, NULL); |
|
| 442 } |
|
| 443 |
|
| 444 static gboolean |
|
| 445 check_for_and_do_command(GaimConversation *conv) |
|
| 446 { |
|
| 447 GaimGtkConversation *gtkconv; |
|
| 448 char *cmd; |
|
| 449 const char *prefix; |
|
| 450 GaimAccount *account; |
|
| 451 GtkTextIter start; |
|
| 452 |
|
| 453 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 454 account = gaim_conversation_get_account(conv); |
|
| 455 prefix = gaim_gtk_get_cmd_prefix(); |
|
| 456 |
|
| 457 cmd = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); |
|
| 458 gtk_text_buffer_get_start_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &start); |
|
| 459 |
|
| 460 if (cmd && (strncmp(cmd, prefix, strlen(prefix)) == 0) |
|
| 461 && !gtk_text_iter_get_child_anchor(&start)) { |
|
| 462 GaimCmdStatus status; |
|
| 463 char *error, *cmdline, *markup, *send_history; |
|
| 464 GtkTextIter end; |
|
| 465 |
|
| 466 send_history = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); |
|
| 467 send_history_add(conv, send_history); |
|
| 468 g_free(send_history); |
|
| 469 |
|
| 470 cmdline = cmd + strlen(prefix); |
|
| 471 |
|
| 472 gtk_text_iter_forward_chars(&start, g_utf8_strlen(prefix, -1)); |
|
| 473 gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->entry)->text_buffer, &end); |
|
| 474 markup = gtk_imhtml_get_markup_range(GTK_IMHTML(gtkconv->entry), &start, &end); |
|
| 475 status = gaim_cmd_do_command(conv, cmdline, markup, &error); |
|
| 476 g_free(cmd); |
|
| 477 g_free(markup); |
|
| 478 |
|
| 479 switch (status) { |
|
| 480 case GAIM_CMD_STATUS_OK: |
|
| 481 return TRUE; |
|
| 482 case GAIM_CMD_STATUS_NOT_FOUND: |
|
| 483 if (!gaim_prefs_get_bool("/gaim/gtk/conversations/passthrough_unknown_commands")) { |
|
| 484 gaim_conversation_write(conv, "", _("No such command."), |
|
| 485 GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 486 |
|
| 487 return TRUE; |
|
| 488 } |
|
| 489 return FALSE; |
|
| 490 case GAIM_CMD_STATUS_WRONG_ARGS: |
|
| 491 gaim_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments " |
|
| 492 "to that command."), |
|
| 493 GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 494 return TRUE; |
|
| 495 case GAIM_CMD_STATUS_FAILED: |
|
| 496 gaim_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."), |
|
| 497 GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 498 if(error) |
|
| 499 g_free(error); |
|
| 500 return TRUE; |
|
| 501 case GAIM_CMD_STATUS_WRONG_TYPE: |
|
| 502 if(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 503 gaim_conversation_write(conv, "", _("That command only works in chats, not IMs."), |
|
| 504 GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 505 else |
|
| 506 gaim_conversation_write(conv, "", _("That command only works in IMs, not chats."), |
|
| 507 GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 508 return TRUE; |
|
| 509 case GAIM_CMD_STATUS_WRONG_PRPL: |
|
| 510 gaim_conversation_write(conv, "", _("That command doesn't work on this protocol."), |
|
| 511 GAIM_MESSAGE_NO_LOG, time(NULL)); |
|
| 512 return TRUE; |
|
| 513 } |
|
| 514 } |
|
| 515 |
|
| 516 g_free(cmd); |
|
| 517 return FALSE; |
|
| 518 } |
|
| 519 |
|
| 520 static void |
|
| 521 send_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 522 { |
|
| 523 GaimConversation *conv = gtkconv->active_conv; |
|
| 524 GaimAccount *account; |
|
| 525 GaimConnection *gc; |
|
| 526 GaimMessageFlags flags = 0; |
|
| 527 char *buf, *clean; |
|
| 528 |
|
| 529 account = gaim_conversation_get_account(conv); |
|
| 530 |
|
| 531 if (!gaim_account_is_connected(account)) |
|
| 532 return; |
|
| 533 |
|
| 534 if ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) && |
|
| 535 gaim_conv_chat_has_left(GAIM_CONV_CHAT(conv))) |
|
| 536 return; |
|
| 537 |
|
| 538 if (check_for_and_do_command(conv)) { |
|
| 539 gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); |
|
| 540 return; |
|
| 541 } |
|
| 542 |
|
| 543 buf = gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); |
|
| 544 clean = gtk_imhtml_get_text(GTK_IMHTML(gtkconv->entry), NULL, NULL); |
|
| 545 |
|
| 546 gtk_widget_grab_focus(gtkconv->entry); |
|
| 547 |
|
| 548 if (strlen(clean) == 0) { |
|
| 549 g_free(clean); |
|
| 550 return; |
|
| 551 } |
|
| 552 |
|
| 553 gaim_idle_touch(); |
|
| 554 |
|
| 555 /* XXX: is there a better way to tell if the message has images? */ |
|
| 556 if (GTK_IMHTML(gtkconv->entry)->im_images != NULL) |
|
| 557 flags |= GAIM_MESSAGE_IMAGES; |
|
| 558 |
|
| 559 gc = gaim_account_get_connection(account); |
|
| 560 if (gc && (conv->features & GAIM_CONNECTION_NO_NEWLINES)) { |
|
| 561 char **bufs; |
|
| 562 int i; |
|
| 563 |
|
| 564 bufs = gtk_imhtml_get_markup_lines(GTK_IMHTML(gtkconv->entry)); |
|
| 565 for (i = 0; bufs[i]; i++) { |
|
| 566 send_history_add(conv, bufs[i]); |
|
| 567 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 568 gaim_conv_im_send_with_flags(GAIM_CONV_IM(conv), bufs[i], flags); |
|
| 569 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 570 gaim_conv_chat_send_with_flags(GAIM_CONV_CHAT(conv), bufs[i], flags); |
|
| 571 } |
|
| 572 |
|
| 573 g_strfreev(bufs); |
|
| 574 |
|
| 575 } else { |
|
| 576 send_history_add(conv, buf); |
|
| 577 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 578 gaim_conv_im_send_with_flags(GAIM_CONV_IM(conv), buf, flags); |
|
| 579 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 580 gaim_conv_chat_send_with_flags(GAIM_CONV_CHAT(conv), buf, flags); |
|
| 581 } |
|
| 582 |
|
| 583 g_free(clean); |
|
| 584 g_free(buf); |
|
| 585 |
|
| 586 gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); |
|
| 587 gtkconv_set_unseen(gtkconv, GAIM_UNSEEN_NONE); |
|
| 588 } |
|
| 589 |
|
| 590 static void |
|
| 591 add_remove_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 592 { |
|
| 593 GaimAccount *account; |
|
| 594 const char *name; |
|
| 595 GaimConversation *conv = gtkconv->active_conv; |
|
| 596 |
|
| 597 account = gaim_conversation_get_account(conv); |
|
| 598 name = gaim_conversation_get_name(conv); |
|
| 599 |
|
| 600 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 601 GaimBuddy *b; |
|
| 602 |
|
| 603 b = gaim_find_buddy(account, name); |
|
| 604 if (b != NULL) |
|
| 605 gaim_gtkdialogs_remove_buddy(b); |
|
| 606 else if (account != NULL && gaim_account_is_connected(account)) |
|
| 607 gaim_blist_request_add_buddy(account, (char *)name, NULL, NULL); |
|
| 608 } else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 609 GaimChat *c; |
|
| 610 |
|
| 611 c = gaim_blist_find_chat(account, name); |
|
| 612 if (c != NULL) |
|
| 613 gaim_gtkdialogs_remove_chat(c); |
|
| 614 else if (account != NULL && gaim_account_is_connected(account)) |
|
| 615 gaim_blist_request_add_chat(account, NULL, NULL, name); |
|
| 616 } |
|
| 617 |
|
| 618 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry); |
|
| 619 } |
|
| 620 |
|
| 621 static void chat_do_info(GaimGtkConversation *gtkconv, const char *who) |
|
| 622 { |
|
| 623 GaimConversation *conv = gtkconv->active_conv; |
|
| 624 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 625 GaimConnection *gc; |
|
| 626 |
|
| 627 if ((gc = gaim_conversation_get_gc(conv))) { |
|
| 628 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 629 |
|
| 630 /* |
|
| 631 * If there are special needs for getting info on users in |
|
| 632 * buddy chat "rooms"... |
|
| 633 */ |
|
| 634 if (prpl_info->get_cb_info != NULL) |
|
| 635 { |
|
| 636 prpl_info->get_cb_info(gc, |
|
| 637 gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)), who); |
|
| 638 } |
|
| 639 else |
|
| 640 prpl_info->get_info(gc, who); |
|
| 641 } |
|
| 642 } |
|
| 643 |
|
| 644 |
|
| 645 static void |
|
| 646 info_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 647 { |
|
| 648 GaimConversation *conv = gtkconv->active_conv; |
|
| 649 |
|
| 650 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 651 serv_get_info(gaim_conversation_get_gc(conv), |
|
| 652 gaim_conversation_get_name(conv)); |
|
| 653 |
|
| 654 gtk_widget_grab_focus(gtkconv->entry); |
|
| 655 } else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 656 /* Get info of the person currently selected in the GtkTreeView */ |
|
| 657 GaimGtkChatPane *gtkchat; |
|
| 658 GtkTreeIter iter; |
|
| 659 GtkTreeModel *model; |
|
| 660 GtkTreeSelection *sel; |
|
| 661 char *name; |
|
| 662 |
|
| 663 gtkchat = gtkconv->u.chat; |
|
| 664 |
|
| 665 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 666 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list)); |
|
| 667 |
|
| 668 if (gtk_tree_selection_get_selected(sel, NULL, &iter)) |
|
| 669 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &name, -1); |
|
| 670 else |
|
| 671 return; |
|
| 672 |
|
| 673 chat_do_info(gtkconv, name); |
|
| 674 g_free(name); |
|
| 675 } |
|
| 676 } |
|
| 677 |
|
| 678 static void |
|
| 679 block_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 680 { |
|
| 681 GaimConversation *conv = gtkconv->active_conv; |
|
| 682 GaimAccount *account; |
|
| 683 |
|
| 684 account = gaim_conversation_get_account(conv); |
|
| 685 |
|
| 686 if (account != NULL && gaim_account_is_connected(account)) |
|
| 687 gaim_gtk_request_add_block(account, gaim_conversation_get_name(conv)); |
|
| 688 |
|
| 689 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry); |
|
| 690 } |
|
| 691 |
|
| 692 static void |
|
| 693 do_invite(GtkWidget *w, int resp, InviteBuddyInfo *info) |
|
| 694 { |
|
| 695 const char *buddy, *message; |
|
| 696 GaimGtkConversation *gtkconv; |
|
| 697 |
|
| 698 gtkconv = GAIM_GTK_CONVERSATION(info->conv); |
|
| 699 |
|
| 700 if (resp == GTK_RESPONSE_OK) { |
|
| 701 buddy = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(info->entry)->entry)); |
|
| 702 message = gtk_entry_get_text(GTK_ENTRY(info->message)); |
|
| 703 |
|
| 704 if (!g_ascii_strcasecmp(buddy, "")) |
|
| 705 return; |
|
| 706 |
|
| 707 serv_chat_invite(gaim_conversation_get_gc(info->conv), |
|
| 708 gaim_conv_chat_get_id(GAIM_CONV_CHAT(info->conv)), |
|
| 709 message, buddy); |
|
| 710 } |
|
| 711 |
|
| 712 gtk_widget_destroy(invite_dialog); |
|
| 713 invite_dialog = NULL; |
|
| 714 |
|
| 715 g_free(info); |
|
| 716 } |
|
| 717 |
|
| 718 static void |
|
| 719 invite_dnd_recv(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, |
|
| 720 GtkSelectionData *sd, guint inf, guint t, gpointer data) |
|
| 721 { |
|
| 722 InviteBuddyInfo *info = (InviteBuddyInfo *)data; |
|
| 723 const char *convprotocol; |
|
| 724 |
|
| 725 convprotocol = gaim_account_get_protocol_id(gaim_conversation_get_account(info->conv)); |
|
| 726 |
|
| 727 if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) |
|
| 728 { |
|
| 729 GaimBlistNode *node = NULL; |
|
| 730 GaimBuddy *buddy; |
|
| 731 |
|
| 732 memcpy(&node, sd->data, sizeof(node)); |
|
| 733 |
|
| 734 if (GAIM_BLIST_NODE_IS_CONTACT(node)) |
|
| 735 buddy = gaim_contact_get_priority_buddy((GaimContact *)node); |
|
| 736 else if (GAIM_BLIST_NODE_IS_BUDDY(node)) |
|
| 737 buddy = (GaimBuddy *)node; |
|
| 738 else |
|
| 739 return; |
|
| 740 |
|
| 741 if (strcmp(convprotocol, gaim_account_get_protocol_id(buddy->account))) |
|
| 742 { |
|
| 743 gaim_notify_error(NULL, NULL, |
|
| 744 _("That buddy is not on the same protocol as this " |
|
| 745 "chat."), NULL); |
|
| 746 } |
|
| 747 else |
|
| 748 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(info->entry)->entry), buddy->name); |
|
| 749 |
|
| 750 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
|
| 751 } |
|
| 752 else if (sd->target == gdk_atom_intern("application/x-im-contact", FALSE)) |
|
| 753 { |
|
| 754 char *protocol = NULL; |
|
| 755 char *username = NULL; |
|
| 756 GaimAccount *account; |
|
| 757 |
|
| 758 if (gaim_gtk_parse_x_im_contact((const char *)sd->data, FALSE, &account, |
|
| 759 &protocol, &username, NULL)) |
|
| 760 { |
|
| 761 if (account == NULL) |
|
| 762 { |
|
| 763 gaim_notify_error(NULL, NULL, |
|
| 764 _("You are not currently signed on with an account that " |
|
| 765 "can invite that buddy."), NULL); |
|
| 766 } |
|
| 767 else if (strcmp(convprotocol, gaim_account_get_protocol_id(account))) |
|
| 768 { |
|
| 769 gaim_notify_error(NULL, NULL, |
|
| 770 _("That buddy is not on the same protocol as this " |
|
| 771 "chat."), NULL); |
|
| 772 } |
|
| 773 else |
|
| 774 { |
|
| 775 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(info->entry)->entry), username); |
|
| 776 } |
|
| 777 } |
|
| 778 |
|
| 779 if (username != NULL) g_free(username); |
|
| 780 if (protocol != NULL) g_free(protocol); |
|
| 781 |
|
| 782 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
|
| 783 } |
|
| 784 } |
|
| 785 |
|
| 786 static const GtkTargetEntry dnd_targets[] = |
|
| 787 { |
|
| 788 {"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, 0}, |
|
| 789 {"application/x-im-contact", 0, 1} |
|
| 790 }; |
|
| 791 |
|
| 792 static void |
|
| 793 invite_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 794 { |
|
| 795 GaimConversation *conv = gtkconv->active_conv; |
|
| 796 InviteBuddyInfo *info = NULL; |
|
| 797 |
|
| 798 if (invite_dialog == NULL) { |
|
| 799 GaimConnection *gc; |
|
| 800 GaimGtkWindow *gtkwin; |
|
| 801 GtkWidget *label; |
|
| 802 GtkWidget *vbox, *hbox; |
|
| 803 GtkWidget *table; |
|
| 804 GtkWidget *img; |
|
| 805 |
|
| 806 img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, |
|
| 807 GTK_ICON_SIZE_DIALOG); |
|
| 808 |
|
| 809 info = g_new0(InviteBuddyInfo, 1); |
|
| 810 info->conv = conv; |
|
| 811 |
|
| 812 gc = gaim_conversation_get_gc(conv); |
|
| 813 gtkwin = gaim_gtkconv_get_window(gtkconv); |
|
| 814 |
|
| 815 /* Create the new dialog. */ |
|
| 816 invite_dialog = gtk_dialog_new_with_buttons( |
|
| 817 _("Invite Buddy Into Chat Room"), |
|
| 818 GTK_WINDOW(gtkwin->window), 0, |
|
| 819 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
|
| 820 GAIM_STOCK_INVITE, GTK_RESPONSE_OK, NULL); |
|
| 821 |
|
| 822 gtk_dialog_set_default_response(GTK_DIALOG(invite_dialog), |
|
| 823 GTK_RESPONSE_OK); |
|
| 824 gtk_container_set_border_width(GTK_CONTAINER(invite_dialog), GAIM_HIG_BOX_SPACE); |
|
| 825 gtk_window_set_resizable(GTK_WINDOW(invite_dialog), FALSE); |
|
| 826 gtk_dialog_set_has_separator(GTK_DIALOG(invite_dialog), FALSE); |
|
| 827 |
|
| 828 info->window = GTK_WIDGET(invite_dialog); |
|
| 829 |
|
| 830 /* Setup the outside spacing. */ |
|
| 831 vbox = GTK_DIALOG(invite_dialog)->vbox; |
|
| 832 |
|
| 833 gtk_box_set_spacing(GTK_BOX(vbox), GAIM_HIG_BORDER); |
|
| 834 gtk_container_set_border_width(GTK_CONTAINER(vbox), GAIM_HIG_BOX_SPACE); |
|
| 835 |
|
| 836 /* Setup the inner hbox and put the dialog's icon in it. */ |
|
| 837 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BORDER); |
|
| 838 gtk_container_add(GTK_CONTAINER(vbox), hbox); |
|
| 839 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); |
|
| 840 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); |
|
| 841 |
|
| 842 /* Setup the right vbox. */ |
|
| 843 vbox = gtk_vbox_new(FALSE, 0); |
|
| 844 gtk_container_add(GTK_CONTAINER(hbox), vbox); |
|
| 845 |
|
| 846 /* Put our happy label in it. */ |
|
| 847 label = gtk_label_new(_("Please enter the name of the user you wish " |
|
| 848 "to invite, along with an optional invite " |
|
| 849 "message.")); |
|
| 850 gtk_widget_set_size_request(label, 350, -1); |
|
| 851 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
|
| 852 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
|
| 853 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
|
| 854 |
|
| 855 /* hbox for the table, and to give it some spacing on the left. */ |
|
| 856 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 857 gtk_container_add(GTK_CONTAINER(vbox), hbox); |
|
| 858 |
|
| 859 /* Setup the table we're going to use to lay stuff out. */ |
|
| 860 table = gtk_table_new(2, 2, FALSE); |
|
| 861 gtk_table_set_row_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE); |
|
| 862 gtk_table_set_col_spacings(GTK_TABLE(table), GAIM_HIG_BOX_SPACE); |
|
| 863 gtk_container_set_border_width(GTK_CONTAINER(table), GAIM_HIG_BORDER); |
|
| 864 gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); |
|
| 865 |
|
| 866 /* Now the Buddy label */ |
|
| 867 label = gtk_label_new(NULL); |
|
| 868 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Buddy:")); |
|
| 869 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
|
| 870 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); |
|
| 871 |
|
| 872 /* Now the Buddy drop-down entry field. */ |
|
| 873 info->entry = gtk_combo_new(); |
|
| 874 gtk_combo_set_case_sensitive(GTK_COMBO(info->entry), FALSE); |
|
| 875 gtk_entry_set_activates_default( |
|
| 876 GTK_ENTRY(GTK_COMBO(info->entry)->entry), TRUE); |
|
| 877 |
|
| 878 gtk_table_attach_defaults(GTK_TABLE(table), info->entry, 1, 2, 0, 1); |
|
| 879 gtk_label_set_mnemonic_widget(GTK_LABEL(label), info->entry); |
|
| 880 |
|
| 881 /* Fill in the names. */ |
|
| 882 gtk_combo_set_popdown_strings(GTK_COMBO(info->entry), |
|
| 883 generate_invite_user_names(gc)); |
|
| 884 |
|
| 885 |
|
| 886 /* Now the label for "Message" */ |
|
| 887 label = gtk_label_new(NULL); |
|
| 888 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Message:")); |
|
| 889 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
|
| 890 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2); |
|
| 891 |
|
| 892 |
|
| 893 /* And finally, the Message entry field. */ |
|
| 894 info->message = gtk_entry_new(); |
|
| 895 gtk_entry_set_activates_default(GTK_ENTRY(info->message), TRUE); |
|
| 896 |
|
| 897 gtk_table_attach_defaults(GTK_TABLE(table), info->message, 1, 2, 1, 2); |
|
| 898 gtk_label_set_mnemonic_widget(GTK_LABEL(label), info->message); |
|
| 899 |
|
| 900 /* Connect the signals. */ |
|
| 901 g_signal_connect(G_OBJECT(invite_dialog), "response", |
|
| 902 G_CALLBACK(do_invite), info); |
|
| 903 /* Setup drag-and-drop */ |
|
| 904 gtk_drag_dest_set(info->window, |
|
| 905 GTK_DEST_DEFAULT_MOTION | |
|
| 906 GTK_DEST_DEFAULT_DROP, |
|
| 907 dnd_targets, |
|
| 908 sizeof(dnd_targets) / sizeof(GtkTargetEntry), |
|
| 909 GDK_ACTION_COPY); |
|
| 910 gtk_drag_dest_set(info->entry, |
|
| 911 GTK_DEST_DEFAULT_MOTION | |
|
| 912 GTK_DEST_DEFAULT_DROP, |
|
| 913 dnd_targets, |
|
| 914 sizeof(dnd_targets) / sizeof(GtkTargetEntry), |
|
| 915 GDK_ACTION_COPY); |
|
| 916 |
|
| 917 g_signal_connect(G_OBJECT(info->window), "drag_data_received", |
|
| 918 G_CALLBACK(invite_dnd_recv), info); |
|
| 919 g_signal_connect(G_OBJECT(info->entry), "drag_data_received", |
|
| 920 G_CALLBACK(invite_dnd_recv), info); |
|
| 921 |
|
| 922 } |
|
| 923 |
|
| 924 gtk_widget_show_all(invite_dialog); |
|
| 925 |
|
| 926 if (info != NULL) |
|
| 927 gtk_widget_grab_focus(GTK_COMBO(info->entry)->entry); |
|
| 928 } |
|
| 929 |
|
| 930 static void |
|
| 931 menu_new_conv_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 932 { |
|
| 933 gaim_gtkdialogs_im(); |
|
| 934 } |
|
| 935 |
|
| 936 static void |
|
| 937 savelog_writefile_cb(void *user_data, const char *filename) |
|
| 938 { |
|
| 939 GaimConversation *conv = (GaimConversation *)user_data; |
|
| 940 FILE *fp; |
|
| 941 const char *name; |
|
| 942 gchar *text; |
|
| 943 |
|
| 944 if ((fp = g_fopen(filename, "w+")) == NULL) { |
|
| 945 gaim_notify_error(conv, NULL, _("Unable to open file."), NULL); |
|
| 946 return; |
|
| 947 } |
|
| 948 |
|
| 949 name = gaim_conversation_get_name(conv); |
|
| 950 fprintf(fp, "<html>\n<head><title>%s</title></head>\n<body>", name); |
|
| 951 fprintf(fp, _("<h1>Conversation with %s</h1>\n"), name); |
|
| 952 |
|
| 953 text = gtk_imhtml_get_markup( |
|
| 954 GTK_IMHTML(GAIM_GTK_CONVERSATION(conv)->imhtml)); |
|
| 955 fprintf(fp, "%s", text); |
|
| 956 g_free(text); |
|
| 957 |
|
| 958 fprintf(fp, "\n</body>\n</html>\n"); |
|
| 959 fclose(fp); |
|
| 960 } |
|
| 961 |
|
| 962 /* |
|
| 963 * It would be kinda cool if this gave the option of saving a |
|
| 964 * plaintext v. HTML file. |
|
| 965 */ |
|
| 966 static void |
|
| 967 menu_save_as_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 968 { |
|
| 969 GaimGtkWindow *win = data; |
|
| 970 GaimConversation *conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 971 gchar *buf; |
|
| 972 |
|
| 973 buf = g_strdup_printf("%s.html", gaim_normalize(conv->account, conv->name)); |
|
| 974 |
|
| 975 gaim_request_file(conv, _("Save Conversation"), gaim_escape_filename(buf), |
|
| 976 TRUE, G_CALLBACK(savelog_writefile_cb), NULL, conv); |
|
| 977 |
|
| 978 g_free(buf); |
|
| 979 } |
|
| 980 |
|
| 981 static void |
|
| 982 menu_view_log_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 983 { |
|
| 984 GaimGtkWindow *win = data; |
|
| 985 GaimConversation *conv; |
|
| 986 GaimLogType type; |
|
| 987 GaimGtkBuddyList *gtkblist; |
|
| 988 GdkCursor *cursor; |
|
| 989 const char *name; |
|
| 990 GaimAccount *account; |
|
| 991 GSList *buddies; |
|
| 992 GSList *cur; |
|
| 993 |
|
| 994 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 995 |
|
| 996 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 997 type = GAIM_LOG_IM; |
|
| 998 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 999 type = GAIM_LOG_CHAT; |
|
| 1000 else |
|
| 1001 return; |
|
| 1002 |
|
| 1003 gtkblist = gaim_gtk_blist_get_default_gtk_blist(); |
|
| 1004 cursor = gdk_cursor_new(GDK_WATCH); |
|
| 1005 |
|
| 1006 gdk_window_set_cursor(gtkblist->window->window, cursor); |
|
| 1007 gdk_window_set_cursor(win->window->window, cursor); |
|
| 1008 gdk_cursor_unref(cursor); |
|
| 1009 while (gtk_events_pending()) |
|
| 1010 gtk_main_iteration(); |
|
| 1011 |
|
| 1012 name = gaim_conversation_get_name(conv); |
|
| 1013 account = gaim_conversation_get_account(conv); |
|
| 1014 |
|
| 1015 buddies = gaim_find_buddies(account, name); |
|
| 1016 for (cur = buddies; cur != NULL; cur = cur->next) |
|
| 1017 { |
|
| 1018 GaimBlistNode *node = cur->data; |
|
| 1019 if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL))) |
|
| 1020 { |
|
| 1021 gaim_gtk_log_show_contact((GaimContact *)node->parent); |
|
| 1022 g_slist_free(buddies); |
|
| 1023 gdk_window_set_cursor(gtkblist->window->window, NULL); |
|
| 1024 gdk_window_set_cursor(win->window->window, NULL); |
|
| 1025 return; |
|
| 1026 } |
|
| 1027 } |
|
| 1028 g_slist_free(buddies); |
|
| 1029 |
|
| 1030 gaim_gtk_log_show(type, name, account); |
|
| 1031 |
|
| 1032 gdk_window_set_cursor(gtkblist->window->window, NULL); |
|
| 1033 gdk_window_set_cursor(win->window->window, NULL); |
|
| 1034 } |
|
| 1035 |
|
| 1036 static void |
|
| 1037 menu_clear_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1038 { |
|
| 1039 GaimGtkWindow *win = data; |
|
| 1040 GaimConversation *conv; |
|
| 1041 GaimGtkConversation *gtkconv; |
|
| 1042 |
|
| 1043 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1044 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 1045 |
|
| 1046 gtk_imhtml_clear(GTK_IMHTML(gtkconv->imhtml)); |
|
| 1047 } |
|
| 1048 |
|
| 1049 struct _search { |
|
| 1050 GaimGtkConversation *gtkconv; |
|
| 1051 GtkWidget *entry; |
|
| 1052 }; |
|
| 1053 |
|
| 1054 static void do_search_cb(GtkWidget *widget, gint resp, struct _search *s) |
|
| 1055 { |
|
| 1056 switch (resp) { |
|
| 1057 case GTK_RESPONSE_OK: |
|
| 1058 gtk_imhtml_search_find(GTK_IMHTML(s->gtkconv->imhtml), |
|
| 1059 gtk_entry_get_text(GTK_ENTRY(s->entry))); |
|
| 1060 break; |
|
| 1061 |
|
| 1062 case GTK_RESPONSE_DELETE_EVENT: |
|
| 1063 case GTK_RESPONSE_CLOSE: |
|
| 1064 gtk_imhtml_search_clear(GTK_IMHTML(s->gtkconv->imhtml)); |
|
| 1065 gtk_widget_destroy(s->gtkconv->dialogs.search); |
|
| 1066 s->gtkconv->dialogs.search = NULL; |
|
| 1067 g_free(s); |
|
| 1068 break; |
|
| 1069 } |
|
| 1070 } |
|
| 1071 |
|
| 1072 static void |
|
| 1073 menu_find_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1074 { |
|
| 1075 GaimGtkWindow *gtkwin = data; |
|
| 1076 GaimConversation *conv = gaim_gtk_conv_window_get_active_conversation(gtkwin); |
|
| 1077 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 1078 GtkWidget *hbox; |
|
| 1079 GtkWidget *img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_QUESTION, |
|
| 1080 GTK_ICON_SIZE_DIALOG); |
|
| 1081 GtkWidget *label; |
|
| 1082 struct _search *s; |
|
| 1083 |
|
| 1084 if (gtkconv->dialogs.search) { |
|
| 1085 gtk_window_present(GTK_WINDOW(gtkconv->dialogs.search)); |
|
| 1086 return; |
|
| 1087 } |
|
| 1088 |
|
| 1089 s = g_malloc(sizeof(struct _search)); |
|
| 1090 s->gtkconv = gtkconv; |
|
| 1091 |
|
| 1092 gtkconv->dialogs.search = gtk_dialog_new_with_buttons(_("Find"), |
|
| 1093 GTK_WINDOW(gtkwin->window), GTK_DIALOG_DESTROY_WITH_PARENT, |
|
| 1094 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, |
|
| 1095 GTK_STOCK_FIND, GTK_RESPONSE_OK, NULL); |
|
| 1096 gtk_dialog_set_default_response(GTK_DIALOG(gtkconv->dialogs.search), |
|
| 1097 GTK_RESPONSE_OK); |
|
| 1098 g_signal_connect(G_OBJECT(gtkconv->dialogs.search), "response", |
|
| 1099 G_CALLBACK(do_search_cb), s); |
|
| 1100 |
|
| 1101 gtk_container_set_border_width(GTK_CONTAINER(gtkconv->dialogs.search), GAIM_HIG_BOX_SPACE); |
|
| 1102 gtk_window_set_resizable(GTK_WINDOW(gtkconv->dialogs.search), FALSE); |
|
| 1103 gtk_dialog_set_has_separator(GTK_DIALOG(gtkconv->dialogs.search), FALSE); |
|
| 1104 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(gtkconv->dialogs.search)->vbox), GAIM_HIG_BORDER); |
|
| 1105 gtk_container_set_border_width( |
|
| 1106 GTK_CONTAINER(GTK_DIALOG(gtkconv->dialogs.search)->vbox), GAIM_HIG_BOX_SPACE); |
|
| 1107 |
|
| 1108 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BORDER); |
|
| 1109 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(gtkconv->dialogs.search)->vbox), |
|
| 1110 hbox); |
|
| 1111 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); |
|
| 1112 |
|
| 1113 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); |
|
| 1114 gtk_dialog_set_response_sensitive(GTK_DIALOG(gtkconv->dialogs.search), |
|
| 1115 GTK_RESPONSE_OK, FALSE); |
|
| 1116 |
|
| 1117 label = gtk_label_new(NULL); |
|
| 1118 gtk_label_set_markup_with_mnemonic(GTK_LABEL(label), _("_Search for:")); |
|
| 1119 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
|
| 1120 |
|
| 1121 s->entry = gtk_entry_new(); |
|
| 1122 gtk_entry_set_activates_default(GTK_ENTRY(s->entry), TRUE); |
|
| 1123 gtk_label_set_mnemonic_widget(GTK_LABEL(label), GTK_WIDGET(s->entry)); |
|
| 1124 g_signal_connect(G_OBJECT(s->entry), "changed", |
|
| 1125 G_CALLBACK(gaim_gtk_set_sensitive_if_input), |
|
| 1126 gtkconv->dialogs.search); |
|
| 1127 gtk_box_pack_start(GTK_BOX(hbox), s->entry, FALSE, FALSE, 0); |
|
| 1128 |
|
| 1129 gtk_widget_show_all(gtkconv->dialogs.search); |
|
| 1130 gtk_widget_grab_focus(s->entry); |
|
| 1131 } |
|
| 1132 |
|
| 1133 static void |
|
| 1134 menu_send_file_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1135 { |
|
| 1136 GaimGtkWindow *win = data; |
|
| 1137 GaimConversation *conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1138 |
|
| 1139 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 1140 serv_send_file(gaim_conversation_get_gc(conv), gaim_conversation_get_name(conv), NULL); |
|
| 1141 } |
|
| 1142 |
|
| 1143 } |
|
| 1144 |
|
| 1145 static void |
|
| 1146 menu_add_pounce_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1147 { |
|
| 1148 GaimGtkWindow *win = data; |
|
| 1149 GaimConversation *conv; |
|
| 1150 |
|
| 1151 conv = gaim_gtk_conv_window_get_active_gtkconv(win)->active_conv; |
|
| 1152 |
|
| 1153 gaim_gtk_pounce_editor_show(gaim_conversation_get_account(conv), |
|
| 1154 gaim_conversation_get_name(conv), NULL); |
|
| 1155 } |
|
| 1156 |
|
| 1157 static void |
|
| 1158 menu_insert_link_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1159 { |
|
| 1160 GaimGtkWindow *win = data; |
|
| 1161 GaimGtkConversation *gtkconv; |
|
| 1162 GtkIMHtmlToolbar *toolbar; |
|
| 1163 |
|
| 1164 gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 1165 toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar); |
|
| 1166 |
|
| 1167 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->link), |
|
| 1168 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->link))); |
|
| 1169 } |
|
| 1170 |
|
| 1171 static void |
|
| 1172 menu_insert_image_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1173 { |
|
| 1174 GaimGtkWindow *win = data; |
|
| 1175 GaimConversation *conv; |
|
| 1176 GaimGtkConversation *gtkconv; |
|
| 1177 GtkIMHtmlToolbar *toolbar; |
|
| 1178 |
|
| 1179 gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 1180 conv = gtkconv->active_conv; |
|
| 1181 toolbar = GTK_IMHTMLTOOLBAR(gtkconv->toolbar); |
|
| 1182 |
|
| 1183 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toolbar->image), |
|
| 1184 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toolbar->image))); |
|
| 1185 } |
|
| 1186 |
|
| 1187 static void |
|
| 1188 menu_alias_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1189 { |
|
| 1190 GaimGtkWindow *win = data; |
|
| 1191 GaimConversation *conv; |
|
| 1192 GaimAccount *account; |
|
| 1193 const char *name; |
|
| 1194 |
|
| 1195 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1196 account = gaim_conversation_get_account(conv); |
|
| 1197 name = gaim_conversation_get_name(conv); |
|
| 1198 |
|
| 1199 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 1200 GaimBuddy *b; |
|
| 1201 |
|
| 1202 b = gaim_find_buddy(account, name); |
|
| 1203 if (b != NULL) |
|
| 1204 gaim_gtkdialogs_alias_buddy(b); |
|
| 1205 } else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 1206 GaimChat *c; |
|
| 1207 |
|
| 1208 c = gaim_blist_find_chat(account, name); |
|
| 1209 if (c != NULL) |
|
| 1210 gaim_gtkdialogs_alias_chat(c); |
|
| 1211 } |
|
| 1212 } |
|
| 1213 |
|
| 1214 static void |
|
| 1215 menu_get_info_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1216 { |
|
| 1217 GaimGtkWindow *win = data; |
|
| 1218 GaimConversation *conv; |
|
| 1219 |
|
| 1220 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1221 |
|
| 1222 info_cb(NULL, GAIM_GTK_CONVERSATION(conv)); |
|
| 1223 } |
|
| 1224 |
|
| 1225 static void |
|
| 1226 menu_invite_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1227 { |
|
| 1228 GaimGtkWindow *win = data; |
|
| 1229 GaimConversation *conv; |
|
| 1230 |
|
| 1231 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1232 |
|
| 1233 invite_cb(NULL, GAIM_GTK_CONVERSATION(conv)); |
|
| 1234 } |
|
| 1235 |
|
| 1236 static void |
|
| 1237 menu_block_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1238 { |
|
| 1239 GaimGtkWindow *win = data; |
|
| 1240 GaimConversation *conv; |
|
| 1241 |
|
| 1242 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1243 |
|
| 1244 block_cb(NULL, GAIM_GTK_CONVERSATION(conv)); |
|
| 1245 } |
|
| 1246 |
|
| 1247 static void |
|
| 1248 menu_add_remove_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1249 { |
|
| 1250 GaimGtkWindow *win = data; |
|
| 1251 GaimConversation *conv; |
|
| 1252 |
|
| 1253 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1254 |
|
| 1255 add_remove_cb(NULL, GAIM_GTK_CONVERSATION(conv)); |
|
| 1256 } |
|
| 1257 |
|
| 1258 static void |
|
| 1259 menu_close_conv_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1260 { |
|
| 1261 GaimGtkWindow *win = data; |
|
| 1262 |
|
| 1263 close_conv_cb(NULL, GAIM_GTK_CONVERSATION(gaim_gtk_conv_window_get_active_conversation(win))); |
|
| 1264 } |
|
| 1265 |
|
| 1266 static void |
|
| 1267 menu_logging_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1268 { |
|
| 1269 GaimGtkWindow *win = data; |
|
| 1270 GaimConversation *conv; |
|
| 1271 gboolean logging; |
|
| 1272 |
|
| 1273 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1274 |
|
| 1275 if (conv == NULL) |
|
| 1276 return; |
|
| 1277 |
|
| 1278 logging = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); |
|
| 1279 |
|
| 1280 if (logging == gaim_conversation_is_logging(conv)) |
|
| 1281 return; |
|
| 1282 |
|
| 1283 if (logging) |
|
| 1284 { |
|
| 1285 /* Enable logging first so the message below can be logged. */ |
|
| 1286 gaim_conversation_set_logging(conv, TRUE); |
|
| 1287 |
|
| 1288 gaim_conversation_write(conv, NULL, |
|
| 1289 _("Logging started. Future messages in this conversation will be logged."), |
|
| 1290 conv->logs ? (GAIM_MESSAGE_SYSTEM) : |
|
| 1291 (GAIM_MESSAGE_SYSTEM | GAIM_MESSAGE_NO_LOG), |
|
| 1292 time(NULL)); |
|
| 1293 } |
|
| 1294 else |
|
| 1295 { |
|
| 1296 gaim_conversation_write(conv, NULL, |
|
| 1297 _("Logging stopped. Future messages in this conversation will not be logged."), |
|
| 1298 conv->logs ? (GAIM_MESSAGE_SYSTEM) : |
|
| 1299 (GAIM_MESSAGE_SYSTEM | GAIM_MESSAGE_NO_LOG), |
|
| 1300 time(NULL)); |
|
| 1301 |
|
| 1302 /* Disable the logging second, so that the above message can be logged. */ |
|
| 1303 gaim_conversation_set_logging(conv, FALSE); |
|
| 1304 } |
|
| 1305 } |
|
| 1306 |
|
| 1307 static void |
|
| 1308 menu_toolbar_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1309 { |
|
| 1310 GaimGtkWindow *win = data; |
|
| 1311 GaimConversation *conv; |
|
| 1312 GaimGtkConversation *gtkconv; |
|
| 1313 |
|
| 1314 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1315 |
|
| 1316 if (conv == NULL) |
|
| 1317 return; |
|
| 1318 |
|
| 1319 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 1320 |
|
| 1321 gaim_prefs_set_bool("/gaim/gtk/conversations/show_formatting_toolbar", |
|
| 1322 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))); |
|
| 1323 } |
|
| 1324 |
|
| 1325 static void |
|
| 1326 menu_sounds_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1327 { |
|
| 1328 GaimGtkWindow *win = data; |
|
| 1329 GaimConversation *conv; |
|
| 1330 GaimGtkConversation *gtkconv; |
|
| 1331 |
|
| 1332 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1333 |
|
| 1334 if (!conv) |
|
| 1335 return; |
|
| 1336 |
|
| 1337 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 1338 |
|
| 1339 gtkconv->make_sound = |
|
| 1340 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); |
|
| 1341 } |
|
| 1342 |
|
| 1343 static void |
|
| 1344 menu_timestamps_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 1345 { |
|
| 1346 GaimGtkWindow *win = data; |
|
| 1347 GaimConversation *conv; |
|
| 1348 GaimGtkConversation *gtkconv; |
|
| 1349 |
|
| 1350 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 1351 |
|
| 1352 if (!conv) |
|
| 1353 return; |
|
| 1354 |
|
| 1355 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 1356 |
|
| 1357 gaim_prefs_set_bool("/gaim/gtk/conversations/show_timestamps", |
|
| 1358 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))); |
|
| 1359 } |
|
| 1360 |
|
| 1361 static void |
|
| 1362 chat_do_im(GaimGtkConversation *gtkconv, const char *who) |
|
| 1363 { |
|
| 1364 GaimConversation *conv = gtkconv->active_conv; |
|
| 1365 GaimAccount *account; |
|
| 1366 GaimConnection *gc; |
|
| 1367 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 1368 char *real_who; |
|
| 1369 |
|
| 1370 account = gaim_conversation_get_account(conv); |
|
| 1371 g_return_if_fail(account != NULL); |
|
| 1372 |
|
| 1373 gc = gaim_account_get_connection(account); |
|
| 1374 g_return_if_fail(gc != NULL); |
|
| 1375 |
|
| 1376 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 1377 |
|
| 1378 if (prpl_info && prpl_info->get_cb_real_name) |
|
| 1379 real_who = prpl_info->get_cb_real_name(gc, |
|
| 1380 gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)), who); |
|
| 1381 else |
|
| 1382 real_who = g_strdup(who); |
|
| 1383 |
|
| 1384 if(!real_who) |
|
| 1385 return; |
|
| 1386 |
|
| 1387 gaim_gtkdialogs_im_with_user(account, real_who); |
|
| 1388 |
|
| 1389 g_free(real_who); |
|
| 1390 } |
|
| 1391 |
|
| 1392 static void |
|
| 1393 chat_im_button_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 1394 { |
|
| 1395 GaimGtkChatPane *gtkchat; |
|
| 1396 GtkTreeIter iter; |
|
| 1397 GtkTreeModel *model; |
|
| 1398 GtkTreeSelection *sel; |
|
| 1399 char *name; |
|
| 1400 |
|
| 1401 gtkchat = gtkconv->u.chat; |
|
| 1402 |
|
| 1403 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1404 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1405 |
|
| 1406 if (gtk_tree_selection_get_selected(sel, NULL, &iter)) |
|
| 1407 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &name, -1); |
|
| 1408 else |
|
| 1409 return; |
|
| 1410 |
|
| 1411 chat_do_im(gtkconv, name); |
|
| 1412 g_free(name); |
|
| 1413 } |
|
| 1414 |
|
| 1415 static void |
|
| 1416 ignore_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1417 { |
|
| 1418 GaimConversation *conv = gtkconv->active_conv; |
|
| 1419 GaimGtkChatPane *gtkchat; |
|
| 1420 GaimConvChat *chat; |
|
| 1421 GaimConvChatBuddyFlags flags; |
|
| 1422 GtkTreeIter iter; |
|
| 1423 GtkTreeModel *model; |
|
| 1424 GtkTreeSelection *sel; |
|
| 1425 char *name; |
|
| 1426 char *alias; |
|
| 1427 |
|
| 1428 chat = GAIM_CONV_CHAT(conv); |
|
| 1429 gtkchat = gtkconv->u.chat; |
|
| 1430 |
|
| 1431 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1432 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1433 |
|
| 1434 if (gtk_tree_selection_get_selected(sel, NULL, &iter)) { |
|
| 1435 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, |
|
| 1436 CHAT_USERS_NAME_COLUMN, &name, |
|
| 1437 CHAT_USERS_ALIAS_COLUMN, &alias, |
|
| 1438 CHAT_USERS_FLAGS_COLUMN, &flags, |
|
| 1439 -1); |
|
| 1440 gtk_list_store_remove(GTK_LIST_STORE(model), &iter); |
|
| 1441 } |
|
| 1442 else |
|
| 1443 return; |
|
| 1444 |
|
| 1445 if (gaim_conv_chat_is_user_ignored(chat, name)) |
|
| 1446 gaim_conv_chat_unignore(chat, name); |
|
| 1447 else |
|
| 1448 gaim_conv_chat_ignore(chat, name); |
|
| 1449 |
|
| 1450 add_chat_buddy_common(conv, name, flags, alias, NULL); |
|
| 1451 g_free(name); |
|
| 1452 g_free(alias); |
|
| 1453 } |
|
| 1454 |
|
| 1455 static void |
|
| 1456 menu_chat_im_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1457 { |
|
| 1458 const char *who = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1459 |
|
| 1460 chat_do_im(gtkconv, who); |
|
| 1461 } |
|
| 1462 |
|
| 1463 static void |
|
| 1464 menu_chat_send_file_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1465 { |
|
| 1466 GaimConversation *conv = gtkconv->active_conv; |
|
| 1467 const char *who = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1468 GaimConnection *gc = gaim_conversation_get_gc(conv); |
|
| 1469 |
|
| 1470 serv_send_file(gc, who, NULL); |
|
| 1471 } |
|
| 1472 |
|
| 1473 static void |
|
| 1474 menu_chat_info_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1475 { |
|
| 1476 char *who; |
|
| 1477 |
|
| 1478 who = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1479 |
|
| 1480 chat_do_info(gtkconv, who); |
|
| 1481 } |
|
| 1482 |
|
| 1483 static void |
|
| 1484 menu_chat_get_away_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1485 { |
|
| 1486 GaimConversation *conv = gtkconv->active_conv; |
|
| 1487 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 1488 GaimConnection *gc; |
|
| 1489 char *who; |
|
| 1490 |
|
| 1491 gc = gaim_conversation_get_gc(conv); |
|
| 1492 who = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1493 |
|
| 1494 if (gc != NULL) { |
|
| 1495 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 1496 |
|
| 1497 /* |
|
| 1498 * May want to expand this to work similarly to menu_info_cb? |
|
| 1499 */ |
|
| 1500 |
|
| 1501 if (prpl_info->get_cb_away != NULL) |
|
| 1502 { |
|
| 1503 prpl_info->get_cb_away(gc, |
|
| 1504 gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)), who); |
|
| 1505 } |
|
| 1506 } |
|
| 1507 } |
|
| 1508 |
|
| 1509 static void |
|
| 1510 menu_chat_add_remove_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1511 { |
|
| 1512 GaimConversation *conv = gtkconv->active_conv; |
|
| 1513 GaimAccount *account; |
|
| 1514 GaimBuddy *b; |
|
| 1515 char *name; |
|
| 1516 |
|
| 1517 account = gaim_conversation_get_account(conv); |
|
| 1518 name = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1519 b = gaim_find_buddy(account, name); |
|
| 1520 |
|
| 1521 if (b != NULL) |
|
| 1522 gaim_gtkdialogs_remove_buddy(b); |
|
| 1523 else if (account != NULL && gaim_account_is_connected(account)) |
|
| 1524 gaim_blist_request_add_buddy(account, name, NULL, NULL); |
|
| 1525 |
|
| 1526 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry); |
|
| 1527 } |
|
| 1528 |
|
| 1529 static GtkTextMark * |
|
| 1530 get_mark_for_user(GaimGtkConversation *gtkconv, const char *who) |
|
| 1531 { |
|
| 1532 GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)); |
|
| 1533 char *tmp = g_strconcat("user:", who, NULL); |
|
| 1534 GtkTextMark *mark = gtk_text_buffer_get_mark(buf, tmp); |
|
| 1535 |
|
| 1536 g_free(tmp); |
|
| 1537 return mark; |
|
| 1538 } |
|
| 1539 |
|
| 1540 static void |
|
| 1541 menu_last_said_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 1542 { |
|
| 1543 GtkTextMark *mark; |
|
| 1544 const char *who; |
|
| 1545 |
|
| 1546 who = g_object_get_data(G_OBJECT(w), "user_data"); |
|
| 1547 mark = get_mark_for_user(gtkconv, who); |
|
| 1548 |
|
| 1549 if (mark != NULL) |
|
| 1550 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0); |
|
| 1551 else |
|
| 1552 g_return_if_reached(); |
|
| 1553 } |
|
| 1554 |
|
| 1555 static GtkWidget * |
|
| 1556 create_chat_menu(GaimConversation *conv, const char *who, |
|
| 1557 GaimPluginProtocolInfo *prpl_info, GaimConnection *gc) |
|
| 1558 { |
|
| 1559 static GtkWidget *menu = NULL; |
|
| 1560 GaimConvChat *chat = GAIM_CONV_CHAT(conv); |
|
| 1561 gboolean is_me = FALSE; |
|
| 1562 GtkWidget *button; |
|
| 1563 |
|
| 1564 /* |
|
| 1565 * If a menu already exists, destroy it before creating a new one, |
|
| 1566 * thus freeing-up the memory it occupied. |
|
| 1567 */ |
|
| 1568 if (menu) |
|
| 1569 gtk_widget_destroy(menu); |
|
| 1570 |
|
| 1571 if (!strcmp(chat->nick, gaim_normalize(conv->account, who))) |
|
| 1572 is_me = TRUE; |
|
| 1573 |
|
| 1574 menu = gtk_menu_new(); |
|
| 1575 |
|
| 1576 if (!is_me) { |
|
| 1577 button = gaim_new_item_from_stock(menu, _("IM"), GAIM_STOCK_IM, |
|
| 1578 G_CALLBACK(menu_chat_im_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1579 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1580 |
|
| 1581 if (prpl_info && prpl_info->send_file |
|
| 1582 && (!prpl_info->can_receive_file || prpl_info->can_receive_file(gc, who))) { |
|
| 1583 button = gaim_new_item_from_stock(menu, _("Send File"), |
|
| 1584 GAIM_STOCK_FILE_TRANSFER, G_CALLBACK(menu_chat_send_file_cb), |
|
| 1585 GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1586 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1587 } |
|
| 1588 |
|
| 1589 if (gaim_conv_chat_is_user_ignored(GAIM_CONV_CHAT(conv), who)) |
|
| 1590 button = gaim_new_item_from_stock(menu, _("Un-Ignore"), GAIM_STOCK_IGNORE, |
|
| 1591 G_CALLBACK(ignore_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1592 else |
|
| 1593 button = gaim_new_item_from_stock(menu, _("Ignore"), GAIM_STOCK_IGNORE, |
|
| 1594 G_CALLBACK(ignore_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1595 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1596 } |
|
| 1597 |
|
| 1598 if (prpl_info->get_info || prpl_info->get_cb_info) { |
|
| 1599 button = gaim_new_item_from_stock(menu, _("Info"), GAIM_STOCK_INFO, |
|
| 1600 G_CALLBACK(menu_chat_info_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1601 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1602 } |
|
| 1603 |
|
| 1604 if (prpl_info->get_cb_away) { |
|
| 1605 button = gaim_new_item_from_stock(menu, _("Get Away Message"), GAIM_STOCK_AWAY, |
|
| 1606 G_CALLBACK(menu_chat_get_away_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1607 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1608 } |
|
| 1609 |
|
| 1610 if (!is_me && !(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME)) { |
|
| 1611 if (gaim_find_buddy(gc->account, who)) |
|
| 1612 button = gaim_new_item_from_stock(menu, _("Remove"), GTK_STOCK_REMOVE, |
|
| 1613 G_CALLBACK(menu_chat_add_remove_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1614 else |
|
| 1615 button = gaim_new_item_from_stock(menu, _("Add"), GTK_STOCK_ADD, |
|
| 1616 G_CALLBACK(menu_chat_add_remove_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1617 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1618 } |
|
| 1619 |
|
| 1620 button = gaim_new_item_from_stock(menu, _("Last said"), GTK_STOCK_INDEX, |
|
| 1621 G_CALLBACK(menu_last_said_cb), GAIM_GTK_CONVERSATION(conv), 0, 0, NULL); |
|
| 1622 g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free); |
|
| 1623 if (!get_mark_for_user(GAIM_GTK_CONVERSATION(conv), who)) |
|
| 1624 gtk_widget_set_sensitive(button, FALSE); |
|
| 1625 |
|
| 1626 return menu; |
|
| 1627 } |
|
| 1628 |
|
| 1629 |
|
| 1630 static gint |
|
| 1631 gtkconv_chat_popup_menu_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 1632 { |
|
| 1633 GaimConversation *conv = gtkconv->active_conv; |
|
| 1634 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 1635 GaimGtkChatPane *gtkchat; |
|
| 1636 GaimConnection *gc; |
|
| 1637 GaimAccount *account; |
|
| 1638 GtkTreeSelection *sel; |
|
| 1639 GtkTreeIter iter; |
|
| 1640 GtkTreeModel *model; |
|
| 1641 GtkWidget *menu; |
|
| 1642 gchar *who; |
|
| 1643 |
|
| 1644 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 1645 gtkchat = gtkconv->u.chat; |
|
| 1646 account = gaim_conversation_get_account(conv); |
|
| 1647 gc = account->gc; |
|
| 1648 |
|
| 1649 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1650 |
|
| 1651 if (gc != NULL) |
|
| 1652 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 1653 |
|
| 1654 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1655 if(!gtk_tree_selection_get_selected(sel, NULL, &iter)) |
|
| 1656 return FALSE; |
|
| 1657 |
|
| 1658 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); |
|
| 1659 menu = create_chat_menu (conv, who, prpl_info, gc); |
|
| 1660 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, |
|
| 1661 gaim_gtk_treeview_popup_menu_position_func, widget, |
|
| 1662 0, GDK_CURRENT_TIME); |
|
| 1663 g_free(who); |
|
| 1664 |
|
| 1665 return TRUE; |
|
| 1666 } |
|
| 1667 |
|
| 1668 |
|
| 1669 static gint |
|
| 1670 right_click_chat_cb(GtkWidget *widget, GdkEventButton *event, |
|
| 1671 GaimGtkConversation *gtkconv) |
|
| 1672 { |
|
| 1673 GaimConversation *conv = gtkconv->active_conv; |
|
| 1674 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 1675 GaimGtkChatPane *gtkchat; |
|
| 1676 GaimConnection *gc; |
|
| 1677 GaimAccount *account; |
|
| 1678 GtkTreePath *path; |
|
| 1679 GtkTreeIter iter; |
|
| 1680 GtkTreeModel *model; |
|
| 1681 GtkTreeViewColumn *column; |
|
| 1682 gchar *who; |
|
| 1683 int x, y; |
|
| 1684 |
|
| 1685 gtkchat = gtkconv->u.chat; |
|
| 1686 account = gaim_conversation_get_account(conv); |
|
| 1687 gc = account->gc; |
|
| 1688 |
|
| 1689 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 1690 |
|
| 1691 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(gtkchat->list), |
|
| 1692 event->x, event->y, &path, &column, &x, &y); |
|
| 1693 |
|
| 1694 if (path == NULL) |
|
| 1695 return FALSE; |
|
| 1696 |
|
| 1697 if (gc != NULL) |
|
| 1698 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 1699 |
|
| 1700 gtk_tree_selection_select_path(GTK_TREE_SELECTION( |
|
| 1701 gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkchat->list))), path); |
|
| 1702 |
|
| 1703 gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path); |
|
| 1704 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &who, -1); |
|
| 1705 |
|
| 1706 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { |
|
| 1707 chat_do_im(gtkconv, who); |
|
| 1708 } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) { |
|
| 1709 /* Move to user's anchor */ |
|
| 1710 GtkTextMark *mark = get_mark_for_user(gtkconv, who); |
|
| 1711 |
|
| 1712 if(mark != NULL) |
|
| 1713 gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW(gtkconv->imhtml), mark, 0.1, FALSE, 0, 0); |
|
| 1714 } else if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { |
|
| 1715 GtkWidget *menu = create_chat_menu (conv, who, prpl_info, gc); |
|
| 1716 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, |
|
| 1717 event->button, event->time); |
|
| 1718 } |
|
| 1719 |
|
| 1720 g_free(who); |
|
| 1721 gtk_tree_path_free(path); |
|
| 1722 |
|
| 1723 return TRUE; |
|
| 1724 } |
|
| 1725 |
|
| 1726 static void |
|
| 1727 move_to_next_unread_tab(GaimGtkConversation *gtkconv, gboolean forward) |
|
| 1728 { |
|
| 1729 GaimGtkConversation *next_gtkconv = NULL; |
|
| 1730 GaimGtkWindow *win; |
|
| 1731 int index, i, total, found = 0; |
|
| 1732 |
|
| 1733 win = gtkconv->win; |
|
| 1734 index = gtk_notebook_page_num(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont); |
|
| 1735 total = gaim_gtk_conv_window_get_gtkconv_count(win); |
|
| 1736 |
|
| 1737 /* First check the tabs after (forward) or before (!forward) this position. */ |
|
| 1738 for (i = forward ? index + 1 : index - 1; |
|
| 1739 !found && (next_gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(win, i)); |
|
| 1740 forward ? i++ : i--) { |
|
| 1741 if (i == -1) { |
|
| 1742 break; |
|
| 1743 } |
|
| 1744 |
|
| 1745 if (next_gtkconv->unseen_state > 0) |
|
| 1746 found = 1; |
|
| 1747 } |
|
| 1748 |
|
| 1749 if (!found) { |
|
| 1750 /* Now check from the beginning up to (forward) or end back to (!forward) this position. */ |
|
| 1751 for (i = forward ? 0 : total - 1; |
|
| 1752 !found && (forward ? i < index : i >= 0) && |
|
| 1753 (next_gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(win, i)); |
|
| 1754 forward ? i++ : i--) { |
|
| 1755 |
|
| 1756 if (next_gtkconv->unseen_state > 0) |
|
| 1757 found = 1; |
|
| 1758 } |
|
| 1759 |
|
| 1760 if (!found) { |
|
| 1761 /* Okay, just grab the next (forward) or previous (!forward) conversation tab. */ |
|
| 1762 if (forward) { |
|
| 1763 index++; |
|
| 1764 } |
|
| 1765 else { |
|
| 1766 index = (index == 0) ? total - 1 : index - 1; |
|
| 1767 } |
|
| 1768 if (!(next_gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(win, index))) |
|
| 1769 next_gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(win, 0); |
|
| 1770 } |
|
| 1771 } |
|
| 1772 |
|
| 1773 if (next_gtkconv != NULL && next_gtkconv != gtkconv) |
|
| 1774 gaim_gtk_conv_window_switch_gtkconv(win, next_gtkconv); |
|
| 1775 } |
|
| 1776 |
|
| 1777 static gboolean |
|
| 1778 entry_key_press_cb(GtkWidget *entry, GdkEventKey *event, gpointer data) |
|
| 1779 { |
|
| 1780 GaimGtkWindow *win; |
|
| 1781 GaimConversation *conv; |
|
| 1782 GaimGtkConversation *gtkconv; |
|
| 1783 int curconv; |
|
| 1784 |
|
| 1785 gtkconv = (GaimGtkConversation *)data; |
|
| 1786 conv = gtkconv->active_conv; |
|
| 1787 win = gtkconv->win; |
|
| 1788 curconv = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook)); |
|
| 1789 |
|
| 1790 /* If CTRL was held down... */ |
|
| 1791 if (event->state & GDK_CONTROL_MASK) { |
|
| 1792 switch (event->keyval) { |
|
| 1793 case GDK_Up: |
|
| 1794 if (!conv->send_history) |
|
| 1795 break; |
|
| 1796 |
|
| 1797 if (!conv->send_history->prev) { |
|
| 1798 GtkTextIter start, end; |
|
| 1799 |
|
| 1800 if (conv->send_history->data) |
|
| 1801 g_free(conv->send_history->data); |
|
| 1802 |
|
| 1803 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, |
|
| 1804 &start); |
|
| 1805 gtk_text_buffer_get_end_iter(gtkconv->entry_buffer, &end); |
|
| 1806 |
|
| 1807 conv->send_history->data = |
|
| 1808 gtk_imhtml_get_markup(GTK_IMHTML(gtkconv->entry)); |
|
| 1809 } |
|
| 1810 |
|
| 1811 if (conv->send_history->next && conv->send_history->next->data) { |
|
| 1812 GObject *object; |
|
| 1813 GtkTextIter iter; |
|
| 1814 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); |
|
| 1815 |
|
| 1816 conv->send_history = conv->send_history->next; |
|
| 1817 |
|
| 1818 /* Block the signal to prevent application of default formatting. */ |
|
| 1819 object = g_object_ref(G_OBJECT(gtkconv->entry)); |
|
| 1820 g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, |
|
| 1821 NULL, gtkconv); |
|
| 1822 /* Clear the formatting. */ |
|
| 1823 gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); |
|
| 1824 /* Unblock the signal. */ |
|
| 1825 g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, |
|
| 1826 NULL, gtkconv); |
|
| 1827 g_object_unref(object); |
|
| 1828 |
|
| 1829 gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); |
|
| 1830 gtk_imhtml_append_text_with_images( |
|
| 1831 GTK_IMHTML(gtkconv->entry), conv->send_history->data, |
|
| 1832 0, NULL); |
|
| 1833 /* this is mainly just a hack so the formatting at the |
|
| 1834 * cursor gets picked up. */ |
|
| 1835 gtk_text_buffer_get_end_iter(buffer, &iter); |
|
| 1836 gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter); |
|
| 1837 } |
|
| 1838 |
|
| 1839 return TRUE; |
|
| 1840 break; |
|
| 1841 |
|
| 1842 case GDK_Down: |
|
| 1843 if (!conv->send_history) |
|
| 1844 break; |
|
| 1845 |
|
| 1846 if (conv->send_history->prev && conv->send_history->prev->data) { |
|
| 1847 GObject *object; |
|
| 1848 GtkTextIter iter; |
|
| 1849 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); |
|
| 1850 |
|
| 1851 conv->send_history = conv->send_history->prev; |
|
| 1852 |
|
| 1853 /* Block the signal to prevent application of default formatting. */ |
|
| 1854 object = g_object_ref(G_OBJECT(gtkconv->entry)); |
|
| 1855 g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, |
|
| 1856 NULL, gtkconv); |
|
| 1857 /* Clear the formatting. */ |
|
| 1858 gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); |
|
| 1859 /* Unblock the signal. */ |
|
| 1860 g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA, 0, 0, NULL, |
|
| 1861 NULL, gtkconv); |
|
| 1862 g_object_unref(object); |
|
| 1863 |
|
| 1864 gtk_imhtml_clear(GTK_IMHTML(gtkconv->entry)); |
|
| 1865 gtk_imhtml_append_text_with_images( |
|
| 1866 GTK_IMHTML(gtkconv->entry), conv->send_history->data, |
|
| 1867 0, NULL); |
|
| 1868 /* this is mainly just a hack so the formatting at the |
|
| 1869 * cursor gets picked up. */ |
|
| 1870 if (*(char *)conv->send_history->data) { |
|
| 1871 gtk_text_buffer_get_end_iter(buffer, &iter); |
|
| 1872 gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter); |
|
| 1873 } else { |
|
| 1874 /* Restore the default formatting */ |
|
| 1875 default_formatize(gtkconv); |
|
| 1876 } |
|
| 1877 } |
|
| 1878 |
|
| 1879 return TRUE; |
|
| 1880 break; |
|
| 1881 |
|
| 1882 case GDK_Page_Down: |
|
| 1883 case ']': |
|
| 1884 if (!gaim_gtk_conv_window_get_gtkconv_at_index(win, curconv + 1)) |
|
| 1885 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0); |
|
| 1886 else |
|
| 1887 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), curconv + 1); |
|
| 1888 return TRUE; |
|
| 1889 break; |
|
| 1890 |
|
| 1891 case GDK_Page_Up: |
|
| 1892 case '[': |
|
| 1893 if (!gaim_gtk_conv_window_get_gtkconv_at_index(win, curconv - 1)) |
|
| 1894 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), -1); |
|
| 1895 else |
|
| 1896 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), curconv - 1); |
|
| 1897 return TRUE; |
|
| 1898 break; |
|
| 1899 |
|
| 1900 case GDK_Tab: |
|
| 1901 case GDK_ISO_Left_Tab: |
|
| 1902 if (event->state & GDK_SHIFT_MASK) { |
|
| 1903 move_to_next_unread_tab(gtkconv, FALSE); |
|
| 1904 } else { |
|
| 1905 move_to_next_unread_tab(gtkconv, TRUE); |
|
| 1906 } |
|
| 1907 |
|
| 1908 return TRUE; |
|
| 1909 break; |
|
| 1910 |
|
| 1911 case GDK_comma: |
|
| 1912 gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook), |
|
| 1913 gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv), |
|
| 1914 curconv - 1); |
|
| 1915 break; |
|
| 1916 |
|
| 1917 case GDK_period: |
|
| 1918 gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook), |
|
| 1919 gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), curconv), |
|
| 1920 #if GTK_CHECK_VERSION(2,2,0) |
|
| 1921 (curconv + 1) % gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->notebook))); |
|
| 1922 #else |
|
| 1923 (curconv + 1) % g_list_length(GTK_NOTEBOOK(win->notebook)->children)); |
|
| 1924 #endif |
|
| 1925 break; |
|
| 1926 |
|
| 1927 } /* End of switch */ |
|
| 1928 } |
|
| 1929 |
|
| 1930 /* If ALT (or whatever) was held down... */ |
|
| 1931 else if (event->state & GDK_MOD1_MASK) |
|
| 1932 { |
|
| 1933 if (event->keyval > '0' && event->keyval <= '9') |
|
| 1934 { |
|
| 1935 guint switchto = event->keyval - '1'; |
|
| 1936 if (switchto < gaim_gtk_conv_window_get_gtkconv_count(win)) |
|
| 1937 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), switchto); |
|
| 1938 |
|
| 1939 return TRUE; |
|
| 1940 } |
|
| 1941 } |
|
| 1942 |
|
| 1943 /* If neither CTRL nor ALT were held down... */ |
|
| 1944 else |
|
| 1945 { |
|
| 1946 switch (event->keyval) |
|
| 1947 { |
|
| 1948 case GDK_Tab: |
|
| 1949 return tab_complete(conv); |
|
| 1950 break; |
|
| 1951 |
|
| 1952 case GDK_Page_Up: |
|
| 1953 gtk_imhtml_page_up(GTK_IMHTML(gtkconv->imhtml)); |
|
| 1954 return TRUE; |
|
| 1955 break; |
|
| 1956 |
|
| 1957 case GDK_Page_Down: |
|
| 1958 gtk_imhtml_page_down(GTK_IMHTML(gtkconv->imhtml)); |
|
| 1959 return TRUE; |
|
| 1960 break; |
|
| 1961 |
|
| 1962 } |
|
| 1963 } |
|
| 1964 return FALSE; |
|
| 1965 } |
|
| 1966 |
|
| 1967 /* |
|
| 1968 * NOTE: |
|
| 1969 * This guy just kills a single right click from being propagated any |
|
| 1970 * further. I have no idea *why* we need this, but we do ... It |
|
| 1971 * prevents right clicks on the GtkTextView in a convo dialog from |
|
| 1972 * going all the way down to the notebook. I suspect a bug in |
|
| 1973 * GtkTextView, but I'm not ready to point any fingers yet. |
|
| 1974 */ |
|
| 1975 static gboolean |
|
| 1976 entry_stop_rclick_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) |
|
| 1977 { |
|
| 1978 if (event->button == 3 && event->type == GDK_BUTTON_PRESS) { |
|
| 1979 /* Right single click */ |
|
| 1980 g_signal_stop_emission_by_name(G_OBJECT(widget), "button_press_event"); |
|
| 1981 |
|
| 1982 return TRUE; |
|
| 1983 } |
|
| 1984 |
|
| 1985 return FALSE; |
|
| 1986 } |
|
| 1987 |
|
| 1988 /* |
|
| 1989 * If someone tries to type into the conversation backlog of a |
|
| 1990 * conversation window then we yank focus from the conversation backlog |
|
| 1991 * and give it to the text entry box so that people can type |
|
| 1992 * all the live long day and it will get entered into the entry box. |
|
| 1993 */ |
|
| 1994 static gboolean |
|
| 1995 refocus_entry_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) |
|
| 1996 { |
|
| 1997 GaimGtkConversation *gtkconv = data; |
|
| 1998 |
|
| 1999 /* If we have a valid key for the conversation display, then exit */ |
|
| 2000 if ((event->state & GDK_CONTROL_MASK) || |
|
| 2001 (event->keyval == GDK_F10) || |
|
| 2002 (event->keyval == GDK_Shift_L) || |
|
| 2003 (event->keyval == GDK_Shift_R) || |
|
| 2004 (event->keyval == GDK_Escape) || |
|
| 2005 (event->keyval == GDK_Up) || |
|
| 2006 (event->keyval == GDK_Down) || |
|
| 2007 (event->keyval == GDK_Left) || |
|
| 2008 (event->keyval == GDK_Right) || |
|
| 2009 (event->keyval == GDK_Home) || |
|
| 2010 (event->keyval == GDK_End) || |
|
| 2011 (event->keyval == GDK_Tab) || |
|
| 2012 (event->keyval == GDK_ISO_Left_Tab)) |
|
| 2013 return FALSE; |
|
| 2014 |
|
| 2015 if (event->type == GDK_KEY_RELEASE) |
|
| 2016 gtk_widget_grab_focus(gtkconv->entry); |
|
| 2017 |
|
| 2018 gtk_widget_event(gtkconv->entry, (GdkEvent *)event); |
|
| 2019 |
|
| 2020 return TRUE; |
|
| 2021 } |
|
| 2022 |
|
| 2023 static void |
|
| 2024 gaim_gtkconv_set_active_conversation(GaimConversation *conv) |
|
| 2025 { |
|
| 2026 GaimGtkConversation *gtkconv; |
|
| 2027 GaimConversation *old_conv; |
|
| 2028 GtkIMHtml *entry; |
|
| 2029 const char *protocol_name; |
|
| 2030 |
|
| 2031 g_return_if_fail(conv != NULL); |
|
| 2032 |
|
| 2033 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2034 old_conv = gtkconv->active_conv; |
|
| 2035 |
|
| 2036 if (old_conv == conv) |
|
| 2037 return; |
|
| 2038 |
|
| 2039 gaim_conversation_close_logs(old_conv); |
|
| 2040 gtkconv->active_conv = conv; |
|
| 2041 |
|
| 2042 gaim_conversation_set_logging(conv, |
|
| 2043 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging))); |
|
| 2044 |
|
| 2045 entry = GTK_IMHTML(gtkconv->entry); |
|
| 2046 protocol_name = gaim_account_get_protocol_name(conv->account); |
|
| 2047 gtk_imhtml_set_protocol_name(entry, protocol_name); |
|
| 2048 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), protocol_name); |
|
| 2049 |
|
| 2050 if (!(conv->features & GAIM_CONNECTION_HTML)) |
|
| 2051 gtk_imhtml_clear_formatting(GTK_IMHTML(gtkconv->entry)); |
|
| 2052 else if (conv->features & GAIM_CONNECTION_FORMATTING_WBFO && |
|
| 2053 !(old_conv->features & GAIM_CONNECTION_FORMATTING_WBFO)) |
|
| 2054 { |
|
| 2055 /* The old conversation allowed formatting on parts of the |
|
| 2056 * buffer, but the new one only allows it on the whole |
|
| 2057 * buffer. This code saves the formatting from the current |
|
| 2058 * position of the cursor, clears the formatting, then |
|
| 2059 * applies the saved formatting to the entire buffer. */ |
|
| 2060 |
|
| 2061 gboolean bold; |
|
| 2062 gboolean italic; |
|
| 2063 gboolean underline; |
|
| 2064 char *fontface = gtk_imhtml_get_current_fontface(entry); |
|
| 2065 char *forecolor = gtk_imhtml_get_current_forecolor(entry); |
|
| 2066 char *backcolor = gtk_imhtml_get_current_backcolor(entry); |
|
| 2067 char *background = gtk_imhtml_get_current_background(entry); |
|
| 2068 gint fontsize = gtk_imhtml_get_current_fontsize(entry); |
|
| 2069 gboolean bold2; |
|
| 2070 gboolean italic2; |
|
| 2071 gboolean underline2; |
|
| 2072 |
|
| 2073 gtk_imhtml_get_current_format(entry, &bold, &italic, &underline); |
|
| 2074 |
|
| 2075 /* Clear existing formatting */ |
|
| 2076 gtk_imhtml_clear_formatting(entry); |
|
| 2077 |
|
| 2078 /* Apply saved formatting to the whole buffer. */ |
|
| 2079 |
|
| 2080 gtk_imhtml_get_current_format(entry, &bold2, &italic2, &underline2); |
|
| 2081 |
|
| 2082 if (bold != bold2) |
|
| 2083 gtk_imhtml_toggle_bold(entry); |
|
| 2084 |
|
| 2085 if (italic != italic2) |
|
| 2086 gtk_imhtml_toggle_italic(entry); |
|
| 2087 |
|
| 2088 if (underline != underline2) |
|
| 2089 gtk_imhtml_toggle_underline(entry); |
|
| 2090 |
|
| 2091 gtk_imhtml_toggle_fontface(entry, fontface); |
|
| 2092 |
|
| 2093 if (!(conv->features & GAIM_CONNECTION_NO_FONTSIZE)) |
|
| 2094 gtk_imhtml_font_set_size(entry, fontsize); |
|
| 2095 |
|
| 2096 gtk_imhtml_toggle_forecolor(entry, forecolor); |
|
| 2097 |
|
| 2098 if (!(conv->features & GAIM_CONNECTION_NO_BGCOLOR)) |
|
| 2099 { |
|
| 2100 gtk_imhtml_toggle_backcolor(entry, backcolor); |
|
| 2101 gtk_imhtml_toggle_background(entry, background); |
|
| 2102 } |
|
| 2103 |
|
| 2104 g_free(fontface); |
|
| 2105 g_free(forecolor); |
|
| 2106 g_free(backcolor); |
|
| 2107 g_free(background); |
|
| 2108 } |
|
| 2109 else |
|
| 2110 { |
|
| 2111 /* This is done in default_formatize, which is called from clear_formatting_cb, |
|
| 2112 * which is (obviously) a clear_formatting signal handler. However, if we're |
|
| 2113 * here, we didn't call gtk_imhtml_clear_formatting() (because we want to |
|
| 2114 * preserve the formatting exactly as it is), so we have to do this now. */ |
|
| 2115 gtk_imhtml_set_whole_buffer_formatting_only(entry, |
|
| 2116 (conv->features & GAIM_CONNECTION_FORMATTING_WBFO)); |
|
| 2117 } |
|
| 2118 |
|
| 2119 gaim_signal_emit(gaim_gtk_conversations_get_handle(), "conversation-switched", conv); |
|
| 2120 } |
|
| 2121 |
|
| 2122 void |
|
| 2123 gaim_gtkconv_switch_active_conversation(GaimConversation *conv) |
|
| 2124 { |
|
| 2125 GaimGtkConversation *gtkconv; |
|
| 2126 |
|
| 2127 g_return_if_fail(conv != NULL); |
|
| 2128 |
|
| 2129 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2130 |
|
| 2131 gaim_gtkconv_set_active_conversation(conv); |
|
| 2132 |
|
| 2133 gray_stuff_out(gtkconv); |
|
| 2134 update_typing_icon(gtkconv); |
|
| 2135 |
|
| 2136 gtk_window_set_title(GTK_WINDOW(gtkconv->win->window), |
|
| 2137 gtk_label_get_text(GTK_LABEL(gtkconv->tab_label))); |
|
| 2138 } |
|
| 2139 |
|
| 2140 static void |
|
| 2141 menu_conv_sel_send_cb(GObject *m, gpointer data) |
|
| 2142 { |
|
| 2143 GaimAccount *account = g_object_get_data(m, "gaim_account"); |
|
| 2144 gchar *name = g_object_get_data(m, "gaim_buddy_name"); |
|
| 2145 GaimConversation *conv; |
|
| 2146 |
|
| 2147 if (gtk_check_menu_item_get_active((GtkCheckMenuItem*) m) == FALSE) |
|
| 2148 return; |
|
| 2149 |
|
| 2150 conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, name); |
|
| 2151 gaim_gtkconv_switch_active_conversation(conv); |
|
| 2152 } |
|
| 2153 |
|
| 2154 static void |
|
| 2155 insert_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *position, |
|
| 2156 gchar *new_text, gint new_text_length, gpointer user_data) |
|
| 2157 { |
|
| 2158 GaimGtkConversation *gtkconv = (GaimGtkConversation *)user_data; |
|
| 2159 GaimConversation *conv; |
|
| 2160 |
|
| 2161 g_return_if_fail(gtkconv != NULL); |
|
| 2162 |
|
| 2163 conv = gtkconv->active_conv; |
|
| 2164 |
|
| 2165 if (!gaim_prefs_get_bool("/core/conversations/im/send_typing")) |
|
| 2166 return; |
|
| 2167 |
|
| 2168 got_typing_keypress(gtkconv, (gtk_text_iter_is_start(position) && |
|
| 2169 gtk_text_iter_is_end(position))); |
|
| 2170 } |
|
| 2171 |
|
| 2172 static void |
|
| 2173 delete_text_cb(GtkTextBuffer *textbuffer, GtkTextIter *start_pos, |
|
| 2174 GtkTextIter *end_pos, gpointer user_data) |
|
| 2175 { |
|
| 2176 GaimGtkConversation *gtkconv = (GaimGtkConversation *)user_data; |
|
| 2177 GaimConversation *conv; |
|
| 2178 GaimConvIm *im; |
|
| 2179 |
|
| 2180 g_return_if_fail(gtkconv != NULL); |
|
| 2181 |
|
| 2182 conv = gtkconv->active_conv; |
|
| 2183 |
|
| 2184 if (!gaim_prefs_get_bool("/core/conversations/im/send_typing")) |
|
| 2185 return; |
|
| 2186 |
|
| 2187 im = GAIM_CONV_IM(conv); |
|
| 2188 |
|
| 2189 if (gtk_text_iter_is_start(start_pos) && gtk_text_iter_is_end(end_pos)) { |
|
| 2190 |
|
| 2191 /* We deleted all the text, so turn off typing. */ |
|
| 2192 if (gaim_conv_im_get_type_again_timeout(im)) |
|
| 2193 gaim_conv_im_stop_type_again_timeout(im); |
|
| 2194 |
|
| 2195 serv_send_typing(gaim_conversation_get_gc(conv), |
|
| 2196 gaim_conversation_get_name(conv), |
|
| 2197 GAIM_NOT_TYPING); |
|
| 2198 } |
|
| 2199 else { |
|
| 2200 /* We're deleting, but not all of it, so it counts as typing. */ |
|
| 2201 got_typing_keypress(gtkconv, FALSE); |
|
| 2202 } |
|
| 2203 } |
|
| 2204 |
|
| 2205 /************************************************************************** |
|
| 2206 * A bunch of buddy icon functions |
|
| 2207 **************************************************************************/ |
|
| 2208 GdkPixbuf * |
|
| 2209 gaim_gtkconv_get_tab_icon(GaimConversation *conv, gboolean small_icon) |
|
| 2210 { |
|
| 2211 GaimAccount *account = NULL; |
|
| 2212 const char *name = NULL; |
|
| 2213 GdkPixbuf *status = NULL; |
|
| 2214 |
|
| 2215 g_return_val_if_fail(conv != NULL, NULL); |
|
| 2216 |
|
| 2217 account = gaim_conversation_get_account(conv); |
|
| 2218 name = gaim_conversation_get_name(conv); |
|
| 2219 |
|
| 2220 g_return_val_if_fail(account != NULL, NULL); |
|
| 2221 g_return_val_if_fail(name != NULL, NULL); |
|
| 2222 |
|
| 2223 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 2224 GaimBuddy *b = gaim_find_buddy(account, name); |
|
| 2225 if (b != NULL) { |
|
| 2226 status = gaim_gtk_blist_get_status_icon((GaimBlistNode*)b, |
|
| 2227 (small_icon ? GAIM_STATUS_ICON_SMALL : GAIM_STATUS_ICON_LARGE)); |
|
| 2228 } |
|
| 2229 } |
|
| 2230 |
|
| 2231 if (!status) { |
|
| 2232 GdkPixbuf *pixbuf; |
|
| 2233 pixbuf = gaim_gtk_create_prpl_icon(account); |
|
| 2234 |
|
| 2235 if (small_icon && pixbuf != NULL) |
|
| 2236 { |
|
| 2237 status = gdk_pixbuf_scale_simple(pixbuf, 15, 15, |
|
| 2238 GDK_INTERP_BILINEAR); |
|
| 2239 g_object_unref(pixbuf); |
|
| 2240 } |
|
| 2241 else |
|
| 2242 status = pixbuf; |
|
| 2243 } |
|
| 2244 return status; |
|
| 2245 } |
|
| 2246 |
|
| 2247 static void |
|
| 2248 update_tab_icon(GaimConversation *conv) |
|
| 2249 { |
|
| 2250 GaimGtkConversation *gtkconv; |
|
| 2251 GaimGtkWindow *win; |
|
| 2252 GaimAccount *account; |
|
| 2253 const char *name; |
|
| 2254 GdkPixbuf *status = NULL; |
|
| 2255 |
|
| 2256 g_return_if_fail(conv != NULL); |
|
| 2257 |
|
| 2258 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2259 win = gtkconv->win; |
|
| 2260 if (conv != gtkconv->active_conv) |
|
| 2261 return; |
|
| 2262 |
|
| 2263 name = gaim_conversation_get_name(conv); |
|
| 2264 account = gaim_conversation_get_account(conv); |
|
| 2265 |
|
| 2266 status = gaim_gtkconv_get_tab_icon(conv, TRUE); |
|
| 2267 |
|
| 2268 g_return_if_fail(status != NULL); |
|
| 2269 |
|
| 2270 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->icon), status); |
|
| 2271 gtk_image_set_from_pixbuf(GTK_IMAGE(gtkconv->menu_icon), status); |
|
| 2272 |
|
| 2273 if (status != NULL) |
|
| 2274 g_object_unref(status); |
|
| 2275 |
|
| 2276 if (gaim_gtk_conv_window_is_active_conversation(conv) && |
|
| 2277 (gaim_conversation_get_type(conv) != GAIM_CONV_TYPE_IM || |
|
| 2278 gtkconv->u.im->anim == NULL)) |
|
| 2279 { |
|
| 2280 status = gaim_gtkconv_get_tab_icon(conv, FALSE); |
|
| 2281 |
|
| 2282 gtk_window_set_icon(GTK_WINDOW(win->window), status); |
|
| 2283 |
|
| 2284 if (status != NULL) |
|
| 2285 g_object_unref(status); |
|
| 2286 } |
|
| 2287 } |
|
| 2288 |
|
| 2289 static gboolean |
|
| 2290 redraw_icon(gpointer data) |
|
| 2291 { |
|
| 2292 GaimGtkConversation *gtkconv = (GaimGtkConversation *)data; |
|
| 2293 GaimConversation *conv = gtkconv->active_conv; |
|
| 2294 GaimAccount *account; |
|
| 2295 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 2296 |
|
| 2297 GdkPixbuf *buf; |
|
| 2298 GdkPixbuf *scale; |
|
| 2299 GdkPixmap *pm; |
|
| 2300 GdkBitmap *bm; |
|
| 2301 gint delay; |
|
| 2302 int scale_width, scale_height; |
|
| 2303 |
|
| 2304 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2305 account = gaim_conversation_get_account(conv); |
|
| 2306 if(account && account->gc) |
|
| 2307 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(account->gc->prpl); |
|
| 2308 |
|
| 2309 gdk_pixbuf_animation_iter_advance(gtkconv->u.im->iter, NULL); |
|
| 2310 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); |
|
| 2311 |
|
| 2312 gaim_gtk_buddy_icon_get_scale_size(buf, prpl_info ? &prpl_info->icon_spec : |
|
| 2313 NULL, &scale_width, &scale_height); |
|
| 2314 |
|
| 2315 /* this code is ugly, and scares me */ |
|
| 2316 scale = gdk_pixbuf_scale_simple(buf, |
|
| 2317 MAX(gdk_pixbuf_get_width(buf) * scale_width / |
|
| 2318 gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1), |
|
| 2319 MAX(gdk_pixbuf_get_height(buf) * scale_height / |
|
| 2320 gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1), |
|
| 2321 GDK_INTERP_BILINEAR); |
|
| 2322 |
|
| 2323 gdk_pixbuf_render_pixmap_and_mask(scale, &pm, &bm, 100); |
|
| 2324 g_object_unref(G_OBJECT(scale)); |
|
| 2325 gtk_image_set_from_pixmap(GTK_IMAGE(gtkconv->u.im->icon), pm, bm); |
|
| 2326 g_object_unref(G_OBJECT(pm)); |
|
| 2327 gtk_widget_queue_draw(gtkconv->u.im->icon); |
|
| 2328 |
|
| 2329 if (bm) |
|
| 2330 g_object_unref(G_OBJECT(bm)); |
|
| 2331 |
|
| 2332 delay = gdk_pixbuf_animation_iter_get_delay_time(gtkconv->u.im->iter); |
|
| 2333 |
|
| 2334 if (delay < 100) |
|
| 2335 delay = 100; |
|
| 2336 |
|
| 2337 gtkconv->u.im->icon_timer = g_timeout_add(delay, redraw_icon, gtkconv); |
|
| 2338 |
|
| 2339 return FALSE; |
|
| 2340 } |
|
| 2341 |
|
| 2342 static void |
|
| 2343 start_anim(GtkObject *obj, GaimGtkConversation *gtkconv) |
|
| 2344 { |
|
| 2345 int delay; |
|
| 2346 |
|
| 2347 if (gtkconv->u.im->anim == NULL) |
|
| 2348 return; |
|
| 2349 |
|
| 2350 if (gtkconv->u.im->icon_timer != 0) |
|
| 2351 return; |
|
| 2352 |
|
| 2353 if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) |
|
| 2354 return; |
|
| 2355 |
|
| 2356 delay = gdk_pixbuf_animation_iter_get_delay_time(gtkconv->u.im->iter); |
|
| 2357 |
|
| 2358 if (delay < 100) |
|
| 2359 delay = 100; |
|
| 2360 |
|
| 2361 gtkconv->u.im->icon_timer = g_timeout_add(delay, redraw_icon, gtkconv); |
|
| 2362 } |
|
| 2363 |
|
| 2364 static void |
|
| 2365 remove_icon(GaimGtkConversation *gtkconv) |
|
| 2366 { |
|
| 2367 GaimConversation *conv = gtkconv->active_conv; |
|
| 2368 GaimGtkWindow *gtkwin; |
|
| 2369 |
|
| 2370 g_return_if_fail(conv != NULL); |
|
| 2371 |
|
| 2372 if (gtkconv->u.im->icon_container != NULL) |
|
| 2373 gtk_widget_destroy(gtkconv->u.im->icon_container); |
|
| 2374 |
|
| 2375 if (gtkconv->u.im->anim != NULL) |
|
| 2376 g_object_unref(G_OBJECT(gtkconv->u.im->anim)); |
|
| 2377 |
|
| 2378 if (gtkconv->u.im->icon_timer != 0) |
|
| 2379 g_source_remove(gtkconv->u.im->icon_timer); |
|
| 2380 |
|
| 2381 if (gtkconv->u.im->iter != NULL) |
|
| 2382 g_object_unref(G_OBJECT(gtkconv->u.im->iter)); |
|
| 2383 |
|
| 2384 gtkconv->u.im->icon_timer = 0; |
|
| 2385 gtkconv->u.im->icon = NULL; |
|
| 2386 gtkconv->u.im->anim = NULL; |
|
| 2387 gtkconv->u.im->iter = NULL; |
|
| 2388 gtkconv->u.im->icon_container = NULL; |
|
| 2389 gtkconv->u.im->show_icon = FALSE; |
|
| 2390 |
|
| 2391 gtkwin = gtkconv->win; |
|
| 2392 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkwin->menu.show_icon), FALSE); |
|
| 2393 } |
|
| 2394 |
|
| 2395 static void |
|
| 2396 saveicon_writefile_cb(void *user_data, const char *filename) |
|
| 2397 { |
|
| 2398 GaimGtkConversation *gtkconv = (GaimGtkConversation *)user_data; |
|
| 2399 GaimConversation *conv = gtkconv->active_conv; |
|
| 2400 FILE *fp; |
|
| 2401 GaimBuddyIcon *icon; |
|
| 2402 const void *data; |
|
| 2403 size_t len; |
|
| 2404 |
|
| 2405 if ((fp = g_fopen(filename, "wb")) == NULL) { |
|
| 2406 gaim_notify_error(conv, NULL, _("Unable to open file."), NULL); |
|
| 2407 return; |
|
| 2408 } |
|
| 2409 |
|
| 2410 icon = gaim_conv_im_get_icon(GAIM_CONV_IM(conv)); |
|
| 2411 data = gaim_buddy_icon_get_data(icon, &len); |
|
| 2412 |
|
| 2413 if ((len <= 0) || (data == NULL)) { |
|
| 2414 gaim_notify_error(conv, NULL, _("Unable to save icon file to disk."), NULL); |
|
| 2415 return; |
|
| 2416 } |
|
| 2417 |
|
| 2418 fwrite(data, 1, len, fp); |
|
| 2419 fclose(fp); |
|
| 2420 } |
|
| 2421 |
|
| 2422 static void |
|
| 2423 icon_menu_save_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 2424 { |
|
| 2425 GaimConversation *conv = gtkconv->active_conv; |
|
| 2426 const gchar *ext; |
|
| 2427 gchar *buf; |
|
| 2428 |
|
| 2429 g_return_if_fail(conv != NULL); |
|
| 2430 |
|
| 2431 ext = gaim_buddy_icon_get_type(gaim_conv_im_get_icon(GAIM_CONV_IM(conv))); |
|
| 2432 if (ext == NULL) |
|
| 2433 ext = "icon"; |
|
| 2434 |
|
| 2435 buf = g_strdup_printf("%s.%s", gaim_normalize(conv->account, conv->name), ext); |
|
| 2436 |
|
| 2437 gaim_request_file(conv, _("Save Icon"), buf, TRUE, |
|
| 2438 G_CALLBACK(saveicon_writefile_cb), NULL, gtkconv); |
|
| 2439 |
|
| 2440 g_free(buf); |
|
| 2441 } |
|
| 2442 |
|
| 2443 static void |
|
| 2444 stop_anim(GtkObject *obj, GaimGtkConversation *gtkconv) |
|
| 2445 { |
|
| 2446 if (gtkconv->u.im->icon_timer != 0) |
|
| 2447 g_source_remove(gtkconv->u.im->icon_timer); |
|
| 2448 |
|
| 2449 gtkconv->u.im->icon_timer = 0; |
|
| 2450 } |
|
| 2451 |
|
| 2452 |
|
| 2453 static void |
|
| 2454 toggle_icon_animate_cb(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 2455 { |
|
| 2456 gtkconv->u.im->animate = |
|
| 2457 gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)); |
|
| 2458 |
|
| 2459 if (gtkconv->u.im->animate) |
|
| 2460 start_anim(NULL, gtkconv); |
|
| 2461 else |
|
| 2462 stop_anim(NULL, gtkconv); |
|
| 2463 } |
|
| 2464 |
|
| 2465 static gboolean |
|
| 2466 icon_menu(GtkObject *obj, GdkEventButton *e, GaimGtkConversation *gtkconv) |
|
| 2467 { |
|
| 2468 static GtkWidget *menu = NULL; |
|
| 2469 GtkWidget *button; |
|
| 2470 |
|
| 2471 if (e->button != 3 || e->type != GDK_BUTTON_PRESS) |
|
| 2472 return FALSE; |
|
| 2473 |
|
| 2474 /* |
|
| 2475 * If a menu already exists, destroy it before creating a new one, |
|
| 2476 * thus freeing-up the memory it occupied. |
|
| 2477 */ |
|
| 2478 if (menu != NULL) |
|
| 2479 gtk_widget_destroy(menu); |
|
| 2480 |
|
| 2481 menu = gtk_menu_new(); |
|
| 2482 |
|
| 2483 if (gtkconv->u.im->anim && |
|
| 2484 !(gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim))) |
|
| 2485 { |
|
| 2486 gaim_new_check_item(menu, _("Animate"), |
|
| 2487 G_CALLBACK(toggle_icon_animate_cb), gtkconv, |
|
| 2488 gtkconv->u.im->icon_timer); |
|
| 2489 } |
|
| 2490 |
|
| 2491 button = gtk_menu_item_new_with_label(_("Hide Icon")); |
|
| 2492 g_signal_connect_swapped(G_OBJECT(button), "activate", |
|
| 2493 G_CALLBACK(remove_icon), gtkconv); |
|
| 2494 gtk_menu_shell_append(GTK_MENU_SHELL(menu), button); |
|
| 2495 gtk_widget_show(button); |
|
| 2496 |
|
| 2497 gaim_new_item_from_stock(menu, _("Save Icon As..."), GTK_STOCK_SAVE_AS, |
|
| 2498 G_CALLBACK(icon_menu_save_cb), gtkconv, |
|
| 2499 0, 0, NULL); |
|
| 2500 |
|
| 2501 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, e->button, e->time); |
|
| 2502 |
|
| 2503 return TRUE; |
|
| 2504 } |
|
| 2505 |
|
| 2506 static void |
|
| 2507 menu_buddyicon_cb(gpointer data, guint action, GtkWidget *widget) |
|
| 2508 { |
|
| 2509 GaimGtkWindow *win = data; |
|
| 2510 GaimConversation *conv; |
|
| 2511 GaimGtkConversation *gtkconv; |
|
| 2512 gboolean active; |
|
| 2513 |
|
| 2514 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 2515 |
|
| 2516 if (!conv) |
|
| 2517 return; |
|
| 2518 |
|
| 2519 g_return_if_fail(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM); |
|
| 2520 |
|
| 2521 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2522 |
|
| 2523 active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); |
|
| 2524 gtkconv->u.im->show_icon = active; |
|
| 2525 if (active) |
|
| 2526 gaim_gtkconv_update_buddy_icon(conv); |
|
| 2527 else |
|
| 2528 remove_icon(gtkconv); |
|
| 2529 } |
|
| 2530 |
|
| 2531 /************************************************************************** |
|
| 2532 * End of the bunch of buddy icon functions |
|
| 2533 **************************************************************************/ |
|
| 2534 void |
|
| 2535 gaim_gtkconv_present_conversation(GaimConversation *conv) |
|
| 2536 { |
|
| 2537 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2538 |
|
| 2539 if(gtkconv->win==hidden_convwin) { |
|
| 2540 gaim_gtk_conv_window_remove_gtkconv(hidden_convwin, gtkconv); |
|
| 2541 gaim_gtkconv_placement_place(gtkconv); |
|
| 2542 } |
|
| 2543 |
|
| 2544 gaim_gtkconv_set_active_conversation(conv); |
|
| 2545 gaim_gtk_conv_window_switch_gtkconv(gtkconv->win, gtkconv); |
|
| 2546 gaim_gtk_conv_window_raise(gtkconv->win); |
|
| 2547 gtk_window_present(GTK_WINDOW(gtkconv->win->window)); |
|
| 2548 } |
|
| 2549 |
|
| 2550 GList * |
|
| 2551 gaim_gtk_conversations_find_unseen_list(GaimConversationType type, |
|
| 2552 GaimUnseenState min_state, |
|
| 2553 gboolean hidden_only, |
|
| 2554 guint max_count) |
|
| 2555 { |
|
| 2556 GList *l; |
|
| 2557 GList *r = NULL; |
|
| 2558 guint c = 0; |
|
| 2559 |
|
| 2560 if (type == GAIM_CONV_TYPE_IM) { |
|
| 2561 l = gaim_get_ims(); |
|
| 2562 } else if (type == GAIM_CONV_TYPE_CHAT) { |
|
| 2563 l = gaim_get_chats(); |
|
| 2564 } else { |
|
| 2565 l = gaim_get_conversations(); |
|
| 2566 } |
|
| 2567 |
|
| 2568 for (; l != NULL && (max_count == 0 || c < max_count); l = l->next) { |
|
| 2569 GaimConversation *conv = (GaimConversation*)l->data; |
|
| 2570 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2571 |
|
| 2572 if(gtkconv->active_conv != conv) |
|
| 2573 continue; |
|
| 2574 |
|
| 2575 if (gtkconv->unseen_state >= min_state |
|
| 2576 && (!hidden_only || |
|
| 2577 (hidden_only && gtkconv->win == hidden_convwin))) { |
|
| 2578 |
|
| 2579 r = g_list_prepend(r, conv); |
|
| 2580 c++; |
|
| 2581 } |
|
| 2582 } |
|
| 2583 |
|
| 2584 return r; |
|
| 2585 } |
|
| 2586 |
|
| 2587 static void |
|
| 2588 unseen_conv_menu_cb(GtkMenuItem *item, GaimConversation *conv) |
|
| 2589 { |
|
| 2590 g_return_if_fail(conv != NULL); |
|
| 2591 gaim_gtkconv_present_conversation(conv); |
|
| 2592 } |
|
| 2593 |
|
| 2594 guint |
|
| 2595 gaim_gtk_conversations_fill_menu(GtkWidget *menu, GList *convs) |
|
| 2596 { |
|
| 2597 GList *l; |
|
| 2598 guint ret=0; |
|
| 2599 |
|
| 2600 g_return_val_if_fail(menu != NULL, 0); |
|
| 2601 g_return_val_if_fail(convs != NULL, 0); |
|
| 2602 |
|
| 2603 for (l = convs; l != NULL ; l = l->next) { |
|
| 2604 GaimConversation *conv = (GaimConversation*)l->data; |
|
| 2605 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 2606 |
|
| 2607 GtkWidget *icon = gtk_image_new(); |
|
| 2608 GdkPixbuf *pbuf = gaim_gtkconv_get_tab_icon(conv, TRUE); |
|
| 2609 GtkWidget *item; |
|
| 2610 gchar *text = g_strdup_printf("%s (%d)", |
|
| 2611 gtk_label_get_text(GTK_LABEL(gtkconv->tab_label)), |
|
| 2612 gtkconv->unseen_count); |
|
| 2613 |
|
| 2614 gtk_image_set_from_pixbuf(GTK_IMAGE(icon), pbuf); |
|
| 2615 g_object_unref(pbuf); |
|
| 2616 |
|
| 2617 item = gtk_image_menu_item_new_with_label(text); |
|
| 2618 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), icon); |
|
| 2619 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(unseen_conv_menu_cb), conv); |
|
| 2620 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 2621 g_free(text); |
|
| 2622 ret++; |
|
| 2623 } |
|
| 2624 |
|
| 2625 return ret; |
|
| 2626 } |
|
| 2627 |
|
| 2628 GaimGtkWindow * |
|
| 2629 gaim_gtkconv_get_window(GaimGtkConversation *gtkconv) |
|
| 2630 { |
|
| 2631 g_return_val_if_fail(gtkconv != NULL, NULL); |
|
| 2632 return gtkconv->win; |
|
| 2633 } |
|
| 2634 |
|
| 2635 static GtkItemFactoryEntry menu_items[] = |
|
| 2636 { |
|
| 2637 /* Conversation menu */ |
|
| 2638 { N_("/_Conversation"), NULL, NULL, 0, "<Branch>", NULL }, |
|
| 2639 |
|
| 2640 { N_("/Conversation/New Instant _Message..."), "<CTL>M", menu_new_conv_cb, |
|
| 2641 0, "<StockItem>", GAIM_STOCK_IM }, |
|
| 2642 |
|
| 2643 { "/Conversation/sep0", NULL, NULL, 0, "<Separator>", NULL }, |
|
| 2644 |
|
| 2645 { N_("/Conversation/_Find..."), NULL, menu_find_cb, 0, |
|
| 2646 "<StockItem>", GTK_STOCK_FIND }, |
|
| 2647 { N_("/Conversation/View _Log"), NULL, menu_view_log_cb, 0, "<StockItem>", GAIM_STOCK_LOG }, |
|
| 2648 { N_("/Conversation/_Save As..."), NULL, menu_save_as_cb, 0, |
|
| 2649 "<StockItem>", GTK_STOCK_SAVE_AS }, |
|
| 2650 { N_("/Conversation/Clea_r Scrollback"), "<CTL>L", menu_clear_cb, 0, "<StockItem>", GTK_STOCK_CLEAR }, |
|
| 2651 |
|
| 2652 { "/Conversation/sep1", NULL, NULL, 0, "<Separator>", NULL }, |
|
| 2653 |
|
| 2654 { N_("/Conversation/Se_nd File..."), NULL, menu_send_file_cb, 0, "<StockItem>", GAIM_STOCK_FILE_TRANSFER }, |
|
| 2655 { N_("/Conversation/Add Buddy _Pounce..."), NULL, menu_add_pounce_cb, |
|
| 2656 0, "<StockItem>", GAIM_STOCK_POUNCE }, |
|
| 2657 { N_("/Conversation/_Get Info"), "<CTL>O", menu_get_info_cb, 0, |
|
| 2658 "<StockItem>", GAIM_STOCK_INFO }, |
|
| 2659 { N_("/Conversation/In_vite..."), NULL, menu_invite_cb, 0, |
|
| 2660 "<StockItem>", GAIM_STOCK_INVITE }, |
|
| 2661 |
|
| 2662 { "/Conversation/sep2", NULL, NULL, 0, "<Separator>", NULL }, |
|
| 2663 |
|
| 2664 { N_("/Conversation/Al_ias..."), NULL, menu_alias_cb, 0, |
|
| 2665 "<StockItem>", GAIM_STOCK_EDIT }, |
|
| 2666 { N_("/Conversation/_Block..."), NULL, menu_block_cb, 0, |
|
| 2667 "<StockItem>", GAIM_STOCK_BLOCK }, |
|
| 2668 { N_("/Conversation/_Add..."), NULL, menu_add_remove_cb, 0, |
|
| 2669 "<StockItem>", GTK_STOCK_ADD }, |
|
| 2670 { N_("/Conversation/_Remove..."), NULL, menu_add_remove_cb, 0, |
|
| 2671 "<StockItem>", GTK_STOCK_REMOVE }, |
|
| 2672 |
|
| 2673 { "/Conversation/sep3", NULL, NULL, 0, "<Separator>", NULL }, |
|
| 2674 |
|
| 2675 { N_("/Conversation/Insert Lin_k..."), NULL, menu_insert_link_cb, 0, |
|
| 2676 "<StockItem>", GAIM_STOCK_LINK }, |
|
| 2677 { N_("/Conversation/Insert Imag_e..."), NULL, menu_insert_image_cb, 0, |
|
| 2678 "<StockItem>", GAIM_STOCK_IMAGE }, |
|
| 2679 |
|
| 2680 { "/Conversation/sep4", NULL, NULL, 0, "<Separator>", NULL }, |
|
| 2681 |
|
| 2682 { N_("/Conversation/_Close"), NULL, menu_close_conv_cb, 0, |
|
| 2683 "<StockItem>", GTK_STOCK_CLOSE }, |
|
| 2684 |
|
| 2685 /* Options */ |
|
| 2686 { N_("/_Options"), NULL, NULL, 0, "<Branch>", NULL }, |
|
| 2687 { N_("/Options/Enable _Logging"), NULL, menu_logging_cb, 0, "<CheckItem>", NULL }, |
|
| 2688 { N_("/Options/Enable _Sounds"), NULL, menu_sounds_cb, 0, "<CheckItem>", NULL }, |
|
| 2689 { N_("/Options/Show Buddy _Icon"), NULL, menu_buddyicon_cb, 0, "<CheckItem>", NULL }, |
|
| 2690 { "/Options/sep0", NULL, NULL, 0, "<Separator>", NULL }, |
|
| 2691 { N_("/Options/Show Formatting _Toolbars"), NULL, menu_toolbar_cb, 0, "<CheckItem>", NULL }, |
|
| 2692 { N_("/Options/Show Ti_mestamps"), "F2", menu_timestamps_cb, 0, "<CheckItem>", NULL }, |
|
| 2693 }; |
|
| 2694 |
|
| 2695 static const int menu_item_count = |
|
| 2696 sizeof(menu_items) / sizeof(*menu_items); |
|
| 2697 |
|
| 2698 static char * |
|
| 2699 item_factory_translate_func (const char *path, gpointer func_data) |
|
| 2700 { |
|
| 2701 return _((char *)path); |
|
| 2702 } |
|
| 2703 |
|
| 2704 static void |
|
| 2705 sound_method_pref_changed_cb(const char *name, GaimPrefType type, |
|
| 2706 gconstpointer value, gpointer data) |
|
| 2707 { |
|
| 2708 GaimGtkWindow *win = data; |
|
| 2709 const char *method = value; |
|
| 2710 |
|
| 2711 if (!strcmp(method, "none")) |
|
| 2712 { |
|
| 2713 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds), |
|
| 2714 FALSE); |
|
| 2715 gtk_widget_set_sensitive(win->menu.sounds, FALSE); |
|
| 2716 } |
|
| 2717 else |
|
| 2718 { |
|
| 2719 GaimGtkConversation *gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 2720 |
|
| 2721 if (gtkconv != NULL) |
|
| 2722 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds), |
|
| 2723 TRUE); |
|
| 2724 gtk_widget_set_sensitive(win->menu.sounds, TRUE); |
|
| 2725 |
|
| 2726 } |
|
| 2727 } |
|
| 2728 |
|
| 2729 static void |
|
| 2730 show_buddy_icons_pref_changed_cb(const char *name, GaimPrefType type, |
|
| 2731 gconstpointer value, gpointer data) |
|
| 2732 { |
|
| 2733 GaimGtkWindow *win = data; |
|
| 2734 gboolean show_icons = GPOINTER_TO_INT(value); |
|
| 2735 |
|
| 2736 if (!show_icons) |
|
| 2737 { |
|
| 2738 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_icon), |
|
| 2739 FALSE); |
|
| 2740 gtk_widget_set_sensitive(win->menu.show_icon, FALSE); |
|
| 2741 } |
|
| 2742 else |
|
| 2743 { |
|
| 2744 GaimGtkConversation *gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 2745 |
|
| 2746 if (gtkconv != NULL) |
|
| 2747 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_icon), |
|
| 2748 TRUE); |
|
| 2749 gtk_widget_set_sensitive(win->menu.show_icon, TRUE); |
|
| 2750 |
|
| 2751 } |
|
| 2752 } |
|
| 2753 |
|
| 2754 |
|
| 2755 static GtkWidget * |
|
| 2756 setup_menubar(GaimGtkWindow *win) |
|
| 2757 { |
|
| 2758 GtkAccelGroup *accel_group; |
|
| 2759 const char *method; |
|
| 2760 |
|
| 2761 accel_group = gtk_accel_group_new (); |
|
| 2762 gtk_window_add_accel_group(GTK_WINDOW(win->window), accel_group); |
|
| 2763 g_object_unref(accel_group); |
|
| 2764 |
|
| 2765 win->menu.item_factory = |
|
| 2766 gtk_item_factory_new(GTK_TYPE_MENU_BAR, "<main>", accel_group); |
|
| 2767 |
|
| 2768 gtk_item_factory_set_translate_func(win->menu.item_factory, |
|
| 2769 item_factory_translate_func, |
|
| 2770 NULL, NULL); |
|
| 2771 |
|
| 2772 gtk_item_factory_create_items(win->menu.item_factory, menu_item_count, |
|
| 2773 menu_items, win); |
|
| 2774 g_signal_connect(G_OBJECT(accel_group), "accel-changed", |
|
| 2775 G_CALLBACK(gaim_gtk_save_accels_cb), NULL); |
|
| 2776 |
|
| 2777 |
|
| 2778 win->menu.menubar = |
|
| 2779 gtk_item_factory_get_widget(win->menu.item_factory, "<main>"); |
|
| 2780 |
|
| 2781 |
|
| 2782 win->menu.view_log = |
|
| 2783 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2784 N_("/Conversation/View Log")); |
|
| 2785 |
|
| 2786 /* --- */ |
|
| 2787 |
|
| 2788 win->menu.send_file = |
|
| 2789 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2790 N_("/Conversation/Send File...")); |
|
| 2791 |
|
| 2792 win->menu.add_pounce = |
|
| 2793 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2794 N_("/Conversation/Add Buddy Pounce...")); |
|
| 2795 |
|
| 2796 /* --- */ |
|
| 2797 |
|
| 2798 win->menu.get_info = |
|
| 2799 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2800 N_("/Conversation/Get Info")); |
|
| 2801 |
|
| 2802 win->menu.invite = |
|
| 2803 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2804 N_("/Conversation/Invite...")); |
|
| 2805 |
|
| 2806 /* --- */ |
|
| 2807 |
|
| 2808 win->menu.alias = |
|
| 2809 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2810 N_("/Conversation/Alias...")); |
|
| 2811 |
|
| 2812 win->menu.block = |
|
| 2813 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2814 N_("/Conversation/Block...")); |
|
| 2815 |
|
| 2816 win->menu.add = |
|
| 2817 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2818 N_("/Conversation/Add...")); |
|
| 2819 |
|
| 2820 win->menu.remove = |
|
| 2821 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2822 N_("/Conversation/Remove...")); |
|
| 2823 |
|
| 2824 /* --- */ |
|
| 2825 |
|
| 2826 win->menu.insert_link = |
|
| 2827 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2828 N_("/Conversation/Insert Link...")); |
|
| 2829 |
|
| 2830 win->menu.insert_image = |
|
| 2831 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2832 N_("/Conversation/Insert Image...")); |
|
| 2833 |
|
| 2834 /* --- */ |
|
| 2835 |
|
| 2836 win->menu.logging = |
|
| 2837 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2838 N_("/Options/Enable Logging")); |
|
| 2839 win->menu.sounds = |
|
| 2840 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2841 N_("/Options/Enable Sounds")); |
|
| 2842 method = gaim_prefs_get_string("/gaim/gtk/sound/method"); |
|
| 2843 if (!strcmp(method, "none")) |
|
| 2844 { |
|
| 2845 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds), |
|
| 2846 FALSE); |
|
| 2847 gtk_widget_set_sensitive(win->menu.sounds, FALSE); |
|
| 2848 } |
|
| 2849 gaim_prefs_connect_callback(win, "/gaim/gtk/sound/method", |
|
| 2850 sound_method_pref_changed_cb, win); |
|
| 2851 |
|
| 2852 win->menu.show_formatting_toolbar = |
|
| 2853 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2854 N_("/Options/Show Formatting Toolbars")); |
|
| 2855 win->menu.show_timestamps = |
|
| 2856 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2857 N_("/Options/Show Timestamps")); |
|
| 2858 win->menu.show_icon = |
|
| 2859 gtk_item_factory_get_widget(win->menu.item_factory, |
|
| 2860 N_("/Options/Show Buddy Icon")); |
|
| 2861 if (!gaim_prefs_get_bool("/gaim/gtk/conversations/im/show_buddy_icons")) |
|
| 2862 { |
|
| 2863 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_icon), |
|
| 2864 FALSE); |
|
| 2865 gtk_widget_set_sensitive(win->menu.show_icon, FALSE); |
|
| 2866 } |
|
| 2867 gaim_prefs_connect_callback(win, "/gaim/gtk/conversations/im/show_buddy_icons", |
|
| 2868 show_buddy_icons_pref_changed_cb, win); |
|
| 2869 |
|
| 2870 win->menu.tray = gaim_gtk_menu_tray_new(); |
|
| 2871 gtk_menu_shell_append(GTK_MENU_SHELL(win->menu.menubar), |
|
| 2872 win->menu.tray); |
|
| 2873 gtk_widget_show(win->menu.tray); |
|
| 2874 |
|
| 2875 gtk_widget_show(win->menu.menubar); |
|
| 2876 |
|
| 2877 return win->menu.menubar; |
|
| 2878 } |
|
| 2879 |
|
| 2880 |
|
| 2881 /************************************************************************** |
|
| 2882 * Utility functions |
|
| 2883 **************************************************************************/ |
|
| 2884 |
|
| 2885 static void |
|
| 2886 got_typing_keypress(GaimGtkConversation *gtkconv, gboolean first) |
|
| 2887 { |
|
| 2888 GaimConversation *conv = gtkconv->active_conv; |
|
| 2889 GaimConvIm *im; |
|
| 2890 |
|
| 2891 /* |
|
| 2892 * We know we got something, so we at least have to make sure we don't |
|
| 2893 * send GAIM_TYPED any time soon. |
|
| 2894 */ |
|
| 2895 |
|
| 2896 im = GAIM_CONV_IM(conv); |
|
| 2897 |
|
| 2898 if (gaim_conv_im_get_type_again_timeout(im)) |
|
| 2899 gaim_conv_im_stop_type_again_timeout(im); |
|
| 2900 |
|
| 2901 gaim_conv_im_start_type_again_timeout(im); |
|
| 2902 |
|
| 2903 if (first || (gaim_conv_im_get_type_again(im) != 0 && |
|
| 2904 time(NULL) > gaim_conv_im_get_type_again(im))) { |
|
| 2905 |
|
| 2906 int timeout = serv_send_typing(gaim_conversation_get_gc(conv), |
|
| 2907 (char *)gaim_conversation_get_name(conv), |
|
| 2908 GAIM_TYPING); |
|
| 2909 |
|
| 2910 if (timeout) |
|
| 2911 gaim_conv_im_set_type_again(im, time(NULL) + timeout); |
|
| 2912 else |
|
| 2913 gaim_conv_im_set_type_again(im, 0); |
|
| 2914 } |
|
| 2915 } |
|
| 2916 |
|
| 2917 static void |
|
| 2918 update_typing_icon(GaimGtkConversation *gtkconv) |
|
| 2919 { |
|
| 2920 GaimGtkWindow *gtkwin; |
|
| 2921 GaimConvIm *im = NULL; |
|
| 2922 GaimConversation *conv = gtkconv->active_conv; |
|
| 2923 char *stock_id, *tooltip; |
|
| 2924 |
|
| 2925 gtkwin = gtkconv->win; |
|
| 2926 |
|
| 2927 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 2928 im = GAIM_CONV_IM(conv); |
|
| 2929 |
|
| 2930 if (gtkwin->menu.typing_icon) { |
|
| 2931 gtk_widget_hide(gtkwin->menu.typing_icon); |
|
| 2932 } |
|
| 2933 |
|
| 2934 if (!im || (gaim_conv_im_get_typing_state(im) == GAIM_NOT_TYPING)) |
|
| 2935 return; |
|
| 2936 |
|
| 2937 if (gaim_conv_im_get_typing_state(im) == GAIM_TYPING) { |
|
| 2938 stock_id = GAIM_STOCK_TYPING; |
|
| 2939 tooltip = _("User is typing..."); |
|
| 2940 } else { |
|
| 2941 stock_id = GAIM_STOCK_TYPED; |
|
| 2942 tooltip = _("User has typed something and stopped"); |
|
| 2943 } |
|
| 2944 |
|
| 2945 if (gtkwin->menu.typing_icon == NULL) |
|
| 2946 { |
|
| 2947 gtkwin->menu.typing_icon = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU); |
|
| 2948 gaim_gtk_menu_tray_append(GAIM_GTK_MENU_TRAY(gtkwin->menu.tray), |
|
| 2949 gtkwin->menu.typing_icon, |
|
| 2950 tooltip); |
|
| 2951 } |
|
| 2952 else |
|
| 2953 { |
|
| 2954 gtk_image_set_from_stock(GTK_IMAGE(gtkwin->menu.typing_icon), stock_id, GTK_ICON_SIZE_MENU); |
|
| 2955 gaim_gtk_menu_tray_set_tooltip(GAIM_GTK_MENU_TRAY(gtkwin->menu.tray), |
|
| 2956 gtkwin->menu.typing_icon, |
|
| 2957 tooltip); |
|
| 2958 } |
|
| 2959 |
|
| 2960 gtk_widget_show(gtkwin->menu.typing_icon); |
|
| 2961 } |
|
| 2962 |
|
| 2963 static gboolean |
|
| 2964 update_send_to_selection(GaimGtkWindow *win) |
|
| 2965 { |
|
| 2966 GaimAccount *account; |
|
| 2967 GaimConversation *conv; |
|
| 2968 GtkWidget *menu; |
|
| 2969 GList *child; |
|
| 2970 GaimBuddy *b; |
|
| 2971 |
|
| 2972 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 2973 |
|
| 2974 if (conv == NULL) |
|
| 2975 return FALSE; |
|
| 2976 |
|
| 2977 account = gaim_conversation_get_account(conv); |
|
| 2978 |
|
| 2979 if (win->menu.send_to == NULL) |
|
| 2980 return FALSE; |
|
| 2981 |
|
| 2982 if (!(b = gaim_find_buddy(account, conv->name))) |
|
| 2983 return FALSE; |
|
| 2984 |
|
| 2985 |
|
| 2986 gtk_widget_show(win->menu.send_to); |
|
| 2987 |
|
| 2988 menu = gtk_menu_item_get_submenu( |
|
| 2989 GTK_MENU_ITEM(win->menu.send_to)); |
|
| 2990 |
|
| 2991 for (child = gtk_container_get_children(GTK_CONTAINER(menu)); |
|
| 2992 child != NULL; |
|
| 2993 child = child->next) { |
|
| 2994 |
|
| 2995 GtkWidget *item = child->data; |
|
| 2996 GaimBuddy *item_buddy; |
|
| 2997 GaimAccount *item_account = g_object_get_data(G_OBJECT(item), "gaim_account"); |
|
| 2998 gchar *buddy_name = g_object_get_data(G_OBJECT(item), |
|
| 2999 "gaim_buddy_name"); |
|
| 3000 item_buddy = gaim_find_buddy(item_account, buddy_name); |
|
| 3001 |
|
| 3002 if (b == item_buddy) { |
|
| 3003 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); |
|
| 3004 break; |
|
| 3005 } |
|
| 3006 } |
|
| 3007 |
|
| 3008 return FALSE; |
|
| 3009 } |
|
| 3010 |
|
| 3011 static gboolean |
|
| 3012 send_to_item_enter_notify_cb(GtkWidget *menuitem, GdkEventCrossing *event, GtkWidget *label) |
|
| 3013 { |
|
| 3014 gtk_widget_set_sensitive(GTK_WIDGET(label), TRUE); |
|
| 3015 return FALSE; |
|
| 3016 } |
|
| 3017 |
|
| 3018 static gboolean |
|
| 3019 send_to_item_leave_notify_cb(GtkWidget *menuitem, GdkEventCrossing *event, GtkWidget *label) |
|
| 3020 { |
|
| 3021 gtk_widget_set_sensitive(GTK_WIDGET(label), FALSE); |
|
| 3022 return FALSE; |
|
| 3023 } |
|
| 3024 |
|
| 3025 static void |
|
| 3026 create_sendto_item(GtkWidget *menu, GtkSizeGroup *sg, GSList **group, GaimBuddy *buddy, GaimAccount *account, const char *name) |
|
| 3027 { |
|
| 3028 GtkWidget *box; |
|
| 3029 GtkWidget *label; |
|
| 3030 GtkWidget *image; |
|
| 3031 GtkWidget *menuitem; |
|
| 3032 GdkPixbuf *pixbuf; |
|
| 3033 gchar *text; |
|
| 3034 |
|
| 3035 /* Create a pixmap for the protocol icon. */ |
|
| 3036 if (buddy != NULL) |
|
| 3037 pixbuf = gaim_gtk_blist_get_status_icon((GaimBlistNode*)buddy, GAIM_STATUS_ICON_SMALL); |
|
| 3038 else |
|
| 3039 { |
|
| 3040 GdkPixbuf *unscaled = gaim_gtk_create_prpl_icon(account); |
|
| 3041 |
|
| 3042 /* XXX: 15 is the size for GAIM_STATUS_ICON_SMALL in gtkblist.c */ |
|
| 3043 pixbuf = gdk_pixbuf_scale_simple(unscaled, 15, 15, |
|
| 3044 GDK_INTERP_BILINEAR); |
|
| 3045 g_object_unref(G_OBJECT(unscaled)); |
|
| 3046 } |
|
| 3047 |
|
| 3048 /* Now convert it to GtkImage */ |
|
| 3049 if (pixbuf == NULL) |
|
| 3050 image = gtk_image_new(); |
|
| 3051 else |
|
| 3052 image = gtk_image_new_from_pixbuf(pixbuf); |
|
| 3053 |
|
| 3054 gtk_size_group_add_widget(sg, image); |
|
| 3055 |
|
| 3056 g_object_unref(G_OBJECT(pixbuf)); |
|
| 3057 |
|
| 3058 /* Make our menu item */ |
|
| 3059 text = g_strdup_printf("%s (%s)", name, gaim_account_get_username(account)); |
|
| 3060 menuitem = gtk_radio_menu_item_new_with_label(*group, text); |
|
| 3061 g_free(text); |
|
| 3062 *group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(menuitem)); |
|
| 3063 |
|
| 3064 /* Do some evil, see some evil, speak some evil. */ |
|
| 3065 box = gtk_hbox_new(FALSE, 0); |
|
| 3066 |
|
| 3067 label = gtk_bin_get_child(GTK_BIN(menuitem)); |
|
| 3068 g_object_ref(label); |
|
| 3069 gtk_container_remove(GTK_CONTAINER(menuitem), label); |
|
| 3070 |
|
| 3071 gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0); |
|
| 3072 gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 4); |
|
| 3073 |
|
| 3074 if (buddy != NULL && |
|
| 3075 !gaim_presence_is_online(gaim_buddy_get_presence(buddy)) && |
|
| 3076 !gaim_account_supports_offline_message(account, buddy)) |
|
| 3077 { |
|
| 3078 gtk_widget_set_sensitive(label, FALSE); |
|
| 3079 |
|
| 3080 /* Set the label sensitive when the menuitem is highlighted and |
|
| 3081 * insensitive again when the mouse leaves it. This way, it |
|
| 3082 * doesn't appear weird from the highlighting of the embossed |
|
| 3083 * (insensitive style) text.*/ |
|
| 3084 g_signal_connect(menuitem, "enter-notify-event", |
|
| 3085 G_CALLBACK(send_to_item_enter_notify_cb), label); |
|
| 3086 g_signal_connect(menuitem, "leave-notify-event", |
|
| 3087 G_CALLBACK(send_to_item_leave_notify_cb), label); |
|
| 3088 } |
|
| 3089 |
|
| 3090 g_object_unref(label); |
|
| 3091 |
|
| 3092 gtk_container_add(GTK_CONTAINER(menuitem), box); |
|
| 3093 |
|
| 3094 gtk_widget_show(label); |
|
| 3095 gtk_widget_show(image); |
|
| 3096 gtk_widget_show(box); |
|
| 3097 |
|
| 3098 /* Set our data and callbacks. */ |
|
| 3099 g_object_set_data(G_OBJECT(menuitem), "gaim_account", account); |
|
| 3100 g_object_set_data_full(G_OBJECT(menuitem), "gaim_buddy_name", g_strdup(name), g_free); |
|
| 3101 |
|
| 3102 g_signal_connect(G_OBJECT(menuitem), "activate", |
|
| 3103 G_CALLBACK(menu_conv_sel_send_cb), NULL); |
|
| 3104 |
|
| 3105 gtk_widget_show(menuitem); |
|
| 3106 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem); |
|
| 3107 } |
|
| 3108 |
|
| 3109 static void |
|
| 3110 generate_send_to_items(GaimGtkWindow *win) |
|
| 3111 { |
|
| 3112 GtkWidget *menu; |
|
| 3113 GSList *group = NULL; |
|
| 3114 GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |
|
| 3115 GaimGtkConversation *gtkconv; |
|
| 3116 GSList *l, *buds; |
|
| 3117 |
|
| 3118 g_return_if_fail(win != NULL); |
|
| 3119 |
|
| 3120 gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 3121 |
|
| 3122 g_return_if_fail(gtkconv != NULL); |
|
| 3123 |
|
| 3124 if (win->menu.send_to != NULL) |
|
| 3125 gtk_widget_destroy(win->menu.send_to); |
|
| 3126 |
|
| 3127 /* Build the Send To menu */ |
|
| 3128 win->menu.send_to = gtk_menu_item_new_with_mnemonic(_("_Send To")); |
|
| 3129 gtk_widget_show(win->menu.send_to); |
|
| 3130 |
|
| 3131 menu = gtk_menu_new(); |
|
| 3132 gtk_menu_shell_insert(GTK_MENU_SHELL(win->menu.menubar), |
|
| 3133 win->menu.send_to, 2); |
|
| 3134 gtk_menu_item_set_submenu(GTK_MENU_ITEM(win->menu.send_to), menu); |
|
| 3135 |
|
| 3136 gtk_widget_show(menu); |
|
| 3137 |
|
| 3138 if (gtkconv->active_conv->type == GAIM_CONV_TYPE_IM) { |
|
| 3139 buds = gaim_find_buddies(gtkconv->active_conv->account, gtkconv->active_conv->name); |
|
| 3140 |
|
| 3141 if (buds == NULL) |
|
| 3142 { |
|
| 3143 /* The user isn't on the buddy list. */ |
|
| 3144 create_sendto_item(menu, sg, &group, NULL, gtkconv->active_conv->account, gtkconv->active_conv->name); |
|
| 3145 } |
|
| 3146 else |
|
| 3147 { |
|
| 3148 GList *list = NULL, *iter; |
|
| 3149 for (l = buds; l != NULL; l = l->next) |
|
| 3150 { |
|
| 3151 GaimBlistNode *node; |
|
| 3152 |
|
| 3153 node = (GaimBlistNode *) gaim_buddy_get_contact((GaimBuddy *)l->data); |
|
| 3154 |
|
| 3155 for (node = node->child; node != NULL; node = node->next) |
|
| 3156 { |
|
| 3157 GaimBuddy *buddy = (GaimBuddy *)node; |
|
| 3158 GaimAccount *account; |
|
| 3159 |
|
| 3160 if (!GAIM_BLIST_NODE_IS_BUDDY(node)) |
|
| 3161 continue; |
|
| 3162 |
|
| 3163 account = gaim_buddy_get_account(buddy); |
|
| 3164 if (gaim_account_is_connected(account)) |
|
| 3165 { |
|
| 3166 /* Use the GaimPresence to get unique buddies. */ |
|
| 3167 GaimPresence *presence = gaim_buddy_get_presence(buddy); |
|
| 3168 if (!g_list_find(list, presence)) |
|
| 3169 list = g_list_prepend(list, presence); |
|
| 3170 } |
|
| 3171 } |
|
| 3172 } |
|
| 3173 |
|
| 3174 /* Loop over the list backwards so we get the items in the right order, |
|
| 3175 * since we did a g_list_prepend() earlier. */ |
|
| 3176 for (iter = g_list_last(list); iter != NULL; iter = iter->prev) |
|
| 3177 { |
|
| 3178 GaimPresence *pre = iter->data; |
|
| 3179 GaimBuddy *buddy = gaim_presence_get_buddies(pre)->data; |
|
| 3180 create_sendto_item(menu, sg, &group, buddy, |
|
| 3181 gaim_buddy_get_account(buddy), gaim_buddy_get_name(buddy)); |
|
| 3182 } |
|
| 3183 g_list_free(list); |
|
| 3184 g_slist_free(buds); |
|
| 3185 } |
|
| 3186 } |
|
| 3187 |
|
| 3188 g_object_unref(sg); |
|
| 3189 |
|
| 3190 gtk_widget_show(win->menu.send_to); |
|
| 3191 /* TODO: This should never be insensitive. Possibly hidden or not. */ |
|
| 3192 if (!group) |
|
| 3193 gtk_widget_set_sensitive(win->menu.send_to, FALSE); |
|
| 3194 update_send_to_selection(win); |
|
| 3195 } |
|
| 3196 |
|
| 3197 static GList * |
|
| 3198 generate_invite_user_names(GaimConnection *gc) |
|
| 3199 { |
|
| 3200 GaimBlistNode *gnode,*cnode,*bnode; |
|
| 3201 static GList *tmp = NULL; |
|
| 3202 |
|
| 3203 if (tmp) |
|
| 3204 g_list_free(tmp); |
|
| 3205 |
|
| 3206 tmp = g_list_append(NULL, ""); |
|
| 3207 |
|
| 3208 if (gc != NULL) { |
|
| 3209 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) { |
|
| 3210 if(!GAIM_BLIST_NODE_IS_GROUP(gnode)) |
|
| 3211 continue; |
|
| 3212 for(cnode = gnode->child; cnode; cnode = cnode->next) { |
|
| 3213 if(!GAIM_BLIST_NODE_IS_CONTACT(cnode)) |
|
| 3214 continue; |
|
| 3215 for(bnode = cnode->child; bnode; bnode = bnode->next) { |
|
| 3216 GaimBuddy *buddy; |
|
| 3217 |
|
| 3218 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) |
|
| 3219 continue; |
|
| 3220 |
|
| 3221 buddy = (GaimBuddy *)bnode; |
|
| 3222 |
|
| 3223 if (buddy->account == gc->account && |
|
| 3224 GAIM_BUDDY_IS_ONLINE(buddy)) |
|
| 3225 tmp = g_list_insert_sorted(tmp, buddy->name, |
|
| 3226 (GCompareFunc)g_utf8_collate); |
|
| 3227 } |
|
| 3228 } |
|
| 3229 } |
|
| 3230 } |
|
| 3231 |
|
| 3232 return tmp; |
|
| 3233 } |
|
| 3234 |
|
| 3235 static GdkPixbuf * |
|
| 3236 get_chat_buddy_status_icon(GaimConvChat *chat, const char *name, GaimConvChatBuddyFlags flags) |
|
| 3237 { |
|
| 3238 GdkPixbuf *pixbuf, *scale, *scale2; |
|
| 3239 char *filename; |
|
| 3240 const char *image = NULL; |
|
| 3241 |
|
| 3242 if (flags & GAIM_CBFLAGS_FOUNDER) { |
|
| 3243 image = "founder.png"; |
|
| 3244 } else if (flags & GAIM_CBFLAGS_OP) { |
|
| 3245 image = "op.png"; |
|
| 3246 } else if (flags & GAIM_CBFLAGS_HALFOP) { |
|
| 3247 image = "halfop.png"; |
|
| 3248 } else if (flags & GAIM_CBFLAGS_VOICE) { |
|
| 3249 image = "voice.png"; |
|
| 3250 } else if ((!flags) && gaim_conv_chat_is_user_ignored(chat, name)) { |
|
| 3251 image = "ignored.png"; |
|
| 3252 } else { |
|
| 3253 return NULL; |
|
| 3254 } |
|
| 3255 |
|
| 3256 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", image, NULL); |
|
| 3257 pixbuf = gdk_pixbuf_new_from_file(filename, NULL); |
|
| 3258 g_free(filename); |
|
| 3259 |
|
| 3260 if (!pixbuf) |
|
| 3261 return NULL; |
|
| 3262 |
|
| 3263 scale = gdk_pixbuf_scale_simple(pixbuf, 15, 15, GDK_INTERP_BILINEAR); |
|
| 3264 g_object_unref(pixbuf); |
|
| 3265 |
|
| 3266 if (flags && gaim_conv_chat_is_user_ignored(chat, name)) { |
|
| 3267 filename = g_build_filename(DATADIR, "pixmaps", "gaim", "status", "default", "ignored.png", NULL); |
|
| 3268 pixbuf = gdk_pixbuf_new_from_file(filename, NULL); |
|
| 3269 g_free(filename); |
|
| 3270 scale2 = gdk_pixbuf_scale_simple(pixbuf, 15, 15, GDK_INTERP_BILINEAR); |
|
| 3271 g_object_unref(pixbuf); |
|
| 3272 gdk_pixbuf_composite(scale2, scale, 0, 0, 15, 15, 0, 0, 1, 1, GDK_INTERP_BILINEAR, 192); |
|
| 3273 g_object_unref(scale2); |
|
| 3274 } |
|
| 3275 |
|
| 3276 return scale; |
|
| 3277 } |
|
| 3278 |
|
| 3279 static void |
|
| 3280 add_chat_buddy_common(GaimConversation *conv, const char *name, GaimConvChatBuddyFlags flags, const char *alias, const char *old_name) |
|
| 3281 { |
|
| 3282 GaimGtkConversation *gtkconv; |
|
| 3283 GaimGtkChatPane *gtkchat; |
|
| 3284 GaimConvChat *chat; |
|
| 3285 GaimConnection *gc; |
|
| 3286 GaimPluginProtocolInfo *prpl_info; |
|
| 3287 GtkListStore *ls; |
|
| 3288 GdkPixbuf *pixbuf; |
|
| 3289 GtkTreeIter iter; |
|
| 3290 gboolean is_me = FALSE; |
|
| 3291 gboolean is_buddy; |
|
| 3292 |
|
| 3293 chat = GAIM_CONV_CHAT(conv); |
|
| 3294 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 3295 gtkchat = gtkconv->u.chat; |
|
| 3296 gc = gaim_conversation_get_gc(conv); |
|
| 3297 |
|
| 3298 if (!gc || !(prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl))) |
|
| 3299 return; |
|
| 3300 |
|
| 3301 ls = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list))); |
|
| 3302 |
|
| 3303 pixbuf = get_chat_buddy_status_icon(chat, name, flags); |
|
| 3304 |
|
| 3305 if (!strcmp(chat->nick, gaim_normalize(conv->account, old_name != NULL ? old_name : name))) |
|
| 3306 is_me = TRUE; |
|
| 3307 |
|
| 3308 is_buddy = (gaim_find_buddy(conv->account, name) != NULL); |
|
| 3309 |
|
| 3310 gtk_list_store_append(ls, &iter); |
|
| 3311 |
|
| 3312 if (is_me) |
|
| 3313 { |
|
| 3314 GdkColor send_color; |
|
| 3315 gdk_color_parse(SEND_COLOR, &send_color); |
|
| 3316 |
|
| 3317 gtk_list_store_set(ls, &iter, |
|
| 3318 CHAT_USERS_ICON_COLUMN, pixbuf, |
|
| 3319 CHAT_USERS_ALIAS_COLUMN, alias, |
|
| 3320 CHAT_USERS_NAME_COLUMN, name, |
|
| 3321 CHAT_USERS_FLAGS_COLUMN, flags, |
|
| 3322 CHAT_USERS_COLOR_COLUMN, &send_color, |
|
| 3323 CHAT_USERS_BUDDY_COLUMN, is_buddy, |
|
| 3324 -1); |
|
| 3325 } |
|
| 3326 else |
|
| 3327 { |
|
| 3328 gtk_list_store_set(ls, &iter, |
|
| 3329 CHAT_USERS_ICON_COLUMN, pixbuf, |
|
| 3330 CHAT_USERS_ALIAS_COLUMN, alias, |
|
| 3331 CHAT_USERS_NAME_COLUMN, name, |
|
| 3332 CHAT_USERS_FLAGS_COLUMN, flags, |
|
| 3333 CHAT_USERS_COLOR_COLUMN, get_nick_color(gtkconv, name), |
|
| 3334 CHAT_USERS_BUDDY_COLUMN, is_buddy, |
|
| 3335 -1); |
|
| 3336 } |
|
| 3337 |
|
| 3338 if (pixbuf) |
|
| 3339 g_object_unref(pixbuf); |
|
| 3340 } |
|
| 3341 |
|
| 3342 static void |
|
| 3343 tab_complete_process_item(int *most_matched, char *entered, char **partial, char *nick_partial, |
|
| 3344 GList **matches, gboolean command, char *name) |
|
| 3345 { |
|
| 3346 strncpy(nick_partial, name, strlen(entered)); |
|
| 3347 nick_partial[strlen(entered)] = '\0'; |
|
| 3348 if (gaim_utf8_strcasecmp(nick_partial, entered)) |
|
| 3349 return; |
|
| 3350 |
|
| 3351 /* if we're here, it's a possible completion */ |
|
| 3352 |
|
| 3353 if (*most_matched == -1) { |
|
| 3354 /* |
|
| 3355 * this will only get called once, since from now |
|
| 3356 * on *most_matched is >= 0 |
|
| 3357 */ |
|
| 3358 *most_matched = strlen(name); |
|
| 3359 *partial = g_strdup(name); |
|
| 3360 } |
|
| 3361 else if (*most_matched) { |
|
| 3362 char *tmp = g_strdup(name); |
|
| 3363 |
|
| 3364 while (gaim_utf8_strcasecmp(tmp, *partial)) { |
|
| 3365 (*partial)[*most_matched] = '\0'; |
|
| 3366 if (*most_matched < strlen(tmp)) |
|
| 3367 tmp[*most_matched] = '\0'; |
|
| 3368 (*most_matched)--; |
|
| 3369 } |
|
| 3370 (*most_matched)++; |
|
| 3371 |
|
| 3372 g_free(tmp); |
|
| 3373 } |
|
| 3374 |
|
| 3375 *matches = g_list_insert_sorted(*matches, g_strdup(name), |
|
| 3376 (GCompareFunc)gaim_utf8_strcasecmp); |
|
| 3377 } |
|
| 3378 |
|
| 3379 static gboolean |
|
| 3380 tab_complete(GaimConversation *conv) |
|
| 3381 { |
|
| 3382 GaimGtkConversation *gtkconv; |
|
| 3383 GtkTextIter cursor, word_start, start_buffer; |
|
| 3384 int start; |
|
| 3385 int most_matched = -1; |
|
| 3386 char *entered, *partial = NULL; |
|
| 3387 char *text; |
|
| 3388 char *nick_partial; |
|
| 3389 const char *prefix; |
|
| 3390 GList *matches = NULL; |
|
| 3391 gboolean command = FALSE; |
|
| 3392 |
|
| 3393 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 3394 |
|
| 3395 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer); |
|
| 3396 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor, |
|
| 3397 gtk_text_buffer_get_insert(gtkconv->entry_buffer)); |
|
| 3398 |
|
| 3399 word_start = cursor; |
|
| 3400 |
|
| 3401 /* if there's nothing there just return */ |
|
| 3402 if (!gtk_text_iter_compare(&cursor, &start_buffer)) |
|
| 3403 return (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) ? TRUE : FALSE; |
|
| 3404 |
|
| 3405 text = gtk_text_buffer_get_text(gtkconv->entry_buffer, &start_buffer, |
|
| 3406 &cursor, FALSE); |
|
| 3407 |
|
| 3408 /* if we're at the end of ": " we need to move back 2 spaces */ |
|
| 3409 start = strlen(text) - 1; |
|
| 3410 |
|
| 3411 if (strlen(text) >= 2 && !strncmp(&text[start-1], ": ", 2)) { |
|
| 3412 gtk_text_iter_backward_chars(&word_start, 2); |
|
| 3413 start-=2; |
|
| 3414 } |
|
| 3415 |
|
| 3416 /* find the start of the word that we're tabbing */ |
|
| 3417 while (start >= 0 && text[start] != ' ') { |
|
| 3418 gtk_text_iter_backward_char(&word_start); |
|
| 3419 start--; |
|
| 3420 } |
|
| 3421 |
|
| 3422 prefix = gaim_gtk_get_cmd_prefix(); |
|
| 3423 if (start == -1 && (strlen(text) >= strlen(prefix)) && !strncmp(text, prefix, strlen(prefix))) { |
|
| 3424 command = TRUE; |
|
| 3425 gtk_text_iter_forward_chars(&word_start, strlen(prefix)); |
|
| 3426 } |
|
| 3427 |
|
| 3428 g_free(text); |
|
| 3429 |
|
| 3430 entered = gtk_text_buffer_get_text(gtkconv->entry_buffer, &word_start, |
|
| 3431 &cursor, FALSE); |
|
| 3432 |
|
| 3433 if (!g_utf8_strlen(entered, -1)) { |
|
| 3434 g_free(entered); |
|
| 3435 return (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) ? TRUE : FALSE; |
|
| 3436 } |
|
| 3437 |
|
| 3438 nick_partial = g_malloc(strlen(entered)+1); |
|
| 3439 |
|
| 3440 if (command) { |
|
| 3441 GList *list = gaim_cmd_list(conv); |
|
| 3442 GList *l; |
|
| 3443 |
|
| 3444 /* Commands */ |
|
| 3445 for (l = list; l != NULL; l = l->next) { |
|
| 3446 tab_complete_process_item(&most_matched, entered, &partial, nick_partial, |
|
| 3447 &matches, TRUE, l->data); |
|
| 3448 } |
|
| 3449 g_list_free(list); |
|
| 3450 } else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 3451 GaimConvChat *chat = GAIM_CONV_CHAT(conv); |
|
| 3452 GList *l = gaim_conv_chat_get_users(chat); |
|
| 3453 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(GAIM_GTK_CONVERSATION(conv)->u.chat->list)); |
|
| 3454 GtkTreeIter iter; |
|
| 3455 int f; |
|
| 3456 |
|
| 3457 /* Users */ |
|
| 3458 for (; l != NULL; l = l->next) { |
|
| 3459 tab_complete_process_item(&most_matched, entered, &partial, nick_partial, |
|
| 3460 &matches, TRUE, ((GaimConvChatBuddy *)l->data)->name); |
|
| 3461 } |
|
| 3462 |
|
| 3463 |
|
| 3464 /* Aliases */ |
|
| 3465 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) |
|
| 3466 { |
|
| 3467 do { |
|
| 3468 char *name; |
|
| 3469 char *alias; |
|
| 3470 |
|
| 3471 gtk_tree_model_get(model, &iter, |
|
| 3472 CHAT_USERS_NAME_COLUMN, &name, |
|
| 3473 CHAT_USERS_ALIAS_COLUMN, &alias, |
|
| 3474 -1); |
|
| 3475 |
|
| 3476 if (strcmp(name, alias)) |
|
| 3477 tab_complete_process_item(&most_matched, entered, &partial, nick_partial, |
|
| 3478 &matches, FALSE, alias); |
|
| 3479 g_free(name); |
|
| 3480 g_free(alias); |
|
| 3481 |
|
| 3482 f = gtk_tree_model_iter_next(model, &iter); |
|
| 3483 } while (f != 0); |
|
| 3484 } |
|
| 3485 } else { |
|
| 3486 g_free(nick_partial); |
|
| 3487 g_free(entered); |
|
| 3488 return FALSE; |
|
| 3489 } |
|
| 3490 |
|
| 3491 g_free(nick_partial); |
|
| 3492 |
|
| 3493 /* we're only here if we're doing new style */ |
|
| 3494 |
|
| 3495 /* if there weren't any matches, return */ |
|
| 3496 if (!matches) { |
|
| 3497 /* if matches isn't set partials won't be either */ |
|
| 3498 g_free(entered); |
|
| 3499 return (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) ? TRUE : FALSE; |
|
| 3500 } |
|
| 3501 |
|
| 3502 gtk_text_buffer_delete(gtkconv->entry_buffer, &word_start, &cursor); |
|
| 3503 |
|
| 3504 if (!matches->next) { |
|
| 3505 /* there was only one match. fill it in. */ |
|
| 3506 gtk_text_buffer_get_start_iter(gtkconv->entry_buffer, &start_buffer); |
|
| 3507 gtk_text_buffer_get_iter_at_mark(gtkconv->entry_buffer, &cursor, |
|
| 3508 gtk_text_buffer_get_insert(gtkconv->entry_buffer)); |
|
| 3509 |
|
| 3510 if (!gtk_text_iter_compare(&cursor, &start_buffer)) { |
|
| 3511 char *tmp = g_strdup_printf("%s: ", (char *)matches->data); |
|
| 3512 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, tmp, -1); |
|
| 3513 g_free(tmp); |
|
| 3514 } |
|
| 3515 else |
|
| 3516 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, |
|
| 3517 matches->data, -1); |
|
| 3518 |
|
| 3519 g_free(matches->data); |
|
| 3520 matches = g_list_remove(matches, matches->data); |
|
| 3521 } |
|
| 3522 else { |
|
| 3523 /* |
|
| 3524 * there were lots of matches, fill in as much as possible |
|
| 3525 * and display all of them |
|
| 3526 */ |
|
| 3527 char *addthis = g_malloc0(1); |
|
| 3528 |
|
| 3529 while (matches) { |
|
| 3530 char *tmp = addthis; |
|
| 3531 addthis = g_strconcat(tmp, matches->data, " ", NULL); |
|
| 3532 g_free(tmp); |
|
| 3533 g_free(matches->data); |
|
| 3534 matches = g_list_remove(matches, matches->data); |
|
| 3535 } |
|
| 3536 |
|
| 3537 gaim_conversation_write(conv, NULL, addthis, GAIM_MESSAGE_NO_LOG, |
|
| 3538 time(NULL)); |
|
| 3539 gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, partial, -1); |
|
| 3540 g_free(addthis); |
|
| 3541 } |
|
| 3542 |
|
| 3543 g_free(entered); |
|
| 3544 g_free(partial); |
|
| 3545 |
|
| 3546 return TRUE; |
|
| 3547 } |
|
| 3548 |
|
| 3549 static void topic_callback(GtkWidget *w, GaimGtkConversation *gtkconv) |
|
| 3550 { |
|
| 3551 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 3552 GaimConnection *gc; |
|
| 3553 GaimConversation *conv = gtkconv->active_conv; |
|
| 3554 GaimGtkChatPane *gtkchat; |
|
| 3555 char *new_topic; |
|
| 3556 const char *current_topic; |
|
| 3557 |
|
| 3558 gc = gaim_conversation_get_gc(conv); |
|
| 3559 |
|
| 3560 if(!gc || !(prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl))) |
|
| 3561 return; |
|
| 3562 |
|
| 3563 if(prpl_info->set_chat_topic == NULL) |
|
| 3564 return; |
|
| 3565 |
|
| 3566 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 3567 gtkchat = gtkconv->u.chat; |
|
| 3568 new_topic = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtkchat->topic_text))); |
|
| 3569 current_topic = gaim_conv_chat_get_topic(GAIM_CONV_CHAT(conv)); |
|
| 3570 |
|
| 3571 if(current_topic && !g_utf8_collate(new_topic, current_topic)){ |
|
| 3572 g_free(new_topic); |
|
| 3573 return; |
|
| 3574 } |
|
| 3575 |
|
| 3576 gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), current_topic); |
|
| 3577 |
|
| 3578 prpl_info->set_chat_topic(gc, gaim_conv_chat_get_id(GAIM_CONV_CHAT(conv)), |
|
| 3579 new_topic); |
|
| 3580 |
|
| 3581 g_free(new_topic); |
|
| 3582 } |
|
| 3583 |
|
| 3584 static gint |
|
| 3585 sort_chat_users(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata) |
|
| 3586 { |
|
| 3587 GaimConvChatBuddyFlags f1 = 0, f2 = 0; |
|
| 3588 char *user1 = NULL, *user2 = NULL; |
|
| 3589 gboolean buddy1 = FALSE, buddy2 = FALSE; |
|
| 3590 gint ret = 0; |
|
| 3591 |
|
| 3592 gtk_tree_model_get(model, a, |
|
| 3593 CHAT_USERS_ALIAS_COLUMN, &user1, |
|
| 3594 CHAT_USERS_FLAGS_COLUMN, &f1, |
|
| 3595 CHAT_USERS_BUDDY_COLUMN, &buddy1, |
|
| 3596 -1); |
|
| 3597 gtk_tree_model_get(model, b, |
|
| 3598 CHAT_USERS_ALIAS_COLUMN, &user2, |
|
| 3599 CHAT_USERS_FLAGS_COLUMN, &f2, |
|
| 3600 CHAT_USERS_BUDDY_COLUMN, &buddy2, |
|
| 3601 -1); |
|
| 3602 |
|
| 3603 if (user1 == NULL || user2 == NULL) { |
|
| 3604 if (!(user1 == NULL && user2 == NULL)) |
|
| 3605 ret = (user1 == NULL) ? -1: 1; |
|
| 3606 } else if (f1 != f2) { |
|
| 3607 /* sort more important users first */ |
|
| 3608 ret = (f1 > f2) ? -1 : 1; |
|
| 3609 } else if (buddy1 != buddy2) { |
|
| 3610 ret = buddy1 ? -1 : 1; |
|
| 3611 } else { |
|
| 3612 ret = gaim_utf8_strcasecmp(user1, user2); |
|
| 3613 } |
|
| 3614 |
|
| 3615 g_free(user1); |
|
| 3616 g_free(user2); |
|
| 3617 |
|
| 3618 return ret; |
|
| 3619 } |
|
| 3620 |
|
| 3621 static void |
|
| 3622 update_chat_alias(GaimBuddy *buddy, GaimConversation *conv, GaimConnection *gc, GaimPluginProtocolInfo *prpl_info) |
|
| 3623 { |
|
| 3624 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 3625 GaimConvChat *chat = GAIM_CONV_CHAT(conv); |
|
| 3626 GtkTreeModel *model; |
|
| 3627 char *normalized_name; |
|
| 3628 GtkTreeIter iter; |
|
| 3629 int f; |
|
| 3630 |
|
| 3631 g_return_if_fail(buddy != NULL); |
|
| 3632 g_return_if_fail(conv != NULL); |
|
| 3633 |
|
| 3634 /* This is safe because this callback is only used in chats, not IMs. */ |
|
| 3635 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkconv->u.chat->list)); |
|
| 3636 |
|
| 3637 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) |
|
| 3638 return; |
|
| 3639 |
|
| 3640 normalized_name = g_strdup(gaim_normalize(conv->account, buddy->name)); |
|
| 3641 |
|
| 3642 do { |
|
| 3643 char *name; |
|
| 3644 |
|
| 3645 gtk_tree_model_get(model, &iter, CHAT_USERS_NAME_COLUMN, &name, -1); |
|
| 3646 |
|
| 3647 if (!strcmp(normalized_name, gaim_normalize(conv->account, name))) { |
|
| 3648 const char *alias = name; |
|
| 3649 GaimBuddy *buddy2; |
|
| 3650 |
|
| 3651 if (strcmp(chat->nick, gaim_normalize(conv->account, name))) { |
|
| 3652 /* This user is not me, so look into updating the alias. */ |
|
| 3653 |
|
| 3654 if ((buddy2 = gaim_find_buddy(conv->account, name)) != NULL) |
|
| 3655 alias = gaim_buddy_get_contact_alias(buddy2); |
|
| 3656 |
|
| 3657 gtk_list_store_set(GTK_LIST_STORE(model), &iter, |
|
| 3658 CHAT_USERS_ALIAS_COLUMN, alias, |
|
| 3659 -1); |
|
| 3660 } |
|
| 3661 g_free(name); |
|
| 3662 break; |
|
| 3663 } |
|
| 3664 |
|
| 3665 f = gtk_tree_model_iter_next(model, &iter); |
|
| 3666 |
|
| 3667 g_free(name); |
|
| 3668 } while (f != 0); |
|
| 3669 |
|
| 3670 g_free(normalized_name); |
|
| 3671 } |
|
| 3672 |
|
| 3673 static void |
|
| 3674 blist_node_aliased_cb(GaimBlistNode *node, const char *old_alias, GaimConversation *conv) |
|
| 3675 { |
|
| 3676 GaimConnection *gc; |
|
| 3677 GaimPluginProtocolInfo *prpl_info; |
|
| 3678 |
|
| 3679 g_return_if_fail(node != NULL); |
|
| 3680 g_return_if_fail(conv != NULL); |
|
| 3681 |
|
| 3682 gc = gaim_conversation_get_gc(conv); |
|
| 3683 g_return_if_fail(gc != NULL); |
|
| 3684 g_return_if_fail(gc->prpl != NULL); |
|
| 3685 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 3686 |
|
| 3687 if (prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME) |
|
| 3688 return; |
|
| 3689 |
|
| 3690 if (GAIM_BLIST_NODE_IS_CONTACT(node)) |
|
| 3691 { |
|
| 3692 GaimBlistNode *bnode; |
|
| 3693 |
|
| 3694 for(bnode = node->child; bnode; bnode = bnode->next) { |
|
| 3695 |
|
| 3696 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode)) |
|
| 3697 continue; |
|
| 3698 |
|
| 3699 update_chat_alias((GaimBuddy *)bnode, conv, gc, prpl_info); |
|
| 3700 } |
|
| 3701 } |
|
| 3702 else if (GAIM_BLIST_NODE_IS_BUDDY(node)) |
|
| 3703 update_chat_alias((GaimBuddy *)node, conv, gc, prpl_info); |
|
| 3704 } |
|
| 3705 |
|
| 3706 static void |
|
| 3707 buddy_cb_common(GaimBuddy *buddy, GaimConversation *conv, gboolean is_buddy) |
|
| 3708 { |
|
| 3709 GtkTreeModel *model; |
|
| 3710 char *normalized_name; |
|
| 3711 GtkTreeIter iter; |
|
| 3712 int f; |
|
| 3713 |
|
| 3714 g_return_if_fail(buddy != NULL); |
|
| 3715 g_return_if_fail(conv != NULL); |
|
| 3716 |
|
| 3717 /* Do nothing if the buddy does not belong to the conv's account */ |
|
| 3718 if (gaim_buddy_get_account(buddy) != gaim_conversation_get_account(conv)) |
|
| 3719 return; |
|
| 3720 |
|
| 3721 /* This is safe because this callback is only used in chats, not IMs. */ |
|
| 3722 model = gtk_tree_view_get_model(GTK_TREE_VIEW(GAIM_GTK_CONVERSATION(conv)->u.chat->list)); |
|
| 3723 |
|
| 3724 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) |
|
| 3725 return; |
|
| 3726 |
|
| 3727 normalized_name = g_strdup(gaim_normalize(conv->account, buddy->name)); |
|
| 3728 |
|
| 3729 do { |
|
| 3730 char *name; |
|
| 3731 |
|
| 3732 gtk_tree_model_get(model, &iter, CHAT_USERS_NAME_COLUMN, &name, -1); |
|
| 3733 |
|
| 3734 if (!strcmp(normalized_name, gaim_normalize(conv->account, name))) { |
|
| 3735 gtk_list_store_set(GTK_LIST_STORE(model), &iter, CHAT_USERS_BUDDY_COLUMN, is_buddy, -1); |
|
| 3736 g_free(name); |
|
| 3737 break; |
|
| 3738 } |
|
| 3739 |
|
| 3740 f = gtk_tree_model_iter_next(model, &iter); |
|
| 3741 |
|
| 3742 g_free(name); |
|
| 3743 } while (f != 0); |
|
| 3744 |
|
| 3745 g_free(normalized_name); |
|
| 3746 |
|
| 3747 blist_node_aliased_cb((GaimBlistNode *)buddy, NULL, conv); |
|
| 3748 } |
|
| 3749 |
|
| 3750 static void |
|
| 3751 buddy_added_cb(GaimBuddy *buddy, GaimConversation *conv) |
|
| 3752 { |
|
| 3753 buddy_cb_common(buddy, conv, TRUE); |
|
| 3754 } |
|
| 3755 |
|
| 3756 static void |
|
| 3757 buddy_removed_cb(GaimBuddy *buddy, GaimConversation *conv) |
|
| 3758 { |
|
| 3759 /* If there's another buddy for the same "dude" on the list, do nothing. */ |
|
| 3760 if (gaim_find_buddy(buddy->account, buddy->name) != NULL) |
|
| 3761 return; |
|
| 3762 |
|
| 3763 buddy_cb_common(buddy, conv, FALSE); |
|
| 3764 } |
|
| 3765 |
|
| 3766 static void send_menu_cb(GtkWidget *widget, GaimGtkConversation *gtkconv) |
|
| 3767 { |
|
| 3768 g_signal_emit_by_name(gtkconv->entry, "message_send"); |
|
| 3769 } |
|
| 3770 |
|
| 3771 static void |
|
| 3772 entry_popup_menu_cb(GtkIMHtml *imhtml, GtkMenu *menu, gpointer data) |
|
| 3773 { |
|
| 3774 GtkWidget *menuitem; |
|
| 3775 GaimGtkConversation *gtkconv = data; |
|
| 3776 |
|
| 3777 g_return_if_fail(menu != NULL); |
|
| 3778 g_return_if_fail(gtkconv != NULL); |
|
| 3779 |
|
| 3780 menuitem = gaim_new_item_from_stock(NULL, _("_Send"), GAIM_STOCK_SEND, |
|
| 3781 G_CALLBACK(send_menu_cb), gtkconv, |
|
| 3782 0, 0, NULL); |
|
| 3783 if (gtk_text_buffer_get_char_count(imhtml->text_buffer) == 0) |
|
| 3784 gtk_widget_set_sensitive(menuitem, FALSE); |
|
| 3785 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 0); |
|
| 3786 |
|
| 3787 menuitem = gtk_separator_menu_item_new(); |
|
| 3788 gtk_widget_show(menuitem); |
|
| 3789 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1); |
|
| 3790 } |
|
| 3791 |
|
| 3792 static GtkWidget * |
|
| 3793 setup_chat_pane(GaimGtkConversation *gtkconv) |
|
| 3794 { |
|
| 3795 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 3796 GaimConversation *conv = gtkconv->active_conv; |
|
| 3797 GaimGtkChatPane *gtkchat; |
|
| 3798 GaimConnection *gc; |
|
| 3799 GtkWidget *vpaned, *hpaned; |
|
| 3800 GtkWidget *vbox, *hbox, *frame; |
|
| 3801 GtkWidget *lbox, *bbox; |
|
| 3802 GtkWidget *label; |
|
| 3803 GtkWidget *list; |
|
| 3804 GtkWidget *button; |
|
| 3805 GtkWidget *sw; |
|
| 3806 GtkListStore *ls; |
|
| 3807 GtkCellRenderer *rend; |
|
| 3808 GtkTreeViewColumn *col; |
|
| 3809 void *blist_handle = gaim_blist_get_handle(); |
|
| 3810 GList *focus_chain = NULL; |
|
| 3811 |
|
| 3812 gtkchat = gtkconv->u.chat; |
|
| 3813 gc = gaim_conversation_get_gc(conv); |
|
| 3814 |
|
| 3815 /* Setup the outer pane. */ |
|
| 3816 vpaned = gtk_vpaned_new(); |
|
| 3817 gtk_widget_show(vpaned); |
|
| 3818 |
|
| 3819 /* Setup the top part of the pane. */ |
|
| 3820 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 3821 gtk_paned_pack1(GTK_PANED(vpaned), vbox, TRUE, TRUE); |
|
| 3822 gtk_widget_show(vbox); |
|
| 3823 |
|
| 3824 if (gc != NULL) |
|
| 3825 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 3826 |
|
| 3827 if (prpl_info->options & OPT_PROTO_CHAT_TOPIC) |
|
| 3828 { |
|
| 3829 hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 3830 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
|
| 3831 gtk_widget_show(hbox); |
|
| 3832 |
|
| 3833 label = gtk_label_new(_("Topic:")); |
|
| 3834 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); |
|
| 3835 gtk_widget_show(label); |
|
| 3836 |
|
| 3837 gtkchat->topic_text = gtk_entry_new(); |
|
| 3838 |
|
| 3839 if(prpl_info->set_chat_topic == NULL) { |
|
| 3840 gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE); |
|
| 3841 } else { |
|
| 3842 g_signal_connect(GTK_OBJECT(gtkchat->topic_text), "activate", |
|
| 3843 G_CALLBACK(topic_callback), gtkconv); |
|
| 3844 } |
|
| 3845 |
|
| 3846 gtk_box_pack_start(GTK_BOX(hbox), gtkchat->topic_text, TRUE, TRUE, 0); |
|
| 3847 gtk_widget_show(gtkchat->topic_text); |
|
| 3848 } |
|
| 3849 |
|
| 3850 /* Setup the horizontal pane. */ |
|
| 3851 hpaned = gtk_hpaned_new(); |
|
| 3852 gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0); |
|
| 3853 gtk_widget_show(hpaned); |
|
| 3854 |
|
| 3855 /* Setup gtkihmtml. */ |
|
| 3856 frame = gaim_gtk_create_imhtml(FALSE, >kconv->imhtml, NULL); |
|
| 3857 gtk_widget_set_name(gtkconv->imhtml, "gaim_gtkconv_imhtml"); |
|
| 3858 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), TRUE); |
|
| 3859 gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE); |
|
| 3860 gtk_widget_show(frame); |
|
| 3861 |
|
| 3862 gtk_widget_set_size_request(gtkconv->imhtml, |
|
| 3863 gaim_prefs_get_int("/gaim/gtk/conversations/chat/default_width"), |
|
| 3864 gaim_prefs_get_int("/gaim/gtk/conversations/chat/default_height")); |
|
| 3865 g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate", |
|
| 3866 G_CALLBACK(size_allocate_cb), gtkconv); |
|
| 3867 |
|
| 3868 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event", |
|
| 3869 G_CALLBACK(entry_stop_rclick_cb), NULL); |
|
| 3870 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", |
|
| 3871 G_CALLBACK(refocus_entry_cb), gtkconv); |
|
| 3872 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", |
|
| 3873 G_CALLBACK(refocus_entry_cb), gtkconv); |
|
| 3874 |
|
| 3875 /* Build the right pane. */ |
|
| 3876 lbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 3877 gtk_paned_pack2(GTK_PANED(hpaned), lbox, FALSE, TRUE); |
|
| 3878 gtk_widget_show(lbox); |
|
| 3879 |
|
| 3880 /* Setup the label telling how many people are in the room. */ |
|
| 3881 gtkchat->count = gtk_label_new(_("0 people in room")); |
|
| 3882 gtk_box_pack_start(GTK_BOX(lbox), gtkchat->count, FALSE, FALSE, 0); |
|
| 3883 gtk_widget_show(gtkchat->count); |
|
| 3884 |
|
| 3885 /* Setup the list of users. */ |
|
| 3886 sw = gtk_scrolled_window_new(NULL, NULL); |
|
| 3887 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), |
|
| 3888 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); |
|
| 3889 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); |
|
| 3890 gtk_box_pack_start(GTK_BOX(lbox), sw, TRUE, TRUE, 0); |
|
| 3891 gtk_widget_show(sw); |
|
| 3892 |
|
| 3893 ls = gtk_list_store_new(CHAT_USERS_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, |
|
| 3894 G_TYPE_STRING, G_TYPE_INT, GDK_TYPE_COLOR, G_TYPE_BOOLEAN); |
|
| 3895 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(ls), CHAT_USERS_ALIAS_COLUMN, |
|
| 3896 sort_chat_users, NULL, NULL); |
|
| 3897 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls), CHAT_USERS_ALIAS_COLUMN, |
|
| 3898 GTK_SORT_ASCENDING); |
|
| 3899 |
|
| 3900 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls)); |
|
| 3901 |
|
| 3902 rend = gtk_cell_renderer_pixbuf_new(); |
|
| 3903 |
|
| 3904 col = gtk_tree_view_column_new_with_attributes(NULL, rend, |
|
| 3905 "pixbuf", CHAT_USERS_ICON_COLUMN, NULL); |
|
| 3906 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_AUTOSIZE); |
|
| 3907 gtk_tree_view_append_column(GTK_TREE_VIEW(list), col); |
|
| 3908 |
|
| 3909 g_signal_connect(G_OBJECT(list), "button_press_event", |
|
| 3910 G_CALLBACK(right_click_chat_cb), gtkconv); |
|
| 3911 g_signal_connect(G_OBJECT(list), "popup-menu", |
|
| 3912 G_CALLBACK(gtkconv_chat_popup_menu_cb), gtkconv); |
|
| 3913 |
|
| 3914 rend = gtk_cell_renderer_text_new(); |
|
| 3915 |
|
| 3916 g_object_set(rend, |
|
| 3917 "foreground-set", TRUE, |
|
| 3918 "weight", PANGO_WEIGHT_BOLD, |
|
| 3919 NULL); |
|
| 3920 col = gtk_tree_view_column_new_with_attributes(NULL, rend, |
|
| 3921 "text", CHAT_USERS_ALIAS_COLUMN, |
|
| 3922 "foreground-gdk", CHAT_USERS_COLOR_COLUMN, |
|
| 3923 "weight-set", CHAT_USERS_BUDDY_COLUMN, |
|
| 3924 NULL); |
|
| 3925 |
|
| 3926 gaim_signal_connect(blist_handle, "buddy-added", |
|
| 3927 gtkchat, GAIM_CALLBACK(buddy_added_cb), conv); |
|
| 3928 gaim_signal_connect(blist_handle, "buddy-removed", |
|
| 3929 gtkchat, GAIM_CALLBACK(buddy_removed_cb), conv); |
|
| 3930 gaim_signal_connect(blist_handle, "blist-node-aliased", |
|
| 3931 gtkchat, GAIM_CALLBACK(blist_node_aliased_cb), conv); |
|
| 3932 |
|
| 3933 #if GTK_CHECK_VERSION(2,6,0) |
|
| 3934 gtk_tree_view_column_set_expand(col, TRUE); |
|
| 3935 g_object_set(rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
|
| 3936 #endif |
|
| 3937 |
|
| 3938 gtk_tree_view_append_column(GTK_TREE_VIEW(list), col); |
|
| 3939 |
|
| 3940 gtk_widget_set_size_request(list, 150, -1); |
|
| 3941 |
|
| 3942 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE); |
|
| 3943 gtk_widget_show(list); |
|
| 3944 |
|
| 3945 gtkchat->list = list; |
|
| 3946 |
|
| 3947 gtk_container_add(GTK_CONTAINER(sw), list); |
|
| 3948 |
|
| 3949 /* Setup the user list toolbar. */ |
|
| 3950 bbox = gtk_hbox_new(TRUE, GAIM_HIG_BOX_SPACE); |
|
| 3951 gtk_box_pack_start(GTK_BOX(lbox), bbox, FALSE, FALSE, 0); |
|
| 3952 gtk_widget_show(bbox); |
|
| 3953 |
|
| 3954 /* IM */ |
|
| 3955 button = gaim_pixbuf_button_from_stock(NULL, GAIM_STOCK_IM, |
|
| 3956 GAIM_BUTTON_VERTICAL); |
|
| 3957 gtkchat->userlist_im = button; |
|
| 3958 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); |
|
| 3959 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
|
| 3960 gtk_tooltips_set_tip(gtkconv->tooltips, button, _("IM the user"), NULL); |
|
| 3961 g_signal_connect(G_OBJECT(button), "clicked", |
|
| 3962 G_CALLBACK(chat_im_button_cb), gtkconv); |
|
| 3963 |
|
| 3964 gtk_widget_show(button); |
|
| 3965 |
|
| 3966 /* Ignore */ |
|
| 3967 button = gaim_pixbuf_button_from_stock(NULL, GAIM_STOCK_IGNORE, |
|
| 3968 GAIM_BUTTON_VERTICAL); |
|
| 3969 gtkchat->userlist_ignore = button; |
|
| 3970 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); |
|
| 3971 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
|
| 3972 gtk_tooltips_set_tip(gtkconv->tooltips, button, |
|
| 3973 _("Ignore the user"), NULL); |
|
| 3974 g_signal_connect(G_OBJECT(button), "clicked", |
|
| 3975 G_CALLBACK(ignore_cb), gtkconv); |
|
| 3976 gtk_widget_show(button); |
|
| 3977 |
|
| 3978 /* Info */ |
|
| 3979 button = gaim_pixbuf_button_from_stock(NULL, GAIM_STOCK_INFO, |
|
| 3980 GAIM_BUTTON_VERTICAL); |
|
| 3981 gtkchat->userlist_info = button; |
|
| 3982 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); |
|
| 3983 gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); |
|
| 3984 gtk_tooltips_set_tip(gtkconv->tooltips, button, |
|
| 3985 _("Get the user's information"), NULL); |
|
| 3986 g_signal_connect(G_OBJECT(button), "clicked", |
|
| 3987 G_CALLBACK(info_cb), gtkconv); |
|
| 3988 |
|
| 3989 gtk_widget_show(button); |
|
| 3990 |
|
| 3991 /* Setup the bottom half of the conversation window */ |
|
| 3992 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 3993 gtk_paned_pack2(GTK_PANED(vpaned), vbox, FALSE, TRUE); |
|
| 3994 gtk_widget_show(vbox); |
|
| 3995 |
|
| 3996 gtkconv->lower_hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 3997 gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, TRUE, TRUE, 0); |
|
| 3998 gtk_widget_show(gtkconv->lower_hbox); |
|
| 3999 |
|
| 4000 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 4001 gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox, TRUE, TRUE, 0); |
|
| 4002 gtk_widget_show(vbox); |
|
| 4003 |
|
| 4004 /* Setup the toolbar, entry widget and all signals */ |
|
| 4005 frame = gaim_gtk_create_imhtml(TRUE, >kconv->entry, >kconv->toolbar); |
|
| 4006 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); |
|
| 4007 gtk_widget_show(frame); |
|
| 4008 |
|
| 4009 g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup", |
|
| 4010 G_CALLBACK(entry_popup_menu_cb), gtkconv); |
|
| 4011 |
|
| 4012 gtk_widget_set_name(gtkconv->entry, "gaim_gtkconv_entry"); |
|
| 4013 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry), |
|
| 4014 gaim_account_get_protocol_name(conv->account)); |
|
| 4015 gtk_widget_set_size_request(gtkconv->entry, -1, |
|
| 4016 gaim_prefs_get_int("/gaim/gtk/conversations/chat/entry_height")); |
|
| 4017 gtkconv->entry_buffer = |
|
| 4018 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); |
|
| 4019 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); |
|
| 4020 |
|
| 4021 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", |
|
| 4022 G_CALLBACK(entry_key_press_cb), gtkconv); |
|
| 4023 g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", |
|
| 4024 G_CALLBACK(send_cb), gtkconv); |
|
| 4025 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event", |
|
| 4026 G_CALLBACK(entry_stop_rclick_cb), NULL); |
|
| 4027 g_signal_connect(G_OBJECT(gtkconv->entry), "size-allocate", |
|
| 4028 G_CALLBACK(size_allocate_cb), gtkconv); |
|
| 4029 |
|
| 4030 default_formatize(gtkconv); |
|
| 4031 |
|
| 4032 /* |
|
| 4033 * Focus for chat windows should be as follows: |
|
| 4034 * Tab title -> chat topic -> conversation scrollback -> user list -> |
|
| 4035 * user list buttons -> entry -> buttons at bottom |
|
| 4036 */ |
|
| 4037 focus_chain = g_list_prepend(focus_chain, gtkconv->entry); |
|
| 4038 gtk_container_set_focus_chain(GTK_CONTAINER(vbox), focus_chain); |
|
| 4039 |
|
| 4040 return vpaned; |
|
| 4041 } |
|
| 4042 |
|
| 4043 static GtkWidget * |
|
| 4044 setup_im_pane(GaimGtkConversation *gtkconv) |
|
| 4045 { |
|
| 4046 GaimConversation *conv = gtkconv->active_conv; |
|
| 4047 GtkWidget *frame; |
|
| 4048 GtkWidget *paned; |
|
| 4049 GtkWidget *vbox; |
|
| 4050 GtkWidget *vbox2; |
|
| 4051 GList *focus_chain = NULL; |
|
| 4052 |
|
| 4053 /* Setup the outer pane */ |
|
| 4054 paned = gtk_vpaned_new(); |
|
| 4055 gtk_widget_show(paned); |
|
| 4056 |
|
| 4057 /* Setup the top part of the pane */ |
|
| 4058 vbox = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 4059 gtk_paned_pack1(GTK_PANED(paned), vbox, TRUE, TRUE); |
|
| 4060 gtk_widget_show(vbox); |
|
| 4061 |
|
| 4062 /* Setup the gtkimhtml widget */ |
|
| 4063 frame = gaim_gtk_create_imhtml(FALSE, >kconv->imhtml, NULL); |
|
| 4064 gtk_widget_set_name(gtkconv->imhtml, "gaim_gtkconv_imhtml"); |
|
| 4065 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml),TRUE); |
|
| 4066 gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); |
|
| 4067 gtk_widget_show(frame); |
|
| 4068 |
|
| 4069 gtk_widget_set_size_request(gtkconv->imhtml, |
|
| 4070 gaim_prefs_get_int("/gaim/gtk/conversations/im/default_width"), |
|
| 4071 gaim_prefs_get_int("/gaim/gtk/conversations/im/default_height")); |
|
| 4072 g_signal_connect(G_OBJECT(gtkconv->imhtml), "size-allocate", |
|
| 4073 G_CALLBACK(size_allocate_cb), gtkconv); |
|
| 4074 |
|
| 4075 g_signal_connect_after(G_OBJECT(gtkconv->imhtml), "button_press_event", |
|
| 4076 G_CALLBACK(entry_stop_rclick_cb), NULL); |
|
| 4077 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_press_event", |
|
| 4078 G_CALLBACK(refocus_entry_cb), gtkconv); |
|
| 4079 g_signal_connect(G_OBJECT(gtkconv->imhtml), "key_release_event", |
|
| 4080 G_CALLBACK(refocus_entry_cb), gtkconv); |
|
| 4081 |
|
| 4082 /* Setup the bottom half of the conversation window */ |
|
| 4083 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 4084 gtk_paned_pack2(GTK_PANED(paned), vbox2, FALSE, TRUE); |
|
| 4085 gtk_widget_show(vbox2); |
|
| 4086 |
|
| 4087 gtkconv->lower_hbox = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 4088 gtk_box_pack_start(GTK_BOX(vbox2), gtkconv->lower_hbox, TRUE, TRUE, 0); |
|
| 4089 gtk_widget_show(gtkconv->lower_hbox); |
|
| 4090 |
|
| 4091 vbox2 = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 4092 gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), vbox2, TRUE, TRUE, 0); |
|
| 4093 gtk_widget_show(vbox2); |
|
| 4094 |
|
| 4095 /* Setup the toolbar, entry widget and all signals */ |
|
| 4096 frame = gaim_gtk_create_imhtml(TRUE, >kconv->entry, >kconv->toolbar); |
|
| 4097 gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, 0); |
|
| 4098 gtk_widget_show(frame); |
|
| 4099 |
|
| 4100 g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup", |
|
| 4101 G_CALLBACK(entry_popup_menu_cb), gtkconv); |
|
| 4102 |
|
| 4103 gtk_widget_set_name(gtkconv->entry, "gaim_gtkconv_entry"); |
|
| 4104 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->entry), |
|
| 4105 gaim_account_get_protocol_name(conv->account)); |
|
| 4106 gtk_widget_set_size_request(gtkconv->entry, -1, |
|
| 4107 gaim_prefs_get_int("/gaim/gtk/conversations/im/entry_height")); |
|
| 4108 gtkconv->entry_buffer = |
|
| 4109 gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry)); |
|
| 4110 g_object_set_data(G_OBJECT(gtkconv->entry_buffer), "user_data", gtkconv); |
|
| 4111 |
|
| 4112 g_signal_connect(G_OBJECT(gtkconv->entry), "key_press_event", |
|
| 4113 G_CALLBACK(entry_key_press_cb), gtkconv); |
|
| 4114 g_signal_connect_after(G_OBJECT(gtkconv->entry), "message_send", |
|
| 4115 G_CALLBACK(send_cb), gtkconv); |
|
| 4116 g_signal_connect_after(G_OBJECT(gtkconv->entry), "button_press_event", |
|
| 4117 G_CALLBACK(entry_stop_rclick_cb), NULL); |
|
| 4118 g_signal_connect(G_OBJECT(gtkconv->entry), "size-allocate", |
|
| 4119 G_CALLBACK(size_allocate_cb), gtkconv); |
|
| 4120 |
|
| 4121 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "insert_text", |
|
| 4122 G_CALLBACK(insert_text_cb), gtkconv); |
|
| 4123 g_signal_connect(G_OBJECT(gtkconv->entry_buffer), "delete_range", |
|
| 4124 G_CALLBACK(delete_text_cb), gtkconv); |
|
| 4125 |
|
| 4126 /* had to move this after the imtoolbar is attached so that the |
|
| 4127 * signals get fired to toggle the buttons on the toolbar as well. |
|
| 4128 */ |
|
| 4129 default_formatize(gtkconv); |
|
| 4130 |
|
| 4131 g_signal_connect_after(G_OBJECT(gtkconv->entry), "format_function_clear", |
|
| 4132 G_CALLBACK(clear_formatting_cb), gtkconv); |
|
| 4133 |
|
| 4134 gtkconv->u.im->animate = gaim_prefs_get_bool("/gaim/gtk/conversations/im/animate_buddy_icons"); |
|
| 4135 gtkconv->u.im->show_icon = TRUE; |
|
| 4136 |
|
| 4137 /* |
|
| 4138 * Focus for IM windows should be as follows: |
|
| 4139 * Tab title -> conversation scrollback -> entry |
|
| 4140 */ |
|
| 4141 focus_chain = g_list_prepend(focus_chain, gtkconv->entry); |
|
| 4142 gtk_container_set_focus_chain(GTK_CONTAINER(vbox2), focus_chain); |
|
| 4143 |
|
| 4144 return paned; |
|
| 4145 } |
|
| 4146 |
|
| 4147 static void |
|
| 4148 conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y, |
|
| 4149 GtkSelectionData *sd, guint info, guint t, |
|
| 4150 GaimGtkConversation *gtkconv) |
|
| 4151 { |
|
| 4152 GaimConversation *conv = gtkconv->active_conv; |
|
| 4153 GaimGtkWindow *win = gtkconv->win; |
|
| 4154 GaimConversation *c; |
|
| 4155 if (sd->target == gdk_atom_intern("GAIM_BLIST_NODE", FALSE)) |
|
| 4156 { |
|
| 4157 GaimBlistNode *n = NULL; |
|
| 4158 GaimBuddy *b; |
|
| 4159 GaimGtkConversation *gtkconv = NULL; |
|
| 4160 |
|
| 4161 n = *(GaimBlistNode **)sd->data; |
|
| 4162 |
|
| 4163 if (GAIM_BLIST_NODE_IS_CONTACT(n)) |
|
| 4164 b = gaim_contact_get_priority_buddy((GaimContact*)n); |
|
| 4165 else if (GAIM_BLIST_NODE_IS_BUDDY(n)) |
|
| 4166 b = (GaimBuddy*)n; |
|
| 4167 else |
|
| 4168 return; |
|
| 4169 |
|
| 4170 /* |
|
| 4171 * If we already have an open conversation with this buddy, then |
|
| 4172 * just move the conv to this window. Otherwise, create a new |
|
| 4173 * conv and add it to this window. |
|
| 4174 */ |
|
| 4175 c = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, b->name, b->account); |
|
| 4176 if (c != NULL) { |
|
| 4177 GaimGtkWindow *oldwin; |
|
| 4178 gtkconv = GAIM_GTK_CONVERSATION(c); |
|
| 4179 oldwin = gtkconv->win; |
|
| 4180 if (oldwin != win) { |
|
| 4181 gaim_gtk_conv_window_remove_gtkconv(oldwin, gtkconv); |
|
| 4182 gaim_gtk_conv_window_add_gtkconv(win, gtkconv); |
|
| 4183 } |
|
| 4184 } else { |
|
| 4185 c = gaim_conversation_new(GAIM_CONV_TYPE_IM, b->account, b->name); |
|
| 4186 gtkconv = GAIM_GTK_CONVERSATION(c); |
|
| 4187 if (gtkconv->win != win) |
|
| 4188 { |
|
| 4189 gaim_gtk_conv_window_remove_gtkconv(gtkconv->win, gtkconv); |
|
| 4190 gaim_gtk_conv_window_add_gtkconv(win, gtkconv); |
|
| 4191 } |
|
| 4192 } |
|
| 4193 |
|
| 4194 /* Make this conversation the active conversation */ |
|
| 4195 gaim_gtk_conv_window_switch_gtkconv(win, gtkconv); |
|
| 4196 |
|
| 4197 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
|
| 4198 } |
|
| 4199 else if (sd->target == gdk_atom_intern("application/x-im-contact", FALSE)) |
|
| 4200 { |
|
| 4201 char *protocol = NULL; |
|
| 4202 char *username = NULL; |
|
| 4203 GaimAccount *account; |
|
| 4204 GaimGtkConversation *gtkconv; |
|
| 4205 |
|
| 4206 if (gaim_gtk_parse_x_im_contact((const char *)sd->data, FALSE, &account, |
|
| 4207 &protocol, &username, NULL)) |
|
| 4208 { |
|
| 4209 if (account == NULL) |
|
| 4210 { |
|
| 4211 gaim_notify_error(NULL, NULL, |
|
| 4212 _("You are not currently signed on with an account that " |
|
| 4213 "can add that buddy."), NULL); |
|
| 4214 } |
|
| 4215 else |
|
| 4216 { |
|
| 4217 c = gaim_conversation_new(GAIM_CONV_TYPE_IM, account, username); |
|
| 4218 gtkconv = GAIM_GTK_CONVERSATION(c); |
|
| 4219 if (gtkconv->win != win) |
|
| 4220 { |
|
| 4221 gaim_gtk_conv_window_remove_gtkconv(gtkconv->win, gtkconv); |
|
| 4222 gaim_gtk_conv_window_add_gtkconv(win, gtkconv); |
|
| 4223 } |
|
| 4224 } |
|
| 4225 } |
|
| 4226 |
|
| 4227 if (username != NULL) g_free(username); |
|
| 4228 if (protocol != NULL) g_free(protocol); |
|
| 4229 |
|
| 4230 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
|
| 4231 } |
|
| 4232 else if (sd->target == gdk_atom_intern("text/uri-list", FALSE)) { |
|
| 4233 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 4234 gaim_dnd_file_manage(sd, gaim_conversation_get_account(conv), gaim_conversation_get_name(conv)); |
|
| 4235 gtk_drag_finish(dc, TRUE, (dc->action == GDK_ACTION_MOVE), t); |
|
| 4236 } |
|
| 4237 else |
|
| 4238 gtk_drag_finish(dc, FALSE, FALSE, t); |
|
| 4239 } |
|
| 4240 |
|
| 4241 |
|
| 4242 static const GtkTargetEntry te[] = |
|
| 4243 { |
|
| 4244 GTK_IMHTML_DND_TARGETS, |
|
| 4245 {"GAIM_BLIST_NODE", GTK_TARGET_SAME_APP, GTK_IMHTML_DRAG_NUM}, |
|
| 4246 {"application/x-im-contact", 0, GTK_IMHTML_DRAG_NUM + 1} |
|
| 4247 }; |
|
| 4248 |
|
| 4249 static GaimGtkConversation * |
|
| 4250 gaim_gtk_conv_find_gtkconv(GaimConversation * conv) |
|
| 4251 { |
|
| 4252 GaimBuddy *bud = gaim_find_buddy(conv->account, conv->name), *b; |
|
| 4253 GaimContact *c; |
|
| 4254 GaimBlistNode *cn; |
|
| 4255 |
|
| 4256 if (!bud) |
|
| 4257 return NULL; |
|
| 4258 |
|
| 4259 if (!(c = gaim_buddy_get_contact(bud))) |
|
| 4260 return NULL; |
|
| 4261 |
|
| 4262 cn = (GaimBlistNode *)c; |
|
| 4263 for (b = (GaimBuddy *)cn->child; b; b = (GaimBuddy *) ((GaimBlistNode *)b)->next) { |
|
| 4264 GaimConversation *conv; |
|
| 4265 if ((conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, b->name, b->account))) { |
|
| 4266 if (conv->ui_data) |
|
| 4267 return conv->ui_data; |
|
| 4268 } |
|
| 4269 } |
|
| 4270 |
|
| 4271 return NULL; |
|
| 4272 } |
|
| 4273 |
|
| 4274 static void |
|
| 4275 buddy_update_cb(GaimBlistNode *bnode, gpointer null) |
|
| 4276 { |
|
| 4277 GList *list; |
|
| 4278 |
|
| 4279 g_return_if_fail(bnode); |
|
| 4280 g_return_if_fail(GAIM_BLIST_NODE_IS_BUDDY(bnode)); |
|
| 4281 |
|
| 4282 for (list = gaim_gtk_conv_windows_get_list(); list; list = list->next) |
|
| 4283 { |
|
| 4284 GaimGtkWindow *win = list->data; |
|
| 4285 GaimConversation *conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 4286 |
|
| 4287 if (gaim_conversation_get_type(conv) != GAIM_CONV_TYPE_IM) |
|
| 4288 continue; |
|
| 4289 |
|
| 4290 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_MENU); |
|
| 4291 } |
|
| 4292 } |
|
| 4293 |
|
| 4294 /************************************************************************** |
|
| 4295 * Conversation UI operations |
|
| 4296 **************************************************************************/ |
|
| 4297 static void |
|
| 4298 private_gtkconv_new(GaimConversation *conv, gboolean hidden) |
|
| 4299 { |
|
| 4300 GaimGtkConversation *gtkconv; |
|
| 4301 GaimConversationType conv_type = gaim_conversation_get_type(conv); |
|
| 4302 GtkWidget *pane = NULL; |
|
| 4303 GtkWidget *tab_cont; |
|
| 4304 |
|
| 4305 if (conv_type == GAIM_CONV_TYPE_IM && (gtkconv = gaim_gtk_conv_find_gtkconv(conv))) { |
|
| 4306 conv->ui_data = gtkconv; |
|
| 4307 if (!g_list_find(gtkconv->convs, conv)) |
|
| 4308 gtkconv->convs = g_list_prepend(gtkconv->convs, conv); |
|
| 4309 gaim_gtkconv_switch_active_conversation(conv); |
|
| 4310 return; |
|
| 4311 } |
|
| 4312 |
|
| 4313 gtkconv = g_new0(GaimGtkConversation, 1); |
|
| 4314 conv->ui_data = gtkconv; |
|
| 4315 gtkconv->active_conv = conv; |
|
| 4316 gtkconv->convs = g_list_prepend(gtkconv->convs, conv); |
|
| 4317 |
|
| 4318 /* Setup some initial variables. */ |
|
| 4319 gtkconv->sg = gtk_size_group_new(GTK_SIZE_GROUP_BOTH); |
|
| 4320 gtkconv->tooltips = gtk_tooltips_new(); |
|
| 4321 gtkconv->unseen_state = GAIM_UNSEEN_NONE; |
|
| 4322 gtkconv->unseen_count = 0; |
|
| 4323 |
|
| 4324 if (conv_type == GAIM_CONV_TYPE_IM) { |
|
| 4325 gtkconv->u.im = g_malloc0(sizeof(GaimGtkImPane)); |
|
| 4326 |
|
| 4327 pane = setup_im_pane(gtkconv); |
|
| 4328 } else if (conv_type == GAIM_CONV_TYPE_CHAT) { |
|
| 4329 gtkconv->u.chat = g_malloc0(sizeof(GaimGtkChatPane)); |
|
| 4330 pane = setup_chat_pane(gtkconv); |
|
| 4331 } |
|
| 4332 |
|
| 4333 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->imhtml), |
|
| 4334 gtk_imhtml_get_format_functions(GTK_IMHTML(gtkconv->imhtml)) | GTK_IMHTML_IMAGE); |
|
| 4335 |
|
| 4336 if (pane == NULL) { |
|
| 4337 if (conv_type == GAIM_CONV_TYPE_CHAT) |
|
| 4338 g_free(gtkconv->u.chat); |
|
| 4339 else if (conv_type == GAIM_CONV_TYPE_IM) |
|
| 4340 g_free(gtkconv->u.im); |
|
| 4341 |
|
| 4342 g_free(gtkconv); |
|
| 4343 conv->ui_data = NULL; |
|
| 4344 return; |
|
| 4345 } |
|
| 4346 |
|
| 4347 /* Setup drag-and-drop */ |
|
| 4348 gtk_drag_dest_set(pane, |
|
| 4349 GTK_DEST_DEFAULT_MOTION | |
|
| 4350 GTK_DEST_DEFAULT_DROP, |
|
| 4351 te, sizeof(te) / sizeof(GtkTargetEntry), |
|
| 4352 GDK_ACTION_COPY); |
|
| 4353 gtk_drag_dest_set(pane, |
|
| 4354 GTK_DEST_DEFAULT_MOTION | |
|
| 4355 GTK_DEST_DEFAULT_DROP, |
|
| 4356 te, sizeof(te) / sizeof(GtkTargetEntry), |
|
| 4357 GDK_ACTION_COPY); |
|
| 4358 gtk_drag_dest_set(gtkconv->imhtml, 0, |
|
| 4359 te, sizeof(te) / sizeof(GtkTargetEntry), |
|
| 4360 GDK_ACTION_COPY); |
|
| 4361 |
|
| 4362 gtk_drag_dest_set(gtkconv->entry, 0, |
|
| 4363 te, sizeof(te) / sizeof(GtkTargetEntry), |
|
| 4364 GDK_ACTION_COPY); |
|
| 4365 |
|
| 4366 g_signal_connect(G_OBJECT(pane), "drag_data_received", |
|
| 4367 G_CALLBACK(conv_dnd_recv), gtkconv); |
|
| 4368 g_signal_connect(G_OBJECT(gtkconv->imhtml), "drag_data_received", |
|
| 4369 G_CALLBACK(conv_dnd_recv), gtkconv); |
|
| 4370 g_signal_connect(G_OBJECT(gtkconv->entry), "drag_data_received", |
|
| 4371 G_CALLBACK(conv_dnd_recv), gtkconv); |
|
| 4372 |
|
| 4373 /* Setup the container for the tab. */ |
|
| 4374 gtkconv->tab_cont = tab_cont = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 4375 g_object_set_data(G_OBJECT(tab_cont), "GaimGtkConversation", gtkconv); |
|
| 4376 gtk_container_set_border_width(GTK_CONTAINER(tab_cont), GAIM_HIG_BOX_SPACE); |
|
| 4377 gtk_container_add(GTK_CONTAINER(tab_cont), pane); |
|
| 4378 gtk_widget_show(pane); |
|
| 4379 |
|
| 4380 gtkconv->make_sound = TRUE; |
|
| 4381 |
|
| 4382 if (gaim_prefs_get_bool("/gaim/gtk/conversations/show_formatting_toolbar")) |
|
| 4383 gtk_widget_show(gtkconv->toolbar); |
|
| 4384 else |
|
| 4385 gtk_widget_hide(gtkconv->toolbar); |
|
| 4386 |
|
| 4387 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), |
|
| 4388 gaim_prefs_get_bool("/gaim/gtk/conversations/show_timestamps")); |
|
| 4389 gtk_imhtml_set_protocol_name(GTK_IMHTML(gtkconv->imhtml), |
|
| 4390 gaim_account_get_protocol_name(conv->account)); |
|
| 4391 |
|
| 4392 g_signal_connect_swapped(G_OBJECT(pane), "focus", |
|
| 4393 G_CALLBACK(gtk_widget_grab_focus), |
|
| 4394 gtkconv->entry); |
|
| 4395 |
|
| 4396 if (hidden) |
|
| 4397 gaim_gtk_conv_window_add_gtkconv(hidden_convwin, gtkconv); |
|
| 4398 else |
|
| 4399 gaim_gtkconv_placement_place(gtkconv); |
|
| 4400 |
|
| 4401 if (nick_colors == NULL) { |
|
| 4402 nbr_nick_colors = NUM_NICK_COLORS; |
|
| 4403 nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->imhtml)->base[GTK_STATE_NORMAL]); |
|
| 4404 } |
|
| 4405 } |
|
| 4406 |
|
| 4407 static void |
|
| 4408 gaim_gtkconv_new_hidden(GaimConversation *conv) |
|
| 4409 { |
|
| 4410 private_gtkconv_new(conv, TRUE); |
|
| 4411 } |
|
| 4412 |
|
| 4413 void |
|
| 4414 gaim_gtkconv_new(GaimConversation *conv) |
|
| 4415 { |
|
| 4416 private_gtkconv_new(conv, FALSE); |
|
| 4417 } |
|
| 4418 |
|
| 4419 static void |
|
| 4420 received_im_msg_cb(GaimAccount *account, char *sender, char *message, |
|
| 4421 GaimConversation *conv, int flags) |
|
| 4422 { |
|
| 4423 GaimConversationUiOps *ui_ops = gaim_gtk_conversations_get_conv_ui_ops(); |
|
| 4424 if (conv != NULL) |
|
| 4425 return; |
|
| 4426 |
|
| 4427 /* create hidden conv if hide_new pref is always */ |
|
| 4428 if (strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "always") == 0) |
|
| 4429 { |
|
| 4430 ui_ops->create_conversation = gaim_gtkconv_new_hidden; |
|
| 4431 gaim_conversation_new(GAIM_CONV_TYPE_IM, account, sender); |
|
| 4432 ui_ops->create_conversation = gaim_gtkconv_new; |
|
| 4433 return; |
|
| 4434 } |
|
| 4435 |
|
| 4436 /* create hidden conv if hide_new pref is away and account is away */ |
|
| 4437 if (strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "away") == 0 && |
|
| 4438 !gaim_status_is_available(gaim_account_get_active_status(account))) |
|
| 4439 { |
|
| 4440 ui_ops->create_conversation = gaim_gtkconv_new_hidden; |
|
| 4441 gaim_conversation_new(GAIM_CONV_TYPE_IM, account, sender); |
|
| 4442 ui_ops->create_conversation = gaim_gtkconv_new; |
|
| 4443 return; |
|
| 4444 } |
|
| 4445 } |
|
| 4446 |
|
| 4447 static void |
|
| 4448 gaim_gtkconv_destroy(GaimConversation *conv) |
|
| 4449 { |
|
| 4450 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4451 |
|
| 4452 gtkconv->convs = g_list_remove(gtkconv->convs, conv); |
|
| 4453 /* Don't destroy ourselves until all our convos are gone */ |
|
| 4454 if (gtkconv->convs) |
|
| 4455 return; |
|
| 4456 |
|
| 4457 gaim_gtk_conv_window_remove_gtkconv(gtkconv->win, gtkconv); |
|
| 4458 |
|
| 4459 /* If the "Save Conversation" or "Save Icon" dialogs are open then close them */ |
|
| 4460 gaim_request_close_with_handle(conv); |
|
| 4461 |
|
| 4462 gtk_widget_destroy(gtkconv->tab_cont); |
|
| 4463 g_object_unref(gtkconv->tab_cont); |
|
| 4464 |
|
| 4465 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 4466 if (gtkconv->u.im->icon_timer != 0) |
|
| 4467 g_source_remove(gtkconv->u.im->icon_timer); |
|
| 4468 |
|
| 4469 if (gtkconv->u.im->anim != NULL) |
|
| 4470 g_object_unref(G_OBJECT(gtkconv->u.im->anim)); |
|
| 4471 |
|
| 4472 g_free(gtkconv->u.im); |
|
| 4473 } else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 4474 gaim_signals_disconnect_by_handle(gtkconv->u.chat); |
|
| 4475 g_free(gtkconv->u.chat); |
|
| 4476 } |
|
| 4477 |
|
| 4478 gtk_object_sink(GTK_OBJECT(gtkconv->tooltips)); |
|
| 4479 |
|
| 4480 g_free(gtkconv); |
|
| 4481 } |
|
| 4482 |
|
| 4483 static void |
|
| 4484 gaim_gtkconv_write_im(GaimConversation *conv, const char *who, |
|
| 4485 const char *message, GaimMessageFlags flags, |
|
| 4486 time_t mtime) |
|
| 4487 { |
|
| 4488 GaimGtkConversation *gtkconv; |
|
| 4489 |
|
| 4490 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4491 gaim_gtkconv_set_active_conversation(conv); |
|
| 4492 |
|
| 4493 gaim_conversation_write(conv, who, message, flags, mtime); |
|
| 4494 } |
|
| 4495 |
|
| 4496 static void |
|
| 4497 gaim_gtkconv_write_chat(GaimConversation *conv, const char *who, |
|
| 4498 const char *message, GaimMessageFlags flags, time_t mtime) |
|
| 4499 { |
|
| 4500 GaimGtkConversation *gtkconv; |
|
| 4501 |
|
| 4502 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4503 gaim_gtkconv_set_active_conversation(conv); |
|
| 4504 |
|
| 4505 flags |= GAIM_MESSAGE_COLORIZE; |
|
| 4506 |
|
| 4507 gaim_conversation_write(conv, who, message, flags, mtime); |
|
| 4508 } |
|
| 4509 |
|
| 4510 /* The callback for an event on a link tag. */ |
|
| 4511 static gboolean buddytag_event(GtkTextTag *tag, GObject *imhtml, |
|
| 4512 GdkEvent *event, GtkTextIter *arg2, gpointer data) { |
|
| 4513 if (event->type == GDK_BUTTON_PRESS |
|
| 4514 || event->type == GDK_2BUTTON_PRESS) { |
|
| 4515 GdkEventButton *btn_event = (GdkEventButton*) event; |
|
| 4516 GaimConversation *conv = data; |
|
| 4517 char *buddyname; |
|
| 4518 |
|
| 4519 /* strlen("BUDDY ") == 6 */ |
|
| 4520 g_return_val_if_fail((tag->name != NULL) |
|
| 4521 && (strlen(tag->name) > 6), FALSE); |
|
| 4522 |
|
| 4523 buddyname = (tag->name) + 6; |
|
| 4524 |
|
| 4525 if (btn_event->button == 2 |
|
| 4526 && event->type == GDK_2BUTTON_PRESS) { |
|
| 4527 chat_do_info(GAIM_GTK_CONVERSATION(conv), buddyname); |
|
| 4528 |
|
| 4529 return TRUE; |
|
| 4530 } else if (btn_event->button == 3 |
|
| 4531 && event->type == GDK_BUTTON_PRESS) { |
|
| 4532 GtkTextIter start, end; |
|
| 4533 |
|
| 4534 /* we shouldn't display the popup |
|
| 4535 * if the user has selected something: */ |
|
| 4536 if (!gtk_text_buffer_get_selection_bounds( |
|
| 4537 gtk_text_iter_get_buffer(arg2), |
|
| 4538 &start, &end)) { |
|
| 4539 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 4540 GtkWidget *menu = NULL; |
|
| 4541 GaimConnection *gc = |
|
| 4542 gaim_conversation_get_gc(conv); |
|
| 4543 |
|
| 4544 |
|
| 4545 if (gc != NULL) |
|
| 4546 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO( |
|
| 4547 gc->prpl); |
|
| 4548 |
|
| 4549 menu = create_chat_menu(conv, buddyname, |
|
| 4550 prpl_info, gc); |
|
| 4551 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, |
|
| 4552 NULL, GTK_WIDGET(imhtml), |
|
| 4553 btn_event->button, |
|
| 4554 btn_event->time); |
|
| 4555 |
|
| 4556 /* Don't propagate the event any further */ |
|
| 4557 return TRUE; |
|
| 4558 } |
|
| 4559 } |
|
| 4560 } |
|
| 4561 |
|
| 4562 return FALSE; |
|
| 4563 } |
|
| 4564 |
|
| 4565 static GtkTextTag *get_buddy_tag(GaimConversation *conv, const char *who) { |
|
| 4566 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4567 GtkTextTag *buddytag; |
|
| 4568 /* strlen("BUDDY ") == 6 */ |
|
| 4569 gchar str[strlen(who) + 7]; |
|
| 4570 |
|
| 4571 g_snprintf(str, sizeof(str), "BUDDY %s", who); |
|
| 4572 str[sizeof(str)] = '\0'; |
|
| 4573 |
|
| 4574 buddytag = gtk_text_tag_table_lookup( |
|
| 4575 gtk_text_buffer_get_tag_table( |
|
| 4576 GTK_IMHTML(gtkconv->imhtml)->text_buffer), str); |
|
| 4577 |
|
| 4578 if (buddytag == NULL) { |
|
| 4579 buddytag = gtk_text_buffer_create_tag( |
|
| 4580 GTK_IMHTML(gtkconv->imhtml)->text_buffer, str, NULL); |
|
| 4581 |
|
| 4582 g_signal_connect(G_OBJECT(buddytag), "event", |
|
| 4583 G_CALLBACK(buddytag_event), conv); |
|
| 4584 } |
|
| 4585 |
|
| 4586 return buddytag; |
|
| 4587 } |
|
| 4588 |
|
| 4589 static void |
|
| 4590 gaim_gtkconv_write_conv(GaimConversation *conv, const char *name, const char *alias, |
|
| 4591 const char *message, GaimMessageFlags flags, |
|
| 4592 time_t mtime) |
|
| 4593 { |
|
| 4594 GaimGtkConversation *gtkconv; |
|
| 4595 GaimGtkWindow *win; |
|
| 4596 GaimConnection *gc; |
|
| 4597 GaimAccount *account; |
|
| 4598 GaimPluginProtocolInfo *prpl_info; |
|
| 4599 int gtk_font_options = 0; |
|
| 4600 int gtk_font_options_all = 0; |
|
| 4601 int max_scrollback_lines = gaim_prefs_get_int( |
|
| 4602 "/gaim/gtk/conversations/scrollback_lines"); |
|
| 4603 int line_count; |
|
| 4604 char buf2[BUF_LONG]; |
|
| 4605 char *mdate; |
|
| 4606 char color[10]; |
|
| 4607 char *str; |
|
| 4608 char *with_font_tag; |
|
| 4609 char *sml_attrib = NULL; |
|
| 4610 size_t length; |
|
| 4611 GaimConversationType type; |
|
| 4612 char *displaying; |
|
| 4613 gboolean plugin_return; |
|
| 4614 struct tm tm = *(localtime(&mtime)); |
|
| 4615 |
|
| 4616 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4617 |
|
| 4618 if(gaim_prefs_get_bool("/gaim/gtk/conversations/use_smooth_scrolling")) |
|
| 4619 gtk_font_options_all |= GTK_IMHTML_USE_SMOOTHSCROLLING; |
|
| 4620 |
|
| 4621 /* Set the active conversation to the one that just messaged us. */ |
|
| 4622 /* TODO: consider not doing this if the account is offline or something */ |
|
| 4623 gaim_gtkconv_set_active_conversation(conv); |
|
| 4624 type = gaim_conversation_get_type(conv); |
|
| 4625 |
|
| 4626 gc = gaim_conversation_get_gc(conv); |
|
| 4627 account = gaim_conversation_get_account(conv); |
|
| 4628 |
|
| 4629 displaying = g_strdup(message); |
|
| 4630 plugin_return = GPOINTER_TO_INT(gaim_signal_emit_return_1( |
|
| 4631 gaim_gtk_conversations_get_handle(), (type == GAIM_CONV_TYPE_IM ? |
|
| 4632 "displaying-im-msg" : "displaying-chat-msg"), |
|
| 4633 account, conv, &displaying, flags)); |
|
| 4634 if (plugin_return) |
|
| 4635 { |
|
| 4636 g_free(displaying); |
|
| 4637 return; |
|
| 4638 } |
|
| 4639 message = displaying; |
|
| 4640 length = strlen(message) + 1; |
|
| 4641 |
|
| 4642 win = gtkconv->win; |
|
| 4643 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 4644 |
|
| 4645 line_count = gtk_text_buffer_get_line_count( |
|
| 4646 gtk_text_view_get_buffer(GTK_TEXT_VIEW( |
|
| 4647 gtkconv->imhtml))); |
|
| 4648 |
|
| 4649 /* If we're sitting at more than 100 lines more than the |
|
| 4650 max scrollback, trim down to max scrollback */ |
|
| 4651 if (max_scrollback_lines > 0 |
|
| 4652 && line_count > (max_scrollback_lines + 100)) { |
|
| 4653 GtkTextBuffer *text_buffer = gtk_text_view_get_buffer( |
|
| 4654 GTK_TEXT_VIEW(gtkconv->imhtml)); |
|
| 4655 GtkTextIter start, end; |
|
| 4656 |
|
| 4657 gtk_text_buffer_get_start_iter(text_buffer, &start); |
|
| 4658 gtk_text_buffer_get_iter_at_line(text_buffer, &end, |
|
| 4659 (line_count - max_scrollback_lines)); |
|
| 4660 gtk_imhtml_delete(GTK_IMHTML(gtkconv->imhtml), &start, &end); |
|
| 4661 } |
|
| 4662 |
|
| 4663 if (type == GAIM_CONV_TYPE_CHAT) |
|
| 4664 { |
|
| 4665 /* Create anchor for user */ |
|
| 4666 GtkTextIter iter; |
|
| 4667 char *tmp = g_strconcat("user:", name, NULL); |
|
| 4668 |
|
| 4669 gtk_text_buffer_get_end_iter(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), &iter); |
|
| 4670 gtk_text_buffer_create_mark(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)), |
|
| 4671 tmp, &iter, TRUE); |
|
| 4672 g_free(tmp); |
|
| 4673 } |
|
| 4674 |
|
| 4675 if (gtk_text_buffer_get_char_count(gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->imhtml)))) |
|
| 4676 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", gtk_font_options_all); |
|
| 4677 |
|
| 4678 mdate = gaim_signal_emit_return_1(gaim_gtk_conversations_get_handle(), |
|
| 4679 "conversation-timestamp", |
|
| 4680 conv, &tm); |
|
| 4681 if (mdate == NULL) |
|
| 4682 { |
|
| 4683 char buf[64]; |
|
| 4684 |
|
| 4685 if (time(NULL) > mtime + 20*60) /* show date if older than 20 minutes */ |
|
| 4686 strftime(buf, sizeof(buf), "%x %X", &tm); |
|
| 4687 else |
|
| 4688 strftime(buf, sizeof(buf), "%X", &tm); |
|
| 4689 |
|
| 4690 mdate = g_strdup(buf); |
|
| 4691 } |
|
| 4692 |
|
| 4693 if(gc) |
|
| 4694 sml_attrib = g_strdup_printf("sml=\"%s\"", |
|
| 4695 gaim_account_get_protocol_name(conv->account)); |
|
| 4696 |
|
| 4697 gtk_font_options |= GTK_IMHTML_NO_COMMENTS; |
|
| 4698 |
|
| 4699 if ((flags & GAIM_MESSAGE_RECV) && |
|
| 4700 !gaim_prefs_get_bool("/gaim/gtk/conversations/show_incoming_formatting")) |
|
| 4701 gtk_font_options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES | GTK_IMHTML_NO_FORMATTING; |
|
| 4702 |
|
| 4703 /* this is gonna crash one day, I can feel it. */ |
|
| 4704 if (GAIM_PLUGIN_PROTOCOL_INFO(gaim_find_prpl(gaim_account_get_protocol_id(conv->account)))->options & |
|
| 4705 OPT_PROTO_USE_POINTSIZE) { |
|
| 4706 gtk_font_options |= GTK_IMHTML_USE_POINTSIZE; |
|
| 4707 } |
|
| 4708 |
|
| 4709 |
|
| 4710 /* TODO: These colors should not be hardcoded so log.c can use them */ |
|
| 4711 if (flags & GAIM_MESSAGE_SYSTEM) { |
|
| 4712 g_snprintf(buf2, sizeof(buf2), |
|
| 4713 "<FONT %s><FONT SIZE=\"2\"><!--(%s) --></FONT><B>%s</B></FONT>", |
|
| 4714 sml_attrib ? sml_attrib : "", mdate, message); |
|
| 4715 |
|
| 4716 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
|
| 4717 |
|
| 4718 } else if (flags & GAIM_MESSAGE_ERROR) { |
|
| 4719 g_snprintf(buf2, sizeof(buf2), |
|
| 4720 "<FONT COLOR=\"#ff0000\"><FONT %s><FONT SIZE=\"2\"><!--(%s) --></FONT><B>%s</B></FONT></FONT>", |
|
| 4721 sml_attrib ? sml_attrib : "", mdate, message); |
|
| 4722 |
|
| 4723 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
|
| 4724 |
|
| 4725 } else if (flags & GAIM_MESSAGE_NO_LOG) { |
|
| 4726 g_snprintf(buf2, BUF_LONG, |
|
| 4727 "<B><FONT %s COLOR=\"#777777\">%s</FONT></B>", |
|
| 4728 sml_attrib ? sml_attrib : "", message); |
|
| 4729 |
|
| 4730 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
|
| 4731 } else if (flags & GAIM_MESSAGE_RAW) { |
|
| 4732 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), message, gtk_font_options_all); |
|
| 4733 } else { |
|
| 4734 char *new_message = g_memdup(message, length); |
|
| 4735 char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup("")); |
|
| 4736 /* The initial offset is to deal with |
|
| 4737 * escaped entities making the string longer */ |
|
| 4738 int tag_start_offset = alias ? (strlen(alias_escaped) - strlen(alias)) : 0; |
|
| 4739 int tag_end_offset = 0; |
|
| 4740 GtkSmileyTree *tree = NULL; |
|
| 4741 GHashTable *smiley_data = NULL; |
|
| 4742 |
|
| 4743 if (flags & GAIM_MESSAGE_SEND) |
|
| 4744 { |
|
| 4745 /* Temporarily revert to the original smiley-data to avoid showing up |
|
| 4746 * custom smileys of the buddy when sending message |
|
| 4747 */ |
|
| 4748 tree = GTK_IMHTML(gtkconv->imhtml)->default_smilies; |
|
| 4749 GTK_IMHTML(gtkconv->imhtml)->default_smilies = |
|
| 4750 GTK_IMHTML(gtkconv->entry)->default_smilies; |
|
| 4751 smiley_data = GTK_IMHTML(gtkconv->imhtml)->smiley_data; |
|
| 4752 GTK_IMHTML(gtkconv->imhtml)->smiley_data = GTK_IMHTML(gtkconv->entry)->smiley_data; |
|
| 4753 } |
|
| 4754 |
|
| 4755 if (flags & GAIM_MESSAGE_WHISPER) { |
|
| 4756 str = g_malloc(1024); |
|
| 4757 |
|
| 4758 /* If we're whispering, it's not an autoresponse. */ |
|
| 4759 if (gaim_message_meify(new_message, -1 )) { |
|
| 4760 g_snprintf(str, 1024, "***%s", alias_escaped); |
|
| 4761 strcpy(color, "#6C2585"); |
|
| 4762 tag_start_offset += 3; |
|
| 4763 } |
|
| 4764 else { |
|
| 4765 g_snprintf(str, 1024, "*%s*:", alias_escaped); |
|
| 4766 tag_start_offset += 1; |
|
| 4767 tag_end_offset = 2; |
|
| 4768 strcpy(color, "#00FF00"); |
|
| 4769 } |
|
| 4770 } |
|
| 4771 else { |
|
| 4772 if (gaim_message_meify(new_message, -1)) { |
|
| 4773 str = g_malloc(1024); |
|
| 4774 |
|
| 4775 if (flags & GAIM_MESSAGE_AUTO_RESP) { |
|
| 4776 g_snprintf(str, 1024, "%s ***%s", AUTO_RESPONSE, alias_escaped); |
|
| 4777 tag_start_offset += 4 |
|
| 4778 + strlen(AUTO_RESPONSE); |
|
| 4779 } else { |
|
| 4780 g_snprintf(str, 1024, "***%s", alias_escaped); |
|
| 4781 tag_start_offset += 3; |
|
| 4782 } |
|
| 4783 |
|
| 4784 if (flags & GAIM_MESSAGE_NICK) |
|
| 4785 strcpy(color, HIGHLIGHT_COLOR); |
|
| 4786 else |
|
| 4787 strcpy(color, "#062585"); |
|
| 4788 } |
|
| 4789 else { |
|
| 4790 str = g_malloc(1024); |
|
| 4791 if (flags & GAIM_MESSAGE_AUTO_RESP) { |
|
| 4792 g_snprintf(str, 1024, "%s %s", alias_escaped, AUTO_RESPONSE); |
|
| 4793 tag_start_offset += 1 |
|
| 4794 + strlen(AUTO_RESPONSE); |
|
| 4795 } else { |
|
| 4796 g_snprintf(str, 1024, "%s:", alias_escaped); |
|
| 4797 tag_end_offset = 1; |
|
| 4798 } |
|
| 4799 if (flags & GAIM_MESSAGE_NICK) |
|
| 4800 strcpy(color, HIGHLIGHT_COLOR); |
|
| 4801 else if (flags & GAIM_MESSAGE_RECV) { |
|
| 4802 if (flags & GAIM_MESSAGE_COLORIZE) { |
|
| 4803 GdkColor *col = get_nick_color(gtkconv, name); |
|
| 4804 |
|
| 4805 g_snprintf(color, sizeof(color), "#%02X%02X%02X", |
|
| 4806 col->red >> 8, col->green >> 8, col->blue >> 8); |
|
| 4807 } else |
|
| 4808 strcpy(color, RECV_COLOR); |
|
| 4809 } |
|
| 4810 else if (flags & GAIM_MESSAGE_SEND) |
|
| 4811 strcpy(color, SEND_COLOR); |
|
| 4812 else { |
|
| 4813 gaim_debug_error("gtkconv", "message missing flags\n"); |
|
| 4814 strcpy(color, "#000000"); |
|
| 4815 } |
|
| 4816 } |
|
| 4817 } |
|
| 4818 |
|
| 4819 if(alias_escaped) |
|
| 4820 g_free(alias_escaped); |
|
| 4821 |
|
| 4822 /* Are we in a chat where we can tell which users are buddies? */ |
|
| 4823 if (!(prpl_info->options & OPT_PROTO_UNIQUE_CHATNAME) && |
|
| 4824 gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 4825 |
|
| 4826 /* Bold buddies to make them stand out from non-buddies. */ |
|
| 4827 if (flags & GAIM_MESSAGE_SEND || |
|
| 4828 flags & GAIM_MESSAGE_NICK || |
|
| 4829 gaim_find_buddy(account, name) != NULL) { |
|
| 4830 g_snprintf(buf2, BUF_LONG, |
|
| 4831 "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--(%s) --></FONT>" |
|
| 4832 "<B>%s</B></FONT> ", |
|
| 4833 color, sml_attrib ? sml_attrib : "", mdate, str); |
|
| 4834 } else { |
|
| 4835 g_snprintf(buf2, BUF_LONG, |
|
| 4836 "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--(%s) --></FONT>" |
|
| 4837 "%s</FONT> ", |
|
| 4838 color, sml_attrib ? sml_attrib : "", mdate, str); |
|
| 4839 |
|
| 4840 } |
|
| 4841 } else { |
|
| 4842 /* Bold everyone's name to make the name stand out from the message. */ |
|
| 4843 g_snprintf(buf2, BUF_LONG, |
|
| 4844 "<FONT COLOR=\"%s\" %s><FONT SIZE=\"2\"><!--(%s) --></FONT>" |
|
| 4845 "<B>%s</B></FONT> ", |
|
| 4846 color, sml_attrib ? sml_attrib : "", mdate, str); |
|
| 4847 } |
|
| 4848 |
|
| 4849 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, gtk_font_options_all); |
|
| 4850 |
|
| 4851 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT && |
|
| 4852 !(flags & GAIM_MESSAGE_SEND)) { |
|
| 4853 |
|
| 4854 GtkTextIter start, end; |
|
| 4855 GtkTextTag *buddytag = get_buddy_tag(conv, name); |
|
| 4856 |
|
| 4857 gtk_text_buffer_get_end_iter( |
|
| 4858 GTK_IMHTML(gtkconv->imhtml)->text_buffer, |
|
| 4859 &end); |
|
| 4860 gtk_text_iter_backward_chars(&end, |
|
| 4861 tag_end_offset + 1); |
|
| 4862 |
|
| 4863 gtk_text_buffer_get_end_iter( |
|
| 4864 GTK_IMHTML(gtkconv->imhtml)->text_buffer, |
|
| 4865 &start); |
|
| 4866 gtk_text_iter_backward_chars(&start, |
|
| 4867 strlen(str) + 1 - tag_start_offset); |
|
| 4868 |
|
| 4869 gtk_text_buffer_apply_tag( |
|
| 4870 GTK_IMHTML(gtkconv->imhtml)->text_buffer, |
|
| 4871 buddytag, &start, &end); |
|
| 4872 } |
|
| 4873 |
|
| 4874 g_free(str); |
|
| 4875 |
|
| 4876 if(gc){ |
|
| 4877 char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : ""); |
|
| 4878 char *post = "</font>"; |
|
| 4879 int pre_len = strlen(pre); |
|
| 4880 int post_len = strlen(post); |
|
| 4881 |
|
| 4882 with_font_tag = g_malloc(length + pre_len + post_len + 1); |
|
| 4883 |
|
| 4884 strcpy(with_font_tag, pre); |
|
| 4885 memcpy(with_font_tag + pre_len, new_message, length); |
|
| 4886 strcpy(with_font_tag + pre_len + length, post); |
|
| 4887 |
|
| 4888 length += pre_len + post_len; |
|
| 4889 g_free(pre); |
|
| 4890 } |
|
| 4891 else |
|
| 4892 with_font_tag = g_memdup(new_message, length); |
|
| 4893 |
|
| 4894 gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), |
|
| 4895 with_font_tag, gtk_font_options | gtk_font_options_all); |
|
| 4896 |
|
| 4897 if (flags & GAIM_MESSAGE_SEND) |
|
| 4898 { |
|
| 4899 /* Restore the smiley-data */ |
|
| 4900 GTK_IMHTML(gtkconv->imhtml)->default_smilies = tree; |
|
| 4901 GTK_IMHTML(gtkconv->imhtml)->smiley_data = smiley_data; |
|
| 4902 } |
|
| 4903 |
|
| 4904 g_free(with_font_tag); |
|
| 4905 g_free(new_message); |
|
| 4906 } |
|
| 4907 |
|
| 4908 g_free(mdate); |
|
| 4909 g_free(sml_attrib); |
|
| 4910 |
|
| 4911 /* Tab highlighting stuff */ |
|
| 4912 if (!gaim_gtkconv_has_focus(conv)) |
|
| 4913 { |
|
| 4914 GaimUnseenState unseen = GAIM_UNSEEN_NONE; |
|
| 4915 |
|
| 4916 if ((flags & GAIM_MESSAGE_NICK) == GAIM_MESSAGE_NICK) |
|
| 4917 unseen = GAIM_UNSEEN_NICK; |
|
| 4918 else if (((flags & GAIM_MESSAGE_SYSTEM) == GAIM_MESSAGE_SYSTEM) || |
|
| 4919 ((flags & GAIM_MESSAGE_ERROR) == GAIM_MESSAGE_ERROR)) |
|
| 4920 unseen = GAIM_UNSEEN_EVENT; |
|
| 4921 else if ((flags & GAIM_MESSAGE_NO_LOG) == GAIM_MESSAGE_NO_LOG) |
|
| 4922 unseen = GAIM_UNSEEN_NO_LOG; |
|
| 4923 else |
|
| 4924 unseen = GAIM_UNSEEN_TEXT; |
|
| 4925 |
|
| 4926 gtkconv_set_unseen(gtkconv, unseen); |
|
| 4927 } |
|
| 4928 |
|
| 4929 gaim_signal_emit(gaim_gtk_conversations_get_handle(), |
|
| 4930 (type == GAIM_CONV_TYPE_IM ? "displayed-im-msg" : "displayed-chat-msg"), |
|
| 4931 account, conv, message, flags); |
|
| 4932 g_free(displaying); |
|
| 4933 } |
|
| 4934 |
|
| 4935 static void |
|
| 4936 gaim_gtkconv_chat_add_users(GaimConversation *conv, GList *users, GList *flags, GList *aliases, gboolean new_arrivals) |
|
| 4937 { |
|
| 4938 GaimConvChat *chat; |
|
| 4939 GaimGtkConversation *gtkconv; |
|
| 4940 GaimGtkChatPane *gtkchat; |
|
| 4941 GList *l; |
|
| 4942 GList *ll; |
|
| 4943 GList *lll; |
|
| 4944 char tmp[BUF_LONG]; |
|
| 4945 int num_users; |
|
| 4946 |
|
| 4947 chat = GAIM_CONV_CHAT(conv); |
|
| 4948 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4949 gtkchat = gtkconv->u.chat; |
|
| 4950 |
|
| 4951 num_users = g_list_length(gaim_conv_chat_get_users(chat)); |
|
| 4952 |
|
| 4953 g_snprintf(tmp, sizeof(tmp), |
|
| 4954 ngettext("%d person in room", "%d people in room", |
|
| 4955 num_users), |
|
| 4956 num_users); |
|
| 4957 |
|
| 4958 gtk_label_set_text(GTK_LABEL(gtkchat->count), tmp); |
|
| 4959 |
|
| 4960 l = users; |
|
| 4961 ll = flags; |
|
| 4962 lll = aliases; |
|
| 4963 while (l != NULL && ll != NULL && lll != NULL) { |
|
| 4964 add_chat_buddy_common(conv, (const char *)l->data, GPOINTER_TO_INT(ll->data), (const char *)lll->data, NULL); |
|
| 4965 l = l->next; |
|
| 4966 ll = ll->next; |
|
| 4967 lll = lll->next; |
|
| 4968 } |
|
| 4969 } |
|
| 4970 |
|
| 4971 static void |
|
| 4972 gaim_gtkconv_chat_rename_user(GaimConversation *conv, const char *old_name, |
|
| 4973 const char *new_name, const char *new_alias) |
|
| 4974 { |
|
| 4975 GaimConvChat *chat; |
|
| 4976 GaimGtkConversation *gtkconv; |
|
| 4977 GaimGtkChatPane *gtkchat; |
|
| 4978 GaimConvChatBuddyFlags flags; |
|
| 4979 GtkTreeIter iter; |
|
| 4980 GtkTreeModel *model; |
|
| 4981 int f = 1; |
|
| 4982 |
|
| 4983 chat = GAIM_CONV_CHAT(conv); |
|
| 4984 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 4985 gtkchat = gtkconv->u.chat; |
|
| 4986 |
|
| 4987 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 4988 |
|
| 4989 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) |
|
| 4990 return; |
|
| 4991 |
|
| 4992 while (f != 0) { |
|
| 4993 char *val; |
|
| 4994 |
|
| 4995 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &val, CHAT_USERS_FLAGS_COLUMN, &flags, -1); |
|
| 4996 |
|
| 4997 if (!gaim_utf8_strcasecmp(old_name, val)) { |
|
| 4998 gtk_list_store_remove(GTK_LIST_STORE(model), &iter); |
|
| 4999 g_free(val); |
|
| 5000 break; |
|
| 5001 } |
|
| 5002 |
|
| 5003 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); |
|
| 5004 |
|
| 5005 g_free(val); |
|
| 5006 } |
|
| 5007 |
|
| 5008 if (!gaim_conv_chat_find_user(chat, old_name)) |
|
| 5009 return; |
|
| 5010 |
|
| 5011 g_return_if_fail(new_alias != NULL); |
|
| 5012 |
|
| 5013 add_chat_buddy_common(conv, new_name, flags, new_alias, old_name); |
|
| 5014 } |
|
| 5015 |
|
| 5016 static void |
|
| 5017 gaim_gtkconv_chat_remove_users(GaimConversation *conv, GList *users) |
|
| 5018 { |
|
| 5019 GaimConvChat *chat; |
|
| 5020 GaimGtkConversation *gtkconv; |
|
| 5021 GaimGtkChatPane *gtkchat; |
|
| 5022 GtkTreeIter iter; |
|
| 5023 GtkTreeModel *model; |
|
| 5024 GList *l; |
|
| 5025 char tmp[BUF_LONG]; |
|
| 5026 int num_users; |
|
| 5027 gboolean f; |
|
| 5028 |
|
| 5029 chat = GAIM_CONV_CHAT(conv); |
|
| 5030 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5031 gtkchat = gtkconv->u.chat; |
|
| 5032 |
|
| 5033 num_users = g_list_length(gaim_conv_chat_get_users(chat)); |
|
| 5034 |
|
| 5035 for (l = users; l != NULL; l = l->next) { |
|
| 5036 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 5037 |
|
| 5038 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) |
|
| 5039 continue; |
|
| 5040 |
|
| 5041 do { |
|
| 5042 char *val; |
|
| 5043 |
|
| 5044 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, |
|
| 5045 CHAT_USERS_NAME_COLUMN, &val, -1); |
|
| 5046 |
|
| 5047 if (!gaim_utf8_strcasecmp((char *)l->data, val)) { |
|
| 5048 #if GTK_CHECK_VERSION(2,2,0) |
|
| 5049 f = gtk_list_store_remove(GTK_LIST_STORE(model), &iter); |
|
| 5050 #else |
|
| 5051 gtk_list_store_remove(GTK_LIST_STORE(model), &iter); |
|
| 5052 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); |
|
| 5053 #endif |
|
| 5054 } |
|
| 5055 else |
|
| 5056 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); |
|
| 5057 |
|
| 5058 g_free(val); |
|
| 5059 } while (f); |
|
| 5060 } |
|
| 5061 |
|
| 5062 g_snprintf(tmp, sizeof(tmp), |
|
| 5063 ngettext("%d person in room", "%d people in room", |
|
| 5064 num_users), num_users); |
|
| 5065 |
|
| 5066 gtk_label_set_text(GTK_LABEL(gtkchat->count), tmp); |
|
| 5067 } |
|
| 5068 |
|
| 5069 static void |
|
| 5070 gaim_gtkconv_chat_update_user(GaimConversation *conv, const char *user) |
|
| 5071 { |
|
| 5072 GaimConvChat *chat; |
|
| 5073 GaimConvChatBuddyFlags flags; |
|
| 5074 GaimGtkConversation *gtkconv; |
|
| 5075 GaimGtkChatPane *gtkchat; |
|
| 5076 GtkTreeIter iter; |
|
| 5077 GtkTreeModel *model; |
|
| 5078 int f = 1; |
|
| 5079 char *alias = NULL; |
|
| 5080 |
|
| 5081 chat = GAIM_CONV_CHAT(conv); |
|
| 5082 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5083 gtkchat = gtkconv->u.chat; |
|
| 5084 |
|
| 5085 model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkchat->list)); |
|
| 5086 |
|
| 5087 if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter)) |
|
| 5088 return; |
|
| 5089 |
|
| 5090 while (f != 0) { |
|
| 5091 char *val; |
|
| 5092 |
|
| 5093 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &val, -1); |
|
| 5094 |
|
| 5095 if (!gaim_utf8_strcasecmp(user, val)) { |
|
| 5096 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_ALIAS_COLUMN, &alias, -1); |
|
| 5097 gtk_list_store_remove(GTK_LIST_STORE(model), &iter); |
|
| 5098 g_free(val); |
|
| 5099 break; |
|
| 5100 } |
|
| 5101 |
|
| 5102 f = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter); |
|
| 5103 |
|
| 5104 g_free(val); |
|
| 5105 } |
|
| 5106 |
|
| 5107 if (!gaim_conv_chat_find_user(chat, user)) |
|
| 5108 { |
|
| 5109 g_free(alias); |
|
| 5110 return; |
|
| 5111 } |
|
| 5112 |
|
| 5113 g_return_if_fail(alias != NULL); |
|
| 5114 |
|
| 5115 flags = gaim_conv_chat_user_get_flags(chat, user); |
|
| 5116 |
|
| 5117 add_chat_buddy_common(conv, user, flags, alias, NULL); |
|
| 5118 g_free(alias); |
|
| 5119 } |
|
| 5120 |
|
| 5121 gboolean |
|
| 5122 gaim_gtkconv_has_focus(GaimConversation *conv) |
|
| 5123 { |
|
| 5124 GaimGtkConversation *gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5125 GaimGtkWindow *win; |
|
| 5126 gboolean has_focus; |
|
| 5127 |
|
| 5128 win = gtkconv->win; |
|
| 5129 |
|
| 5130 g_object_get(G_OBJECT(win->window), "has-toplevel-focus", &has_focus, NULL); |
|
| 5131 |
|
| 5132 if (has_focus && gaim_gtk_conv_window_is_active_conversation(conv)) |
|
| 5133 return TRUE; |
|
| 5134 |
|
| 5135 return FALSE; |
|
| 5136 } |
|
| 5137 |
|
| 5138 static void gaim_gtkconv_custom_smiley_allocated(GdkPixbufLoader *loader, gpointer user_data) |
|
| 5139 { |
|
| 5140 GtkIMHtmlSmiley *smiley; |
|
| 5141 |
|
| 5142 smiley = (GtkIMHtmlSmiley *)user_data; |
|
| 5143 smiley->icon = gdk_pixbuf_loader_get_animation(loader); |
|
| 5144 |
|
| 5145 if (smiley->icon) |
|
| 5146 g_object_ref(G_OBJECT(smiley->icon)); |
|
| 5147 #ifdef DEBUG_CUSTOM_SMILEY |
|
| 5148 gaim_debug_info("custom-smiley", "gaim_gtkconv_custom_smiley_allocated(): got GdkPixbufAnimation %p for smiley '%s'\n", smiley->icon, smiley->smile); |
|
| 5149 #endif |
|
| 5150 } |
|
| 5151 |
|
| 5152 static void gaim_gtkconv_custom_smiley_closed(GdkPixbufLoader *loader, gpointer user_data) |
|
| 5153 { |
|
| 5154 GtkIMHtmlSmiley *smiley; |
|
| 5155 GtkWidget *icon = NULL; |
|
| 5156 GtkTextChildAnchor *anchor = NULL; |
|
| 5157 GSList *current = NULL; |
|
| 5158 |
|
| 5159 smiley = (GtkIMHtmlSmiley *)user_data; |
|
| 5160 if (!smiley->imhtml) { |
|
| 5161 #ifdef DEBUG_CUSTOM_SMILEY |
|
| 5162 gaim_debug_error("custom-smiley", "gaim_gtkconv_custom_smiley_closed(): orphan smiley found: %p\n", smiley); |
|
| 5163 #endif |
|
| 5164 g_object_unref(G_OBJECT(loader)); |
|
| 5165 smiley->loader = NULL; |
|
| 5166 return; |
|
| 5167 } |
|
| 5168 |
|
| 5169 for (current = smiley->anchors; current; current = g_slist_next(current)) { |
|
| 5170 |
|
| 5171 icon = gtk_image_new_from_animation(smiley->icon); |
|
| 5172 |
|
| 5173 #ifdef DEBUG_CUSTOM_SMILEY |
|
| 5174 gaim_debug_info("custom-smiley", "gaim_gtkconv_custom_smiley_closed(): got GtkImage %p from GtkPixbufAnimation %p for smiley '%s'\n", |
|
| 5175 icon, smiley->icon, smiley->smile); |
|
| 5176 #endif |
|
| 5177 if (icon) { |
|
| 5178 gtk_widget_show(icon); |
|
| 5179 |
|
| 5180 anchor = GTK_TEXT_CHILD_ANCHOR(current->data); |
|
| 5181 |
|
| 5182 g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_plaintext", g_strdup(gaim_unescape_html(smiley->smile)), g_free); |
|
| 5183 g_object_set_data_full(G_OBJECT(anchor), "gtkimhtml_htmltext", g_strdup(smiley->smile), g_free); |
|
| 5184 |
|
| 5185 if (smiley->imhtml) |
|
| 5186 gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(smiley->imhtml), icon, anchor); |
|
| 5187 } |
|
| 5188 |
|
| 5189 } |
|
| 5190 |
|
| 5191 g_slist_free(smiley->anchors); |
|
| 5192 smiley->anchors = NULL; |
|
| 5193 |
|
| 5194 g_object_unref(G_OBJECT(loader)); |
|
| 5195 smiley->loader = NULL; |
|
| 5196 } |
|
| 5197 |
|
| 5198 static gboolean |
|
| 5199 add_custom_smiley_for_imhtml(GtkIMHtml *imhtml, const char *sml, const char *smile) |
|
| 5200 { |
|
| 5201 GtkIMHtmlSmiley *smiley; |
|
| 5202 GdkPixbufLoader *loader; |
|
| 5203 |
|
| 5204 smiley = gtk_imhtml_smiley_get(imhtml, sml, smile); |
|
| 5205 |
|
| 5206 if (smiley) { |
|
| 5207 |
|
| 5208 if (!(smiley->flags & GTK_IMHTML_SMILEY_CUSTOM)) { |
|
| 5209 return FALSE; |
|
| 5210 } |
|
| 5211 |
|
| 5212 /* Close the old GdkPixbufAnimation, then create a new one for |
|
| 5213 * the smiley we are about to receive */ |
|
| 5214 g_object_unref(G_OBJECT(smiley->icon)); |
|
| 5215 |
|
| 5216 /* XXX: Is it necessary to _unref the loader first? */ |
|
| 5217 smiley->loader = gdk_pixbuf_loader_new(); |
|
| 5218 smiley->icon = NULL; |
|
| 5219 |
|
| 5220 g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(gaim_gtkconv_custom_smiley_allocated), smiley); |
|
| 5221 g_signal_connect(smiley->loader, "closed", G_CALLBACK(gaim_gtkconv_custom_smiley_closed), smiley); |
|
| 5222 |
|
| 5223 return TRUE; |
|
| 5224 } |
|
| 5225 |
|
| 5226 loader = gdk_pixbuf_loader_new(); |
|
| 5227 |
|
| 5228 /* this is wrong, this file ought not call g_new on GtkIMHtmlSmiley */ |
|
| 5229 /* Let gtk_imhtml have a gtk_imhtml_smiley_new function, and let |
|
| 5230 GtkIMHtmlSmiley by opaque */ |
|
| 5231 smiley = g_new0(GtkIMHtmlSmiley, 1); |
|
| 5232 smiley->file = NULL; |
|
| 5233 smiley->smile = g_strdup(smile); |
|
| 5234 smiley->loader = loader; |
|
| 5235 smiley->flags = smiley->flags | GTK_IMHTML_SMILEY_CUSTOM; |
|
| 5236 |
|
| 5237 g_signal_connect(smiley->loader, "area_prepared", G_CALLBACK(gaim_gtkconv_custom_smiley_allocated), smiley); |
|
| 5238 g_signal_connect(smiley->loader, "closed", G_CALLBACK(gaim_gtkconv_custom_smiley_closed), smiley); |
|
| 5239 |
|
| 5240 gtk_imhtml_associate_smiley(imhtml, sml, smiley); |
|
| 5241 |
|
| 5242 return TRUE; |
|
| 5243 } |
|
| 5244 |
|
| 5245 static gboolean |
|
| 5246 gaim_gtkconv_custom_smiley_add(GaimConversation *conv, const char *smile, gboolean remote) |
|
| 5247 { |
|
| 5248 GaimGtkConversation *gtkconv; |
|
| 5249 struct smiley_list *list; |
|
| 5250 const char *sml = NULL, *conv_sml; |
|
| 5251 |
|
| 5252 if (!conv || !smile || !*smile) { |
|
| 5253 return FALSE; |
|
| 5254 } |
|
| 5255 |
|
| 5256 /* If smileys are off, return false */ |
|
| 5257 if (gaim_gtkthemes_smileys_disabled()) |
|
| 5258 return FALSE; |
|
| 5259 |
|
| 5260 /* If possible add this smiley to the current theme. |
|
| 5261 * The addition is only temporary: custom smilies aren't saved to disk. */ |
|
| 5262 conv_sml = gaim_account_get_protocol_name(conv->account); |
|
| 5263 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5264 |
|
| 5265 for (list = (struct smiley_list *)current_smiley_theme->list; list; list = list->next) { |
|
| 5266 if (!strcmp(list->sml, conv_sml)) { |
|
| 5267 sml = list->sml; |
|
| 5268 break; |
|
| 5269 } |
|
| 5270 } |
|
| 5271 |
|
| 5272 if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->imhtml), sml, smile)) |
|
| 5273 return FALSE; |
|
| 5274 |
|
| 5275 if (!remote) /* If it's a local custom smiley, then add it for the entry */ |
|
| 5276 if (!add_custom_smiley_for_imhtml(GTK_IMHTML(gtkconv->entry), sml, smile)) |
|
| 5277 return FALSE; |
|
| 5278 |
|
| 5279 return TRUE; |
|
| 5280 } |
|
| 5281 |
|
| 5282 static void |
|
| 5283 gaim_gtkconv_custom_smiley_write(GaimConversation *conv, const char *smile, |
|
| 5284 const guchar *data, gsize size) |
|
| 5285 { |
|
| 5286 GaimGtkConversation *gtkconv; |
|
| 5287 GtkIMHtmlSmiley *smiley; |
|
| 5288 GdkPixbufLoader *loader; |
|
| 5289 const char *sml; |
|
| 5290 |
|
| 5291 sml = gaim_account_get_protocol_name(conv->account); |
|
| 5292 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5293 smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile); |
|
| 5294 |
|
| 5295 if (!smiley) |
|
| 5296 return; |
|
| 5297 |
|
| 5298 loader = smiley->loader; |
|
| 5299 if (!loader) |
|
| 5300 return; |
|
| 5301 |
|
| 5302 gdk_pixbuf_loader_write(loader, data, size, NULL); |
|
| 5303 } |
|
| 5304 |
|
| 5305 static void |
|
| 5306 gaim_gtkconv_custom_smiley_close(GaimConversation *conv, const char *smile) |
|
| 5307 { |
|
| 5308 GaimGtkConversation *gtkconv; |
|
| 5309 GtkIMHtmlSmiley *smiley; |
|
| 5310 GdkPixbufLoader *loader; |
|
| 5311 const char *sml; |
|
| 5312 |
|
| 5313 g_return_if_fail(conv != NULL); |
|
| 5314 g_return_if_fail(smile != NULL); |
|
| 5315 |
|
| 5316 sml = gaim_account_get_protocol_name(conv->account); |
|
| 5317 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5318 smiley = gtk_imhtml_smiley_get(GTK_IMHTML(gtkconv->imhtml), sml, smile); |
|
| 5319 |
|
| 5320 if (!smiley) |
|
| 5321 return; |
|
| 5322 |
|
| 5323 loader = smiley->loader; |
|
| 5324 |
|
| 5325 if (!loader) |
|
| 5326 return; |
|
| 5327 |
|
| 5328 |
|
| 5329 |
|
| 5330 gaim_debug_info("gtkconv", "About to close the smiley pixbuf\n"); |
|
| 5331 |
|
| 5332 gdk_pixbuf_loader_close(loader, NULL); |
|
| 5333 |
|
| 5334 } |
|
| 5335 |
|
| 5336 /* |
|
| 5337 * Makes sure all the menu items and all the buttons are hidden/shown and |
|
| 5338 * sensitive/insensitive. This is called after changing tabs and when an |
|
| 5339 * account signs on or off. |
|
| 5340 */ |
|
| 5341 static void |
|
| 5342 gray_stuff_out(GaimGtkConversation *gtkconv) |
|
| 5343 { |
|
| 5344 GaimGtkWindow *win; |
|
| 5345 GaimConversation *conv = gtkconv->active_conv; |
|
| 5346 GaimConnection *gc; |
|
| 5347 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 5348 GdkPixbuf *window_icon = NULL; |
|
| 5349 GtkIMHtmlButtons buttons; |
|
| 5350 GaimAccount *account; |
|
| 5351 |
|
| 5352 win = gaim_gtkconv_get_window(gtkconv); |
|
| 5353 gc = gaim_conversation_get_gc(conv); |
|
| 5354 account = gaim_conversation_get_account(conv); |
|
| 5355 |
|
| 5356 if (gc != NULL) |
|
| 5357 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); |
|
| 5358 |
|
| 5359 if (win->menu.send_to != NULL) |
|
| 5360 update_send_to_selection(win); |
|
| 5361 |
|
| 5362 /* |
|
| 5363 * Handle hiding and showing stuff based on what type of conv this is. |
|
| 5364 * Stuff that Gaim IMs support in general should be shown for IM |
|
| 5365 * conversations. Stuff that Gaim chats support in general should be |
|
| 5366 * shown for chat conversations. It doesn't matter whether the PRPL |
|
| 5367 * supports it or not--that only affects if the button or menu item |
|
| 5368 * is sensitive or not. |
|
| 5369 */ |
|
| 5370 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) { |
|
| 5371 /* Show stuff that applies to IMs, hide stuff that applies to chats */ |
|
| 5372 |
|
| 5373 /* Deal with menu items */ |
|
| 5374 gtk_widget_show(win->menu.view_log); |
|
| 5375 gtk_widget_show(win->menu.send_file); |
|
| 5376 gtk_widget_show(win->menu.add_pounce); |
|
| 5377 gtk_widget_show(win->menu.get_info); |
|
| 5378 gtk_widget_hide(win->menu.invite); |
|
| 5379 gtk_widget_show(win->menu.alias); |
|
| 5380 gtk_widget_show(win->menu.block); |
|
| 5381 |
|
| 5382 if (gaim_find_buddy(account, gaim_conversation_get_name(conv)) == NULL) { |
|
| 5383 gtk_widget_show(win->menu.add); |
|
| 5384 gtk_widget_hide(win->menu.remove); |
|
| 5385 } else { |
|
| 5386 gtk_widget_show(win->menu.remove); |
|
| 5387 gtk_widget_hide(win->menu.add); |
|
| 5388 } |
|
| 5389 |
|
| 5390 gtk_widget_show(win->menu.insert_link); |
|
| 5391 gtk_widget_show(win->menu.insert_image); |
|
| 5392 gtk_widget_show(win->menu.show_icon); |
|
| 5393 } else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 5394 /* Show stuff that applies to Chats, hide stuff that applies to IMs */ |
|
| 5395 |
|
| 5396 /* Deal with menu items */ |
|
| 5397 gtk_widget_show(win->menu.view_log); |
|
| 5398 gtk_widget_hide(win->menu.send_file); |
|
| 5399 gtk_widget_hide(win->menu.add_pounce); |
|
| 5400 gtk_widget_hide(win->menu.get_info); |
|
| 5401 gtk_widget_show(win->menu.invite); |
|
| 5402 gtk_widget_show(win->menu.alias); |
|
| 5403 gtk_widget_hide(win->menu.block); |
|
| 5404 gtk_widget_hide(win->menu.show_icon); |
|
| 5405 |
|
| 5406 if (gaim_blist_find_chat(account, gaim_conversation_get_name(conv)) == NULL) { |
|
| 5407 /* If the chat is NOT in the buddy list */ |
|
| 5408 gtk_widget_show(win->menu.add); |
|
| 5409 gtk_widget_hide(win->menu.remove); |
|
| 5410 } else { |
|
| 5411 /* If the chat IS in the buddy list */ |
|
| 5412 gtk_widget_hide(win->menu.add); |
|
| 5413 gtk_widget_show(win->menu.remove); |
|
| 5414 } |
|
| 5415 |
|
| 5416 gtk_widget_show(win->menu.insert_link); |
|
| 5417 gtk_widget_hide(win->menu.insert_image); |
|
| 5418 } |
|
| 5419 |
|
| 5420 /* |
|
| 5421 * Handle graying stuff out based on whether an account is connected |
|
| 5422 * and what features that account supports. |
|
| 5423 */ |
|
| 5424 if ((gc != NULL) && |
|
| 5425 ((gaim_conversation_get_type(conv) != GAIM_CONV_TYPE_CHAT) || |
|
| 5426 !gaim_conv_chat_has_left(GAIM_CONV_CHAT(conv)) )) |
|
| 5427 { |
|
| 5428 /* Account is online */ |
|
| 5429 /* Deal with the toolbar */ |
|
| 5430 if (conv->features & GAIM_CONNECTION_HTML) |
|
| 5431 { |
|
| 5432 buttons = GTK_IMHTML_ALL; /* Everything on */ |
|
| 5433 if (conv->features & GAIM_CONNECTION_NO_BGCOLOR) |
|
| 5434 buttons &= ~GTK_IMHTML_BACKCOLOR; |
|
| 5435 if (conv->features & GAIM_CONNECTION_NO_FONTSIZE) |
|
| 5436 { |
|
| 5437 buttons &= ~GTK_IMHTML_GROW; |
|
| 5438 buttons &= ~GTK_IMHTML_SHRINK; |
|
| 5439 } |
|
| 5440 if (conv->features & GAIM_CONNECTION_NO_URLDESC) |
|
| 5441 buttons &= ~GTK_IMHTML_LINKDESC; |
|
| 5442 } else { |
|
| 5443 buttons = GTK_IMHTML_SMILEY | GTK_IMHTML_IMAGE; |
|
| 5444 } |
|
| 5445 |
|
| 5446 if (!(prpl_info->options & OPT_PROTO_IM_IMAGE) || |
|
| 5447 conv->features & GAIM_CONNECTION_NO_IMAGES) |
|
| 5448 buttons &= ~GTK_IMHTML_IMAGE; |
|
| 5449 |
|
| 5450 gtk_imhtml_set_format_functions(GTK_IMHTML(gtkconv->entry), buttons); |
|
| 5451 gtk_imhtmltoolbar_associate_smileys(GTK_IMHTMLTOOLBAR(gtkconv->toolbar), gaim_account_get_protocol_id(account)); |
|
| 5452 |
|
| 5453 /* Deal with menu items */ |
|
| 5454 gtk_widget_set_sensitive(win->menu.view_log, TRUE); |
|
| 5455 gtk_widget_set_sensitive(win->menu.add_pounce, TRUE); |
|
| 5456 gtk_widget_set_sensitive(win->menu.get_info, (prpl_info->get_info != NULL)); |
|
| 5457 gtk_widget_set_sensitive(win->menu.invite, (prpl_info->chat_invite != NULL)); |
|
| 5458 gtk_widget_set_sensitive(win->menu.block, (prpl_info->add_deny != NULL)); |
|
| 5459 gtk_widget_set_sensitive(win->menu.insert_link, (conv->features & GAIM_CONNECTION_HTML)); |
|
| 5460 gtk_widget_set_sensitive(win->menu.insert_image, (prpl_info->options & OPT_PROTO_IM_IMAGE) && !(conv->features & GAIM_CONNECTION_NO_IMAGES)); |
|
| 5461 |
|
| 5462 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 5463 { |
|
| 5464 gtk_widget_set_sensitive(win->menu.add, (prpl_info->add_buddy != NULL)); |
|
| 5465 gtk_widget_set_sensitive(win->menu.remove, (prpl_info->remove_buddy != NULL)); |
|
| 5466 gtk_widget_set_sensitive(win->menu.send_file, |
|
| 5467 (prpl_info->send_file != NULL && (!prpl_info->can_receive_file || |
|
| 5468 prpl_info->can_receive_file(gc, gaim_conversation_get_name(conv))))); |
|
| 5469 gtk_widget_set_sensitive(win->menu.alias, |
|
| 5470 (gaim_find_buddy(account, gaim_conversation_get_name(conv)) != NULL)); |
|
| 5471 } |
|
| 5472 else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 5473 { |
|
| 5474 gtk_widget_set_sensitive(win->menu.add, (prpl_info->join_chat != NULL)); |
|
| 5475 gtk_widget_set_sensitive(win->menu.remove, (prpl_info->join_chat != NULL)); |
|
| 5476 gtk_widget_set_sensitive(win->menu.alias, |
|
| 5477 (gaim_blist_find_chat(account, gaim_conversation_get_name(conv)) != NULL)); |
|
| 5478 } |
|
| 5479 |
|
| 5480 /* Deal with chat userlist buttons */ |
|
| 5481 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 5482 { |
|
| 5483 gtk_widget_set_sensitive(gtkconv->u.chat->userlist_im, TRUE); |
|
| 5484 gtk_widget_set_sensitive(gtkconv->u.chat->userlist_ignore, TRUE); |
|
| 5485 gtk_widget_set_sensitive(gtkconv->u.chat->userlist_info, (prpl_info->get_info != NULL)); |
|
| 5486 } |
|
| 5487 } else { |
|
| 5488 /* Account is offline */ |
|
| 5489 /* Or it's a chat that we've left. */ |
|
| 5490 |
|
| 5491 /* Then deal with menu items */ |
|
| 5492 gtk_widget_set_sensitive(win->menu.view_log, TRUE); |
|
| 5493 gtk_widget_set_sensitive(win->menu.send_file, FALSE); |
|
| 5494 gtk_widget_set_sensitive(win->menu.add_pounce, TRUE); |
|
| 5495 gtk_widget_set_sensitive(win->menu.get_info, FALSE); |
|
| 5496 gtk_widget_set_sensitive(win->menu.invite, FALSE); |
|
| 5497 gtk_widget_set_sensitive(win->menu.alias, FALSE); |
|
| 5498 gtk_widget_set_sensitive(win->menu.block, FALSE); |
|
| 5499 gtk_widget_set_sensitive(win->menu.add, FALSE); |
|
| 5500 gtk_widget_set_sensitive(win->menu.remove, FALSE); |
|
| 5501 gtk_widget_set_sensitive(win->menu.insert_link, TRUE); |
|
| 5502 gtk_widget_set_sensitive(win->menu.insert_image, FALSE); |
|
| 5503 |
|
| 5504 /* Deal with chat userlist buttons */ |
|
| 5505 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 5506 { |
|
| 5507 gtk_widget_set_sensitive(gtkconv->u.chat->userlist_im, FALSE); |
|
| 5508 gtk_widget_set_sensitive(gtkconv->u.chat->userlist_ignore, FALSE); |
|
| 5509 gtk_widget_set_sensitive(gtkconv->u.chat->userlist_info, FALSE); |
|
| 5510 } |
|
| 5511 } |
|
| 5512 |
|
| 5513 /* |
|
| 5514 * Update the window's icon |
|
| 5515 */ |
|
| 5516 if (gaim_gtk_conv_window_is_active_conversation(conv)) |
|
| 5517 { |
|
| 5518 if ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) && |
|
| 5519 (gtkconv->u.im->anim)) |
|
| 5520 { |
|
| 5521 window_icon = |
|
| 5522 gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); |
|
| 5523 g_object_ref(window_icon); |
|
| 5524 } else { |
|
| 5525 window_icon = gaim_gtkconv_get_tab_icon(conv, FALSE); |
|
| 5526 } |
|
| 5527 gtk_window_set_icon(GTK_WINDOW(win->window), window_icon); |
|
| 5528 if (window_icon != NULL) |
|
| 5529 g_object_unref(G_OBJECT(window_icon)); |
|
| 5530 } |
|
| 5531 } |
|
| 5532 |
|
| 5533 static void |
|
| 5534 gaim_gtkconv_update_fields(GaimConversation *conv, GaimGtkConvFields fields) |
|
| 5535 { |
|
| 5536 GaimGtkConversation *gtkconv; |
|
| 5537 GaimGtkWindow *win; |
|
| 5538 |
|
| 5539 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5540 if (!gtkconv) |
|
| 5541 return; |
|
| 5542 win = gaim_gtkconv_get_window(gtkconv); |
|
| 5543 if (!win) |
|
| 5544 return; |
|
| 5545 |
|
| 5546 if (fields & GAIM_GTKCONV_SET_TITLE) |
|
| 5547 { |
|
| 5548 gaim_conversation_autoset_title(conv); |
|
| 5549 } |
|
| 5550 |
|
| 5551 if (fields & GAIM_GTKCONV_BUDDY_ICON) |
|
| 5552 { |
|
| 5553 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 5554 gaim_gtkconv_update_buddy_icon(conv); |
|
| 5555 } |
|
| 5556 |
|
| 5557 if (fields & GAIM_GTKCONV_MENU) |
|
| 5558 { |
|
| 5559 gray_stuff_out(GAIM_GTK_CONVERSATION(conv)); |
|
| 5560 generate_send_to_items(win); |
|
| 5561 } |
|
| 5562 |
|
| 5563 if (fields & GAIM_GTKCONV_TAB_ICON) |
|
| 5564 { |
|
| 5565 update_tab_icon(conv); |
|
| 5566 generate_send_to_items(win); /* To update the icons in SendTo menu */ |
|
| 5567 } |
|
| 5568 |
|
| 5569 if ((fields & GAIM_GTKCONV_TOPIC) && |
|
| 5570 gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 5571 { |
|
| 5572 const char *topic; |
|
| 5573 GaimConvChat *chat = GAIM_CONV_CHAT(conv); |
|
| 5574 GaimGtkChatPane *gtkchat = gtkconv->u.chat; |
|
| 5575 |
|
| 5576 topic = gaim_conv_chat_get_topic(chat); |
|
| 5577 |
|
| 5578 gtk_entry_set_text(GTK_ENTRY(gtkchat->topic_text), topic ? topic : ""); |
|
| 5579 gtk_tooltips_set_tip(gtkconv->tooltips, gtkchat->topic_text, |
|
| 5580 topic ? topic : "", NULL); |
|
| 5581 } |
|
| 5582 |
|
| 5583 if (fields & GAIM_GTKCONV_SMILEY_THEME) |
|
| 5584 gaim_gtkthemes_smiley_themeize(GAIM_GTK_CONVERSATION(conv)->imhtml); |
|
| 5585 |
|
| 5586 if ((fields & GAIM_GTKCONV_COLORIZE_TITLE) || |
|
| 5587 (fields & GAIM_GTKCONV_SET_TITLE)) |
|
| 5588 { |
|
| 5589 char *title; |
|
| 5590 GaimConvIm *im = NULL; |
|
| 5591 GaimAccount *account = gaim_conversation_get_account(conv); |
|
| 5592 /* I think this is a little longer than it needs to be but I'm lazy. */ |
|
| 5593 char style[51]; |
|
| 5594 |
|
| 5595 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 5596 im = GAIM_CONV_IM(conv); |
|
| 5597 |
|
| 5598 if (!gaim_account_is_connected(account) |
|
| 5599 || ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) |
|
| 5600 && gaim_conv_chat_has_left(GAIM_CONV_CHAT(conv)))) |
|
| 5601 title = g_strdup_printf("(%s)", gaim_conversation_get_title(conv)); |
|
| 5602 else |
|
| 5603 title = g_strdup(gaim_conversation_get_title(conv)); |
|
| 5604 |
|
| 5605 *style = '\0'; |
|
| 5606 |
|
| 5607 if (!GTK_WIDGET_REALIZED(gtkconv->tab_label)) |
|
| 5608 gtk_widget_realize(gtkconv->tab_label); |
|
| 5609 |
|
| 5610 if (im != NULL && |
|
| 5611 gaim_conv_im_get_typing_state(im) == GAIM_TYPING) |
|
| 5612 { |
|
| 5613 strncpy(style, "color=\"#47A046\"", sizeof(style)); |
|
| 5614 } |
|
| 5615 else if (im != NULL && |
|
| 5616 gaim_conv_im_get_typing_state(im) == GAIM_TYPED) |
|
| 5617 { |
|
| 5618 strncpy(style, "color=\"#D1940C\"", sizeof(style)); |
|
| 5619 } |
|
| 5620 else if (gtkconv->unseen_state == GAIM_UNSEEN_NICK) |
|
| 5621 { |
|
| 5622 strncpy(style, "color=\"#0D4E91\" style=\"italic\" weight=\"bold\"", sizeof(style)); |
|
| 5623 } |
|
| 5624 else if (gtkconv->unseen_state == GAIM_UNSEEN_TEXT) |
|
| 5625 { |
|
| 5626 strncpy(style, "color=\"#DF421E\" weight=\"bold\"", sizeof(style)); |
|
| 5627 } |
|
| 5628 else if (gtkconv->unseen_state == GAIM_UNSEEN_EVENT) |
|
| 5629 { |
|
| 5630 strncpy(style, "color=\"#868272\" style=\"italic\"", sizeof(style)); |
|
| 5631 } |
|
| 5632 |
|
| 5633 if (*style != '\0') |
|
| 5634 { |
|
| 5635 char *html_title,*label; |
|
| 5636 |
|
| 5637 html_title = g_markup_escape_text(title, -1); |
|
| 5638 |
|
| 5639 label = g_strdup_printf("<span %s>%s</span>", |
|
| 5640 style, html_title); |
|
| 5641 g_free(html_title); |
|
| 5642 gtk_label_set_markup(GTK_LABEL(gtkconv->tab_label), label); |
|
| 5643 g_free(label); |
|
| 5644 } |
|
| 5645 else |
|
| 5646 gtk_label_set_text(GTK_LABEL(gtkconv->tab_label), title); |
|
| 5647 |
|
| 5648 if (gaim_gtk_conv_window_is_active_conversation(conv)) |
|
| 5649 update_typing_icon(gtkconv); |
|
| 5650 |
|
| 5651 gtk_label_set_text(GTK_LABEL(gtkconv->menu_label), title); |
|
| 5652 if (gaim_gtk_conv_window_is_active_conversation(conv)) |
|
| 5653 gtk_window_set_title(GTK_WINDOW(win->window), title); |
|
| 5654 |
|
| 5655 g_free(title); |
|
| 5656 } |
|
| 5657 } |
|
| 5658 |
|
| 5659 static void |
|
| 5660 gaim_gtkconv_updated(GaimConversation *conv, GaimConvUpdateType type) |
|
| 5661 { |
|
| 5662 GaimGtkConvFields flags = 0; |
|
| 5663 |
|
| 5664 g_return_if_fail(conv != NULL); |
|
| 5665 |
|
| 5666 if (type == GAIM_CONV_UPDATE_ACCOUNT) |
|
| 5667 { |
|
| 5668 flags = GAIM_GTKCONV_ALL; |
|
| 5669 } |
|
| 5670 else if (type == GAIM_CONV_UPDATE_TYPING || |
|
| 5671 type == GAIM_CONV_UPDATE_UNSEEN || |
|
| 5672 type == GAIM_CONV_UPDATE_TITLE) |
|
| 5673 { |
|
| 5674 flags = GAIM_GTKCONV_COLORIZE_TITLE; |
|
| 5675 } |
|
| 5676 else if (type == GAIM_CONV_UPDATE_TOPIC) |
|
| 5677 { |
|
| 5678 flags = GAIM_GTKCONV_TOPIC; |
|
| 5679 } |
|
| 5680 else if (type == GAIM_CONV_ACCOUNT_ONLINE || |
|
| 5681 type == GAIM_CONV_ACCOUNT_OFFLINE) |
|
| 5682 { |
|
| 5683 flags = GAIM_GTKCONV_MENU | GAIM_GTKCONV_TAB_ICON | GAIM_GTKCONV_SET_TITLE; |
|
| 5684 } |
|
| 5685 else if (type == GAIM_CONV_UPDATE_AWAY) |
|
| 5686 { |
|
| 5687 flags = GAIM_GTKCONV_TAB_ICON; |
|
| 5688 } |
|
| 5689 else if (type == GAIM_CONV_UPDATE_ADD || |
|
| 5690 type == GAIM_CONV_UPDATE_REMOVE || |
|
| 5691 type == GAIM_CONV_UPDATE_CHATLEFT) |
|
| 5692 { |
|
| 5693 flags = GAIM_GTKCONV_SET_TITLE | GAIM_GTKCONV_MENU; |
|
| 5694 } |
|
| 5695 else if (type == GAIM_CONV_UPDATE_ICON) |
|
| 5696 { |
|
| 5697 flags = GAIM_GTKCONV_BUDDY_ICON; |
|
| 5698 } |
|
| 5699 else if (type == GAIM_CONV_UPDATE_FEATURES) |
|
| 5700 { |
|
| 5701 flags = GAIM_GTKCONV_MENU; |
|
| 5702 } |
|
| 5703 |
|
| 5704 gaim_gtkconv_update_fields(conv, flags); |
|
| 5705 } |
|
| 5706 |
|
| 5707 static GaimConversationUiOps conversation_ui_ops = |
|
| 5708 { |
|
| 5709 gaim_gtkconv_new, |
|
| 5710 gaim_gtkconv_destroy, /* destroy_conversation */ |
|
| 5711 gaim_gtkconv_write_chat, /* write_chat */ |
|
| 5712 gaim_gtkconv_write_im, /* write_im */ |
|
| 5713 gaim_gtkconv_write_conv, /* write_conv */ |
|
| 5714 gaim_gtkconv_chat_add_users, /* chat_add_users */ |
|
| 5715 gaim_gtkconv_chat_rename_user, /* chat_rename_user */ |
|
| 5716 gaim_gtkconv_chat_remove_users, /* chat_remove_users */ |
|
| 5717 gaim_gtkconv_chat_update_user, /* chat_update_user */ |
|
| 5718 gaim_gtkconv_present_conversation, /* present */ |
|
| 5719 gaim_gtkconv_has_focus, /* has_focus */ |
|
| 5720 gaim_gtkconv_custom_smiley_add, /* custom_smiley_add */ |
|
| 5721 gaim_gtkconv_custom_smiley_write, /* custom_smiley_write */ |
|
| 5722 gaim_gtkconv_custom_smiley_close /* custom_smiley_close */ |
|
| 5723 }; |
|
| 5724 |
|
| 5725 GaimConversationUiOps * |
|
| 5726 gaim_gtk_conversations_get_conv_ui_ops(void) |
|
| 5727 { |
|
| 5728 return &conversation_ui_ops; |
|
| 5729 } |
|
| 5730 |
|
| 5731 /************************************************************************** |
|
| 5732 * Public conversation utility functions |
|
| 5733 **************************************************************************/ |
|
| 5734 void |
|
| 5735 gaim_gtkconv_update_buddy_icon(GaimConversation *conv) |
|
| 5736 { |
|
| 5737 GaimGtkConversation *gtkconv; |
|
| 5738 GaimGtkWindow *win; |
|
| 5739 |
|
| 5740 GdkPixbufLoader *loader; |
|
| 5741 GdkPixbufAnimation *anim; |
|
| 5742 GError *err = NULL; |
|
| 5743 |
|
| 5744 const void *data; |
|
| 5745 size_t len; |
|
| 5746 |
|
| 5747 GdkPixbuf *buf; |
|
| 5748 |
|
| 5749 GtkWidget *event; |
|
| 5750 GtkWidget *frame; |
|
| 5751 GdkPixbuf *scale; |
|
| 5752 GdkPixmap *pm; |
|
| 5753 GdkBitmap *bm; |
|
| 5754 int scale_width, scale_height; |
|
| 5755 |
|
| 5756 GaimAccount *account; |
|
| 5757 GaimPluginProtocolInfo *prpl_info = NULL; |
|
| 5758 |
|
| 5759 GaimBuddyIcon *icon; |
|
| 5760 |
|
| 5761 g_return_if_fail(conv != NULL); |
|
| 5762 g_return_if_fail(GAIM_IS_GTK_CONVERSATION(conv)); |
|
| 5763 g_return_if_fail(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM); |
|
| 5764 |
|
| 5765 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5766 win = gtkconv->win; |
|
| 5767 if (conv != gtkconv->active_conv) |
|
| 5768 return; |
|
| 5769 |
|
| 5770 if (!gtkconv->u.im->show_icon) |
|
| 5771 return; |
|
| 5772 |
|
| 5773 account = gaim_conversation_get_account(conv); |
|
| 5774 if(account && account->gc) |
|
| 5775 prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(account->gc->prpl); |
|
| 5776 |
|
| 5777 /* Remove the current icon stuff */ |
|
| 5778 if (gtkconv->u.im->icon_container != NULL) |
|
| 5779 gtk_widget_destroy(gtkconv->u.im->icon_container); |
|
| 5780 gtkconv->u.im->icon_container = NULL; |
|
| 5781 |
|
| 5782 if (gtkconv->u.im->anim != NULL) |
|
| 5783 g_object_unref(G_OBJECT(gtkconv->u.im->anim)); |
|
| 5784 |
|
| 5785 gtkconv->u.im->anim = NULL; |
|
| 5786 |
|
| 5787 if (gtkconv->u.im->icon_timer != 0) |
|
| 5788 g_source_remove(gtkconv->u.im->icon_timer); |
|
| 5789 |
|
| 5790 gtkconv->u.im->icon_timer = 0; |
|
| 5791 |
|
| 5792 if (gtkconv->u.im->iter != NULL) |
|
| 5793 g_object_unref(G_OBJECT(gtkconv->u.im->iter)); |
|
| 5794 |
|
| 5795 gtkconv->u.im->iter = NULL; |
|
| 5796 |
|
| 5797 if (!gaim_prefs_get_bool("/gaim/gtk/conversations/im/show_buddy_icons")) |
|
| 5798 return; |
|
| 5799 |
|
| 5800 if (gaim_conversation_get_gc(conv) == NULL) |
|
| 5801 return; |
|
| 5802 |
|
| 5803 icon = gaim_conv_im_get_icon(GAIM_CONV_IM(conv)); |
|
| 5804 |
|
| 5805 if (icon == NULL) |
|
| 5806 return; |
|
| 5807 |
|
| 5808 data = gaim_buddy_icon_get_data(icon, &len); |
|
| 5809 |
|
| 5810 loader = gdk_pixbuf_loader_new(); |
|
| 5811 gdk_pixbuf_loader_write(loader, data, len, NULL); |
|
| 5812 gdk_pixbuf_loader_close(loader, &err); |
|
| 5813 anim = gdk_pixbuf_loader_get_animation(loader); |
|
| 5814 if (anim) |
|
| 5815 g_object_ref(G_OBJECT(anim)); |
|
| 5816 g_object_unref(loader); |
|
| 5817 |
|
| 5818 if (!anim) |
|
| 5819 return; |
|
| 5820 gtkconv->u.im->anim = anim; |
|
| 5821 |
|
| 5822 if (err) { |
|
| 5823 gaim_debug(GAIM_DEBUG_ERROR, "gtkconv", |
|
| 5824 "Buddy icon error: %s\n", err->message); |
|
| 5825 g_error_free(err); |
|
| 5826 } |
|
| 5827 |
|
| 5828 if (!gtkconv->u.im->anim) |
|
| 5829 return; |
|
| 5830 |
|
| 5831 if (gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)) { |
|
| 5832 gtkconv->u.im->iter = NULL; |
|
| 5833 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); |
|
| 5834 } else { |
|
| 5835 gtkconv->u.im->iter = |
|
| 5836 gdk_pixbuf_animation_get_iter(gtkconv->u.im->anim, NULL); /* LEAK */ |
|
| 5837 buf = gdk_pixbuf_animation_iter_get_pixbuf(gtkconv->u.im->iter); |
|
| 5838 if (gtkconv->u.im->animate) |
|
| 5839 start_anim(NULL, gtkconv); |
|
| 5840 } |
|
| 5841 |
|
| 5842 gaim_gtk_buddy_icon_get_scale_size(buf, prpl_info ? &prpl_info->icon_spec : |
|
| 5843 NULL, &scale_width, &scale_height); |
|
| 5844 scale = gdk_pixbuf_scale_simple(buf, |
|
| 5845 MAX(gdk_pixbuf_get_width(buf) * scale_width / |
|
| 5846 gdk_pixbuf_animation_get_width(gtkconv->u.im->anim), 1), |
|
| 5847 MAX(gdk_pixbuf_get_height(buf) * scale_height / |
|
| 5848 gdk_pixbuf_animation_get_height(gtkconv->u.im->anim), 1), |
|
| 5849 GDK_INTERP_BILINEAR); |
|
| 5850 |
|
| 5851 gdk_pixbuf_render_pixmap_and_mask(scale, &pm, &bm, 100); |
|
| 5852 g_object_unref(G_OBJECT(scale)); |
|
| 5853 |
|
| 5854 |
|
| 5855 gtkconv->u.im->icon_container = gtk_vbox_new(FALSE, 0); |
|
| 5856 |
|
| 5857 frame = gtk_frame_new(NULL); |
|
| 5858 gtk_frame_set_shadow_type(GTK_FRAME(frame), |
|
| 5859 (bm ? GTK_SHADOW_NONE : GTK_SHADOW_IN)); |
|
| 5860 gtk_box_pack_start(GTK_BOX(gtkconv->u.im->icon_container), frame, |
|
| 5861 FALSE, FALSE, 0); |
|
| 5862 |
|
| 5863 event = gtk_event_box_new(); |
|
| 5864 gtk_container_add(GTK_CONTAINER(frame), event); |
|
| 5865 g_signal_connect(G_OBJECT(event), "button-press-event", |
|
| 5866 G_CALLBACK(icon_menu), gtkconv); |
|
| 5867 gtk_widget_show(event); |
|
| 5868 |
|
| 5869 gtkconv->u.im->icon = gtk_image_new_from_pixmap(pm, bm); |
|
| 5870 gtk_widget_set_size_request(gtkconv->u.im->icon, scale_width, scale_height); |
|
| 5871 gtk_container_add(GTK_CONTAINER(event), gtkconv->u.im->icon); |
|
| 5872 gtk_widget_show(gtkconv->u.im->icon); |
|
| 5873 |
|
| 5874 g_object_unref(G_OBJECT(pm)); |
|
| 5875 |
|
| 5876 if (bm) |
|
| 5877 g_object_unref(G_OBJECT(bm)); |
|
| 5878 |
|
| 5879 gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), |
|
| 5880 gtkconv->u.im->icon_container, FALSE, FALSE, 0); |
|
| 5881 |
|
| 5882 gtk_widget_show(gtkconv->u.im->icon_container); |
|
| 5883 gtk_widget_show(frame); |
|
| 5884 |
|
| 5885 /* The buddy icon code needs badly to be fixed. */ |
|
| 5886 buf = gdk_pixbuf_animation_get_static_image(gtkconv->u.im->anim); |
|
| 5887 if(gaim_gtk_conv_window_is_active_conversation(conv)) |
|
| 5888 gtk_window_set_icon(GTK_WINDOW(win->window), buf); |
|
| 5889 } |
|
| 5890 |
|
| 5891 void |
|
| 5892 gaim_gtkconv_update_buttons_by_protocol(GaimConversation *conv) |
|
| 5893 { |
|
| 5894 GaimGtkWindow *win; |
|
| 5895 |
|
| 5896 if (!GAIM_IS_GTK_CONVERSATION(conv)) |
|
| 5897 return; |
|
| 5898 |
|
| 5899 win = GAIM_GTK_CONVERSATION(conv)->win; |
|
| 5900 |
|
| 5901 if (win != NULL && gaim_gtk_conv_window_is_active_conversation(conv)) |
|
| 5902 gray_stuff_out(GAIM_GTK_CONVERSATION(conv)); |
|
| 5903 } |
|
| 5904 |
|
| 5905 int |
|
| 5906 gaim_gtkconv_get_tab_at_xy(GaimGtkWindow *win, int x, int y, gboolean *to_right) |
|
| 5907 { |
|
| 5908 gint nb_x, nb_y, x_rel, y_rel; |
|
| 5909 GtkNotebook *notebook; |
|
| 5910 GtkWidget *page, *tab; |
|
| 5911 gint i, page_num = -1; |
|
| 5912 gint count; |
|
| 5913 gboolean horiz; |
|
| 5914 |
|
| 5915 if (to_right) |
|
| 5916 *to_right = FALSE; |
|
| 5917 |
|
| 5918 notebook = GTK_NOTEBOOK(win->notebook); |
|
| 5919 |
|
| 5920 gdk_window_get_origin(win->notebook->window, &nb_x, &nb_y); |
|
| 5921 x_rel = x - nb_x; |
|
| 5922 y_rel = y - nb_y; |
|
| 5923 |
|
| 5924 horiz = (gtk_notebook_get_tab_pos(notebook) == GTK_POS_TOP || |
|
| 5925 gtk_notebook_get_tab_pos(notebook) == GTK_POS_BOTTOM); |
|
| 5926 |
|
| 5927 #if GTK_CHECK_VERSION(2,2,0) |
|
| 5928 count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)); |
|
| 5929 #else |
|
| 5930 /* this is hacky, but it's only for Gtk 2.0.0... */ |
|
| 5931 count = g_list_length(GTK_NOTEBOOK(notebook)->children); |
|
| 5932 #endif |
|
| 5933 |
|
| 5934 for (i = 0; i < count; i++) { |
|
| 5935 |
|
| 5936 page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i); |
|
| 5937 tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(notebook), page); |
|
| 5938 |
|
| 5939 if (horiz) { |
|
| 5940 if (x_rel >= tab->allocation.x - GAIM_HIG_BOX_SPACE && |
|
| 5941 x_rel <= tab->allocation.x + tab->allocation.width + GAIM_HIG_BOX_SPACE) { |
|
| 5942 page_num = i; |
|
| 5943 |
|
| 5944 if (to_right && x_rel >= tab->allocation.x + tab->allocation.width/2) |
|
| 5945 *to_right = TRUE; |
|
| 5946 |
|
| 5947 break; |
|
| 5948 } |
|
| 5949 } else { |
|
| 5950 if (y_rel >= tab->allocation.y - GAIM_HIG_BOX_SPACE && |
|
| 5951 y_rel <= tab->allocation.y + tab->allocation.height + GAIM_HIG_BOX_SPACE) { |
|
| 5952 page_num = i; |
|
| 5953 |
|
| 5954 if (to_right && y_rel >= tab->allocation.y + tab->allocation.height/2) |
|
| 5955 *to_right = TRUE; |
|
| 5956 |
|
| 5957 break; |
|
| 5958 } |
|
| 5959 } |
|
| 5960 } |
|
| 5961 |
|
| 5962 if (page_num == -1) { |
|
| 5963 /* Add after the last tab */ |
|
| 5964 page_num = count - 1; |
|
| 5965 } |
|
| 5966 |
|
| 5967 return page_num; |
|
| 5968 } |
|
| 5969 |
|
| 5970 static void |
|
| 5971 close_on_tabs_pref_cb(const char *name, GaimPrefType type, |
|
| 5972 gconstpointer value, gpointer data) |
|
| 5973 { |
|
| 5974 GList *l; |
|
| 5975 GaimConversation *conv; |
|
| 5976 GaimGtkConversation *gtkconv; |
|
| 5977 |
|
| 5978 for (l = gaim_get_conversations(); l != NULL; l = l->next) { |
|
| 5979 conv = (GaimConversation *)l->data; |
|
| 5980 |
|
| 5981 if (!GAIM_IS_GTK_CONVERSATION(conv)) |
|
| 5982 continue; |
|
| 5983 |
|
| 5984 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 5985 |
|
| 5986 if (value) |
|
| 5987 gtk_widget_show(gtkconv->close); |
|
| 5988 else |
|
| 5989 gtk_widget_hide(gtkconv->close); |
|
| 5990 } |
|
| 5991 } |
|
| 5992 |
|
| 5993 static void |
|
| 5994 spellcheck_pref_cb(const char *name, GaimPrefType type, |
|
| 5995 gconstpointer value, gpointer data) |
|
| 5996 { |
|
| 5997 #ifdef USE_GTKSPELL |
|
| 5998 GList *cl; |
|
| 5999 GaimConversation *conv; |
|
| 6000 GaimGtkConversation *gtkconv; |
|
| 6001 GtkSpell *spell; |
|
| 6002 |
|
| 6003 for (cl = gaim_get_conversations(); cl != NULL; cl = cl->next) { |
|
| 6004 |
|
| 6005 conv = (GaimConversation *)cl->data; |
|
| 6006 |
|
| 6007 if (!GAIM_IS_GTK_CONVERSATION(conv)) |
|
| 6008 continue; |
|
| 6009 |
|
| 6010 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 6011 |
|
| 6012 if (value) |
|
| 6013 gaim_gtk_setup_gtkspell(GTK_TEXT_VIEW(gtkconv->entry)); |
|
| 6014 else { |
|
| 6015 spell = gtkspell_get_from_text_view(GTK_TEXT_VIEW(gtkconv->entry)); |
|
| 6016 gtkspell_detach(spell); |
|
| 6017 } |
|
| 6018 } |
|
| 6019 #endif |
|
| 6020 } |
|
| 6021 |
|
| 6022 static void |
|
| 6023 tab_side_pref_cb(const char *name, GaimPrefType type, |
|
| 6024 gconstpointer value, gpointer data) |
|
| 6025 { |
|
| 6026 GList *l; |
|
| 6027 GtkPositionType pos; |
|
| 6028 GaimGtkWindow *win; |
|
| 6029 |
|
| 6030 pos = GPOINTER_TO_INT(value); |
|
| 6031 |
|
| 6032 for (l = gaim_gtk_conv_windows_get_list(); l != NULL; l = l->next) { |
|
| 6033 win = l->data; |
|
| 6034 |
|
| 6035 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(win->notebook), pos&~8); |
|
| 6036 } |
|
| 6037 } |
|
| 6038 |
|
| 6039 static void |
|
| 6040 show_timestamps_pref_cb(const char *name, GaimPrefType type, |
|
| 6041 gconstpointer value, gpointer data) |
|
| 6042 { |
|
| 6043 GList *l; |
|
| 6044 GaimConversation *conv; |
|
| 6045 GaimGtkConversation *gtkconv; |
|
| 6046 GaimGtkWindow *win; |
|
| 6047 |
|
| 6048 for (l = gaim_get_conversations(); l != NULL; l = l->next) |
|
| 6049 { |
|
| 6050 conv = (GaimConversation *)l->data; |
|
| 6051 |
|
| 6052 if (!GAIM_IS_GTK_CONVERSATION(conv)) |
|
| 6053 continue; |
|
| 6054 |
|
| 6055 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 6056 win = gtkconv->win; |
|
| 6057 |
|
| 6058 gtk_check_menu_item_set_active( |
|
| 6059 GTK_CHECK_MENU_ITEM(win->menu.show_timestamps), |
|
| 6060 (gboolean)GPOINTER_TO_INT(value)); |
|
| 6061 |
|
| 6062 gtk_imhtml_show_comments(GTK_IMHTML(gtkconv->imhtml), |
|
| 6063 (gboolean)GPOINTER_TO_INT(value)); |
|
| 6064 } |
|
| 6065 } |
|
| 6066 |
|
| 6067 static void |
|
| 6068 show_formatting_toolbar_pref_cb(const char *name, GaimPrefType type, |
|
| 6069 gconstpointer value, gpointer data) |
|
| 6070 { |
|
| 6071 GList *l; |
|
| 6072 GaimConversation *conv; |
|
| 6073 GaimGtkConversation *gtkconv; |
|
| 6074 GaimGtkWindow *win; |
|
| 6075 |
|
| 6076 for (l = gaim_get_conversations(); l != NULL; l = l->next) |
|
| 6077 { |
|
| 6078 conv = (GaimConversation *)l->data; |
|
| 6079 |
|
| 6080 if (!GAIM_IS_GTK_CONVERSATION(conv)) |
|
| 6081 continue; |
|
| 6082 |
|
| 6083 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 6084 win = gtkconv->win; |
|
| 6085 |
|
| 6086 gtk_check_menu_item_set_active( |
|
| 6087 GTK_CHECK_MENU_ITEM(win->menu.show_formatting_toolbar), |
|
| 6088 (gboolean)GPOINTER_TO_INT(value)); |
|
| 6089 |
|
| 6090 if ((gboolean)GPOINTER_TO_INT(value)) |
|
| 6091 gtk_widget_show(gtkconv->toolbar); |
|
| 6092 else |
|
| 6093 gtk_widget_hide(gtkconv->toolbar); |
|
| 6094 } |
|
| 6095 } |
|
| 6096 |
|
| 6097 static void |
|
| 6098 animate_buddy_icons_pref_cb(const char *name, GaimPrefType type, |
|
| 6099 gconstpointer value, gpointer data) |
|
| 6100 { |
|
| 6101 GList *l; |
|
| 6102 GaimConversation *conv; |
|
| 6103 GaimGtkConversation *gtkconv; |
|
| 6104 GaimGtkWindow *win; |
|
| 6105 |
|
| 6106 if (!gaim_prefs_get_bool("/gaim/gtk/conversations/im/show_buddy_icons")) |
|
| 6107 return; |
|
| 6108 |
|
| 6109 /* Set the "animate" flag for each icon based on the new preference */ |
|
| 6110 for (l = gaim_get_ims(); l != NULL; l = l->next) { |
|
| 6111 conv = (GaimConversation *)l->data; |
|
| 6112 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 6113 gtkconv->u.im->animate = GPOINTER_TO_INT(value); |
|
| 6114 } |
|
| 6115 |
|
| 6116 /* Now either stop or start animation for the active conversation in each window */ |
|
| 6117 for (l = gaim_gtk_conv_windows_get_list(); l != NULL; l = l->next) { |
|
| 6118 win = l->data; |
|
| 6119 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 6120 gaim_gtkconv_update_buddy_icon(conv); |
|
| 6121 } |
|
| 6122 } |
|
| 6123 |
|
| 6124 static void |
|
| 6125 show_buddy_icons_pref_cb(const char *name, GaimPrefType type, |
|
| 6126 gconstpointer value, gpointer data) |
|
| 6127 { |
|
| 6128 GList *l; |
|
| 6129 |
|
| 6130 for (l = gaim_get_conversations(); l != NULL; l = l->next) { |
|
| 6131 GaimConversation *conv = l->data; |
|
| 6132 |
|
| 6133 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 6134 gaim_gtkconv_update_buddy_icon(conv); |
|
| 6135 } |
|
| 6136 } |
|
| 6137 |
|
| 6138 static void |
|
| 6139 conv_placement_usetabs_cb(const char *name, GaimPrefType type, |
|
| 6140 gconstpointer value, gpointer data) |
|
| 6141 { |
|
| 6142 gaim_prefs_trigger_callback("/gaim/gtk/conversations/placement"); |
|
| 6143 } |
|
| 6144 |
|
| 6145 static void |
|
| 6146 account_status_changed_cb(GaimAccount *account, GaimStatus *oldstatus, |
|
| 6147 GaimStatus *newstatus) |
|
| 6148 { |
|
| 6149 GList *l; |
|
| 6150 GaimConversation *conv = NULL; |
|
| 6151 GaimGtkConversation *gtkconv; |
|
| 6152 |
|
| 6153 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "away")!=0) |
|
| 6154 return; |
|
| 6155 |
|
| 6156 if(gaim_status_is_available(oldstatus) || !gaim_status_is_available(newstatus)) |
|
| 6157 return; |
|
| 6158 |
|
| 6159 for (l = hidden_convwin->gtkconvs; l != NULL; l = l->next) { |
|
| 6160 gtkconv = l->data; |
|
| 6161 |
|
| 6162 conv = gtkconv->active_conv; |
|
| 6163 |
|
| 6164 if(!gaim_status_is_available( |
|
| 6165 gaim_account_get_active_status( |
|
| 6166 gaim_conversation_get_account(conv)))) |
|
| 6167 continue; |
|
| 6168 |
|
| 6169 gaim_gtk_conv_window_remove_gtkconv(hidden_convwin, gtkconv); |
|
| 6170 gaim_gtkconv_placement_place(gtkconv); |
|
| 6171 } |
|
| 6172 } |
|
| 6173 |
|
| 6174 static void |
|
| 6175 hide_new_pref_cb(const char *name, GaimPrefType type, |
|
| 6176 gconstpointer value, gpointer data) |
|
| 6177 { |
|
| 6178 GList *l; |
|
| 6179 GaimConversation *conv = NULL; |
|
| 6180 GaimGtkConversation *gtkconv; |
|
| 6181 gboolean when_away = FALSE; |
|
| 6182 |
|
| 6183 if(!hidden_convwin) |
|
| 6184 return; |
|
| 6185 |
|
| 6186 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "always")==0) |
|
| 6187 return; |
|
| 6188 |
|
| 6189 if(strcmp(gaim_prefs_get_string("/gaim/gtk/conversations/im/hide_new"), "away")==0) |
|
| 6190 when_away = TRUE; |
|
| 6191 |
|
| 6192 for (l = hidden_convwin->gtkconvs; l != NULL; l = l->next) { |
|
| 6193 gtkconv = l->data; |
|
| 6194 |
|
| 6195 conv = gtkconv->active_conv; |
|
| 6196 |
|
| 6197 if(when_away && !gaim_status_is_available( |
|
| 6198 gaim_account_get_active_status( |
|
| 6199 gaim_conversation_get_account(conv)))) |
|
| 6200 continue; |
|
| 6201 |
|
| 6202 gaim_gtk_conv_window_remove_gtkconv(hidden_convwin, gtkconv); |
|
| 6203 gaim_gtkconv_placement_place(gtkconv); |
|
| 6204 } |
|
| 6205 } |
|
| 6206 |
|
| 6207 |
|
| 6208 static void |
|
| 6209 conv_placement_pref_cb(const char *name, GaimPrefType type, |
|
| 6210 gconstpointer value, gpointer data) |
|
| 6211 { |
|
| 6212 GaimConvPlacementFunc func; |
|
| 6213 |
|
| 6214 if (strcmp(name, "/gaim/gtk/conversations/placement")) |
|
| 6215 return; |
|
| 6216 |
|
| 6217 func = gaim_gtkconv_placement_get_fnc(value); |
|
| 6218 |
|
| 6219 if (func == NULL) |
|
| 6220 return; |
|
| 6221 |
|
| 6222 gaim_gtkconv_placement_set_current_func(func); |
|
| 6223 } |
|
| 6224 |
|
| 6225 static GaimGtkConversation * |
|
| 6226 get_gtkconv_with_contact(GaimContact *contact) |
|
| 6227 { |
|
| 6228 GaimBlistNode *node; |
|
| 6229 |
|
| 6230 node = ((GaimBlistNode*)contact)->child; |
|
| 6231 |
|
| 6232 for (; node; node = node->next) |
|
| 6233 { |
|
| 6234 GaimBuddy *buddy = (GaimBuddy*)node; |
|
| 6235 GaimConversation *conv; |
|
| 6236 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, buddy->account); |
|
| 6237 if (conv) |
|
| 6238 return GAIM_GTK_CONVERSATION(conv); |
|
| 6239 } |
|
| 6240 return NULL; |
|
| 6241 } |
|
| 6242 |
|
| 6243 static void |
|
| 6244 account_signed_off_cb(GaimConnection *gc, gpointer event) |
|
| 6245 { |
|
| 6246 GList *iter; |
|
| 6247 GaimAccount *account; |
|
| 6248 |
|
| 6249 account = gaim_connection_get_account(gc); |
|
| 6250 |
|
| 6251 for (iter = gaim_get_conversations(); iter; iter = iter->next) |
|
| 6252 { |
|
| 6253 GaimConversation *conv = iter->data; |
|
| 6254 |
|
| 6255 /* This seems fine in theory, but we also need to cover the |
|
| 6256 * case of this account matching one of the other buddies in |
|
| 6257 * one of the contacts containing the buddy corresponding to |
|
| 6258 * a conversation. It's easier to just update them all. */ |
|
| 6259 /* if (gaim_conversation_get_account(conv) == account) */ |
|
| 6260 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON | |
|
| 6261 GAIM_GTKCONV_MENU | GAIM_GTKCONV_COLORIZE_TITLE); |
|
| 6262 } |
|
| 6263 } |
|
| 6264 |
|
| 6265 static gboolean |
|
| 6266 update_buddy_status_timeout(GaimBuddy *buddy) |
|
| 6267 { |
|
| 6268 /* To remove the signing-on/off door icon */ |
|
| 6269 GaimConversation *conv; |
|
| 6270 |
|
| 6271 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, buddy->account); |
|
| 6272 if (conv) |
|
| 6273 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON); |
|
| 6274 |
|
| 6275 return FALSE; |
|
| 6276 } |
|
| 6277 |
|
| 6278 static void |
|
| 6279 update_buddy_status_changed(GaimBuddy *buddy, GaimStatus *old, GaimStatus *newstatus) |
|
| 6280 { |
|
| 6281 GaimGtkConversation *gtkconv; |
|
| 6282 GaimConversation *conv; |
|
| 6283 |
|
| 6284 gtkconv = get_gtkconv_with_contact(gaim_buddy_get_contact(buddy)); |
|
| 6285 if (gtkconv) |
|
| 6286 { |
|
| 6287 conv = gtkconv->active_conv; |
|
| 6288 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON | GAIM_GTKCONV_COLORIZE_TITLE); |
|
| 6289 if ((gaim_status_is_online(old) ^ gaim_status_is_online(newstatus)) != 0) |
|
| 6290 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_MENU); |
|
| 6291 } |
|
| 6292 |
|
| 6293 /* In case a conversation is started after the buddy has signed-on/off */ |
|
| 6294 g_timeout_add(11000, (GSourceFunc)update_buddy_status_timeout, buddy); |
|
| 6295 } |
|
| 6296 |
|
| 6297 static void |
|
| 6298 update_buddy_idle_changed(GaimBuddy *buddy, gboolean old, gboolean newidle) |
|
| 6299 { |
|
| 6300 GaimConversation *conv; |
|
| 6301 |
|
| 6302 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, buddy->account); |
|
| 6303 if (conv) |
|
| 6304 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON); |
|
| 6305 } |
|
| 6306 |
|
| 6307 static void |
|
| 6308 update_buddy_icon(GaimBuddy *buddy) |
|
| 6309 { |
|
| 6310 GaimConversation *conv; |
|
| 6311 |
|
| 6312 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, buddy->name, buddy->account); |
|
| 6313 if (conv) |
|
| 6314 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_BUDDY_ICON); |
|
| 6315 } |
|
| 6316 |
|
| 6317 static void |
|
| 6318 update_buddy_sign(GaimBuddy *buddy, const char *which) |
|
| 6319 { |
|
| 6320 GaimPresence *presence; |
|
| 6321 GaimStatus *on, *off; |
|
| 6322 |
|
| 6323 presence = gaim_buddy_get_presence(buddy); |
|
| 6324 if (!presence) |
|
| 6325 return; |
|
| 6326 off = gaim_presence_get_status(presence, "offline"); |
|
| 6327 on = gaim_presence_get_status(presence, "available"); |
|
| 6328 |
|
| 6329 if (*(which+1) == 'f') |
|
| 6330 update_buddy_status_changed(buddy, on, off); |
|
| 6331 else |
|
| 6332 update_buddy_status_changed(buddy, off, on); |
|
| 6333 } |
|
| 6334 |
|
| 6335 static void |
|
| 6336 update_conversation_switched(GaimConversation *conv) |
|
| 6337 { |
|
| 6338 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TAB_ICON | GAIM_GTKCONV_SET_TITLE | |
|
| 6339 GAIM_GTKCONV_MENU | GAIM_GTKCONV_BUDDY_ICON); |
|
| 6340 } |
|
| 6341 |
|
| 6342 static void |
|
| 6343 update_buddy_typing(GaimAccount *account, const char *who) |
|
| 6344 { |
|
| 6345 GaimConversation *conv; |
|
| 6346 GaimGtkConversation *gtkconv; |
|
| 6347 |
|
| 6348 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, who, account); |
|
| 6349 if (!conv) |
|
| 6350 return; |
|
| 6351 |
|
| 6352 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 6353 if (gtkconv && gtkconv->active_conv == conv) |
|
| 6354 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_COLORIZE_TITLE); |
|
| 6355 } |
|
| 6356 |
|
| 6357 static void |
|
| 6358 update_chat(GaimConversation *conv) |
|
| 6359 { |
|
| 6360 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TOPIC | |
|
| 6361 GAIM_GTKCONV_MENU | GAIM_GTKCONV_SET_TITLE); |
|
| 6362 } |
|
| 6363 |
|
| 6364 static void |
|
| 6365 update_chat_topic(GaimConversation *conv, const char *old, const char *new) |
|
| 6366 { |
|
| 6367 gaim_gtkconv_update_fields(conv, GAIM_GTKCONV_TOPIC); |
|
| 6368 } |
|
| 6369 |
|
| 6370 void * |
|
| 6371 gaim_gtk_conversations_get_handle(void) |
|
| 6372 { |
|
| 6373 static int handle; |
|
| 6374 |
|
| 6375 return &handle; |
|
| 6376 } |
|
| 6377 |
|
| 6378 void |
|
| 6379 gaim_gtk_conversations_init(void) |
|
| 6380 { |
|
| 6381 void *handle = gaim_gtk_conversations_get_handle(); |
|
| 6382 void *blist_handle = gaim_blist_get_handle(); |
|
| 6383 |
|
| 6384 /* Conversations */ |
|
| 6385 gaim_prefs_add_none("/gaim/gtk/conversations"); |
|
| 6386 gaim_prefs_add_bool("/gaim/gtk/conversations/use_smooth_scrolling", TRUE); |
|
| 6387 gaim_prefs_add_bool("/gaim/gtk/conversations/close_on_tabs", TRUE); |
|
| 6388 gaim_prefs_add_bool("/gaim/gtk/conversations/send_bold", FALSE); |
|
| 6389 gaim_prefs_add_bool("/gaim/gtk/conversations/send_italic", FALSE); |
|
| 6390 gaim_prefs_add_bool("/gaim/gtk/conversations/send_underline", FALSE); |
|
| 6391 gaim_prefs_add_bool("/gaim/gtk/conversations/spellcheck", TRUE); |
|
| 6392 gaim_prefs_add_bool("/gaim/gtk/conversations/show_incoming_formatting", TRUE); |
|
| 6393 |
|
| 6394 gaim_prefs_add_bool("/gaim/gtk/conversations/show_timestamps", TRUE); |
|
| 6395 gaim_prefs_add_bool("/gaim/gtk/conversations/show_formatting_toolbar", TRUE); |
|
| 6396 gaim_prefs_add_bool("/gaim/gtk/conversations/passthrough_unknown_commands", FALSE); |
|
| 6397 |
|
| 6398 gaim_prefs_add_string("/gaim/gtk/conversations/placement", "last"); |
|
| 6399 gaim_prefs_add_int("/gaim/gtk/conversations/placement_number", 1); |
|
| 6400 gaim_prefs_add_string("/gaim/gtk/conversations/bgcolor", ""); |
|
| 6401 gaim_prefs_add_string("/gaim/gtk/conversations/fgcolor", ""); |
|
| 6402 gaim_prefs_add_string("/gaim/gtk/conversations/font_face", ""); |
|
| 6403 gaim_prefs_add_int("/gaim/gtk/conversations/font_size", 3); |
|
| 6404 gaim_prefs_add_bool("/gaim/gtk/conversations/tabs", TRUE); |
|
| 6405 gaim_prefs_add_int("/gaim/gtk/conversations/tab_side", GTK_POS_TOP); |
|
| 6406 gaim_prefs_add_int("/gaim/gtk/conversations/scrollback_lines", 4000); |
|
| 6407 |
|
| 6408 /* Conversations -> Chat */ |
|
| 6409 gaim_prefs_add_none("/gaim/gtk/conversations/chat"); |
|
| 6410 gaim_prefs_add_int("/gaim/gtk/conversations/chat/default_width", 410); |
|
| 6411 gaim_prefs_add_int("/gaim/gtk/conversations/chat/default_height", 160); |
|
| 6412 gaim_prefs_add_int("/gaim/gtk/conversations/chat/entry_height", 50); |
|
| 6413 |
|
| 6414 /* Conversations -> IM */ |
|
| 6415 gaim_prefs_add_none("/gaim/gtk/conversations/im"); |
|
| 6416 |
|
| 6417 gaim_prefs_add_bool("/gaim/gtk/conversations/im/animate_buddy_icons", TRUE); |
|
| 6418 |
|
| 6419 gaim_prefs_add_int("/gaim/gtk/conversations/im/default_width", 410); |
|
| 6420 gaim_prefs_add_int("/gaim/gtk/conversations/im/default_height", 160); |
|
| 6421 gaim_prefs_add_int("/gaim/gtk/conversations/im/entry_height", 50); |
|
| 6422 gaim_prefs_add_bool("/gaim/gtk/conversations/im/show_buddy_icons", TRUE); |
|
| 6423 |
|
| 6424 gaim_prefs_add_string("/gaim/gtk/conversations/im/hide_new", "never"); |
|
| 6425 |
|
| 6426 /* Connect callbacks. */ |
|
| 6427 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/close_on_tabs", |
|
| 6428 close_on_tabs_pref_cb, NULL); |
|
| 6429 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/show_timestamps", |
|
| 6430 show_timestamps_pref_cb, NULL); |
|
| 6431 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/show_formatting_toolbar", |
|
| 6432 show_formatting_toolbar_pref_cb, NULL); |
|
| 6433 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/spellcheck", |
|
| 6434 spellcheck_pref_cb, NULL); |
|
| 6435 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/tab_side", |
|
| 6436 tab_side_pref_cb, NULL); |
|
| 6437 |
|
| 6438 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/tabs", |
|
| 6439 conv_placement_usetabs_cb, NULL); |
|
| 6440 |
|
| 6441 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/placement", |
|
| 6442 conv_placement_pref_cb, NULL); |
|
| 6443 gaim_prefs_trigger_callback("/gaim/gtk/conversations/placement"); |
|
| 6444 |
|
| 6445 /* IM callbacks */ |
|
| 6446 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/animate_buddy_icons", |
|
| 6447 animate_buddy_icons_pref_cb, NULL); |
|
| 6448 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/show_buddy_icons", |
|
| 6449 show_buddy_icons_pref_cb, NULL); |
|
| 6450 gaim_prefs_connect_callback(handle, "/gaim/gtk/conversations/im/hide_new", |
|
| 6451 hide_new_pref_cb, NULL); |
|
| 6452 |
|
| 6453 |
|
| 6454 |
|
| 6455 /********************************************************************** |
|
| 6456 * Register signals |
|
| 6457 **********************************************************************/ |
|
| 6458 gaim_signal_register(handle, "conversation-dragging", |
|
| 6459 gaim_marshal_VOID__POINTER_POINTER, NULL, 2, |
|
| 6460 gaim_value_new(GAIM_TYPE_BOXED, |
|
| 6461 "GaimGtkWindow *"), |
|
| 6462 gaim_value_new(GAIM_TYPE_BOXED, |
|
| 6463 "GaimGtkWindow *")); |
|
| 6464 |
|
| 6465 gaim_signal_register(handle, "conversation-timestamp", |
|
| 6466 gaim_marshal_POINTER__POINTER_POINTER, |
|
| 6467 gaim_value_new(GAIM_TYPE_POINTER), 2, |
|
| 6468 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6469 GAIM_SUBTYPE_CONVERSATION), |
|
| 6470 gaim_value_new(GAIM_TYPE_POINTER)); |
|
| 6471 |
|
| 6472 gaim_signal_register(handle, "displaying-im-msg", |
|
| 6473 gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT, |
|
| 6474 gaim_value_new(GAIM_TYPE_BOOLEAN), 4, |
|
| 6475 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6476 GAIM_SUBTYPE_ACCOUNT), |
|
| 6477 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6478 GAIM_SUBTYPE_CONVERSATION), |
|
| 6479 gaim_value_new_outgoing(GAIM_TYPE_STRING), |
|
| 6480 gaim_value_new(G_TYPE_INT)); |
|
| 6481 |
|
| 6482 gaim_signal_register(handle, "displayed-im-msg", |
|
| 6483 gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT, |
|
| 6484 NULL, 4, |
|
| 6485 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6486 GAIM_SUBTYPE_ACCOUNT), |
|
| 6487 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6488 GAIM_SUBTYPE_CONVERSATION), |
|
| 6489 gaim_value_new(GAIM_TYPE_STRING), |
|
| 6490 gaim_value_new(G_TYPE_INT)); |
|
| 6491 |
|
| 6492 gaim_signal_register(handle, "displaying-chat-msg", |
|
| 6493 gaim_marshal_BOOLEAN__POINTER_POINTER_POINTER_UINT, |
|
| 6494 gaim_value_new(GAIM_TYPE_BOOLEAN), 4, |
|
| 6495 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6496 GAIM_SUBTYPE_ACCOUNT), |
|
| 6497 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6498 GAIM_SUBTYPE_CONVERSATION), |
|
| 6499 gaim_value_new_outgoing(GAIM_TYPE_STRING), |
|
| 6500 gaim_value_new(G_TYPE_INT)); |
|
| 6501 |
|
| 6502 gaim_signal_register(handle, "displayed-chat-msg", |
|
| 6503 gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT, |
|
| 6504 NULL, 4, |
|
| 6505 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6506 GAIM_SUBTYPE_ACCOUNT), |
|
| 6507 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6508 GAIM_SUBTYPE_CONVERSATION), |
|
| 6509 gaim_value_new(GAIM_TYPE_STRING), |
|
| 6510 gaim_value_new(G_TYPE_INT)); |
|
| 6511 |
|
| 6512 gaim_signal_register(handle, "conversation-switched", |
|
| 6513 gaim_marshal_VOID__POINTER_POINTER, NULL, 1, |
|
| 6514 gaim_value_new(GAIM_TYPE_SUBTYPE, |
|
| 6515 GAIM_SUBTYPE_CONVERSATION)); |
|
| 6516 |
|
| 6517 /********************************************************************** |
|
| 6518 * Register commands |
|
| 6519 **********************************************************************/ |
|
| 6520 gaim_cmd_register("say", "S", GAIM_CMD_P_DEFAULT, |
|
| 6521 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, |
|
| 6522 say_command_cb, _("say <message>: Send a message normally as if you weren't using a command."), NULL); |
|
| 6523 gaim_cmd_register("me", "S", GAIM_CMD_P_DEFAULT, |
|
| 6524 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, |
|
| 6525 me_command_cb, _("me <action>: Send an IRC style action to a buddy or chat."), NULL); |
|
| 6526 gaim_cmd_register("debug", "w", GAIM_CMD_P_DEFAULT, |
|
| 6527 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, |
|
| 6528 debug_command_cb, _("debug <option>: Send various debug information to the current conversation."), NULL); |
|
| 6529 gaim_cmd_register("clear", "", GAIM_CMD_P_DEFAULT, |
|
| 6530 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, |
|
| 6531 clear_command_cb, _("clear: Clears the conversation scrollback."), NULL); |
|
| 6532 gaim_cmd_register("help", "w", GAIM_CMD_P_DEFAULT, |
|
| 6533 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, NULL, |
|
| 6534 help_command_cb, _("help <command>: Help on a specific command."), NULL); |
|
| 6535 |
|
| 6536 /********************************************************************** |
|
| 6537 * UI operations |
|
| 6538 **********************************************************************/ |
|
| 6539 |
|
| 6540 gaim_signal_connect(gaim_connections_get_handle(), "signed-on", handle, |
|
| 6541 G_CALLBACK(account_signed_off_cb), |
|
| 6542 GINT_TO_POINTER(GAIM_CONV_ACCOUNT_ONLINE)); |
|
| 6543 gaim_signal_connect(gaim_connections_get_handle(), "signed-off", handle, |
|
| 6544 G_CALLBACK(account_signed_off_cb), |
|
| 6545 GINT_TO_POINTER(GAIM_CONV_ACCOUNT_OFFLINE)); |
|
| 6546 |
|
| 6547 gaim_signal_connect(gaim_conversations_get_handle(), "received-im-msg", |
|
| 6548 handle, G_CALLBACK(received_im_msg_cb), NULL); |
|
| 6549 |
|
| 6550 gaim_conversations_set_ui_ops(&conversation_ui_ops); |
|
| 6551 |
|
| 6552 hidden_convwin = gaim_gtk_conv_window_new(); |
|
| 6553 window_list = g_list_remove(window_list, hidden_convwin); |
|
| 6554 |
|
| 6555 gaim_signal_connect(gaim_accounts_get_handle(), "account-status-changed", |
|
| 6556 handle, GAIM_CALLBACK(account_status_changed_cb), NULL); |
|
| 6557 |
|
| 6558 /* Callbacks to update a conversation */ |
|
| 6559 gaim_signal_connect(blist_handle, "buddy-added", handle, |
|
| 6560 G_CALLBACK(buddy_update_cb), NULL); |
|
| 6561 gaim_signal_connect(blist_handle, "buddy-removed", handle, |
|
| 6562 G_CALLBACK(buddy_update_cb), NULL); |
|
| 6563 gaim_signal_connect(blist_handle, "buddy-signed-on", |
|
| 6564 handle, GAIM_CALLBACK(update_buddy_sign), "on"); |
|
| 6565 gaim_signal_connect(blist_handle, "buddy-signed-off", |
|
| 6566 handle, GAIM_CALLBACK(update_buddy_sign), "off"); |
|
| 6567 gaim_signal_connect(blist_handle, "buddy-status-changed", |
|
| 6568 handle, GAIM_CALLBACK(update_buddy_status_changed), NULL); |
|
| 6569 gaim_signal_connect(blist_handle, "buddy-idle-changed", |
|
| 6570 handle, GAIM_CALLBACK(update_buddy_idle_changed), NULL); |
|
| 6571 gaim_signal_connect(blist_handle, "buddy-icon-changed", |
|
| 6572 handle, GAIM_CALLBACK(update_buddy_icon), NULL); |
|
| 6573 gaim_signal_connect(gaim_conversations_get_handle(), "buddy-typing", |
|
| 6574 handle, GAIM_CALLBACK(update_buddy_typing), NULL); |
|
| 6575 gaim_signal_connect(gaim_conversations_get_handle(), "buddy-typing-stopped", |
|
| 6576 handle, GAIM_CALLBACK(update_buddy_typing), NULL); |
|
| 6577 gaim_signal_connect(gaim_gtk_conversations_get_handle(), "conversation-switched", |
|
| 6578 handle, GAIM_CALLBACK(update_conversation_switched), NULL); |
|
| 6579 gaim_signal_connect(gaim_conversations_get_handle(), "chat-left", handle, |
|
| 6580 GAIM_CALLBACK(update_chat), NULL); |
|
| 6581 gaim_signal_connect(gaim_conversations_get_handle(), "chat-joined", handle, |
|
| 6582 GAIM_CALLBACK(update_chat), NULL); |
|
| 6583 gaim_signal_connect(gaim_conversations_get_handle(), "chat-topic-changed", handle, |
|
| 6584 GAIM_CALLBACK(update_chat_topic), NULL); |
|
| 6585 gaim_signal_connect_priority(gaim_conversations_get_handle(), "conversation-updated", handle, |
|
| 6586 GAIM_CALLBACK(gaim_gtkconv_updated), NULL, |
|
| 6587 GAIM_SIGNAL_PRIORITY_LOWEST); |
|
| 6588 } |
|
| 6589 |
|
| 6590 void |
|
| 6591 gaim_gtk_conversations_uninit(void) |
|
| 6592 { |
|
| 6593 gaim_prefs_disconnect_by_handle(gaim_gtk_conversations_get_handle()); |
|
| 6594 gaim_signals_disconnect_by_handle(gaim_gtk_conversations_get_handle()); |
|
| 6595 gaim_signals_unregister_by_instance(gaim_gtk_conversations_get_handle()); |
|
| 6596 gaim_gtk_conv_window_destroy(hidden_convwin); |
|
| 6597 hidden_convwin=NULL; |
|
| 6598 } |
|
| 6599 |
|
| 6600 |
|
| 6601 |
|
| 6602 |
|
| 6603 |
|
| 6604 |
|
| 6605 |
|
| 6606 |
|
| 6607 |
|
| 6608 |
|
| 6609 |
|
| 6610 |
|
| 6611 |
|
| 6612 |
|
| 6613 |
|
| 6614 |
|
| 6615 /* down here is where gtkconvwin.c ought to start. except they share like every freaking function, |
|
| 6616 * and touch each others' private members all day long */ |
|
| 6617 |
|
| 6618 /** |
|
| 6619 * @file gtkconvwin.c GTK+ Conversation Window API |
|
| 6620 * @ingroup gtkui |
|
| 6621 * |
|
| 6622 * gaim |
|
| 6623 * |
|
| 6624 * Gaim is the legal property of its developers, whose names are too numerous |
|
| 6625 * to list here. Please refer to the COPYRIGHT file distributed with this |
|
| 6626 * source distribution. |
|
| 6627 * |
|
| 6628 * This program is free software; you can redistribute it and/or modify |
|
| 6629 * it under the terms of the GNU General Public License as published by |
|
| 6630 * the Free Software Foundation; either version 2 of the License, or |
|
| 6631 * (at your option) any later version. |
|
| 6632 * |
|
| 6633 * This program is distributed in the hope that it will be useful, |
|
| 6634 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 6635 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
| 6636 * GNU General Public License for more details. |
|
| 6637 * |
|
| 6638 * You should have received a copy of the GNU General Public License |
|
| 6639 * along with this program; if not, write to the Free Software |
|
| 6640 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
| 6641 * |
|
| 6642 */ |
|
| 6643 #include "internal.h" |
|
| 6644 #include "gtkgaim.h" |
|
| 6645 |
|
| 6646 |
|
| 6647 #include <gdk/gdkkeysyms.h> |
|
| 6648 |
|
| 6649 #include "account.h" |
|
| 6650 #include "cmds.h" |
|
| 6651 #include "debug.h" |
|
| 6652 #include "imgstore.h" |
|
| 6653 #include "log.h" |
|
| 6654 #include "notify.h" |
|
| 6655 #include "prpl.h" |
|
| 6656 #include "request.h" |
|
| 6657 #include "util.h" |
|
| 6658 |
|
| 6659 #include "gtkdnd-hints.h" |
|
| 6660 #include "gtkblist.h" |
|
| 6661 #include "gtkconv.h" |
|
| 6662 #include "gtkdialogs.h" |
|
| 6663 #include "gtkmenutray.h" |
|
| 6664 #include "gtkpounce.h" |
|
| 6665 #include "gtkprefs.h" |
|
| 6666 #include "gtkprivacy.h" |
|
| 6667 #include "gtkutils.h" |
|
| 6668 #include "gtkstock.h" |
|
| 6669 #include "gtkimhtml.h" |
|
| 6670 #include "gtkimhtmltoolbar.h" |
|
| 6671 |
|
| 6672 static void |
|
| 6673 do_close(GtkWidget *w, int resp, GaimGtkWindow *win) |
|
| 6674 { |
|
| 6675 gtk_widget_destroy(warn_close_dialog); |
|
| 6676 warn_close_dialog = NULL; |
|
| 6677 |
|
| 6678 if (resp == GTK_RESPONSE_OK) |
|
| 6679 gaim_gtk_conv_window_destroy(win); |
|
| 6680 } |
|
| 6681 |
|
| 6682 static void |
|
| 6683 build_warn_close_dialog(GaimGtkWindow *gtkwin) |
|
| 6684 { |
|
| 6685 GtkWidget *label; |
|
| 6686 GtkWidget *vbox, *hbox; |
|
| 6687 GtkWidget *img; |
|
| 6688 |
|
| 6689 g_return_if_fail(warn_close_dialog == NULL); |
|
| 6690 |
|
| 6691 |
|
| 6692 warn_close_dialog = gtk_dialog_new_with_buttons( |
|
| 6693 _("Confirm close"), |
|
| 6694 GTK_WINDOW(gtkwin->window), GTK_DIALOG_MODAL, |
|
| 6695 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
|
| 6696 GAIM_STOCK_CLOSE_TABS, GTK_RESPONSE_OK, NULL); |
|
| 6697 |
|
| 6698 gtk_dialog_set_default_response(GTK_DIALOG(warn_close_dialog), |
|
| 6699 GTK_RESPONSE_OK); |
|
| 6700 |
|
| 6701 gtk_container_set_border_width(GTK_CONTAINER(warn_close_dialog), |
|
| 6702 6); |
|
| 6703 gtk_window_set_resizable(GTK_WINDOW(warn_close_dialog), FALSE); |
|
| 6704 gtk_dialog_set_has_separator(GTK_DIALOG(warn_close_dialog), |
|
| 6705 FALSE); |
|
| 6706 |
|
| 6707 /* Setup the outside spacing. */ |
|
| 6708 vbox = GTK_DIALOG(warn_close_dialog)->vbox; |
|
| 6709 |
|
| 6710 gtk_box_set_spacing(GTK_BOX(vbox), 12); |
|
| 6711 gtk_container_set_border_width(GTK_CONTAINER(vbox), 6); |
|
| 6712 |
|
| 6713 img = gtk_image_new_from_stock(GAIM_STOCK_DIALOG_WARNING, |
|
| 6714 GTK_ICON_SIZE_DIALOG); |
|
| 6715 /* Setup the inner hbox and put the dialog's icon in it. */ |
|
| 6716 hbox = gtk_hbox_new(FALSE, 12); |
|
| 6717 gtk_container_add(GTK_CONTAINER(vbox), hbox); |
|
| 6718 gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 0); |
|
| 6719 gtk_misc_set_alignment(GTK_MISC(img), 0, 0); |
|
| 6720 |
|
| 6721 /* Setup the right vbox. */ |
|
| 6722 vbox = gtk_vbox_new(FALSE, 12); |
|
| 6723 gtk_container_add(GTK_CONTAINER(hbox), vbox); |
|
| 6724 |
|
| 6725 label = gtk_label_new(_("You have unread messages. Are you sure you want to close the window?")); |
|
| 6726 gtk_widget_set_size_request(label, 350, -1); |
|
| 6727 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); |
|
| 6728 gtk_misc_set_alignment(GTK_MISC(label), 0, 0); |
|
| 6729 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
|
| 6730 |
|
| 6731 /* Connect the signals. */ |
|
| 6732 g_signal_connect(G_OBJECT(warn_close_dialog), "response", |
|
| 6733 G_CALLBACK(do_close), gtkwin); |
|
| 6734 |
|
| 6735 } |
|
| 6736 |
|
| 6737 /************************************************************************** |
|
| 6738 * Callbacks |
|
| 6739 **************************************************************************/ |
|
| 6740 |
|
| 6741 static gboolean |
|
| 6742 close_win_cb(GtkWidget *w, GdkEventAny *e, gpointer d) |
|
| 6743 { |
|
| 6744 GaimGtkWindow *win = d; |
|
| 6745 GList *l; |
|
| 6746 |
|
| 6747 /* If there are unread messages then show a warning dialog */ |
|
| 6748 for (l = gaim_gtk_conv_window_get_gtkconvs(win); |
|
| 6749 l != NULL; l = l->next) |
|
| 6750 { |
|
| 6751 GaimGtkConversation *gtkconv = l->data; |
|
| 6752 if (gaim_conversation_get_type(gtkconv->active_conv) == GAIM_CONV_TYPE_IM && |
|
| 6753 gtkconv->unseen_state >= GAIM_UNSEEN_TEXT) |
|
| 6754 { |
|
| 6755 build_warn_close_dialog(win); |
|
| 6756 gtk_widget_show_all(warn_close_dialog); |
|
| 6757 |
|
| 6758 return TRUE; |
|
| 6759 } |
|
| 6760 } |
|
| 6761 |
|
| 6762 gaim_gtk_conv_window_destroy(win); |
|
| 6763 |
|
| 6764 return TRUE; |
|
| 6765 } |
|
| 6766 |
|
| 6767 static void |
|
| 6768 gtkconv_set_unseen(GaimGtkConversation *gtkconv, GaimUnseenState state) |
|
| 6769 { |
|
| 6770 if (state == GAIM_UNSEEN_NONE) |
|
| 6771 { |
|
| 6772 gtkconv->unseen_count = 0; |
|
| 6773 gtkconv->unseen_state = GAIM_UNSEEN_NONE; |
|
| 6774 } |
|
| 6775 else |
|
| 6776 { |
|
| 6777 if (state >= GAIM_UNSEEN_TEXT) |
|
| 6778 gtkconv->unseen_count++; |
|
| 6779 |
|
| 6780 if (state > gtkconv->unseen_state) |
|
| 6781 gtkconv->unseen_state = state; |
|
| 6782 } |
|
| 6783 |
|
| 6784 gaim_conversation_update(gtkconv->active_conv, GAIM_CONV_UPDATE_UNSEEN); |
|
| 6785 } |
|
| 6786 |
|
| 6787 /* |
|
| 6788 * When a conversation window is focused, we know the user |
|
| 6789 * has looked at it so we know there are no longer unseen |
|
| 6790 * messages. |
|
| 6791 */ |
|
| 6792 static gint |
|
| 6793 focus_win_cb(GtkWidget *w, GdkEventFocus *e, gpointer d) |
|
| 6794 { |
|
| 6795 GaimGtkWindow *win = d; |
|
| 6796 GaimGtkConversation *gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 6797 |
|
| 6798 gtkconv_set_unseen(gtkconv, GAIM_UNSEEN_NONE); |
|
| 6799 |
|
| 6800 return FALSE; |
|
| 6801 } |
|
| 6802 |
|
| 6803 #if !GTK_CHECK_VERSION(2,6,0) |
|
| 6804 /* Courtesy of Galeon! */ |
|
| 6805 static void |
|
| 6806 tab_close_button_state_changed_cb(GtkWidget *widget, GtkStateType prev_state) |
|
| 6807 { |
|
| 6808 if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE) |
|
| 6809 gtk_widget_set_state(widget, GTK_STATE_NORMAL); |
|
| 6810 } |
|
| 6811 #endif |
|
| 6812 |
|
| 6813 static void |
|
| 6814 notebook_init_grab(GaimGtkWindow *gtkwin, GtkWidget *widget) |
|
| 6815 { |
|
| 6816 static GdkCursor *cursor = NULL; |
|
| 6817 |
|
| 6818 gtkwin->in_drag = TRUE; |
|
| 6819 |
|
| 6820 if (gtkwin->drag_leave_signal) { |
|
| 6821 g_signal_handler_disconnect(G_OBJECT(widget), |
|
| 6822 gtkwin->drag_leave_signal); |
|
| 6823 gtkwin->drag_leave_signal = 0; |
|
| 6824 } |
|
| 6825 |
|
| 6826 if (cursor == NULL) |
|
| 6827 cursor = gdk_cursor_new(GDK_FLEUR); |
|
| 6828 |
|
| 6829 /* Grab the pointer */ |
|
| 6830 gtk_grab_add(gtkwin->notebook); |
|
| 6831 #ifndef _WIN32 |
|
| 6832 /* Currently for win32 GTK+ (as of 2.2.1), gdk_pointer_is_grabbed will |
|
| 6833 always be true after a button press. */ |
|
| 6834 if (!gdk_pointer_is_grabbed()) |
|
| 6835 #endif |
|
| 6836 gdk_pointer_grab(gtkwin->notebook->window, FALSE, |
|
| 6837 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, |
|
| 6838 NULL, cursor, GDK_CURRENT_TIME); |
|
| 6839 } |
|
| 6840 |
|
| 6841 static gboolean |
|
| 6842 notebook_motion_cb(GtkWidget *widget, GdkEventButton *e, GaimGtkWindow *win) |
|
| 6843 { |
|
| 6844 |
|
| 6845 /* |
|
| 6846 * Make sure the user moved the mouse far enough for the |
|
| 6847 * drag to be initiated. |
|
| 6848 */ |
|
| 6849 if (win->in_predrag) { |
|
| 6850 if (e->x_root < win->drag_min_x || |
|
| 6851 e->x_root >= win->drag_max_x || |
|
| 6852 e->y_root < win->drag_min_y || |
|
| 6853 e->y_root >= win->drag_max_y) { |
|
| 6854 |
|
| 6855 win->in_predrag = FALSE; |
|
| 6856 notebook_init_grab(win, widget); |
|
| 6857 } |
|
| 6858 } |
|
| 6859 else { /* Otherwise, draw the arrows. */ |
|
| 6860 GaimGtkWindow *dest_win; |
|
| 6861 GtkNotebook *dest_notebook; |
|
| 6862 GtkWidget *tab; |
|
| 6863 gint nb_x, nb_y, page_num; |
|
| 6864 gint arrow1_x, arrow1_y, arrow2_x, arrow2_y; |
|
| 6865 gboolean horiz_tabs = FALSE; |
|
| 6866 GaimGtkConversation *gtkconv; |
|
| 6867 gboolean to_right = FALSE; |
|
| 6868 |
|
| 6869 /* Get the window that the cursor is over. */ |
|
| 6870 dest_win = gaim_gtk_conv_window_get_at_xy(e->x_root, e->y_root); |
|
| 6871 |
|
| 6872 if (dest_win == NULL) { |
|
| 6873 dnd_hints_hide_all(); |
|
| 6874 |
|
| 6875 return TRUE; |
|
| 6876 } |
|
| 6877 |
|
| 6878 dest_notebook = GTK_NOTEBOOK(dest_win->notebook); |
|
| 6879 |
|
| 6880 gdk_window_get_origin(GTK_WIDGET(dest_notebook)->window, &nb_x, &nb_y); |
|
| 6881 |
|
| 6882 arrow1_x = arrow2_x = nb_x; |
|
| 6883 arrow1_y = arrow2_y = nb_y; |
|
| 6884 |
|
| 6885 page_num = gaim_gtkconv_get_tab_at_xy(dest_win, |
|
| 6886 e->x_root, e->y_root, &to_right); |
|
| 6887 to_right = to_right && (win != dest_win); |
|
| 6888 |
|
| 6889 if (gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_TOP || |
|
| 6890 gtk_notebook_get_tab_pos(dest_notebook) == GTK_POS_BOTTOM) { |
|
| 6891 |
|
| 6892 horiz_tabs = TRUE; |
|
| 6893 } |
|
| 6894 |
|
| 6895 gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(dest_win, page_num); |
|
| 6896 tab = gtkconv->tabby; |
|
| 6897 |
|
| 6898 if (horiz_tabs) { |
|
| 6899 arrow1_x = arrow2_x = nb_x + tab->allocation.x; |
|
| 6900 |
|
| 6901 if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) { |
|
| 6902 arrow1_x += tab->allocation.width; |
|
| 6903 arrow2_x += tab->allocation.width; |
|
| 6904 } |
|
| 6905 |
|
| 6906 arrow1_y = nb_y + tab->allocation.y; |
|
| 6907 arrow2_y = nb_y + tab->allocation.y + tab->allocation.height; |
|
| 6908 } else { |
|
| 6909 arrow1_x = nb_x + tab->allocation.x; |
|
| 6910 arrow2_x = nb_x + tab->allocation.x + tab->allocation.width; |
|
| 6911 arrow1_y = arrow2_y = nb_y + tab->allocation.y; |
|
| 6912 |
|
| 6913 if (((gpointer)win == (gpointer)dest_win && win->drag_tab < page_num) || to_right) { |
|
| 6914 arrow1_y += tab->allocation.height; |
|
| 6915 arrow2_y += tab->allocation.height; |
|
| 6916 } |
|
| 6917 } |
|
| 6918 |
|
| 6919 if (horiz_tabs) { |
|
| 6920 dnd_hints_show(HINT_ARROW_DOWN, arrow1_x, arrow1_y); |
|
| 6921 dnd_hints_show(HINT_ARROW_UP, arrow2_x, arrow2_y); |
|
| 6922 } else { |
|
| 6923 dnd_hints_show(HINT_ARROW_RIGHT, arrow1_x, arrow1_y); |
|
| 6924 dnd_hints_show(HINT_ARROW_LEFT, arrow2_x, arrow2_y); |
|
| 6925 } |
|
| 6926 } |
|
| 6927 |
|
| 6928 return TRUE; |
|
| 6929 } |
|
| 6930 |
|
| 6931 static gboolean |
|
| 6932 notebook_leave_cb(GtkWidget *widget, GdkEventCrossing *e, GaimGtkWindow *win) |
|
| 6933 { |
|
| 6934 if (win->in_drag) |
|
| 6935 return FALSE; |
|
| 6936 |
|
| 6937 if (e->x_root < win->drag_min_x || |
|
| 6938 e->x_root >= win->drag_max_x || |
|
| 6939 e->y_root < win->drag_min_y || |
|
| 6940 e->y_root >= win->drag_max_y) { |
|
| 6941 |
|
| 6942 win->in_predrag = FALSE; |
|
| 6943 notebook_init_grab(win, widget); |
|
| 6944 } |
|
| 6945 |
|
| 6946 return TRUE; |
|
| 6947 } |
|
| 6948 |
|
| 6949 /* |
|
| 6950 * THANK YOU GALEON! |
|
| 6951 */ |
|
| 6952 static gboolean |
|
| 6953 notebook_press_cb(GtkWidget *widget, GdkEventButton *e, GaimGtkWindow *win) |
|
| 6954 { |
|
| 6955 gint nb_x, nb_y, x_rel, y_rel; |
|
| 6956 int tab_clicked; |
|
| 6957 GtkWidget *page; |
|
| 6958 GtkWidget *tab; |
|
| 6959 |
|
| 6960 if (e->button != 1 || e->type != GDK_BUTTON_PRESS) |
|
| 6961 return FALSE; |
|
| 6962 |
|
| 6963 |
|
| 6964 if (win->in_drag) { |
|
| 6965 gaim_debug(GAIM_DEBUG_WARNING, "gtkconv", |
|
| 6966 "Already in the middle of a window drag at tab_press_cb\n"); |
|
| 6967 return TRUE; |
|
| 6968 } |
|
| 6969 |
|
| 6970 /* |
|
| 6971 * Make sure a tab was actually clicked. The arrow buttons |
|
| 6972 * mess things up. |
|
| 6973 */ |
|
| 6974 tab_clicked = gaim_gtkconv_get_tab_at_xy(win, e->x_root, e->y_root, NULL); |
|
| 6975 |
|
| 6976 if (tab_clicked == -1) |
|
| 6977 return FALSE; |
|
| 6978 |
|
| 6979 /* |
|
| 6980 * Get the relative position of the press event, with regards to |
|
| 6981 * the position of the notebook. |
|
| 6982 */ |
|
| 6983 gdk_window_get_origin(win->notebook->window, &nb_x, &nb_y); |
|
| 6984 |
|
| 6985 x_rel = e->x_root - nb_x; |
|
| 6986 y_rel = e->y_root - nb_y; |
|
| 6987 |
|
| 6988 /* Reset the min/max x/y */ |
|
| 6989 win->drag_min_x = 0; |
|
| 6990 win->drag_min_y = 0; |
|
| 6991 win->drag_max_x = 0; |
|
| 6992 win->drag_max_y = 0; |
|
| 6993 |
|
| 6994 /* Find out which tab was dragged. */ |
|
| 6995 page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), tab_clicked); |
|
| 6996 tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(win->notebook), page); |
|
| 6997 |
|
| 6998 win->drag_min_x = tab->allocation.x + nb_x; |
|
| 6999 win->drag_min_y = tab->allocation.y + nb_y; |
|
| 7000 win->drag_max_x = tab->allocation.width + win->drag_min_x; |
|
| 7001 win->drag_max_y = tab->allocation.height + win->drag_min_y; |
|
| 7002 |
|
| 7003 /* Make sure the click occurred in the tab. */ |
|
| 7004 if (e->x_root < win->drag_min_x || |
|
| 7005 e->x_root >= win->drag_max_x || |
|
| 7006 e->y_root < win->drag_min_y || |
|
| 7007 e->y_root >= win->drag_max_y) { |
|
| 7008 |
|
| 7009 return FALSE; |
|
| 7010 } |
|
| 7011 |
|
| 7012 win->in_predrag = TRUE; |
|
| 7013 win->drag_tab = tab_clicked; |
|
| 7014 |
|
| 7015 /* Connect the new motion signals. */ |
|
| 7016 win->drag_motion_signal = |
|
| 7017 g_signal_connect(G_OBJECT(widget), "motion_notify_event", |
|
| 7018 G_CALLBACK(notebook_motion_cb), win); |
|
| 7019 |
|
| 7020 win->drag_leave_signal = |
|
| 7021 g_signal_connect(G_OBJECT(widget), "leave_notify_event", |
|
| 7022 G_CALLBACK(notebook_leave_cb), win); |
|
| 7023 |
|
| 7024 return FALSE; |
|
| 7025 } |
|
| 7026 |
|
| 7027 static gboolean |
|
| 7028 notebook_release_cb(GtkWidget *widget, GdkEventButton *e, GaimGtkWindow *win) |
|
| 7029 { |
|
| 7030 GaimGtkWindow *dest_win; |
|
| 7031 GaimConversation *conv; |
|
| 7032 GaimGtkConversation *gtkconv; |
|
| 7033 gint dest_page_num = 0; |
|
| 7034 gboolean new_window = FALSE; |
|
| 7035 gboolean to_right = FALSE; |
|
| 7036 |
|
| 7037 /* |
|
| 7038 * Don't check to make sure that the event's window matches the |
|
| 7039 * widget's, because we may be getting an event passed on from the |
|
| 7040 * close button. |
|
| 7041 */ |
|
| 7042 if (e->button != 1 && e->type != GDK_BUTTON_RELEASE) |
|
| 7043 return FALSE; |
|
| 7044 |
|
| 7045 if (gdk_pointer_is_grabbed()) { |
|
| 7046 gdk_pointer_ungrab(GDK_CURRENT_TIME); |
|
| 7047 gtk_grab_remove(widget); |
|
| 7048 } |
|
| 7049 |
|
| 7050 if (!win->in_predrag && !win->in_drag) |
|
| 7051 return FALSE; |
|
| 7052 |
|
| 7053 /* Disconnect the motion signal. */ |
|
| 7054 if (win->drag_motion_signal) { |
|
| 7055 g_signal_handler_disconnect(G_OBJECT(widget), |
|
| 7056 win->drag_motion_signal); |
|
| 7057 |
|
| 7058 win->drag_motion_signal = 0; |
|
| 7059 } |
|
| 7060 |
|
| 7061 /* |
|
| 7062 * If we're in a pre-drag, we'll also need to disconnect the leave |
|
| 7063 * signal. |
|
| 7064 */ |
|
| 7065 if (win->in_predrag) { |
|
| 7066 win->in_predrag = FALSE; |
|
| 7067 |
|
| 7068 if (win->drag_leave_signal) { |
|
| 7069 g_signal_handler_disconnect(G_OBJECT(widget), |
|
| 7070 win->drag_leave_signal); |
|
| 7071 |
|
| 7072 win->drag_leave_signal = 0; |
|
| 7073 } |
|
| 7074 } |
|
| 7075 |
|
| 7076 /* If we're not in drag... */ |
|
| 7077 /* We're perfectly normal people! */ |
|
| 7078 if (!win->in_drag) |
|
| 7079 return FALSE; |
|
| 7080 |
|
| 7081 win->in_drag = FALSE; |
|
| 7082 |
|
| 7083 dnd_hints_hide_all(); |
|
| 7084 |
|
| 7085 dest_win = gaim_gtk_conv_window_get_at_xy(e->x_root, e->y_root); |
|
| 7086 |
|
| 7087 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 7088 |
|
| 7089 if (dest_win == NULL) { |
|
| 7090 /* If the current window doesn't have any other conversations, |
|
| 7091 * there isn't much point transferring the conv to a new window. */ |
|
| 7092 if (gaim_gtk_conv_window_get_gtkconv_count(win) > 1) { |
|
| 7093 /* Make a new window to stick this to. */ |
|
| 7094 dest_win = gaim_gtk_conv_window_new(); |
|
| 7095 new_window = TRUE; |
|
| 7096 } |
|
| 7097 } |
|
| 7098 |
|
| 7099 if (dest_win == NULL) |
|
| 7100 return FALSE; |
|
| 7101 |
|
| 7102 gaim_signal_emit(gaim_gtk_conversations_get_handle(), |
|
| 7103 "conversation-dragging", win, dest_win); |
|
| 7104 |
|
| 7105 /* Get the destination page number. */ |
|
| 7106 if (!new_window) |
|
| 7107 dest_page_num = gaim_gtkconv_get_tab_at_xy(dest_win, |
|
| 7108 e->x_root, e->y_root, &to_right); |
|
| 7109 |
|
| 7110 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 7111 |
|
| 7112 if (win == dest_win) { |
|
| 7113 gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont, dest_page_num); |
|
| 7114 } else { |
|
| 7115 gaim_gtk_conv_window_remove_gtkconv(win, gtkconv); |
|
| 7116 gaim_gtk_conv_window_add_gtkconv(dest_win, gtkconv); |
|
| 7117 gtk_notebook_reorder_child(GTK_NOTEBOOK(dest_win->notebook), gtkconv->tab_cont, dest_page_num + to_right); |
|
| 7118 gaim_gtk_conv_window_switch_gtkconv(dest_win, gtkconv); |
|
| 7119 if (new_window) { |
|
| 7120 gint win_width, win_height; |
|
| 7121 |
|
| 7122 gtk_window_get_size(GTK_WINDOW(dest_win->window), |
|
| 7123 &win_width, &win_height); |
|
| 7124 |
|
| 7125 gtk_window_move(GTK_WINDOW(dest_win->window), |
|
| 7126 e->x_root - (win_width / 2), |
|
| 7127 e->y_root - (win_height / 2)); |
|
| 7128 |
|
| 7129 gaim_gtk_conv_window_show(dest_win); |
|
| 7130 } |
|
| 7131 } |
|
| 7132 |
|
| 7133 gtk_widget_grab_focus(GAIM_GTK_CONVERSATION(conv)->entry); |
|
| 7134 |
|
| 7135 return TRUE; |
|
| 7136 } |
|
| 7137 |
|
| 7138 |
|
| 7139 static void |
|
| 7140 before_switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num, |
|
| 7141 gpointer user_data) |
|
| 7142 { |
|
| 7143 GaimGtkWindow *win; |
|
| 7144 GaimConversation *conv; |
|
| 7145 GaimGtkConversation *gtkconv; |
|
| 7146 |
|
| 7147 win = user_data; |
|
| 7148 conv = gaim_gtk_conv_window_get_active_conversation(win); |
|
| 7149 |
|
| 7150 g_return_if_fail(conv != NULL); |
|
| 7151 |
|
| 7152 if (gaim_conversation_get_type(conv) != GAIM_CONV_TYPE_IM) |
|
| 7153 return; |
|
| 7154 |
|
| 7155 gtkconv = GAIM_GTK_CONVERSATION(conv); |
|
| 7156 |
|
| 7157 stop_anim(NULL, gtkconv); |
|
| 7158 } |
|
| 7159 static void |
|
| 7160 close_window(GtkWidget *w, GaimGtkWindow *win) |
|
| 7161 { |
|
| 7162 close_win_cb(w, NULL, win); |
|
| 7163 } |
|
| 7164 |
|
| 7165 static void |
|
| 7166 detach_tab_cb(GtkWidget *w, GObject *menu) |
|
| 7167 { |
|
| 7168 GaimGtkWindow *win, *new_window; |
|
| 7169 GaimGtkConversation *gtkconv; |
|
| 7170 |
|
| 7171 gtkconv = g_object_get_data(menu, "clicked_tab"); |
|
| 7172 |
|
| 7173 if (!gtkconv) |
|
| 7174 return; |
|
| 7175 |
|
| 7176 win = gaim_gtkconv_get_window(gtkconv); |
|
| 7177 /* Nothing to do if there's only one tab in the window */ |
|
| 7178 if (gaim_gtk_conv_window_get_gtkconv_count(win) == 1) |
|
| 7179 return; |
|
| 7180 |
|
| 7181 gaim_gtk_conv_window_remove_gtkconv(win, gtkconv); |
|
| 7182 |
|
| 7183 new_window = gaim_gtk_conv_window_new(); |
|
| 7184 gaim_gtk_conv_window_add_gtkconv(new_window, gtkconv); |
|
| 7185 gaim_gtk_conv_window_show(new_window); |
|
| 7186 } |
|
| 7187 |
|
| 7188 static void |
|
| 7189 close_others_cb(GtkWidget *w, GObject *menu) |
|
| 7190 { |
|
| 7191 GList *iter; |
|
| 7192 GaimGtkConversation *gtkconv; |
|
| 7193 GaimGtkWindow *win; |
|
| 7194 |
|
| 7195 gtkconv = g_object_get_data(menu, "clicked_tab"); |
|
| 7196 |
|
| 7197 if (!gtkconv) |
|
| 7198 return; |
|
| 7199 |
|
| 7200 win = gaim_gtkconv_get_window(gtkconv); |
|
| 7201 |
|
| 7202 for (iter = gaim_gtk_conv_window_get_gtkconvs(win); iter; ) |
|
| 7203 { |
|
| 7204 GaimGtkConversation *gconv = iter->data; |
|
| 7205 iter = iter->next; |
|
| 7206 |
|
| 7207 if (gconv != gtkconv) |
|
| 7208 { |
|
| 7209 close_conv_cb(NULL, gconv); |
|
| 7210 } |
|
| 7211 } |
|
| 7212 } |
|
| 7213 |
|
| 7214 static void close_tab_cb(GtkWidget *w, GObject *menu) |
|
| 7215 { |
|
| 7216 GaimGtkConversation *gtkconv; |
|
| 7217 |
|
| 7218 gtkconv = g_object_get_data(menu, "clicked_tab"); |
|
| 7219 |
|
| 7220 if (gtkconv) |
|
| 7221 close_conv_cb(NULL, gtkconv); |
|
| 7222 } |
|
| 7223 |
|
| 7224 static gboolean |
|
| 7225 right_click_menu_cb(GtkNotebook *notebook, GdkEventButton *event, GaimGtkWindow *win) |
|
| 7226 { |
|
| 7227 GtkWidget *item, *menu; |
|
| 7228 GaimGtkConversation *gtkconv; |
|
| 7229 |
|
| 7230 if (event->type != GDK_BUTTON_PRESS || event->button != 3) |
|
| 7231 return FALSE; |
|
| 7232 |
|
| 7233 gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(win, |
|
| 7234 gaim_gtkconv_get_tab_at_xy(win, event->x_root, event->y_root, NULL)); |
|
| 7235 |
|
| 7236 if (g_object_get_data(G_OBJECT(notebook->menu), "clicked_tab")) |
|
| 7237 { |
|
| 7238 g_object_set_data(G_OBJECT(notebook->menu), "clicked_tab", gtkconv); |
|
| 7239 return FALSE; |
|
| 7240 } |
|
| 7241 |
|
| 7242 g_object_set_data(G_OBJECT(notebook->menu), "clicked_tab", gtkconv); |
|
| 7243 |
|
| 7244 menu = notebook->menu; |
|
| 7245 gaim_separator(GTK_WIDGET(menu)); |
|
| 7246 |
|
| 7247 item = gtk_menu_item_new_with_label("Close other tabs"); |
|
| 7248 gtk_widget_show(item); |
|
| 7249 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 7250 g_signal_connect(G_OBJECT(item), "activate", |
|
| 7251 G_CALLBACK(close_others_cb), menu); |
|
| 7252 |
|
| 7253 item = gtk_menu_item_new_with_label("Close all tabs"); |
|
| 7254 gtk_widget_show(item); |
|
| 7255 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 7256 g_signal_connect(G_OBJECT(item), "activate", |
|
| 7257 G_CALLBACK(close_window), win); |
|
| 7258 |
|
| 7259 gaim_separator(menu); |
|
| 7260 |
|
| 7261 item = gtk_menu_item_new_with_label("Detach this tab"); |
|
| 7262 gtk_widget_show(item); |
|
| 7263 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 7264 g_signal_connect(G_OBJECT(item), "activate", |
|
| 7265 G_CALLBACK(detach_tab_cb), menu); |
|
| 7266 |
|
| 7267 item = gtk_menu_item_new_with_label("Close this tab"); |
|
| 7268 gtk_widget_show(item); |
|
| 7269 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 7270 g_signal_connect(G_OBJECT(item), "activate", |
|
| 7271 G_CALLBACK(close_tab_cb), menu); |
|
| 7272 |
|
| 7273 return FALSE; |
|
| 7274 } |
|
| 7275 |
|
| 7276 static void |
|
| 7277 switch_conv_cb(GtkNotebook *notebook, GtkWidget *page, gint page_num, |
|
| 7278 gpointer user_data) |
|
| 7279 { |
|
| 7280 GaimGtkWindow *win; |
|
| 7281 GaimConversation *conv; |
|
| 7282 GaimGtkConversation *gtkconv; |
|
| 7283 const char *sound_method; |
|
| 7284 |
|
| 7285 win = user_data; |
|
| 7286 gtkconv = gaim_gtk_conv_window_get_gtkconv_at_index(win, page_num); |
|
| 7287 conv = gtkconv->active_conv; |
|
| 7288 |
|
| 7289 g_return_if_fail(conv != NULL); |
|
| 7290 |
|
| 7291 /* |
|
| 7292 * Only set "unseen" to "none" if the window has focus |
|
| 7293 */ |
|
| 7294 if (gaim_gtk_conv_window_has_focus(win)) |
|
| 7295 gtkconv_set_unseen(gtkconv, GAIM_UNSEEN_NONE); |
|
| 7296 |
|
| 7297 /* Update the menubar */ |
|
| 7298 |
|
| 7299 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gtkconv->win->menu.logging), |
|
| 7300 gaim_conversation_is_logging(conv)); |
|
| 7301 |
|
| 7302 generate_send_to_items(win); |
|
| 7303 |
|
| 7304 gaim_gtkconv_switch_active_conversation(conv); |
|
| 7305 |
|
| 7306 sound_method = gaim_prefs_get_string("/gaim/gtk/sound/method"); |
|
| 7307 if (strcmp(sound_method, "none") != 0) |
|
| 7308 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.sounds), |
|
| 7309 gtkconv->make_sound); |
|
| 7310 |
|
| 7311 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_formatting_toolbar), |
|
| 7312 gaim_prefs_get_bool("/gaim/gtk/conversations/show_formatting_toolbar")); |
|
| 7313 |
|
| 7314 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_timestamps), |
|
| 7315 gaim_prefs_get_bool("/gaim/gtk/conversations/show_timestamps")); |
|
| 7316 |
|
| 7317 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM && |
|
| 7318 gaim_prefs_get_bool("/gaim/gtk/conversations/im/show_buddy_icons")) |
|
| 7319 { |
|
| 7320 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(win->menu.show_icon), |
|
| 7321 gtkconv->u.im->show_icon); |
|
| 7322 } |
|
| 7323 |
|
| 7324 /* |
|
| 7325 * We pause icons when they are not visible. If this icon should |
|
| 7326 * be animated then start it back up again. |
|
| 7327 */ |
|
| 7328 if ((gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) && |
|
| 7329 (gtkconv->u.im->animate)) |
|
| 7330 start_anim(NULL, gtkconv); |
|
| 7331 |
|
| 7332 gaim_signal_emit(gaim_gtk_conversations_get_handle(), "conversation-switched", conv); |
|
| 7333 } |
|
| 7334 |
|
| 7335 /************************************************************************** |
|
| 7336 * GTK+ window ops |
|
| 7337 **************************************************************************/ |
|
| 7338 |
|
| 7339 GList * |
|
| 7340 gaim_gtk_conv_windows_get_list() |
|
| 7341 { |
|
| 7342 return window_list; |
|
| 7343 } |
|
| 7344 |
|
| 7345 GaimGtkWindow * |
|
| 7346 gaim_gtk_conv_window_new() |
|
| 7347 { |
|
| 7348 GaimGtkWindow *win; |
|
| 7349 GtkPositionType pos; |
|
| 7350 GtkWidget *testidea; |
|
| 7351 GtkWidget *menubar; |
|
| 7352 |
|
| 7353 win = g_malloc0(sizeof(GaimGtkWindow)); |
|
| 7354 |
|
| 7355 window_list = g_list_append(window_list, win); |
|
| 7356 |
|
| 7357 /* Create the window. */ |
|
| 7358 win->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); |
|
| 7359 gtk_window_set_role(GTK_WINDOW(win->window), "conversation"); |
|
| 7360 gtk_window_set_resizable(GTK_WINDOW(win->window), TRUE); |
|
| 7361 gtk_container_set_border_width(GTK_CONTAINER(win->window), 0); |
|
| 7362 GTK_WINDOW(win->window)->allow_shrink = TRUE; |
|
| 7363 |
|
| 7364 g_signal_connect(G_OBJECT(win->window), "delete_event", |
|
| 7365 G_CALLBACK(close_win_cb), win); |
|
| 7366 |
|
| 7367 g_signal_connect(G_OBJECT(win->window), "focus_in_event", |
|
| 7368 G_CALLBACK(focus_win_cb), win); |
|
| 7369 |
|
| 7370 /* Create the notebook. */ |
|
| 7371 win->notebook = gtk_notebook_new(); |
|
| 7372 |
|
| 7373 pos = gaim_prefs_get_int("/gaim/gtk/conversations/tab_side"); |
|
| 7374 |
|
| 7375 #if 0 |
|
| 7376 gtk_notebook_set_tab_hborder(GTK_NOTEBOOK(win->notebook), 0); |
|
| 7377 gtk_notebook_set_tab_vborder(GTK_NOTEBOOK(win->notebook), 0); |
|
| 7378 #endif |
|
| 7379 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(win->notebook), pos); |
|
| 7380 gtk_notebook_set_scrollable(GTK_NOTEBOOK(win->notebook), TRUE); |
|
| 7381 gtk_notebook_popup_enable(GTK_NOTEBOOK(win->notebook)); |
|
| 7382 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE); |
|
| 7383 gtk_notebook_set_show_border(GTK_NOTEBOOK(win->notebook), FALSE); |
|
| 7384 |
|
| 7385 g_signal_connect(G_OBJECT(win->notebook), "button-press-event", |
|
| 7386 G_CALLBACK(right_click_menu_cb), win); |
|
| 7387 |
|
| 7388 gtk_widget_show(win->notebook); |
|
| 7389 |
|
| 7390 g_signal_connect(G_OBJECT(win->notebook), "switch_page", |
|
| 7391 G_CALLBACK(before_switch_conv_cb), win); |
|
| 7392 g_signal_connect_after(G_OBJECT(win->notebook), "switch_page", |
|
| 7393 G_CALLBACK(switch_conv_cb), win); |
|
| 7394 |
|
| 7395 /* Setup the tab drag and drop signals. */ |
|
| 7396 gtk_widget_add_events(win->notebook, |
|
| 7397 GDK_BUTTON1_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK); |
|
| 7398 g_signal_connect(G_OBJECT(win->notebook), "button_press_event", |
|
| 7399 G_CALLBACK(notebook_press_cb), win); |
|
| 7400 g_signal_connect(G_OBJECT(win->notebook), "button_release_event", |
|
| 7401 G_CALLBACK(notebook_release_cb), win); |
|
| 7402 |
|
| 7403 testidea = gtk_vbox_new(FALSE, 0); |
|
| 7404 |
|
| 7405 /* Setup the menubar. */ |
|
| 7406 menubar = setup_menubar(win); |
|
| 7407 gtk_box_pack_start(GTK_BOX(testidea), menubar, FALSE, TRUE, 0); |
|
| 7408 |
|
| 7409 gtk_box_pack_start(GTK_BOX(testidea), win->notebook, TRUE, TRUE, 0); |
|
| 7410 |
|
| 7411 gtk_container_add(GTK_CONTAINER(win->window), testidea); |
|
| 7412 |
|
| 7413 gtk_widget_show(testidea); |
|
| 7414 |
|
| 7415 return win; |
|
| 7416 } |
|
| 7417 |
|
| 7418 void |
|
| 7419 gaim_gtk_conv_window_destroy(GaimGtkWindow *win) |
|
| 7420 { |
|
| 7421 gaim_prefs_disconnect_by_handle(win); |
|
| 7422 window_list = g_list_remove(window_list, win); |
|
| 7423 |
|
| 7424 if (win->gtkconvs) { |
|
| 7425 while (win->gtkconvs) { |
|
| 7426 GList *nextgtk = win->gtkconvs->next; |
|
| 7427 GaimGtkConversation *gtkconv = win->gtkconvs->data; |
|
| 7428 GList *nextcore = gtkconv->convs->next; |
|
| 7429 GaimConversation *conv = gtkconv->convs->data; |
|
| 7430 gaim_conversation_destroy(conv); |
|
| 7431 if (!nextgtk && !nextcore) |
|
| 7432 /* we'll end up invoking ourselves when we destroy our last child */ |
|
| 7433 /* so don't destroy ourselves right now */ |
|
| 7434 return; |
|
| 7435 } |
|
| 7436 return; |
|
| 7437 } |
|
| 7438 gtk_widget_destroy(win->window); |
|
| 7439 |
|
| 7440 g_object_unref(G_OBJECT(win->menu.item_factory)); |
|
| 7441 |
|
| 7442 g_free(win); |
|
| 7443 } |
|
| 7444 |
|
| 7445 void |
|
| 7446 gaim_gtk_conv_window_show(GaimGtkWindow *win) |
|
| 7447 { |
|
| 7448 gtk_widget_show(win->window); |
|
| 7449 } |
|
| 7450 |
|
| 7451 void |
|
| 7452 gaim_gtk_conv_window_hide(GaimGtkWindow *win) |
|
| 7453 { |
|
| 7454 gtk_widget_hide(win->window); |
|
| 7455 } |
|
| 7456 |
|
| 7457 void |
|
| 7458 gaim_gtk_conv_window_raise(GaimGtkWindow *win) |
|
| 7459 { |
|
| 7460 gtk_window_present(GTK_WINDOW(win->window)); |
|
| 7461 } |
|
| 7462 |
|
| 7463 void |
|
| 7464 gaim_gtk_conv_window_switch_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv) |
|
| 7465 { |
|
| 7466 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), |
|
| 7467 gtk_notebook_page_num(GTK_NOTEBOOK(win->notebook), |
|
| 7468 gtkconv->tab_cont)); |
|
| 7469 } |
|
| 7470 |
|
| 7471 void |
|
| 7472 gaim_gtk_conv_window_add_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv) |
|
| 7473 { |
|
| 7474 GaimConversation *conv = gtkconv->active_conv; |
|
| 7475 GaimGtkConversation *focus_gtkconv; |
|
| 7476 GtkWidget *tabby, *menu_tabby; |
|
| 7477 GtkWidget *tab_cont = gtkconv->tab_cont; |
|
| 7478 GtkWidget *close_image; |
|
| 7479 GaimConversationType conv_type; |
|
| 7480 const char *name; |
|
| 7481 const gchar *tmp_lab; |
|
| 7482 gint close_button_width, close_button_height, focus_width, focus_pad; |
|
| 7483 gboolean tabs_side = FALSE; |
|
| 7484 gint angle = 0; |
|
| 7485 |
|
| 7486 name = gaim_conversation_get_name(conv); |
|
| 7487 conv_type = gaim_conversation_get_type(conv); |
|
| 7488 |
|
| 7489 |
|
| 7490 win->gtkconvs = g_list_append(win->gtkconvs, gtkconv); |
|
| 7491 gtkconv->win = win; |
|
| 7492 |
|
| 7493 if (gaim_prefs_get_int("/gaim/gtk/conversations/tab_side") == GTK_POS_LEFT || |
|
| 7494 gaim_prefs_get_int("/gaim/gtk/conversations/tab_side") == GTK_POS_RIGHT) |
|
| 7495 tabs_side = TRUE; |
|
| 7496 else if (gaim_prefs_get_int("/gaim/gtk/conversations/tab_side") == (GTK_POS_LEFT|8)) |
|
| 7497 angle = 90; |
|
| 7498 else if (gaim_prefs_get_int("/gaim/gtk/conversations/tab_side") == (GTK_POS_RIGHT|8)) |
|
| 7499 angle = 270; |
|
| 7500 |
|
| 7501 if (angle) |
|
| 7502 gtkconv->tabby = tabby = gtk_vbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 7503 else |
|
| 7504 gtkconv->tabby = tabby = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 7505 gtkconv->menu_tabby = menu_tabby = gtk_hbox_new(FALSE, GAIM_HIG_BOX_SPACE); |
|
| 7506 |
|
| 7507 /* Close button. */ |
|
| 7508 gtkconv->close = gtk_button_new(); |
|
| 7509 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &close_button_width, &close_button_height); |
|
| 7510 if (gtk_check_version(2, 4, 2) == NULL) { |
|
| 7511 /* Need to account for extra padding around the gtkbutton */ |
|
| 7512 gtk_widget_style_get(GTK_WIDGET(gtkconv->close), |
|
| 7513 "focus-line-width", &focus_width, |
|
| 7514 "focus-padding", &focus_pad, |
|
| 7515 NULL); |
|
| 7516 close_button_width += (focus_width + focus_pad) * 2; |
|
| 7517 close_button_height += (focus_width + focus_pad) * 2; |
|
| 7518 } |
|
| 7519 gtk_widget_set_size_request(GTK_WIDGET(gtkconv->close), |
|
| 7520 close_button_width, close_button_height); |
|
| 7521 |
|
| 7522 gtk_button_set_relief(GTK_BUTTON(gtkconv->close), GTK_RELIEF_NONE); |
|
| 7523 close_image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); |
|
| 7524 gtk_widget_show(close_image); |
|
| 7525 gtk_container_add(GTK_CONTAINER(gtkconv->close), close_image); |
|
| 7526 gtk_tooltips_set_tip(gtkconv->tooltips, gtkconv->close, |
|
| 7527 _("Close conversation"), NULL); |
|
| 7528 |
|
| 7529 g_signal_connect(G_OBJECT(gtkconv->close), "clicked", |
|
| 7530 G_CALLBACK(close_conv_cb), gtkconv); |
|
| 7531 |
|
| 7532 #if !GTK_CHECK_VERSION(2,6,0) |
|
| 7533 /* |
|
| 7534 * I love Galeon. They have a fix for that stupid annoying visible |
|
| 7535 * border bug. I love you guys! -- ChipX86 |
|
| 7536 */ |
|
| 7537 /* This is fixed properly in some version of Gtk before 2.6.0 */ |
|
| 7538 g_signal_connect(G_OBJECT(gtkconv->close), "state_changed", |
|
| 7539 G_CALLBACK(tab_close_button_state_changed_cb), NULL); |
|
| 7540 #endif |
|
| 7541 |
|
| 7542 /* Status icon. */ |
|
| 7543 gtkconv->icon = gtk_image_new(); |
|
| 7544 gtkconv->menu_icon = gtk_image_new(); |
|
| 7545 update_tab_icon(conv); |
|
| 7546 |
|
| 7547 /* Tab label. */ |
|
| 7548 gtkconv->tab_label = gtk_label_new(tmp_lab = gaim_conversation_get_title(conv)); |
|
| 7549 |
|
| 7550 #if GTK_CHECK_VERSION(2,6,0) |
|
| 7551 if (!angle) |
|
| 7552 g_object_set(G_OBJECT(gtkconv->tab_label), "ellipsize", PANGO_ELLIPSIZE_END, NULL); |
|
| 7553 gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), 6); |
|
| 7554 if (tabs_side) { |
|
| 7555 gtk_label_set_width_chars(GTK_LABEL(gtkconv->tab_label), MIN(g_utf8_strlen(tmp_lab, -1), 18)); |
|
| 7556 } |
|
| 7557 if (angle) |
|
| 7558 gtk_label_set_angle(GTK_LABEL(gtkconv->tab_label), angle); |
|
| 7559 #endif |
|
| 7560 gtkconv->menu_label = gtk_label_new(gaim_conversation_get_title(conv)); |
|
| 7561 #if 0 |
|
| 7562 gtk_misc_set_alignment(GTK_MISC(gtkconv->tab_label), 0.00, 0.5); |
|
| 7563 gtk_misc_set_padding(GTK_MISC(gtkconv->tab_label), 4, 0); |
|
| 7564 #endif |
|
| 7565 |
|
| 7566 /* Pack it all together. */ |
|
| 7567 if (angle == 90) |
|
| 7568 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->close, FALSE, FALSE, 0); |
|
| 7569 else |
|
| 7570 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->icon, FALSE, FALSE, 0); |
|
| 7571 gtk_box_pack_start(GTK_BOX(menu_tabby), gtkconv->menu_icon, |
|
| 7572 FALSE, FALSE, 0); |
|
| 7573 |
|
| 7574 gtk_widget_show_all(gtkconv->icon); |
|
| 7575 gtk_widget_show_all(gtkconv->menu_icon); |
|
| 7576 |
|
| 7577 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->tab_label, TRUE, TRUE, 0); |
|
| 7578 gtk_box_pack_start(GTK_BOX(menu_tabby), gtkconv->menu_label, TRUE, TRUE, 0); |
|
| 7579 gtk_widget_show(gtkconv->tab_label); |
|
| 7580 gtk_widget_show(gtkconv->menu_label); |
|
| 7581 gtk_misc_set_alignment(GTK_MISC(gtkconv->menu_label), 0, 0); |
|
| 7582 |
|
| 7583 if (angle == 90) |
|
| 7584 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->icon, FALSE, FALSE, 0); |
|
| 7585 else |
|
| 7586 gtk_box_pack_start(GTK_BOX(tabby), gtkconv->close, FALSE, FALSE, 0); |
|
| 7587 if (gaim_prefs_get_bool("/gaim/gtk/conversations/close_on_tabs")) |
|
| 7588 gtk_widget_show(gtkconv->close); |
|
| 7589 |
|
| 7590 gtk_widget_show(tabby); |
|
| 7591 gtk_widget_show(menu_tabby); |
|
| 7592 |
|
| 7593 if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) |
|
| 7594 gaim_gtkconv_update_buddy_icon(conv); |
|
| 7595 |
|
| 7596 /* Add this pane to the conversation's notebook. */ |
|
| 7597 gtk_notebook_append_page_menu(GTK_NOTEBOOK(win->notebook), tab_cont, tabby, menu_tabby); |
|
| 7598 gtk_notebook_set_tab_label_packing(GTK_NOTEBOOK(win->notebook), tab_cont, !tabs_side && !angle, TRUE, GTK_PACK_START); |
|
| 7599 |
|
| 7600 |
|
| 7601 gtk_widget_show(tab_cont); |
|
| 7602 |
|
| 7603 if (gaim_gtk_conv_window_get_gtkconv_count(win) == 1) { |
|
| 7604 /* Er, bug in notebooks? Switch to the page manually. */ |
|
| 7605 gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0); |
|
| 7606 |
|
| 7607 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), |
|
| 7608 gaim_prefs_get_bool("/gaim/gtk/conversations/tabs")); |
|
| 7609 } else |
|
| 7610 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), TRUE); |
|
| 7611 |
|
| 7612 focus_gtkconv = g_list_nth_data(gaim_gtk_conv_window_get_gtkconvs(win), |
|
| 7613 gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook))); |
|
| 7614 gtk_widget_grab_focus(focus_gtkconv->entry); |
|
| 7615 |
|
| 7616 if (gaim_gtk_conv_window_get_gtkconv_count(win) == 1) |
|
| 7617 update_send_to_selection(win); |
|
| 7618 } |
|
| 7619 |
|
| 7620 void |
|
| 7621 gaim_gtk_conv_window_remove_gtkconv(GaimGtkWindow *win, GaimGtkConversation *gtkconv) |
|
| 7622 { |
|
| 7623 unsigned int index; |
|
| 7624 GaimConversationType conv_type; |
|
| 7625 |
|
| 7626 conv_type = gaim_conversation_get_type(gtkconv->active_conv); |
|
| 7627 index = gtk_notebook_page_num(GTK_NOTEBOOK(win->notebook), gtkconv->tab_cont); |
|
| 7628 |
|
| 7629 g_object_ref(gtkconv->tab_cont); |
|
| 7630 gtk_object_sink(GTK_OBJECT(gtkconv->tab_cont)); |
|
| 7631 |
|
| 7632 gtk_notebook_remove_page(GTK_NOTEBOOK(win->notebook), index); |
|
| 7633 |
|
| 7634 /* go back to tabless if need be */ |
|
| 7635 if (gaim_gtk_conv_window_get_gtkconv_count(win) <= 2) { |
|
| 7636 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), |
|
| 7637 gaim_prefs_get_bool("/gaim/gtk/conversations/tabs")); |
|
| 7638 } |
|
| 7639 |
|
| 7640 win->gtkconvs = g_list_remove(win->gtkconvs, gtkconv); |
|
| 7641 |
|
| 7642 if (!win->gtkconvs && win != hidden_convwin) |
|
| 7643 gaim_gtk_conv_window_destroy(win); |
|
| 7644 } |
|
| 7645 |
|
| 7646 GaimGtkConversation * |
|
| 7647 gaim_gtk_conv_window_get_gtkconv_at_index(const GaimGtkWindow *win, int index) |
|
| 7648 { |
|
| 7649 GtkWidget *tab_cont; |
|
| 7650 |
|
| 7651 if (index == -1) |
|
| 7652 index = 0; |
|
| 7653 tab_cont = gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), index); |
|
| 7654 return tab_cont ? g_object_get_data(G_OBJECT(tab_cont), "GaimGtkConversation") : NULL; |
|
| 7655 } |
|
| 7656 |
|
| 7657 GaimGtkConversation * |
|
| 7658 gaim_gtk_conv_window_get_active_gtkconv(const GaimGtkWindow *win) |
|
| 7659 { |
|
| 7660 int index; |
|
| 7661 GtkWidget *tab_cont; |
|
| 7662 |
|
| 7663 index = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook)); |
|
| 7664 if (index == -1) |
|
| 7665 index = 0; |
|
| 7666 tab_cont = gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), index); |
|
| 7667 if (!tab_cont) |
|
| 7668 return NULL; |
|
| 7669 return g_object_get_data(G_OBJECT(tab_cont), "GaimGtkConversation"); |
|
| 7670 } |
|
| 7671 |
|
| 7672 |
|
| 7673 GaimConversation * |
|
| 7674 gaim_gtk_conv_window_get_active_conversation(const GaimGtkWindow *win) |
|
| 7675 { |
|
| 7676 GaimGtkConversation *gtkconv; |
|
| 7677 |
|
| 7678 gtkconv = gaim_gtk_conv_window_get_active_gtkconv(win); |
|
| 7679 return gtkconv ? gtkconv->active_conv : NULL; |
|
| 7680 } |
|
| 7681 |
|
| 7682 gboolean |
|
| 7683 gaim_gtk_conv_window_is_active_conversation(const GaimConversation *conv) |
|
| 7684 { |
|
| 7685 return conv == gaim_gtk_conv_window_get_active_conversation(GAIM_GTK_CONVERSATION(conv)->win); |
|
| 7686 } |
|
| 7687 |
|
| 7688 gboolean |
|
| 7689 gaim_gtk_conv_window_has_focus(GaimGtkWindow *win) |
|
| 7690 { |
|
| 7691 gboolean has_focus = FALSE; |
|
| 7692 |
|
| 7693 g_object_get(G_OBJECT(win->window), "has-toplevel-focus", &has_focus, NULL); |
|
| 7694 |
|
| 7695 return has_focus; |
|
| 7696 } |
|
| 7697 |
|
| 7698 GaimGtkWindow * |
|
| 7699 gaim_gtk_conv_window_get_at_xy(int x, int y) |
|
| 7700 { |
|
| 7701 GaimGtkWindow *win; |
|
| 7702 GdkWindow *gdkwin; |
|
| 7703 GList *l; |
|
| 7704 |
|
| 7705 gdkwin = gdk_window_at_pointer(&x, &y); |
|
| 7706 |
|
| 7707 if (gdkwin) |
|
| 7708 gdkwin = gdk_window_get_toplevel(gdkwin); |
|
| 7709 |
|
| 7710 for (l = gaim_gtk_conv_windows_get_list(); l != NULL; l = l->next) { |
|
| 7711 win = l->data; |
|
| 7712 |
|
| 7713 if (gdkwin == win->window->window) |
|
| 7714 return win; |
|
| 7715 } |
|
| 7716 |
|
| 7717 return NULL; |
|
| 7718 } |
|
| 7719 |
|
| 7720 GList * |
|
| 7721 gaim_gtk_conv_window_get_gtkconvs(GaimGtkWindow *win) |
|
| 7722 { |
|
| 7723 return win->gtkconvs; |
|
| 7724 } |
|
| 7725 |
|
| 7726 guint |
|
| 7727 gaim_gtk_conv_window_get_gtkconv_count(GaimGtkWindow *win) |
|
| 7728 { |
|
| 7729 return g_list_length(win->gtkconvs); |
|
| 7730 } |
|
| 7731 |
|
| 7732 GaimGtkWindow * |
|
| 7733 gaim_gtk_conv_window_first_with_type(GaimConversationType type) |
|
| 7734 { |
|
| 7735 GList *wins, *convs; |
|
| 7736 GaimGtkWindow *win; |
|
| 7737 GaimGtkConversation *conv; |
|
| 7738 |
|
| 7739 if (type == GAIM_CONV_TYPE_UNKNOWN) |
|
| 7740 return NULL; |
|
| 7741 |
|
| 7742 for (wins = gaim_gtk_conv_windows_get_list(); wins != NULL; wins = wins->next) { |
|
| 7743 win = wins->data; |
|
| 7744 |
|
| 7745 for (convs = win->gtkconvs; |
|
| 7746 convs != NULL; |
|
| 7747 convs = convs->next) { |
|
| 7748 |
|
| 7749 conv = convs->data; |
|
| 7750 |
|
| 7751 if (gaim_conversation_get_type(conv->active_conv) == type) |
|
| 7752 return win; |
|
| 7753 } |
|
| 7754 } |
|
| 7755 |
|
| 7756 return NULL; |
|
| 7757 } |
|
| 7758 |
|
| 7759 GaimGtkWindow * |
|
| 7760 gaim_gtk_conv_window_last_with_type(GaimConversationType type) |
|
| 7761 { |
|
| 7762 GList *wins, *convs; |
|
| 7763 GaimGtkWindow *win; |
|
| 7764 GaimGtkConversation *conv; |
|
| 7765 |
|
| 7766 if (type == GAIM_CONV_TYPE_UNKNOWN) |
|
| 7767 return NULL; |
|
| 7768 |
|
| 7769 for (wins = g_list_last(gaim_gtk_conv_windows_get_list()); |
|
| 7770 wins != NULL; |
|
| 7771 wins = wins->prev) { |
|
| 7772 |
|
| 7773 win = wins->data; |
|
| 7774 |
|
| 7775 for (convs = win->gtkconvs; |
|
| 7776 convs != NULL; |
|
| 7777 convs = convs->next) { |
|
| 7778 |
|
| 7779 conv = convs->data; |
|
| 7780 |
|
| 7781 if (gaim_conversation_get_type(conv->active_conv) == type) |
|
| 7782 return win; |
|
| 7783 } |
|
| 7784 } |
|
| 7785 |
|
| 7786 return NULL; |
|
| 7787 } |
|
| 7788 |
|
| 7789 |
|
| 7790 /************************************************************************** |
|
| 7791 * Conversation placement functions |
|
| 7792 **************************************************************************/ |
|
| 7793 typedef struct |
|
| 7794 { |
|
| 7795 char *id; |
|
| 7796 char *name; |
|
| 7797 GaimConvPlacementFunc fnc; |
|
| 7798 |
|
| 7799 } ConvPlacementData; |
|
| 7800 |
|
| 7801 static GList *conv_placement_fncs = NULL; |
|
| 7802 static GaimConvPlacementFunc place_conv = NULL; |
|
| 7803 |
|
| 7804 /* This one places conversations in the last made window. */ |
|
| 7805 static void |
|
| 7806 conv_placement_last_created_win(GaimGtkConversation *conv) |
|
| 7807 { |
|
| 7808 GaimGtkWindow *win; |
|
| 7809 |
|
| 7810 GList *l = g_list_last(gaim_gtk_conv_windows_get_list()); |
|
| 7811 win = l ? l->data : NULL;; |
|
| 7812 |
|
| 7813 if (win == NULL) { |
|
| 7814 win = gaim_gtk_conv_window_new(); |
|
| 7815 |
|
| 7816 gaim_gtk_conv_window_add_gtkconv(win, conv); |
|
| 7817 gaim_gtk_conv_window_show(win); |
|
| 7818 } else { |
|
| 7819 gaim_gtk_conv_window_add_gtkconv(win, conv); |
|
| 7820 } |
|
| 7821 } |
|
| 7822 |
|
| 7823 /* This one places conversations in the last made window of the same type. */ |
|
| 7824 static void |
|
| 7825 conv_placement_last_created_win_type(GaimGtkConversation *conv) |
|
| 7826 { |
|
| 7827 GaimGtkWindow *win; |
|
| 7828 |
|
| 7829 win = gaim_gtk_conv_window_last_with_type(gaim_conversation_get_type(conv->active_conv)); |
|
| 7830 |
|
| 7831 if (win == NULL) { |
|
| 7832 win = gaim_gtk_conv_window_new(); |
|
| 7833 |
|
| 7834 gaim_gtk_conv_window_add_gtkconv(win, conv); |
|
| 7835 gaim_gtk_conv_window_show(win); |
|
| 7836 } else |
|
| 7837 gaim_gtk_conv_window_add_gtkconv(win, conv); |
|
| 7838 } |
|
| 7839 |
|
| 7840 /* This one places each conversation in its own window. */ |
|
| 7841 static void |
|
| 7842 conv_placement_new_window(GaimGtkConversation *conv) |
|
| 7843 { |
|
| 7844 GaimGtkWindow *win; |
|
| 7845 |
|
| 7846 win = gaim_gtk_conv_window_new(); |
|
| 7847 |
|
| 7848 gaim_gtk_conv_window_add_gtkconv(win, conv); |
|
| 7849 |
|
| 7850 gaim_gtk_conv_window_show(win); |
|
| 7851 } |
|
| 7852 |
|
| 7853 static GaimGroup * |
|
| 7854 conv_get_group(GaimGtkConversation *conv) |
|
| 7855 { |
|
| 7856 GaimGroup *group = NULL; |
|
| 7857 |
|
| 7858 if (gaim_conversation_get_type(conv->active_conv) == GAIM_CONV_TYPE_IM) { |
|
| 7859 GaimBuddy *buddy; |
|
| 7860 |
|
| 7861 buddy = gaim_find_buddy(gaim_conversation_get_account(conv->active_conv), |
|
| 7862 gaim_conversation_get_name(conv->active_conv)); |
|
| 7863 |
|
| 7864 if (buddy != NULL) |
|
| 7865 group = gaim_buddy_get_group(buddy); |
|
| 7866 |
|
| 7867 } else if (gaim_conversation_get_type(conv->active_conv) == GAIM_CONV_TYPE_CHAT) { |
|
| 7868 GaimChat *chat; |
|
| 7869 |
|
| 7870 chat = gaim_blist_find_chat(gaim_conversation_get_account(conv->active_conv), |
|
| 7871 gaim_conversation_get_name(conv->active_conv)); |
|
| 7872 |
|
| 7873 if (chat != NULL) |
|
| 7874 group = gaim_chat_get_group(chat); |
|
| 7875 } |
|
| 7876 |
|
| 7877 return group; |
|
| 7878 } |
|
| 7879 |
|
| 7880 /* |
|
| 7881 * This groups things by, well, group. Buddies from groups will always be |
|
| 7882 * grouped together, and a buddy from a group not belonging to any currently |
|
| 7883 * open windows will get a new window. |
|
| 7884 */ |
|
| 7885 static void |
|
| 7886 conv_placement_by_group(GaimGtkConversation *conv) |
|
| 7887 { |
|
| 7888 GaimConversationType type; |
|
| 7889 GaimGroup *group = NULL; |
|
| 7890 GList *wl, *cl; |
|
| 7891 |
|
| 7892 type = gaim_conversation_get_type(conv->active_conv); |
|
| 7893 |
|
| 7894 group = conv_get_group(conv); |
|
| 7895 |
|
| 7896 /* Go through the list of IMs and find one with this group. */ |
|
| 7897 for (wl = gaim_gtk_conv_windows_get_list(); wl != NULL; wl = wl->next) { |
|
| 7898 GaimGtkWindow *win2; |
|
| 7899 GaimGtkConversation *conv2; |
|
| 7900 GaimGroup *group2 = NULL; |
|
| 7901 |
|
| 7902 win2 = wl->data; |
|
| 7903 |
|
| 7904 for (cl = win2->gtkconvs; |
|
| 7905 cl != NULL; |
|
| 7906 cl = cl->next) { |
|
| 7907 conv2 = cl->data; |
|
| 7908 |
|
| 7909 group2 = conv_get_group(conv2); |
|
| 7910 |
|
| 7911 if (group == group2) { |
|
| 7912 gaim_gtk_conv_window_add_gtkconv(win2, conv); |
|
| 7913 |
|
| 7914 return; |
|
| 7915 } |
|
| 7916 } |
|
| 7917 } |
|
| 7918 |
|
| 7919 /* Make a new window. */ |
|
| 7920 conv_placement_new_window(conv); |
|
| 7921 } |
|
| 7922 |
|
| 7923 /* This groups things by account. Otherwise, the same semantics as above */ |
|
| 7924 static void |
|
| 7925 conv_placement_by_account(GaimGtkConversation *conv) |
|
| 7926 { |
|
| 7927 GaimConversationType type; |
|
| 7928 GList *wins, *convs; |
|
| 7929 GaimAccount *account; |
|
| 7930 |
|
| 7931 account = gaim_conversation_get_account(conv->active_conv); |
|
| 7932 type = gaim_conversation_get_type(conv->active_conv); |
|
| 7933 |
|
| 7934 /* Go through the list of IMs and find one with this group. */ |
|
| 7935 for (wins = gaim_gtk_conv_windows_get_list(); wins != NULL; wins = wins->next) { |
|
| 7936 GaimGtkWindow *win2; |
|
| 7937 GaimGtkConversation *conv2; |
|
| 7938 |
|
| 7939 win2 = wins->data; |
|
| 7940 |
|
| 7941 for (convs = win2->gtkconvs; |
|
| 7942 convs != NULL; |
|
| 7943 convs = convs->next) { |
|
| 7944 conv2 = convs->data; |
|
| 7945 |
|
| 7946 if (account == gaim_conversation_get_account(conv2->active_conv)) { |
|
| 7947 gaim_gtk_conv_window_add_gtkconv(win2, conv); |
|
| 7948 return; |
|
| 7949 } |
|
| 7950 } |
|
| 7951 } |
|
| 7952 |
|
| 7953 /* Make a new window. */ |
|
| 7954 conv_placement_new_window(conv); |
|
| 7955 } |
|
| 7956 |
|
| 7957 static ConvPlacementData * |
|
| 7958 get_conv_placement_data(const char *id) |
|
| 7959 { |
|
| 7960 ConvPlacementData *data = NULL; |
|
| 7961 GList *n; |
|
| 7962 |
|
| 7963 for (n = conv_placement_fncs; n; n = n->next) { |
|
| 7964 data = n->data; |
|
| 7965 if (!strcmp(data->id, id)) |
|
| 7966 return data; |
|
| 7967 } |
|
| 7968 |
|
| 7969 return NULL; |
|
| 7970 } |
|
| 7971 |
|
| 7972 static void |
|
| 7973 add_conv_placement_fnc(const char *id, const char *name, |
|
| 7974 GaimConvPlacementFunc fnc) |
|
| 7975 { |
|
| 7976 ConvPlacementData *data; |
|
| 7977 |
|
| 7978 data = g_new(ConvPlacementData, 1); |
|
| 7979 |
|
| 7980 data->id = g_strdup(id); |
|
| 7981 data->name = g_strdup(name); |
|
| 7982 data->fnc = fnc; |
|
| 7983 |
|
| 7984 conv_placement_fncs = g_list_append(conv_placement_fncs, data); |
|
| 7985 } |
|
| 7986 |
|
| 7987 static void |
|
| 7988 ensure_default_funcs(void) |
|
| 7989 { |
|
| 7990 if (conv_placement_fncs == NULL) { |
|
| 7991 add_conv_placement_fnc("last", _("Last created window"), |
|
| 7992 conv_placement_last_created_win); |
|
| 7993 add_conv_placement_fnc("im_chat", _("Separate IM and Chat windows"), |
|
| 7994 conv_placement_last_created_win_type); |
|
| 7995 add_conv_placement_fnc("new", _("New window"), |
|
| 7996 conv_placement_new_window); |
|
| 7997 add_conv_placement_fnc("group", _("By group"), |
|
| 7998 conv_placement_by_group); |
|
| 7999 add_conv_placement_fnc("account", _("By account"), |
|
| 8000 conv_placement_by_account); |
|
| 8001 } |
|
| 8002 } |
|
| 8003 |
|
| 8004 GList * |
|
| 8005 gaim_gtkconv_placement_get_options(void) |
|
| 8006 { |
|
| 8007 GList *n, *list = NULL; |
|
| 8008 ConvPlacementData *data; |
|
| 8009 |
|
| 8010 ensure_default_funcs(); |
|
| 8011 |
|
| 8012 for (n = conv_placement_fncs; n; n = n->next) { |
|
| 8013 data = n->data; |
|
| 8014 list = g_list_append(list, data->name); |
|
| 8015 list = g_list_append(list, data->id); |
|
| 8016 } |
|
| 8017 |
|
| 8018 return list; |
|
| 8019 } |
|
| 8020 |
|
| 8021 |
|
| 8022 void |
|
| 8023 gaim_gtkconv_placement_add_fnc(const char *id, const char *name, |
|
| 8024 GaimConvPlacementFunc fnc) |
|
| 8025 { |
|
| 8026 g_return_if_fail(id != NULL); |
|
| 8027 g_return_if_fail(name != NULL); |
|
| 8028 g_return_if_fail(fnc != NULL); |
|
| 8029 |
|
| 8030 ensure_default_funcs(); |
|
| 8031 |
|
| 8032 add_conv_placement_fnc(id, name, fnc); |
|
| 8033 } |
|
| 8034 |
|
| 8035 void |
|
| 8036 gaim_gtkconv_placement_remove_fnc(const char *id) |
|
| 8037 { |
|
| 8038 ConvPlacementData *data = get_conv_placement_data(id); |
|
| 8039 |
|
| 8040 if (data == NULL) |
|
| 8041 return; |
|
| 8042 |
|
| 8043 conv_placement_fncs = g_list_remove(conv_placement_fncs, data); |
|
| 8044 |
|
| 8045 g_free(data->id); |
|
| 8046 g_free(data->name); |
|
| 8047 g_free(data); |
|
| 8048 } |
|
| 8049 |
|
| 8050 const char * |
|
| 8051 gaim_gtkconv_placement_get_name(const char *id) |
|
| 8052 { |
|
| 8053 ConvPlacementData *data; |
|
| 8054 |
|
| 8055 ensure_default_funcs(); |
|
| 8056 |
|
| 8057 data = get_conv_placement_data(id); |
|
| 8058 |
|
| 8059 if (data == NULL) |
|
| 8060 return NULL; |
|
| 8061 |
|
| 8062 return data->name; |
|
| 8063 } |
|
| 8064 |
|
| 8065 GaimConvPlacementFunc |
|
| 8066 gaim_gtkconv_placement_get_fnc(const char *id) |
|
| 8067 { |
|
| 8068 ConvPlacementData *data; |
|
| 8069 |
|
| 8070 ensure_default_funcs(); |
|
| 8071 |
|
| 8072 data = get_conv_placement_data(id); |
|
| 8073 |
|
| 8074 if (data == NULL) |
|
| 8075 return NULL; |
|
| 8076 |
|
| 8077 return data->fnc; |
|
| 8078 } |
|
| 8079 |
|
| 8080 void |
|
| 8081 gaim_gtkconv_placement_set_current_func(GaimConvPlacementFunc func) |
|
| 8082 { |
|
| 8083 g_return_if_fail(func != NULL); |
|
| 8084 |
|
| 8085 /* If tabs are enabled, set the function, otherwise, NULL it out. */ |
|
| 8086 if (gaim_prefs_get_bool("/gaim/gtk/conversations/tabs")) |
|
| 8087 place_conv = func; |
|
| 8088 else |
|
| 8089 place_conv = NULL; |
|
| 8090 } |
|
| 8091 |
|
| 8092 GaimConvPlacementFunc |
|
| 8093 gaim_gtkconv_placement_get_current_func(void) |
|
| 8094 { |
|
| 8095 return place_conv; |
|
| 8096 } |
|
| 8097 |
|
| 8098 void |
|
| 8099 gaim_gtkconv_placement_place(GaimGtkConversation *gtkconv) |
|
| 8100 { |
|
| 8101 if (place_conv) |
|
| 8102 place_conv(gtkconv); |
|
| 8103 else |
|
| 8104 conv_placement_new_window(gtkconv); |
|
| 8105 } |
|
| 8106 |
|
| 8107 gboolean |
|
| 8108 gaim_gtkconv_is_hidden(GaimGtkConversation *gtkconv) |
|
| 8109 { |
|
| 8110 g_return_val_if_fail(gtkconv != NULL, FALSE); |
|
| 8111 |
|
| 8112 return (gtkconv->win == hidden_convwin); |
|
| 8113 } |
|
| 8114 |
|
| 8115 |
|
| 8116 /* Algorithm from http://www.w3.org/TR/AERT#color-contrast */ |
|
| 8117 static gboolean |
|
| 8118 color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast) |
|
| 8119 { |
|
| 8120 gulong fg_brightness; |
|
| 8121 gulong bg_brightness; |
|
| 8122 gulong br_diff; |
|
| 8123 gulong col_diff; |
|
| 8124 int fred, fgreen, fblue, bred, bgreen, bblue; |
|
| 8125 |
|
| 8126 /* this algorithm expects colors between 0 and 255 for each of red green and blue. |
|
| 8127 * GTK on the other hand has values between 0 and 65535 |
|
| 8128 * Err suggested I >> 8, which grabbed the high bits. |
|
| 8129 */ |
|
| 8130 |
|
| 8131 fred = foreground.red >> 8 ; |
|
| 8132 fgreen = foreground.green >> 8 ; |
|
| 8133 fblue = foreground.blue >> 8 ; |
|
| 8134 |
|
| 8135 |
|
| 8136 bred = background.red >> 8 ; |
|
| 8137 bgreen = background.green >> 8 ; |
|
| 8138 bblue = background.blue >> 8 ; |
|
| 8139 |
|
| 8140 fg_brightness = (fred * 299 + fgreen * 587 + fblue * 114) / 1000; |
|
| 8141 bg_brightness = (bred * 299 + bgreen * 587 + bblue * 114) / 1000; |
|
| 8142 br_diff = abs(fg_brightness - bg_brightness); |
|
| 8143 |
|
| 8144 col_diff = abs(fred - bred) + abs(fgreen - bgreen) + abs(fblue - bblue); |
|
| 8145 |
|
| 8146 return ((col_diff > color_contrast) && (br_diff > brightness_contrast)); |
|
| 8147 } |
|
| 8148 |
|
| 8149 |
|
| 8150 static GdkColor* |
|
| 8151 generate_nick_colors(guint *color_count, GdkColor background) |
|
| 8152 { |
|
| 8153 guint numcolors = *color_count; |
|
| 8154 guint i = 0, j = 0; |
|
| 8155 GdkColor *colors = g_new(GdkColor, numcolors); |
|
| 8156 GdkColor nick_highlight; |
|
| 8157 GdkColor send_color; |
|
| 8158 time_t breakout_time; |
|
| 8159 |
|
| 8160 gdk_color_parse(HIGHLIGHT_COLOR, &nick_highlight); |
|
| 8161 gdk_color_parse(SEND_COLOR, &send_color); |
|
| 8162 |
|
| 8163 srand(background.red + background.green + background.blue + 1); |
|
| 8164 |
|
| 8165 breakout_time = time(NULL) + 3; |
|
| 8166 |
|
| 8167 /* first we look through the list of "good" colors: colors that differ from every other color in the |
|
| 8168 * list. only some of them will differ from the background color though. lets see if we can find |
|
| 8169 * numcolors of them that do |
|
| 8170 */ |
|
| 8171 while (i < numcolors && j < NUM_NICK_SEED_COLORS && time(NULL) < breakout_time) |
|
| 8172 { |
|
| 8173 GdkColor color = nick_seed_colors[j]; |
|
| 8174 |
|
| 8175 if (color_is_visible(color, background, MIN_COLOR_CONTRAST, MIN_BRIGHTNESS_CONTRAST) && |
|
| 8176 color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) && |
|
| 8177 color_is_visible(color, send_color, MIN_COLOR_CONTRAST / 4, 0)) |
|
| 8178 { |
|
| 8179 colors[i] = color; |
|
| 8180 i++; |
|
| 8181 } |
|
| 8182 j++; |
|
| 8183 } |
|
| 8184 |
|
| 8185 /* we might not have found numcolors in the last loop. if we did, we'll never enter this one. |
|
| 8186 * if we did not, lets just find some colors that don't conflict with the background. its |
|
| 8187 * expensive to find colors that not only don't conflict with the background, but also do not |
|
| 8188 * conflict with each other. |
|
| 8189 */ |
|
| 8190 while(i < numcolors && time(NULL) < breakout_time) |
|
| 8191 { |
|
| 8192 GdkColor color = { 0, rand() % 65536, rand() % 65536, rand() % 65536 }; |
|
| 8193 |
|
| 8194 gaim_debug(GAIM_DEBUG_WARNING, NULL, |
|
| 8195 "Looking for random colors to fill the list, I have found %i so far.\n",i); |
|
| 8196 |
|
| 8197 if (color_is_visible(color, background, MIN_COLOR_CONTRAST, MIN_BRIGHTNESS_CONTRAST) && |
|
| 8198 color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) && |
|
| 8199 color_is_visible(color, send_color, MIN_COLOR_CONTRAST / 4, 0)) |
|
| 8200 { |
|
| 8201 colors[i] = color; |
|
| 8202 i++; |
|
| 8203 } |
|
| 8204 } |
|
| 8205 |
|
| 8206 if (i < numcolors) { |
|
| 8207 GdkColor *c = colors; |
|
| 8208 gaim_debug(GAIM_DEBUG_WARNING, NULL, "Unable to generate enough random colors before timeout. %u colors found.\n", i); |
|
| 8209 colors = g_memdup(c, i * sizeof(GdkColor)); |
|
| 8210 g_free(c); |
|
| 8211 *color_count = i; |
|
| 8212 |
|
| 8213 } |
|
| 8214 |
|
| 8215 return colors; |
|
| 8216 } |
|