printers: Introduce PpDetailsDialog

This dialog handles the editing of printer properties such as
name, location, automatic discovery of driver, manual selection
of printer driver, and manual selection of ppd file.

https://bugzilla.gnome.org/show_bug.cgi?id=767600
This commit is contained in:
Felipe Borges 2017-01-23 12:30:10 +01:00
parent 37e37961e5
commit 7e3d89e13a
8 changed files with 741 additions and 1 deletions

View file

@ -45,6 +45,8 @@ libprinters_la_SOURCES = \
pp-job.h \
pp-jobs-dialog.c \
pp-jobs-dialog.h \
pp-details-dialog.c \
pp-details-dialog.h \
pp-samba.c \
pp-samba.h \
pp-print-device.c \

View file

@ -525,7 +525,7 @@ enum
static void
on_printer_changed (PpPrinterEntry *printer_entry,
gpointer user_data)
gpointer user_data)
{
actualize_printers_list (user_data);
}

View file

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.12"/>
<template class="PpDetailsDialog" parent="GtkDialog">
<property name="can_focus">False</property>
<property name="border_width">0</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="use-header-bar">1</property>
<signal name="response" handler="printer_name_edit_cb" swapped="yes"/>
<signal name="response" handler="printer_location_edit_cb" swapped="yes"/>
<child internal-child="vbox">
<object class="GtkBox">
<property name="margin">20</property>
<property name="halign">center</property>
<child>
<object class="GtkGrid">
<property name="row-spacing">10</property>
<property name="column-spacing">10</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Name</property>
<property name="halign">end</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="printer_name_entry">
<property name="halign">fill</property>
<property name="width_request">320</property>
<signal name="focus-out-event" handler="printer_name_edit_cb"/>
<signal name="changed" handler="printer_name_changed"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Location</property>
<property name="halign">end</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="printer_location_entry">
<property name="width_request">320</property>
<property name="halign">fill</property>
<signal name="focus-out-event" handler="printer_location_edit_cb"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Address</property>
<property name="halign">end</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="printer_address_label">
<property name="label">192.168.0.1</property>
<property name="halign">start</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Driver</property>
<property name="halign">end</property>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkStack" id="printer_model_stack">
<property name="halign">start</property>
<child>
<object class="GtkLabel" id="printer_model_label">
<property name="halign">start</property>
<property name="label">HP Inkjet Delux 9000</property>
</object>
<packing>
<property name="name">printer_model_label</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="halign">start</property>
<property name="spacing">5</property>
<child>
<object class="GtkSpinner">
<property name="active">True</property>
<property name="halign">start</property>
</object>
</child>
<child>
<object class="GtkLabel">
<property name="halign">start</property>
<property name="label" translatable="yes">Searching for preferred drivers…</property>
</object>
</child>
</object>
<packing>
<property name="name">loading</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<property name="halign">start</property>
<child>
<object class="GtkButton" id="search_for_drivers_button">
<property name="label" translatable="yes">Search for Drivers</property>
<property name="halign">fill</property>
<signal name="clicked" handler="search_for_drivers"/>
</object>
</child>
<child>
<object class="GtkButton" id="select_from_database_button">
<property name="label" translatable="yes">Select from Database…</property>
<property name="halign">fill</property>
<signal name="clicked" handler="select_ppd_in_dialog"/>
</object>
</child>
<child>
<object class="GtkButton" id="install_ppd_button">
<property name="label" translatable="yes">Install PPD File…</property>
<property name="halign">fill</property>
<signal name="clicked" handler="select_ppd_manually"/>
</object>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
</object>
</child>
</object>
</child>
</template>
<object class="GtkSizeGroup">
<property name="mode">horizontal</property>
<widgets>
<widget name="search_for_drivers_button"/>
<widget name="select_from_database_button"/>
<widget name="install_ppd_button"/>
</widgets>
</object>
</interface>

View file

