~bcurtiswx/ubuntu/precise/empathy/3.4.2.1-0ubuntu1

« back to all changes in this revision

Viewing changes to libempathy-gtk/gossip-chat.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons
  • Date: 2007-05-20 15:31:42 UTC
  • Revision ID: james.westby@ubuntu.com-20070520153142-r3auwguxdgxhktqb
Tags: upstream-0.4
ImportĀ upstreamĀ versionĀ 0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * Copyright (C) 2002-2007 Imendio AB
 
4
 * Copyright (C) 2007 Collabora Ltd.
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License as
 
8
 * published by the Free Software Foundation; either version 2 of the
 
9
 * License, or (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public
 
17
 * License along with this program; if not, write to the
 
18
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
19
 * Boston, MA 02111-1307, USA.
 
20
 * 
 
21
 * Authors: Mikael Hallendal <micke@imendio.com>
 
22
 *          Richard Hult <richard@imendio.com>
 
23
 *          Martyn Russell <martyn@imendio.com>
 
24
 *          Geert-Jan Van den Bogaerde <geertjan@gnome.org>
 
25
 *          Xavier Claessens <xclaesse@gmail.com>
 
26
 */
 
27
 
 
28
#include "config.h"
 
29
 
 
30
#include <string.h>
 
31
#include <stdlib.h>
 
32
 
 
33
#include <gdk/gdkkeysyms.h>
 
34
#include <glib/gi18n.h>
 
35
#include <gtk/gtk.h>
 
36
#include <glade/glade.h>
 
37
 
 
38
#include <libempathy/empathy-contact-manager.h>
 
39
#include <libempathy/gossip-debug.h>
 
40
#include <libempathy/gossip-utils.h>
 
41
#include <libempathy/gossip-conf.h>
 
42
#include <libempathy/empathy-marshal.h>
 
43
 
 
44
#include "gossip-chat.h"
 
45
#include "gossip-chat-window.h"
 
46
#include "gossip-geometry.h"
 
47
#include "gossip-preferences.h"
 
48
#include "gossip-spell.h"
 
49
//#include "gossip-spell-dialog.h"
 
50
#include "gossip-ui-utils.h"
 
51
 
 
52
#define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GOSSIP_TYPE_CHAT, GossipChatPriv))
 
53
 
 
54
#define DEBUG_DOMAIN "Chat"
 
55
 
 
56
#define CHAT_DIR_CREATE_MODE  (S_IRUSR | S_IWUSR | S_IXUSR)
 
57
#define CHAT_FILE_CREATE_MODE (S_IRUSR | S_IWUSR)
 
58
 
 
59
#define IS_ENTER(v) (v == GDK_Return || v == GDK_ISO_Enter || v == GDK_KP_Enter)
 
60
 
 
61
#define MAX_INPUT_HEIGHT 150
 
62
 
 
63
#define COMPOSING_STOP_TIMEOUT 5
 
64
 
 
65
struct _GossipChatPriv {
 
66
        EmpathyContactManager *manager;
 
67
        EmpathyTpChat         *tp_chat;
 
68
        GossipChatWindow      *window;
 
69
        GtkTooltips           *tooltips;
 
70
        guint                  composing_stop_timeout_id;
 
71
        gboolean               sensitive;
 
72
        gchar                 *id;
 
73
        GSList                *sent_messages;
 
74
        gint                   sent_messages_index;
 
75
        GList                 *compositors;
 
76
        guint                  scroll_idle_id;
 
77
        /* Used to automatically shrink a window that has temporarily
 
78
         * grown due to long input. 
 
79
         */
 
80
        gint                   padding_height;
 
81
        gint                   default_window_height;
 
82
        gint                   last_input_height;
 
83
        gboolean               vscroll_visible;
 
84
};
 
85
 
 
86
typedef struct {
 
87
        GossipChat  *chat;
 
88
        gchar       *word;
 
89
 
 
90
        GtkTextIter  start;
 
91
        GtkTextIter  end;
 
92
} GossipChatSpell;
 
93
 
 
94
static void             gossip_chat_class_init            (GossipChatClass *klass);
 
95
static void             gossip_chat_init                  (GossipChat      *chat);
 
96
static void             chat_finalize                     (GObject         *object);
 
97
static void             chat_destroy_cb                   (EmpathyTpChat   *tp_chat,
 
98
                                                           GossipChat      *chat);
 
99
static void             chat_send                         (GossipChat      *chat,
 
100
                                                           const gchar     *msg);
 
101
static void             chat_input_text_view_send         (GossipChat      *chat);
 
102
static void             chat_message_received_cb          (EmpathyTpChat   *tp_chat,
 
103
                                                           GossipMessage   *message,
 
104
                                                           GossipChat      *chat);
 
105
void                    chat_sent_message_add             (GossipChat      *chat,
 
106
                                                           const gchar     *str);
 
107
const gchar *           chat_sent_message_get_next        (GossipChat      *chat);
 
108
const gchar *           chat_sent_message_get_last        (GossipChat      *chat);
 
109
static gboolean         chat_input_key_press_event_cb     (GtkWidget       *widget,
 
110
                                                           GdkEventKey     *event,
 
111
                                                           GossipChat      *chat);
 
112
static void             chat_input_text_buffer_changed_cb (GtkTextBuffer   *buffer,
 
113
                                                           GossipChat      *chat);
 
114
static gboolean         chat_text_view_focus_in_event_cb  (GtkWidget       *widget,
 
115
                                                           GdkEvent        *event,
 
116
                                                           GossipChat      *chat);
 
117
static void             chat_text_view_scroll_hide_cb     (GtkWidget       *widget,
 
118
                                                           GossipChat      *chat);
 
119
static void             chat_text_view_size_allocate_cb   (GtkWidget       *widget,
 
120
                                                           GtkAllocation   *allocation,
 
121
                                                           GossipChat      *chat);
 
122
static void             chat_text_view_realize_cb         (GtkWidget       *widget,
 
123
                                                           GossipChat      *chat);
 
124
static void             chat_text_populate_popup_cb       (GtkTextView     *view,
 
125
                                                           GtkMenu         *menu,
 
126
                                                           GossipChat      *chat);
 
127
static void             chat_text_check_word_spelling_cb  (GtkMenuItem     *menuitem,
 
128
                                                           GossipChatSpell *chat_spell);
 
129
static GossipChatSpell *chat_spell_new                    (GossipChat      *chat,
 
130
                                                           const gchar     *word,
 
131
                                                           GtkTextIter      start,
 
132
                                                           GtkTextIter      end);
 
133
static void             chat_spell_free                   (GossipChatSpell *chat_spell);
 
134
static void             chat_composing_start              (GossipChat      *chat);
 
135
static void             chat_composing_stop               (GossipChat      *chat);
 
136
static void             chat_composing_remove_timeout     (GossipChat      *chat);
 
137
static gboolean         chat_composing_stop_timeout_cb    (GossipChat      *chat);
 
138
static void             chat_state_changed_cb             (EmpathyTpChat   *tp_chat,
 
139
                                                           GossipContact   *contact,
 
140
                                                           TelepathyChannelChatState  state,
 
141
                                                           GossipChat      *chat);
 
142
static gboolean         chat_scroll_down_idle_func        (GossipChat      *chat);
 
143
 
 
144
enum {
 
145
        COMPOSING,
 
146
        NEW_MESSAGE,
 
147
        NAME_CHANGED,
 
148
        STATUS_CHANGED,
 
149
        LAST_SIGNAL
 
150
};
 
151
 
 
152
static guint signals[LAST_SIGNAL] = { 0 };
 
153
 
 
154
G_DEFINE_TYPE (GossipChat, gossip_chat, G_TYPE_OBJECT);
 
155
 
 
156
static void
 
157
gossip_chat_class_init (GossipChatClass *klass)
 
158
{
 
159
        GObjectClass *object_class;
 
160
 
 
161
        object_class = G_OBJECT_CLASS (klass);
 
162
 
 
163
        object_class->finalize = chat_finalize;
 
164
 
 
165
        signals[COMPOSING] =
 
166
                g_signal_new ("composing",
 
167
                              G_OBJECT_CLASS_TYPE (object_class),
 
168
                              G_SIGNAL_RUN_LAST,
 
169
                              0,
 
170
                              NULL, NULL,
 
171
                              g_cclosure_marshal_VOID__BOOLEAN,
 
172
                              G_TYPE_NONE,
 
173
                              1, G_TYPE_BOOLEAN);
 
174
 
 
175
        signals[NEW_MESSAGE] =
 
176
                g_signal_new ("new-message",
 
177
                              G_OBJECT_CLASS_TYPE (object_class),
 
178
                              G_SIGNAL_RUN_LAST,
 
179
                              0,
 
180
                              NULL, NULL,
 
181
                              empathy_marshal_VOID__OBJECT_BOOLEAN,
 
182
                              G_TYPE_NONE,
 
183
                              2, GOSSIP_TYPE_MESSAGE, G_TYPE_BOOLEAN);
 
184
 
 
185
        signals[NAME_CHANGED] =
 
186
                g_signal_new ("name-changed",
 
187
                              G_OBJECT_CLASS_TYPE (object_class),
 
188
                              G_SIGNAL_RUN_LAST,
 
189
                              0,
 
190
                              NULL, NULL,
 
191
                              g_cclosure_marshal_VOID__POINTER,
 
192
                              G_TYPE_NONE,
 
193
                              1, G_TYPE_POINTER);
 
194
 
 
195
        signals[STATUS_CHANGED] =
 
196
                g_signal_new ("status-changed",
 
197
                              G_OBJECT_CLASS_TYPE (object_class),
 
198
                              G_SIGNAL_RUN_LAST,
 
199
                              0,
 
200
                              NULL, NULL,
 
201
                              g_cclosure_marshal_VOID__VOID,
 
202
                              G_TYPE_NONE,
 
203
                              0);
 
204
 
 
205
        g_type_class_add_private (object_class, sizeof (GossipChatPriv));
 
206
}
 
207
 
 
208
static void
 
209
gossip_chat_init (GossipChat *chat)
 
210
{
 
211
        GossipChatPriv *priv;
 
212
        GtkTextBuffer  *buffer;
 
213
 
 
214
        chat->view = gossip_chat_view_new ();
 
215
        chat->input_text_view = gtk_text_view_new ();
 
216
 
 
217
        chat->is_first_char = TRUE;
 
218
 
 
219
        g_object_set (chat->input_text_view,
 
220
                      "pixels-above-lines", 2,
 
221
                      "pixels-below-lines", 2,
 
222
                      "pixels-inside-wrap", 1,
 
223
                      "right-margin", 2,
 
224
                      "left-margin", 2,
 
225
                      "wrap-mode", GTK_WRAP_WORD_CHAR,
 
226
                      NULL);
 
227
 
 
228
        priv = GET_PRIV (chat);
 
229
 
 
230
        priv->manager = empathy_contact_manager_new ();
 
231
        priv->tooltips = gtk_tooltips_new ();
 
232
        priv->default_window_height = -1;
 
233
        priv->vscroll_visible = FALSE;
 
234
        priv->sensitive = TRUE;
 
235
        priv->sent_messages = NULL;
 
236
        priv->sent_messages_index = -1;
 
237
 
 
238
        g_signal_connect (chat->input_text_view,
 
239
                          "key_press_event",
 
240
                          G_CALLBACK (chat_input_key_press_event_cb),
 
241
                          chat);
 
242
 
 
243
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
244
        g_signal_connect (buffer,
 
245
                          "changed",
 
246
                          G_CALLBACK (chat_input_text_buffer_changed_cb),
 
247
                          chat);
 
248
        g_signal_connect (chat->view,
 
249
                          "focus_in_event",
 
250
                          G_CALLBACK (chat_text_view_focus_in_event_cb),
 
251
                          chat);
 
252
 
 
253
        g_signal_connect (chat->input_text_view,
 
254
                          "size_allocate",
 
255
                          G_CALLBACK (chat_text_view_size_allocate_cb),
 
256
                          chat);
 
257
 
 
258
        g_signal_connect (chat->input_text_view,
 
259
                          "realize",
 
260
                          G_CALLBACK (chat_text_view_realize_cb),
 
261
                          chat);
 
262
 
 
263
        g_signal_connect (GTK_TEXT_VIEW (chat->input_text_view),
 
264
                          "populate_popup",
 
265
                          G_CALLBACK (chat_text_populate_popup_cb),
 
266
                          chat);
 
267
 
 
268
        /* create misspelt words identification tag */
 
269
        gtk_text_buffer_create_tag (buffer,
 
270
                                    "misspelled",
 
271
                                    "underline", PANGO_UNDERLINE_ERROR,
 
272
                                    NULL);
 
273
 
 
274
 
 
275
 
 
276
        /* Turn off scrolling temporarily */
 
277
        gossip_chat_view_scroll (chat->view, FALSE);
 
278
#if 0
 
279
FIXME:
 
280
        /* Add messages from last conversation */
 
281
        log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
 
282
        messages = gossip_log_get_last_for_contact (log_manager, priv->contact);
 
283
        num_messages  = g_list_length (messages);
 
284
 
 
285
        for (l = messages, i = 0; l; l = l->next, i++) {
 
286
                message = l->data;
 
287
 
 
288
                if (num_messages - i > 10) {
 
289
                        continue;
 
290
                }
 
291
 
 
292
                sender = gossip_message_get_sender (message);
 
293
                if (gossip_contact_equal (priv->own_contact, sender)) {
 
294
                        gossip_chat_view_append_message_from_self (view,
 
295
                                                                   message,
 
296
                                                                   priv->own_contact,
 
297
                                                                   priv->own_avatar);
 
298
                } else {
 
299
                        gossip_chat_view_append_message_from_other (view,
 
300
                                                                    message,
 
301
                                                                    sender,
 
302
                                                                    priv->other_avatar);
 
303
                }
 
304
        }
 
305
 
 
306
        g_list_foreach (messages, (GFunc) g_object_unref, NULL);
 
307
        g_list_free (messages);
 
308
#endif
 
309
        /* Turn back on scrolling */
 
310
        gossip_chat_view_scroll (chat->view, TRUE);
 
311
 
 
312
        /* Scroll to the most recent messages, we reference the chat
 
313
         * for the duration of the scroll func.
 
314
         */
 
315
        priv->scroll_idle_id = g_idle_add ((GSourceFunc) chat_scroll_down_idle_func, 
 
316
                                           g_object_ref (chat));
 
317
 
 
318
}
 
319
 
 
320
static void
 
321
chat_finalize (GObject *object)
 
322
{
 
323
        GossipChat     *chat;
 
324
        GossipChatPriv *priv;
 
325
 
 
326
        chat = GOSSIP_CHAT (object);
 
327
        priv = GET_PRIV (chat);
 
328
 
 
329
        gossip_debug (DEBUG_DOMAIN, "Finalized: %p", object);
 
330
 
 
331
        g_slist_foreach (priv->sent_messages, (GFunc) g_free, NULL);
 
332
        g_slist_free (priv->sent_messages);
 
333
 
 
334
        g_list_foreach (priv->compositors, (GFunc) g_object_unref, NULL);
 
335
        g_list_free (priv->compositors);
 
336
 
 
337
        chat_composing_remove_timeout (chat);
 
338
        g_object_unref (chat->account);
 
339
        g_object_unref (priv->manager);
 
340
 
 
341
        if (priv->tp_chat) {
 
342
                g_object_unref (priv->tp_chat);
 
343
        }
 
344
 
 
345
        if (priv->scroll_idle_id) {
 
346
                g_source_remove (priv->scroll_idle_id);
 
347
        }
 
348
 
 
349
        g_free (priv->id);
 
350
 
 
351
        G_OBJECT_CLASS (gossip_chat_parent_class)->finalize (object);
 
352
}
 
353
 
 
354
static void
 
355
chat_destroy_cb (EmpathyTpChat *tp_chat,
 
356
                 GossipChat    *chat)
 
357
{
 
358
        GossipChatPriv *priv;
 
359
        GtkWidget      *widget;
 
360
 
 
361
        priv = GET_PRIV (chat);
 
362
 
 
363
        if (priv->tp_chat) {
 
364
                g_object_unref (priv->tp_chat);
 
365
                priv->tp_chat = NULL;
 
366
        }
 
367
 
 
368
        gossip_chat_view_append_event (chat->view, _("Disconnected"));
 
369
 
 
370
        widget = gossip_chat_get_widget (chat);
 
371
        gtk_widget_set_sensitive (widget, FALSE);
 
372
        priv->sensitive = FALSE;
 
373
}
 
374
 
 
375
static void
 
376
chat_send (GossipChat  *chat,
 
377
           const gchar *msg)
 
378
{
 
379
        GossipChatPriv   *priv;
 
380
        //GossipLogManager *log_manager;
 
381
        GossipMessage    *message;
 
382
        GossipContact    *own_contact;
 
383
 
 
384
        priv = GET_PRIV (chat);
 
385
 
 
386
        if (G_STR_EMPTY (msg)) {
 
387
                return;
 
388
        }
 
389
 
 
390
        chat_sent_message_add (chat, msg);
 
391
 
 
392
        if (g_str_has_prefix (msg, "/clear")) {
 
393
                gossip_chat_view_clear (chat->view);
 
394
                return;
 
395
        }
 
396
 
 
397
        /* FIXME: add here something to let group/privrate chat handle
 
398
         *        some special messages */
 
399
 
 
400
        /* FIXME: gossip_app_set_not_away ();*/
 
401
 
 
402
        own_contact = empathy_contact_manager_get_user (priv->manager, chat->account);
 
403
        message = gossip_message_new (msg);
 
404
        gossip_message_set_sender (message, own_contact);
 
405
 
 
406
        //FIXME: log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
 
407
        //gossip_log_message_for_contact (log_manager, message, FALSE);
 
408
 
 
409
        empathy_tp_chat_send (priv->tp_chat, message);
 
410
 
 
411
        g_object_unref (message);
 
412
}
 
413
 
 
414
static void
 
415
chat_input_text_view_send (GossipChat *chat)
 
416
{
 
417
        GossipChatPriv *priv;
 
418
        GtkTextBuffer  *buffer;
 
419
        GtkTextIter     start, end;
 
420
        gchar          *msg;
 
421
 
 
422
        priv = GET_PRIV (chat);
 
423
 
 
424
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
425
 
 
426
        gtk_text_buffer_get_bounds (buffer, &start, &end);
 
427
        msg = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
 
428
 
 
429
        /* clear the input field */
 
430
        gtk_text_buffer_set_text (buffer, "", -1);
 
431
 
 
432
        chat_send (chat, msg);
 
433
 
 
434
        g_free (msg);
 
435
 
 
436
        chat->is_first_char = TRUE;
 
437
}
 
438
 
 
439
static void
 
440
chat_message_received_cb (EmpathyTpChat *tp_chat,
 
441
                          GossipMessage *message,
 
442
                          GossipChat    *chat)
 
443
{
 
444
        GossipChatPriv *priv;
 
445
        //GossipLogManager      *log_manager;
 
446
        GossipContact         *sender;
 
447
 
 
448
        priv = GET_PRIV (chat);
 
449
 
 
450
        sender = gossip_message_get_sender (message);
 
451
        gossip_debug (DEBUG_DOMAIN, "Appending message ('%s')",
 
452
                      gossip_contact_get_name (sender));
 
453
 
 
454
/*FIXME:
 
455
        log_manager = gossip_session_get_log_manager (gossip_app_get_session ());
 
456
        gossip_log_message_for_contact (log_manager, message, TRUE);
 
457
*/
 
458
        gossip_chat_view_append_message (chat->view, message);
 
459
 
 
460
        if (gossip_chat_should_play_sound (chat)) {
 
461
                // FIXME: gossip_sound_play (GOSSIP_SOUND_CHAT);
 
462
        }
 
463
 
 
464
        g_signal_emit (chat, signals[NEW_MESSAGE], 0, message, FALSE);
 
465
}
 
466
 
 
467
void 
 
468
chat_sent_message_add (GossipChat  *chat,
 
469
                       const gchar *str)
 
470
{
 
471
        GossipChatPriv *priv;
 
472
        GSList         *list;
 
473
        GSList         *item;
 
474
 
 
475
        priv = GET_PRIV (chat);
 
476
 
 
477
        /* Save the sent message in our repeat buffer */
 
478
        list = priv->sent_messages;
 
479
        
 
480
        /* Remove any other occurances of this msg */
 
481
        while ((item = g_slist_find_custom (list, str, (GCompareFunc) strcmp)) != NULL) {
 
482
                list = g_slist_remove_link (list, item);
 
483
                g_free (item->data);
 
484
                g_slist_free1 (item);
 
485
        }
 
486
 
 
487
        /* Trim the list to the last 10 items */
 
488
        while (g_slist_length (list) > 10) {
 
489
                item = g_slist_last (list);
 
490
                if (item) {
 
491
                        list = g_slist_remove_link (list, item);
 
492
                        g_free (item->data);
 
493
                        g_slist_free1 (item);
 
494
                }
 
495
        }
 
496
 
 
497
        /* Add new message */
 
498
        list = g_slist_prepend (list, g_strdup (str));
 
499
 
 
500
        /* Set list and reset the index */
 
501
        priv->sent_messages = list;
 
502
        priv->sent_messages_index = -1;
 
503
}
 
504
 
 
505
const gchar *
 
506
chat_sent_message_get_next (GossipChat *chat)
 
507
{
 
508
        GossipChatPriv *priv;
 
509
        gint            max;
 
510
        
 
511
        priv = GET_PRIV (chat);
 
512
 
 
513
        if (!priv->sent_messages) {
 
514
                gossip_debug (DEBUG_DOMAIN, 
 
515
                              "No sent messages, next message is NULL");
 
516
                return NULL;
 
517
        }
 
518
 
 
519
        max = g_slist_length (priv->sent_messages) - 1;
 
520
 
 
521
        if (priv->sent_messages_index < max) {
 
522
                priv->sent_messages_index++;
 
523
        }
 
524
        
 
525
        gossip_debug (DEBUG_DOMAIN, 
 
526
                      "Returning next message index:%d",
 
527
                      priv->sent_messages_index);
 
528
 
 
529
        return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index);
 
530
}
 
