summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Kasik <mkasik@redhat.com>2011-03-07 17:09:00 +0100
committerMarek Kasik <mkasik@redhat.com>2011-03-07 17:09:00 +0100
commit3fb4d4af2745124c3d370c0fcc94fdb5886af245 (patch)
treed400d7b8b742c7ff823275cacb507b9e1f202f21
parentc96cc691026084eea4db47ee8050f553a8cbfcc2 (diff)
downloadgnome-control-center-3fb4d4af2745124c3d370c0fcc94fdb5886af245.tar.gz
printers: Add dialog for adding new printers
The dialog is able to add printers discovered by cupsGetDevices(). It can also add printers from a remote CUPS server by typing its address and selecting printer we want to add (#640734).
-rw-r--r--panels/printers/Makefile.am5
-rw-r--r--panels/printers/cc-printers-panel.c273
-rw-r--r--panels/printers/new-printer-dialog.ui247
-rw-r--r--panels/printers/pp-new-printer-dialog.c1066
-rw-r--r--panels/printers/pp-new-printer-dialog.h39
-rw-r--r--panels/printers/pp-utils.c385
-rw-r--r--panels/printers/pp-utils.h59
7 files changed, 1887 insertions, 187 deletions
diff --git a/panels/printers/Makefile.am b/panels/printers/Makefile.am
index 96caf475c..4f319eb6f 100644
--- a/panels/printers/Makefile.am
+++ b/panels/printers/Makefile.am
@@ -2,6 +2,7 @@ cappletname = printers
uidir = $(pkgdatadir)/ui/printers
dist_ui_DATA = \
+ new-printer-dialog.ui \
printers.ui
INCLUDES = \
@@ -17,6 +18,10 @@ ccpanels_LTLIBRARIES = libprinters.la
libprinters_la_SOURCES = \
printers-module.c \
+ pp-utils.c \
+ pp-utils.h \
+ pp-new-printer-dialog.c \
+ pp-new-printer-dialog.h \
cc-printers-panel.c \
cc-printers-panel.h
diff --git a/panels/printers/cc-printers-panel.c b/panels/printers/cc-printers-panel.c
index 84bd7f7ec..9dc64011f 100644
--- a/panels/printers/cc-printers-panel.c
+++ b/panels/printers/cc-printers-panel.c
@@ -33,6 +33,8 @@
#include <math.h>
#include "cc-lockbutton.h"
+#include "pp-new-printer-dialog.h"
+#include "pp-utils.h"
G_DEFINE_DYNAMIC_TYPE (CcPrintersPanel, cc_printers_panel, CC_TYPE_PANEL)
@@ -71,6 +73,8 @@ struct _CcPrintersPanelPrivate
GPermission *permission;
+ PpNewPrinterDialog *pp_new_printer_dialog;
+
gpointer dummy;
};
@@ -172,32 +176,6 @@ cc_printers_panel_class_finalize (CcPrintersPanelClass *klass)
{
}
-gchar *
-get_ppd_attribute (const gchar *printer_name, const gchar *attribute_name)
-{
- gchar *file_name = NULL;
- ppd_file_t *ppd_file = NULL;
- ppd_attr_t *ppd_attr = NULL;
- gchar *result = NULL;
-
- file_name = cupsGetPPD (printer_name);
-
- if (file_name)
- {
- ppd_file = ppdOpenFile (file_name);
- if (ppd_file)
- {
- ppd_attr = ppdFindAttr (ppd_file, attribute_name, NULL);
- if (ppd_attr != NULL)
- result = g_strdup (ppd_attr->value);
- ppdClose (ppd_file);
- }
- g_unlink (file_name);
- }
-
- return result;
-}
-
enum
{
PRINTER_ID_COLUMN,
@@ -1168,73 +1146,6 @@ enum
ALLOWED_USERS_N_COLUMNS
};
-static int
-ccGetAllowedUsers (gchar ***allowed_users, char *printer_name)
-{
- const char * const attrs[1] = { "requesting-user-name-allowed" };
- http_t *http;
- ipp_t *request = NULL;
- gchar **users = NULL;
- ipp_t *response;
- char uri[HTTP_MAX_URI + 1];
- int num_allowed_users = 0;
-
- http = httpConnectEncrypt (cupsServer (),
- ippPort (),
- cupsEncryption ());
-
- if (http || !allowed_users)
- {
- request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
-
- g_snprintf (uri, sizeof (uri), "ipp://localhost/printers/%s", printer_name);
- ippAddString (request,
- IPP_TAG_OPERATION,
- IPP_TAG_URI,
- "printer-uri",
- NULL,
- uri);
- ippAddStrings (request,
- IPP_TAG_OPERATION,
- IPP_TAG_KEYWORD,
- "requested-attributes",
- 1,
- NULL,
- attrs);
-
- response = cupsDoRequest (http, request, "/");
- if (response)
- {
- ipp_attribute_t *attr = NULL;
- ipp_attribute_t *allowed = NULL;
-
- for (attr = response->attrs; attr != NULL; attr = attr->next)
- {
- if (attr->group_tag == IPP_TAG_PRINTER &&
- attr->value_tag == IPP_TAG_NAME &&
- !g_strcmp0 (attr->name, "requesting-user-name-allowed"))
- allowed = attr;
- }
-
- if (allowed && allowed->num_values > 0)
- {
- int i;
-
- num_allowed_users = allowed->num_values;
- users = g_new (gchar*, num_allowed_users);
-
- for (i = 0; i < num_allowed_users; i ++)
- users[i] = g_strdup (allowed->values[i].string.text);
- }
- ippDelete(response);
- }
- httpClose (http);
- }
-
- *allowed_users = users;
- return num_allowed_users;
-}
-
static void
actualize_allowed_users_list (CcPrintersPanel *self)
{
@@ -1330,33 +1241,6 @@ populate_allowed_users_list (CcPrintersPanel *self)
"changed", G_CALLBACK (allowed_users_selection_changed_cb), self);
}
-static DBusGProxy *
-get_dbus_proxy ()
-{
- DBusGConnection *system_bus;
- DBusGProxy *proxy;
- GError *error;
-
- error = NULL;
- system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
- if (system_bus == NULL)
- {
- /* Translators: Program cannot connect to DBus' system bus */
- g_warning (_("Could not connect to system bus: %s"),
- error->message);
- g_error_free (error);
- return NULL;
- }
-
- error = NULL;
-
- proxy = dbus_g_proxy_new_for_name (system_bus,
- MECHANISM_BUS,
- "/",
- MECHANISM_BUS);
- return proxy;
-}
-
static void
job_process_cb (GtkButton *button,
gpointer user_data)
@@ -1378,7 +1262,10 @@ job_process_cb (GtkButton *button,
if (id >= 0)
{
- proxy = get_dbus_proxy ();
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
if (!proxy)
return;
@@ -1471,7 +1358,10 @@ printer_disable_cb (GObject *gobject,
if (name)
{
- proxy = get_dbus_proxy ();
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
if (!proxy)
return;
@@ -1718,7 +1608,10 @@ allowed_user_remove_cb (GtkToolButton *button,
if (name && printer_name)
{
- proxy = get_dbus_proxy ();
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
if (!proxy)
return;
@@ -1788,7 +1681,10 @@ allowed_user_add_cb (GtkCellRendererText *renderer,
if (new_text && new_text[0] != '\0' && printer_name)
{
- proxy = get_dbus_proxy ();
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
if (!proxy)
return;
@@ -1889,7 +1785,10 @@ printer_set_default_cb (GtkToggleButton *button,
if (name)
{
- proxy = get_dbus_proxy ();
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
if (!proxy)
return;
@@ -1923,6 +1822,58 @@ printer_set_default_cb (GtkToggleButton *button,
}
static void
+new_printer_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ CcPrintersPanelPrivate *priv;
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+
+ priv = PRINTERS_PANEL_PRIVATE (self);
+
+ pp_new_printer_dialog_free (priv->pp_new_printer_dialog);
+ priv->pp_new_printer_dialog = NULL;
+
+ if (response_id == GTK_RESPONSE_OK)
+ actualize_printers_list (self);
+ else if (response_id == GTK_RESPONSE_REJECT)
+ {
+ GtkWidget *message_dialog;
+
+ message_dialog = gtk_message_dialog_new (NULL,
+ 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ /* Translators: Addition of the new printer failed. */
+ _("Failed to add new printer."));
+ g_signal_connect (message_dialog,
+ "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+ gtk_widget_show (message_dialog);
+ }
+}
+
+static void
+printer_add_cb (GtkToolButton *toolbutton,
+ gpointer user_data)
+{
+ CcPrintersPanelPrivate *priv;
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ GtkWidget *widget;
+
+ priv = PRINTERS_PANEL_PRIVATE (self);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (priv->builder, "main-vbox");
+
+ priv->pp_new_printer_dialog = pp_new_printer_dialog_new (
+ GTK_WINDOW (gtk_widget_get_toplevel (widget)),
+ new_printer_dialog_response_cb,
+ self);
+}
+
+static void
printer_remove_cb (GtkToolButton *toolbutton,
gpointer user_data)
{
@@ -1942,7 +1893,10 @@ printer_remove_cb (GtkToolButton *toolbutton,
if (name)
{
- proxy = get_dbus_proxy ();
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
if (!proxy)
return;
@@ -1966,67 +1920,6 @@ printer_remove_cb (GtkToolButton *toolbutton,
}
}
-static ipp_t *
-execute_maintenance_command (const char *printer_name,
- const char *command,
- const char *title)
-{
- http_t *http;
- GError *error = NULL;
- ipp_t *request = NULL;
- ipp_t *response = NULL;
- char uri[HTTP_MAX_URI + 1];
- int fd = -1;
-
- http = httpConnectEncrypt (cupsServer (),
- ippPort (),
- cupsEncryption ());
-
- if (http)
- {
- request = ippNewRequest (IPP_PRINT_JOB);
-
- g_snprintf (uri,
- sizeof (uri),
- "ipp://localhost/printers/%s",
- printer_name);
-
- ippAddString (request,
- IPP_TAG_OPERATION,
- IPP_TAG_URI,
- "printer-uri",
- NULL,
- uri);
-
- ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
- NULL, title);
-
- ippAddString (request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
- NULL, "application/vnd.cups-command");
-
- gchar *file_name = NULL;
- fd = g_file_open_tmp ("ccXXXXXX", &file_name, &error);
-
- if (fd != -1 && !error)
- {
- FILE *file;
-
- file = fdopen (fd, "w");
- fprintf (file, "#CUPS-COMMAND\n");
- fprintf (file, "%s\n", command);
- fclose (file);
-
- response = cupsDoFileRequest (http, request, "/", file_name);
- g_unlink (file_name);
- }
-
- g_free (file_name);
- httpClose (http);
- }
-
- return response;
-}
-
static void
printer_maintenance_cb (GtkButton *button,
gpointer user_data)
@@ -2185,6 +2078,8 @@ cc_printers_panel_init (CcPrintersPanel *self)
priv->num_allowed_users = 0;
priv->current_allowed_user = -1;
+ priv->pp_new_printer_dialog = NULL;
+
gtk_builder_add_objects_from_file (priv->builder,
DATADIR"/printers.ui",
objects, &error);
@@ -2215,6 +2110,10 @@ cc_printers_panel_init (CcPrintersPanel *self)
g_signal_connect (widget, "clicked", G_CALLBACK (job_process_cb), self);
widget = (GtkWidget*)
+ gtk_builder_get_object (priv->builder, "printer-add-button");
+ g_signal_connect (widget, "clicked", G_CALLBACK (printer_add_cb), self);
+
+ widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-remove-button");
g_signal_connect (widget, "clicked", G_CALLBACK (printer_remove_cb), self);
diff --git a/panels/printers/new-printer-dialog.ui b/panels/printers/new-printer-dialog.ui
new file mode 100644
index 000000000..f76c1051b
--- /dev/null
+++ b/panels/printers/new-printer-dialog.ui
@@ -0,0 +1,247 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkDialog" id="dialog">
+ <property name="width_request">500</property>
+ <property name="height_request">350</property>
+ <property name="border_width">5</property>
+ <property name="resizable">False</property>
+ <property name="type_hint">normal</property>
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Add a New Printer</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkTreeView" id="device-types-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkNotebook" id="device-type-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">False</property>
+ <property name="show_border">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="local-devices-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label2">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">page 1</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="network-devices-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <property name="headers_clickable">False</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Address</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="address-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">&#x25CF;</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox3">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkCheckButton" id="search-by-address-checkbutton">
+ <property name="label" translatable="yes">Search by Address</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="search-state-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label3">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">page 2</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label4">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">page 3</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="new-printer-cancel-button">
+ <property name="label" translatable="yes">Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="new-printer-add-button">
+ <property name="label" translatable="yes">Add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">new-printer-cancel-button</action-widget>
+ <action-widget response="0">new-printer-add-button</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/panels/printers/pp-new-printer-dialog.c b/panels/printers/pp-new-printer-dialog.c
new file mode 100644
index 000000000..7c0f3187c
--- /dev/null
+++ b/panels/printers/pp-new-printer-dialog.c
@@ -0,0 +1,1066 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010 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 3 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#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 "pp-new-printer-dialog.h"
+#include "pp-utils.h"
+
+#include <dbus/dbus-glib.h>
+
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
+#define MECHANISM_BUS "org.opensuse.CupsPkHelper.Mechanism"
+
+#define PACKAGE_KIT_BUS "org.freedesktop.PackageKit"
+#define PACKAGE_KIT_PATH "/org/freedesktop/PackageKit"
+#define PACKAGE_KIT_IFACE "org.freedesktop.PackageKit.Modify"
+
+#define ALLOWED_CHARACTERS "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/"
+
+static void pp_new_printer_dialog_hide (PpNewPrinterDialog *pp);
+static void actualize_devices_list (PpNewPrinterDialog *pp, gboolean get_devices);
+
+enum
+{
+ NOTEBOOK_LOCAL_PAGE = 0,
+ NOTEBOOK_NETWORK_PAGE,
+ NOTEBOOK_HP_JETDIRECT_PAGE,
+ NOTEBOOK_N_PAGES
+};
+
+enum
+{
+ DEVICE_TYPE_ID_COLUMN = 0,
+ DEVICE_TYPE_NAME_COLUMN,
+ DEVICE_TYPE_TYPE_COLUMN,
+ DEVICE_TYPE_N_COLUMNS
+};
+
+enum
+{
+ DEVICE_ID_COLUMN = 0,
+ DEVICE_NAME_COLUMN,
+ DEVICE_N_COLUMNS
+};
+
+enum
+{
+ DEVICE_TYPE_LOCAL = 0,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_HP_JETDIRECT,
+ DEVICE_TYPE_N
+};
+
+typedef struct{
+ gchar *device_class;
+ gchar *device_id;
+ gchar *device_info;
+ gchar *device_make_and_model;
+ gchar *device_uri;
+ gchar *device_location;
+ gchar *device_ppd_uri;
+ gchar *display_name;
+ gchar *hostname;
+ gint host_port;
+ gboolean show;
+ gboolean found;
+} CupsDevice;
+
+struct _PpNewPrinterDialog {
+ GtkBuilder *builder;
+ GtkWidget *parent;
+
+ GtkWidget *dialog;
+
+ gchar **device_connection_types;
+ gint num_device_connection_types;
+
+ CupsDevice *devices;
+ gint num_devices;
+
+ UserResponseCallback user_callback;
+ gpointer user_data;
+};
+
+static void
+device_type_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ PpNewPrinterDialog *pp = (PpNewPrinterDialog *) user_data;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *device_type_name = NULL;
+ gint device_type_id = -1;
+ gint device_type = -1;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ DEVICE_TYPE_ID_COLUMN, &device_type_id,
+ DEVICE_TYPE_NAME_COLUMN, &device_type_name,
+ DEVICE_TYPE_TYPE_COLUMN, &device_type,
+ -1);
+ }
+
+ if (device_type >= 0)
+ {
+ GtkWidget *widget;
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "device-type-notebook");
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), device_type);
+ }
+}
+
+static void
+free_devices (PpNewPrinterDialog *pp)
+{
+ int i;
+
+ for (i = 0; i < pp->num_devices; i++)
+ {
+ g_free (pp->devices[i].device_class);
+ g_free (pp->devices[i].device_id);
+ g_free (pp->devices[i].device_info);
+ g_free (pp->devices[i].device_make_and_model);
+ g_free (pp->devices[i].device_uri);
+ g_free (pp->devices[i].device_location);
+ g_free (pp->devices[i].device_ppd_uri);
+ g_free (pp->devices[i].display_name);
+ g_free (pp->devices[i].hostname);
+ }
+
+ pp->num_devices = 0;
+ pp->devices = NULL;
+}
+
+static void
+store_device_parameter (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ PpNewPrinterDialog *pp = (PpNewPrinterDialog *) user_data;
+ gchar *cut;
+ gint index = -1;
+
+ cut = g_strrstr ((gchar *)key, ":");
+ if (cut)
+ index = atoi ((gchar *)cut + 1);
+
+ if (index >= 0)
+ {
+ if (g_str_has_prefix ((gchar *)key, "device-class"))
+ pp->devices[index].device_class = g_strdup ((gchar *)value);
+ else if (g_str_has_prefix ((gchar *)key, "device-id"))
+ pp->devices[index].device_id = g_strdup ((gchar *)value);
+ else if (g_str_has_prefix ((gchar *)key, "device-info"))
+ pp->devices[index].device_info = g_strdup ((gchar *)value);
+ else if (g_str_has_prefix ((gchar *)key, "device-make-and-model"))
+ pp->devices[index].device_make_and_model = g_strdup ((gchar *)value);
+ else if (g_str_has_prefix ((gchar *)key, "device-uri"))
+ pp->devices[index].device_uri = g_strdup ((gchar *)value);
+ else if (g_str_has_prefix ((gchar *)key, "device-location"))
+ pp->devices[index].device_location = g_strdup ((gchar *)value);
+ }
+}
+
+static void
+devices_get (PpNewPrinterDialog *pp)
+{
+ DBusGProxy *proxy;
+ GError *error = NULL;
+ char *ret_error = NULL;
+ gint i, j;
+
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
+
+ if (!proxy)
+ return;
+
+ GHashTable *devices = NULL;
+ dbus_g_proxy_call (proxy, "DevicesGet", &error,
+ G_TYPE_INT, 60,
+ G_TYPE_STRING, CUPS_INCLUDE_ALL,
+ G_TYPE_STRING, CUPS_EXCLUDE_NONE,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ DBUS_TYPE_G_STRING_STRING_HASHTABLE, &devices,
+ G_TYPE_INVALID);
+
+ g_object_unref (proxy);
+
+ if (error || (ret_error && ret_error[0] != '\0'))
+ {
+ if (error)
+ g_warning ("%s", error->message);
+
+ if (ret_error && ret_error[0] != '\0')
+ g_warning ("%s", ret_error);
+ }
+
+ free_devices (pp);
+ if (devices)
+ {
+ GList *keys;
+ GList *iter;
+ gchar *cut;
+ gint max_index = -1;
+ gint index;
+
+ keys = g_hash_table_get_keys (devices);
+ for (iter = keys; iter; iter = iter->next)
+ {
+ index = -1;
+
+ cut = g_strrstr ((gchar *)iter->data, ":");
+ if (cut)
+ index = atoi (cut + 1);
+
+ if (index > max_index)
+ max_index = index;
+ }
+
+ if (max_index >= 0)
+ {
+ pp->num_devices = max_index + 1;
+ pp->devices = g_new0 (CupsDevice, pp->num_devices);
+
+ g_hash_table_foreach (devices, store_device_parameter, pp);
+
+ /* Assign names to devices */
+ for (i = 0; i < pp->num_devices; i++)
+ {
+ gchar *name = NULL;
+
+ if (pp->devices[i].device_id)
+ {
+ name = get_tag_value (pp->devices[i].device_id, "mdl");
+ name = g_strcanon (name, ALLOWED_CHARACTERS, '-');
+ }
+ else if (pp->devices[i].device_info)
+ {
+ name = g_strdup (pp->devices[i].device_info);
+ name = g_strcanon (name, ALLOWED_CHARACTERS, '-');
+ }
+
+ pp->devices[i].display_name = name;
+ }
+
+ /* Set show bool
+ * Don't show duplicates.
+ * Show devices with device-id.
+ * Other preferences should apply here.
+ */
+ for (i = 0; i < pp->num_devices; i++)
+ {
+ for (j = 0; j < pp->num_devices; j++)
+ {
+ if (i != j)
+ {
+ if (g_strcmp0 (pp->devices[i].display_name, pp->devices[j].display_name) == 0)
+ {
+ if (pp->devices[i].device_id && !pp->devices[j].show)
+ {
+ pp->devices[i].show = TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ g_hash_table_destroy (devices);
+ }
+
+ g_clear_error (&error);
+}
+
+static void
+search_address_cb (GtkToggleButton *togglebutton,
+ gpointer user_data)
+{
+ PpNewPrinterDialog *pp = (PpNewPrinterDialog*) user_data;
+ GtkWidget *widget;
+ gint i;
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "search-by-address-checkbutton");
+
+ if (widget && gtk_toggle_button_get_active (togglebutton))
+ {
+ gchar *uri = NULL;
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "address-entry");
+ uri = g_strdup (gtk_entry_get_text (GTK_ENTRY (widget)));
+
+ if (uri && uri[0] != '\0')
+ {
+ http_t *http;
+ cups_dest_t *dests = NULL;
+ gint num_dests = 0;
+ gchar *tmp = NULL;
+ gchar *host = NULL;
+ gchar *port_string = NULL;
+ int port = 631;
+ gchar *position;
+
+ if (g_strrstr (uri, "://"))
+ tmp = g_strrstr (uri, "://") + 3;
+ else
+ tmp = uri;
+
+ if (g_strrstr (tmp, "@"))
+ tmp = g_strrstr (tmp, "@") + 1;
+
+ if ((position = g_strrstr (tmp, "/")))
+ {
+ *position = '\0';
+ host = g_strdup (tmp);
+ *position = '/';
+ }
+ else
+ host = g_strdup (tmp);
+
+ if ((position = g_strrstr (host, ":")))
+ {
+ *position = '\0';
+ port_string = position + 1;
+ }
+
+ if (port_string)
+ port = atoi (port_string);
+
+ /* Search for CUPS server */
+ if (host)
+ {
+ http = httpConnectEncrypt (host, port, cupsEncryption ());
+ if (http)
+ {
+ gchar *device_uri = NULL;
+ gchar *device_ppd_uri = NULL;
+
+ num_dests = cupsGetDests2 (http, &dests);
+
+ if (num_dests > 0)
+ {
+ CupsDevice *devices = NULL;
+ devices = g_new0 (CupsDevice, pp->num_devices + num_dests);
+
+ for (i = 0; i < pp->num_devices; i++)
+ {
+ devices[i] = pp->devices[i];
+ pp->devices[i].device_class = NULL;
+ pp->devices[i].device_id = NULL;
+ pp->devices[i].device_info = NULL;
+ pp->devices[i].device_make_and_model = NULL;
+ pp->devices[i].device_uri = NULL;
+ pp->devices[i].device_location = NULL;
+ pp->devices[i].device_ppd_uri = NULL;
+ pp->devices[i].display_name = NULL;
+ pp->devices[i].hostname = NULL;
+ }
+
+ g_free (pp->devices);
+ pp->devices = devices;
+
+ for (i = 0; i < num_dests; i++)
+ {
+ device_uri = g_strdup_printf ("ipp://%s:%d/printers/%s", host, port, dests[i].name);
+ device_ppd_uri = g_strdup_printf ("%s.ppd", device_uri);
+
+ pp->devices[pp->num_devices + i].device_class = g_strdup ("network");
+ pp->devices[pp->num_devices + i].device_uri = device_uri;
+ pp->devices[pp->num_devices + i].display_name = g_strdup (dests[i].name);
+ pp->devices[pp->num_devices + i].device_ppd_uri = device_ppd_uri;
+ pp->devices[pp->num_devices + i].show = TRUE;
+ pp->devices[pp->num_devices + i].hostname = g_strdup (host);
+ pp->devices[pp->num_devices + i].host_port = port;
+ pp->devices[pp->num_devices + i].found = TRUE;
+ }
+
+ pp->num_devices += num_dests;
+ }
+
+ httpClose (http);
+ }
+ g_free (host);
+ }
+ }
+ g_free (uri);
+ }
+ else
+ {
+ gint length = 0;
+ gint j = 0;
+
+ for (i = 0; i < pp->num_devices; i++)
+ if (!pp->devices[i].found)
+ length++;
+
+ CupsDevice *devices = NULL;
+ devices = g_new0 (CupsDevice, length);
+
+ for (i = 0; i < pp->num_devices; i++)
+ {
+ if (!pp->devices[i].found)
+ {
+ devices[j] = pp->devices[i];
+ pp->devices[i].device_class = NULL;
+ pp->devices[i].device_id = NULL;
+ pp->devices[i].device_info = NULL;
+ pp->devices[i].device_make_and_model = NULL;
+ pp->devices[i].device_uri = NULL;
+ pp->devices[i].device_location = NULL;
+ pp->devices[i].device_ppd_uri = NULL;
+ pp->devices[i].display_name = NULL;
+ pp->devices[i].hostname = NULL;
+ j++;
+ }
+ }
+
+ g_free (pp->devices);
+ pp->devices = devices;
+ pp->num_devices = length;
+ }
+
+ actualize_devices_list (pp, FALSE);
+}
+
+static void
+actualize_devices_list (PpNewPrinterDialog *pp, gboolean get_devices)
+{
+ GtkListStore *network_store;
+ GtkListStore *local_store;
+ GtkTreeView *network_treeview;
+ GtkTreeView *local_treeview;
+ GtkTreeIter iter;
+ gint i;
+
+ if (get_devices)
+ devices_get (pp);
+
+ network_treeview = (GtkTreeView*)
+ gtk_builder_get_object (pp->builder, "network-devices-treeview");
+
+ local_treeview = (GtkTreeView*)
+ gtk_builder_get_object (pp->builder, "local-devices-treeview");
+
+ network_store = gtk_list_store_new (DEVICE_N_COLUMNS,
+ G_TYPE_INT,
+ G_TYPE_STRING);
+
+ local_store = gtk_list_store_new (DEVICE_N_COLUMNS,
+ G_TYPE_INT,
+ G_TYPE_STRING);
+
+ for (i = 0; i < pp->num_devices; i++)
+ {
+ if (pp->devices[i].device_id || pp->devices[i].device_ppd_uri)
+ {
+ if (g_strcmp0 (pp->devices[i].device_class, "network") == 0)
+ {
+ gtk_list_store_append (network_store, &iter);
+ gtk_list_store_set (network_store, &iter,
+ DEVICE_ID_COLUMN, i,
+ DEVICE_NAME_COLUMN, pp->devices[i].display_name,
+ -1);
+ }
+ else if (g_strcmp0 (pp->devices[i].device_class, "direct") == 0)
+ {
+ gtk_list_store_append (local_store, &iter);
+ gtk_list_store_set (local_store, &iter,
+ DEVICE_ID_COLUMN, i,
+ DEVICE_NAME_COLUMN, pp->devices[i].display_name,
+ -1);
+ }
+ }
+ }
+
+ gtk_tree_view_set_model (network_treeview, GTK_TREE_MODEL (network_store));
+ gtk_tree_view_set_model (local_treeview, GTK_TREE_MODEL (local_store));
+
+ gtk_tree_model_get_iter_first ((GtkTreeModel *) network_store, &iter);
+ gtk_tree_selection_select_iter (
+ gtk_tree_view_get_selection (GTK_TREE_VIEW (network_treeview)),
+ &iter);
+
+ gtk_tree_model_get_iter_first ((GtkTreeModel *) local_store, &iter);
+ gtk_tree_selection_select_iter (
+ gtk_tree_view_get_selection (GTK_TREE_VIEW (local_treeview)),
+ &iter);
+
+ g_object_unref (network_store);
+ g_object_unref (local_store);
+}
+
+static void
+populate_devices_list (PpNewPrinterDialog *pp)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkWidget *network_treeview;
+ GtkWidget *local_treeview;
+
+ network_treeview = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "network-devices-treeview");
+
+ local_treeview = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "local-devices-treeview");
+
+ actualize_devices_list (pp, TRUE);
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ /* Translators: Column of devices which can be installed */
+ column = gtk_tree_view_column_new_with_attributes (_("Devices"), renderer,
+ "text", DEVICE_NAME_COLUMN, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (network_treeview), column);
+
+ /* Translators: Column of devices which can be installed */
+ column = gtk_tree_view_column_new_with_attributes (_("Devices"), renderer,
+ "text", DEVICE_NAME_COLUMN, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (local_treeview), column);
+}
+
+static void
+actualize_device_types_list (PpNewPrinterDialog *pp)
+{
+ GtkListStore *store;
+ GtkTreeView *treeview;
+ GtkTreeIter iter;
+ gint i;
+
+ treeview = (GtkTreeView*)
+ gtk_builder_get_object (pp->builder, "device-types-treeview");
+
+ store = gtk_list_store_new (DEVICE_TYPE_N_COLUMNS,
+ G_TYPE_INT,
+ G_TYPE_STRING,
+ G_TYPE_INT);
+
+ pp->device_connection_types = g_new (gchar*, 2);
+ pp->num_device_connection_types = 2;
+
+ /* Translators: Local means local printers */
+ pp->device_connection_types[0] = g_strdup (_("Local"));
+ /* Translators: Network means network printers */
+ pp->device_connection_types[1] = g_strdup (_("Network"));
+
+ for (i = 0; i < pp->num_device_connection_types; i++)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ DEVICE_TYPE_ID_COLUMN, i,
+ DEVICE_TYPE_NAME_COLUMN, pp->device_connection_types[i],
+ DEVICE_TYPE_TYPE_COLUMN, i,
+ -1);
+ }
+
+ gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
+
+ gtk_tree_model_get_iter_first ((GtkTreeModel *) store,
+ &iter);
+
+ gtk_tree_selection_select_iter (
+ gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
+ &iter);
+
+ g_object_unref (store);
+}
+
+static void
+populate_device_types_list (PpNewPrinterDialog *pp)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkWidget *treeview;
+
+ treeview = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "device-types-treeview");
+
+ actualize_device_types_list (pp);
+
+ g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)),
+ "changed", G_CALLBACK (device_type_selection_changed_cb), pp);
+
+ renderer = gtk_cell_renderer_text_new ();
+ /* Translators: Device types column (network or local) */
+ column = gtk_tree_view_column_new_with_attributes (_("Device types"), renderer,
+ "text", DEVICE_TYPE_NAME_COLUMN, NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+}
+
+static void
+dialog_closed (GtkWidget *dialog,
+ gint response_id,
+ PpNewPrinterDialog *pp)
+{
+ gtk_widget_destroy (dialog);
+}
+
+static void
+new_printer_add_button_cb (GtkButton *button,
+ gpointer user_data)
+{
+ PpNewPrinterDialog *pp = (PpNewPrinterDialog*) user_data;
+ GtkResponseType dialog_response = GTK_RESPONSE_OK;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *treeview;
+ gchar *device_name = NULL;
+ gchar *ppd_name = NULL;
+ gint device_id = -1;
+ gint device_type = -1;
+
+ treeview = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "device-types-treeview");
+
+ if (gtk_tree_selection_get_selected (
+ gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter))
+ gtk_tree_model_get (model, &iter,
+ DEVICE_TYPE_TYPE_COLUMN, &device_type,
+ -1);
+
+ switch (device_type)
+ {
+ case DEVICE_TYPE_LOCAL:
+ treeview = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "local-devices-treeview");
+ break;
+ case DEVICE_TYPE_NETWORK:
+ treeview = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "network-devices-treeview");
+ break;
+ default:
+ treeview = NULL;
+ break;
+ }
+
+ if (treeview &&
+ gtk_tree_selection_get_selected (
+ gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ DEVICE_ID_COLUMN, &device_id,
+ DEVICE_NAME_COLUMN, &device_name,
+ -1);
+ }
+
+ if (device_id >= 0)
+ {
+ if (pp->devices[device_id].device_ppd_uri)
+ {
+ http_t *http;
+
+ http = httpConnectEncrypt (pp->devices[device_id].hostname,
+ pp->devices[device_id].host_port,
+ cupsEncryption ());
+
+ if (http)
+ {
+ const char *ppd_file_name;
+
+ ppd_file_name = cupsGetPPD2 (http, pp->devices[device_id].display_name);
+
+ if (ppd_file_name)
+ {
+ DBusGProxy *proxy;
+ GError *error = NULL;
+ char *ret_error = NULL;
+
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
+ if (proxy)
+ {
+ dbus_g_proxy_call (proxy, "PrinterAddWithPpdFile", &error,
+ G_TYPE_STRING, pp->devices[device_id].display_name,
+ G_TYPE_STRING, pp->devices[device_id].device_uri,
+ G_TYPE_STRING, ppd_file_name,
+ G_TYPE_STRING, pp->devices[device_id].device_info,
+ G_TYPE_STRING, pp->devices[device_id].device_location,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ G_TYPE_INVALID);
+
+ if (error || (ret_error && ret_error[0] != '\0'))
+ {
+ dialog_response = GTK_RESPONSE_REJECT;
+
+ if (error)
+ g_warning ("%s", error->message);
+
+ if (ret_error && ret_error[0] != '\0')
+ g_warning ("%s", ret_error);
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ ret_error = NULL;
+
+ dbus_g_proxy_call (proxy, "PrinterSetAcceptJobs", &error,
+ G_TYPE_STRING, pp->devices[device_id].display_name,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_STRING, "none",
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_call (proxy, "PrinterSetEnabled", &error,
+ G_TYPE_STRING, pp->devices[device_id].display_name,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ G_TYPE_INVALID);
+
+ if (error || (ret_error && ret_error[0] != '\0'))
+ {
+ if (error)
+ g_warning ("%s", error->message);
+
+ if (ret_error && ret_error[0] != '\0')
+ g_warning ("%s", ret_error);
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ if (g_strcmp0 (pp->devices[device_id].device_class, "direct") == 0)
+ {
+ gchar *commands = get_dest_attr (pp->devices[device_id].display_name, "printer-commands");
+ gchar *commands_lowercase = g_ascii_strdown (commands, -1);
+ ipp_t *response = NULL;
+
+ if (g_strrstr (commands_lowercase, "AutoConfigure"))
+ {
+ response = execute_maintenance_command (pp->devices[device_id].display_name,
+ "AutoConfigure",
+ /* Translators: Name of job which makes printer to autoconfigure itself */
+ _("Automatic configuration"));
+ if (response)
+ {
+ if (response->state == IPP_ERROR)
+ /* Translators: An error has occured during execution of AutoConfigure CUPS maintenance command */
+ g_warning ("An error has occured during automatic configuration of new printer.");
+ ippDelete (response);
+ }
+ }
+ g_free (commands);
+ g_free (commands_lowercase);
+ }
+ }
+ }
+ g_object_unref (proxy);
+ }
+
+ g_unlink (ppd_file_name);
+ }
+ else
+ {
+ dialog_response = GTK_RESPONSE_REJECT;
+ g_warning ("Getting of PPD for %s from %s:%d failed.",
+ pp->devices[device_id].display_name,
+ pp->devices[device_id].hostname,
+ pp->devices[device_id].host_port);
+ }
+ }
+ }
+ else if (pp->devices[device_id].device_id)
+ {
+ /* Try whether CUPS has a driver for the new printer */
+ ppd_name = get_ppd_name (pp->devices[device_id].device_class,
+ pp->devices[device_id].device_id,
+ pp->devices[device_id].device_info,
+ pp->devices[device_id].device_make_and_model,
+ pp->devices[device_id].device_uri,
+ pp->devices[device_id].device_location);
+
+ if (ppd_name == NULL)
+ {
+ /* Try PackageKit to install printer driver */
+ DBusGProxy *proxy;
+ GError *error = NULL;
+
+ proxy = get_dbus_proxy (PACKAGE_KIT_BUS,
+ PACKAGE_KIT_PATH,
+ PACKAGE_KIT_IFACE,
+ FALSE);
+
+ if (proxy)
+ {
+ gchar **device_ids = NULL;
+
+ device_ids = g_new (gchar *, 2);
+ device_ids[0] = pp->devices[device_id].device_id;
+ device_ids[1] = NULL;
+
+ dbus_g_proxy_call (proxy, "InstallPrinterDrivers", &error,
+
+#ifdef GDK_WINDOWING_X11
+ G_TYPE_UINT, GDK_WINDOW_XID (gtk_widget_get_window (GTK_WIDGET (pp->dialog))),
+#else
+ G_TYPE_UINT, 0,
+#endif
+ G_TYPE_STRV, device_ids,
+ G_TYPE_STRING, "hide-finished",
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+
+ g_object_unref (proxy);
+
+ if (error)
+ g_warning ("%s", error->message);
+
+ g_clear_error (&error);
+
+ /* Search CUPS for driver */
+ ppd_name = get_ppd_name (pp->devices[device_id].device_class,
+ pp->devices[device_id].device_id,
+ pp->devices[device_id].device_info,
+ pp->devices[device_id].device_make_and_model,
+ pp->devices[device_id].device_uri,
+ pp->devices[device_id].device_location);
+
+ g_free (device_ids);
+ }
+ }
+
+ /* Add the new printer */
+ if (ppd_name)
+ {
+ DBusGProxy *proxy;
+ GError *error = NULL;
+ char *ret_error = NULL;
+
+ proxy = get_dbus_proxy (MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ TRUE);
+ if (proxy)
+ {
+ dbus_g_proxy_call (proxy, "PrinterAdd", &error,
+ G_TYPE_STRING, pp->devices[device_id].display_name,
+ G_TYPE_STRING, pp->devices[device_id].device_uri,
+ G_TYPE_STRING, ppd_name,
+ G_TYPE_STRING, pp->devices[device_id].device_info,
+ G_TYPE_STRING, pp->devices[device_id].device_location,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ G_TYPE_INVALID);
+
+ if (error || (ret_error && ret_error[0] != '\0'))
+ {
+ dialog_response = GTK_RESPONSE_REJECT;
+
+ if (error)
+ g_warning ("%s", error->message);
+
+ if (ret_error && ret_error[0] != '\0')
+ g_warning ("%s", ret_error);
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ ret_error = NULL;
+
+ dbus_g_proxy_call (proxy, "PrinterSetAcceptJobs", &error,
+ G_TYPE_STRING, pp->devices[device_id].display_name,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_STRING, "none",
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_call (proxy, "PrinterSetEnabled", &error,
+ G_TYPE_STRING, pp->devices[device_id].display_name,
+ G_TYPE_BOOLEAN, TRUE,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &ret_error,
+ G_TYPE_INVALID);
+
+ if (error || (ret_error && ret_error[0] != '\0'))
+ {
+ if (error)
+ g_warning ("%s", error->message);
+
+ if (ret_error && ret_error[0] != '\0')
+ g_warning ("%s", ret_error);
+
+ g_clear_error (&error);
+ }
+ else
+ {
+ if (g_strcmp0 (pp->devices[device_id].device_class, "direct") == 0)
+ {
+ gchar *commands = get_dest_attr (pp->devices[device_id].display_name, "printer-commands");
+ gchar *commands_lowercase = g_ascii_strdown (commands, -1);
+ ipp_t *response = NULL;
+
+ if (g_strrstr (commands_lowercase, "AutoConfigure"))
+ {
+ response = execute_maintenance_command (pp->devices[device_id].display_name,
+ "AutoConfigure",
+ /* Translators: Name of job which makes printer to autoconfigure itself */
+ _("Automatic configuration"));
+ if (response)
+ {
+ if (response->state == IPP_ERROR)
+ /* Translators: An error has occured during execution of AutoConfigure CUPS maintenance command */
+ g_warning ("An error has occured during automatic configuration of new printer.");
+ ippDelete (response);
+ }
+ }
+ g_free (commands);
+ g_free (commands_lowercase);
+ }
+ }
+ }
+ g_object_unref (proxy);
+ }
+ }
+ }
+ }
+
+ pp_new_printer_dialog_hide (pp);
+ pp->user_callback (GTK_DIALOG (pp->dialog), dialog_response, pp->user_data);
+}
+
+static void
+new_printer_cancel_button_cb (GtkButton *button,
+ gpointer user_data)
+{
+ PpNewPrinterDialog *pp = (PpNewPrinterDialog*) user_data;
+
+ pp_new_printer_dialog_hide (pp);
+ pp->user_callback (GTK_DIALOG (pp->dialog), GTK_RESPONSE_CANCEL, pp->user_data);
+}
+
+PpNewPrinterDialog *
+pp_new_printer_dialog_new (GtkWindow *parent,
+ UserResponseCallback user_callback,
+ gpointer user_data)
+{
+ PpNewPrinterDialog *pp;
+ GtkWidget *widget;
+ GError *error = NULL;
+ gchar *objects[] = { "dialog", "main-vbox", NULL };
+
+ pp = g_new0 (PpNewPrinterDialog, 1);
+
+ pp->builder = gtk_builder_new ();
+ pp->parent = GTK_WIDGET (parent);
+
+ gtk_builder_add_objects_from_file (pp->builder,
+ DATADIR"/new-printer-dialog.ui",
+ objects, &error);
+
+ if (error)
+ {
+ g_warning ("Could not load ui: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ pp->device_connection_types = NULL;
+ pp->num_device_connection_types = 0;
+
+ pp->devices = NULL;
+ pp->num_devices = 0;
+
+ pp->dialog = (GtkWidget *) gtk_builder_get_object (pp->builder, "dialog");
+
+ pp->user_callback = user_callback;
+ pp->user_data = user_data;
+
+ /* connect signals */
+ g_signal_connect (pp->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "new-printer-add-button");
+ g_signal_connect (widget, "clicked", G_CALLBACK (new_printer_add_button_cb), pp);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "new-printer-cancel-button");
+ g_signal_connect (widget, "clicked", G_CALLBACK (new_printer_cancel_button_cb), pp);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (pp->builder, "search-by-address-checkbutton");
+ g_signal_connect (widget, "toggled", G_CALLBACK (search_address_cb), pp);
+
+ gtk_window_set_transient_for (GTK_WINDOW (pp->dialog), GTK_WINDOW (parent));
+ gtk_window_set_modal (GTK_WINDOW (pp->dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (pp->dialog));
+ gtk_widget_show_all (GTK_WIDGET (pp->dialog));
+
+ populate_device_types_list (pp);
+ populate_devices_list (pp);
+
+ return pp;
+}
+
+void
+pp_new_printer_dialog_free (PpNewPrinterDialog *pp)
+{
+ gint i;
+
+ for (i = 0; i < pp->num_device_connection_types; i++)
+ g_free (pp->device_connection_types[i]);
+ g_free (pp->device_connection_types);
+ pp->device_connection_types = NULL;
+
+ free_devices (pp);
+
+ gtk_widget_destroy (GTK_WIDGET (pp->dialog));
+ pp->dialog = NULL;
+
+ g_object_unref (pp->builder);
+ pp->builder = NULL;
+
+ g_free (pp);
+}
+
+static void
+pp_new_printer_dialog_hide (PpNewPrinterDialog *pp)
+{
+ gtk_widget_hide (GTK_WIDGET (pp->dialog));
+}
diff --git a/panels/printers/pp-new-printer-dialog.h b/panels/printers/pp-new-printer-dialog.h
new file mode 100644
index 000000000..618c0d716
--- /dev/null
+++ b/panels/printers/pp-new-printer-dialog.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010 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 3 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __PP_NEW_PRINTER_DIALOG_H__
+#define __PP_NEW_PRINTER_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _PpNewPrinterDialog PpNewPrinterDialog;
+
+typedef void (*UserResponseCallback) (GtkDialog *dialog, gint response_id, gpointer user_data);
+
+PpNewPrinterDialog *pp_new_printer_dialog_new (GtkWindow *parent,
+ UserResponseCallback user_callback,
+ gpointer user_data);
+void pp_new_printer_dialog_free (PpNewPrinterDialog *dialog);
+
+G_END_DECLS
+
+#endif
diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c
new file mode 100644
index 000000000..39a1416f9
--- /dev/null
+++ b/panels/printers/pp-utils.c
@@ -0,0 +1,385 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010 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 3 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#include <cups/cups.h>
+#include <dbus/dbus-glib.h>
+
+#include "pp-utils.h"
+
+DBusGProxy *
+get_dbus_proxy (const gchar *name,
+ const gchar *path,
+ const gchar *iface,
+ const gboolean system_bus)
+{
+ DBusGConnection *bus;
+ DBusGProxy *proxy;
+ GError *error;
+
+ error = NULL;
+ if (system_bus)
+ bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ else
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+
+ if (bus == NULL)
+ {
+ if (system_bus)
+ /* Translators: Program cannot connect to DBus' system bus */
+ g_warning ("Could not connect to system bus: %s", error->message);
+ else
+ /* Translators: Program cannot connect to DBus' session bus */
+ g_warning ("Could not connect to session bus: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ error = NULL;
+
+ proxy = dbus_g_proxy_new_for_name (bus, name, path, iface);
+
+ return proxy;
+}
+
+gchar *get_tag_value (const gchar *tag_string, const gchar *tag_name)
+{
+ gchar **tag_string_splitted = NULL;
+ gchar *tag_value = NULL;
+ gint tag_name_length = strlen (tag_name);
+ gint i;
+
+ tag_string_splitted = g_strsplit (tag_string, ";", 0);
+ for (i = 0; i < g_strv_length (tag_string_splitted); i++)
+ if (g_ascii_strncasecmp (tag_string_splitted[i], tag_name, tag_name_length) == 0)
+ if (strlen (tag_string_splitted[i]) > tag_name_length + 1)
+ tag_value = g_strdup (tag_string_splitted[i] + tag_name_length + 1);
+ g_strfreev (tag_string_splitted);
+
+ return tag_value;
+}
+
+gchar *
+get_ppd_name (gchar *device_class,
+ gchar *device_id,
+ gchar *device_info,
+ gchar *device_make_and_model,
+ gchar *device_uri,
+ gchar *device_location)
+{
+ http_t *http = NULL;
+ ipp_t *request = NULL;
+ ipp_t *response = NULL;
+ gchar *mfg = NULL;
+ gchar *mdl = NULL;
+ gchar *result = NULL;
+
+ mfg = get_tag_value (device_id, "mfg");
+ mdl = get_tag_value (device_id, "mdl");
+
+ http = httpConnectEncrypt (cupsServer (),
+ ippPort (),
+ cupsEncryption ());
+
+ if (http)
+ {
+ request = ippNewRequest (CUPS_GET_PPDS);
+ if (device_id)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-device-id",
+ NULL, device_id);
+ else if (mfg)
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_TEXT, "ppd-make",
+ NULL, mfg);
+ response = cupsDoRequest (http, request, "/");
+
+ if (response)
+ {
+ ipp_attribute_t *attr = NULL;
+ const char *ppd_device_id;
+ const char *ppd_make_model;
+ const char *ppd_make;
+ const char *ppd_name;
+ gchar *ppd_mfg;
+ gchar *ppd_mdl;
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ ppd_device_id = "NONE";
+ ppd_make_model = NULL;
+ ppd_make = NULL;
+ ppd_name = NULL;
+ ppd_mfg = NULL;
+ ppd_mdl = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (!strcmp(attr->name, "ppd-device-id") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ ppd_device_id = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "ppd-name") &&
+ attr->value_tag == IPP_TAG_NAME)
+ ppd_name = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "ppd-make") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ ppd_make = attr->values[0].string.text;
+ else if (!strcmp(attr->name, "ppd-make-and-model") &&
+ attr->value_tag == IPP_TAG_TEXT)
+ ppd_make_model = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ if (mfg && mdl && !result)
+ {
+ if (ppd_device_id)
+ {
+ ppd_mfg = get_tag_value (ppd_device_id, "mfg");
+
+ if (ppd_mfg && g_ascii_strcasecmp (ppd_mfg, mfg) == 0)
+ {
+ ppd_mdl = get_tag_value (ppd_device_id, "mdl");
+
+ if (ppd_mdl && g_ascii_strcasecmp (ppd_mdl, mdl) == 0)
+ {
+ result = g_strdup (ppd_name);
+ g_free (ppd_mdl);
+ }
+ g_free (ppd_mfg);
+ }
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+ ippDelete(response);
+ }
+ httpClose (http);
+ }
+
+ g_free (mfg);
+ g_free (mdl);
+
+ return result;
+}
+
+char *
+get_dest_attr (const char *dest_name,
+ const char *attr)
+{
+ cups_dest_t *dests;
+ int num_dests;
+ cups_dest_t *dest;
+ const char *value;
+ char *ret;
+
+ if (dest_name == NULL)
+ return NULL;
+
+ ret = NULL;
+
+ num_dests = cupsGetDests (&dests);
+ if (num_dests < 1) {
+ g_debug ("Unable to get printer destinations");
+ return NULL;
+ }
+
+ dest = cupsGetDest (dest_name, NULL, num_dests, dests);
+ if (dest == NULL) {
+ g_debug ("Unable to find a printer named '%s'", dest_name);
+ goto out;
+ }
+
+ value = cupsGetOption (attr, dest->num_options, dest->options);
+ if (value == NULL) {
+ g_debug ("Unable to get %s for '%s'", attr, dest_name);
+ goto out;
+ }
+ ret = g_strdup (value);
+out:
+ cupsFreeDests (num_dests, dests);
+
+ return ret;
+}
+
+ipp_t *
+execute_maintenance_command (const char *printer_name,
+ const char *command,
+ const char *title)
+{
+ http_t *http;
+ GError *error = NULL;
+ ipp_t *request = NULL;
+ ipp_t *response = NULL;
+ char uri[HTTP_MAX_URI + 1];
+ int fd = -1;
+
+ http = httpConnectEncrypt (cupsServer (),
+ ippPort (),
+ cupsEncryption ());
+
+ if (http)
+ {
+ request = ippNewRequest (IPP_PRINT_JOB);
+
+ g_snprintf (uri,
+ sizeof (uri),
+ "ipp://localhost/printers/%s",
+ printer_name);
+
+ ippAddString (request,
+ IPP_TAG_OPERATION,
+ IPP_TAG_URI,
+ "printer-uri",
+ NULL,
+ uri);
+
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
+ NULL, title);
+
+ ippAddString (request, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
+ NULL, "application/vnd.cups-command");
+
+ gchar *file_name = NULL;
+ fd = g_file_open_tmp ("ccXXXXXX", &file_name, &error);
+
+ if (fd != -1 && !error)
+ {
+ FILE *file;
+
+ file = fdopen (fd, "w");
+ fprintf (file, "#CUPS-COMMAND\n");
+ fprintf (file, "%s\n", command);
+ fclose (file);
+
+ response = cupsDoFileRequest (http, request, "/", file_name);
+ g_unlink (file_name);
+ }
+
+ g_free (file_name);
+ httpClose (http);
+ }
+
+ return response;
+}
+
+int
+ccGetAllowedUsers (gchar ***allowed_users, const char *printer_name)
+{
+ const char * const attrs[1] = { "requesting-user-name-allowed" };
+ http_t *http;
+ ipp_t *request = NULL;
+ gchar **users = NULL;
+ ipp_t *response;
+ char uri[HTTP_MAX_URI + 1];
+ int num_allowed_users = 0;
+
+ http = httpConnectEncrypt (cupsServer (),
+ ippPort (),
+ cupsEncryption ());
+
+ if (http || !allowed_users)
+ {
+ request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
+
+ g_snprintf (uri, sizeof (uri), "ipp://localhost/printers/%s", printer_name);
+ ippAddString (request,
+ IPP_TAG_OPERATION,
+ IPP_TAG_URI,
+ "printer-uri",
+ NULL,
+ uri);
+ ippAddStrings (request,
+ IPP_TAG_OPERATION,
+ IPP_TAG_KEYWORD,
+ "requested-attributes",
+ 1,
+ NULL,
+ attrs);
+
+ response = cupsDoRequest (http, request, "/");
+ if (response)
+ {
+ ipp_attribute_t *attr = NULL;
+ ipp_attribute_t *allowed = NULL;
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ if (attr->group_tag == IPP_TAG_PRINTER &&
+ attr->value_tag == IPP_TAG_NAME &&
+ !g_strcmp0 (attr->name, "requesting-user-name-allowed"))
+ allowed = attr;
+ }
+
+ if (allowed && allowed->num_values > 0)
+ {
+ int i;
+
+ num_allowed_users = allowed->num_values;
+ users = g_new (gchar*, num_allowed_users);
+
+ for (i = 0; i < num_allowed_users; i ++)
+ users[i] = g_strdup (allowed->values[i].string.text);
+ }
+ ippDelete(response);
+ }
+ httpClose (http);
+ }
+
+ *allowed_users = users;
+ return num_allowed_users;
+}
+
+gchar *
+get_ppd_attribute (const gchar *printer_name, const gchar *attribute_name)
+{
+ const char *file_name = NULL;
+ ppd_file_t *ppd_file = NULL;
+ ppd_attr_t *ppd_attr = NULL;
+ gchar *result = NULL;
+
+ file_name = cupsGetPPD (printer_name);
+
+ if (file_name)
+ {
+ ppd_file = ppdOpenFile (file_name);
+ if (ppd_file)
+ {
+ ppd_attr = ppdFindAttr (ppd_file, attribute_name, NULL);
+ if (ppd_attr != NULL)
+ result = g_strdup (ppd_attr->value);
+ ppdClose (ppd_file);
+ }
+ g_unlink (file_name);
+ }
+
+ return result;
+}
diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h
new file mode 100644
index 000000000..e314f5bbd
--- /dev/null
+++ b/panels/printers/pp-utils.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2010 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 3 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __PP_UTILS_H__
+#define __PP_UTILS_H__
+
+#include <gtk/gtk.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+DBusGProxy *get_dbus_proxy (const gchar *name,
+ const gchar *path,
+ const gchar *iface,
+ const gboolean system_bus);
+
+gchar *get_tag_value (const gchar *tag_string,
+ const gchar *tag_name);
+
+gchar *get_ppd_name (gchar *device_class,
+ gchar *device_id,
+ gchar *device_info,
+ gchar *device_make_and_model,
+ gchar *device_uri,
+ gchar *device_location);
+
+char *get_dest_attr (const char *dest_name,
+ const char *attr);
+
+ipp_t *execute_maintenance_command (const char *printer_name,
+ const char *command,
+ const char *title);
+
+int ccGetAllowedUsers (gchar ***allowed_users,
+ const char *printer_name);
+
+gchar *get_ppd_attribute (const gchar *printer_name,
+ const gchar *attribute_name);
+
+G_END_DECLS
+
+#endif /* __PP_UTILS_H */