@ -0,0 +1,447 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2016 Red Hat, Inc,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Felipe Borges <feborges@redhat.com>
*/
#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <cups/cups.h>
#include <cups/ppd.h>
#include "cc-editable-entry.h"
#include "pp-details-dialog.h"
#include "pp-ppd-selection-dialog.h"
#include "pp-utils.h"
struct _PpDetailsDialog {
GtkDialog parent;
GtkEntry *printer_name_entry;
GtkEntry *printer_location_entry;
GtkLabel *printer_address_label;
GtkLabel *printer_model_label;
GtkStack *printer_model_stack;
GtkWidget *search_for_drivers_button;
gchar *printer_name;
gchar *printer_location;
gchar *ppd_file_name;
PPDList *all_ppds_list;
GCancellable *get_all_ppds_cancellable;
GCancellable *get_ppd_names_cancellable;
/* Dialogs */
PpPPDSelectionDialog *pp_ppd_selection_dialog;
};
struct _PpDetailsDialogClass
{
GtkDialogClass parent_class;
};
G_DEFINE_TYPE (PpDetailsDialog, pp_details_dialog, GTK_TYPE_DIALOG);
static gboolean
printer_name_edit_cb (GtkWidget *entry,
GdkEventFocus *event,
PpDetailsDialog *self)
{
const gchar *new_name;
new_name = gtk_entry_get_text (GTK_ENTRY (self->printer_name_entry));
if (g_strcmp0 (self->printer_name, new_name) != 0)
{
printer_rename (self->printer_name, new_name);
self->printer_name = g_strdup (new_name);
}
return FALSE;
}
static void
printer_name_changed (GtkEditable *editable,
gpointer user_data)
{
PpDetailsDialog *self = (PpDetailsDialog *) user_data;
GtkWidget *widget;
const gchar *name;
gchar *title;
name = gtk_entry_get_text (GTK_ENTRY (self->printer_name_entry));
/* Translators: This is the title of the dialog. %s is the printer name. */
title = g_strdup_printf (_("%s Details"), name);
widget = gtk_dialog_get_header_bar (GTK_DIALOG (self));
gtk_header_bar_set_title (GTK_HEADER_BAR (widget), title);
g_free (title);
}
static gboolean
printer_location_edit_cb (GtkWidget *entry,
GdkEventFocus *event,
PpDetailsDialog *self)
{
const gchar *location;
location = gtk_entry_get_text (GTK_ENTRY (self->printer_location_entry));
if (g_strcmp0 (self->printer_location, location) != 0)
{
printer_set_location (self->printer_name, location);
self->printer_location = g_strdup (location);
}
return FALSE;
}
static void
ppd_names_free (gpointer user_data)
{
PPDName **names = (PPDName **) user_data;
gint i;
if (names)
{
for (i = 0; names[i]; i++)
{
g_free (names[i]->ppd_name);
g_free (names[i]->ppd_display_name);
g_free (names[i]);
}
g_free (names);
}
}
static void set_ppd_cb (gchar *printer_name, gboolean success, gpointer user_data);
static void
get_ppd_names_cb (PPDName **names,
const gchar *printer_name,
gboolean cancelled,
gpointer user_data)
{
PpDetailsDialog *self = (PpDetailsDialog*) user_data;
if (!cancelled)
{
if (names != NULL)
{
gtk_label_set_text (self->printer_model_label, names[0]->ppd_display_name);
printer_set_ppd_async (printer_name,
names[0]->ppd_name,
self->get_ppd_names_cancellable,
set_ppd_cb,
self);
ppd_names_free (names);
}
else
{
gtk_label_set_text (self->printer_model_label, _("No suitable driver found"));
}
gtk_stack_set_visible_child_name (self->printer_model_stack, "printer_model_label");
}
}
static void
search_for_drivers (GtkButton *button,
PpDetailsDialog *self)
{
gtk_stack_set_visible_child_name (self->printer_model_stack, "loading");
gtk_widget_set_sensitive (self->search_for_drivers_button, FALSE);
self->get_ppd_names_cancellable = g_cancellable_new ();
get_ppd_names_async (self->printer_name,
1,
self->get_ppd_names_cancellable,
get_ppd_names_cb,
self);
}
static void
set_ppd_cb (gchar *printer_name,
gboolean success,
gpointer user_data)
{
PpDetailsDialog *self = (PpDetailsDialog*) user_data;
gtk_label_set_text (GTK_LABEL (self->printer_model_label), self->ppd_file_name);
}
static void
ppd_selection_dialog_response_cb (GtkDialog *dialog,
gint response_id,
gpointer user_data)
{
PpDetailsDialog *self = (PpDetailsDialog*) user_data;
if (response_id == GTK_RESPONSE_OK)
{
gchar *ppd_name;
ppd_name = pp_ppd_selection_dialog_get_ppd_name (self->pp_ppd_selection_dialog);
if (self->printer_name && ppd_name)
{
GCancellable *cancellable;
cancellable = g_cancellable_new ();
printer_set_ppd_async (self->printer_name,
ppd_name,
cancellable,
set_ppd_cb,
self);
g_clear_pointer (&self->ppd_file_name, g_free);
self->ppd_file_name = g_strdup (ppd_name);
}
g_free (ppd_name);
}
pp_ppd_selection_dialog_free (self->pp_ppd_selection_dialog);
self->pp_ppd_selection_dialog = NULL;
}
static void
get_all_ppds_async_cb (PPDList *ppds,
gpointer user_data)
{
PpDetailsDialog *self = user_data;
self->all_ppds_list = ppds;
if (self->pp_ppd_selection_dialog)
pp_ppd_selection_dialog_set_ppd_list (self->pp_ppd_selection_dialog,
self->all_ppds_list);
g_object_unref (self->get_all_ppds_cancellable);
self->get_all_ppds_cancellable = NULL;
}
static void
select_ppd_in_dialog (GtkButton *button,
PpDetailsDialog *self)
{
gchar *device_id = NULL;
gchar *manufacturer = NULL;
g_clear_pointer (&self->ppd_file_name, g_free);
self->ppd_file_name = g_strdup (cupsGetPPD (self->printer_name));
if (!self->pp_ppd_selection_dialog)
{
device_id =
get_ppd_attribute (self->ppd_file_name,
"1284DeviceID");
if (device_id)
{
manufacturer = get_tag_value (device_id, "mfg");
if (!manufacturer)
manufacturer = get_tag_value (device_id, "manufacturer");
}
if (manufacturer == NULL)
{
manufacturer =
get_ppd_attribute (self->ppd_file_name,
"Manufacturer");
}
if (manufacturer == NULL)
{
manufacturer = g_strdup ("Raw");
}
if (self->all_ppds_list == NULL)
{
self->get_all_ppds_cancellable = g_cancellable_new ();
get_all_ppds_async (self->get_all_ppds_cancellable, get_all_ppds_async_cb, self);
}
self->pp_ppd_selection_dialog = pp_ppd_selection_dialog_new (
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
self->all_ppds_list,
manufacturer,
ppd_selection_dialog_response_cb,
self);
g_free (manufacturer);
g_free (device_id);
}
}
static void
select_ppd_manually (GtkButton *button,
PpDetailsDialog *self)
{
GtkFileFilter *filter;
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new (_("Select PPD File"),
GTK_WINDOW (self),
GTK_FILE_CHOOSER_ACTION_OPEN,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Open"), GTK_RESPONSE_ACCEPT,
NULL);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter,
_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)"));
gtk_file_filter_add_pattern (filter, "*.ppd");
gtk_file_filter_add_pattern (filter, "*.PPD");
gtk_file_filter_add_pattern (filter, "*.ppd.gz");
gtk_file_filter_add_pattern (filter, "*.PPD.gz");
gtk_file_filter_add_pattern (filter, "*.PPD.GZ");
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
gchar *ppd_filename;
ppd_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (self->printer_name && ppd_filename)
{
printer_set_ppd_file_async (self->printer_name,
ppd_filename,
NULL,
set_ppd_cb,
self);
}
g_free (ppd_filename);
}
gtk_widget_destroy (dialog);
}
static void
pp_details_dialog_init (PpDetailsDialog *self)
{
gtk_widget_init_template (GTK_WIDGET (self));
}
static void
pp_details_dialog_class_init (PpDetailsDialogClass *klass)
{
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/printers/details-dialog.ui");
gtk_widget_class_bind_template_child (widget_class, PpDetailsDialog, printer_name_entry);
gtk_widget_class_bind_template_child (widget_class, PpDetailsDialog, printer_location_entry);
gtk_widget_class_bind_template_child (widget_class, PpDetailsDialog, printer_address_label);
gtk_widget_class_bind_template_child (widget_class, PpDetailsDialog, printer_model_label);
gtk_widget_class_bind_template_child (widget_class, PpDetailsDialog, printer_model_stack);
gtk_widget_class_bind_template_child (widget_class, PpDetailsDialog, search_for_drivers_button);
gtk_widget_class_bind_template_callback (widget_class, printer_name_edit_cb);
gtk_widget_class_bind_template_callback (widget_class, printer_name_changed);
gtk_widget_class_bind_template_callback (widget_class, printer_location_edit_cb);
gtk_widget_class_bind_template_callback (widget_class, search_for_drivers);
gtk_widget_class_bind_template_callback (widget_class, select_ppd_in_dialog);
gtk_widget_class_bind_template_callback (widget_class, select_ppd_manually);
}
PpDetailsDialog *
pp_details_dialog_new (GtkWindow *parent,
gchar *printer_name,
gchar *printer_location,
gchar *printer_address,
gchar *printer_make_and_model,
gboolean sensitive)
{
PpDetailsDialog *self;
gchar *title;
gchar *printer_url;
self = g_object_new (PP_DETAILS_DIALOG_TYPE,
"transient-for", parent,
"use-header-bar", TRUE,
NULL);
self->printer_name = g_strdup (printer_name);
self->printer_location = g_strdup (printer_location);
self->ppd_file_name = NULL;
/* Translators: This is the title of the dialog. %s is the printer name. */
title = g_strdup_printf (_("%s Details"), printer_name);
gtk_window_set_title (GTK_WINDOW (self), title);
printer_url = g_strdup_printf ("<a href=\"http://%s:%d\">%s</a>", printer_address, ippPort (), printer_address);
gtk_label_set_markup (GTK_LABEL (self->printer_address_label), printer_url);
g_free (printer_url);
gtk_entry_set_text (GTK_ENTRY (self->printer_name_entry), printer_name);
gtk_entry_set_text (GTK_ENTRY (self->printer_location_entry), printer_location);
gtk_label_set_text (GTK_LABEL (self->printer_model_label), printer_make_and_model);
gtk_widget_set_sensitive (gtk_dialog_get_content_area (GTK_DIALOG (self)), sensitive);
return self;
}
void
pp_details_dialog_free (PpDetailsDialog *self)
{
if (self != NULL)
{
g_clear_pointer (&self->printer_name, g_free);
g_clear_pointer (&self->printer_location, g_free);
g_clear_pointer (&self->ppd_file_name, g_free);
if (self->all_ppds_list != NULL)
{
ppd_list_free (self->all_ppds_list);
self->all_ppds_list = NULL;
}
if (self->get_all_ppds_cancellable != NULL)
{
g_cancellable_cancel (self->get_all_ppds_cancellable);
g_clear_object (&self->get_all_ppds_cancellable);
}
if (self->get_ppd_names_cancellable != NULL)
{
g_cancellable_cancel (self->get_ppd_names_cancellable);
g_object_unref (self->get_ppd_names_cancellable);
self->get_ppd_names_cancellable = NULL;
}
gtk_widget_destroy (GTK_WIDGET (self));
}
}