531
 
 
532
const gchar *
 
533
chat_sent_message_get_last (GossipChat *chat)
 
534
{
 
535
        GossipChatPriv *priv;
 
536
 
 
537
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
 
538
 
 
539
        priv = GET_PRIV (chat);
 
540
        
 
541
        if (!priv->sent_messages) {
 
542
                gossip_debug (DEBUG_DOMAIN, 
 
543
                              "No sent messages, last message is NULL");
 
544
                return NULL;
 
545
        }
 
546
 
 
547
        if (priv->sent_messages_index >= 0) {
 
548
                priv->sent_messages_index--;
 
549
        }
 
550
 
 
551
        gossip_debug (DEBUG_DOMAIN, 
 
552
                      "Returning last message index:%d",
 
553
                      priv->sent_messages_index);
 
554
 
 
555
        return g_slist_nth_data (priv->sent_messages, priv->sent_messages_index);
 
556
}
 
557
 
 
558
static gboolean
 
559
chat_input_key_press_event_cb (GtkWidget   *widget,
 
560
                               GdkEventKey *event,
 
561
                               GossipChat  *chat)
 
562
{
 
563
        GossipChatPriv *priv;
 
564
        GtkAdjustment  *adj;
 
565
        gdouble         val;
 
566
        GtkWidget      *text_view_sw;
 
567
 
 
568
        priv = GET_PRIV (chat);
 
569
 
 
570
        if (event->keyval == GDK_Tab && !(event->state & GDK_CONTROL_MASK)) {
 
571
                return TRUE;
 
572
        }
 
573
 
 
574
        /* Catch ctrl+up/down so we can traverse messages we sent */
 
575
        if ((event->state & GDK_CONTROL_MASK) && 
 
576
            (event->keyval == GDK_Up || 
 
577
             event->keyval == GDK_Down)) {
 
578
                GtkTextBuffer *buffer;
 
579
                const gchar   *str;
 
580
 
 
581
                buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
582
 
 
583
                if (event->keyval == GDK_Up) {
 
584
                        str = chat_sent_message_get_next (chat);
 
585
                } else {
 
586
                        str = chat_sent_message_get_last (chat);
 
587
                }
 
588
 
 
589
                g_signal_handlers_block_by_func (buffer, 
 
590
                                                 chat_input_text_buffer_changed_cb,
 
591
                                                 chat);
 
592
                gtk_text_buffer_set_text (buffer, str ? str : "", -1);
 
593
                g_signal_handlers_unblock_by_func (buffer, 
 
594
                                                   chat_input_text_buffer_changed_cb,
 
595
                                                   chat);
 
596
 
 
597
                return TRUE;    
 
598
        }
 
599
 
 
600
        /* Catch enter but not ctrl/shift-enter */
 
601
        if (IS_ENTER (event->keyval) && !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
 
602
                GtkTextView *view;
 
603
 
 
604
                /* This is to make sure that kinput2 gets the enter. And if
 
605
                 * it's handled there we shouldn't send on it. This is because
 
606
                 * kinput2 uses Enter to commit letters. See:
 
607
                 * http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=104299
 
608
                 */
 
609
 
 
610
                view = GTK_TEXT_VIEW (chat->input_text_view);
 
611
                if (gtk_im_context_filter_keypress (view->im_context, event)) {
 
612
                        GTK_TEXT_VIEW (chat->input_text_view)->need_im_reset = TRUE;
 
613
                        return TRUE;
 
614
                }
 
615
 
 
616
                chat_input_text_view_send (chat);
 
617
                return TRUE;
 
618
        }
 
619
 
 
620
        text_view_sw = gtk_widget_get_parent (GTK_WIDGET (chat->view));
 
621
 
 
622
        if (IS_ENTER (event->keyval) && (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
 
623
                /* Newline for shift-enter. */
 
624
                return FALSE;
 
625
        }
 
626
        else if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK &&
 
627
                 event->keyval == GDK_Page_Up) {
 
628
                adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw));
 
