* fix small logic error
[modest] / src / gnome / modest-gnome-info-bar.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30
31 #include <glib/gi18n.h>
32 #include "modest-gnome-info-bar.h"
33 #include <gtk/gtkprogressbar.h>
34 #include <gtk/gtkstatusbar.h>
35 /* include other impl specific header files */
36
37 /* 'private'/'protected' functions */
38 static void modest_gnome_info_bar_class_init (ModestGnomeInfoBarClass *klass);
39 static void modest_gnome_info_bar_init       (ModestGnomeInfoBar *obj);
40 static void modest_gnome_info_bar_finalize   (GObject *obj);
41
42 static void modest_gnome_info_bar_add_operation    (ModestProgressObject *self,
43                                                     ModestMailOperation  *mail_op);
44
45 static void modest_gnome_info_bar_remove_operation (ModestProgressObject *self,
46                                                     ModestMailOperation  *mail_op);
47
48 static void modest_gnome_info_bar_set_pulsating_mode (ModestGnomeInfoBar *self,
49                                                       const gchar* msg,
50                                                       gboolean is_pulsating);
51
52 static void on_progress_changed                    (ModestMailOperation  *mail_op, 
53                                                     ModestMailOperationState *state,
54                                                     ModestGnomeInfoBar *self);
55
56 static gboolean     progressbar_clean        (GtkProgressBar *bar);
57 static gboolean     statusbar_clean          (GtkStatusbar *bar);
58
59 #define MODEST_GNOME_INFO_BAR_PULSE_INTERVAL 125
60
61 /* list my signals  */
62 enum {
63         /* MY_SIGNAL_1, */
64         /* MY_SIGNAL_2, */
65         LAST_SIGNAL
66 };
67
68 typedef struct _ObservableData ObservableData;
69 struct _ObservableData {
70         guint signal_handler;
71         ModestMailOperation *mail_op;
72 };
73
74 typedef struct _ModestGnomeInfoBarPrivate ModestGnomeInfoBarPrivate;
75 struct _ModestGnomeInfoBarPrivate {
76         GSList              *observables;
77         ModestMailOperation *current;
78
79         GtkWidget *status_bar;
80         GtkWidget *progress_bar;
81
82         guint status_bar_timeout;
83         guint progress_bar_timeout;
84         guint pulsating_timeout;
85 };
86
87 #define MODEST_GNOME_INFO_BAR_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
88                                               MODEST_TYPE_GNOME_INFO_BAR, \
89                                               ModestGnomeInfoBarPrivate))
90 /* globals */
91 static GtkHBoxClass *parent_class = NULL;
92
93 /* uncomment the following if you have defined any signals */
94 /* static guint signals[LAST_SIGNAL] = {0}; */
95
96 static void
97 modest_progress_object_init (gpointer g, gpointer iface_data)
98 {
99         ModestProgressObjectIface *klass = (ModestProgressObjectIface *)g;
100
101         klass->add_operation_func = modest_gnome_info_bar_add_operation;
102         klass->remove_operation_func = modest_gnome_info_bar_remove_operation;
103 }
104
105 GType
106 modest_gnome_info_bar_get_type (void)
107 {
108         static GType my_type = 0;
109         if (!my_type) {
110                 static const GTypeInfo my_info = {
111                         sizeof(ModestGnomeInfoBarClass),
112                         NULL,           /* base init */
113                         NULL,           /* base finalize */
114                         (GClassInitFunc) modest_gnome_info_bar_class_init,
115                         NULL,           /* class finalize */
116                         NULL,           /* class data */
117                         sizeof(ModestGnomeInfoBar),
118                         1,              /* n_preallocs */
119                         (GInstanceInitFunc) modest_gnome_info_bar_init,
120                         NULL
121                 };
122
123                 static const GInterfaceInfo modest_progress_object_info = 
124                 {
125                   (GInterfaceInitFunc) modest_progress_object_init, /* interface_init */
126                   NULL,         /* interface_finalize */
127                   NULL          /* interface_data */
128                 };
129
130                 my_type = g_type_register_static (GTK_TYPE_HBOX,
131                                                   "ModestGnomeInfoBar",
132                                                   &my_info, 0);
133
134                 g_type_add_interface_static (my_type, MODEST_TYPE_PROGRESS_OBJECT, 
135                                              &modest_progress_object_info);
136         }
137         return my_type;
138 }
139
140 static void
141 modest_gnome_info_bar_class_init (ModestGnomeInfoBarClass *klass)
142 {
143         GObjectClass *gobject_class;
144         gobject_class = (GObjectClass*) klass;
145
146         parent_class            = g_type_class_peek_parent (klass);
147         gobject_class->finalize = modest_gnome_info_bar_finalize;
148
149         g_type_class_add_private (gobject_class, sizeof(ModestGnomeInfoBarPrivate));
150 }
151
152 static void
153 modest_gnome_info_bar_init (ModestGnomeInfoBar *obj)
154 {
155         ModestGnomeInfoBarPrivate *priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE(obj);
156
157         priv->observables = NULL;
158         priv->current = NULL;
159
160         /* Status bar */
161         priv->status_bar = gtk_statusbar_new ();
162         gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (priv->status_bar), FALSE);
163
164         /* Progress bar */
165         priv->progress_bar = gtk_progress_bar_new ();
166         gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress_bar), 1.0);
167         gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (priv->progress_bar),
168                                         PANGO_ELLIPSIZE_END);
169
170         /* Timeouts */
171         priv->status_bar_timeout = 0;
172         priv->progress_bar_timeout = 0;
173
174         /* Pack */
175         gtk_box_pack_start (GTK_BOX (obj), priv->status_bar, TRUE, TRUE, 0);
176         gtk_box_pack_start (GTK_BOX (obj), priv->progress_bar, FALSE, FALSE, 0);
177 }
178
179 static void
180 destroy_observable_data (ObservableData *data)
181 {
182         g_signal_handler_disconnect (data->mail_op, data->signal_handler);
183         g_object_unref (data->mail_op);
184 }
185
186 static void
187 modest_gnome_info_bar_finalize (GObject *obj)
188 {
189         ModestGnomeInfoBarPrivate *priv;
190
191         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE(obj);
192         if (priv->observables) {
193                 GSList *tmp;
194
195                 for (tmp = priv->observables; tmp; tmp = g_slist_next (tmp)) {
196                         destroy_observable_data ((ObservableData *) tmp->data);
197                         g_free (tmp->data);
198                 }
199                 g_slist_free (priv->observables);
200                 priv->observables = NULL;
201         }
202
203         if (priv->status_bar_timeout > 0) {
204                 g_source_remove (priv->status_bar_timeout);
205                 priv->status_bar_timeout = 0;
206         }
207
208         if (priv->progress_bar_timeout > 0) {
209                 g_source_remove (priv->progress_bar_timeout);
210                 priv->progress_bar_timeout = 0;
211         }
212
213         G_OBJECT_CLASS(parent_class)->finalize (obj);
214 }
215
216 GtkWidget *
217 modest_gnome_info_bar_new (void)
218 {
219         return GTK_WIDGET (g_object_new (MODEST_TYPE_GNOME_INFO_BAR, NULL));
220 }
221
222 static void 
223 modest_gnome_info_bar_add_operation (ModestProgressObject *self,
224                                      ModestMailOperation  *mail_op)
225 {
226         ModestGnomeInfoBar *me;
227         ObservableData *data;
228         ModestGnomeInfoBarPrivate *priv;
229
230         me = MODEST_GNOME_INFO_BAR (self);
231         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (me);
232
233         data = g_malloc0 (sizeof (ObservableData));
234         data->mail_op = g_object_ref (mail_op);
235         data->signal_handler = g_signal_connect (data->mail_op, 
236                                                  "progress-changed",
237                                                  G_CALLBACK (on_progress_changed),
238                                                  me);
239
240         if (priv->observables == NULL) {
241                 priv->current = mail_op;
242         }
243         priv->observables = g_slist_append (priv->observables, data);
244 }
245
246 static gint
247 compare_observable_data (ObservableData *data1, ObservableData *data2)
248 {
249         if (data1->mail_op == data2->mail_op)
250                 return 0;
251         else 
252                 return 1;
253 }
254
255 static void 
256 modest_gnome_info_bar_remove_operation (ModestProgressObject *self,
257                                         ModestMailOperation  *mail_op)
258 {
259         ModestGnomeInfoBar *me;
260         ModestGnomeInfoBarPrivate *priv;
261         GSList *link;
262         ObservableData *tmp_data = NULL;
263         gboolean is_current;
264
265         me = MODEST_GNOME_INFO_BAR (self);
266         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (me);
267
268         is_current = (priv->current == mail_op);
269
270         /* Find item */
271         tmp_data = g_malloc0 (sizeof (ObservableData));
272         tmp_data->mail_op = mail_op;
273         link = g_slist_find_custom (priv->observables,
274                                     tmp_data,
275                                     (GCompareFunc) compare_observable_data);
276         
277         /* Remove the item */
278         if (link) {
279                 ObservableData *ob_data = link->data;
280                 g_signal_handler_disconnect (ob_data->mail_op, ob_data->signal_handler);
281                 g_object_unref (ob_data->mail_op);
282                 g_free (ob_data);
283                 priv->observables = g_slist_delete_link (priv->observables, link);
284                 tmp_data->mail_op = NULL;
285                 link = NULL;
286         }
287         
288         /* Update the current mail operation */
289         if (is_current) {
290                 if (priv->observables)
291                         priv->current = ((ObservableData *) priv->observables->data)->mail_op;
292                 else
293                         priv->current = NULL;
294
295                 /* Refresh the view */
296                 modest_gnome_info_bar_set_pulsating_mode (me, NULL, FALSE);
297                 progressbar_clean (GTK_PROGRESS_BAR (priv->progress_bar));
298         }
299         
300         /* free */
301         g_free(tmp_data);
302 }
303
304 static void 
305 on_progress_changed (ModestMailOperation  *mail_op, 
306                      ModestMailOperationState *state,
307                      ModestGnomeInfoBar *self)
308 {
309         ModestGnomeInfoBarPrivate *priv;
310         gboolean determined = FALSE;
311
312         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (self);
313
314         /* If the mail operation is the currently shown one */
315         if (priv->current == mail_op) {
316                 gchar *msg = NULL;
317                 
318                 determined = (state->done > 0 && state->total > 1) && 
319                         !(state->done == 1 && state->total == 100);
320
321                 switch (state->op_type) {
322                 case MODEST_MAIL_OPERATION_TYPE_RECEIVE:                
323                         if (determined)
324                                 msg = g_strdup_printf(_("mcen_me_receiving"),
325                                                       state->done, state->total); 
326                         else 
327                                 msg = g_strdup(_("mail_me_receiving"));
328                         break;
329                 case MODEST_MAIL_OPERATION_TYPE_SEND:           
330                         if (determined)
331                                 msg = g_strdup_printf(_("mcen_me_sending"), state->done,
332                                                       state->total);
333                         else
334                                 msg = g_strdup(_("mail_me_sending"));
335                         break;
336                         
337                 case MODEST_MAIL_OPERATION_TYPE_OPEN:           
338                         msg = g_strdup(_("mail_me_opening"));
339                         break;
340                 default:
341                         msg = g_strdup("");
342                 }
343                 
344                 /* If we have byte information use it */
345                 if ((state->bytes_done != 0) && (state->bytes_total != 0))
346                         modest_gnome_info_bar_set_progress (self, msg,
347                                                             state->bytes_done,
348                                                             state->bytes_total);
349                 else if ((state->done == 0) && (state->total == 0))
350                         modest_gnome_info_bar_set_pulsating_mode (self, msg, TRUE);
351                 else
352                         modest_gnome_info_bar_set_progress (self, msg,
353                                                             state->done,
354                                                             state->total);
355                 g_free (msg);
356         }
357 }
358
359 static gboolean
360 progressbar_clean (GtkProgressBar *bar)
361 {
362         gtk_progress_bar_set_fraction (bar, 0);
363         gtk_progress_bar_set_text (bar, 0);
364         return FALSE;
365 }
366
367 static gboolean
368 statusbar_clean (GtkStatusbar *bar)
369 {
370         gtk_statusbar_push (bar, 0, "");
371         return FALSE;
372 }
373
374 void 
375 modest_gnome_info_bar_set_message    (ModestGnomeInfoBar *self,
376                                       const gchar *message)
377 {
378         ModestGnomeInfoBarPrivate *priv;
379
380         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (self);
381
382         /* Set a message. Clean it after 2.5 seconds */
383         gtk_statusbar_push (GTK_STATUSBAR (priv->status_bar), 0, message);
384         priv->status_bar_timeout = g_timeout_add (2500, 
385                                                   (GSourceFunc) statusbar_clean, 
386                                                   priv->status_bar);
387 }
388
389 static gboolean
390 modest_gnome_info_bar_is_pulsating (ModestGnomeInfoBar *self)
391 {
392         ModestGnomeInfoBarPrivate *priv;
393
394         g_return_val_if_fail (MODEST_IS_GNOME_INFO_BAR(self), FALSE);
395
396         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (self);
397         
398         return priv->pulsating_timeout != 0;
399 }
400
401 void 
402 modest_gnome_info_bar_set_progress   (ModestGnomeInfoBar *self,
403                                       const gchar *message,
404                                       gint done,
405                                       gint total)
406 {
407         ModestGnomeInfoBarPrivate *priv;
408         gboolean determined = FALSE;
409
410         g_return_if_fail (MODEST_IS_GNOME_INFO_BAR(self));
411         g_return_if_fail (done <= total);
412         
413         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (self);
414
415         if (modest_gnome_info_bar_is_pulsating (self))
416                 modest_gnome_info_bar_set_pulsating_mode (self, NULL, FALSE);
417
418         /* Set progress. Tinymail sometimes returns us 1/100 when it
419            does not have any clue, NOTE that 1/100 could be also a
420            valid progress (we will loose it), but it will be recovered
421            once the done is greater than 1 */
422         determined = (done > 0 && total > 1) && 
423                 !(done == 1 && total == 100);
424         if (!determined) {
425                 gtk_progress_bar_pulse (GTK_PROGRESS_BAR (priv->progress_bar));
426         } else {
427                 gdouble percent = 0;
428                 if (total != 0) /* Avoid division by zero. */
429                         percent = (gdouble)done/(gdouble)total;
430
431                 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (priv->progress_bar),
432                                                percent);
433         }
434         
435         /* Set text */
436         gtk_progress_bar_set_text (GTK_PROGRESS_BAR (priv->progress_bar), 
437                                    (message && message[0] != '\0')?message:" ");
438 }
439
440 static gboolean
441 do_pulse (gpointer data)
442 {
443         ModestGnomeInfoBarPrivate *priv;
444         
445         g_return_val_if_fail (MODEST_IS_GNOME_INFO_BAR(data), FALSE);
446         
447         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (data);
448         gtk_progress_bar_pulse (GTK_PROGRESS_BAR (priv->progress_bar));
449         return TRUE;
450 }
451
452 static void
453 modest_gnome_info_bar_set_pulsating_mode (ModestGnomeInfoBar *self,
454                                           const gchar* msg,
455                                           gboolean is_pulsating)
456 {
457         ModestGnomeInfoBarPrivate *priv;
458
459         g_return_if_fail (MODEST_IS_GNOME_INFO_BAR (self));
460
461         priv = MODEST_GNOME_INFO_BAR_GET_PRIVATE (self);
462         
463         if (msg != NULL)
464                 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (priv->progress_bar), msg);
465         
466         if (is_pulsating == (priv->pulsating_timeout != 0))
467                 return;
468         else if (is_pulsating && (priv->pulsating_timeout == 0)) {
469                 /* enable */
470                 priv->pulsating_timeout = g_timeout_add (MODEST_GNOME_INFO_BAR_PULSE_INTERVAL,
471                                                          do_pulse, self);
472         } else if (!is_pulsating && (priv->pulsating_timeout != 0)) {
473                 /* disable */
474                 g_source_remove (priv->pulsating_timeout);
475                 priv->pulsating_timeout = 0;
476         }
477 }