Fix: hildon api renamed, following the change
[hildon-application-manager:mainline.git] / src / util.cc
1 /*
2  * This file is part of the hildon-application-manager.
3  *
4  * Copyright (C) 2005, 2006, 2007, 2008 Nokia Corporation.  All Rights reserved.
5  *
6  * Contact: Marius Vollmer <marius.vollmer@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version
10  * 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23
24 #include <string.h>
25 #include <unistd.h>
26 #include <assert.h>
27 #include <libintl.h>
28 #include <ctype.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <sys/statvfs.h>
33 #include <signal.h>
34
35 #include <gconf/gconf-client.h>
36 #include <hildon/hildon-file-chooser-dialog.h>
37 #include <gdk/gdkkeysyms.h>
38 #include <conic.h>
39 #include <dbus/dbus.h>
40
41 #include "util.h"
42 #include "details.h"
43 #include "log.h"
44 #include "settings.h"
45 #include "menu.h"
46 #include "operations.h"
47 #include "apt-worker-client.h"
48 #include "user_files.h"
49 #include "update-notifier-conf.h"
50 #include "package-info-cell-renderer.h"
51 #include "confutils.h"
52
53 #define _(x) gettext (x)
54 #define _FM(x) dgettext ("hildon-fm", x)
55 #define _HCS(x) dgettext ("hildon-common-strings", x)
56
57 static Window parent_xid = None;
58 static GSList *dialog_stack = NULL;
59 static bool main_window_shown = false;
60
61 static time_t idle_since = 0;
62 static guint idle_timeout_id = 0;
63 static void (*idle_cont) (void *) = NULL;
64 static void *idle_data = NULL;
65
66 #define IDLE_TIMEOUT_SECS 60
67 #define SCREENSHOT_DIR "launch"
68 #define HILDON_APP_MGR_SERVICE "com.nokia.hildon_application_manager"
69
70 static void
71 dialog_realized (GtkWidget *dialog, gpointer data)
72 {
73   GdkWindow *win = dialog->window;
74
75   XSetTransientForHint (GDK_WINDOW_XDISPLAY (win), GDK_WINDOW_XID (win),
76                         parent_xid);
77 }
78
79 void
80 push_dialog (GtkWidget *dialog)
81 {
82   /* Setting a dialog application modal affects us, not the
83      application that the dialog is transient for.  So we only make it
84      application modal when the dialog stack is shown on top of us.
85   */
86   g_assert (dialog != NULL);
87
88   gtk_window_set_modal (GTK_WINDOW (dialog), parent_xid == None);
89
90   if (dialog_stack)
91     {
92       g_debug ("parent %p", dialog_stack->data);
93       gtk_window_set_transient_for (GTK_WINDOW (dialog),
94                                     GTK_WINDOW (dialog_stack->data));
95     }
96   else if (parent_xid != None)
97     g_signal_connect (dialog, "realize",
98                       G_CALLBACK (dialog_realized), NULL);
99   else
100     {
101       /* This happens for dialogs outside of a interaction flow.
102        */
103       gtk_window_set_transient_for (GTK_WINDOW (dialog),
104                                     get_main_window ());
105     }
106
107   g_debug ("pushing dialog %p", dialog);
108   dialog_stack = g_slist_prepend (dialog_stack, dialog);
109 }
110
111 void
112 pop_dialog (GtkWidget *dialog)
113 {
114   g_assert (dialog_stack);
115   g_debug ("child = %p ~ parent = %p", dialog, dialog_stack->data);
116   g_assert (dialog_stack->data == dialog);
117
118   {
119     GSList *old = dialog_stack;
120     dialog_stack = dialog_stack->next;
121     g_slist_free_1 (old);
122   }
123 }
124
125 static bool interaction_flow_active = false;
126
127 bool
128 is_idle ()
129 {
130   return dialog_stack == NULL && !interaction_flow_active;
131 }
132
133 bool
134 is_topmost_dialog (GtkWidget *dialog)
135 {
136   return (dialog_stack != NULL) && (dialog_stack->data == dialog);
137 }
138
139 bool
140 start_interaction_flow ()
141 {
142   /* XXX - We don't allow interaction flows to start when a dialog is
143            open.  This is a bit too restrictive since we should be
144            able to run one just fine in parallel to showing the
145            "Details" or "Catalogues" dialog, say.  We don't allow it
146            since it would mess with having a single stack of dialogs.
147   */
148
149   if (dialog_stack != NULL
150       || interaction_flow_active)
151     {
152       irritate_user (_("ai_ni_operation_progress"));
153       return false;
154     }
155
156   g_assert (dialog_stack == NULL);
157
158   interaction_flow_active = true;
159   parent_xid = None;
160   dialog_stack = g_slist_prepend (dialog_stack, get_main_window ());
161
162   if (idle_timeout_id)
163     {
164       g_source_remove (idle_timeout_id);
165       idle_timeout_id = 0;
166     }
167
168   return true;
169 }
170
171 bool
172 start_foreign_interaction_flow (Window parent)
173 {
174   if (dialog_stack != NULL
175       || interaction_flow_active)
176     return false;
177
178   g_assert (dialog_stack == NULL);
179
180   interaction_flow_active = true;
181   parent_xid = parent;
182   return true;
183 }
184
185 bool
186 start_interaction_flow_when_idle (void (*cont) (void *), void *data)
187 {
188   if (is_idle () && time (NULL) > idle_since + IDLE_TIMEOUT_SECS)
189     {
190       if (start_interaction_flow ())
191         cont (data);
192       return true;
193     }
194   else if (idle_cont != NULL)
195     {
196       return false;
197     }
198   else
199     {
200       idle_cont = cont;
201       idle_data = data;
202       return true;
203     }
204 }
205
206 static gboolean
207 idle_callback (gpointer unused)
208 {
209   if (idle_cont)
210     {
211       if (start_interaction_flow ())
212         {
213           void (*cont) (void *) = idle_cont;
214           void *data = idle_data;
215
216           idle_cont = NULL;
217           idle_data = NULL;
218
219           cont (data);
220         }
221     }
222
223   return FALSE;
224 }
225
226 void
227 reset_idle_timer ()
228 {
229   if (idle_timeout_id)
230     g_source_remove (idle_timeout_id);
231
232   if (is_idle ())
233     {
234       idle_since = time (NULL);
235       idle_timeout_id = g_timeout_add (IDLE_TIMEOUT_SECS * 1000,
236                                        idle_callback,
237                                        NULL);
238     }
239 }
240
241 void
242 end_interaction_flow ()
243 {
244   g_assert (interaction_flow_active);
245
246   if (parent_xid == None)
247     {
248       g_assert (g_slist_length (dialog_stack) == 1);
249
250       GtkWidget* initmainwin = GTK_WIDGET (dialog_stack->data);
251       GtkWidget* curmainwin = GTK_WIDGET (get_main_window ());
252       if (initmainwin != curmainwin)
253         g_debug ("We lose the initial interaction flow window!");
254
255       pop_dialog (initmainwin);
256     }
257
258   interaction_flow_active = false;
259   parent_xid = None;
260
261   reset_idle_timer ();
262 }
263
264 bool
265 is_interaction_flow_active ()
266 {
267   return interaction_flow_active;
268 }
269
270 static gboolean
271 screenshot_already_exists ()
272 {
273   gchar *sshot_dir = NULL;
274   GDir *dir;
275   gboolean res = FALSE;
276
277   sshot_dir = g_strdup_printf ("%s/%s", g_get_user_cache_dir (), SCREENSHOT_DIR);
278   dir = g_dir_open (sshot_dir, 0, NULL);
279   if (dir)
280     {
281       const gchar *sshot_file;
282       while (!res && (sshot_file = g_dir_read_name (dir)) != NULL)
283         {
284           if (g_str_has_prefix (sshot_file, HILDON_APP_MGR_SERVICE))
285             res = TRUE;
286         }
287       g_dir_close (dir);
288     }
289
290   g_free (sshot_dir);
291   return res;
292 }
293
294 void
295 maybe_take_screenshot (GtkWindow *win)
296 {
297   if (!screenshot_already_exists ())
298     hildon_gtk_window_take_screenshot (win, TRUE);
299 }
300
301 void
302 present_main_window ()
303 {
304   reset_idle_timer ();
305   main_window_shown = true;
306   gtk_window_present (get_main_window ());
307 }
308
309 void
310 hide_main_window ()
311 {
312   if (GTK_IS_WIDGET (get_main_window ()))
313     gtk_widget_hide (GTK_WIDGET (get_main_window ()));
314   main_window_shown = false;
315   if (!interaction_flow_active)
316     exit (0);
317 }
318
319 void
320 maybe_exit ()
321 {
322   if (!main_window_shown && !interaction_flow_active)
323     exit (0);
324 }
325
326 struct ayn_closure {
327   package_info *pi;
328   detail_kind kind;
329   void (*cont) (bool res, void *data);
330   void (*details) (void *data);
331   void *data;
332 };
333
334 static void
335 yes_no_details_done (void *data)
336 {
337 }
338
339 static void
340 yes_no_response (GtkDialog *dialog, gint response, gpointer clos)
341 {
342   ayn_closure *c = (ayn_closure *)clos;
343
344   if (response == 1)
345     {
346       if (c->pi)
347         show_package_details (c->pi, c->kind, false,  yes_no_details_done, c);
348       else if (c->details)
349         c->details (c->data);
350       return;
351     }
352
353   void (*cont) (bool res, void *data) = c->cont;
354   void *data = c->data;
355   if (c->pi)
356     c->pi->unref ();
357   delete c;
358
359   pop_dialog (GTK_WIDGET (dialog));
360   gtk_widget_destroy (GTK_WIDGET (dialog));
361   if (cont)
362     cont (response == GTK_RESPONSE_OK, data);
363 }
364
365 void
366 ask_yes_no (const gchar *question,
367             void (*cont) (bool res, void *data),
368             void *data)
369 {
370   GtkWidget *dialog;
371   ayn_closure *c = new ayn_closure;
372   c->pi = NULL;
373   c->cont = cont;
374   c->details = NULL;
375   c->data = data;
376
377   dialog = hildon_note_new_confirmation (NULL, question);
378   push_dialog (dialog);
379
380   g_signal_connect (dialog, "response",
381                     G_CALLBACK (yes_no_response), c);
382   gtk_widget_show_all (dialog);
383 }
384
385 void
386 ask_yes_no_with_title (const gchar *title,
387                        const gchar *question,
388                        void (*cont) (bool res, void *data),
389                        void *data)
390 {
391   GtkWidget *dialog;
392   ayn_closure *c = new ayn_closure;
393   c->pi = NULL;
394   c->cont = cont;
395   c->details = NULL;
396   c->data = data;
397
398   dialog = gtk_dialog_new_with_buttons
399     (title,
400      NULL,
401      GTK_DIALOG_MODAL,
402      _("ai_bd_confirm_ok"),      GTK_RESPONSE_OK,
403      NULL);
404   push_dialog (dialog);
405
406   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
407   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
408                      gtk_label_new (question));
409
410   g_signal_connect (dialog, "response",
411                     G_CALLBACK (yes_no_response), c);
412   gtk_widget_show_all (dialog);
413 }
414
415 void
416 ask_custom (const gchar *question,
417             const gchar *ok_label, const gchar *cancel_label,
418             void (*cont) (bool res, void *data),
419             void *data)
420 {
421   GtkWidget *dialog;
422   ayn_closure *c = new ayn_closure;
423   c->pi = NULL;
424   c->cont = cont;
425   c->details = NULL;
426   c->data = data;
427
428   dialog = hildon_note_new_confirmation_add_buttons
429     (NULL,
430      question,
431      ok_label, GTK_RESPONSE_OK,
432      cancel_label, GTK_RESPONSE_CANCEL,
433      NULL);
434   push_dialog (dialog);
435
436   g_signal_connect (dialog, "response",
437                     G_CALLBACK (yes_no_response), c);
438   gtk_widget_show_all (dialog);
439 }
440
441 void
442 ask_yes_no_with_details (const gchar *title,
443                          const gchar *question,
444                          package_info *pi, detail_kind kind,
445                          void (*cont) (bool res, void *data),
446                          void *data)
447 {
448   GtkWidget *dialog;
449   ayn_closure *c = new ayn_closure;
450   c->pi = pi;
451   pi->ref ();
452   c->kind = kind;
453   c->cont = cont;
454   c->details = NULL;
455   c->data = data;
456
457   char *ok = (kind == remove_details) ?
458     _("ai_bd_confirm_uninstall") : _("ai_bd_confirm_ok");
459
460   dialog = gtk_dialog_new_with_buttons
461     (title,
462      NULL,
463      GTK_DIALOG_MODAL,
464      _("ai_bd_confirm_details"), 1,
465      ok, GTK_RESPONSE_OK,
466      NULL);
467   push_dialog (dialog);
468
469   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
470   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
471                      gtk_label_new (question));
472
473   g_signal_connect (dialog, "response",
474                     G_CALLBACK (yes_no_response), c);
475   gtk_widget_show_all (dialog);
476 }
477
478 void
479 ask_yes_no_with_arbitrary_details (const gchar *title,
480                                    const gchar *question,
481                                    void (*cont) (bool res, void *data),
482                                    void (*details) (void *data),
483                                    void *data)
484 {
485   GtkWidget *dialog;
486   ayn_closure *c = new ayn_closure;
487   c->pi = NULL;
488   c->cont = cont;
489   c->details = details;
490   c->data = data;
491
492   dialog = gtk_dialog_new_with_buttons
493     (title,
494      NULL,
495      GTK_DIALOG_MODAL,
496      _("ai_bd_add_catalogue_details"), 1,
497      _("ai_bd_add_catalogue_ok"),      GTK_RESPONSE_OK,
498      NULL);
499   push_dialog (dialog);
500
501   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
502   GtkWidget *label = gtk_label_new (question);
503   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
504   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox),
505                      label);
506
507   g_signal_connect (dialog, "response",
508                     G_CALLBACK (yes_no_response), c);
509   gtk_widget_show_all (dialog);
510 }
511
512 struct auwc_closure {
513   void (*cont) (void *);
514   void *data;
515 };
516
517 static void
518 annoy_user_response (GtkDialog *dialog, gint response, gpointer data)
519 {
520   auwc_closure * closure_data = (auwc_closure *) data;
521
522   pop_dialog (GTK_WIDGET (dialog));
523   gtk_widget_destroy (GTK_WIDGET (dialog));
524
525   if (closure_data != NULL)
526     {
527       if (closure_data->cont != NULL)
528         closure_data->cont (closure_data->data);
529       delete closure_data;
530     }
531 }
532
533 void
534 annoy_user (const gchar *text, void (*cont) (void *data), void *data)
535 {
536   GtkWidget *dialog;
537   auwc_closure * closure_data = new auwc_closure;
538
539   dialog = hildon_note_new_information (NULL, text);
540   push_dialog (dialog);
541   closure_data->cont = cont;
542   closure_data->data = data;
543   g_signal_connect (dialog, "response",
544                     G_CALLBACK (annoy_user_response), closure_data);
545   gtk_widget_show_all (dialog);
546 }
547
548 struct auwd_closure {
549   package_info *pi;
550   detail_kind kind;
551   int variant;
552   void (*details) (void *data);
553   void (*cont) (void *data);
554   void *data;
555 };
556
557 static void
558 annoy_details_done (void *data)
559 {
560 }
561
562 static void
563 annoy_user_with_details_response (GtkDialog *dialog, gint response,
564                                   gpointer data)
565 {
566   auwd_closure *c = (auwd_closure *)data;
567
568   if (response == 1)
569     {
570       if (c->pi)
571         show_package_details (c->pi, c->kind, true, annoy_details_done, c);
572       else
573         {
574           if (c->variant == 2)
575             {
576               pop_dialog (GTK_WIDGET (dialog));
577               gtk_widget_destroy (GTK_WIDGET (dialog));
578               if (c->pi)
579                 c->pi->unref ();
580               if (c->details)
581                 c->details (c->data);
582               delete c;
583             }
584           else
585             {
586               if (c->details)
587                 c->details (c->data);
588             }
589         }
590     }
591   else
592     {
593       pop_dialog (GTK_WIDGET (dialog));
594       gtk_widget_destroy (GTK_WIDGET (dialog));
595       if (c->pi)
596         c->pi->unref ();
597       if (c->cont)
598         c->cont(c->data);
599       delete c;
600     }
601 }
602
603 /* There are two variants of the "with-details" dialog (which might
604    get unified soon, or not).  Variant one uses the "Close" label and
605    runs the details callback as a subroutine of the dialog.  Variant 2
606    uses the "Ok" label and closes the dialog before invoking the
607    details callback.
608 */
609
610 static void
611 annoy_user_with_details_1 (const gchar *text,
612                            void (*details) (void *data),
613                            package_info *pi, detail_kind kind,
614                            int variant,
615                            void (*cont) (void *data),
616                            void *data)
617 {
618   GtkWidget *dialog, *label;
619   auwd_closure *c = new auwd_closure;
620
621   dialog = gtk_dialog_new_with_buttons (NULL, NULL, GTK_DIALOG_MODAL,
622                                         _("ai_ni_bd_details"), 1,
623                                         NULL);
624   push_dialog (dialog);
625
626   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
627   label = gtk_label_new (text);
628   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
629   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
630                       TRUE, TRUE, HILDON_MARGIN_DEFAULT);
631
632   if (pi)
633     pi->ref ();
634   c->pi = pi;
635   c->kind = kind;
636   c->variant = variant;
637   c->details = details;
638   c->cont = cont;
639   c->data = data;
640   g_signal_connect (dialog, "response",
641                     G_CALLBACK (annoy_user_with_details_response), c);
642   gtk_widget_show_all (dialog);
643 }
644
645 void
646 annoy_user_with_details (const gchar *text,
647                          package_info *pi, detail_kind kind,
648                          void (*cont) (void *data),
649                          void *data)
650 {
651   annoy_user_with_details_1 (text,
652                              NULL,
653                              pi, kind, 1,
654                              cont,
655                              data);
656 }
657
658 void
659 annoy_user_with_arbitrary_details (const gchar *text,
660                                    void (*details) (void *data),
661                                    void (*cont) (void *data),
662                                    void *data)
663 {
664   annoy_user_with_details_1 (text,
665                              details,
666                              NULL, no_details, 1,
667                              cont,
668                              data);
669 }
670
671 void
672 annoy_user_with_arbitrary_details_2 (const gchar *text,
673                                      void (*details) (void *data),
674                                      void (*cont) (void *data),
675                                      void *data)
676 {
677   annoy_user_with_details_1 (text,
678                              details,
679                              NULL, no_details, 2,
680                              cont,
681                              data);
682 }
683
684 void
685 annoy_user_with_errno (int err, const gchar *detail,
686                        void (*cont) (void *), void *data)
687 {
688   add_log ("%s: %s\n", detail, strerror (err));
689
690   const char *msg;
691   if (err == ENAMETOOLONG)
692     msg = _HCS ("file_ib_name_too_long");
693   else if (err == EPERM || err == EACCES)
694     msg = _FM ("sfil_ib_saving_not_allowed");
695   else if (err == ENOENT)
696     msg = _HCS ("sfil_ni_cannot_continue_target_folder_deleted");
697   else if (err == ENOSPC)
698     msg = _HCS ("sfil_ni_not_enough_memory");
699   else
700     msg = _("ai_ni_operation_failed");
701
702   annoy_user (msg, cont, data);
703 }
704
705 void
706 annoy_user_with_gnome_vfs_result (GnomeVFSResult result, const gchar *detail,
707                                   void (*cont) (void *), void *data)
708 {
709   add_log ("%s: %s\n", detail, gnome_vfs_result_to_string (result));
710
711   if (result == GNOME_VFS_ERROR_NAME_TOO_LONG)
712     {
713       irritate_user (_HCS ("file_ib_name_too_long"));
714       cont (data);
715     }
716   else if (result == GNOME_VFS_ERROR_ACCESS_DENIED
717            || result == GNOME_VFS_ERROR_NOT_PERMITTED)
718     {
719       irritate_user (_FM ("sfil_ib_saving_not_allowed"));
720       cont (data);
721     }
722   else if (result == GNOME_VFS_ERROR_NOT_FOUND)
723     annoy_user (_HCS ("sfil_ni_cannot_continue_target_folder_deleted"),
724                 cont, data);
725   else if (result == GNOME_VFS_ERROR_NO_SPACE)
726     annoy_user (_HCS ("sfil_ni_not_enough_memory"),
727                 cont, data);
728   else if (result == GNOME_VFS_ERROR_READ_ONLY)
729     annoy_user (_FM ("ckdg_fi_properties_read_only"),
730                 cont, data);
731   else if (result == GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM)
732     annoy_user (_FM ("sfil_ib_readonly_location"),
733                 cont, data);
734   else
735     annoy_user (_("ai_ni_operation_failed"), cont, data);
736 }
737
738 void
739 irritate_user (const gchar *text)
740 {
741   hildon_banner_show_information (GTK_WIDGET (get_main_window()),
742                                   NULL, text);
743 }
744
745 void
746 what_the_fock_p ()
747 {
748   irritate_user (_("ai_ni_operation_failed"));
749 }
750
751 static void
752 pannable_area_size_request (GtkWidget *widget, GtkRequisition *requisition,
753                             gpointer user_data)
754 {
755   GtkRequisition child_req;
756   GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
757
758   if (child && GTK_IS_VIEWPORT (child))
759     child = gtk_bin_get_child (GTK_BIN (child));
760
761   if (child)
762     {
763       gtk_widget_size_request (child, &child_req);
764       requisition->height = MIN (350, child_req.height);
765     }
766 }
767
768 void
769 hildon_pannable_area_set_size_request_children (HildonPannableArea *area)
770 {
771   g_signal_connect (area, "size-request",
772                     G_CALLBACK (pannable_area_size_request), NULL);
773 }
774
775 static GtkWidget *
776 make_scare_user_with_legalese (bool multiple)
777 {
778   GtkWidget *scroll;
779   GtkWidget *label;
780
781   label = make_small_label ((multiple) ?
782                             _("ai_nc_non_verified_package_multiple") :
783                             _("ai_nc_non_verified_package"));
784   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
785
786   scroll = hildon_pannable_area_new ();
787   hildon_pannable_area_add_with_viewport (HILDON_PANNABLE_AREA (scroll), label);
788   hildon_pannable_area_set_size_request_children (HILDON_PANNABLE_AREA (scroll));
789
790   return scroll;
791 }
792
793 static void
794 user_agreed (GtkToggleButton *button, GtkWidget *dialog)
795 {
796   gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
797                                      GTK_RESPONSE_OK,
798                                      hildon_check_button_get_active
799                                      (HILDON_CHECK_BUTTON (button)));
800 }
801
802 void
803 install_confirm (bool scare_user, package_info *pi, bool multiple,
804                  void (*cont) (bool res, void *data),
805                  void (*details) (void *data),
806                  void *data)
807 {
808   ayn_closure *c = new ayn_closure;
809   c->pi = NULL;
810   c->cont = cont;
811   c->details = details;
812   c->data = data;
813
814   GtkWidget *dialog, *label;
815   char *text = NULL;
816   char size_buf[20];
817
818   if (pi->info.download_size > 0)
819     size_string_general (size_buf, 20, pi->info.download_size);
820   else
821     size_string_general (size_buf, 20, pi->info.install_user_size_delta);
822
823   text = g_strdup_printf ((pi->installed_version
824                            ? _("ai_nc_update") : _("ai_nc_install")),
825                           pi->get_display_name (false),
826                           pi->get_display_version (false), size_buf);
827
828   label = gtk_label_new (text);
829   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
830   g_free (text);
831
832   dialog = gtk_dialog_new_with_buttons ((pi->installed_version
833                                          ? _("ai_ti_confirm_update")
834                                          : _("ai_ti_confirm_install")),
835                                         NULL,
836                                         GTK_DIALOG_MODAL,
837                                         _("ai_bd_confirm_details"), 1,
838                                         _("ai_bd_confirm_ok"), GTK_RESPONSE_OK,
839                                         NULL);
840
841   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
842
843   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
844                       TRUE, TRUE, 1);
845
846   if (scare_user)
847     {
848       GtkWidget *legalese = make_scare_user_with_legalese (multiple);
849
850       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), legalese,
851                           TRUE, TRUE, 1);
852
853       gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
854                                          GTK_RESPONSE_OK, FALSE);
855
856       GtkWidget *check = hildon_check_button_new (HILDON_SIZE_FINGER_HEIGHT);
857       gtk_button_set_label (GTK_BUTTON (check),
858                             _("ai_ti_confirmation_checkbox"));
859       gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox), check,
860                           TRUE, TRUE, 1);
861       g_signal_connect (check, "toggled", G_CALLBACK (user_agreed), dialog);
862     }
863
864   push_dialog (dialog);
865
866   g_signal_connect (dialog, "response", G_CALLBACK (yes_no_response), c);
867
868   gtk_widget_show_all (dialog);
869 }
870
871 /* Entertaining the user.
872  */
873
874 static PangoFontDescription *
875 get_small_font (GtkWidget *widget)
876 {
877   static PangoFontDescription *small_font = NULL;
878   gint size = 0;
879
880   if (small_font == NULL)
881     {
882       GtkStyle *fontstyle = NULL;
883
884       fontstyle = gtk_rc_get_style_by_paths (gtk_widget_get_settings (GTK_WIDGET(widget)),
885                                              "osso-SystemFont", NULL,
886                                              G_TYPE_NONE);
887
888       if (fontstyle) {
889         small_font = pango_font_description_copy (fontstyle->font_desc);
890       } else {
891         small_font = pango_font_description_from_string ("Nokia Sans 16.75");
892       }
893       size = pango_font_description_get_size(small_font);
894       size = gint (size * PANGO_SCALE_SMALL);
895       pango_font_description_set_size(small_font, size);
896
897     }
898
899   return small_font;
900 }
901
902 static void
903 progressbar_dialog_realized (GtkWidget *widget, gpointer data)
904 {
905   GdkWindow *win = widget->window;
906   gdk_window_set_decorations (win, GDK_DECOR_BORDER);
907 }
908
909 struct entertainment_data {
910   gint depth;
911
912   GtkWidget *dialog, *bar, *cancel_button;
913   gint pulse_id;
914
915   char *main_title, *sub_title;
916   gboolean strong_main_title;
917
918   int n_games;
919   entertainment_game *games;
920   int current_game;
921   double completed_fraction;
922
923   int64_t already, total;
924
925   void (*cancel_callback) (void *);
926   void *cancel_data;
927   bool was_cancelled;
928   bool was_broke;
929 };
930
931 static entertainment_data entertainment;
932
933 static gboolean
934 entertainment_pulse (gpointer data)
935 {
936   entertainment_data *ent = (entertainment_data *)data;
937
938   if (ent->bar)
939     gtk_progress_bar_pulse (GTK_PROGRESS_BAR (ent->bar));
940   return TRUE;
941 }
942
943 static void
944 entertainment_start_pulsing ()
945 {
946   if (entertainment.pulse_id == 0)
947     entertainment.pulse_id =
948       gtk_timeout_add (500, entertainment_pulse, &entertainment) + 1;
949 }
950
951 static void
952 entertainment_stop_pulsing ()
953 {
954   if (entertainment.pulse_id != 0)
955     {
956       g_source_remove (entertainment.pulse_id - 1);
957       entertainment.pulse_id = 0;
958     }
959 }
960
961 static void
962 entertainment_update_progress ()
963 {
964   if (entertainment.bar)
965     {
966       if (entertainment.already < 0)
967         entertainment_start_pulsing ();
968       else
969         {
970           entertainment_game *game =
971             &entertainment.games[entertainment.current_game];
972           double fraction =
973             (entertainment.completed_fraction
974              + game->fraction * (((double)entertainment.already)
975                                  / entertainment.total));
976
977           entertainment_stop_pulsing ();
978           gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (entertainment.bar),
979                                          fraction);
980         }
981     }
982 }
983
984 static void
985 entertainment_update_cancel ()
986 {
987   if (entertainment.cancel_button)
988     gtk_widget_set_sensitive (entertainment.cancel_button,
989                               entertainment.cancel_callback != NULL);
990 }
991
992 static void
993 entertainment_update_title ()
994 {
995   if (entertainment.dialog)
996     {
997       if (entertainment.sub_title && !entertainment.strong_main_title)
998         gtk_progress_bar_set_text (GTK_PROGRESS_BAR (entertainment.bar),
999                                    entertainment.sub_title);
1000       else
1001         gtk_progress_bar_set_text (GTK_PROGRESS_BAR (entertainment.bar),
1002                                    entertainment.main_title);
1003     }
1004 }
1005
1006 static gboolean
1007 entertainment_focus_in_event_cb (GtkWidget *dialog,
1008                                  GdkEventFocus *event,
1009                                  gpointer unused)
1010 {
1011   entertainment_update_progress ();
1012   return FALSE;
1013 }
1014
1015 static gboolean
1016 entertainment_focus_out_event_cb (GtkWidget *dialog,
1017                                   GdkEventFocus *event,
1018                                   gpointer unused)
1019 {
1020   g_assert (dialog != NULL);
1021
1022   /* Stop progressbar activity if not the topmost one */
1023   if (!is_topmost_dialog (dialog))
1024       entertainment_stop_pulsing ();
1025
1026   return FALSE;
1027 }
1028
1029 static void
1030 entertainment_response (GtkWidget *widget, int response, void *data)
1031 {
1032   if (response == GTK_RESPONSE_CANCEL)
1033     cancel_entertainment ();
1034 }
1035
1036 static gboolean
1037 entertainment_delete (GtkDialog *dialog, GdkEventAny *event, gpointer data)
1038 {
1039   return TRUE;
1040 }
1041
1042 void
1043 entertainment_insensitive_press (GtkWidget *widget, gpointer unused)
1044 {
1045   irritate_user (_("ai_ib_unable_cancel"));
1046 }
1047
1048 void
1049 start_entertaining_user_silently ()
1050 {
1051   entertainment.depth++;
1052 }
1053
1054 static entertainment_game default_entertainment_game = {
1055   -1, 1.0
1056 };
1057
1058 void
1059 start_entertaining_user (gboolean with_button)
1060 {
1061   entertainment.depth++;
1062
1063   if (entertainment.games == NULL)
1064     set_entertainment_games (1, &default_entertainment_game);
1065
1066   if (entertainment.dialog == NULL)
1067     {
1068       GtkWidget *box;
1069
1070       /* Build a custom dialog with two labels for main title and
1071          subtitle (only in red-pill mode), a progress bar and a
1072          'Cancel' button, and save references to all the needed
1073          widgets in the entertainment_data struct */
1074
1075       /* Create the dialog */
1076       entertainment.was_cancelled = false;
1077       entertainment.was_broke = false;
1078       entertainment.dialog = gtk_dialog_new ();
1079       gtk_window_set_modal (GTK_WINDOW (entertainment.dialog), TRUE);
1080       gtk_window_set_decorated (GTK_WINDOW (entertainment.dialog), FALSE);
1081       gtk_dialog_set_has_separator (GTK_DIALOG (entertainment.dialog), FALSE);
1082       gtk_window_set_position (GTK_WINDOW (entertainment.dialog),
1083                                GTK_WIN_POS_CENTER_ON_PARENT);
1084
1085       /* Add the internal box */
1086       box = gtk_vbox_new (FALSE, HILDON_MARGIN_DOUBLE);
1087       gtk_container_add
1088         (GTK_CONTAINER (GTK_DIALOG (entertainment.dialog)->vbox), box);
1089
1090       /* Add the progress bar */
1091       entertainment.bar = gtk_progress_bar_new ();
1092       gtk_progress_bar_set_text (GTK_PROGRESS_BAR (entertainment.bar),
1093                                  entertainment.main_title);
1094       gtk_progress_bar_set_ellipsize (GTK_PROGRESS_BAR (entertainment.bar),
1095                                       PANGO_ELLIPSIZE_END);
1096       g_object_set (G_OBJECT (entertainment.bar), "text-xalign", 0.5, NULL);
1097       gtk_box_pack_start (GTK_BOX (box), entertainment.bar, FALSE, FALSE, 0);
1098
1099       gtk_dialog_set_default_response (GTK_DIALOG (entertainment.dialog),
1100                                        GTK_RESPONSE_CANCEL);
1101
1102       entertainment.cancel_button = NULL;
1103       if (with_button)
1104         {
1105           /* Cancel button: add to action area and set default response */
1106           entertainment.cancel_button =
1107             gtk_dialog_add_button (GTK_DIALOG (entertainment.dialog),
1108                                    dgettext ("hildon-libs", "wdgt_bd_stop"),
1109                                    GTK_RESPONSE_CANCEL);
1110
1111           g_signal_connect (entertainment.cancel_button,
1112                             "insensitive-press",
1113                             G_CALLBACK (entertainment_insensitive_press),
1114                             &entertainment);
1115
1116           gtk_widget_show (entertainment.cancel_button);
1117           gtk_widget_set_no_show_all (entertainment.cancel_button, FALSE);
1118
1119           gtk_widget_show (GTK_DIALOG (entertainment.dialog)->action_area);
1120         }
1121       else
1122         gtk_widget_hide (GTK_DIALOG (entertainment.dialog)->action_area);
1123
1124       /* Connect signals */
1125       g_signal_connect (entertainment.dialog, "delete-event",
1126                         G_CALLBACK (entertainment_delete), NULL);
1127
1128       g_signal_connect (entertainment.dialog, "realize",
1129                     G_CALLBACK (progressbar_dialog_realized), NULL);
1130
1131       g_signal_connect(G_OBJECT(entertainment.dialog), "focus-out-event",
1132                        G_CALLBACK(entertainment_focus_out_event_cb), NULL);
1133
1134       g_signal_connect(G_OBJECT(entertainment.dialog), "focus-in-event",
1135                        G_CALLBACK(entertainment_focus_in_event_cb), NULL);
1136
1137       g_signal_connect (entertainment.dialog, "response",
1138                         G_CALLBACK (entertainment_response), &entertainment);
1139
1140       respond_on_escape (GTK_DIALOG (entertainment.dialog),
1141                          GTK_RESPONSE_CANCEL);
1142
1143       /* Update info */
1144       entertainment_update_progress ();
1145
1146       if (with_button)
1147         entertainment_update_cancel ();
1148
1149       /* Show the dialog */
1150       push_dialog (entertainment.dialog);
1151       gtk_widget_show_all (entertainment.dialog);
1152     }
1153 }
1154
1155 void
1156 stop_entertaining_user ()
1157 {
1158   entertainment.depth--;
1159
1160   if (entertainment.depth == 0
1161       && entertainment.dialog != NULL)
1162     {
1163       entertainment_stop_pulsing ();
1164
1165       pop_dialog (entertainment.dialog);
1166       gtk_widget_destroy (entertainment.dialog);
1167
1168       entertainment.dialog = NULL;
1169       entertainment.bar = NULL;
1170       entertainment.cancel_button = NULL;
1171
1172       entertainment.cancel_callback = NULL;
1173       entertainment.cancel_data = NULL;
1174
1175       set_entertainment_games (1, &default_entertainment_game);
1176     }
1177 }
1178
1179 void
1180 cancel_entertainment ()
1181 {
1182   entertainment.was_cancelled = true;
1183   if (entertainment.cancel_callback)
1184     entertainment.cancel_callback (entertainment.cancel_data);
1185 }
1186
1187 void
1188 break_entertainment ()
1189 {
1190   entertainment.was_broke = true;
1191   cancel_entertainment ();
1192 }
1193
1194 void
1195 set_entertainment_main_title (const char *main_title, bool strong)
1196 {
1197   /* Free memory if needed */
1198   if (entertainment.main_title)
1199     {
1200       g_free (entertainment.main_title);
1201       entertainment.main_title = NULL;
1202     }
1203
1204   entertainment.strong_main_title = strong;
1205   entertainment.main_title = g_strdup (main_title);
1206   entertainment_update_title ();
1207 }
1208
1209 void
1210 set_entertainment_sub_title (const char *sub_title)
1211 {
1212   /* Free memory if needed */
1213   if (entertainment.sub_title)
1214     {
1215       g_free (entertainment.sub_title);
1216       entertainment.sub_title = NULL;
1217     }
1218
1219   entertainment.sub_title = g_strdup (sub_title);
1220   entertainment_update_title ();
1221 }
1222 void
1223 set_entertainment_games (int n_games, entertainment_game *games)
1224 {
1225   entertainment.n_games = n_games;
1226   entertainment.games = games;
1227   entertainment.current_game = 0;
1228   entertainment.completed_fraction = 0.0;
1229 }
1230
1231 void
1232 set_entertainment_fun (const char *sub_title,
1233                        int game, int64_t already, int64_t total)
1234 {
1235   if (game != -1
1236       && entertainment.games
1237       && entertainment.games[entertainment.current_game].id != -1
1238       && entertainment.games[entertainment.current_game].id != game)
1239     {
1240       int next_game;
1241
1242       for (next_game = entertainment.current_game + 1;
1243            next_game < entertainment.n_games; next_game++)
1244         {
1245           if (entertainment.games[next_game].id == -1
1246               || entertainment.games[next_game].id == game)
1247             break;
1248         }
1249
1250       if (next_game < entertainment.n_games)
1251         {
1252           entertainment.completed_fraction +=
1253             entertainment.games[entertainment.current_game].fraction;
1254           entertainment.current_game = next_game;
1255         }
1256     }
1257
1258   if ((sub_title &&
1259        (entertainment.sub_title == NULL ||
1260         strcmp (entertainment.sub_title, sub_title))) ||
1261       (sub_title == NULL && entertainment.sub_title != NULL))
1262     {
1263       /* Set the subtitle */
1264       set_entertainment_sub_title (sub_title);
1265     }
1266
1267   entertainment.already = already;
1268   entertainment.total = total;
1269   entertainment_update_progress ();
1270 }
1271
1272 void
1273 set_entertainment_download_fun (int game, int64_t already, int64_t total)
1274 {
1275   static char *sub_title = NULL;
1276   static int64_t last_total = 0;
1277
1278   if (total != last_total || sub_title == NULL)
1279     {
1280       char size_buf[20];
1281       size_string_detailed (size_buf, 20, total);
1282       g_free (sub_title);
1283       sub_title = g_strdup_printf (_("ai_nw_downloading"), size_buf);
1284     }
1285
1286   set_entertainment_fun (sub_title, game, already, total);
1287 }
1288
1289 void
1290 set_entertainment_cancel (void (*callback) (void *data),
1291                           void *data)
1292 {
1293   entertainment.cancel_callback = callback;
1294   entertainment.cancel_data = data;
1295   entertainment_update_cancel ();
1296 }
1297
1298 void
1299 set_entertainment_system_modal (void)
1300 {
1301   if (entertainment.dialog != NULL)
1302     {
1303       gtk_window_set_transient_for (GTK_WINDOW (entertainment.dialog),
1304                                     NULL);
1305
1306       /* Force the transient hint to be applied */
1307       gtk_widget_hide (entertainment.dialog);
1308       gtk_widget_show (entertainment.dialog);
1309     }
1310 }
1311
1312 bool
1313 entertainment_was_cancelled ()
1314 {
1315   return entertainment.was_cancelled;
1316 }
1317
1318 bool
1319 entertainment_was_broke ()
1320 {
1321   return entertainment.was_broke;
1322 }
1323
1324
1325 /* Progress banners
1326  */
1327
1328 static int updating_level = 0;
1329 static bool allow_updating_banner = true;
1330
1331 static GtkWindow*
1332 get_topmost_window ()
1333 {
1334   if (dialog_stack && dialog_stack->data && dialog_stack->data != entertainment.dialog)
1335     return GTK_WINDOW (dialog_stack->data);
1336   else
1337     return get_main_window ();
1338 }
1339
1340 static void
1341 refresh_updating_banner ()
1342 {
1343   static GtkWindow *win = NULL;
1344   bool show_it = (updating_level > 0 && allow_updating_banner);
1345
1346   if (show_it && win == NULL)
1347     {
1348       win = get_topmost_window ();
1349       hildon_gtk_window_set_progress_indicator (win, 1);
1350     }
1351
1352   if (!show_it && win != NULL)
1353     {
1354       hildon_gtk_window_set_progress_indicator (win, 0);
1355       win = NULL;
1356     }
1357 }
1358
1359 static gboolean
1360 updating_timeout (gpointer unused)
1361 {
1362   updating_level++;
1363   refresh_updating_banner ();
1364   return FALSE;
1365 }
1366
1367 void
1368 show_updating ()
1369 {
1370   // We must never cancel this timeout since otherwise the
1371   // UPDATING_LEVEL will get out of sync.
1372   gtk_timeout_add (2000, updating_timeout, NULL);
1373 }
1374
1375 void
1376 hide_updating ()
1377 {
1378   updating_level--;
1379   refresh_updating_banner ();
1380 }
1381
1382 void
1383 allow_updating ()
1384 {
1385   allow_updating_banner = true;
1386   refresh_updating_banner ();
1387 }
1388
1389 void
1390 prevent_updating ()
1391 {
1392   allow_updating_banner = false;
1393   refresh_updating_banner ();
1394 }
1395
1396 static gboolean
1397 no_button_events (GtkWidget *widget, GdkEventButton *event, gpointer data)
1398 {
1399   g_signal_stop_emission_by_name (widget, "button-press-event");
1400   return FALSE;
1401 }
1402
1403 GtkWidget *
1404 make_small_text_view (const char *text)
1405 {
1406   GtkWidget *scroll;
1407   GtkWidget *view;
1408   GtkTextBuffer *buffer;
1409
1410   scroll = GTK_WIDGET (g_object_new
1411                        (HILDON_TYPE_PANNABLE_AREA,
1412                         "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
1413                         "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
1414                         "mov-mode", HILDON_MOVEMENT_MODE_BOTH,
1415                         NULL));
1416
1417   view = gtk_text_view_new ();
1418   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
1419
1420   if (text)
1421     gtk_text_buffer_set_text (buffer, text, -1);
1422
1423   gtk_text_view_set_editable (GTK_TEXT_VIEW (view), 0);
1424   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view), 0);
1425   g_signal_connect (view, "button-press-event",
1426                     G_CALLBACK (no_button_events), NULL);
1427   gtk_container_add (GTK_CONTAINER (scroll), view);
1428   gtk_widget_modify_font (view, get_small_font (view));
1429
1430   return scroll;
1431 }
1432
1433 void
1434 set_small_text_view_text (GtkWidget *scroll, const char *text)
1435 {
1436   GtkWidget *view = gtk_bin_get_child (GTK_BIN (scroll));
1437   GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
1438   gtk_text_buffer_set_text (buffer, text, -1);
1439 }
1440
1441 GtkWidget *
1442 make_small_label (const char *text)
1443 {
1444   GtkWidget *label = gtk_label_new (text);
1445   gtk_widget_modify_font (label, get_small_font (label));
1446   return label;
1447 }
1448
1449 static GtkTreeModelFilter *global_tree_model_filter = NULL;
1450 static GtkListStore *global_list_store = NULL;
1451 static bool global_installed;
1452
1453 static bool global_icons_initialized = false;
1454 static GdkPixbuf *default_icon = NULL;
1455 static GdkPixbuf *broken_icon = NULL;
1456
1457 static void
1458 emit_row_changed (GtkTreeModel *model, GtkTreeIter *iter)
1459 {
1460   GtkTreePath *path;
1461
1462   path = gtk_tree_model_get_path (model, iter);
1463   g_signal_emit_by_name (model, "row-changed", path, iter);
1464
1465   gtk_tree_path_free (path);
1466 }
1467
1468 static void
1469 size_string_general_or_empty (char *buf, size_t n, int64_t bytes)
1470 {
1471   if (bytes == 0)
1472     buf[0] = '\0';
1473   else
1474     size_string_general (buf, n, bytes);
1475 }
1476
1477 static void
1478 package_icon_func (GtkTreeViewColumn *column,
1479                    GtkCellRenderer *cell,
1480                    GtkTreeModel *model,
1481                    GtkTreeIter *iter,
1482                    gpointer data)
1483 {
1484   package_info *pi;
1485
1486   gtk_tree_model_get (model, iter, 0, &pi, -1);
1487   if (!pi)
1488     return;
1489
1490   if (!global_icons_initialized)
1491     {
1492       GtkIconTheme *icon_theme = gtk_icon_theme_get_default ();
1493
1494       default_icon = gtk_icon_theme_load_icon (icon_theme,
1495                                                "tasklaunch_default_application",
1496                                                TREE_VIEW_ICON_SIZE,
1497                                                GtkIconLookupFlags (0),
1498                                                NULL);
1499
1500       broken_icon = gtk_icon_theme_load_icon (icon_theme,
1501                                               "app_install_broken_application",
1502                                               TREE_VIEW_ICON_SIZE,
1503                                               GtkIconLookupFlags (0),
1504                                               NULL);
1505
1506       global_icons_initialized = true;
1507     }
1508
1509   GdkPixbuf *icon;
1510   if (pi->broken)
1511     icon = broken_icon;
1512   else
1513     {
1514       if (global_installed)
1515         icon = pi->installed_icon;
1516       else
1517         icon = pi->available_icon;
1518     }
1519
1520   g_object_set (cell,
1521                 "pixbuf", icon ? icon : default_icon,
1522                 NULL);
1523 }
1524
1525 static void
1526 package_info_func (GtkTreeViewColumn *column,
1527                    GtkCellRenderer *cell,
1528                    GtkTreeModel *model,
1529                    GtkTreeIter *iter,
1530                    gpointer data)
1531 {
1532   package_info *pi;
1533   const gchar *package_name = NULL;
1534   const gchar *package_version = NULL;
1535   const gchar *package_description = NULL;
1536   gchar buf[20];
1537
1538   gtk_tree_model_get (model, iter, 0, &pi, -1);
1539   if (!pi)
1540     return;
1541
1542   package_name = pi->get_display_name (global_installed);
1543   package_version = pi->get_display_version (global_installed);
1544
1545   if (global_installed)
1546     size_string_general_or_empty (buf, 20, pi->installed_size);
1547   else if (pi->have_info)
1548     size_string_general_or_empty (buf, 20, pi->info.download_size);
1549   else
1550     strcpy (buf, "-");
1551
1552   if (global_installed)
1553     package_description = pi->installed_short_description;
1554   else
1555     {
1556       package_description = pi->available_short_description;
1557       if (package_description == NULL)
1558         package_description = pi->installed_short_description;
1559     }
1560
1561   g_object_set (cell,
1562                 "package-name", package_name,
1563                 "package-version", package_version,
1564                 "package-description", package_description,
1565                 "package-size", buf,
1566                 NULL);
1567 }
1568
1569 static GtkTreePath *global_target_path = NULL;
1570 static package_info_callback *global_selection_callback;
1571 static package_info *current_package_info = NULL;
1572
1573 void
1574 reset_global_target_path ()
1575 {
1576   if (global_target_path)
1577     gtk_tree_path_free (global_target_path);
1578   global_target_path = NULL;
1579 }
1580
1581 static package_info_callback *global_activation_callback;
1582
1583 static void
1584 global_row_activated (GtkTreeView *treeview,
1585                       GtkTreePath *path,
1586                       GtkTreeViewColumn *column,
1587                       gpointer data)
1588 {
1589   GtkTreeModel *model = gtk_tree_view_get_model (treeview);
1590   GtkTreeIter iter;
1591
1592   assert (model == GTK_TREE_MODEL (global_tree_model_filter));
1593
1594   if (global_activation_callback &&
1595       gtk_tree_model_get_iter (model, &iter, path))
1596     {
1597       package_info *pi;
1598
1599       /* Save global GtkTreePath */
1600       if (global_target_path != NULL)
1601         reset_global_target_path ();
1602       global_target_path = gtk_tree_model_get_path (model, &iter);
1603
1604       /* Better save path for the previous element, if present */
1605       gtk_tree_path_prev (global_target_path);
1606
1607       gtk_tree_model_get (model, &iter, 0, &pi, -1);
1608       if (pi)
1609         global_activation_callback (pi);
1610     }
1611 }
1612
1613 static void set_global_package_list (GList *packages,
1614                                      bool installed,
1615                                      package_info_callback *selected,
1616                                      package_info_callback *activated);
1617
1618 static GList *global_packages = NULL;
1619
1620 static gboolean
1621 global_package_list_key_pressed (GtkWidget * widget,
1622                                  GdkEventKey * event)
1623 {
1624   GtkTreePath *cursor_path = NULL;
1625   GtkTreeIter iter;
1626   bool result = FALSE;
1627
1628   switch (event->keyval)
1629     {
1630     case HILDON_HARDKEY_LEFT:
1631       return TRUE;
1632       break;
1633     case HILDON_HARDKEY_RIGHT:
1634       return TRUE;
1635       break;
1636     case HILDON_HARDKEY_UP:
1637       // we set the focus to the last button of the main_trail
1638 //       gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &cursor_path, NULL);
1639
1640 //       if (cursor_path)
1641 //         {
1642 //           if (!gtk_tree_path_prev (cursor_path))
1643 //             {
1644 //               GList *children = NULL;
1645
1646 //               children =
1647 //                 gtk_container_get_children (GTK_CONTAINER (get_main_trail()));
1648
1649 //               if (children)
1650 //                 {
1651 //                   GList *last_child = g_list_last (children);
1652
1653 //                   while (last_child &&
1654 //                          ((!GTK_WIDGET_CAN_FOCUS (last_child->data)) ||
1655 //                          (!GTK_WIDGET_IS_SENSITIVE (last_child->data))))
1656 //                     last_child = g_list_previous (last_child);
1657
1658 //                   if (last_child)
1659 //                     gtk_widget_grab_focus (GTK_WIDGET (last_child->data));
1660
1661 //                   g_list_free (children);
1662 //                result = TRUE;
1663 //                 }
1664 //             }
1665
1666 //           gtk_tree_path_free(cursor_path);
1667 //         }
1668
1669       break;
1670     case HILDON_HARDKEY_DOWN:
1671       /* Avoid to jump to the trail when pressing down while in the last item */
1672       gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &cursor_path, NULL);
1673       if (cursor_path)
1674         {
1675           gtk_tree_model_get_iter (GTK_TREE_MODEL (global_tree_model_filter),
1676                                    &iter, cursor_path);
1677           result = !gtk_tree_model_iter_next (GTK_TREE_MODEL (global_tree_model_filter),
1678                                               &iter);
1679           gtk_tree_path_free(cursor_path);
1680         }
1681     }
1682
1683   return result;
1684 }
1685
1686 #if defined (TAP_AND_HOLD) && defined (MAEMO_CHANGES)
1687 static gboolean
1688 button_press_cb (GtkWidget *treeview, GdkEventButton *event, gpointer data)
1689 {
1690   g_return_val_if_fail (treeview != NULL && GTK_IS_TREE_VIEW (treeview), FALSE);
1691
1692   if (event->window == gtk_tree_view_get_bin_window (GTK_TREE_VIEW (treeview)))
1693     {
1694       GtkTreeModel *model = NULL;
1695
1696       model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
1697       if (model)
1698         {
1699           GtkTreePath *tp = NULL;
1700
1701           gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
1702                                          event->x,
1703                                          event->y,
1704                                          &tp, NULL, NULL, NULL);
1705
1706           if (tp)
1707             {
1708               GtkTreeIter itr;
1709
1710               if (gtk_tree_model_get_iter (model, &itr, tp))
1711                 {
1712                   package_info *pi = NULL;
1713
1714                   gtk_tree_model_get (model, &itr, 0, &pi, -1);
1715
1716                   /* Update the global variable */
1717                   current_package_info = pi;
1718
1719                   /* Use the callback, if present */
1720                   if (global_selection_callback)
1721                     global_selection_callback (pi);
1722                 }
1723
1724               gtk_tree_path_free (tp);
1725             }
1726         }
1727     }
1728
1729   return FALSE;
1730 }
1731 #endif /* TAP_AND_HOLD && MAEMO_CHANGES */
1732
1733 #if HILDON_CHECK_VERSION (2,2,4)
1734
1735 static gboolean
1736 live_search_filter_func (GtkTreeModel *model,
1737                          GtkTreeIter  *iter,
1738                          gchar        *text,
1739                          gpointer      data)
1740 {
1741     package_info *pi = NULL;
1742     gchar *needle = NULL;
1743     gchar *name = NULL;
1744     gboolean retvalue = FALSE;
1745     GtkWidget *live = GTK_WIDGET (data);
1746
1747     if (global_packages == NULL)
1748       return FALSE;
1749
1750     /* Get package info */
1751     gtk_tree_model_get (model, iter, 0, &pi, -1);
1752
1753     /* Row could be empty - must check */
1754     if (pi == NULL)
1755       {
1756         /* Must ensure consitent state if row is empty */
1757         reset_global_target_path ();
1758         gtk_widget_hide (live);
1759         return FALSE;
1760       }
1761
1762     /* Casefold strings for ease of comparison */
1763     needle = g_utf8_casefold (text, -1);
1764     name = g_utf8_casefold (pi->get_display_name(global_installed), -1);
1765
1766     /* Search by name (case insensitive) */
1767     retvalue = g_str_has_prefix (name, needle);
1768
1769     /* Free */
1770     g_free (name);
1771     g_free (needle);
1772
1773     return retvalue;
1774 }
1775
1776 #endif
1777
1778 GtkWidget *
1779 make_global_package_list (GtkWidget *window,
1780                           GList *packages,
1781                           bool installed,
1782                           const char *empty_label,
1783                           const char *op_label,
1784                           package_info_callback *selected,
1785                           package_info_callback *activated)
1786 {
1787   GtkCellRenderer *renderer;
1788   GtkTreeViewColumn *column;
1789   GtkWidget *tree, *scroller, *alignment, *vbox;
1790
1791 #if HILDON_CHECK_VERSION (2,2,4)
1792   GtkWidget *live;
1793 #endif
1794
1795 #if defined (TAP_AND_HOLD) && defined (MAEMO_CHANGES)
1796   GtkWidget *menu = NULL;
1797 #endif /* TAP_AND_HOLD && MAEMO_CHANGES */
1798
1799   if (packages == NULL)
1800     {
1801       GtkWidget *label = gtk_label_new (empty_label);
1802       hildon_helper_set_logical_font (label, "LargeSystemFont");
1803       hildon_helper_set_logical_color (label, GTK_RC_FG, GTK_STATE_NORMAL,
1804                                        "SecondaryTextColor");
1805       gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
1806       return label;
1807     }
1808
1809   /* Just create a new model for the first time */
1810   if (global_list_store == NULL)
1811     global_list_store = gtk_list_store_new (1, G_TYPE_POINTER);
1812
1813   if (global_tree_model_filter != NULL)
1814     g_object_unref (global_tree_model_filter);
1815
1816   /* Create a tree model filter with the actual model inside */
1817   global_tree_model_filter =
1818     GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (global_list_store), NULL));
1819
1820   /* Insert the filter into the treeview */
1821   tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (global_tree_model_filter));
1822
1823   column = gtk_tree_view_column_new ();
1824
1825   renderer = gtk_cell_renderer_pixbuf_new ();
1826   gtk_tree_view_column_pack_start(column, renderer, FALSE);
1827   gtk_tree_view_column_set_cell_data_func (column, renderer,
1828                                            package_icon_func, NULL, NULL);
1829
1830   renderer = package_info_cell_renderer_new ();
1831   package_info_cell_renderer_listen_style (PACKAGE_INFO_CELL_RENDERER(renderer),
1832                                            tree);
1833   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1834   gtk_tree_view_column_set_cell_data_func (column, renderer,
1835                                            package_info_func, NULL, NULL);
1836
1837   gtk_tree_view_insert_column (GTK_TREE_VIEW (tree), column, -1);
1838
1839   scroller = hildon_pannable_area_new();
1840   gtk_container_add (GTK_CONTAINER (scroller), tree);
1841
1842   alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
1843   gtk_alignment_set_padding (GTK_ALIGNMENT (alignment),
1844                              HILDON_MARGIN_HALF,
1845                              0,
1846                              HILDON_MARGIN_DOUBLE,
1847                              HILDON_MARGIN_DOUBLE);
1848   gtk_container_add (GTK_CONTAINER (alignment), scroller);
1849
1850   g_signal_connect (tree, "row-activated",
1851                     G_CALLBACK (global_row_activated), NULL);
1852
1853   g_signal_connect (tree, "key-press-event",
1854                     G_CALLBACK (global_package_list_key_pressed), NULL);
1855
1856 #if defined (TAP_AND_HOLD) && defined (MAEMO_CHANGES)
1857   /* Create the contextual menu */
1858   menu = create_package_menu (op_label);
1859
1860   gtk_widget_show_all (menu);
1861
1862   g_signal_connect (tree, "button-press-event",
1863                     G_CALLBACK (button_press_cb), NULL);
1864
1865   gtk_widget_tap_and_hold_setup (tree, menu, NULL,
1866                                  GtkWidgetTapAndHoldFlags (0));
1867 #endif /* TAP_AND_HOLD && MAEMO_CHANGES */
1868
1869   set_global_package_list (packages, installed, selected, activated);
1870
1871   grab_focus_on_map (tree);
1872
1873   /* Scroll to desired cell, if needed */
1874   if (global_target_path != NULL)
1875     gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree),
1876                                   global_target_path,
1877                                   NULL, FALSE, 0, 0);
1878
1879 #if HILDON_CHECK_VERSION (2,2,4)
1880   /* Live search */
1881   live = hildon_live_search_new ();
1882   hildon_live_search_set_filter (HILDON_LIVE_SEARCH (live),
1883                                  global_tree_model_filter);
1884   hildon_live_search_set_visible_func (HILDON_LIVE_SEARCH (live),
1885                                        live_search_filter_func, live, NULL);
1886   hildon_live_search_widget_hook (HILDON_LIVE_SEARCH (live),
1887                                   window, GTK_TREE_VIEW (tree));
1888 #endif
1889
1890   /* Pack the packages list and the live search widget toghether */
1891   vbox = gtk_vbox_new (FALSE, 0);
1892   gtk_box_pack_start (GTK_BOX (vbox), alignment, TRUE, TRUE, 0);
1893
1894 #if HILDON_CHECK_VERSION (2,2,4)
1895   gtk_box_pack_start (GTK_BOX (vbox), live, FALSE, FALSE, 0);
1896 #endif
1897
1898   /* Prepare visibility */
1899   gtk_widget_show_all (alignment);
1900
1901 #if HILDON_CHECK_VERSION (2,2,4)
1902   gtk_widget_hide (live);
1903 #endif
1904
1905   gtk_widget_hide (vbox);
1906
1907   return vbox;
1908 }
1909
1910 /*
1911  * This function check if the package is in section "user/hidden"
1912  */
1913 bool
1914 package_is_hidden (package_info *pi)
1915 {
1916   if (red_pill_mode && red_pill_show_all)
1917     return false;
1918
1919   if (!pi->available_section)
1920     return false;
1921
1922   const char *hidden = "user/hidden";
1923   const char *section = pi->available_section;
1924
1925   size_t len = strlen (hidden);
1926   return strlen (section) == len && !strncmp (section, hidden, len);
1927 }
1928
1929 static void
1930 set_global_package_list (GList *packages,
1931                          bool installed,
1932                          package_info_callback *selected,
1933                          package_info_callback *activated)
1934 {
1935   if (global_list_store)
1936     {
1937       /* @FIXME:
1938          Gotta set entries to NULL first, because some bug in maemo-gtk/hildon
1939          is causing calls to cat_icon_func/cat_text_func with garbage data
1940       */
1941       GtkTreeIter itr;
1942       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (global_list_store), &itr))
1943         do
1944           {
1945             gtk_list_store_set(global_list_store, &itr, 0, NULL, -1);
1946           } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (global_list_store), &itr));
1947       gtk_list_store_clear (global_list_store);
1948     }
1949
1950   for (GList *p = global_packages; p; p = p->next)
1951     {
1952       package_info *pi = (package_info *)p->data;
1953       pi->model = NULL;
1954     }
1955
1956   global_installed = installed;
1957   global_selection_callback = selected;
1958   global_activation_callback = activated;
1959   global_packages = packages;
1960
1961   int pos = 0;
1962   for (GList *p = global_packages; p; p = p->next)
1963     {
1964       package_info *pi = (package_info *)p->data;
1965
1966       /* don't insert the package if it isn't installed
1967        * and it's in the section "user/hidden"
1968        */
1969       if (!pi->installed_version && package_is_hidden (pi))
1970         continue;
1971
1972       pi->model = GTK_TREE_MODEL (global_list_store);
1973       gtk_list_store_insert_with_values (global_list_store, &pi->iter,
1974                                          pos,
1975                                          0, pi,
1976                                          -1);
1977       pos++;
1978     }
1979 }
1980
1981 void
1982 clear_global_package_list ()
1983 {
1984   set_global_package_list (NULL, false, NULL, NULL);
1985 }
1986
1987 void
1988 global_package_info_changed (package_info *pi)
1989 {
1990   if (pi->model)
1991     emit_row_changed (pi->model, &pi->iter);
1992 }
1993
1994 static GtkWidget *global_section_list = NULL;
1995 static section_activated *global_section_activated;
1996
1997 static void
1998 section_clicked (GtkWidget *widget, gpointer data)
1999 {
2000   if (!GTK_IS_WIDGET (widget) || (data == NULL))
2001     return;
2002
2003   section_info *si = (section_info *)data;
2004   if (global_section_activated)
2005     global_section_activated (si);
2006 }
2007
2008 enum {
2009   SECTION_LS_TEXT_COLUMN,
2010   SECTION_LS_PIXBUF_COLUMN,
2011   SECTION_LS_SI_COLUMN,
2012   SECTION_LS_N_COLUMNS
2013 };
2014
2015 static void
2016 icon_view_item_activated (GtkWidget *icon_view,
2017                           GtkTreePath *tp,
2018                           GtkTreeModel *tm)
2019 {
2020   GtkTreeIter itr;
2021
2022   if (gtk_tree_model_get_iter (tm, &itr, tp))
2023     {
2024       section_info *si = NULL;
2025
2026       gtk_tree_model_get (tm, &itr, SECTION_LS_SI_COLUMN, &si, -1);
2027
2028       section_clicked (icon_view, si);
2029     }
2030 }
2031
2032 static void
2033 icon_view_is_dying (GtkTreeModel *tm, GtkIconView *stale_pointer)
2034 {
2035   section_info *si;
2036   GtkTreeIter itr;
2037
2038   if (gtk_tree_model_get_iter_first (tm, &itr))
2039     do
2040       {
2041         gtk_tree_model_get (tm, &itr, SECTION_LS_SI_COLUMN, &si, -1);
2042         si->unref ();
2043       }
2044     while (gtk_tree_model_iter_next (tm, &itr));
2045
2046   g_object_unref (G_OBJECT (tm));
2047 }
2048
2049 static void
2050 set_text_cr_style (GtkWidget *widget, GtkStyle *prev_style, GObject *cr_text)
2051 {
2052   GtkStyle *style = NULL;
2053
2054   style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget),
2055                                      "SmallSystemFont",
2056                                      NULL,
2057                                      G_TYPE_NONE);
2058   if (style)
2059     {
2060       PangoAttrList *attr_list = pango_attr_list_new ();
2061
2062       if (attr_list)
2063         {
2064           PangoAttribute *attr =
2065             pango_attr_font_desc_new (
2066               pango_font_description_copy (style->font_desc));
2067
2068           pango_attr_list_insert (attr_list, attr);
2069           g_object_set (cr_text, "attributes", attr_list, NULL);
2070         }
2071     }
2072 }
2073
2074 #define ICONS_GRID_ITEM_WIDTH HILDON_ICON_PIXEL_SIZE_XLARGE \
2075   + HILDON_MARGIN_DEFAULT + HILDON_MARGIN_HALF
2076
2077 static GtkWidget *
2078 make_my_icon_view (GtkTreeModel *tm)
2079 {
2080   GList *ls_cr = NULL, *itr = NULL;
2081   GtkCellRenderer *cr_text = NULL;
2082   GtkWidget *icon_view = GTK_WIDGET (g_object_new (
2083                                     GTK_TYPE_ICON_VIEW,
2084                                     "model", tm,
2085                                     "text-column",    SECTION_LS_TEXT_COLUMN,
2086                                     "pixbuf-column",  SECTION_LS_PIXBUF_COLUMN,
2087                                     "column-spacing", HILDON_MARGIN_DOUBLE,
2088                                     "item-width", ICONS_GRID_ITEM_WIDTH,
2089                                     "row-spacing",    HILDON_MARGIN_DOUBLE,
2090                                     NULL));
2091
2092   for (ls_cr = itr = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (icon_view));
2093        itr;
2094        itr = itr->next)
2095     {
2096       if (g_type_is_a (G_TYPE_FROM_INSTANCE (itr->data),
2097                        GTK_TYPE_CELL_RENDERER_TEXT))
2098         break;
2099     }
2100
2101   if (itr)
2102     cr_text = GTK_CELL_RENDERER (itr->data);
2103   g_list_free (ls_cr);
2104
2105   if (cr_text) {
2106     g_signal_connect (G_OBJECT (icon_view),
2107                       "style-set",
2108                       G_CALLBACK (set_text_cr_style),
2109                       cr_text);
2110     set_text_cr_style (icon_view, NULL, G_OBJECT(cr_text));
2111     g_object_set(G_OBJECT(cr_text),
2112                  "wrap-mode",  PANGO_WRAP_WORD,
2113                  "wrap-width", ICONS_GRID_ITEM_WIDTH,
2114                  NULL);
2115   }
2116
2117   return icon_view;
2118 }
2119
2120 #define SECTION_ICON_PATTERN "/etc/hildon/theme/backgrounds/app_install_%s.png"
2121
2122 static GdkPixbuf *
2123 pixbuf_from_si(section_info *si)
2124 {
2125   GdkPixbuf *pb = NULL;
2126   char *icon_fname;
2127
2128   /*
2129    * Assumptions about section_info:
2130    * 1. Non-NULL canonical name and rank of 2 == "other"
2131    * 2. NULL canonical name == "all"
2132    * 3. Non-NULL canonical name is a valid pre-defined section
2133    */
2134
2135   icon_fname = g_strdup_printf (SECTION_ICON_PATTERN,
2136                                 si->untranslated_name
2137                                   ? 2 == si->rank
2138                                     ? "other"
2139                                     : si->untranslated_name
2140                                   : "all");
2141   pb = gdk_pixbuf_new_from_file (icon_fname, NULL);
2142
2143   if (!pb)
2144     {
2145       /*
2146        * If all else fails, try "other"
2147        * This should never happen
2148        */
2149       g_free(icon_fname);
2150       icon_fname = g_strdup_printf(SECTION_ICON_PATTERN, "other");
2151       pb = gdk_pixbuf_new_from_file (icon_fname, NULL);
2152     }
2153
2154   g_free (icon_fname);
2155
2156   return pb;
2157 }
2158
2159 GtkWidget *
2160 make_global_section_list (GList *sections, section_activated *act)
2161 {
2162   global_section_activated = act;
2163
2164   if (sections == NULL)
2165     {
2166       GtkWidget *label = gtk_label_new (_("ai_li_no_applications_available"));
2167       hildon_helper_set_logical_font (label, "LargeSystemFont");
2168       hildon_helper_set_logical_color (label, GTK_RC_FG, GTK_STATE_NORMAL,
2169                                        "SecondaryTextColor");
2170       gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
2171       return label;
2172     }
2173
2174   GtkWidget *scroller;
2175   GtkListStore *ls = NULL;
2176   GtkTreeIter itr;
2177   GtkWidget *icon_view;
2178
2179   scroller = hildon_pannable_area_new ();
2180   g_object_set (G_OBJECT (scroller), "vovershoot-max", 0, NULL);
2181
2182   ls = gtk_list_store_new (SECTION_LS_N_COLUMNS,
2183                            G_TYPE_STRING,
2184                            GDK_TYPE_PIXBUF,
2185                            G_TYPE_POINTER);
2186
2187   for (GList *s = sections; s; s = s ->next)
2188     {
2189       section_info *si = (section_info *)s->data;
2190
2191       if (si->rank == SECTION_RANK_HIDDEN)
2192         continue;
2193
2194       gtk_list_store_append (ls, &itr);
2195       gtk_list_store_set (ls, &itr,
2196                           SECTION_LS_TEXT_COLUMN,   si->name,
2197                           SECTION_LS_PIXBUF_COLUMN, pixbuf_from_si(si),
2198                           SECTION_LS_SI_COLUMN,     si,
2199                           -1);
2200
2201       si->ref ();
2202     }
2203
2204   icon_view = make_my_icon_view (GTK_TREE_MODEL (ls));
2205   g_object_weak_ref (G_OBJECT(icon_view), (GWeakNotify)icon_view_is_dying, ls);
2206   g_signal_connect (G_OBJECT (icon_view),
2207                     "item-activated",
2208                     G_CALLBACK (icon_view_item_activated),
2209                     ls);
2210   gtk_container_add (GTK_CONTAINER(scroller), icon_view);
2211
2212   global_section_list = scroller;
2213   g_object_ref (scroller);
2214
2215   /* Prepare visibility */
2216   gtk_widget_show_all (icon_view);
2217   gtk_widget_hide (scroller);
2218
2219   return scroller;
2220 }
2221
2222 void
2223 clear_global_section_list ()
2224 {
2225   if (global_section_list)
2226     g_object_unref (global_section_list);
2227
2228   global_section_list = NULL;
2229 }
2230
2231 enum {
2232   COLUMN_SP_INSTALLABLE,
2233   COLUMN_SP_SELECTED,
2234   COLUMN_SP_NAME,
2235   COLUMN_SP_SIZE,
2236   COLUMN_SP_PACKAGE_INFO,
2237   SP_N_COLUMNS
2238 };
2239
2240 static GtkListStore *
2241 make_select_package_list_store (GList *package_list, int64_t *total_size)
2242 {
2243   GtkListStore *list_store = NULL;
2244   GList * node = NULL;
2245   int64_t acc_size = 0;
2246
2247   list_store = gtk_list_store_new (SP_N_COLUMNS,
2248                                    G_TYPE_BOOLEAN,
2249                                    G_TYPE_BOOLEAN,
2250                                    G_TYPE_STRING,
2251                                    G_TYPE_STRING,
2252                                    G_TYPE_POINTER);
2253
2254   for (node = package_list; node != NULL; node = g_list_next (node))
2255     {
2256       package_info *pi = (package_info *) node->data;
2257       GtkTreeIter iter;
2258       gboolean installable = (pi->available_version != NULL) && (strlen(pi->available_version) > 0);
2259       char package_size_str[20] = "";
2260       if (installable)
2261         size_string_general (package_size_str, 20,
2262                              pi->info.install_user_size_delta);
2263       gtk_list_store_append (list_store, &iter);
2264       gtk_list_store_set (list_store, &iter,
2265                           COLUMN_SP_INSTALLABLE, installable,
2266                           COLUMN_SP_SELECTED, installable,
2267                           COLUMN_SP_NAME, pi->get_display_name (false),
2268                           COLUMN_SP_SIZE, package_size_str,
2269                           COLUMN_SP_PACKAGE_INFO, pi,
2270                           -1);
2271       acc_size += pi->info.install_user_size_delta;
2272       pi->ref ();
2273     }
2274
2275   if (total_size != NULL)
2276     *total_size = acc_size;
2277
2278   return list_store;
2279 }
2280
2281 void fill_required_space_label (GtkWidget *label,
2282                                 int64_t size)
2283 {
2284   gchar * text = NULL;
2285   gchar size_str[20];
2286
2287   size_string_general (size_str, 20, size);
2288
2289   text = g_strdup_printf (_("ai_ia_storage"), size_str);
2290   gtk_label_set_text (GTK_LABEL(label), text);
2291   g_free (text);
2292 }
2293
2294 struct upls_closure {
2295   GtkWidget *button;
2296   GtkWidget *required_space_label;
2297 };
2298
2299 void update_packages_list_selection (GtkTreeModel *model,
2300                                      GtkTreePath *path, GtkTreeIter *i,
2301                                      gpointer data)
2302 {
2303   gboolean has_iter = FALSE;
2304   gboolean selected_packages = FALSE;
2305   int64_t acc_size = 0;
2306   GtkWidget *button;
2307   GtkWidget *label = NULL;
2308   GtkTreeIter iter;
2309   upls_closure *closure = (upls_closure *)data;
2310
2311   has_iter = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
2312
2313   button = closure->button;
2314   label = closure->required_space_label;
2315
2316   while (has_iter)
2317     {
2318       gboolean selected = FALSE;
2319       package_info *pi = NULL;
2320
2321       gtk_tree_model_get (GTK_TREE_MODEL(model), &iter,
2322                           COLUMN_SP_SELECTED, &selected,
2323                           COLUMN_SP_PACKAGE_INFO, &pi,
2324                           -1);
2325       if (selected)
2326         {
2327           selected_packages = TRUE;
2328           acc_size += pi->info.install_user_size_delta;
2329         }
2330       has_iter = gtk_tree_model_iter_next (GTK_TREE_MODEL(model), &iter);
2331     }
2332
2333   /* Set sensitiveness to the OK button and update required size label */
2334   gtk_widget_set_sensitive (button, selected_packages);
2335   fill_required_space_label (label, acc_size);
2336 }
2337
2338 struct spl_closure
2339 {
2340   GList *package_list;
2341   char *title;
2342   char *question;
2343
2344   GtkListStore *list_store;
2345   void (*cont) (gboolean res, GList *package_list, void *data);
2346   void *data;
2347 };
2348
2349 void select_package_list_response (GtkDialog *dialog,
2350                                    gint response,
2351                                    gpointer user_data)
2352 {
2353   spl_closure *closure = (spl_closure *)user_data;
2354   gboolean res = FALSE;
2355   GList *package_list = NULL;
2356   GtkTreeIter iter;
2357   gboolean has_iter = FALSE;
2358
2359   res = (response == GTK_RESPONSE_OK);
2360
2361   has_iter = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(closure->list_store), &iter);
2362   while (has_iter)
2363     {
2364       gboolean selected = FALSE;
2365       package_info *pi = NULL;
2366
2367       gtk_tree_model_get (GTK_TREE_MODEL(closure->list_store), &iter,
2368                           COLUMN_SP_SELECTED, &selected,
2369                           COLUMN_SP_PACKAGE_INFO, &pi,
2370                           -1);
2371       if (selected)
2372         package_list = g_list_prepend (package_list, pi);
2373       else
2374         pi->unref ();
2375       has_iter = gtk_tree_model_iter_next (GTK_TREE_MODEL(closure->list_store), &iter);
2376     }
2377   package_list = g_list_reverse (package_list);
2378   g_object_unref(closure->list_store);
2379
2380   pop_dialog (GTK_WIDGET (dialog));
2381   gtk_widget_destroy (GTK_WIDGET (dialog));
2382
2383   if (closure->cont)
2384     {
2385       closure->cont (res, package_list, closure->data);
2386     }
2387   else
2388     {
2389       GList *node = NULL;
2390       for (node = package_list; node != NULL; node = g_list_next (node))
2391         {
2392           if (node->data != NULL)
2393             {
2394               ((package_info *) (node->data))->unref ();
2395             }
2396         }
2397       g_list_free (package_list);
2398     }
2399
2400   delete closure;
2401 }
2402
2403 static void
2404 package_selected_activated_callback (GtkTreeView *treeview,
2405                                      GtkTreePath *path,
2406                                      GtkTreeViewColumn *col,
2407                                      gpointer unused)
2408 {
2409   GtkTreeIter iter;
2410   gboolean selected;
2411   gboolean installable;
2412
2413   gtk_tree_model_get_iter (gtk_tree_view_get_model (treeview),
2414                            &iter, path);
2415   gtk_tree_model_get (gtk_tree_view_get_model (treeview), &iter,
2416                       COLUMN_SP_SELECTED, &selected,
2417                       COLUMN_SP_INSTALLABLE, &installable,
2418                       -1);
2419
2420   if (installable)
2421     {
2422       gtk_list_store_set (GTK_LIST_STORE(gtk_tree_view_get_model (treeview)),
2423                           &iter, COLUMN_SP_SELECTED, !selected, -1);
2424     }
2425 }
2426
2427 static void
2428 select_package_list_with_info (void *data)
2429 {
2430   spl_closure *c = (spl_closure *)data;
2431
2432   GtkWidget *dialog;
2433   GtkWidget *ok_button;
2434   GtkListStore *list_store;
2435   GtkWidget *tree_view;
2436   GtkTreeViewColumn *column;
2437   GtkCellRenderer *renderer;
2438   GtkWidget *scroller;
2439   GtkWidget *message_label;
2440   GtkWidget *required_space_label;
2441   int64_t total_size;
2442   gint result;
2443   gint padding = 4;
2444
2445   /* Dialog creation: we can't use gtk_dialog_new_with_buttons ()
2446      because we need to get access to the OK GtkButton in order to
2447      connect to the 'insensitive_press' signal emmited when clicking
2448      on it while it's insensitve
2449   */
2450   dialog = gtk_dialog_new ();
2451   gtk_window_set_title (GTK_WINDOW (dialog), c->title);
2452   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
2453   gtk_dialog_add_button (GTK_DIALOG (dialog),
2454                          dgettext ("hildon-libs", "wdgt_bd_no"),
2455                          GTK_RESPONSE_CANCEL);
2456   ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
2457                                      dgettext ("hildon-libs", "wdgt_bd_yes"),
2458                                      GTK_RESPONSE_OK);
2459
2460   push_dialog (dialog);
2461
2462   gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
2463   list_store = make_select_package_list_store (c->package_list, &total_size);
2464
2465   /* Set the message dialog */
2466   message_label = make_small_label (c->question);
2467   gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE);
2468   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), message_label,
2469                       FALSE, FALSE, padding);
2470
2471   /* Add required space label */
2472   required_space_label = gtk_label_new (NULL);
2473   gtk_box_pack_end (GTK_BOX(GTK_DIALOG(dialog)->vbox), required_space_label,
2474                     FALSE, FALSE, padding);
2475   fill_required_space_label (required_space_label, total_size);
2476
2477   /* Set up treeview */
2478   tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2479   renderer = gtk_cell_renderer_toggle_new ();
2480   gtk_cell_renderer_toggle_set_radio (GTK_CELL_RENDERER_TOGGLE (renderer), FALSE);
2481   column = gtk_tree_view_column_new_with_attributes ("Marked", renderer,
2482                                                      "active", COLUMN_SP_SELECTED,
2483                                                      "activatable", COLUMN_SP_INSTALLABLE,
2484                                                      NULL);
2485   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
2486                                column);
2487   renderer = gtk_cell_renderer_text_new ();
2488   g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2489   column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
2490                                                      "text", COLUMN_SP_NAME,
2491                                                      "sensitive", COLUMN_SP_INSTALLABLE,
2492                                                      NULL);
2493   gtk_tree_view_column_set_expand(column, TRUE);
2494   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
2495                                column);
2496   renderer = gtk_cell_renderer_text_new ();
2497   column = gtk_tree_view_column_new_with_attributes ("Size", renderer,
2498                                                      "text", COLUMN_SP_SIZE,
2499                                                      "sensitive", COLUMN_SP_INSTALLABLE,
2500                                                      NULL);
2501   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view),
2502                                column);
2503
2504   /* Set up an scrolled window for the treeview */
2505   scroller = hildon_pannable_area_new ();
2506   gtk_container_add (GTK_CONTAINER (scroller), tree_view);
2507   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), scroller,
2508                       TRUE, TRUE, padding);
2509
2510   /* Add separator */
2511   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
2512                       gtk_hseparator_new (),
2513                       FALSE, FALSE, padding);
2514
2515   c->list_store = list_store;
2516
2517   /* Prepare data to be passed to the right signal handler */
2518   upls_closure *upls_data = new upls_closure;
2519   upls_data->button = ok_button;
2520   upls_data->required_space_label = required_space_label;
2521
2522   update_packages_list_selection(GTK_TREE_MODEL (c->list_store),
2523                                  NULL, NULL, upls_data);
2524
2525   /* Connect signals */
2526   g_signal_connect (tree_view, "row-activated",
2527                     G_CALLBACK (package_selected_activated_callback),
2528                     NULL);
2529
2530   g_signal_connect (list_store, "row-changed",
2531                     G_CALLBACK (update_packages_list_selection),
2532                     upls_data);
2533
2534   gtk_widget_set_size_request (dialog, -1, 350);
2535   gtk_widget_show_all (dialog);
2536
2537   /* Run dialog, waiting for a response */
2538   result = gtk_dialog_run (GTK_DIALOG (dialog));
2539   select_package_list_response (GTK_DIALOG (dialog), result, c);
2540
2541   delete upls_data;
2542 }
2543
2544 void
2545 select_package_list (GList *package_list,
2546                      const gchar *title,
2547                      const gchar *question,
2548                      void (*cont) (gboolean res, GList *pl, void *data),
2549                      void *data)
2550 {
2551   spl_closure *closure = new spl_closure;
2552
2553   closure->package_list = package_list;
2554   closure->title = g_strdup (title);
2555   closure->question = g_strdup (question);
2556   closure->cont = cont;
2557   closure->data = data;
2558
2559   get_package_infos (package_list,
2560                      true,
2561                      select_package_list_with_info,
2562                      closure);
2563 }
2564
2565 #define KILO 1024.0
2566 #define MEGA (KILO * KILO)
2567 #define GIGA (KILO * MEGA)
2568
2569 void
2570 size_string_general (char *buf, size_t n, int64_t bytes)
2571 {
2572   double num = (double) bytes;
2573
2574   if (num == 0)
2575     snprintf (buf, n, _FM ("sfil_li_size_kb"), 0);
2576   else if (num < 1 * KILO)
2577     snprintf (buf, n, _FM ("sfil_li_size_kb"), 1);
2578   else
2579     {
2580       // round to nearest KILO
2581       // bytes ~ num * KILO
2582       num = (bytes + KILO / 2.0) / KILO;
2583       if (num < 100)
2584         snprintf (buf, n, _FM ("sfil_li_size_1kb_99kb"), (int) num);
2585       else
2586         {
2587           // round to nearest 100 KILO
2588           // bytes ~ num * 100 * KILO
2589           num = (bytes + 50.0 * KILO) / (100.0 * KILO);
2590           if (num < 10)
2591             snprintf (buf, n, _FM ("sfil_li_size_100kb_1mb"),
2592                       (int) (bytes / KILO));
2593           else
2594             {
2595               // round to nearest MEGA
2596               // bytes ~ num * MEGA
2597               num = (bytes + MEGA / 2) / (MEGA);
2598               if (num < 10)
2599                 snprintf (buf, n, _FM ("sfil_li_size_1mb_10mb"), (float) num);
2600               else if (num < 1000)
2601                 snprintf (buf, n, _FM ("sfil_li_size_10mb_1gb"), (float) num);
2602               else
2603                 snprintf (buf, n, _FM ("sfil_li_size_1gb_or_greater"),
2604                           (float) 1.0 * bytes / GIGA);
2605             }
2606         }
2607     }
2608 }
2609
2610 void
2611 size_string_detailed (char *buf, size_t n, int64_t bytes)
2612 {
2613   double num = (double) bytes;
2614
2615   if (num == 0)
2616     snprintf (buf, n, _FM ("ckdg_va_properties_size_kb"), 0);
2617   else if (num < 1 * KILO)
2618     snprintf (buf, n, _FM ("ckdg_va_properties_size_kb"), 1);
2619   else
2620     {
2621       // round to nearest KILO
2622       // bytes ~ num * KILO
2623       num = (bytes + KILO / 2.0) / KILO;
2624       if (num < 100)
2625         snprintf (buf, n, _FM ("ckdg_va_properties_size_1kb_99kb"), (int) num);
2626       else
2627         {
2628           // round to nearest 100 KILO
2629           // bytes ~ num * 100 * KILO
2630           num = (bytes + 50.0 * KILO) / (100.0 * KILO);
2631           if (num < 10)
2632             snprintf (buf, n, _FM ("ckdg_va_properties_size_100kb_1mb"),
2633                       (int) (bytes / KILO));
2634           else
2635             {
2636               // round to nearest MEGA
2637               // bytes ~ num * MEGA
2638               num = (bytes + MEGA / 2) / (MEGA);
2639               if (num < 10)
2640                 snprintf (buf, n, _FM ("ckdg_va_properties_size_1mb_10mb"),
2641                           (float) num);
2642               else if (num < 1000)
2643                 snprintf (buf, n, _FM ("ckdg_va_properties_size_10mb_1gb"),
2644                           (float) num);
2645               else
2646                 snprintf (buf, n,
2647                           _FM ("ckdg_va_properties_size_1gb_or_greater"),
2648                           (float) 1.0 * bytes / GIGA);
2649             }
2650         }
2651     }
2652 }
2653
2654 struct fcd_closure {
2655   void (*cont) (char *uri, void *data);
2656   void *data;
2657 };
2658
2659 static void
2660 fcd_response (GtkDialog *dialog, gint response, gpointer clos)
2661 {
2662   fcd_closure *c = (fcd_closure *)clos;
2663   void (*cont) (char *uri, void *data) = c->cont;
2664   void *data = c->data;
2665   delete c;
2666
2667   char *uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
2668
2669   pop_dialog (GTK_WIDGET (dialog));
2670   gtk_widget_destroy (GTK_WIDGET (dialog));
2671
2672   if (response == GTK_RESPONSE_OK)
2673     {
2674       if (cont)
2675         cont (uri, data);
2676     }
2677   else
2678     {
2679       g_free (uri);
2680       if (cont)
2681         cont (NULL, data);
2682     }
2683 }
2684
2685 void
2686 show_deb_file_chooser (void (*cont) (char *uri, void *data),
2687                        void *data)
2688 {
2689   fcd_closure *c = new fcd_closure;
2690   c->cont = cont;
2691   c->data = data;
2692
2693   GtkWidget *fcd;
2694   GtkFileFilter *filter;
2695
2696   fcd = hildon_file_chooser_dialog_new_with_properties
2697     (NULL,
2698      "action",            GTK_FILE_CHOOSER_ACTION_OPEN,
2699      "title",             _("ai_ti_select_package"),
2700      "empty_text",        _("ai_ia_select_package_no_packages"),
2701      "open_button_text",  _("ai_bd_select_package"),
2702      NULL);
2703   gtk_window_set_modal (GTK_WINDOW (fcd), TRUE);
2704   push_dialog (fcd);
2705
2706   filter = gtk_file_filter_new ();
2707   gtk_file_filter_add_mime_type (filter, "application/x-deb");
2708   gtk_file_filter_add_mime_type (filter, "application/x-debian-package");
2709   gtk_file_filter_add_mime_type (filter, "application/x-install-instructions");
2710   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(fcd), filter);
2711   // XXX - gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER(fcd), TRUE);
2712
2713   g_signal_connect (fcd, "response",
2714                     G_CALLBACK (fcd_response), c);
2715
2716   gtk_widget_show_all (fcd);
2717 }
2718
2719 void
2720 show_file_chooser_for_save (const char *title,
2721                             GtkWindow *parent,
2722                             const char *default_folder,
2723                             const char *default_filename,
2724                             void (*cont) (char *uri, void *data),
2725                             void *data)
2726 {
2727   fcd_closure *c = new fcd_closure;
2728   c->cont = cont;
2729   c->data = data;
2730
2731   GtkWidget *fcd;
2732
2733   fcd = hildon_file_chooser_dialog_new_with_properties
2734     (NULL,
2735      "action",            GTK_FILE_CHOOSER_ACTION_SAVE,
2736      "title",             title,
2737      NULL);
2738   push_dialog (fcd);
2739   gtk_window_set_modal (GTK_WINDOW (fcd), TRUE);
2740
2741   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fcd), default_filename);
2742
2743   if (default_folder)
2744     gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (fcd), default_folder);
2745
2746   g_signal_connect (fcd, "response",
2747                     G_CALLBACK (fcd_response), c);
2748
2749   gtk_widget_show_all (fcd);
2750 }
2751
2752 static void
2753 b64decode (const unsigned char *str, GdkPixbufLoader *loader)
2754 {
2755   unsigned const char *cur, *start;
2756   int d, dlast, phase;
2757   unsigned char c;
2758   static int table[256] = {
2759     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
2760     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
2761     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
2762     52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
2763     -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
2764     15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
2765     -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
2766     41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
2767     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
2768     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
2769     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
2770     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
2771     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
2772     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
2773     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
2774     -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
2775   };
2776
2777   const size_t loader_size = 2048;
2778   unsigned char loader_buf[loader_size], *loader_ptr;
2779   GError *error = NULL;
2780
2781   d = dlast = phase = 0;
2782   start = str;
2783   loader_ptr = loader_buf;
2784
2785   for (cur = str; *cur != '\0'; ++cur )
2786     {
2787       d = table[(int)*cur];
2788      &