629
                gtk_adjustment_set_value (adj, adj->value - adj->page_size);
 
630
 
 
631
                return TRUE;
 
632
        }
 
633
        else if ((event->state & GDK_CONTROL_MASK) != GDK_CONTROL_MASK &&
 
634
                 event->keyval == GDK_Page_Down) {
 
635
                adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (text_view_sw));
 
636
                val = MIN (adj->value + adj->page_size, adj->upper - adj->page_size);
 
637
                gtk_adjustment_set_value (adj, val);
 
638
 
 
639
                return TRUE;
 
640
        }
 
641
 
 
642
        return FALSE;
 
643
}
 
644
 
 
645
static gboolean
 
646
chat_text_view_focus_in_event_cb (GtkWidget  *widget,
 
647
                                  GdkEvent   *event,
 
648
                                  GossipChat *chat)
 
649
{
 
650
        gtk_widget_grab_focus (chat->input_text_view);
 
651
 
 
652
        return TRUE;
 
653
}
 
654
 
 
655
static void
 
656
chat_input_text_buffer_changed_cb (GtkTextBuffer *buffer,
 
657
                                   GossipChat    *chat)
 
658
{
 
659
        GossipChatPriv *priv;
 
660
        GtkTextIter     start, end;
 
661
        gchar          *str;
 
662
        gboolean        spell_checker = FALSE;
 
663
 
 
664
        priv = GET_PRIV (chat);
 
665
 
 
666
        if (gtk_text_buffer_get_char_count (buffer) == 0) {
 
667
                chat_composing_stop (chat);
 
668
        } else {
 
669
                chat_composing_start (chat);
 
670
        }
 
671
 
 
672
        gossip_conf_get_bool (gossip_conf_get (),
 
673
                              GOSSIP_PREFS_CHAT_SPELL_CHECKER_ENABLED,
 
674
                              &spell_checker);
 
675
 
 
676
        if (chat->is_first_char) {
 
677
                GtkRequisition  req;
 
678
                gint            window_height;
 
679
                GtkWidget      *dialog;
 
680
                GtkAllocation  *allocation;
 
681
 
 
682
                /* Save the window's size */
 
683
                dialog = gossip_chat_window_get_dialog (priv->window);
 
684
                gtk_window_get_size (GTK_WINDOW (dialog),
 
685
                                     NULL, &window_height);
 
686
 
 
687
                gtk_widget_size_request (chat->input_text_view, &req);
 
688
 
 
689
                allocation = &GTK_WIDGET (chat->view)->allocation;
 
690
 
 
691
                priv->default_window_height = window_height;
 
692
                priv->last_input_height = req.height;
 
693
                priv->padding_height = window_height - req.height - allocation->height;
 
694
 
 
695
                chat->is_first_char = FALSE;
 
696
        }
 
697
 
 
698
        gtk_text_buffer_get_start_iter (buffer, &start);
 
699
 
 
700
        if (!spell_checker) {
 
701
                gtk_text_buffer_get_end_iter (buffer, &end);
 
702
                gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
 
703
                return;
 
704
        }
 
705
 
 
706
        if (!gossip_spell_supported ()) {
 
707
                return;
 
708
        }
 
709
 
 
710
        /* NOTE: this is really inefficient, we shouldn't have to
 
711
           reiterate the whole buffer each time and check each work
 
712
           every time. */
 
713
        while (TRUE) {
 
714
                gboolean correct = FALSE;
 
715
 
 
716
                /* if at start */
 
717
                if (gtk_text_iter_is_start (&start)) {
 
718
                        end = start;
 
719
 
 
720
                        if (!gtk_text_iter_forward_word_end (&end)) {
 
721
                                /* no whole word yet */
 
722
                                break;
 
723
                        }
 
724
                } else {
 
725
                        if (!gtk_text_iter_forward_word_end (&end)) {
 
726
                                /* must be the end of the buffer */
 
727
                                break;
 
728
                        }
 
729
 
 
730
                        start = end;
 
731
                        gtk_text_iter_backward_word_start (&start);
 
732
                }
 
733
 
 
734
                str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
 
735
 
 
736
                /* spell check string */
 
737
                if (!gossip_chat_get_is_command (str)) {
 
738
                        correct = gossip_spell_check (str);
 
739
                } else {
 
740
                        correct = TRUE;
 
741
                }
 
742
 
 
743
                if (!correct) {
 
744
                        gtk_text_buffer_apply_tag_by_name (buffer, "misspelled", &start, &end);
 
745
                } else {
 
746
                        gtk_text_buffer_remove_tag_by_name (buffer, "misspelled", &start, &end);
 
747
                }
 
748
 
 
749
                g_free (str);
 
750
 
 
751
                /* set start iter to the end iters position */
 
752
                start = end;
 
753
        }
 
754
}
 
