diff --git a/panels/printers/jobs-dialog.ui b/panels/printers/jobs-dialog.ui index 579b58f0e..0542538ad 100644 --- a/panels/printers/jobs-dialog.ui +++ b/panels/printers/jobs-dialog.ui @@ -1,7 +1,148 @@ - + - + + + False + start + start + + + True + False + 30 + 30 + 20 + 20 + vertical + 20 + + + True + False + 5 + 5 + Enter credentials to print from %s. + + + False + True + 0 + + + + + True + False + 10 + 15 + + + True + False + start + Domain + 1 + + + 0 + 0 + + + + + True + False + start + Username + 1 + + + 0 + 1 + + + + + True + False + start + Password + 1 + + + 0 + 2 + + + + + True + True + True + + + 1 + 0 + + + + + True + True + True + + + 1 + 1 + + + + + True + True + True + False + * + password + + + 1 + 2 + + + + + False + True + 1 + + + + + A_uthenticate + True + True + True + True + end + + + False + True + 2 + + + + + + + + + + + + 600 500 @@ -39,9 +180,125 @@ + vertical + + + False + + + False + False + 0 + + + + + True + False + 0 + vertical + 12 + + + False + 12 + 6 + end + 2 + + + True + True + False + end + authentication_popover + + + True + False + 5 + + + True + False + True + _Authenticate + + + False + True + 0 + + + + + True + False + down + + + False + True + 1 + + + + + + + True + True + 0 + + + + + False + False + end + 0 + + + + + False + 16 + 12 + + + True + False + start + False + 2 Jobs Require Authentication + + + + + + False + True + 0 + + + + + False + False + 0 + + + + + False + True + 0 + + True + False True @@ -93,23 +350,35 @@ True + False No Active Printer Jobs + False + True 1 no-jobs-page + 1 + + False + True + 2 + + + + diff --git a/panels/printers/pp-jobs-dialog.c b/panels/printers/pp-jobs-dialog.c index 6971256c4..7b2993114 100644 --- a/panels/printers/pp-jobs-dialog.c +++ b/panels/printers/pp-jobs-dialog.c @@ -58,9 +58,141 @@ struct _PpJobsDialog { gchar *printer_name; + gchar **actual_auth_info_required; + GCancellable *get_jobs_cancellable; }; +static gboolean +is_info_required (PpJobsDialog *dialog, + const gchar *info) +{ + gboolean required = FALSE; + gint i; + + if (dialog != NULL && dialog->actual_auth_info_required != NULL) + { + for (i = 0; dialog->actual_auth_info_required[i] != NULL; i++) + { + if (g_strcmp0 (dialog->actual_auth_info_required[i], info) == 0) + { + required = TRUE; + break; + } + } + } + + return required; +} + +static gboolean +is_domain_required (PpJobsDialog *dialog) +{ + return is_info_required (dialog, "domain"); +} + +static gboolean +is_username_required (PpJobsDialog *dialog) +{ + return is_info_required (dialog, "username"); +} + +static gboolean +is_password_required (PpJobsDialog *dialog) +{ + return is_info_required (dialog, "password"); +} + +static gboolean +auth_popup_filled (PpJobsDialog *dialog) +{ + gboolean domain_required; + gboolean username_required; + gboolean password_required; + guint16 domain_length; + guint16 username_length; + guint16 password_length; + + domain_required = is_domain_required (dialog); + username_required = is_username_required (dialog); + password_required = is_password_required (dialog); + + domain_length = gtk_entry_get_text_length (GTK_ENTRY (gtk_builder_get_object (dialog->builder, "domain-entry"))); + username_length = gtk_entry_get_text_length (GTK_ENTRY (gtk_builder_get_object (dialog->builder, "username-entry"))); + password_length = gtk_entry_get_text_length (GTK_ENTRY (gtk_builder_get_object (dialog->builder, "password-entry"))); + + return (!domain_required || domain_length > 0) && + (!username_required || username_length > 0) && + (!password_required || password_length > 0); +} + +static void +auth_entries_changed (GtkEntry *entry, + PpJobsDialog *dialog) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "authenticate-button")); + gtk_widget_set_sensitive (widget, auth_popup_filled (dialog)); +} + +static void +auth_entries_activated (GtkEntry *entry, + PpJobsDialog *dialog) +{ + GtkWidget *widget; + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "authenticate-button")); + if (auth_popup_filled (dialog)) + gtk_button_clicked (GTK_BUTTON (widget)); +} + +static void +authenticate_popover_update (PpJobsDialog *dialog) +{ + GtkWidget *widget; + gboolean domain_required; + gboolean username_required; + gboolean password_required; + + domain_required = is_domain_required (dialog); + username_required = is_username_required (dialog); + password_required = is_password_required (dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "domain-label")); + gtk_widget_set_visible (widget, domain_required); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "domain-entry")); + gtk_widget_set_visible (widget, domain_required); + if (domain_required) + { + gtk_entry_set_text (GTK_ENTRY (widget), ""); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (widget)); + } + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "username-label")); + gtk_widget_set_visible (widget, username_required); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "username-entry")); + gtk_widget_set_visible (widget, username_required); + if (username_required) + { + gtk_entry_set_text (GTK_ENTRY (widget), cupsUser ()); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (widget)); + } + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "password-label")); + gtk_widget_set_visible (widget, password_required); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "password-entry")); + gtk_widget_set_visible (widget, password_required); + if (password_required) + { + gtk_entry_set_text (GTK_ENTRY (widget), ""); + gtk_entry_grab_focus_without_selecting (GTK_ENTRY (widget)); + } + + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authenticate-button")); + gtk_widget_set_sensitive (widget, FALSE); +} + static void job_stop_cb (GtkButton *button, PpJob *job) @@ -88,14 +220,19 @@ static GtkWidget * create_listbox_row (gpointer item, gpointer user_data) { - PpJob *job = (PpJob *)item; - GtkWidget *box; - GtkWidget *widget; - gchar *title; - gchar *state_string = NULL; - gint job_state; + GtkWidget *widget; + GtkWidget *box; + PpJob *job = (PpJob *)item; + gchar **auth_info_required; + gchar *title; + gchar *state_string = NULL; + gint job_state; - g_object_get (job, "title", &title, "state", &job_state, NULL); + g_object_get (job, + "title", &title, + "state", &job_state, + "auth-info-required", &auth_info_required, + NULL); switch (job_state) { @@ -104,8 +241,16 @@ create_listbox_row (gpointer item, state_string = g_strdup (C_("print job", "Pending")); break; case IPP_JOB_HELD: - /* Translators: Job's state (job is held for printing) */ - state_string = g_strdup (C_("print job", "Paused")); + if (auth_info_required == NULL) + { + /* Translators: Job's state (job is held for printing) */ + state_string = g_strdup (C_("print job", "Paused")); + } + else + { + /* Translators: Job's state (job needs authentication to proceed further) */ + state_string = g_strdup_printf ("%s", C_("print job", "Authentication required")); + } break; case IPP_JOB_PROCESSING: /* Translators: Job's state (job is currently printing) */ @@ -137,14 +282,17 @@ create_listbox_row (gpointer item, gtk_widget_set_halign (widget, GTK_ALIGN_START); gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 10); - widget = gtk_label_new (state_string); + widget = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (widget), state_string); gtk_widget_set_halign (widget, GTK_ALIGN_END); gtk_widget_set_margin_end (widget, 64); + gtk_widget_set_margin_start (widget, 64); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 10); widget = gtk_button_new_from_icon_name (job_state == IPP_JOB_HELD ? "media-playback-start-symbolic" : "media-playback-pause-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); g_signal_connect (widget, "clicked", G_CALLBACK (job_pause_cb), item); + gtk_widget_set_sensitive (widget, auth_info_required == NULL); gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 4); widget = gtk_button_new_from_icon_name ("edit-delete-symbolic", @@ -162,13 +310,18 @@ update_jobs_list_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { - PpJobsDialog *dialog = user_data; - PpPrinter *printer = PP_PRINTER (source_object); - GtkWidget *clear_all_button; - GtkStack *stack; - GError *error = NULL; - GList *jobs, *l; - gint num_of_jobs; + PpJobsDialog *dialog = user_data; + PpPrinter *printer = PP_PRINTER (source_object); + GtkWidget *clear_all_button; + GtkWidget *infobar; + GtkWidget *label; + GtkStack *stack; + GError *error = NULL; + GList *jobs, *l; + PpJob *job; + gchar **auth_info_required = NULL; + gchar *text; + gint num_of_jobs, num_of_auth_jobs = 0; g_list_store_remove_all (dialog->store); @@ -201,9 +354,45 @@ update_jobs_list_cb (GObject *source_object, for (l = jobs; l != NULL; l = l->next) { - g_list_store_append (dialog->store, l->data); + job = PP_JOB (l->data); + + g_list_store_append (dialog->store, job); + + g_object_get (G_OBJECT (job), + "auth-info-required", &auth_info_required, + NULL); + if (auth_info_required != NULL) + { + num_of_auth_jobs++; + + if (dialog->actual_auth_info_required == NULL) + dialog->actual_auth_info_required = auth_info_required; + else + g_strfreev (auth_info_required); + + auth_info_required = NULL; + } } + infobar = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authentication-infobar")); + if (num_of_auth_jobs > 0) + { + label = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authenticate-jobs-label")); + + /* Translators: This label shows how many jobs of this printer needs to be authenticated to be printed. */ + text = g_strdup_printf (ngettext ("%u Job Requires Authentication", "%u Jobs Require Authentication", num_of_auth_jobs), num_of_auth_jobs); + gtk_label_set_text (GTK_LABEL (label), text); + g_free (text); + + gtk_widget_show (infobar); + } + else + { + gtk_widget_hide (infobar); + } + + authenticate_popover_update (dialog); + g_list_free (jobs); g_clear_object (&dialog->get_jobs_cancellable); } @@ -265,6 +454,78 @@ on_clear_all_button_clicked (GtkButton *button, } } +static void +pp_job_authenticate_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PpJobsDialog *dialog = user_data; + gboolean result; + GError *error = NULL; + PpJob *job = PP_JOB (source_object); + + result = pp_job_authenticate_finish (job, res, &error); + if (result) + { + pp_jobs_dialog_update (dialog); + } + else if (error != NULL) + { + if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_warning ("Could not authenticate job: %s", error->message); + } + + g_error_free (error); + } +} + +static void +authenticate_button_clicked (GtkWidget *button, + gpointer user_data) +{ + PpJobsDialog *dialog = user_data; + GtkWidget *widget; + PpJob *job; + gchar **auth_info_required = NULL; + gchar **auth_info; + guint num_items; + gint i; + + auth_info = g_new0 (gchar *, g_strv_length (dialog->actual_auth_info_required) + 1); + for (i = 0; dialog->actual_auth_info_required[i] != NULL; i++) + { + if (g_strcmp0 (dialog->actual_auth_info_required[i], "domain") == 0) + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "domain-entry")); + else if (g_strcmp0 (dialog->actual_auth_info_required[i], "username") == 0) + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "username-entry")); + else if (g_strcmp0 (dialog->actual_auth_info_required[i], "password") == 0) + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "password-entry")); + else + widget = NULL; + + if (widget != NULL) + auth_info[i] = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget))); + } + + num_items = g_list_model_get_n_items (G_LIST_MODEL (dialog->store)); + for (i = 0; i < num_items; i++) + { + job = PP_JOB (g_list_model_get_item (G_LIST_MODEL (dialog->store), i)); + + g_object_get (job, "auth-info-required", &auth_info_required, NULL); + if (auth_info_required != NULL) + { + pp_job_authenticate_async (job, auth_info, NULL, pp_job_authenticate_cb, dialog); + + g_strfreev (auth_info_required); + auth_info_required = NULL; + } + } + + g_strfreev (auth_info); +} + PpJobsDialog * pp_jobs_dialog_new (GtkWindow *parent, UserResponseCallback user_callback, @@ -272,9 +533,10 @@ pp_jobs_dialog_new (GtkWindow *parent, gchar *printer_name) { PpJobsDialog *dialog; - GtkButton *clear_all_button; + GtkWidget *widget; GError *error = NULL; - gchar *objects[] = { "jobs-dialog", NULL }; + gchar *objects[] = { "jobs-dialog", "authentication_popover", NULL }; + gchar *text; guint builder_result; gchar *title; @@ -298,19 +560,41 @@ pp_jobs_dialog_new (GtkWindow *parent, dialog->user_callback = user_callback; dialog->user_data = user_data; dialog->printer_name = g_strdup (printer_name); + dialog->actual_auth_info_required = NULL; /* connect signals */ g_signal_connect (dialog->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL); g_signal_connect (dialog->dialog, "response", G_CALLBACK (jobs_dialog_response_cb), dialog); - clear_all_button = GTK_BUTTON (gtk_builder_get_object (dialog->builder, "jobs-clear-all-button")); - g_signal_connect (clear_all_button, "clicked", G_CALLBACK (on_clear_all_button_clicked), dialog); + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "jobs-clear-all-button")); + g_signal_connect (widget, "clicked", G_CALLBACK (on_clear_all_button_clicked), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "authenticate-button")); + g_signal_connect (widget, "clicked", G_CALLBACK (authenticate_button_clicked), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "domain-entry")); + g_signal_connect (widget, "changed", G_CALLBACK (auth_entries_changed), dialog); + g_signal_connect (widget, "activate", G_CALLBACK (auth_entries_activated), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "username-entry")); + g_signal_connect (widget, "changed", G_CALLBACK (auth_entries_changed), dialog); + g_signal_connect (widget, "activate", G_CALLBACK (auth_entries_activated), dialog); + + widget = GTK_WIDGET (gtk_builder_get_object (dialog->builder, "password-entry")); + g_signal_connect (widget, "changed", G_CALLBACK (auth_entries_changed), dialog); + g_signal_connect (widget, "activate", G_CALLBACK (auth_entries_activated), dialog); /* Translators: This is the printer name for which we are showing the active jobs */ title = g_strdup_printf (C_("Printer jobs dialog title", "%s — Active Jobs"), printer_name); gtk_window_set_title (GTK_WINDOW (dialog->dialog), title); g_free (title); + /* Translators: The printer needs authentication info to print. */ + text = g_strdup_printf (_("Enter credentials to print from %s."), printer_name); + widget = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "authentication-label")); + gtk_label_set_text (GTK_LABEL (widget), text); + g_free (text); + dialog->listbox = GTK_LIST_BOX (gtk_builder_get_object (dialog->builder, "jobs-listbox")); gtk_list_box_set_header_func (dialog->listbox, cc_list_box_update_header_func, NULL, NULL); @@ -357,6 +641,8 @@ pp_jobs_dialog_free (PpJobsDialog *dialog) gtk_widget_destroy (GTK_WIDGET (dialog->dialog)); dialog->dialog = NULL; + g_strfreev (dialog->actual_auth_info_required); + g_clear_object (&dialog->builder); g_free (dialog->printer_name); g_free (dialog);