]> git.0d.be Git - empathy.git/blob - src/empathy-ft-manager.c
Merge branch 'gnome-3-8'
[empathy.git] / src / empathy-ft-manager.c
1 /*
2  * Copyright (C) 2003, 2004 Xan Lopez
3  * Copyright (C) 2007 Marco Barisione <marco@barisione.org>
4  * Copyright (C) 2008-2009 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., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301  USA
20  *
21  * Authors: Xan Lopez
22  *          Marco Barisione <marco@barisione.org>
23  *          Jonny Lamb <jonny.lamb@collabora.co.uk>
24  *          Xavier Claessens <xclaesse@gmail.com>
25  *          Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
26  */
27
28 /* The original file transfer manager code was copied from Epiphany */
29
30 #include "config.h"
31 #include "empathy-ft-manager.h"
32
33 #include <glib/gi18n.h>
34
35 #include "empathy-geometry.h"
36 #include "empathy-ui-utils.h"
37 #include "empathy-utils.h"
38
39 #define DEBUG_FLAG EMPATHY_DEBUG_FT
40 #include "empathy-debug.h"
41
42 enum
43 {
44   COL_PERCENT,
45   COL_ICON,
46   COL_MESSAGE,
47   COL_REMAINING,
48   COL_FT_OBJECT
49 };
50
51 typedef struct {
52   GtkTreeModel *model;
53   GHashTable *ft_handler_to_row_ref;
54
55   /* Widgets */
56   GtkWidget *window;
57   GtkWidget *treeview;
58   GtkWidget *open_button;
59   GtkWidget *abort_button;
60   GtkWidget *clear_button;
61 } EmpathyFTManagerPriv;
62
63 enum
64 {
65   RESPONSE_OPEN  = 1,
66   RESPONSE_STOP  = 2,
67   RESPONSE_CLEAR = 3,
68   RESPONSE_CLOSE = 4
69 };
70
71 G_DEFINE_TYPE (EmpathyFTManager, empathy_ft_manager, G_TYPE_OBJECT);
72
73 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyFTManager)
74
75 static EmpathyFTManager *manager_singleton = NULL;
76
77 static void ft_handler_hashing_started_cb (EmpathyFTHandler *handler,
78     EmpathyFTManager *manager);
79
80 static gchar *
81 ft_manager_format_interval (guint interval)
82 {
83   gint hours, mins, secs;
84
85   hours = interval / 3600;
86   interval -= hours * 3600;
87   mins = interval / 60;
88   interval -= mins * 60;
89   secs = interval;
90
91   if (hours > 0)
92     /* Translators: time left, when it is more than one hour */
93     return g_strdup_printf (_("%u:%02u.%02u"), hours, mins, secs);
94   else
95     /* Translators: time left, when is is less than one hour */
96     return g_strdup_printf (_("%02u.%02u"), mins, secs);
97 }
98
99 static void
100 ft_manager_update_buttons (EmpathyFTManager *manager)
101 {
102   GtkTreeSelection *selection;
103   GtkTreeModel *model;
104   GtkTreeIter iter;
105   EmpathyFTHandler *handler;
106   gboolean open_enabled = FALSE;
107   gboolean abort_enabled = FALSE;
108   gboolean clear_enabled = FALSE;
109   gboolean is_completed, is_cancelled;
110   GHashTableIter hash_iter;
111   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
112
113   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
114
115   if (gtk_tree_selection_get_selected (selection, &model, &iter))
116     {
117       gtk_tree_model_get (model, &iter, COL_FT_OBJECT, &handler, -1);
118
119       is_completed = empathy_ft_handler_is_completed (handler);
120       is_cancelled = empathy_ft_handler_is_cancelled (handler);
121
122       /* I can open the file if the transfer is completed and was incoming */
123       open_enabled = (is_completed && empathy_ft_handler_is_incoming (handler));
124
125       /* I can abort if the transfer is not already finished */
126       abort_enabled = (is_cancelled == FALSE && is_completed == FALSE);
127
128       g_object_unref (handler);
129     }
130
131   g_hash_table_iter_init (&hash_iter, priv->ft_handler_to_row_ref);
132
133   while (g_hash_table_iter_next (&hash_iter, (gpointer *) &handler, NULL))
134     {
135       if (empathy_ft_handler_is_completed (handler) ||
136           empathy_ft_handler_is_cancelled (handler))
137           clear_enabled = TRUE;
138
139       if (clear_enabled)
140         break;
141     }
142
143   gtk_widget_set_sensitive (priv->open_button, open_enabled);
144   gtk_widget_set_sensitive (priv->abort_button, abort_enabled);
145
146   if (clear_enabled)
147     gtk_widget_set_sensitive (priv->clear_button, TRUE);
148 }
149
150 static void
151 ft_manager_selection_changed (GtkTreeSelection *selection,
152                               EmpathyFTManager *manager)
153 {
154   ft_manager_update_buttons (manager);
155 }
156
157 static void
158 ft_manager_progress_cell_data_func (GtkTreeViewColumn *col,
159                                     GtkCellRenderer *renderer,
160                                     GtkTreeModel *model,
161                                     GtkTreeIter *iter,
162                                     gpointer user_data)
163 {
164   const gchar *text = NULL;
165   gint percent;
166
167   gtk_tree_model_get (model, iter, COL_PERCENT, &percent, -1);
168
169   if (percent < 0)
170     {
171       percent = 0;
172       text = C_("file transfer percent", "Unknown");
173     }
174
175   g_object_set (renderer, "text", text, "value", percent, NULL);
176 }
177
178 static GtkTreeRowReference *
179 ft_manager_get_row_from_handler (EmpathyFTManager *manager,
180                                  EmpathyFTHandler *handler)
181 {
182   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
183
184   return g_hash_table_lookup (priv->ft_handler_to_row_ref, handler);
185 }
186
187 static void
188 ft_manager_remove_file_from_model (EmpathyFTManager *manager,
189                                    EmpathyFTHandler *handler)
190 {
191   GtkTreeRowReference *row_ref;
192   GtkTreeSelection *selection;
193   GtkTreePath *path = NULL;
194   GtkTreeIter iter;
195   gboolean update_selection;
196   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
197
198   row_ref = ft_manager_get_row_from_handler (manager, handler);
199   g_return_if_fail (row_ref);
200
201   DEBUG ("Removing file transfer from window: contact=%s, filename=%s",
202       empathy_contact_get_alias (empathy_ft_handler_get_contact (handler)),
203       empathy_ft_handler_get_filename (handler));
204
205   /* Get the iter from the row_ref */
206   path = gtk_tree_row_reference_get_path (row_ref);
207   gtk_tree_model_get_iter (priv->model, &iter, path);
208   gtk_tree_path_free (path);
209
210   /* We have to update the selection only if we are removing the selected row */
211   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
212   update_selection = gtk_tree_selection_iter_is_selected (selection, &iter);
213
214   /* Remove tp_file's row. After that iter points to the next row */
215   if (!gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter))
216     {
217       gint n_row;
218
219       /* There is no next row, set iter to the last row */
220       n_row = gtk_tree_model_iter_n_children (priv->model, NULL);
221       if (n_row > 0)
222         gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, n_row - 1);
223       else
224         update_selection = FALSE;
225     }
226
227   if (update_selection)
228     gtk_tree_selection_select_iter (selection, &iter);
229 }
230
231 static gboolean
232 remove_finished_transfer_foreach (gpointer key,
233                                   gpointer value,
234                                   gpointer user_data)
235 {
236   EmpathyFTHandler *handler = key;
237   EmpathyFTManager *manager = user_data;
238
239   if (empathy_ft_handler_is_completed (handler) ||
240       empathy_ft_handler_is_cancelled (handler))
241     {
242       ft_manager_remove_file_from_model (manager, handler);
243       return TRUE;
244     }
245
246   return FALSE;
247 }
248
249 static gchar *
250 ft_manager_format_progress_bytes_and_percentage (guint64 current,
251                                                  guint64 total,
252                                                  gdouble speed,
253                                                  int *percentage)
254 {
255   char *total_str, *current_str, *retval;
256   char *speed_str = NULL;
257
258   total_str = g_format_size (total);
259   current_str = g_format_size (current);
260
261   if (speed > 0)
262     speed_str = g_format_size ((goffset) speed);
263
264   /* translators: first %s is the currently processed size, second %s is
265    * the total file size */
266   retval = speed_str ?
267     g_strdup_printf (_("%s of %s at %s/s"), current_str, total_str, speed_str) :
268     g_strdup_printf (_("%s of %s"), current_str, total_str);
269
270   g_free (total_str);
271   g_free (current_str);
272   g_free (speed_str);
273
274   if (percentage != NULL)
275     {
276       if (total != 0)
277         *percentage = current * 100 / total;
278       else
279         *percentage = -1;
280     }
281
282   return retval;
283 }
284
285 static gchar *
286 ft_manager_format_contact_info (EmpathyFTHandler *handler)
287 {
288   gboolean incoming;
289   const char *filename, *contact_name, *first_line_format;
290   char *retval;
291
292   incoming = empathy_ft_handler_is_incoming (handler);
293   contact_name = empathy_contact_get_alias
294     (empathy_ft_handler_get_contact (handler));
295   filename = empathy_ft_handler_get_filename (handler);
296
297   if (incoming)
298     /* translators: first %s is filename, second %s is the contact name */
299     first_line_format = _("Receiving \"%s\" from %s");
300   else
301     /* translators: first %s is filename, second %s is the contact name */
302     first_line_format = _("Sending \"%s\" to %s");
303
304   retval = g_strdup_printf (first_line_format, filename, contact_name);
305
306   return retval;
307 }
308
309 static gchar *
310 ft_manager_format_error_message (EmpathyFTHandler *handler,
311                                  const GError *error)
312 {
313   const char *contact_name, *filename;
314   EmpathyContact *contact;
315   char *first_line, *message;
316   gboolean incoming;
317
318   contact_name = NULL;
319   incoming = empathy_ft_handler_is_incoming (handler);
320
321   contact = empathy_ft_handler_get_contact (handler);
322   if (contact)
323     contact_name = empathy_contact_get_alias (contact);
324
325   filename = empathy_ft_handler_get_filename (handler);
326
327   if (incoming)
328     /* filename/contact_name here are either both NULL or both valid */
329     if (filename && contact_name)
330       /* translators: first %s is filename, second %s
331        * is the contact name */
332       first_line = g_strdup_printf (_("Error receiving \"%s\" from %s"), filename,
333           contact_name);
334     else
335       first_line = g_strdup (_("Error receiving a file"));
336   else
337     /* translators: first %s is filename, second %s
338      * is the contact name */
339     if (filename && contact_name)
340       first_line = g_strdup_printf (_("Error sending \"%s\" to %s"), filename,
341           contact_name);
342     else
343       first_line = g_strdup (_("Error sending a file"));
344
345   message = g_strdup_printf ("%s\n%s", first_line, error->message);
346
347   g_free (first_line);
348
349   return message;
350 }
351
352 static void
353 ft_manager_update_handler_message (EmpathyFTManager *manager,
354                                    GtkTreeRowReference *row_ref,
355                                    const char *message)
356 {
357   GtkTreePath *path;
358   GtkTreeIter iter;
359   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
360
361   /* Set new value in the store */
362   path = gtk_tree_row_reference_get_path (row_ref);
363   gtk_tree_model_get_iter (priv->model, &iter, path);
364   gtk_list_store_set (GTK_LIST_STORE (priv->model),
365       &iter,
366       COL_MESSAGE, message ? message : "",
367       -1);
368
369   gtk_tree_path_free (path);
370 }
371
372 static void
373 ft_manager_update_handler_progress (EmpathyFTManager *manager,
374                                     GtkTreeRowReference *row_ref,
375                                     int percentage)
376 {
377   GtkTreePath *path;
378   GtkTreeIter iter;
379   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
380
381   /* Set new value in the store */
382   path = gtk_tree_row_reference_get_path (row_ref);
383   gtk_tree_model_get_iter (priv->model, &iter, path);
384   gtk_list_store_set (GTK_LIST_STORE (priv->model),
385       &iter,
386       COL_PERCENT, percentage,
387       -1);
388
389   gtk_tree_path_free (path);
390
391 }
392
393 static void
394 ft_manager_update_handler_time (EmpathyFTManager *manager,
395                                 GtkTreeRowReference *row_ref,
396                                 guint remaining_time)
397 {
398   GtkTreePath *path;
399   GtkTreeIter iter;
400   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
401   char *remaining_str;
402
403   remaining_str = ft_manager_format_interval (remaining_time);
404
405   /* Set new value in the store */
406   path = gtk_tree_row_reference_get_path (row_ref);
407   gtk_tree_model_get_iter (priv->model, &iter, path);
408   gtk_list_store_set (GTK_LIST_STORE (priv->model),
409       &iter,
410       COL_REMAINING, remaining_str,
411       -1);
412
413   gtk_tree_path_free (path);
414   g_free (remaining_str);
415 }
416
417 static void
418 ft_manager_clear_handler_time (EmpathyFTManager *manager,
419                                GtkTreeRowReference *row_ref)
420 {
421   GtkTreePath *path;
422   GtkTreeIter iter;
423   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
424
425   /* Set new value in the store */
426   path = gtk_tree_row_reference_get_path (row_ref);
427   gtk_tree_model_get_iter (priv->model, &iter, path);
428   gtk_list_store_set (GTK_LIST_STORE (priv->model),
429       &iter,
430       COL_REMAINING, NULL,
431       -1);
432
433   gtk_tree_path_free (path);
434 }
435
436 static void
437 ft_handler_transfer_error_cb (EmpathyFTHandler *handler,
438                               GError *error,
439                               EmpathyFTManager *manager)
440 {
441   char *message;
442   GtkTreeRowReference *row_ref;
443
444   DEBUG ("Transfer error %s", error->message);
445
446   row_ref = ft_manager_get_row_from_handler (manager, handler);
447   g_return_if_fail (row_ref != NULL);
448
449   message = ft_manager_format_error_message (handler, error);
450
451   ft_manager_update_handler_message (manager, row_ref, message);
452   ft_manager_clear_handler_time (manager, row_ref);
453   ft_manager_update_buttons (manager);
454
455   g_free (message);
456 }
457
458 static void
459 do_real_transfer_done (EmpathyFTManager *manager,
460                        EmpathyFTHandler *handler)
461 {
462   const char *contact_name;
463   const char *filename;
464   char *first_line, *second_line, *message;
465   char *uri;
466   gboolean incoming;
467   GtkTreeRowReference *row_ref;
468   GtkRecentManager *recent_manager;
469   GFile *file;
470
471   row_ref = ft_manager_get_row_from_handler (manager, handler);
472   g_return_if_fail (row_ref != NULL);
473
474   incoming = empathy_ft_handler_is_incoming (handler);
475   contact_name = empathy_contact_get_alias
476     (empathy_ft_handler_get_contact (handler));
477   filename = empathy_ft_handler_get_filename (handler);
478
479   if (incoming)
480     /* translators: first %s is filename, second %s
481      * is the contact name */
482     first_line = g_strdup_printf (_("\"%s\" received from %s"), filename,
483         contact_name);
484   else
485     /* translators: first %s is filename, second %s
486      * is the contact name */
487     first_line = g_strdup_printf (_("\"%s\" sent to %s"), filename,
488         contact_name);
489
490   second_line = g_strdup (_("File transfer completed"));
491
492   message = g_strdup_printf ("%s\n%s", first_line, second_line);
493   ft_manager_update_handler_message (manager, row_ref, message);
494   ft_manager_clear_handler_time (manager, row_ref);
495
496   /* update buttons */
497   ft_manager_update_buttons (manager);
498
499   g_free (message);
500   g_free (first_line);
501   g_free (second_line);
502
503   recent_manager = gtk_recent_manager_get_default ();
504   file = empathy_ft_handler_get_gfile (handler);
505   uri = g_file_get_uri (file);
506
507   gtk_recent_manager_add_item (recent_manager, uri);
508
509   g_free (uri);
510 }
511
512 static void
513 ft_handler_transfer_done_cb (EmpathyFTHandler *handler,
514                              TpFileTransferChannel *channel,
515                              EmpathyFTManager *manager)
516 {
517   if (empathy_ft_handler_is_incoming (handler) &&
518       empathy_ft_handler_get_use_hash (handler))
519     {
520       DEBUG ("Transfer done, waiting for hashing-started");
521
522       /* connect to the signal and return early */
523       g_signal_connect (handler, "hashing-started",
524           G_CALLBACK (ft_handler_hashing_started_cb), manager);
525
526       return;
527     }
528
529   DEBUG ("Transfer done, no hashing");
530
531   do_real_transfer_done (manager, handler);
532 }
533
534 static void
535 ft_handler_transfer_progress_cb (EmpathyFTHandler *handler,
536                                  guint64 current_bytes,
537                                  guint64 total_bytes,
538                                  guint remaining_time,
539                                  gdouble speed,
540                                  EmpathyFTManager *manager)
541 {
542   char *first_line, *second_line, *message;
543   int percentage;
544   GtkTreeRowReference *row_ref;
545
546   DEBUG ("Transfer progress");
547
548   row_ref = ft_manager_get_row_from_handler (manager, handler);
549   g_return_if_fail (row_ref != NULL);
550
551   first_line = ft_manager_format_contact_info (handler);
552   second_line = ft_manager_format_progress_bytes_and_percentage
553     (current_bytes, total_bytes, speed, &percentage);
554
555   message = g_strdup_printf ("%s\n%s", first_line, second_line);
556
557   ft_manager_update_handler_message (manager, row_ref, message);
558   ft_manager_update_handler_progress (manager, row_ref, percentage);
559
560   if (remaining_time > 0)
561     ft_manager_update_handler_time (manager, row_ref, remaining_time);
562
563   g_free (message);
564   g_free (first_line);
565   g_free (second_line);
566 }
567
568 static void
569 ft_handler_transfer_started_cb (EmpathyFTHandler *handler,
570                                 TpFileTransferChannel *channel,
571                                 EmpathyFTManager *manager)
572 {
573   guint64 transferred_bytes, total_bytes;
574
575   DEBUG ("Transfer started");
576
577   g_signal_connect (handler, "transfer-progress",
578       G_CALLBACK (ft_handler_transfer_progress_cb), manager);
579   g_signal_connect (handler, "transfer-done",
580       G_CALLBACK (ft_handler_transfer_done_cb), manager);
581
582   transferred_bytes = empathy_ft_handler_get_transferred_bytes (handler);
583   total_bytes = empathy_ft_handler_get_total_bytes (handler);
584
585   ft_handler_transfer_progress_cb (handler, transferred_bytes, total_bytes,
586       0, -1, manager);
587 }
588
589 static void
590 ft_handler_hashing_done_cb (EmpathyFTHandler *handler,
591                             EmpathyFTManager *manager)
592 {
593   GtkTreeRowReference *row_ref;
594   char *first_line, *second_line, *message;
595
596   DEBUG ("Hashing done");
597
598   /* update the message */
599   if (empathy_ft_handler_is_incoming (handler))
600     {
601       do_real_transfer_done (manager, handler);
602       return;
603     }
604
605   row_ref = ft_manager_get_row_from_handler (manager, handler);
606   g_return_if_fail (row_ref != NULL);
607
608   first_line = ft_manager_format_contact_info (handler);
609   second_line = g_strdup (_("Waiting for the other participant's response"));
610   message = g_strdup_printf ("%s\n%s", first_line, second_line);
611
612   ft_manager_update_handler_message (manager, row_ref, message);
613
614   g_free (message);
615   g_free (first_line);
616   g_free (second_line);
617
618   g_signal_connect (handler, "transfer-started",
619       G_CALLBACK (ft_handler_transfer_started_cb), manager);
620 }
621
622 static void
623 ft_handler_hashing_progress_cb (EmpathyFTHandler *handler,
624                                 guint64 current_bytes,
625                                 guint64 total_bytes,
626                                 EmpathyFTManager *manager)
627 {
628   char *first_line, *second_line, *message;
629   GtkTreeRowReference *row_ref;
630
631   row_ref = ft_manager_get_row_from_handler (manager, handler);
632   g_return_if_fail (row_ref != NULL);
633
634   if (empathy_ft_handler_is_incoming (handler))
635       first_line = g_strdup_printf (_("Checking integrity of \"%s\""),
636           empathy_ft_handler_get_filename (handler));
637   else
638       first_line =  g_strdup_printf (_("Hashing \"%s\""),
639           empathy_ft_handler_get_filename (handler));
640
641   second_line = ft_manager_format_progress_bytes_and_percentage
642     (current_bytes, total_bytes, -1, NULL);
643
644   message = g_strdup_printf ("%s\n%s", first_line, second_line);
645
646   ft_manager_update_handler_message (manager, row_ref, message);
647
648   g_free (message);
649   g_free (first_line);
650   g_free (second_line);
651 }
652
653 static void
654 ft_handler_hashing_started_cb (EmpathyFTHandler *handler,
655                                EmpathyFTManager *manager)
656 {
657   char *message, *first_line, *second_line;
658   GtkTreeRowReference *row_ref;
659
660   DEBUG ("Hashing started");
661
662   g_signal_connect (handler, "hashing-progress",
663      G_CALLBACK (ft_handler_hashing_progress_cb), manager);
664   g_signal_connect (handler, "hashing-done",
665      G_CALLBACK (ft_handler_hashing_done_cb), manager);
666
667   row_ref = ft_manager_get_row_from_handler (manager, handler);
668   g_return_if_fail (row_ref != NULL);
669
670   first_line = ft_manager_format_contact_info (handler);
671
672   if (empathy_ft_handler_is_incoming (handler))
673       second_line = g_strdup_printf (_("Checking integrity of \"%s\""),
674           empathy_ft_handler_get_filename (handler));
675   else
676       second_line = g_strdup_printf (_("Hashing \"%s\""),
677           empathy_ft_handler_get_filename (handler));
678
679   message = g_strdup_printf ("%s\n%s", first_line, second_line);
680
681   ft_manager_update_handler_message (manager, row_ref, message);
682
683   g_free (first_line);
684   g_free (second_line);
685   g_free (message);
686 }
687
688 static void
689 ft_manager_start_transfer (EmpathyFTManager *manager,
690                            EmpathyFTHandler *handler)
691 {
692   gboolean is_outgoing;
693
694   is_outgoing = !empathy_ft_handler_is_incoming (handler);
695
696   DEBUG ("Start transfer, is outgoing %s",
697       is_outgoing ? "True" : "False");
698
699   /* now connect the signals */
700   g_signal_connect (handler, "transfer-error",
701       G_CALLBACK (ft_handler_transfer_error_cb), manager);
702
703   if (is_outgoing && empathy_ft_handler_get_use_hash (handler)) {
704     g_signal_connect (handler, "hashing-started",
705         G_CALLBACK (ft_handler_hashing_started_cb), manager);
706   } else {
707     /* either incoming or outgoing without hash */
708     g_signal_connect (handler, "transfer-started",
709         G_CALLBACK (ft_handler_transfer_started_cb), manager);
710   }
711
712   empathy_ft_handler_start_transfer (handler);
713 }
714
715 static void
716 ft_manager_add_handler_to_list (EmpathyFTManager *manager,
717                                 EmpathyFTHandler *handler,
718                                 const GError *error)
719 {
720   GtkTreeRowReference *row_ref;
721   GtkTreeIter iter;
722   GtkTreeSelection *selection;
723   GtkTreePath *path;
724   GIcon *icon;
725   const char *content_type, *second_line;
726   char *first_line, *message;
727   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
728
729   icon = NULL;
730
731   /* get the icon name from the mime-type of the file. */
732   content_type = empathy_ft_handler_get_content_type (handler);
733
734   if (content_type != NULL)
735     icon = g_content_type_get_icon (content_type);
736
737   /* append the handler in the store */
738   gtk_list_store_insert_with_values (GTK_LIST_STORE (priv->model),
739       &iter, G_MAXINT, COL_FT_OBJECT, handler,
740       COL_ICON, icon, -1);
741
742   if (icon != NULL)
743     g_object_unref (icon);
744
745   /* insert the new row_ref in the hash table  */
746   path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->model), &iter);
747   row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (priv->model), path);
748   gtk_tree_path_free (path);
749   g_hash_table_insert (priv->ft_handler_to_row_ref, g_object_ref (handler),
750       row_ref);
751
752   /* select the new row */
753   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
754   gtk_tree_selection_select_iter (selection, &iter);
755
756   if (error != NULL)
757     {
758       message = ft_manager_format_error_message (handler, error);
759       ft_manager_update_handler_message (manager, row_ref, message);
760
761       g_free (message);
762       return;
763     }
764
765   /* update the row with the initial values.
766    * the only case where we postpone this is in case we're managing
767    * an outgoing+hashing transfer, as the hashing started signal will
768    * take care of updating the information.
769    */
770   if (empathy_ft_handler_is_incoming (handler) ||
771       !empathy_ft_handler_get_use_hash (handler)) {
772     first_line = ft_manager_format_contact_info (handler);
773     second_line = _("Waiting for the other participant's response");
774     message = g_strdup_printf ("%s\n%s", first_line, second_line);
775
776     ft_manager_update_handler_message (manager, row_ref, message);
777
778     g_free (first_line);
779     g_free (message);
780   }
781
782   /* hook up the signals and start the transfer */
783   ft_manager_start_transfer (manager, handler);
784 }
785
786 static void
787 ft_manager_clear (EmpathyFTManager *manager)
788 {
789   EmpathyFTManagerPriv *priv;
790
791   DEBUG ("Clearing file transfer list");
792
793   priv = GET_PRIV (manager);
794
795   /* Remove completed and cancelled transfers */
796   g_hash_table_foreach_remove (priv->ft_handler_to_row_ref,
797       remove_finished_transfer_foreach, manager);
798
799   /* set the clear button back to insensitive */
800   gtk_widget_set_sensitive (priv->clear_button, FALSE);
801 }
802
803 static void
804 ft_manager_open (EmpathyFTManager *manager)
805 {
806   GtkTreeSelection *selection;
807   GtkTreeIter iter;
808   GtkTreeModel *model;
809   EmpathyFTHandler *handler;
810   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
811
812   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
813
814   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
815     return;
816
817   gtk_tree_model_get (model, &iter, COL_FT_OBJECT, &handler, -1);
818
819   if (empathy_ft_handler_is_completed (handler)){
820     char *uri;
821     GFile *file;
822
823     file = empathy_ft_handler_get_gfile (handler);
824     uri = g_file_get_uri (file);
825
826     DEBUG ("Opening URI: %s", uri);
827     empathy_url_show (GTK_WIDGET (priv->window), uri);
828     g_free (uri);
829   }
830
831   g_object_unref (handler);
832 }
833
834 static void
835 ft_manager_stop (EmpathyFTManager *manager)
836 {
837   GtkTreeSelection *selection;
838   GtkTreeIter iter;
839   GtkTreeModel *model;
840   EmpathyFTHandler *handler;
841   EmpathyFTManagerPriv *priv;
842
843   priv = GET_PRIV (manager);
844
845   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->treeview));
846
847   if (!gtk_tree_selection_get_selected (selection, &model, &iter))
848     return;
849
850   gtk_tree_model_get (model, &iter, COL_FT_OBJECT, &handler, -1);
851   g_return_if_fail (handler != NULL);
852
853   DEBUG ("Stopping file transfer: contact=%s, filename=%s",
854       empathy_contact_get_alias (empathy_ft_handler_get_contact (handler)),
855       empathy_ft_handler_get_filename (handler));
856
857   empathy_ft_handler_cancel_transfer (handler);
858
859   g_object_unref (handler);
860 }
861
862 static gboolean
863 close_window (EmpathyFTManager *manager)
864 {
865   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
866
867   DEBUG ("%p", manager);
868
869   /* remove all the completed/cancelled/errored transfers */
870   ft_manager_clear (manager);
871
872   if (g_hash_table_size (priv->ft_handler_to_row_ref) > 0)
873     {
874       /* There is still FTs on flight, just hide the window */
875       DEBUG ("Hiding window");
876       gtk_widget_hide (priv->window);
877       return TRUE;
878     }
879
880   return FALSE;
881 }
882
883 static void
884 ft_manager_response_cb (GtkWidget *widget,
885                         gint response,
886                         EmpathyFTManager *manager)
887 {
888   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
889
890   switch (response)
891     {
892       case RESPONSE_CLEAR:
893         ft_manager_clear (manager);
894         break;
895       case RESPONSE_OPEN:
896         ft_manager_open (manager);
897         break;
898       case RESPONSE_STOP:
899         ft_manager_stop (manager);
900         break;
901       case RESPONSE_CLOSE:
902         if (!close_window (manager))
903           gtk_widget_destroy (priv->window);
904         break;
905       case GTK_RESPONSE_NONE:
906       case GTK_RESPONSE_DELETE_EVENT:
907         /* Do nothing */
908         break;
909       default:
910         g_assert_not_reached ();
911     }
912 }
913
914 static gboolean
915 ft_manager_delete_event_cb (GtkWidget *widget,
916                             GdkEvent *event,
917                             EmpathyFTManager *manager)
918 {
919   return close_window (manager);
920 }
921
922 static void
923 ft_manager_destroy_cb (GtkWidget *widget,
924                        EmpathyFTManager *manager)
925 {
926   DEBUG ("%p", manager);
927
928   g_object_unref (manager);
929 }
930
931 static gboolean
932 ft_view_button_press_event_cb (GtkWidget *widget,
933                                GdkEventKey *event,
934                                EmpathyFTManager *manager)
935 {
936
937   if (event->type != GDK_2BUTTON_PRESS)
938       return FALSE;
939
940   ft_manager_open (manager);
941
942   return FALSE;
943 }
944
945 static gboolean
946 ft_manager_key_press_event_cb (GtkWidget *widget,
947                                GdkEventKey *event,
948                                gpointer user_data)
949 {
950   if ((event->state & GDK_CONTROL_MASK && event->keyval == GDK_KEY_w)
951       || event->keyval == GDK_KEY_Escape)
952     {
953       gtk_widget_destroy (widget);
954       return TRUE;
955     }
956
957   return FALSE;
958 }
959
960 static void
961 ft_manager_build_ui (EmpathyFTManager *manager)
962 {
963   GtkBuilder *gui;
964   GtkTreeView *view;
965   GtkListStore *liststore;
966   GtkTreeViewColumn *column;
967   GtkCellRenderer *renderer;
968   GtkTreeSelection *selection;
969   gchar *filename;
970   EmpathyFTManagerPriv *priv = GET_PRIV (manager);
971
972   filename = empathy_file_lookup ("empathy-ft-manager.ui", "src");
973   gui = empathy_builder_get_file (filename,
974       "ft_manager_dialog", &priv->window,
975       "ft_list", &priv->treeview,
976       "clear_button", &priv->clear_button,
977       "open_button", &priv->open_button,
978       "abort_button", &priv->abort_button,
979       NULL);
980   g_free (filename);
981
982   empathy_builder_connect (gui, manager,
983       "ft_manager_dialog", "destroy", ft_manager_destroy_cb,
984       "ft_manager_dialog", "response", ft_manager_response_cb,
985       "ft_manager_dialog", "delete-event", ft_manager_delete_event_cb,
986       "ft_manager_dialog", "key-press-event", ft_manager_key_press_event_cb,
987       NULL);
988
989   empathy_builder_unref_and_keep_widget (gui, priv->window);
990
991   /* Window geometry. */
992   empathy_geometry_bind (GTK_WINDOW (priv->window), "ft-manager");
993
994   /* Setup the tree view */
995   view = GTK_TREE_VIEW (priv->treeview);
996   selection = gtk_tree_view_get_selection (view);
997   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
998   g_signal_connect (selection, "changed",
999       G_CALLBACK (ft_manager_selection_changed), manager);
1000   g_signal_connect (view, "button-press-event",
1001                     G_CALLBACK (ft_view_button_press_event_cb),
1002                     manager);
1003   gtk_tree_view_set_headers_visible (view, TRUE);
1004   gtk_tree_view_set_enable_search (view, FALSE);
1005
1006   /* Setup the model */
1007   liststore = gtk_list_store_new (5,
1008       G_TYPE_INT,     /* percent */
1009       G_TYPE_ICON,    /* icon */
1010       G_TYPE_STRING,  /* message */
1011       G_TYPE_STRING,  /* remaining */
1012       G_TYPE_OBJECT); /* ft_handler */
1013   gtk_tree_view_set_model (view, GTK_TREE_MODEL (liststore));
1014   priv->model = GTK_TREE_MODEL (liststore);
1015   g_object_unref (liststore);
1016
1017   /* Progress column */
1018   column = gtk_tree_view_column_new ();
1019   gtk_tree_view_column_set_title (column, _("%"));
1020   gtk_tree_view_column_set_sort_column_id (column, COL_PERCENT);
1021   gtk_tree_view_insert_column (view, column, -1);
1022
1023   renderer = gtk_cell_renderer_progress_new ();
1024   g_object_set (renderer, "xalign", 0.5, NULL);
1025   gtk_tree_view_column_pack_start (column, renderer, FALSE);
1026   gtk_tree_view_column_set_cell_data_func (column, renderer,
1027       ft_manager_progress_cell_data_func, NULL, NULL);
1028
1029   /* Icon and filename column*/
1030   column = gtk_tree_view_column_new ();
1031   gtk_tree_view_column_set_title (column, _("File"));
1032   gtk_tree_view_column_set_expand (column, TRUE);
1033   gtk_tree_view_column_set_resizable (column, TRUE);
1034   gtk_tree_view_column_set_sort_column_id (column, COL_MESSAGE);
1035   gtk_tree_view_column_set_spacing (column, 3);
1036   gtk_tree_view_insert_column (view, column, -1);
1037
1038   renderer = gtk_cell_renderer_pixbuf_new ();
1039   g_object_set (renderer, "xpad", 3,
1040       "stock-size", GTK_ICON_SIZE_DND, NULL);
1041   gtk_tree_view_column_pack_start (column, renderer, FALSE);
1042   gtk_tree_view_column_set_attributes (column, renderer,
1043       "gicon", COL_ICON, NULL);
1044
1045   renderer = gtk_cell_renderer_text_new ();
1046   g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1047   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1048   gtk_tree_view_column_set_attributes (column, renderer,
1049       "text", COL_MESSAGE, NULL);
1050
1051   /* Remaining time column */
1052   column = gtk_tree_view_column_new ();
1053   gtk_tree_view_column_set_title (column, _("Remaining"));
1054   gtk_tree_view_column_set_sort_column_id (column, COL_REMAINING);
1055   gtk_tree_view_insert_column (view, column, -1);
1056
1057   renderer = gtk_cell_renderer_text_new ();
1058   g_object_set (renderer, "xalign", 0.5, NULL);
1059   gtk_tree_view_column_pack_start (column, renderer, FALSE);
1060   gtk_tree_view_column_set_attributes (column, renderer,
1061       "text", COL_REMAINING, NULL);
1062
1063   /* clear button should be sensitive only if there are completed/cancelled
1064    * handlers in the store.
1065    */
1066   gtk_widget_set_sensitive (priv->clear_button, FALSE);
1067 }
1068
1069 /* GObject method overrides */
1070
1071 static void
1072 empathy_ft_manager_finalize (GObject *object)
1073 {
1074   EmpathyFTManagerPriv *priv = GET_PRIV (object);
1075
1076   DEBUG ("FT Manager %p", object);
1077
1078   g_hash_table_unref (priv->ft_handler_to_row_ref);
1079
1080   G_OBJECT_CLASS (empathy_ft_manager_parent_class)->finalize (object);
1081 }
1082
1083 static void
1084 empathy_ft_manager_init (EmpathyFTManager *manager)
1085 {
1086   EmpathyFTManagerPriv *priv;
1087
1088   priv = G_TYPE_INSTANCE_GET_PRIVATE ((manager), EMPATHY_TYPE_FT_MANAGER,
1089       EmpathyFTManagerPriv);
1090
1091   manager->priv = priv;
1092
1093   priv->ft_handler_to_row_ref = g_hash_table_new_full (g_direct_hash,
1094       g_direct_equal, (GDestroyNotify) g_object_unref,
1095       (GDestroyNotify) gtk_tree_row_reference_free);
1096
1097   ft_manager_build_ui (manager);
1098 }
1099
1100 static GObject *
1101 empathy_ft_manager_constructor (GType type,
1102                                 guint n_props,
1103                                 GObjectConstructParam *props)
1104 {
1105   GObject *retval;
1106
1107   if (manager_singleton)
1108     {
1109       retval = G_OBJECT (manager_singleton);
1110     }
1111   else
1112     {
1113       retval = G_OBJECT_CLASS (empathy_ft_manager_parent_class)->constructor
1114           (type, n_props, props);
1115
1116       manager_singleton = EMPATHY_FT_MANAGER (retval);
1117       g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
1118     }
1119
1120   return retval;
1121 }
1122
1123 static void
1124 empathy_ft_manager_class_init (EmpathyFTManagerClass *klass)
1125 {
1126   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1127
1128   object_class->finalize = empathy_ft_manager_finalize;
1129   object_class->constructor = empathy_ft_manager_constructor;
1130
1131   g_type_class_add_private (object_class, sizeof (EmpathyFTManagerPriv));
1132 }
1133
1134 /* public methods */
1135
1136 void
1137 empathy_ft_manager_add_handler (EmpathyFTHandler *handler)
1138 {
1139   EmpathyFTManager *manager;
1140   EmpathyFTManagerPriv *priv;
1141
1142   DEBUG ("Adding handler");
1143
1144   g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler));
1145
1146   manager = g_object_new (EMPATHY_TYPE_FT_MANAGER, NULL);
1147   priv = GET_PRIV (manager);
1148
1149   ft_manager_add_handler_to_list (manager, handler, NULL);
1150   gtk_window_present (GTK_WINDOW (priv->window));
1151 }
1152
1153 void
1154 empathy_ft_manager_display_error (EmpathyFTHandler *handler,
1155                                   const GError *error)
1156 {
1157   EmpathyFTManager *manager;
1158   EmpathyFTManagerPriv *priv;
1159
1160   g_return_if_fail (EMPATHY_IS_FT_HANDLER (handler));
1161   g_return_if_fail (error != NULL);
1162
1163   manager = g_object_new (EMPATHY_TYPE_FT_MANAGER, NULL);
1164   priv = GET_PRIV (manager);
1165
1166   ft_manager_add_handler_to_list (manager, handler, error);
1167   gtk_window_present (GTK_WINDOW (priv->window));
1168 }
1169
1170 void
1171 empathy_ft_manager_show (void)
1172 {
1173   EmpathyFTManager *manager;
1174   EmpathyFTManagerPriv *priv;
1175
1176   manager = g_object_new (EMPATHY_TYPE_FT_MANAGER, NULL);
1177   priv = GET_PRIV (manager);
1178
1179   gtk_window_present (GTK_WINDOW (priv->window));
1180 }