755
 
 
756
typedef struct {
 
757
        GtkWidget *window;
 
758
        gint       width;
 
759
        gint       height;
 
760
} ChangeSizeData;
 
761
 
 
762
static gboolean
 
763
chat_change_size_in_idle_cb (ChangeSizeData *data)
 
764
{
 
765
        gtk_window_resize (GTK_WINDOW (data->window),
 
766
                           data->width, data->height);
 
767
 
 
768
        return FALSE;
 
769
}
 
770
 
 
771
static void
 
772
chat_text_view_scroll_hide_cb (GtkWidget  *widget,
 
773
                               GossipChat *chat)
 
774
{
 
775
        GossipChatPriv *priv;
 
776
        GtkWidget      *sw;
 
777
 
 
778
        priv = GET_PRIV (chat);
 
779
 
 
780
        priv->vscroll_visible = FALSE;
 
781
        g_signal_handlers_disconnect_by_func (widget, chat_text_view_scroll_hide_cb, chat);
 
782
 
 
783
        sw = gtk_widget_get_parent (chat->input_text_view);
 
784
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
 
785
                                        GTK_POLICY_NEVER,
 
786
                                        GTK_POLICY_NEVER);
 
787
        g_object_set (sw, "height-request", -1, NULL);
 
788
}
 