View file

@ -0,0 +1,47 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2016 Red Hat, Inc,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Felipe Borges <feborges@redhat.com>
*/
#ifndef __PP_DETAILS_DIALOG_H__
#define __PP_DETAILS_DIALOG_H__
#include <gtk/gtk.h>
#include "pp-utils.h"
G_BEGIN_DECLS
#define PP_DETAILS_DIALOG_TYPE (pp_details_dialog_get_type ())
#define PP_DETAILS_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PP_DETAILS_DIALOG_TYPE, PpDetailsDialog))
typedef struct _PpDetailsDialog PpDetailsDialog;
typedef struct _PpDetailsDialogClass PpDetailsDialogClass;
GType pp_details_dialog_get_type (void);
PpDetailsDialog *pp_details_dialog_new (GtkWindow *parent,
gchar *printer_name,
gchar *printer_location,
gchar *printer_address,
gchar *printer_make_and_model,
gboolean sensitive);
void pp_details_dialog_free (PpDetailsDialog *dialog);
G_END_DECLS
#endif

View file

@ -24,6 +24,7 @@
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include "pp-details-dialog.h"
#include "pp-options-dialog.h"
#include "pp-jobs-dialog.h"
#include "pp-utils.h"
@ -61,6 +62,7 @@ struct _PpPrinterEntry
GtkLabel *error_status;
/* Dialogs */
PpDetailsDialog *pp_details_dialog;
PpOptionsDialog *pp_options_dialog;
PpJobsDialog *pp_jobs_dialog;
};
@ -282,6 +284,35 @@ supply_levels_draw_cb (GtkWidget *widget,
return TRUE;
}
static void
details_dialog_cb (GtkDialog *dialog,
gint response_id,
gpointer user_data)
{
PpPrinterEntry *self = PP_PRINTER_ENTRY (user_data);
pp_details_dialog_free (self->pp_details_dialog);
self->pp_details_dialog = NULL;
g_signal_emit_by_name (self, "printer-changed");
}
static void
on_show_printer_details_dialog (GtkButton *button,
PpPrinterEntry *self)
{
self->pp_details_dialog = pp_details_dialog_new (
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
self->printer_name,
self->printer_location,
self->printer_hostname,
self->printer_make_and_model,
self->is_authorized);
g_signal_connect (self->pp_details_dialog, "response", G_CALLBACK (details_dialog_cb), self);
gtk_widget_show_all (GTK_WIDGET (self->pp_details_dialog));
}
static void
printer_options_dialog_cb (GtkDialog *dialog,
gint response_id,
@ -685,6 +716,7 @@ pp_printer_entry_class_init (PpPrinterEntryClass *klass)
gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, error_status);
gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_error);
gtk_widget_class_bind_template_callback (widget_class, on_show_printer_details_dialog);
gtk_widget_class_bind_template_callback (widget_class, on_show_printer_options_dialog);
gtk_widget_class_bind_template_callback (widget_class, set_as_default_printer);
gtk_widget_class_bind_template_callback (widget_class, remove_printer);

View file

@ -20,6 +20,17 @@
</packing>
</child>
<child>
<object class="GtkModelButton">
<property name="visible">True</property>
<property name="text" translatable="yes">Printer Details</property>
<signal name="clicked" handler="on_show_printer_details_dialog"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="printer_default_checkbutton">
<property name="visible">True</property>

View file

@ -4,6 +4,7 @@
<file preprocess="xml-stripblanks">authentication-dialog.ui</file>
<file preprocess="xml-stripblanks">jobs-dialog.ui</file>
<file preprocess="xml-stripblanks">new-printer-dialog.ui</file>
<file preprocess="xml-stripblanks">details-dialog.ui</file>
<file preprocess="xml-stripblanks">options-dialog.ui</file>
<file preprocess="xml-stripblanks">ppd-selection-dialog.ui</file>
<file preprocess="xml-stripblanks">printer-entry.ui</file>