/**
* operation: The operation to process
* message: The message that will be pushed to the status bar
* fract: The fraction of the progress bar to fill
* swidget: The SeahorseWidget to extract the gtk widgets from
*
* This gets called whenever an operation updates it's progress status thingy.
* We update the appbar as appropriate. If operation != NULL then we only
* display the latest operation in our special list
*
**/
static void
operation_progress (SeahorseOperation *operation, const gchar *message, 
                    gdouble fract, SeahorseWidget *swidget)
{
    GtkProgressBar *progress;
    GtkStatusbar *status;
    guint id; 
    
    progress = GTK_PROGRESS_BAR (seahorse_widget_get_widget (swidget, "progress"));
    status = GTK_STATUSBAR (seahorse_widget_get_widget (swidget, "status"));
    
    if (!seahorse_operation_is_running (operation))
        fract = 0.0;
    
    if (message != NULL && status) {
        g_return_if_fail (GTK_IS_STATUSBAR (status));
        id = gtk_statusbar_get_context_id (status, "operation-progress");
        gtk_statusbar_pop (status, id);
        if (message[0])
            gtk_statusbar_push (status, id, message);
    }

    if(progress) {
        g_return_if_fail (GTK_IS_PROGRESS_BAR (progress));
        if (fract >= 0.0) {
            stop_pulse (progress);
            gtk_progress_bar_set_fraction (progress, fract);        
        } else { 
            start_pulse (progress);
        }
    }
}
static int
progress_main (int argc, char* argv[])
{
    SeahorseOperation *op;
    GIOChannel *io;

    gtk_init (&argc, &argv);

    op = g_object_new (SEAHORSE_TYPE_OPERATION, NULL);
    seahorse_operation_mark_start (op);

    /* Watch for done (ie: in this case only cancel) */
    g_signal_connect (op, "done", G_CALLBACK (done_handler), NULL);

    /* Hook up an IO channel to stdin */
    io = g_io_channel_unix_new (0);
    g_io_channel_set_encoding (io, NULL, NULL);
    g_io_add_watch (io, G_IO_IN | G_IO_HUP, (GIOFunc)io_handler, op);

    progress_visible = FALSE;
    progress_title = argc > 2 ? argv[2] : NULL;
    g_timeout_add (PROGRESS_DELAY, (GSourceFunc)progress_show, op);

    gtk_main ();

    if (seahorse_operation_is_running (op))
        seahorse_operation_mark_done (op, FALSE, NULL);

    return 0;
}
/* dispose of all our internal references */
static void
seahorse_server_source_dispose (GObject *gobject)
{
    SeahorseServerSource *ssrc;
    SeahorseSource *sksrc;
    
    /*
     * Note that after this executes the rest of the object should
     * still work without a segfault. This basically nullifies the 
     * object, but doesn't free it.
     * 
     * This function should also be able to run multiple times.
     */
  
    ssrc = SEAHORSE_SERVER_SOURCE (gobject);
    sksrc = SEAHORSE_SOURCE (gobject);
    g_assert (ssrc->priv);
    
    /* Clear out all the operations */
    if (ssrc->priv->mop) {
        if(seahorse_operation_is_running (SEAHORSE_OPERATION (ssrc->priv->mop)))
            seahorse_operation_cancel (SEAHORSE_OPERATION (ssrc->priv->mop));
        g_object_unref (ssrc->priv->mop);
        ssrc->priv->mop = NULL;
    }
 
    G_OBJECT_CLASS (parent_class)->dispose (gobject);
}
/**
* operation: The operation to create a new progress window for
*
* Creates a new progress window and adds the operation to it.
*
* Returns FALSE
**/
static gboolean
progress_show (SeahorseOperation *operation)
{
    SeahorseWidget *swidget;
    GtkWidget *w;
    const gchar *title;
    gchar *t;

    if (!seahorse_operation_is_running (operation)) {
        /* Matches the ref in seahorse_progress_show */
        g_object_unref (operation);
        return FALSE;
    }
        
    swidget = seahorse_widget_new_allow_multiple ("progress", NULL);
    g_return_val_if_fail (swidget != NULL, FALSE);

    /* Release our reference on the operation when this window is destroyed */    
    g_object_set_data_full (G_OBJECT (swidget), "operation", operation, 
                            (GDestroyNotify)g_object_unref);    

    w = GTK_WIDGET (seahorse_widget_get_widget (swidget, swidget->name));
    gtk_window_move (GTK_WINDOW (w), 10, 10);

    /* Setup the title */
    title = (const gchar*)g_object_get_data (G_OBJECT (operation), "progress-title");
    if (title) {
            
        /* The window title */
        w = GTK_WIDGET (seahorse_widget_get_widget (swidget, swidget->name));
        g_return_val_if_fail (w != NULL, FALSE);
        gtk_window_set_title (GTK_WINDOW (w), title);
            
        /* The main message title */
        w = GTK_WIDGET (seahorse_widget_get_widget (swidget, "operation-title"));
        g_return_val_if_fail (w != NULL, FALSE);
        t = g_strdup_printf ("<b>%s</b>", title);
        gtk_label_set_markup (GTK_LABEL (w), t);
        g_free (t);
    }

    /* The details */        
    progress_operation_update (operation, NULL, 
                                seahorse_operation_get_progress (operation), swidget);
    g_signal_connect (operation, "progress", 
                      G_CALLBACK (progress_operation_update), swidget);

    /* Cancel events */
    g_signal_connect (seahorse_widget_get_toplevel (swidget), "delete_event",
                      G_CALLBACK (progress_delete_event), operation);
    
    /* Done and cleanup */
    w = GTK_WIDGET (seahorse_widget_get_widget (swidget, swidget->name));
    g_signal_connect (w, "destroy", G_CALLBACK (progress_destroy), operation);
    g_signal_connect (operation, "done", G_CALLBACK (progress_operation_done), swidget);

    return FALSE;
}
/**
 * seahorse_progress_status_set_operation:
 * @swidget: The SeahorseWidget to add the operation to
 * @operation: The operation to add
 *
 * Adds the operation to the widget.
 *
 */