789
 
 
790
static void
 
791
chat_text_view_size_allocate_cb (GtkWidget     *widget,
 
792
                                 GtkAllocation *allocation,
 
793
                                 GossipChat    *chat)
 
794
{
 
795
        GossipChatPriv *priv;
 
796
        gint            width;
 
797
        GtkWidget      *dialog;
 
798
        ChangeSizeData *data;
 
799
        gint            window_height;
 
800
        gint            new_height;
 
801
        GtkAllocation  *view_allocation;
 
802
        gint            current_height;
 
803
        gint            diff;
 
804
        GtkWidget      *sw;
 
805
 
 
806
        priv = GET_PRIV (chat);
 
807
 
 
808
        if (priv->default_window_height <= 0) {
 
809
                return;
 
810
        }
 
811
 
 
812
        sw = gtk_widget_get_parent (widget);
 
813
        if (sw->allocation.height >= MAX_INPUT_HEIGHT && !priv->vscroll_visible) {
 
814
                GtkWidget *vscroll;
 
815
 
 
816
                priv->vscroll_visible = TRUE;
 
817
                gtk_widget_set_size_request (sw, sw->allocation.width, MAX_INPUT_HEIGHT);
 
818
                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
 
819
                                                GTK_POLICY_NEVER,
 
820
                                                GTK_POLICY_AUTOMATIC);
 
821
                vscroll = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sw));
 
822
                g_signal_connect (vscroll, "hide",
 
823
                                  G_CALLBACK (chat_text_view_scroll_hide_cb),
 
824
                                  chat);
 
825
        }
 
826
 
 
827
        if (priv->last_input_height <= allocation->height) {
 
828
                priv->last_input_height = allocation->height;
 
829
                return;
 
830
        }
 
831
 
 
832
        diff = priv->last_input_height - allocation->height;
 
833
        priv->last_input_height = allocation->height;
 
834
 
 
835
        view_allocation = &GTK_WIDGET (chat->view)->allocation;
 
836
 
 
837
        dialog = gossip_chat_window_get_dialog (priv->window);
 
838
        gtk_window_get_size (GTK_WINDOW (dialog), NULL, &current_height);
 
839
 
 
840
        new_height = view_allocation->height + priv->padding_height + allocation->height - diff;
 
841
 
 
842
        if (new_height <= priv->default_window_height) {
 
843
                window_height = priv->default_window_height;
 
844
        } else {
 
845
                window_height = new_height;
 
846
        }
 
847
 
 
848
        if (current_height <= window_height) {
 
849
                return;
 
850
        }
 
851
 
 
852
        /* Restore the window's size */
 
853
        gtk_window_get_size (GTK_WINDOW (dialog), &width, NULL);
 
854
 
 
855
        data = g_new0 (ChangeSizeData, 1);
 
856
        data->window = dialog;
 
857
        data->width  = width;
 
858
        data->height = window_height;
 
859
 
 
860
        g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
 
861
                         (GSourceFunc) chat_change_size_in_idle_cb,
 
862
                         data, g_free);
 
863
}
 
864
 
 
865
static void
 
866
chat_text_view_realize_cb (GtkWidget  *widget,
 
867
                           GossipChat *chat)
 
868
{
 
869
        gossip_debug (DEBUG_DOMAIN, "Setting focus to the input text view");
 
870
        gtk_widget_grab_focus (widget);
 
871
}
 
872
 
 
873
static void
 
874
chat_insert_smiley_activate_cb (GtkWidget  *menuitem,
 
875
                                GossipChat *chat)
 
876
{
 
877
        GtkTextBuffer *buffer;
 
878
        GtkTextIter    iter;
 
879
        const gchar   *smiley;
 
880
 
 
881
        smiley = g_object_get_data (G_OBJECT (menuitem), "smiley_text");
 
882
 
 
883
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
884
 
 
885
        gtk_text_buffer_get_end_iter (buffer, &iter);
 
886
        gtk_text_buffer_insert (buffer, &iter, smiley, -1);
 
887
 
 
888
        gtk_text_buffer_get_end_iter (buffer, &iter);
 
889
        gtk_text_buffer_insert (buffer, &iter, " ", -1);
 
890
}
 
891
 
 
892
static void
 
893
chat_text_populate_popup_cb (GtkTextView *view,
 
894
                             GtkMenu     *menu,
 
895
                             GossipChat  *chat)
 
896
{
 
897
        GossipChatPriv  *priv;
 
898
        GtkTextBuffer   *buffer;
 
899
        GtkTextTagTable *table;
 
900
        GtkTextTag      *tag;
 
901
        gint             x, y;
 
902
        GtkTextIter      iter, start, end;
 
903
        GtkWidget       *item;
 
904
        gchar           *str = NULL;
 
905
        GossipChatSpell *chat_spell;
 
906
        GtkWidget       *smiley_menu;
 
907
 
 
908
        priv = GET_PRIV (chat);
 
909
 
 
910
        /* Add the emoticon menu. */
 
911
        item = gtk_separator_menu_item_new ();
 
912
        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 
913
        gtk_widget_show (item);
 
914
 
 
915
        item = gtk_menu_item_new_with_mnemonic (_("Insert Smiley"));
 
916
        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 
917
        gtk_widget_show (item);
 
918
 
 
919
        smiley_menu = gossip_chat_view_get_smiley_menu (
 
920
                G_CALLBACK (chat_insert_smiley_activate_cb),
 
921
                chat,
 
922
                priv->tooltips);
 
923
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smiley_menu);
 
924
 
 
925
        /* Add the spell check menu item. */
 
926
        buffer = gtk_text_view_get_buffer (view);
 
927
        table = gtk_text_buffer_get_tag_table (buffer);
 
928
 
 
929
        tag = gtk_text_tag_table_lookup (table, "misspelled");
 
