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 @@
-
+
-
+
+
+
+
+
+
+
+
+
600
500
@@ -39,9 +180,125 @@
+ vertical
+
+
+ False
+
+
+ False
+ False
+ 0
+
+
+
+
+ True
+ False
+ 0
+ vertical
+ 12
+
+
+ False
+ 12
+ 6
+ end
+ 2
+
+
+
+ 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);