void 
seahorse_progress_status_set_operation (SeahorseWidget *swidget, 
                                        SeahorseOperation *operation)
{
    SeahorseOperation *prev;
    
    /* 
     * Note that this is not one off, the operation is monitored until it is 
     * replaced, so if the operation starts up again the progress will be 
     * displayed 
     */
    
    g_return_if_fail (SEAHORSE_IS_WIDGET (swidget));
    g_return_if_fail (SEAHORSE_IS_OPERATION (operation));
    
    prev = SEAHORSE_OPERATION (g_object_get_data (G_OBJECT (swidget), "operation"));
    if (prev) {
	    
        /* If it's the same operation, just ignore */
        if (prev == operation)
            return;
        
        /* If the previous one was a multi operation, just piggy back this one in */
        if (SEAHORSE_IS_MULTI_OPERATION (prev)) {
       	    g_object_ref (operation);
            seahorse_multi_operation_take (SEAHORSE_MULTI_OPERATION (prev), operation);
            return;
        }

        /* Otherwise disconnect old progress, replace with new */
        disconnect_progress (swidget, prev);
    }
        
    g_object_ref (operation);
    g_object_set_data_full (G_OBJECT (swidget), "operation", operation, 
                            (GDestroyNotify)g_object_unref);
    g_signal_connect (swidget, "destroy", 
                      G_CALLBACK (disconnect_progress), operation);
    
    if (!seahorse_operation_is_running (operation))
        operation_done (operation, swidget);

    operation_progress (operation, seahorse_operation_get_message (operation),
                        seahorse_operation_get_progress (operation), swidget);
    
    g_signal_connect (operation, "done", G_CALLBACK (operation_done), swidget);
    g_signal_connect (operation, "progress", G_CALLBACK (operation_progress), swidget);
}
static void 
seahorse_hkp_operation_cancel (SeahorseOperation *operation)
{
    SeahorseHKPOperation *hop;
    
    g_assert (SEAHORSE_IS_HKP_OPERATION (operation));
    hop = SEAHORSE_HKP_OPERATION (operation);
    
    g_return_if_fail (seahorse_operation_is_running (operation));
    hop->cancelling = TRUE;
    
    if (hop->session != NULL) 
        soup_session_abort (hop->session);
    
    seahorse_operation_mark_done (operation, TRUE, NULL);
}
/* Cancels operation and marks the HKP operation as failed */
static void
fail_hkp_operation (SeahorseHKPOperation *hop, SoupMessage *msg, const gchar *text)
{
    gchar *t, *server;
    GError *error = NULL;
    
    if (!seahorse_operation_is_running (SEAHORSE_OPERATION (hop)))
        return;

    g_object_get (hop->hsrc, "key-server", &server, NULL);

    if (text) {
        error = g_error_new (HKP_ERROR_DOMAIN, msg ? msg->status_code : 0, "%s", text);

    } else if (msg) {
        /* Make the body lower case, and no tags */
        t = g_strndup (msg->response_body->data, msg->response_body->length);
        if (t != NULL) {
            dehtmlize (t);        
            seahorse_util_string_lower (t);
        }

        if (t && strstr (t, "no keys"))
            error = NULL; /* not found is not an error */
        else if (t && strstr (t, "too many"))
            error = g_error_new (HKP_ERROR_DOMAIN, 0, _("Search was not specific enough. Server '%s' found too many keys."), server);
        else 
            error = g_error_new (HKP_ERROR_DOMAIN, msg->status_code, _("Couldn't communicate with server '%s': %s"),
                                 server, msg->reason_phrase);
        g_free (t);
    } else {

        /* We should always have msg or text */
        g_assert (FALSE);
    }        

    seahorse_operation_mark_done (SEAHORSE_OPERATION (hop), FALSE, error);
    g_free (server);
}
static gboolean
step_operation (FilesCtx *ctx,
                SeahorseToolMode *mode,
                GError **err)
{
    SeahorsePGPOperation *pop = NULL;
    gpgme_data_t data = NULL;
    gboolean ret = FALSE;

    SeahorseOperation *op;
    FileInfo *finfo;
    GList *l;
    gchar *filename;

    /* Reset our done counter */
    ctx->done = 0;

    for (l = ctx->finfos; l; l = g_list_next (l)) {

        finfo = (FileInfo*)l->data;
        if (!finfo || !finfo->file)
            continue;

        ctx->cur = finfo;

        /* A new operation for each context */
        pop = seahorse_pgp_operation_new (NULL);
        op = SEAHORSE_OPERATION (pop);

        data = seahorse_vfs_data_create_full (finfo->file, SEAHORSE_VFS_READ,
                                              (SeahorseVfsProgressCb)progress_cb,
                                              ctx, err);
        if (!data)
            goto finally;

        /* Inhibit popping up of progress dialog */
        seahorse_tool_progress_block (TRUE);

        /* Embed filename during encryption */
        if (mode_encrypt)
        {
            filename = g_file_get_basename (finfo->file);
            gpgme_data_set_file_name (data, filename);
            g_free (filename);
        }

        /* The start callback */
        if (mode->startcb) {
            if (!(mode->startcb) (mode, finfo->uri, data, pop, err))
                goto finally;
        }

        /* Let progress dialog pop up */
        seahorse_tool_progress_block (FALSE);

        /* Run until the operation completes */
        seahorse_util_wait_until ((!seahorse_operation_is_running (op) ||
                                   !seahorse_tool_progress_check ()));

        /* If cancel then reflect that */
        if (seahorse_operation_is_running (op)) {
            seahorse_operation_cancel (op);
            goto finally;
        }

        if (!seahorse_operation_is_successful (op)) {
            seahorse_operation_copy_error (op, err);
            goto finally;
        }

        /* The done callback */
        if (mode->donecb) {
            if (!(mode->donecb) (mode, finfo->uri, data, pop, err))
                goto finally;
        }

        ctx->done += g_file_info_get_size (finfo->info);
        ctx->cur = NULL;

        g_object_unref (pop);
        pop = NULL;

        gpgme_data_release (data);
        data = NULL;
    }

    seahorse_tool_progress_update (1.0, "");
    ret = TRUE;

finally:
    if (pop)
        g_object_unref (pop);
    if (data)
        gpgme_data_release (data);

    return ret;
}
/**
 * on_progress_operation_cancel:
 * @button: ignored
 * @operation: The operation to cancel
 *
 * Cancels an operation
 *
 */
G_MODULE_EXPORT void
on_progress_operation_cancel (GtkButton *button, SeahorseOperation *operation)
{
    if (seahorse_operation_is_running (operation))
        seahorse_operation_cancel (operation);
}