930
 
 
931
        gtk_widget_get_pointer (GTK_WIDGET (view), &x, &y);
 
932
 
 
933
        gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
 
934
                                               GTK_TEXT_WINDOW_WIDGET,
 
935
                                               x, y,
 
936
                                               &x, &y);
 
937
 
 
938
        gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &iter, x, y);
 
939
 
 
940
        start = end = iter;
 
941
 
 
942
        if (gtk_text_iter_backward_to_tag_toggle (&start, tag) &&
 
943
            gtk_text_iter_forward_to_tag_toggle (&end, tag)) {
 
944
 
 
945
                str = gtk_text_buffer_get_text (buffer,
 
946
                                                &start, &end, FALSE);
 
947
        }
 
948
 
 
949
        if (G_STR_EMPTY (str)) {
 
950
                return;
 
951
        }
 
952
 
 
953
        chat_spell = chat_spell_new (chat, str, start, end);
 
954
 
 
955
        g_object_set_data_full (G_OBJECT (menu),
 
956
                                "chat_spell", chat_spell,
 
957
                                (GDestroyNotify) chat_spell_free);
 
958
 
 
959
        item = gtk_separator_menu_item_new ();
 
960
        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 
961
        gtk_widget_show (item);
 
962
 
 
963
        item = gtk_menu_item_new_with_mnemonic (_("_Check Word Spelling..."));
 
964
        g_signal_connect (item,
 
965
                          "activate",
 
966
                          G_CALLBACK (chat_text_check_word_spelling_cb),
 
967
                          chat_spell);
 
968
        gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), item);
 
969
        gtk_widget_show (item);
 
970
}
 
971
 
 
972
static void
 
973
chat_text_check_word_spelling_cb (GtkMenuItem     *menuitem,
 
974
                                  GossipChatSpell *chat_spell)
 
975
{
 
976
/*FIXME:        gossip_spell_dialog_show (chat_spell->chat,
 
977
                                  chat_spell->start,
 
978
                                  chat_spell->end,
 
979
                                  chat_spell->word);*/
 
980
}
 
981
 
 
982
static GossipChatSpell *
 
983
chat_spell_new (GossipChat  *chat,
 
984
                const gchar *word,
 
985
                GtkTextIter  start,
 
986
                GtkTextIter  end)
 
987
{
 
988
        GossipChatSpell *chat_spell;
 
989
 
 
990
        chat_spell = g_new0 (GossipChatSpell, 1);
 
991
 
 
992
        chat_spell->chat = g_object_ref (chat);
 
993
        chat_spell->word = g_strdup (word);
 
994
        chat_spell->start = start;
 
995
        chat_spell->end = end;
 
996
 
 
997
        return chat_spell;
 
998
}
 
999
 
 
1000
static void
 
1001
chat_spell_free (GossipChatSpell *chat_spell)
 
1002
{
 
1003
        g_object_unref (chat_spell->chat);
 
1004
        g_free (chat_spell->word);
 
1005
        g_free (chat_spell);
 
1006
}
 
1007
 
 
1008
static void
 
1009
chat_composing_start (GossipChat *chat)
 
1010
{
 
1011
        GossipChatPriv *priv;
 
1012
 
 
1013
        priv = GET_PRIV (chat);
 
1014
 
 
1015
        if (priv->composing_stop_timeout_id) {
 
1016
                /* Just restart the timeout */
 
1017
                chat_composing_remove_timeout (chat);
 
1018
        } else {
 
1019
                empathy_tp_chat_set_state (priv->tp_chat,
 
1020
                                           TP_CHANNEL_CHAT_STATE_COMPOSING);
 
1021
        }
 
1022
 
 
1023
        priv->composing_stop_timeout_id = g_timeout_add (
 
1024
                1000 * COMPOSING_STOP_TIMEOUT,
 
1025
                (GSourceFunc) chat_composing_stop_timeout_cb,
 
1026
                chat);
 
1027
}
 
1028
 
 
1029
static void
 
1030
chat_composing_stop (GossipChat *chat)
 
1031
{
 
1032
        GossipChatPriv *priv;
 
1033
 
 
1034
        priv = GET_PRIV (chat);
 
1035
 
 
1036
        chat_composing_remove_timeout (chat);
 
1037
        empathy_tp_chat_set_state (priv->tp_chat,
 
1038
                                   TP_CHANNEL_CHAT_STATE_ACTIVE);
 
1039
}
 
1040
 
 
1041
static void
 
1042
chat_composing_remove_timeout (GossipChat *chat)
 
1043
{
 
1044
        GossipChatPriv *priv;
 
1045
 
 
1046
        priv = GET_PRIV (chat);
 
1047
 
 
1048
        if (priv->composing_stop_timeout_id) {
 
1049
                g_source_remove (priv->composing_stop_timeout_id);
 
1050
                priv->composing_stop_timeout_id = 0;
 
1051
        }
 
1052
}
 
1053
 
 
1054
static gboolean
 
1055
chat_composing_stop_timeout_cb (GossipChat *chat)
 
1056
{
 
1057
        GossipChatPriv *priv;
 
1058
 
 
1059
        priv = GET_PRIV (chat);
 
1060
 
 
1061
        priv->composing_stop_timeout_id = 0;
 
1062
        empathy_tp_chat_set_state (priv->tp_chat,
 
1063
                                   TP_CHANNEL_CHAT_STATE_PAUSED);
 
1064
 
 
1065
        return FALSE;
 
1066
}
 
1067
 
 
1068
static void
 
1069
chat_state_changed_cb (EmpathyTpChat             *tp_chat,
 
1070
                       GossipContact             *contact,
 
1071
                       TelepathyChannelChatState  state,
 
1072
                       GossipChat                *chat)
 
1073
{
 
1074
        GossipChatPriv *priv;
 
1075
        GList          *l;
 
1076
        gboolean        was_composing;
 
1077
 
 
1078
        priv = GET_PRIV (chat);
 
1079
 
 
1080
        was_composing = (priv->compositors != NULL);
 
1081
 
 
1082
        /* Find the contact in the list. After that l is the list elem or NULL */
 
1083
        for (l = priv->compositors; l; l = l->next) {
 
1084
                if (gossip_contact_equal (contact, l->data)) {
 
1085
                        break;
 
1086
                }
 
1087
        }
 
1088
 
 
1089
        switch (state) {
 
1090
        case TP_CHANNEL_CHAT_STATE_GONE:
 
1091
        case TP_CHANNEL_CHAT_STATE_INACTIVE:
 
1092
        case TP_CHANNEL_CHAT_STATE_ACTIVE:
 
1093
                /* Contact is not composing */
 
1094
                if (l) {
 
1095
                        priv->compositors = g_list_remove_link (priv->compositors, l);
 
1096
                        g_object_unref (l->data);
 
1097
                        g_list_free1 (l);
 
1098
                }
 
1099
                break;
 
1100
        case TP_CHANNEL_CHAT_STATE_PAUSED:
 
1101
        case TP_CHANNEL_CHAT_STATE_COMPOSING:
 
1102
                /* Contact is composing */
 
1103
                if (!l) {
 
1104
                        priv->compositors = g_list_prepend (priv->compositors,
 
1105
                                                            g_object_ref (contact));
 
1106
                }
 
1107
                break;
 
1108
        default:
 
1109
                g_assert_not_reached ();
 
1110
        }
 
1111
 
 
1112
        gossip_debug (DEBUG_DOMAIN, "Was composing: %s now composing: %s",
 
1113
                      was_composing ? "yes" : "no",
 
1114
                      priv->compositors ? "yes" : "no");
 
1115
 
 
1116
        if ((was_composing && !priv->compositors) ||
 
1117
            (!was_composing && priv->compositors)) {
 
1118
                /* Composing state changed */
 
1119
                g_signal_emit (chat, signals[COMPOSING], 0,
 
1120
                               (gboolean) priv->compositors);
 
1121
        }
 
1122
}
 
