root/trunk/third/nautilus/components/services/install/nautilus-view/nautilus-service-install-view.c @ 15547

Revision 15547, 54.3 KB (checked in by ghudson, 9 years ago)

This commit was generated by cvs2svn to compensate for changes in r15546,
which included commits to RCS files with non-trunk default branches.

Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3/*
4 * Copyright (C) 2000 Eazel, Inc
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 *
21 * Authors: J Shane Culpepper <pepper@eazel.com>
22 *          Robey Pointer <robey@eazel.com>
23 */
24
25#include <config.h>
26
27#include "nautilus-service-install-view.h"
28#include "eazel-services-header.h"
29#include "eazel-services-extensions.h"
30#include <libeazelinstall.h>
31#include "../lib/eazel-install-metadata.h"
32#include "libtrilobite/libtrilobite.h"
33
34#include <rpm/rpmlib.h>
35#include <gnome-xml/tree.h>
36#include <libgnomevfs/gnome-vfs-utils.h>
37#include <libnautilus-extensions/nautilus-background.h>
38#include <libnautilus-extensions/nautilus-gtk-extensions.h>
39#include <libnautilus-extensions/nautilus-gtk-macros.h>
40#include <libnautilus-extensions/nautilus-glib-extensions.h>
41#include <libnautilus-extensions/nautilus-global-preferences.h>
42#include <libnautilus-extensions/nautilus-file-utilities.h>
43#include <libnautilus-extensions/nautilus-string.h>
44#include <libnautilus-extensions/nautilus-label.h>
45#include <libnautilus-extensions/nautilus-gdk-extensions.h>
46#include <libnautilus-extensions/nautilus-password-dialog.h>
47#include <libnautilus-extensions/nautilus-stock-dialogs.h>
48#include <libnautilus-extensions/nautilus-viewport.h>
49#include <stdio.h>
50#include <fcntl.h>
51#include <dirent.h>
52#include <unistd.h>
53#include <errno.h>
54#include <sys/stat.h>
55
56/* for uname */
57#include <sys/utsname.h>
58
59/* number of rows of (label, progressbar) to scroll at the bottom */
60#define STATUS_ROWS             2
61
62#define PROGRESS_BAR_HEIGHT     15
63#define MESSAGE_BOX_HEIGHT      110
64
65/* This ensures that if the arch is detected as i[3-9]86, the
66   requested archtype will be set to i386 */
67#define ASSUME_ix86_IS_i386
68
69/* send the user here after a completed (success or failure) install */
70#define NEXT_URL_ANONYMOUS      "eazel:"
71#define NEXT_URL                "eazel-services:/catalog"
72
73
74static void       nautilus_service_install_view_initialize_class (NautilusServiceInstallViewClass       *klass);
75static void       nautilus_service_install_view_initialize       (NautilusServiceInstallView            *view);
76static void       nautilus_service_install_view_destroy          (GtkObject                             *object);
77static void       service_install_load_location_callback         (NautilusView                          *nautilus_view,
78                                                                  const char                            *location,
79                                                                  NautilusServiceInstallView            *view);
80static void       service_install_stop_loading_callback          (NautilusView                          *nautilus_view,
81                                                                  NautilusServiceInstallView            *view);
82static void       generate_install_form                          (NautilusServiceInstallView            *view);
83static void       nautilus_service_install_view_update_from_uri  (NautilusServiceInstallView            *view,
84                                                                  const char                            *uri);
85static void       show_overall_feedback                          (NautilusServiceInstallView            *view,
86                                                                  char                                  *progress_message);
87
88NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusServiceInstallView, nautilus_service_install_view, GTK_TYPE_EVENT_BOX)
89
90
91/* gtk rulez */
92static void
93add_padding_to_box (GtkWidget *box, int pad_x, int pad_y)
94{
95        GtkWidget *filler;
96
97        filler = gtk_label_new ("");
98        gtk_widget_set_usize (filler, pad_x ? pad_x : 1, pad_y ? pad_y : 1);
99        gtk_widget_show (filler);
100        gtk_box_pack_start (GTK_BOX (box), filler, FALSE, FALSE, 0);
101}
102
103static gboolean
104line_expose (GtkWidget *widget, GdkEventExpose *event)
105{
106        gdk_window_clear_area (widget->window, event->area.x, event->area.y, event->area.width, event->area.height);
107        gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
108        gdk_draw_line (widget->window, widget->style->fg_gc[widget->state],
109                       event->area.x, widget->allocation.height/2,
110                       event->area.x + event->area.width, widget->allocation.height/2);
111        gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], NULL);
112
113        return TRUE;
114}
115
116static GtkWidget *
117horizontal_line_new (int height)
118{
119        GtkWidget *line;
120        NautilusBackground *background;
121
122        line = gtk_drawing_area_new ();
123        gtk_drawing_area_size (GTK_DRAWING_AREA (line), 100, 1);
124        gtk_signal_connect (GTK_OBJECT (line), "expose_event", GTK_SIGNAL_FUNC (line_expose), NULL);
125        gtk_widget_set_usize (line, -2, height);
126
127        background = nautilus_get_widget_background (line);
128        nautilus_background_set_color (background, EAZEL_SERVICES_BACKGROUND_COLOR_SPEC);
129
130        return line;
131}
132
133static void
134install_message_destroy (InstallMessage *im)
135{
136        g_free (im->package_name);
137        g_free (im);
138}
139
140static InstallMessage *
141install_message_new (NautilusServiceInstallView *view, const char *package_name)
142{
143        InstallMessage *im, *im2;
144        GtkWidget *bogus_label;
145        GList *iter;
146
147        im = NULL;
148       
149        for (iter = g_list_first (view->details->message); iter != NULL; iter = g_list_next (iter)) {
150                im = (InstallMessage *)(iter->data);
151                if (strcmp (im->package_name, package_name) == 0) {
152                        break;
153                }
154        }
155        if (iter != NULL) {
156                /* trash the old one */
157                gtk_container_remove (GTK_CONTAINER (view->details->message_box), im->hbox);
158                if (im->line != NULL) {
159                        gtk_container_remove (GTK_CONTAINER (view->details->message_box), im->line);
160                } else if (iter->prev) {
161                        /* remove the line from the one above, if present */
162                        im2 = (InstallMessage *)(iter->prev->data);
163                        gtk_container_remove (GTK_CONTAINER (view->details->message_box), im2->line);
164                        im2->line = NULL;
165                }
166                view->details->message = g_list_remove (view->details->message, im);
167                install_message_destroy (im);
168        }
169
170        im = g_new0 (InstallMessage, 1);
171        im->label = eazel_services_label_new (NULL, 0, 0.0, 0.5, 0, 0,
172                                              EAZEL_SERVICES_BODY_TEXT_COLOR_RGB,
173                                              EAZEL_SERVICES_BACKGROUND_COLOR_RGB,
174                                              NULL, -2, FALSE);
175        nautilus_label_set_justify (NAUTILUS_LABEL (im->label), GTK_JUSTIFY_LEFT);
176        gtk_widget_show (im->label);
177        im->progress_bar = gtk_progress_bar_new ();
178        gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR (im->progress_bar), GTK_PROGRESS_CONTINUOUS);
179        gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR (im->progress_bar), GTK_PROGRESS_LEFT_TO_RIGHT);
180        gtk_progress_bar_update (GTK_PROGRESS_BAR (im->progress_bar), 0.0);
181        gtk_widget_set_usize (im->progress_bar, -2, PROGRESS_BAR_HEIGHT);
182        gtk_widget_show (im->progress_bar);
183
184        im->progress_label = eazel_services_label_new (NULL, 0, 0.0, 0.0, 0, 0,
185                                                       EAZEL_SERVICES_BODY_TEXT_COLOR_RGB,
186                                                       EAZEL_SERVICES_BACKGROUND_COLOR_RGB,
187                                                       NULL, -2, TRUE);
188        nautilus_label_set_justify (NAUTILUS_LABEL (im->progress_label), GTK_JUSTIFY_LEFT);
189
190        gtk_widget_show (im->progress_label);
191
192        bogus_label = gtk_label_new ("");
193        gtk_widget_show (bogus_label);
194
195        im->vbox = gtk_vbox_new (FALSE, 0);
196        gtk_box_pack_start (GTK_BOX (im->vbox), im->progress_bar, FALSE, FALSE, 0);
197        add_padding_to_box (im->vbox, 0, 5);
198        gtk_box_pack_start (GTK_BOX (im->vbox), im->progress_label, FALSE, FALSE, 0);
199        gtk_widget_show (im->vbox);
200
201        im->hbox = gtk_hbox_new (FALSE, 0);
202        add_padding_to_box (im->hbox, 20, 0);
203        gtk_box_pack_start (GTK_BOX (im->hbox), im->label, FALSE, FALSE, 0);
204        gtk_box_pack_start (GTK_BOX (im->hbox), bogus_label, TRUE, TRUE, 0);
205        gtk_box_pack_start (GTK_BOX (im->hbox), im->vbox, FALSE, FALSE, 0);
206        add_padding_to_box (im->hbox, 20, 0);
207        gtk_widget_show (im->hbox);
208
209#if 0
210        if (g_list_length (view->details->message) == STATUS_ROWS) {
211                gtk_widget_set_usize (view->details->pane, -2, view->details->pane->allocation.height);
212                gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view->details->pane),
213                                                GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
214        }
215#endif
216
217        /* add this, and possibly a separating line, to the message box */
218        if (g_list_length (view->details->message) > 0) {
219                /* draw line */
220                im->line = horizontal_line_new (6);
221                gtk_widget_show (im->line);
222                gtk_box_pack_end (GTK_BOX (view->details->message_box), im->line, FALSE, FALSE, 0);
223        } else {
224                im->line = NULL;
225        }
226        gtk_box_pack_end (GTK_BOX (view->details->message_box), im->hbox, FALSE, FALSE, 5);
227
228        gtk_adjustment_changed (gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (view->details->pane)));
229
230        im->package_name = g_strdup (package_name);
231        view->details->message = g_list_prepend (view->details->message, im);
232
233        /* unbelievable!  gtk won't redraw correctly unless we poke it in the ass */
234        gtk_widget_queue_resize (view->details->form);
235        gtk_widget_queue_draw (view->details->form);
236
237        return im;
238}
239
240static void
241generate_install_form (NautilusServiceInstallView       *view) 
242{
243        GtkWidget       *temp_box;
244        GtkWidget       *title;
245        GtkWidget       *viewport;
246        GtkWidget       *filler;
247        NautilusBackground *background;
248
249        /* allocate the parent box to hold everything */
250        view->details->form = gtk_vbox_new (FALSE, 0);
251        gtk_container_add (GTK_CONTAINER (view), view->details->form);
252
253        /* Setup the title */
254        title = eazel_services_header_title_new (_("Easy Install"));
255        gtk_box_pack_start (GTK_BOX (view->details->form), title, FALSE, FALSE, 0);
256        gtk_widget_show (title);
257
258        /* Add package information */
259
260        add_padding_to_box (view->details->form, 0, 6);
261
262        /* Package Name */
263        temp_box = gtk_hbox_new (FALSE, 0);
264        gtk_box_pack_start (GTK_BOX (view->details->form), temp_box, FALSE, FALSE, 0);
265        gtk_widget_show (temp_box);
266        view->details->package_name = eazel_services_label_new (NULL, 0, 0.0, 0.0, 0, 0,
267                                                                EAZEL_SERVICES_BODY_TEXT_COLOR_RGB,
268                                                                EAZEL_SERVICES_BACKGROUND_COLOR_RGB,
269                                                                NULL, 4, TRUE);
270        nautilus_label_set_justify (NAUTILUS_LABEL (view->details->package_name), GTK_JUSTIFY_LEFT);
271        gtk_box_pack_start (GTK_BOX (temp_box), view->details->package_name, FALSE, FALSE, 15);
272        gtk_widget_show (view->details->package_name);
273
274        /* Package Version */
275        temp_box = gtk_hbox_new (FALSE, 0);
276        gtk_box_pack_start (GTK_BOX (view->details->form), temp_box, FALSE, FALSE, 2);
277        gtk_widget_show (temp_box);
278        view->details->package_version = eazel_services_label_new (NULL, 0, 0.0, 0.0, 0, 0,
279                                                                   EAZEL_SERVICES_BODY_TEXT_COLOR_RGB,
280                                                                   EAZEL_SERVICES_BACKGROUND_COLOR_RGB,
281                                                                   NULL, -2, TRUE);
282        nautilus_label_set_justify (NAUTILUS_LABEL (view->details->package_version), GTK_JUSTIFY_LEFT);
283        gtk_box_pack_start (GTK_BOX (temp_box), view->details->package_version, FALSE, FALSE, 15);
284        gtk_widget_show (view->details->package_version);
285
286        add_padding_to_box (view->details->form, 0, 4);
287
288        /* generate the overall progress bar */
289        temp_box = gtk_hbox_new (FALSE, 0);
290        gtk_box_pack_start (GTK_BOX (view->details->form), temp_box, FALSE, FALSE, 2);
291        gtk_widget_show (temp_box);
292        view->details->total_progress_bar = gtk_progress_bar_new ();
293        gtk_widget_set_usize (view->details->total_progress_bar, -2, PROGRESS_BAR_HEIGHT);
294        add_padding_to_box (temp_box, 30, 0);
295        gtk_box_pack_start (GTK_BOX (temp_box), view->details->total_progress_bar, FALSE, FALSE, 0);
296        gtk_widget_show (view->details->total_progress_bar);
297
298        /* add a label for progress messages, but don't show it until there's a message */
299        temp_box = gtk_hbox_new (FALSE, 0);
300        gtk_box_pack_start (GTK_BOX (view->details->form), temp_box, FALSE, FALSE, 2);
301        gtk_widget_show (temp_box);
302        view->details->overall_feedback_text = eazel_services_label_new (NULL, 0, 0.0, 0.0, 0, 0,
303                                                                         EAZEL_SERVICES_BODY_TEXT_COLOR_RGB,
304                                                                         EAZEL_SERVICES_BACKGROUND_COLOR_RGB,
305                                                                         NULL, -2, FALSE);
306        nautilus_label_set_justify (NAUTILUS_LABEL (view->details->overall_feedback_text), GTK_JUSTIFY_LEFT);
307        nautilus_label_set_text (NAUTILUS_LABEL (view->details->overall_feedback_text), " ");
308        gtk_widget_show (view->details->overall_feedback_text);
309        add_padding_to_box (temp_box, 30, 0);
310        gtk_box_pack_start (GTK_BOX (temp_box), view->details->overall_feedback_text, TRUE, TRUE, 0);
311
312        add_padding_to_box (view->details->form, 0, 10);
313
314        /* Package Description */
315        temp_box = gtk_hbox_new (FALSE, 0);
316        gtk_box_pack_start (GTK_BOX (view->details->form), temp_box, FALSE, FALSE, 2);
317        gtk_widget_show (temp_box);
318
319        view->details->package_details = eazel_services_label_new (NULL, 0, 0.0, 0.0, 0, 0,
320                                                                   EAZEL_SERVICES_BODY_TEXT_COLOR_RGB,
321                                                                   EAZEL_SERVICES_BACKGROUND_COLOR_RGB,
322                                                                   NULL, -2, FALSE);
323        nautilus_label_set_justify (NAUTILUS_LABEL (view->details->package_details), GTK_JUSTIFY_LEFT);
324        nautilus_label_set_wrap (NAUTILUS_LABEL (view->details->package_details), TRUE);
325
326        gtk_box_pack_start (GTK_BOX (temp_box), view->details->package_details, FALSE, FALSE, 15);
327        gtk_widget_show (view->details->package_details);
328
329        /* filler blob to separate the top from the bottom */
330        gtk_box_pack_start (GTK_BOX (view->details->form), gtk_label_new (""), TRUE, FALSE, 0);
331
332        /* add a table at the bottom of the screen to hold current progresses */
333        view->details->message_box = gtk_vbox_new (FALSE, 0);
334        filler = gtk_label_new ("");
335        gtk_widget_show (filler);
336        gtk_box_pack_end (GTK_BOX (view->details->message_box), filler, TRUE, TRUE, 0);
337
338        view->details->pane = gtk_scrolled_window_new (NULL, NULL);
339        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view->details->pane), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
340        viewport = nautilus_viewport_new (NULL, NULL);
341        gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
342        gtk_container_add (GTK_CONTAINER (view->details->pane), viewport);
343        gtk_widget_show (viewport);
344        gtk_container_add (GTK_CONTAINER (viewport), view->details->message_box);
345        gtk_widget_show (view->details->message_box);
346        background = nautilus_get_widget_background (viewport);
347        nautilus_background_set_color (background, EAZEL_SERVICES_BACKGROUND_COLOR_SPEC);
348        gtk_widget_set_usize (view->details->pane, -2, MESSAGE_BOX_HEIGHT);
349        gtk_widget_show (view->details->pane);
350        gtk_box_pack_end (GTK_BOX (view->details->form), view->details->pane, FALSE, FALSE, 2);
351
352        /* Setup the progress header */
353        /* FIXME: the middle header is all fubar now, so we just say "messages", but the spec
354         * shows "progress" over the 2nd column of the message box.  it's just too hard to get
355         * it aligned currently.
356         */
357        view->details->middle_title = eazel_services_header_middle_new (_("Messages"), "");
358        gtk_box_pack_end (GTK_BOX (view->details->form), view->details->middle_title, FALSE, FALSE, 0);
359        gtk_widget_show (view->details->middle_title);
360
361        gtk_widget_show (view->details->form);
362}
363
364
365
366/* utility routine to show an error message */
367static void
368show_overall_feedback (NautilusServiceInstallView *view, char *progress_message)
369{
370        nautilus_label_set_text (NAUTILUS_LABEL (view->details->overall_feedback_text), progress_message);
371}
372
373static void
374nautilus_service_install_view_initialize_class (NautilusServiceInstallViewClass *klass)
375{
376
377        GtkObjectClass  *object_class;
378        GtkWidgetClass  *widget_class;
379       
380        object_class = GTK_OBJECT_CLASS (klass);
381        widget_class = GTK_WIDGET_CLASS (klass);
382        parent_class = gtk_type_class (gtk_event_box_get_type ());
383        object_class->destroy = nautilus_service_install_view_destroy;
384}
385
386static void
387nautilus_service_install_view_initialize (NautilusServiceInstallView *view)
388{
389
390        NautilusBackground      *background;
391
392        view->details = g_new0 (NautilusServiceInstallViewDetails, 1);
393        view->details->nautilus_view = nautilus_view_new (GTK_WIDGET (view));
394        gtk_signal_connect (GTK_OBJECT (view->details->nautilus_view), 
395                            "load_location",
396                            GTK_SIGNAL_FUNC (service_install_load_location_callback), 
397                            view);
398        gtk_signal_connect (GTK_OBJECT (view->details->nautilus_view),
399                            "stop_loading",
400                            GTK_SIGNAL_FUNC (service_install_stop_loading_callback),
401                            view);
402
403        background = nautilus_get_widget_background (GTK_WIDGET (view));
404        nautilus_background_set_color (background, EAZEL_SERVICES_BACKGROUND_COLOR_SPEC);
405
406        view->details->core_package = FALSE;
407        view->details->deps = g_hash_table_new (g_str_hash, g_str_equal);
408
409        gtk_widget_show (GTK_WIDGET (view));
410}
411
412static gboolean
413deps_destroy_foreach (char *key, char *value)
414{
415        g_free (key);
416        g_free (value);
417        return TRUE;
418}
419
420static void
421nautilus_service_install_view_destroy (GtkObject *object)
422{
423        NautilusServiceInstallView *view;
424        GNOME_Trilobite_Eazel_Install   service;
425        CORBA_Environment ev;
426
427        view = NAUTILUS_SERVICE_INSTALL_VIEW (object);
428
429        CORBA_exception_init (&ev);
430        service = eazel_install_callback_corba_objref (view->details->installer);
431        GNOME_Trilobite_Eazel_Install_stop (service, &ev);
432        CORBA_exception_free (&ev);
433
434        g_free (view->details->uri);
435        g_free (view->details->current_rpm);
436        g_free (view->details->remembered_password);
437        g_hash_table_foreach_remove (view->details->deps, (GHRFunc)deps_destroy_foreach, NULL);
438        g_hash_table_destroy (view->details->deps);
439        g_list_foreach (view->details->message, (GFunc)install_message_destroy, NULL);
440        g_list_free (view->details->message);
441        g_free (view->details->username);
442
443        if (view->details->root_client) {
444                trilobite_root_client_unref (GTK_OBJECT (view->details->root_client));
445        }
446
447        if (view->details->installer != NULL) {
448                /* this will unref the installer too, which will indirectly cause any ongoing download to abort */
449                eazel_install_callback_unref (GTK_OBJECT (view->details->installer));
450        }
451
452        g_free (view->details);
453       
454        NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, destroy, (object));
455}
456
457NautilusView *
458nautilus_service_install_view_get_nautilus_view (NautilusServiceInstallView *view)
459{
460        return view->details->nautilus_view;
461}
462
463
464static PackageData *
465create_package (char *name, int local_file) 
466{
467        struct utsname buf;
468        PackageData *pack;
469
470        g_assert (name);
471
472        uname (&buf);
473        pack = packagedata_new ();
474        if (local_file) {
475                pack->filename = g_strdup (name);
476        } else if (strncmp (name, "rpm_id%3D", 9) == 0) {
477                pack->eazel_id = g_strdup (name+9);
478        } else if (strncmp (name, "rpm_id=", 7) == 0) {
479                pack->eazel_id = g_strdup (name+7);
480        } else {
481                pack->name = g_strdup (name);
482        }
483        pack->archtype = g_strdup (buf.machine);
484#ifdef ASSUME_ix86_IS_i386
485        if (strlen (pack->archtype)==4 && pack->archtype[0]=='i' &&
486            pack->archtype[1]>='3' && pack->archtype[1]<='9' &&
487            pack->archtype[2]=='8' && pack->archtype[3]=='6') {
488                g_free (pack->archtype);
489                pack->archtype = g_strdup ("i386");
490        }
491#endif
492        pack->distribution = trilobite_get_distribution ();
493        pack->toplevel = TRUE;
494       
495        return pack;
496}
497
498/* quick & dirty: parse the url into (host, port) and a category list */
499/* format:
500 * "eazel-install:" [ "//" [ username "@" ] [ "hostname" [ ":" port ] ] "/" ]
501 *      package-name [ "?version=" version ] ( ";" package-name [ "?version=" version ] )*
502 *
503 * eazel-install:xfig
504 * eazel-install://anonymous@/freeamp
505 * eazel-install://example.com:8888/nautilus?version=1.0;xpdf;sephiroth?version=0.4
506 */
507/* returns TRUE if a hostname was parsed from the uri */
508static gboolean
509nautilus_install_parse_uri (const char *uri, NautilusServiceInstallView *view,
510                            char **host, int *port, char **username)
511{
512        char *p, *q, *pnext, *package_name, *host_spec;
513        GList *packages = NULL;
514        PackageData *pack;
515        gboolean result = FALSE;
516        gboolean another_package;
517
518        view->details->categories = NULL;
519
520        p = strchr (uri, ':');
521        if (! p) {
522                /* bad mojo */
523                return result;
524        }
525        p++;
526
527        /* "//[user@]host[:port]" spec? */
528        if ((*p == '/') && (*(p+1) == '/')) {
529                p += 2;
530
531                q = strchr (p, '/');
532                if (! q) {
533                        q = p + strlen(p);
534                }
535                host_spec = g_strndup (p, q - p);
536
537                /* optional "user@" */
538                p = strchr (host_spec, '@');
539                if (p) {
540                        *p = 0;
541                        g_free (*username);
542                        *username = host_spec;
543                        if (*(p+1)) {
544                                g_free (*host);
545                                *host = g_strdup (p+1);
546                        }
547                } else {
548                        g_free (*host);
549                        *host = host_spec;
550                        result = TRUE;
551                }
552
553                if (*host) {
554                        /* optional ":port" */
555                        p = strchr (*host, ':');
556                        if (p) {
557                                *p = 0;
558                                *port = atoi (p+1);
559                        }
560                }
561
562                /* push p to past the trailing '/' */
563                p = (*q) ? q+1 : q;
564        }
565
566        /* full path specified?  local file instead of server */
567        if (*p == '/') {
568                view->details->using_local_file = 1;
569        }
570
571        if (*p) {
572                do {
573                        pnext = strchr (p, ';');
574                        if ((pnext != NULL) && (*(pnext+1) != '\0')) {
575                                another_package = TRUE;
576                                *pnext++ = '\0';
577                        } else {
578                                another_package = FALSE;
579                        }
580
581                        trilobite_debug ("package '%s'", p);
582                        /* version name specified? */
583                        q = strchr (p, '?');
584                        if (q) {
585                                *q++ = 0;
586                                if (strncmp (q, "version=", 8) == 0) {
587                                        q += 8;
588                                }
589                        }
590
591                        package_name = gnome_vfs_unescape_string_for_display (p);
592                        pack = create_package (package_name, view->details->using_local_file);
593                        if (q) {
594                                pack->version = g_strdup (q);
595                        }
596                        packages = g_list_prepend (packages, pack);
597                        g_free (package_name);
598
599                        if (pnext != NULL) {
600                                p = pnext;
601                        }
602                } while (another_package);
603        }
604
605        trilobite_debug ("host '%s:%d' username '%s'", *host ? *host : "(default)", *host ? *port : 0,
606                         *username ? *username : "(default)");
607
608        /* add to categories */
609        if (packages) {
610                CategoryData *category;
611
612                category = categorydata_new ();
613                category->packages = packages;
614                view->details->categories = g_list_prepend (view->details->categories, category);
615        }
616        return result;
617}
618
619static void
620update_package_info_display (NautilusServiceInstallView *view, const PackageData *pack, const char *format)
621{
622        char *out;
623
624        out = g_strdup_printf (format, pack->name);
625        nautilus_label_set_text (NAUTILUS_LABEL (view->details->package_name), out);
626        g_free (out);
627
628        if ((pack->description != NULL) && 
629            (strchr (pack->description, '\n') != NULL)) {
630                nautilus_label_set_wrap (NAUTILUS_LABEL (view->details->package_details), FALSE);
631        } else {
632                nautilus_label_set_wrap (NAUTILUS_LABEL (view->details->package_details), TRUE);
633        }
634        nautilus_label_set_text (NAUTILUS_LABEL (view->details->package_details), pack->description);
635        out = g_strdup_printf (_("Version: %s"), pack->version);
636        nautilus_label_set_text (NAUTILUS_LABEL (view->details->package_version), out);
637        g_free (out);
638}
639
640/* replace the current progress bar (in the message box) with a centered label saying "Complete!" */
641static void
642current_progress_bar_complete (NautilusServiceInstallView *view, const char *text)
643{
644        /* can't figure out a decent way to do this yet... :( */
645        if (view->details->current_im != NULL) {
646                nautilus_label_set_text (NAUTILUS_LABEL (view->details->current_im->progress_label), text);
647                gtk_widget_queue_resize (view->details->message_box);
648        }
649}
650
651
652static void
653nautilus_service_install_downloading (EazelInstallCallback *cb, const PackageData *pack, int amount, int total,
654                                      NautilusServiceInstallView *view)
655{
656        char *out;
657        const char *needed_by;
658        InstallMessage *im = view->details->current_im;
659        float fake_amount;
660
661        if (view->details->installer == NULL) {
662                g_warning ("Got download notice after unref!");
663                return;
664        }
665
666        /* install lib better damn well know the name of the package by the time we download it! */
667        g_assert (pack->name != NULL);
668
669        if (amount == 0) {
670                /* could be a redundant zero-trigger for the same rpm... */
671                if (view->details->current_rpm && (strcmp (view->details->current_rpm, pack->name) == 0)) {
672                        return;
673                }
674
675                if (view->details->cylon_timer) {
676                        gtk_timeout_remove (view->details->cylon_timer);
677                        view->details->cylon_timer = 0;
678                }
679
680                g_free (view->details->current_rpm);
681                view->details->current_rpm = g_strdup (pack->name);
682
683#if 0
684                /* figure out if this is a toplevel package, and if so, update the header */
685                for (iter = g_list_first (((CategoryData *)(view->details->categories->data))->packages);
686                     iter != NULL; iter = g_list_next (iter)) {
687                        PackageData *pack2 = (PackageData *)(iter->data);
688                        if ((pack2->name != NULL) && (strcmp (pack2->name, pack->name) == 0)) {
689                                out = g_strdup_printf (_("Downloading \"%s\""), pack->name);
690                                nautilus_label_set_text (NAUTILUS_LABEL (view->details->package_name), out);
691                                g_free (out);
692                        }
693                }
694#endif
695
696                if (pack->toplevel) {
697                        update_package_info_display (view, pack, _("Downloading \"%s\""));
698                }
699
700                /* new progress message and bar */
701                im = view->details->current_im = install_message_new (view, pack->name);
702                gtk_progress_set_percentage (GTK_PROGRESS (im->progress_bar), 0.0);
703                out = g_strdup_printf (_("0K of %dK"), total/1024);
704                nautilus_label_set_text (NAUTILUS_LABEL (im->progress_label), out);
705                g_free (out);
706                view->details->last_k = 0;
707
708                needed_by = g_hash_table_lookup (view->details->deps, pack->name);
709                if (needed_by != NULL) {
710                        out = g_strdup_printf (_("The package \"%s\" needs \"%s\" to run.\nI'm now attempting to download it."),
711                                               needed_by, pack->name);
712                } else {
713                        out = g_strdup_printf (_("I'm attempting to download package \"%s\"."), pack->name);
714                }
715                nautilus_label_set_text (NAUTILUS_LABEL (im->label), out);
716                g_free (out);
717        } else if (amount == total) {
718                /* done! */
719                current_progress_bar_complete (view, _("Complete"));
720                gtk_progress_set_percentage (GTK_PROGRESS (im->progress_bar), 1.0);
721                needed_by = g_hash_table_lookup (view->details->deps, pack->name);
722                if (needed_by != NULL) {
723                        out = g_strdup_printf (_("The package \"%s\" needs \"%s\" to run.\nI've downloaded it successfully."),
724                                               needed_by, pack->name);
725                } else {
726                        out = g_strdup_printf (_("I've successfully downloaded package \"%s\"."), pack->name);
727                }
728                nautilus_label_set_text (NAUTILUS_LABEL (im->label), out);
729                g_free (out);
730                g_free (view->details->current_rpm);
731                view->details->current_rpm = NULL;
732                view->details->current_im = NULL;
733                /* update downloaded bytes */
734                view->details->download_bytes_sofar += pack->bytesize;
735                /* not until we get an rpm size */
736                gtk_progress_set_percentage (GTK_PROGRESS (view->details->total_progress_bar),
737                                             (float) view->details->download_bytes_sofar /
738                                             (float) view->details->download_bytes_total);
739        } else {
740                /* could be a leftover event, after user hit STOP (in which case, current_im = NULL) */
741                if ((im != NULL) && (im->progress_bar != NULL)) {
742                        gtk_progress_set_percentage (GTK_PROGRESS (im->progress_bar),
743                                                     (float) amount / (float) total);
744                        if ((amount/1024) >= view->details->last_k + 10) {
745                                out = g_strdup_printf (_("%dK of %dK"), amount/1024, total/1024);
746                                nautilus_label_set_text (NAUTILUS_LABEL (im->progress_label), out);
747                                g_free (out);
748                                view->details->last_k = (amount/1024);
749                        }
750                }
751
752                /* so, for PR3, we are given a "size" field in the softcat XML which is actually
753                 * the size of the decompressed files.  so this little hocus-pocus scales the
754                 * actual size (which we know once we start downloading the file) to match the   
755                 * previously-assumed size
756                 */
757                fake_amount = (float)amount * (float)pack->bytesize / (float)total;
758                gtk_progress_set_percentage (GTK_PROGRESS (view->details->total_progress_bar),
759                                             ((float) view->details->download_bytes_sofar + fake_amount) /
760                                             (float) view->details->download_bytes_total);
761        }
762}
763
764/* keep this info for secret later use */
765static void
766nautilus_service_install_dependency_check (EazelInstallCallback *cb, const PackageData *package,
767                                           const PackageData *needs, NautilusServiceInstallView *view)
768{
769        char *key, *value;
770
771        /* add to deps hash for later */
772        if (g_hash_table_lookup_extended (view->details->deps, needs->name, (void **)&key, (void **)&value)) {
773                g_hash_table_remove (view->details->deps, key);
774                g_free (key);
775                g_free (value);
776        }
777        g_hash_table_insert (view->details->deps, g_strdup (needs->name), g_strdup (package->name));
778
779        value = g_strdup_printf (_("Getting information about package \"%s\" ..."), package->name);
780        show_overall_feedback (view, value);
781        g_free (value);
782}
783
784static void
785nautilus_service_install_check_for_desktop_files (NautilusServiceInstallView *view,
786                                                  EazelInstallCallback *cb,
787                                                  PackageData *package)
788{
789        GList *iterator;
790
791        for (iterator = package->provides; iterator; iterator = g_list_next (iterator)) {
792                char *fname = (char*)(iterator->data);
793                char *ptr;
794
795                ptr = strrchr (fname, '.');
796                if (ptr && ((strcmp (ptr, ".desktop") == 0) ||
797                            (strcmp (ptr, ".kdelink") == 0))) {
798                        view->details->desktop_files = g_list_prepend (view->details->desktop_files,
799                                                                       g_strdup (fname));
800                }
801        }
802}
803
804/* do what gnome ought to do automatically */
805static void
806reply_callback (int reply, gboolean *answer)
807{
808        *answer = (reply == 0);
809}
810
811static void
812flatten_package_tree_foreach (PackageData *package, GList **flattened_list)
813{
814        GList *iter;
815        gboolean found = FALSE;
816        PackageData *pack;
817
818        for (iter = g_list_first (*flattened_list); iter != NULL; iter = g_list_next (iter)) {
819                pack = (PackageData *)(iter->data);
820                if ((strcmp (pack->name, package->name) == 0) &&
821                    (strcmp (pack->version, package->version) == 0)) {
822                        found = TRUE;
823                        break;
824                }
825        }
826
827        if (! found) {
828                /* add it to the flattened list */
829                *flattened_list = g_list_prepend (*flattened_list, package);
830        }
831
832        g_list_foreach (package->soft_depends, (GFunc)flatten_package_tree_foreach, flattened_list);
833        g_list_foreach (package->hard_depends, (GFunc)flatten_package_tree_foreach, flattened_list);
834}
835
836/* given a package tree containing possibly redundant packages, assemble a new list
837 * which contains only those packages with unique <name, version>
838 */
839static void
840flatten_package_tree (GList *package_list_in, GList **flattened_list)
841{
842        g_list_foreach (package_list_in, (GFunc)flatten_package_tree_foreach, flattened_list);
843}
844
845static gboolean
846nautilus_service_install_preflight_check (EazelInstallCallback *cb, const GList *packages,
847                                          int total_bytes, int total_packages,
848                                          NautilusServiceInstallView *view)
849{
850        GtkWidget *dialog;
851        GtkWidget *toplevel;
852        GString *message;
853        gboolean answer;
854        PackageData *package;
855        GList *package_list;
856        GList *iter;
857        char *out;
858        unsigned long total_k;
859
860        if (view->details->cancelled) {
861                /* user has already hit the cancel button */
862                view->details->cancelled_before_downloads = TRUE;
863                return FALSE;
864        }
865
866        /* assemble initial list of packages to browse */
867        package_list = NULL;
868        flatten_package_tree ((GList *)packages, &package_list);
869        package_list = g_list_reverse (package_list);
870
871        show_overall_feedback (view, _("Preparing to download packages..."));
872
873        message = g_string_new ("");
874        message = g_string_append (message, _("I'm about to download and install the following packages:\n\n"));
875
876        view->details->download_bytes_total = view->details->download_bytes_sofar = 0;
877        for (iter = g_list_first (package_list); iter != NULL; iter = g_list_next (iter)) {
878                package = (PackageData *)(iter->data);
879                out = packagedata_get_readable_name (package);
880                g_string_sprintfa (message, " \xB7 %s\n", out);
881                g_free (out);
882                view->details->download_bytes_total += package->bytesize;
883
884                if (package->toplevel) {
885                        nautilus_service_install_check_for_desktop_files (view,
886                                                                          cb,
887                                                                          package);
888                }
889        }
890        total_k = (view->details->download_bytes_total+512)/1024;
891        /* arbitrary dividing line */
892        if (total_k > 4096) {
893                out = g_strdup_printf (_("for a total of %ld MB."), (total_k+512)/1024);
894        } else {
895                out = g_strdup_printf (_("for a total of %ld kB."), total_k);
896        }
897        g_string_sprintfa (message, "\n%s", out);
898        g_free (out);
899
900        message = g_string_append (message, _("\nIs this okay?"));
901        toplevel = gtk_widget_get_toplevel (view->details->message_box);
902
903        if (GTK_IS_WINDOW (toplevel)) {
904                dialog = gnome_ok_cancel_dialog_parented (message->str, (GnomeReplyCallback)reply_callback,
905                                                          &answer, GTK_WINDOW (toplevel));
906        } else {
907                dialog = gnome_ok_cancel_dialog (message->str, (GnomeReplyCallback)reply_callback,
908                                                 &answer);
909        }
910        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
911
912        gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
913        g_string_free (message, TRUE);
914
915        if (!answer) {
916                g_list_free (package_list);
917                view->details->cancelled = TRUE;
918                view->details->cancelled_before_downloads = TRUE;
919                /* EVIL EVIL hack that causes the next dialog to show up instead of being hidden */
920                sleep (1);
921                while (gtk_events_pending ())
922                        gtk_main_iteration ();
923                return answer;
924        }
925
926        if (g_list_length (package_list) == 1) {
927                out = g_strdup (_("Downloading 1 package"));
928        } else {
929                out = g_strdup_printf (_("Downloading %d packages"), g_list_length (package_list));
930        }
931        show_overall_feedback (view, out);
932        g_free (out);
933
934        g_list_free (package_list);
935        view->details->current_package = 0;
936        return answer;
937}
938
939
940static void
941nautilus_service_install_download_failed (EazelInstallCallback *cb, const PackageData *pack,
942                                          NautilusServiceInstallView *view)
943{
944        char *out, *tmp;
945
946        /* no longer "loading" anything */
947        nautilus_view_report_load_complete (view->details->nautilus_view);
948
949        tmp = packagedata_get_readable_name (pack);
950        out = g_strdup_printf (_("Download of package \"%s\" failed!"), tmp);
951        g_free (tmp);
952        if (view->details->current_im != NULL) {
953                nautilus_label_set_text (NAUTILUS_LABEL (view->details->current_im->label), out);
954        }
955        g_free (out);
956}
957
958static void
959previous_install_finished (NautilusServiceInstallView *view)
960{
961        InstallMessage *im;
962        char *needed_by;
963        char *out;
964
965        im = view->details->current_im;
966        if (im != NULL) {
967                current_progress_bar_complete (view, _("Complete"));
968                gtk_progress_set_percentage (GTK_PROGRESS (im->progress_bar), 1.0);
969
970                needed_by = g_hash_table_lookup (view->details->deps, view->details->current_rpm);
971                if (needed_by != NULL) {
972                        out = g_strdup_printf (_("The package \"%s\" needed \"%s\" to run.\nI've downloaded and installed it."),
973                                               needed_by, view->details->current_rpm);
974                } else {
975                        out = g_strdup_printf (_("I've downloaded and installed package \"%s\"."), view->details->current_rpm);
976                }
977                nautilus_label_set_text (NAUTILUS_LABEL (im->label), out);
978                g_free (out);
979        }
980        g_free (view->details->current_rpm);
981        view->details->current_rpm = NULL;
982        view->details->current_im = NULL;
983}
984
985static void
986nautilus_service_install_installing (EazelInstallCallback *cb, const PackageData *pack,
987                                     int current_package, int total_packages,
988                                     int package_progress, int package_total,
989                                     int total_progress, int total_total,
990                                     NautilusServiceInstallView *view)
991{
992        InstallMessage *im;
993        gfloat overall_complete, complete;
994        char *out;
995        char *needed_by;
996
997        im = view->details->current_im;
998        if (current_package != view->details->current_package) {
999                /* no longer "loading" anything */
1000                nautilus_view_report_load_complete (view->details->nautilus_view);
1001
1002                /* starting a new package -- create new progress indicator */
1003                out = g_strdup_printf (_("Installing package %d of %d"), current_package, total_packages);
1004                show_overall_feedback (view, out);
1005                g_free (out);
1006
1007                /* new behavior: sometimes the previous package wasn't quite closed out -- do it now */
1008                if (im != NULL) {
1009                        previous_install_finished (view);
1010                }
1011
1012                /* if you're looking for the place where we notice that one of nautilus's core
1013                 * packages is being upgraded, this is it.  this is an evil, evil way to do it,
1014                 * but nobody's come up with anything better yet.
1015                 */
1016                if (pack->name) {               
1017                        if ((g_strncasecmp (pack->name, "nautilus", 8) == 0) ||
1018                            (g_strncasecmp (pack->name, "gnome-vfs", 9) == 0) ||
1019                            (g_strncasecmp (pack->name, "oaf", 3) == 0)) {
1020                                view->details->core_package = TRUE;
1021                        } 
1022                }
1023
1024                g_free (view->details->current_rpm);
1025                view->details->current_rpm = g_strdup (pack->name);
1026                view->details->current_im = im = install_message_new (view, pack->name);
1027                gtk_progress_set_percentage (GTK_PROGRESS (im->progress_bar), 0.0);
1028                needed_by = g_hash_table_lookup (view->details->deps, pack->name);
1029                if (needed_by != NULL) {
1030                        out = g_strdup_printf (_("The package \"%s\" needs \"%s\" to run.\nI'm now installing it."),
1031                                               needed_by, pack->name);
1032                } else {
1033                        out = g_strdup_printf (_("I'm installing package \"%s\"."), pack->name);
1034                }
1035                nautilus_label_set_text (NAUTILUS_LABEL (im->label), out);
1036                g_free (out);
1037
1038                view->details->current_package = current_package;
1039
1040                if (pack->toplevel) {
1041                        update_package_info_display (view, pack, _("Installing \"%s\""));
1042                }
1043        }
1044
1045        complete = (gfloat) package_progress / package_total;
1046        overall_complete = (gfloat) total_progress / total_total;
1047        gtk_progress_set_percentage (GTK_PROGRESS (im->progress_bar), complete);
1048        gtk_progress_set_percentage (GTK_PROGRESS (view->details->total_progress_bar), overall_complete);
1049        out = g_strdup_printf (_("%d%%"), (int)(complete*100.0));
1050        nautilus_label_set_text (NAUTILUS_LABEL (im->progress_label), out);
1051        g_free (out);
1052
1053        if ((package_progress == package_total) && (package_total > 0)) {
1054                /* done with another package! */
1055                previous_install_finished (view);
1056        }
1057}
1058
1059/* Get a description of the application pointed to by a given dentry and path fragment */
1060static char*
1061nautilus_install_service_describe_menu_entry (GnomeDesktopEntry *dentry,
1062                                              const char        *path_prefix,
1063                                              const char        *path_fragment)
1064{
1065        char *slash;
1066        char *addition = NULL, *addition_tmp;
1067        char *fragment_tmp;
1068
1069        char **pieces;
1070        char *so_far;
1071        int i;
1072        char *dir, *file, *menu;
1073        GnomeDesktopEntry *dir_dentry;
1074
1075        fragment_tmp = g_strdup (path_fragment);
1076        slash = strrchr (fragment_tmp, G_DIR_SEPARATOR);
1077        if (slash != NULL) {
1078                *slash = '\0';
1079        }
1080        pieces = g_strsplit (fragment_tmp, "/", 128); /* FIXME "/" -> G_DIR_SEPARATOR */
1081        g_free (fragment_tmp);
1082        so_far = g_strdup (path_prefix);
1083
1084        for (i=0; pieces[i] != NULL; i++) {
1085
1086                dir = g_strconcat (so_far, pieces[i], "/", NULL);
1087                file = g_strconcat (dir, ".directory", NULL);
1088
1089                g_free (so_far);
1090                so_far = dir;
1091
1092                dir_dentry = gnome_desktop_entry_load (file);
1093                g_free (file);
1094
1095                menu = NULL;
1096                if (dir_dentry != NULL) {
1097                        menu = dir_dentry->name;
1098                } else {
1099                        menu = pieces[i];
1100                }
1101
1102                if (addition == NULL) {
1103                        addition = g_strdup_printf
1104                                        (_(" \xB7 %s is in the Gnome menu under %s"),
1105                                        dentry->name, menu);
1106                } else {
1107                        addition_tmp = g_strconcat (addition, " / ", dir_dentry->name, NULL);
1108                        g_free (addition);
1109                        addition = addition_tmp;
1110                }
1111
1112                /* menu doesn't need to be freed, because it points into another structure */
1113
1114                if (dir_dentry != NULL) {
1115                        gnome_desktop_entry_free (dir_dentry);
1116                }
1117        }       
1118        g_free (so_far);
1119        g_strfreev (pieces);
1120
1121        if (addition == NULL) {
1122                addition = g_strdup_printf (_(" \xB7 %s is in the Gnome menu.\n"), dentry->name);
1123        } else {
1124                addition_tmp = g_strconcat (addition, ".\n", NULL);
1125                g_free (addition);
1126                addition = addition_tmp;
1127        }
1128
1129        return addition;
1130       
1131}
1132
1133/* Get the toplevel menu name for the desktop file installed */
1134static char*
1135nautilus_install_service_locate_menu_entries (NautilusServiceInstallView *view) 
1136{
1137        GList *iterator;
1138        char *result;
1139       
1140        result = g_strdup ("");
1141
1142        for (iterator = view->details->desktop_files; iterator; iterator = g_list_next (iterator)) {
1143                char *fname = (char*)(iterator->data);
1144                char *addition = NULL;
1145                char *tmp;
1146                GnomeDesktopEntry *dentry = gnome_desktop_entry_load (fname);
1147
1148                if (dentry->is_kde) {
1149                        addition = g_strdup_printf (_(" \xB7 %s is in the KDE menu.\n"), dentry->name);
1150                } else {
1151                        /* match desktop files against a set of paths that the panel is known to
1152                         * put in the menu. */
1153                        char *desktop_prefixes[] = {
1154                                "/gnome/apps/",
1155                                "/applnk/"
1156                        };
1157                        int num_prefixes = 2;
1158                        int i;
1159
1160                        for (i=0; i<num_prefixes; i++) {
1161                                char *gnomeapp = desktop_prefixes[i];
1162                                char *apps_ptr = strstr (fname, gnomeapp);
1163                                if (apps_ptr) {
1164                                        char *full_prefix = g_strndup (fname, (apps_ptr)-fname + 
1165                                                        strlen (gnomeapp));
1166                                        addition = nautilus_install_service_describe_menu_entry
1167                                                        (dentry, full_prefix, apps_ptr+strlen (gnomeapp));
1168                                        g_free (full_prefix);
1169                                        if (addition != NULL) {
1170                                                break;
1171                                        }
1172                                }
1173                        }
1174                }
1175                if (addition) {
1176                        tmp = g_strdup_printf ("%s%s", result, addition);
1177                        g_free (result);
1178                        result = tmp;
1179                        g_free (addition);
1180                }
1181                gnome_desktop_entry_free (dentry);
1182        }
1183        return result;
1184}
1185
1186/* most likely OBSOLETE */
1187static gboolean
1188nautilus_service_install_solve_cases (NautilusServiceInstallView *view)
1189{
1190        gboolean answer = FALSE;
1191        GtkWidget *toplevel;
1192        GString *messages;
1193        GList *strings;
1194        GtkWidget *dialog;
1195
1196        messages = g_string_new ("");
1197
1198        if (view->details->problem_cases) {
1199                GList *iterator;
1200                /* Create string versions to show the user */
1201                g_string_sprintfa (messages, "%s\n%s\n\n", 
1202                                   _("I ran into problems while installing."), 
1203                                   _("I'd like to try the following :"));
1204                strings = eazel_install_problem_cases_to_string (view->details->problem,
1205                                                                 view->details->problem_cases);
1206                for (iterator = strings; iterator; iterator = g_list_next (iterator)) {
1207                        g_string_sprintfa (messages, " \xB7 %s\n", (char*)(iterator->data));
1208                }
1209                g_list_foreach (strings, (GFunc)g_free, NULL);
1210                g_list_free (strings);
1211                g_string_sprintfa (messages, "\n%s", 
1212                                   _("Is this ok ?"));
1213               
1214                toplevel = gtk_widget_get_toplevel (view->details->message_box);
1215                if (GTK_IS_WINDOW (toplevel)) {
1216                        dialog = gnome_question_dialog_parented (messages->str, (GnomeReplyCallback)reply_callback,
1217                                                                 &answer, GTK_WINDOW (toplevel));
1218                } else {
1219                        dialog = gnome_question_dialog (messages->str, (GnomeReplyCallback)reply_callback, &answer);
1220                }
1221                gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1222                gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
1223                g_string_free (messages, TRUE);
1224        }
1225
1226        return answer;
1227}
1228
1229static void
1230nautilus_service_install_done (EazelInstallCallback *cb, gboolean success, NautilusServiceInstallView *view)
1231{
1232        CORBA_Environment ev;
1233        GtkWidget *toplevel;
1234        GtkWidget *dialog;
1235        char *message;
1236        char *real_message;
1237        gboolean answer = FALSE;
1238        gboolean question_dialog;
1239
1240        g_assert (NAUTILUS_IS_SERVICE_INSTALL_VIEW (view));
1241
1242        /* no longer "loading" anything */
1243        nautilus_view_report_load_complete (view->details->nautilus_view);
1244
1245        if (view->details->failure) {
1246                success = FALSE;
1247                view->details->failure = FALSE;
1248                /* we have already indicated failure elsewhere.  good day to you, sir. */
1249        }
1250
1251        gtk_progress_set_percentage (GTK_PROGRESS (view->details->total_progress_bar), success ? 1.0 : 0.0);
1252        g_free (view->details->current_rpm);
1253        view->details->current_rpm = NULL;
1254
1255        if (view->details->cancelled) {
1256                message = _("Installation aborted.");
1257        } else if (view->details->already_installed) {
1258                message = _("This package has already been installed.");
1259        } else if (success) {
1260                message = _("Installation complete!");
1261        } else {
1262                message = _("Installation failed!");
1263                answer = nautilus_service_install_solve_cases (view);
1264        }
1265
1266        show_overall_feedback (view, message);
1267
1268        if (answer) {
1269                eazel_install_problem_handle_cases (view->details->problem, 
1270                                                    view->details->installer, 
1271                                                    &(view->details->problem_cases), 
1272                                                    &(view->details->categories),
1273                                                    NULL,
1274                                                    NULL);
1275        } else {
1276                question_dialog = TRUE;
1277
1278                if (success && view->details->desktop_files &&
1279                                !view->details->cancelled &&
1280                                !view->details->already_installed) {
1281                        real_message = g_strdup_printf (_("%s\n%s\nErase the leftover RPM files?"), 
1282                                                        message,
1283                                                        nautilus_install_service_locate_menu_entries (view));
1284                } else if (view->details->cancelled_before_downloads || view->details->already_installed) {
1285                        real_message = g_strdup (message);
1286                        question_dialog = FALSE;
1287                } else {
1288                        if (view->details->cancelled || view->details->failure) {
1289                                real_message = g_strdup_printf (_("%s\nErase the RPM files?"), message);
1290                        } else {
1291                                real_message = g_strdup_printf (_("%s\nErase the leftover RPM files?"), message);
1292                        }
1293                }
1294                toplevel = gtk_widget_get_toplevel (view->details->message_box);
1295                if (GTK_IS_WINDOW (toplevel)) {
1296                        if (question_dialog) {
1297                                dialog = gnome_question_dialog_parented (real_message, (GnomeReplyCallback)reply_callback,
1298                                                                         &answer, GTK_WINDOW (toplevel));
1299                        } else {
1300                                dialog = gnome_ok_dialog_parented (real_message, GTK_WINDOW (toplevel));
1301                                answer = FALSE;
1302                        }
1303                } else {
1304                        if (question_dialog) {
1305                                dialog = gnome_question_dialog (real_message, (GnomeReplyCallback)reply_callback, &answer);
1306                        } else {
1307                                dialog = gnome_ok_dialog (real_message);
1308                                answer = FALSE;
1309                        }
1310                }
1311                gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1312                gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
1313                g_free (real_message);
1314
1315                if (answer) {
1316                        CORBA_exception_init (&ev);
1317                        eazel_install_callback_delete_files (cb, &ev);
1318                        CORBA_exception_free (&ev);
1319                }
1320               
1321                if (success && view->details->core_package) {
1322                        message = _("A core package of Nautilus has been\n"
1323                                    "updated.  You should restart Nautilus.\n\n"
1324                                    "Do you wish to do that now?");
1325                        if (GTK_IS_WINDOW (toplevel)) {
1326                                dialog = gnome_question_dialog_parented (message,
1327                                                                         (GnomeReplyCallback)reply_callback,
1328                                                                         &answer, GTK_WINDOW (toplevel));
1329                        } else {
1330                                dialog = gnome_question_dialog (message, (GnomeReplyCallback)reply_callback,
1331                                                                &answer);
1332                        }
1333                        gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1334                        gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
1335                       
1336                        if (answer) {
1337                                if (execlp ("nautilus", "nautilus", "--restart", NULL) < 0) {
1338                                        g_message ("Exec error %s", strerror (errno));
1339                                }
1340                        }
1341                }
1342
1343                /* send them to the predetermined "next" url
1344                 * -- but only if they haven't set jump-after-install off
1345                 */
1346                if (view->details->username!=NULL && strcasecmp (view->details->username, "anonymous") ==0) {
1347                        /* send anonymous users elsewhere, so they won't have to login */
1348                        message = g_strdup (NEXT_URL_ANONYMOUS);
1349                } else {
1350                        message = g_strdup (NEXT_URL);
1351                }
1352                if (eazel_install_configure_check_jump_after_install (&message)) {
1353                        nautilus_view_open_location_in_this_window (view->details->nautilus_view, message);
1354                }
1355                g_free (message);
1356        }
1357}
1358
1359static void
1360nautilus_service_install_failed (EazelInstallCallback *cb, const PackageData *package, NautilusServiceInstallView *view)
1361{
1362        char *tmp, *message;
1363
1364        g_assert (NAUTILUS_IS_SERVICE_INSTALL_VIEW (view));
1365
1366        /* override the "success" result for install_done signal */
1367        view->details->failure = TRUE;
1368
1369        if (package->status == PACKAGE_ALREADY_INSTALLED) {
1370                view->details->already_installed = TRUE;
1371                return;
1372        }
1373
1374        tmp = packagedata_get_readable_name (package);
1375        message = g_strdup_printf (_("Installation failed on %s"), tmp);
1376        show_overall_feedback (view, message);
1377        g_free (tmp);
1378        g_free (message);
1379
1380        /* Get the new set of problem cases */
1381        eazel_install_problem_tree_to_case (view->details->problem,
1382                                            package,
1383                                            FALSE,
1384                                            &(view->details->problem_cases));
1385
1386}
1387
1388/* signal callback -- ask the user for the root password (for installs) */
1389static char *
1390nautilus_service_need_password (GtkObject *object, const char *prompt, NautilusServiceInstallView *view)
1391{
1392        char *message = NULL;
1393        GtkWidget *dialog;
1394        gboolean okay;
1395        char *out;
1396
1397        if (view->details->remembered_password) {
1398                return g_strdup (view->details->remembered_password);
1399        }
1400
1401        if (view->details->password_attempts > 0) {
1402                message = _("Incorrect password.");
1403        }
1404
1405        dialog = nautilus_password_dialog_new ("Authenticate Me", message, prompt, "", TRUE);
1406        okay = nautilus_password_dialog_run_and_block (NAUTILUS_PASSWORD_DIALOG (dialog));
1407
1408        if (! okay) {
1409                /* cancel */
1410                view->details->password_attempts = 0;
1411                out = g_strdup ("");
1412        } else {
1413                out = nautilus_password_dialog_get_password (NAUTILUS_PASSWORD_DIALOG (dialog));
1414                if (nautilus_password_dialog_get_remember (NAUTILUS_PASSWORD_DIALOG (dialog))) {
1415                        view->details->remembered_password = g_strdup (out);
1416                }
1417        }
1418
1419        gtk_widget_destroy (dialog);
1420/*      gtk_main_iteration (); */
1421
1422        if (okay) {
1423                view->details->password_attempts++;
1424        }
1425
1426        return out;
1427}
1428
1429/* bad password -- let em try again? */
1430static gboolean
1431nautilus_service_try_again (GtkObject *object, NautilusServiceInstallView *view)
1432{
1433        if (view->details->password_attempts == 0) {
1434                /* user hit "cancel" */
1435                return FALSE;
1436        }
1437
1438        /* a wrong password shouldn't be remembered :) */
1439        g_free (view->details->remembered_password);
1440        view->details->remembered_password = NULL;
1441
1442        if (view->details->password_attempts >= 3) {
1443                /* give up. */
1444                view->details->password_attempts = 0;
1445                return FALSE;
1446        }
1447        return TRUE;
1448}
1449
1450static TrilobiteRootClient *
1451set_root_client (BonoboObjectClient *service, NautilusServiceInstallView *view)
1452{
1453        TrilobiteRootClient *root_client = NULL;
1454        CORBA_Environment ev;
1455
1456        CORBA_exception_init (&ev);
1457
1458        if (bonobo_object_client_has_interface (service, "IDL:Trilobite/PasswordQuery:1.0", &ev)) {
1459                root_client = trilobite_root_client_new ();
1460                if (! trilobite_root_client_attach (root_client, service)) {
1461                        g_warning ("unable to attach root client to Trilobite/PasswordQuery!");
1462                }
1463
1464                gtk_signal_connect (GTK_OBJECT (root_client), "need_password",
1465                                    GTK_SIGNAL_FUNC (nautilus_service_need_password),
1466                                    view);
1467                gtk_signal_connect (GTK_OBJECT (root_client), "try_again",
1468                                    GTK_SIGNAL_FUNC (nautilus_service_try_again),
1469                                    view);
1470        } else {
1471                g_warning ("Object does not support IDL:Trilobite/PasswordQuery:1.0");
1472        }
1473
1474        CORBA_exception_free (&ev);
1475        return root_client;
1476}
1477
1478
1479static void
1480nautilus_service_install_view_update_from_uri (NautilusServiceInstallView *view, const char *uri)
1481{
1482        PackageData             *pack;
1483        CategoryData            *category_data;
1484        char                    *host;
1485        int                     port;
1486        GNOME_Trilobite_Eazel_Install   service;
1487        CORBA_Environment       ev;
1488        char                    *out, *p;
1489        gboolean                set_auth;
1490
1491        /* get default host/port */
1492        host = g_strdup (trilobite_get_services_address ());
1493        if ((p = strchr (host, ':')) != NULL) {
1494                *p = 0;
1495                port = atoi (p+1);
1496        } else {
1497                port = 80;
1498        }
1499        view->details->username = g_strdup ("anonymous");
1500        set_auth = !(nautilus_install_parse_uri (uri, view, &host, &port, &view->details->username));
1501
1502        if (! view->details->categories) {
1503                return;
1504        }
1505
1506        /* NOTE: This adds a libeazelinstall packagedata object to the view */
1507        pack = (PackageData*) gtk_object_get_data (GTK_OBJECT (view), "packagedata");
1508        if (pack != NULL) {
1509                /* Destroy the old */
1510                packagedata_destroy (pack, TRUE);
1511        }
1512
1513        /* find the package data for the package we're about to install */
1514        category_data = (CategoryData *) view->details->categories->data;
1515        pack = (PackageData *) category_data->packages->data;
1516
1517        gtk_object_set_data (GTK_OBJECT (view), "packagedata", pack);
1518
1519        if (pack->eazel_id != NULL) {
1520                out = g_strdup_printf (_("Downloading remote package"));
1521        } else if (pack->name != NULL) {
1522                out = g_strdup_printf (_("Downloading \"%s\""), pack->name);
1523        } else {
1524                out = g_strdup_printf (_("Downloading some package"));
1525        }
1526        nautilus_label_set_text (NAUTILUS_LABEL (view->details->package_name), out);
1527        g_free (out);
1528
1529        CORBA_exception_init (&ev);
1530        if (view->details->installer) {
1531                eazel_install_callback_unref (GTK_OBJECT (view->details->installer));
1532        }
1533        view->details->installer = eazel_install_callback_new ();
1534        if (view->details->installer == NULL) {
1535                GtkWidget *toplevel, *dialog;
1536                char *message;
1537
1538                nautilus_view_report_load_complete (view->details->nautilus_view);
1539                gtk_widget_hide (view->details->form);
1540
1541                message = g_strdup (_("The Eazel install service is missing:\nInstalls will not work."));
1542                toplevel = gtk_widget_get_toplevel (view->details->message_box);
1543                if (GTK_IS_WINDOW (toplevel)) {
1544                        dialog = gnome_error_dialog_parented (message, GTK_WINDOW (toplevel));
1545                } else {
1546                        dialog = gnome_error_dialog (message);
1547                }
1548                gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1549                gnome_dialog_run_and_close (GNOME_DIALOG (dialog));
1550                return;
1551        }
1552
1553        view->details->problem = eazel_install_problem_new ();
1554        view->details->root_client = set_root_client (eazel_install_callback_bonobo (view->details->installer), view);
1555        service = eazel_install_callback_corba_objref (view->details->installer);
1556        GNOME_Trilobite_Eazel_Install__set_protocol (service, GNOME_Trilobite_Eazel_PROTOCOL_HTTP, &ev);
1557        GNOME_Trilobite_Eazel_Install__set_server (service, host, &ev);
1558        GNOME_Trilobite_Eazel_Install__set_server_port (service, port, &ev);
1559        GNOME_Trilobite_Eazel_Install__set_auth (service, set_auth, &ev);
1560
1561        if (view->details->username != NULL) {
1562                GNOME_Trilobite_Eazel_Install__set_username (service, view->details->username, &ev);
1563        }
1564        GNOME_Trilobite_Eazel_Install__set_test_mode (service, FALSE, &ev);
1565
1566        gtk_signal_connect (GTK_OBJECT (view->details->installer), "download_progress",
1567                            GTK_SIGNAL_FUNC (nautilus_service_install_downloading), view);
1568        gtk_signal_connect (GTK_OBJECT (view->details->installer), "download_failed",
1569                            nautilus_service_install_download_failed, view);
1570        gtk_signal_connect (GTK_OBJECT (view->details->installer), "dependency_check",
1571                            nautilus_service_install_dependency_check, view);
1572        gtk_signal_connect (GTK_OBJECT (view->details->installer), "preflight_check",
1573                            GTK_SIGNAL_FUNC (nautilus_service_install_preflight_check), view);
1574        gtk_signal_connect (GTK_OBJECT (view->details->installer), "install_progress",
1575                            nautilus_service_install_installing, view);
1576        gtk_signal_connect (GTK_OBJECT (view->details->installer), "install_failed",
1577                            nautilus_service_install_failed, view);
1578        gtk_signal_connect (GTK_OBJECT (view->details->installer), "done",
1579                            nautilus_service_install_done, view);
1580        eazel_install_callback_install_packages (view->details->installer, 
1581                                                 view->details->categories, 
1582                                                 NULL, &ev);
1583
1584        CORBA_exception_free (&ev);
1585
1586        show_overall_feedback (view, _("Contacting the software catalog ..."));
1587        /* let the throbber spin... */
1588}
1589
1590void
1591nautilus_service_install_view_load_uri (NautilusServiceInstallView      *view,
1592                                        const char                      *uri)
1593{
1594
1595        /* dispose of any old uri and copy in the new one */   
1596        g_free (view->details->uri);
1597        view->details->uri = g_strdup (uri);
1598
1599        /* dispose of any old form that was installed */
1600        if (view->details->form != NULL) {
1601                gtk_widget_destroy (view->details->form);
1602                view->details->form = NULL;
1603        }
1604        if (view->details->message) {
1605                g_list_foreach (view->details->message, (GFunc)install_message_destroy, NULL);
1606                g_list_free (view->details->message);
1607                view->details->message = NULL;
1608        }
1609        if (view->details->desktop_files) {
1610                g_list_foreach (view->details->desktop_files, (GFunc)g_free, NULL);
1611                g_list_free (view->details->desktop_files);
1612                view->details->desktop_files = NULL;
1613        }
1614
1615        /* clear some variables */
1616        view->details->already_installed = FALSE;
1617        view->details->cancelled = FALSE;
1618        view->details->failure = FALSE;
1619
1620        generate_install_form (view);
1621
1622        nautilus_view_report_load_underway (NAUTILUS_VIEW (view->details->nautilus_view));
1623        nautilus_service_install_view_update_from_uri (view, uri);
1624}
1625
1626static void
1627service_install_load_location_callback (NautilusView                    *nautilus_view, 
1628                                        const char                      *location,
1629                                        NautilusServiceInstallView      *view)
1630{
1631
1632        g_assert (nautilus_view == view->details->nautilus_view);
1633       
1634        nautilus_view_report_load_underway (nautilus_view);
1635       
1636        nautilus_service_install_view_load_uri (view, location);
1637}
1638
1639static void
1640service_install_stop_loading_callback (NautilusView *nautilus_view, NautilusServiceInstallView *view)
1641{
1642        GNOME_Trilobite_Eazel_Install   service;
1643        CORBA_Environment ev;
1644
1645        view->details->cancelled = TRUE;
1646        show_overall_feedback (view, _("Aborting package downloads..."));
1647        while (gtk_events_pending ()) {
1648                gtk_main_iteration ();
1649        }
1650        /* have to set these up here, because if they hit STOP before any downloads have started, the
1651         * call to _stop below will freeze until we get the preflight signal later.
1652         */
1653
1654        g_assert (nautilus_view == view->details->nautilus_view);
1655
1656        CORBA_exception_init (&ev);
1657        service = eazel_install_callback_corba_objref (view->details->installer);
1658        GNOME_Trilobite_Eazel_Install_stop (service, &ev);
1659        CORBA_exception_free (&ev);
1660
1661        show_overall_feedback (view, _("Package download aborted."));
1662        current_progress_bar_complete (view, _("Aborted"));
1663}
Note: See TracBrowser for help on using the browser.