printers: Update GtkListBox of printers consecutively

Goal of this commit is to not overwhelm UI when a lot of printers is added
to CUPS. You can reproduce this situation when you add e.g. 30 printers using
lpadmin. Th UI stops to respond for some time.

To do so, the printer entries are not deleted every time there is a request
for updating of the list of printers but only new printer entries are added
and printer entries of deleted printers are removed. Other printer
entries are just updated by new method pp_printer_entry_update().
Which, by the way, is almost whole taken from pp_printer_entry_new().

This needed to add sort function for the list of printer entries. It sorts
printers according to their names not taking case into account.

In the similar manner, the filter function was extended to not show printers
which are being deleted. This needs a list of names of deleted printers
which we keep until they are really deleted.
One important thing here is the "reference" object which points to the panel
itself via its "self" key. We pass this object to the pp_printer_delete_async()'s
callback so it knows whether it can remove the printer's name from the list
of deleted printers (once the panel is being destroyed it clears the key itself).
This commit is contained in:
Marek Kasik 2020-06-04 19:53:13 +02:00 committed by Felipe Borges
parent 2b0257a9b4
commit dfbe1faea2
3 changed files with 240 additions and 65 deletions

View file

@ -101,6 +101,8 @@ struct _CcPrintersPanel
gchar *renamed_printer_name;
gchar *old_printer_name;
gchar *deleted_printer_name;
GList *deleted_printers;
GObject *reference;
GHashTable *printer_entries;
gboolean entries_filled;
@ -252,11 +254,37 @@ printer_removed_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
PpPrinter *printer = PP_PRINTER (source_object);
g_autoptr(GError) error = NULL;
g_autofree gchar *printer_name = NULL;
pp_printer_delete_finish (PP_PRINTER (source_object), result, &error);
g_object_get (printer, "printer-name", &printer_name, NULL);
pp_printer_delete_finish (printer, result, &error);
g_object_unref (source_object);
if (user_data != NULL)
{
GObject *reference = G_OBJECT (user_data);
if (g_object_get_data (reference, "self") != NULL)
{
CcPrintersPanel *self = CC_PRINTERS_PANEL (g_object_get_data (reference, "self"));
GList *iter;
for (iter = self->deleted_printers; iter != NULL; iter = iter->next)
{
if (g_strcmp0 (iter->data, printer_name) == 0)
{
g_free (iter->data);
self->deleted_printers = g_list_delete_link (self->deleted_printers, iter);
break;
}
}
}
g_object_unref (reference);
}
if (error != NULL)
g_warning ("Printer could not be deleted: %s", error->message);
}
@ -293,6 +321,10 @@ cc_printers_panel_dispose (GObject *object)
g_clear_pointer (&self->printer_entries, g_hash_table_destroy);
g_clear_pointer (&self->all_ppds_list, ppd_list_free);
free_dests (self);
g_list_free_full (self->deleted_printers, g_free);
self->deleted_printers = NULL;
g_object_set_data (self->reference, "self", NULL);
g_clear_object (&self->reference);
G_OBJECT_CLASS (cc_printers_panel_parent_class)->dispose (object);
}
@ -616,10 +648,14 @@ free_dests (CcPrintersPanel *self)
static void
on_printer_deletion_undone (CcPrintersPanel *self)
{
GtkWidget *widget;
gtk_revealer_set_reveal_child (self->notification, FALSE);
g_clear_pointer (&self->deleted_printer_name, g_free);
actualize_printers_list (self);
widget = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
gtk_list_box_invalidate_filter (GTK_LIST_BOX (widget));
g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
}
@ -634,12 +670,22 @@ on_notification_dismissed (CcPrintersPanel *self)
PpPrinter *printer;
printer = pp_printer_new (self->deleted_printer_name);
/* The reference tells to the callback whether
printers panel was already destroyed so
it knows whether it can access the list
of deleted printers in it (see below).
*/
pp_printer_delete_async (printer,
NULL,
printer_removed_cb,
NULL);
g_object_ref (self->reference));
g_clear_pointer (&self->deleted_printer_name, g_free);
/* List of printers which were recently deleted but are still available
in CUPS due to async nature of the method (e.g. quick deletion
of several printers).
*/
self->deleted_printers = g_list_prepend (self->deleted_printers, self->deleted_printer_name);
self->deleted_printer_name = NULL;
}
gtk_revealer_set_reveal_child (self->notification, FALSE);
@ -662,8 +708,7 @@ on_printer_deleted (CcPrintersPanel *self,
GtkLabel *label;
g_autofree gchar *notification_message = NULL;
g_autofree gchar *printer_name = NULL;
gtk_widget_hide (GTK_WIDGET (printer_entry));
GtkWidget *widget;
on_notification_dismissed (self);
@ -680,6 +725,9 @@ on_printer_deleted (CcPrintersPanel *self,
self->deleted_printer_name = g_strdup (printer_name);
widget = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
gtk_list_box_invalidate_filter (GTK_LIST_BOX (widget));
gtk_revealer_set_reveal_child (self->notification, TRUE);
self->remove_printer_timeout_id = g_timeout_add_seconds (10, G_SOURCE_FUNC (on_remove_printer_timeout), self);
@ -764,6 +812,33 @@ set_current_page (GObject *source_object,
update_sensitivity (user_data);
}
static void
destroy_nonexisting_entries (PpPrinterEntry *entry,
gpointer user_data)
{
CcPrintersPanel *self = (CcPrintersPanel *) user_data;
g_autofree gchar *printer_name = NULL;
gboolean exists = FALSE;
gint i;
g_object_get (G_OBJECT (entry), "printer-name", &printer_name, NULL);
for (i = 0; i < self->num_dests; i++)
{
if (g_strcmp0 (self->dests[i].name, printer_name) == 0)
{
exists = TRUE;
break;
}
}
if (!exists)
{
gtk_widget_destroy (GTK_WIDGET (entry));
g_hash_table_remove (self->printer_entries, printer_name);
}
}
static void
actualize_printers_list_cb (GObject *source_object,
GAsyncResult *result,
@ -775,6 +850,7 @@ actualize_printers_list_cb (GObject *source_object,
PpCupsDests *cups_dests;
gboolean new_printer_available = FALSE;
g_autoptr(GError) error = NULL;
gpointer item;
int i;
cups_dests = pp_cups_get_dests_finish (cups, result, &error);
@ -802,7 +878,7 @@ actualize_printers_list_cb (GObject *source_object,
gtk_stack_set_visible_child_name (GTK_STACK (widget), "printers-list");
widget = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_destroy, NULL);
gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) destroy_nonexisting_entries, self);
for (i = 0; i < self->num_dests; i++)
{
@ -813,13 +889,14 @@ actualize_printers_list_cb (GObject *source_object,
for (i = 0; i < self->num_dests; i++)
{
if (g_strcmp0 (self->dests[i].name, self->deleted_printer_name) == 0)
continue;
if (new_printer_available && g_strcmp0 (self->dests[i].name, self->old_printer_name) == 0)
continue;
add_printer_entry (self, self->dests[i]);
item = g_hash_table_lookup (self->printer_entries, self->dests[i].name);
if (item != NULL)
pp_printer_entry_update (PP_PRINTER_ENTRY (item), self->dests[i], self->is_authorized);
else
add_printer_entry (self, self->dests[i]);
}
if (!self->entries_filled)
@ -837,6 +914,30 @@ actualize_printers_list_cb (GObject *source_object,
update_sensitivity (user_data);
g_object_unref (cups);
if (self->new_printer_name != NULL)
{
GtkScrolledWindow *scrolled_window;
GtkAllocation allocation;
GtkAdjustment *adjustment;
GtkWidget *printer_entry;
/* Scroll the view to show the newly added printer-entry. */
scrolled_window = GTK_SCROLLED_WINDOW (gtk_builder_get_object (self->builder,
"scrolled-window"));
adjustment = gtk_scrolled_window_get_vadjustment (scrolled_window);
printer_entry = GTK_WIDGET (g_hash_table_lookup (self->printer_entries,
self->new_printer_name));
if (printer_entry != NULL)
{
gtk_widget_get_allocation (printer_entry, &allocation);
g_clear_pointer (&self->new_printer_name, g_free);
gtk_adjustment_set_value (adjustment,
allocation.y - gtk_widget_get_margin_top (printer_entry));
}
}
}
static void
@ -870,11 +971,6 @@ static void
new_printer_dialog_response_cb (CcPrintersPanel *self,
gint response_id)
{
GtkScrolledWindow *scrolled_window;
GtkAllocation allocation;
GtkAdjustment *adjustment;
GtkWidget *printer_entry;
if (self->pp_new_printer_dialog)
g_clear_object (&self->pp_new_printer_dialog);
@ -899,22 +995,6 @@ new_printer_dialog_response_cb (CcPrintersPanel *self,
}
actualize_printers_list (self);
if (self->new_printer_name == NULL)
return;
/* Scroll the view to show the newly added printer-entry. */
scrolled_window = GTK_SCROLLED_WINDOW (gtk_builder_get_object (self->builder,
"scrolled-window"));
adjustment = gtk_scrolled_window_get_vadjustment (scrolled_window);
printer_entry = GTK_WIDGET (g_hash_table_lookup (self->printer_entries,
self->new_printer_name));
gtk_widget_get_allocation (printer_entry, &allocation);
g_clear_pointer (&self->new_printer_name, g_free);
gtk_adjustment_set_value (adjustment,
allocation.y - gtk_widget_get_margin_top (printer_entry));
}
static void
@ -1098,30 +1178,85 @@ filter_function (GtkListBoxRow *row,
g_autofree gchar *location = NULL;
g_autofree gchar *printer_name = NULL;
g_autofree gchar *printer_location = NULL;
search_entry = (GtkWidget*)
gtk_builder_get_object (self->builder, "search-entry");
if (gtk_entry_get_text_length (GTK_ENTRY (search_entry)) == 0)
return TRUE;
GList *iter;
g_object_get (G_OBJECT (row),
"printer-name", &printer_name,
"printer-location", &printer_location,
NULL);
name = cc_util_normalize_casefold_and_unaccent (printer_name);
location = cc_util_normalize_casefold_and_unaccent (printer_location);
search_entry = (GtkWidget*)
gtk_builder_get_object (self->builder, "search-entry");
search = cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (search_entry)));
if (gtk_entry_get_text_length (GTK_ENTRY (search_entry)) == 0)
{
retval = TRUE;
}
else
{
name = cc_util_normalize_casefold_and_unaccent (printer_name);
location = cc_util_normalize_casefold_and_unaccent (printer_location);
retval = strstr (name, search) != NULL;
if (location != NULL)
retval = retval || (strstr (location, search) != NULL);
search = cc_util_normalize_casefold_and_unaccent (gtk_entry_get_text (GTK_ENTRY (search_entry)));
retval = strstr (name, search) != NULL;
if (location != NULL)
retval = retval || (strstr (location, search) != NULL);
}
if (self->deleted_printer_name != NULL &&
g_strcmp0 (self->deleted_printer_name, printer_name) == 0)
{
retval = FALSE;
}
if (self->deleted_printers != NULL)
{
for (iter = self->deleted_printers; iter != NULL; iter = iter->next)
{
if (g_strcmp0 (iter->data, printer_name) == 0)
{
retval = FALSE;
break;
}
}
}
return retval;
}
static gint
sort_function (GtkListBoxRow *row1,
GtkListBoxRow *row2,
gpointer user_data)
{
g_autofree gchar *printer_name1 = NULL;
g_autofree gchar *printer_name2 = NULL;
g_object_get (G_OBJECT (row1),
"printer-name", &printer_name1,
NULL);
g_object_get (G_OBJECT (row2),
"printer-name", &printer_name2,
NULL);
if (printer_name1 != NULL)
{
if (printer_name2 != NULL)
return g_ascii_strcasecmp (printer_name1, printer_name2);
else
return 1;
}
else
{
if (printer_name2 != NULL)
return -1;
else
return 0;
}
}
static void
cc_printers_panel_init (CcPrintersPanel *self)
{
@ -1157,6 +1292,8 @@ cc_printers_panel_init (CcPrintersPanel *self)
self->renamed_printer_name = NULL;
self->old_printer_name = NULL;
self->deleted_printer_name = NULL;
self->deleted_printers = NULL;
self->reference = g_object_new (G_TYPE_OBJECT, NULL);
self->permission = NULL;
self->lockdown_settings = NULL;
@ -1172,6 +1309,8 @@ cc_printers_panel_init (CcPrintersPanel *self)
g_type_ensure (CC_TYPE_PERMISSION_INFOBAR);
g_object_set_data_full (self->reference, "self", self, g_free);
builder_result = gtk_builder_add_objects_from_resource (self->builder,
"/org/gnome/control-center/printers/printers.ui",
objects, &error);
@ -1220,6 +1359,10 @@ cc_printers_panel_init (CcPrintersPanel *self)
"search-changed",
G_CALLBACK (gtk_list_box_invalidate_filter),
widget);
gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
sort_function,
NULL,
NULL);
self->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown");
if (self->lockdown_settings)

View file

@ -45,7 +45,6 @@ struct _PpPrinterEntry
{
GtkListBoxRow parent;
gchar *printer_uri;
gchar *printer_name;
gchar *ppd_file_name;
int num_jobs;
@ -705,12 +704,35 @@ PpPrinterEntry *
pp_printer_entry_new (cups_dest_t printer,
gboolean is_authorized)
{
PpPrinterEntry *self;
PpPrinterEntry *self;
self = g_object_new (PP_PRINTER_ENTRY_TYPE, "printer-name", printer.name, NULL);
self->clean_command = pp_maintenance_command_new (self->printer_name,
"Clean",
"all",
/* Translators: Name of job which makes printer to clean its heads */
_("Clean print heads"));
check_clean_heads_maintenance_command (self);
g_signal_connect_object (self->supply_drawing_area, "draw", G_CALLBACK (supply_levels_draw_cb), self, G_CONNECT_SWAPPED);
pp_printer_entry_update (self, printer, is_authorized);
return self;
}
void
pp_printer_entry_update (PpPrinterEntry *self,
cups_dest_t printer,
gboolean is_authorized)
{
cups_ptype_t printer_type = 0;
gboolean is_accepting_jobs = TRUE;
gboolean ink_supply_is_empty;
g_autofree gchar *instance = NULL;
const gchar *printer_uri = NULL;
const gchar *device_uri = NULL;
const gchar *location = NULL;
g_autofree gchar *printer_icon_name = NULL;
const gchar *printer_make_and_model = NULL;
@ -776,8 +798,6 @@ pp_printer_entry_new (cups_dest_t printer,
N_("The optical photo conductor is no longer functioning")
};
self = g_object_new (PP_PRINTER_ENTRY_TYPE, "printer-name", printer.name, NULL);
if (printer.instance)
{
instance = g_strdup_printf ("%s / %s", printer.name, printer.instance);
@ -792,7 +812,7 @@ pp_printer_entry_new (cups_dest_t printer,
for (i = 0; i < printer.num_options; i++)
{
if (g_strcmp0 (printer.options[i].name, "device-uri") == 0)
self->printer_uri = printer.options[i].value;
device_uri = printer.options[i].value;
else if (g_strcmp0 (printer.options[i].name, "printer-uri-supported") == 0)
printer_uri = printer.options[i].value;
else if (g_strcmp0 (printer.options[i].name, "printer-type") == 0)
@ -802,13 +822,25 @@ pp_printer_entry_new (cups_dest_t printer,
else if (g_strcmp0 (printer.options[i].name, "printer-state-reasons") == 0)
reason = printer.options[i].value;
else if (g_strcmp0 (printer.options[i].name, "marker-names") == 0)
self->inklevel->marker_names = g_strcompress (printer.options[i].value);
{
g_free (self->inklevel->marker_names);
self->inklevel->marker_names = g_strcompress (g_strdup (printer.options[i].value));
}
else if (g_strcmp0 (printer.options[i].name, "marker-levels") == 0)
self->inklevel->marker_levels = g_strdup (printer.options[i].value);
{
g_free (self->inklevel->marker_levels);
self->inklevel->marker_levels = g_strdup (printer.options[i].value);
}
else if (g_strcmp0 (printer.options[i].name, "marker-colors") == 0)
self->inklevel->marker_colors = g_strdup (printer.options[i].value);
{
g_free (self->inklevel->marker_colors);
self->inklevel->marker_colors = g_strdup (printer.options[i].value);
}
else if (g_strcmp0 (printer.options[i].name, "marker-types") == 0)
self->inklevel->marker_types = g_strdup (printer.options[i].value);
{
g_free (self->inklevel->marker_types);
self->inklevel->marker_types = g_strdup (printer.options[i].value);
}
else if (g_strcmp0 (printer.options[i].name, "printer-make-and-model") == 0)
printer_make_and_model = printer.options[i].value;
else if (g_strcmp0 (printer.options[i].name, "printer-state") == 0)
@ -872,6 +904,11 @@ pp_printer_entry_new (cups_dest_t printer,
gtk_label_set_label (self->error_status, status);
gtk_widget_set_visible (GTK_WIDGET (self->printer_error), TRUE);
}
else
{
gtk_label_set_label (self->error_status, "");
gtk_widget_set_visible (GTK_WIDGET (self->printer_error), FALSE);
}
switch (self->printer_state)
{
@ -897,7 +934,7 @@ pp_printer_entry_new (cups_dest_t printer,
break;
}
if (printer_is_local (printer_type, self->printer_uri))
if (printer_is_local (printer_type, device_uri))
printer_icon_name = g_strdup ("printer");
else
printer_icon_name = g_strdup ("printer-network");
@ -907,14 +944,8 @@ pp_printer_entry_new (cups_dest_t printer,
self->is_accepting_jobs = is_accepting_jobs;
self->is_authorized = is_authorized;
self->printer_hostname = printer_get_hostname (printer_type, self->printer_uri, printer_uri);
self->clean_command = pp_maintenance_command_new (self->printer_name,
"Clean",
"all",
/* Translators: Name of job which makes printer to clean its heads */
_("Clean print heads"));
check_clean_heads_maintenance_command (self);
g_free (self->printer_hostname);
self->printer_hostname = printer_get_hostname (printer_type, device_uri, printer_uri);
gtk_image_set_from_icon_name (self->printer_icon, printer_icon_name, GTK_ICON_SIZE_DIALOG);
gtk_label_set_text (self->printer_status, printer_status);
@ -945,7 +976,6 @@ pp_printer_entry_new (cups_dest_t printer,
gtk_label_set_text (self->printer_location_address_label, location);
}
g_signal_connect_object (self->supply_drawing_area, "draw", G_CALLBACK (supply_levels_draw_cb), self, G_CONNECT_SWAPPED);
ink_supply_is_empty = supply_level_is_empty (self);
gtk_widget_set_visible (GTK_WIDGET (self->printer_inklevel_label), !ink_supply_is_empty);
gtk_widget_set_visible (GTK_WIDGET (self->supply_frame), !ink_supply_is_empty);
@ -954,8 +984,6 @@ pp_printer_entry_new (cups_dest_t printer,
gtk_widget_set_sensitive (GTK_WIDGET (self->printer_default_checkbutton), self->is_authorized);
gtk_widget_set_sensitive (GTK_WIDGET (self->remove_printer_menuitem), self->is_authorized);
return self;
}
static void

View file

@ -35,3 +35,7 @@ GSList *pp_printer_entry_get_size_group_widgets (PpPrinterEntry *self);
void pp_printer_entry_show_jobs_dialog (PpPrinterEntry *self);
void pp_printer_entry_authenticate_jobs (PpPrinterEntry *self);
void pp_printer_entry_update (PpPrinterEntry *self,
cups_dest_t printer,
gboolean is_authorized);