1123
 
 
1124
/* Scroll down after the back-log has been received. */
 
1125
static gboolean
 
1126
chat_scroll_down_idle_func (GossipChat *chat)
 
1127
{
 
1128
        GossipChatPriv *priv;
 
1129
 
 
1130
        priv = GET_PRIV (chat);
 
1131
 
 
1132
        gossip_chat_scroll_down (chat);
 
1133
        g_object_unref (chat);
 
1134
 
 
1135
        priv->scroll_idle_id = 0;
 
1136
 
 
1137
        return FALSE;
 
1138
}
 
1139
 
 
1140
gboolean
 
1141
gossip_chat_get_is_command (const gchar *str)
 
1142
{
 
1143
        g_return_val_if_fail (str != NULL, FALSE);
 
1144
 
 
1145
        if (str[0] != '/') {
 
1146
                return FALSE;
 
1147
        }
 
1148
 
 
1149
        if (g_str_has_prefix (str, "/me")) {
 
1150
                return TRUE;
 
1151
        }
 
1152
        else if (g_str_has_prefix (str, "/nick")) {
 
1153
                return TRUE;
 
1154
        }
 
1155
        else if (g_str_has_prefix (str, "/topic")) {
 
1156
                return TRUE;
 
1157
        }
 
1158
 
 
1159
        return FALSE;
 
1160
}
 
1161
 
 
1162
void
 
1163
gossip_chat_correct_word (GossipChat  *chat,
 
1164
                          GtkTextIter  start,
 
1165
                          GtkTextIter  end,
 
1166
                          const gchar *new_word)
 
1167
{
 
1168
        GtkTextBuffer *buffer;
 
1169
 
 
1170
        g_return_if_fail (chat != NULL);
 
1171
        g_return_if_fail (new_word != NULL);
 
1172
 
 
1173
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
1174
 
 
1175
        gtk_text_buffer_delete (buffer, &start, &end);
 
1176
        gtk_text_buffer_insert (buffer, &start,
 
1177
                                new_word,
 
1178
                                -1);
 
1179
}
 
1180
 
 
1181
const gchar *
 
1182
gossip_chat_get_name (GossipChat *chat)
 
1183
{
 
1184
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
 
1185
 
 
1186
        if (GOSSIP_CHAT_GET_CLASS (chat)->get_name) {
 
1187
                return GOSSIP_CHAT_GET_CLASS (chat)->get_name (chat);
 
1188
        }
 
1189
 
 
1190
        return NULL;
 
1191
}
 
1192
 
 
1193
gchar *
 
1194
gossip_chat_get_tooltip (GossipChat *chat)
 
1195
{
 
1196
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
 
1197
 
 
1198
        if (GOSSIP_CHAT_GET_CLASS (chat)->get_tooltip) {
 
1199
                return GOSSIP_CHAT_GET_CLASS (chat)->get_tooltip (chat);
 
1200
        }
 
1201
 
 
1202
        return NULL;
 
1203
}
 
1204
 
 
1205
const gchar *
 
1206
gossip_chat_get_status_icon_name (GossipChat *chat)
 
1207
{
 
1208
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
 
1209
 
 
1210
        if (GOSSIP_CHAT_GET_CLASS (chat)->get_status_icon_name) {
 
1211
                return GOSSIP_CHAT_GET_CLASS (chat)->get_status_icon_name (chat);
 
1212
        }
 
1213
 
 
1214
        return NULL;
 
1215
}
 
1216
 
 
1217
GtkWidget *
 
1218
gossip_chat_get_widget (GossipChat *chat)
 
1219
{
 
1220
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), NULL);
 
1221
 
 
1222
        if (GOSSIP_CHAT_GET_CLASS (chat)->get_widget) {
 
1223
                return GOSSIP_CHAT_GET_CLASS (chat)->get_widget (chat);
 
1224
        }
 
1225
 
 
1226
        return NULL;
 
1227
}
 
1228
 
 
1229
gboolean
 
1230
gossip_chat_is_group_chat (GossipChat *chat)
 
1231
{
 
1232
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), FALSE);
 
1233
 
 
1234
        if (GOSSIP_CHAT_GET_CLASS (chat)->is_group_chat) {
 
1235
                return GOSSIP_CHAT_GET_CLASS (chat)->is_group_chat (chat);
 
1236
        }
 
1237
 
 
1238
        return FALSE;
 
1239
}
 
1240
 
 
1241
gboolean 
 
1242
gossip_chat_is_connected (GossipChat *chat)
 
1243
{
 
1244
        GossipChatPriv *priv;
 
1245
 
 
1246
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), FALSE);
 
1247
 
 
1248
        priv = GET_PRIV (chat);
 
1249
 
 
1250
        return (priv->tp_chat != NULL);
 
1251
}
 
1252
 
 
1253
void
 
1254
gossip_chat_save_geometry (GossipChat *chat,
 
1255
                           gint        x,
 
1256
                           gint        y,
 
1257
                           gint        w,
 
1258
                           gint        h)
 
1259
{
 
1260
        gossip_geometry_save (gossip_chat_get_id (chat), x, y, w, h);
 
1261
}
 
1262
 
 
1263
void
 
1264
gossip_chat_load_geometry (GossipChat *chat,
 
1265
                           gint       *x,
 
1266
                           gint       *y,
 
1267
                           gint       *w,
 
1268
                           gint       *h)
 
1269
{
 
1270
        gossip_geometry_load (gossip_chat_get_id (chat), x, y, w, h);
 
1271
}
 
1272
 
 
1273
void
 
1274
gossip_chat_set_tp_chat (GossipChat    *chat,
 
1275
                         EmpathyTpChat *tp_chat)
 
1276
{
 
1277
        GossipChatPriv *priv;
 
1278
        GtkWidget      *widget;
 
1279
 
 
1280
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1281
        g_return_if_fail (EMPATHY_IS_TP_CHAT (tp_chat));
 
1282
 
 
1283
        priv = GET_PRIV (chat);
 
1284
 
 
1285
        if (tp_chat == priv->tp_chat) {
 
1286
                return;
 
1287
        }
 
1288
 
 
1289
        if (priv->tp_chat) {
 
1290
                g_signal_handlers_disconnect_by_func (priv->tp_chat,
 
1291
                                                      chat_message_received_cb,
 
1292
                                                      chat);
 
1293
                g_signal_handlers_disconnect_by_func (priv->tp_chat,
 
1294
                                                      chat_destroy_cb,
 
1295
                                                      chat);
 
1296
                g_object_unref (priv->tp_chat);
 
1297
        }
 
1298
 
 
1299
        g_free (priv->id);
 
1300
        priv->tp_chat = g_object_ref (tp_chat);
 
1301
        priv->id = g_strdup (empathy_tp_chat_get_id (tp_chat));
 
1302
 
 
1303
        g_signal_connect (tp_chat, "message-received",
 
1304
                          G_CALLBACK (chat_message_received_cb),
 
1305
                          chat);
 
1306
        g_signal_connect (tp_chat, "chat-state-changed",
 
1307
                          G_CALLBACK (chat_state_changed_cb),
 
1308
                          chat);
 
1309
        g_signal_connect (tp_chat, "destroy",
 
1310
                          G_CALLBACK (chat_destroy_cb),
 
1311
                          chat);
 
1312
 
 
1313
        empathy_tp_chat_request_pending (tp_chat);
 
1314
 
 
1315
        if (!priv->sensitive) {
 
1316
                widget = gossip_chat_get_widget (chat);
 
1317
                gtk_widget_set_sensitive (widget, TRUE);
 
1318
                gossip_chat_view_append_event (chat->view, _("Connected"));
 
1319
                priv->sensitive = TRUE;
 
1320
        }
 
1321
}
 
1322
 
 
1323
const gchar *
 
1324
gossip_chat_get_id (GossipChat *chat)
 
1325
{
 
1326
        GossipChatPriv *priv;
 
1327
 
 
1328
        priv = GET_PRIV (chat);
 
1329
 
 
1330
        return priv->id;
 
1331
}
 
1332
 
 
1333
void
 
1334
gossip_chat_clear (GossipChat *chat)
 
1335
{
 
1336
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1337
 
 
1338
        gossip_chat_view_clear (chat->view);
 
1339
}
 
1340
 
 
1341
void
 
1342
gossip_chat_set_window (GossipChat       *chat,
 
1343
                        GossipChatWindow *window)
 
1344
{
 
1345
        GossipChatPriv *priv;
 
1346
 
 
1347
        priv = GET_PRIV (chat);
 
1348
        priv->window = window;
 
1349
}
 
1350
 
 
1351
GossipChatWindow *
 
1352
gossip_chat_get_window (GossipChat *chat)
 
1353
{
 
1354
        GossipChatPriv *priv;
 
1355
 
 
1356
        priv = GET_PRIV (chat);
 
1357
 
 
1358
        return priv->window;
 
1359
}
 
1360
 
 
1361
void
 
1362
gossip_chat_scroll_down (GossipChat *chat)
 
1363
{
 
1364
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1365
 
 
1366
        gossip_chat_view_scroll_down (chat->view);
 
1367
}
 
1368
 
 
1369
void
 
1370
gossip_chat_cut (GossipChat *chat)
 
1371
{
 
1372
        GtkTextBuffer *buffer;
 
1373
 
 
1374
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1375
 
 
1376
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
1377
        if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
 
1378
                GtkClipboard *clipboard;
 
1379
 
 
1380
                clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
1381
 
 
1382
                gtk_text_buffer_cut_clipboard (buffer, clipboard, TRUE);
 
1383
        }
 
1384
}
 
1385
 
 
1386
void
 
1387
gossip_chat_copy (GossipChat *chat)
 
1388
{
 
1389
        GtkTextBuffer *buffer;
 
1390
 
 
1391
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1392
 
 
1393
        if (gossip_chat_view_get_selection_bounds (chat->view, NULL, NULL)) {
 
1394
                gossip_chat_view_copy_clipboard (chat->view);
 
1395
                return;
 
1396
        }
 
1397
 
 
1398
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
1399
        if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL)) {
 
1400
                GtkClipboard *clipboard;
 
1401
 
 
1402
                clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
1403
 
 
1404
                gtk_text_buffer_copy_clipboard (buffer, clipboard);
 
1405
        }
 
1406
}
 
1407
 
 
1408
void
 
1409
gossip_chat_paste (GossipChat *chat)
 
1410
{
 
1411
        GtkTextBuffer *buffer;
 
1412
        GtkClipboard  *clipboard;
 
1413
 
 
1414
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1415
 
 
1416
        buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (chat->input_text_view));
 
1417
        clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
 
1418
 
 
1419
        gtk_text_buffer_paste_clipboard (buffer, clipboard, NULL, TRUE);
 
1420
}
 
1421
 
 
1422
void
 
1423
gossip_chat_present (GossipChat *chat)
 
1424
{
 
1425
        GossipChatPriv *priv;
 
1426
 
 
1427
        g_return_if_fail (GOSSIP_IS_CHAT (chat));
 
1428
 
 
1429
        priv = GET_PRIV (chat);
 
1430
 
 
1431
        if (priv->window == NULL) {
 
1432
                GossipChatWindow *window;
 
1433
 
 
1434
                window = gossip_chat_window_get_default ();
 
1435
                if (!window) {
 
1436
                        window = gossip_chat_window_new ();
 
1437
                }
 
1438
 
 
1439
                gossip_chat_window_add_chat (window, chat);
 
1440
        }
 
1441
 
 
1442
        gossip_chat_window_switch_to_chat (priv->window, chat);
 
1443
        gossip_window_present (
 
1444
                GTK_WINDOW (gossip_chat_window_get_dialog (priv->window)),
 
1445
                TRUE);
 
1446
 
 
1447
        gtk_widget_grab_focus (chat->input_text_view); 
 
1448
}
 
1449
 
 
1450
gboolean
 
1451
gossip_chat_should_play_sound (GossipChat *chat)
 
1452
{
 
1453
        GossipChatWindow *window;
 
1454
        gboolean          play = TRUE;
 
1455
 
 
1456
        g_return_val_if_fail (GOSSIP_IS_CHAT (chat), FALSE);
 
1457
 
 
1458
        window = gossip_chat_get_window (chat);
 
1459
        if (!window) {
 
1460
                return TRUE;
 
1461
        }
 
1462
 
 
1463
        play = !gossip_chat_window_has_focus (window);
 
1464
 
 
1465
        return play;
 
1466
}
 
1467
 
 
1468
gboolean
 
1469
gossip_chat_should_highlight_nick (GossipMessage *message)
 
1470
{
 
1471
        GossipContact *my_contact;
 
1472
        const gchar   *msg, *to;
 
1473
        gchar         *cf_msg, *cf_to;
 
1474
        gchar         *ch;
 
1475
        gboolean       ret_val;
 
1476
 
 
1477
        g_return_val_if_fail (GOSSIP_IS_MESSAGE (message), FALSE);
 
1478
 
 
1479
        gossip_debug (DEBUG_DOMAIN, "Highlighting nickname");
 
1480
 
 
1481
        ret_val = FALSE;
 
1482
 
 
1483
        msg = gossip_message_get_body (message);
 
1484
        if (!msg) {
 
1485
                return FALSE;
 
1486
        }
 
1487
 
 
1488
        my_contact = gossip_contact_get_user (gossip_message_get_sender (message));
 
1489
        to = gossip_contact_get_name (my_contact);
 
1490
        if (!to) {
 
1491
                return FALSE;
 
1492
        }
 
1493
 
 
1494
        cf_msg = g_utf8_casefold (msg, -1);
 
1495
        cf_to = g_utf8_casefold (to, -1);
 
1496
 
 
1497
        ch = strstr (cf_msg, cf_to);
 
1498
        if (ch == NULL) {
 
1499
                goto finished;
 
1500
        }
 
1501
 
 
1502
        if (ch != cf_msg) {
 
1503
                /* Not first in the message */
 
1504
                if ((*(ch - 1) != ' ') &&
 
1505
                    (*(ch - 1) != ',') &&
 
1506
                    (*(ch - 1) != '.')) {
 
1507
                        goto finished;
 
1508
                }
 
1509
        }
 
1510
 
 
1511
        ch = ch + strlen (cf_to);
 
1512
        if (ch >= cf_msg + strlen (cf_msg)) {
 
1513
                ret_val = TRUE;
 
1514
                goto finished;
 
1515
        }
 
1516
 
 
1517
        if ((*ch == ' ') ||
 
1518
            (*ch == ',') ||
 
1519
            (*ch == '.') ||
 
1520
            (*ch == ':')) {
 
1521
                ret_val = TRUE;
 
1522
                goto finished;
 
1523
        }
 
1524
 
 
1525
finished:
 
1526
        g_free (cf_msg);
 
1527
        g_free (cf_to);
 
1528
 
 
1529
        return ret_val;
 
1530
}
 
1531