[gnome-control-center/wip/feborges/new-printers-panel: 127/127] printers: redesign the Printer Jobs Dialog



commit 5d7bec85bfdd714b5fde5eb42f8699e5e4e6eccb
Author: Felipe Borges <feborges redhat com>
Date:   Tue Sep 8 16:55:38 2015 +0200

    printers: redesign the Printer Jobs Dialog
    
    Update the Printer Jobs Dialog to match the current designs at
    https://wiki.gnome.org/Design/SystemSettings/Printers
    
    https://bugzilla.gnome.org/show_bug.cgi?id=755626

 panels/printers/Makefile.am      |    2 +
 panels/printers/jobs-dialog.ui   |  145 +++++-------
 panels/printers/pp-job.c         |  261 +++++++++++++++++++++
 panels/printers/pp-job.h         |   47 ++++
 panels/printers/pp-jobs-dialog.c |  468 +++++++++++++-------------------------
 panels/printers/pp-utils.c       |  162 -------------
 panels/printers/pp-utils.h       |   16 --
 7 files changed, 521 insertions(+), 580 deletions(-)
---
diff --git a/panels/printers/Makefile.am b/panels/printers/Makefile.am
index c386486..7fb2c17 100644
--- a/panels/printers/Makefile.am
+++ b/panels/printers/Makefile.am
@@ -37,6 +37,8 @@ libprinters_la_SOURCES =              \
        pp-ppd-selection-dialog.h       \
        pp-options-dialog.c             \
        pp-options-dialog.h             \
+       pp-job.c                        \
+       pp-job.h                        \
        pp-jobs-dialog.c                \
        pp-jobs-dialog.h                \
        pp-authentication-dialog.c      \
diff --git a/panels/printers/jobs-dialog.ui b/panels/printers/jobs-dialog.ui
index 6ac05a3..881ecc7 100644
--- a/panels/printers/jobs-dialog.ui
+++ b/panels/printers/jobs-dialog.ui
@@ -3,143 +3,110 @@
 <interface>
   <requires lib="gtk+" version="3.12"/>
   <object class="GtkDialog" id="jobs-dialog">
-    <property name="width_request">500</property>
-    <property name="height_request">350</property>
+    <property name="width_request">600</property>
+    <property name="height_request">500</property>
     <property name="can_focus">False</property>
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes" comments="Translators: This dialog contains list of active 
print jobs of the selected printer">Active Jobs</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>
-    <child internal-child="vbox">
-      <object class="GtkBox" id="main-vbox">
+    <property name="use-header-bar">1</property>
+    <child internal-child="headerbar">
+      <object class="GtkHeaderBar" id="dialog-header-bar">
         <property name="visible">True</property>
         <property name="can_focus">False</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">10</property>
-        <child internal-child="action_area">
-          <object class="GtkButtonBox" id="dialog-action-area1">
+        <property name="show_close_button">True</property>
+        <child>
+          <object class="GtkButton" id="jobs-clear-all-button">
+            <property name="label" translatable="yes" comments="Translators: this action removes (purges) 
all the listed jobs from the list.">Clear All</property>
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="layout_style">end</property>
-            <child>
-              <object class="GtkButton" id="jobs-close-button">
-                <property name="label" translatable="yes">Close</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">True</property>
-                <property name="position">2</property>
-              </packing>
-            </child>
+            <property name="can_focus">True</property>
+            <property name="receives_default">True</property>
+            <property name="use_underline">True</property>
+            <property name="valign">center</property>
+            <style>
+              <class name="destructive-action"/>
+            </style>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
         </child>
+      </object>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="border_width">0</property>
         <child>
-          <object class="GtkBox" id="box2">
+          <object class="GtkStack" id="stack">
             <property name="visible">True</property>
-            <property name="can_focus">False</property>
-            <property name="hexpand">True</property>
-            <property name="vexpand">True</property>
-            <property name="orientation">vertical</property>
             <child>
-              <object class="GtkScrolledWindow" id="queue-scrolledwindow">
+              <object class="GtkScrolledWindow" id="scrolledwindow">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
-                <property name="shadow_type">in</property>
+                <property name="vexpand">True</property>
+                <property name="hscrollbar-policy">never</property>
+                <property name="shadow_type">none</property>
                 <child>
-                  <object class="GtkTreeView" id="job-treeview">
+                  <object class="GtkListBox" id="jobs-listbox">
                     <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <child internal-child="selection">
-                      <object class="GtkTreeSelection" id="treeview-selection"/>
+                    <property name="can-focus">True</property>
+                    <property name="halign">fill</property>
+                    <property name="valign">fill</property>
+                    <property name="selection-mode">none</property>
+                    <child>
+                      <placeholder/>
                     </child>
                   </object>
                 </child>
               </object>
               <packing>
-                <property name="expand">True</property>
-                <property name="fill">True</property>
-                <property name="position">0</property>
+                <property name="name">list-jobs-page</property>
               </packing>
             </child>
             <child>
-              <object class="GtkToolbar" id="queue-toolbar">
+              <object class="GtkBox" id="no-jobs-dialog">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
-                <property name="icon_size">1</property>
+                <property name="spacing">10</property>
+                <property name="orientation">vertical</property>
+                <property name="valign">center</property>
                 <child>
-                  <object class="GtkToolButton" id="job-release-button">
+                  <object class="GtkImage" id="no-printer-image">
                     <property name="visible">True</property>
-                    <property name="sensitive">False</property>
                     <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Resume Printing</property>
-                    <property name="use_underline">True</property>
-                    <property name="icon_name">media-playback-start-symbolic</property>
+                    <property name="valign">start</property>
+                    <property name="pixel_size">64</property>
+                    <property name="icon_name">printer-symbolic</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
                   </object>
                   <packing>
                     <property name="expand">False</property>
-                    <property name="homogeneous">True</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkToolButton" id="job-hold-button">
+                  <object class="GtkLabel" id="no-printer-label">
                     <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Pause Printing</property>
-                    <property name="use_underline">True</property>
-                    <property name="icon_name">media-playback-pause-symbolic</property>
+                    <property name="label" translatable="yes" comments="Translators: this label describes 
the dialog empty state, with no jobs listed.">No Active Printer Jobs</property>
+                    <style>
+                      <class name="dim-label"/>
+                    </style>
                   </object>
                   <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">True</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkToolButton" id="job-cancel-button">
-                    <property name="visible">True</property>
-                    <property name="sensitive">False</property>
-                    <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">Cancel Print Job</property>
-                    <property name="use_underline">True</property>
-                    <property name="icon_name">media-playback-stop-symbolic</property>
-                  </object>
-                  <packing>
-                    <property name="expand">False</property>
-                    <property name="homogeneous">True</property>
+                    <property name="position">1</property>
                   </packing>
                 </child>
-                <style>
-                  <class name="inline-toolbar"/>
-                </style>
               </object>
               <packing>
-                <property name="expand">False</property>
-                <property name="fill">True</property>
-                <property name="position">1</property>
+                <property name="name">no-jobs-page</property>
               </packing>
             </child>
           </object>
-          <packing>
-            <property name="expand">False</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
         </child>
       </object>
     </child>
-    <action-widgets>
-      <action-widget response="0">jobs-close-button</action-widget>
-    </action-widgets>
   </object>
 </interface>
diff --git a/panels/printers/pp-job.c b/panels/printers/pp-job.c
new file mode 100644
index 0000000..8b7084a
--- /dev/null
+++ b/panels/printers/pp-job.c
@@ -0,0 +1,261 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2015  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 "pp-job.h"
+
+typedef struct
+{
+  GObject parent;
+
+  gint   id;
+  gchar *title;
+  gint   state;
+} PpJobPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (PpJob, pp_job, G_TYPE_OBJECT)
+
+enum
+{
+  PROP_0,
+  PROP_ID,
+  PROP_TITLE,
+  PROP_STATE,
+  LAST_PROPERTY
+};
+
+static GParamSpec *properties[LAST_PROPERTY];
+
+static void
+pp_job_cancel_purge_async_dbus_cb (GObject      *source_object,
+                                   GAsyncResult *res,
+                                   gpointer      user_data)
+{
+  GVariant *output;
+  GError   *error = NULL;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  g_object_unref (source_object);
+
+  if (output != NULL)
+    {
+      g_variant_unref (output);
+    }
+  else
+    {
+      g_error_free (error);
+    }
+}
+
+void
+pp_job_cancel_purge_async (PpJob        *job,
+                           gboolean      job_purge)
+{
+  GDBusConnection *bus;
+  GError          *error = NULL;
+  gint            *job_id;
+
+  g_object_get (job, "id", &job_id, NULL);
+
+  bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+  if (!bus)
+    {
+      g_warning ("Failed to get session bus: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_dbus_connection_call (bus,
+                          MECHANISM_BUS,
+                          "/",
+                          MECHANISM_BUS,
+                          "JobCancelPurge",
+                          g_variant_new ("(ib)",
+                                         job_id,
+                                         job_purge),
+                          G_VARIANT_TYPE ("(s)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          NULL,
+                          pp_job_cancel_purge_async_dbus_cb,
+                          NULL);
+}
+
+static void
+pp_job_set_hold_until_async_dbus_cb (GObject      *source_object,
+                                     GAsyncResult *res,
+                                     gpointer      user_data)
+{
+  GVariant *output;
+  GError   *error = NULL;
+
+  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+                                          res,
+                                          &error);
+  g_object_unref (source_object);
+
+  if (output)
+    {
+      g_variant_unref (output);
+    }
+  else
+    {
+      g_error_free (error);
+    }
+}
+
+void
+pp_job_set_hold_until_async (PpJob        *job,
+                             const gchar  *job_hold_until)
+{
+  GDBusConnection *bus;
+  GError          *error = NULL;
+  gint            *job_id;
+
+  g_object_get (job, "id", &job_id, NULL);
+
+  bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+  if (!bus)
+    {
+      g_warning ("Failed to get session bus: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_dbus_connection_call (bus,
+                          MECHANISM_BUS,
+                          "/",
+                          MECHANISM_BUS,
+                          "JobSetHoldUntil",
+                          g_variant_new ("(is)",
+                                         job_id,
+                                         job_hold_until),
+                          G_VARIANT_TYPE ("(s)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          -1,
+                          NULL,
+                          pp_job_set_hold_until_async_dbus_cb,
+                          NULL);
+}
+
+static void
+pp_job_init (PpJob *obj)
+{
+}
+
+static void
+pp_job_get_property (GObject    *object,
+                     guint       property_id,
+                     GValue     *value,
+                     GParamSpec *pspec)
+{
+  PpJobPrivate *priv;
+
+  priv = pp_job_get_instance_private (PP_JOB (object));
+
+  switch (property_id)
+  {
+    case PROP_ID:
+      g_value_set_int (value, priv->id);
+      break;
+    case PROP_TITLE:
+      g_value_set_string (value, priv->title);
+      break;
+    case PROP_STATE:
+      g_value_set_int (value, priv->state);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static void
+pp_job_set_property (GObject      *object,
+                     guint         property_id,
+                     const GValue *value,
+                     GParamSpec   *pspec)
+{
+  PpJobPrivate *priv;
+
+  priv = pp_job_get_instance_private (PP_JOB (object));
+
+  switch (property_id)
+    {
+      case PROP_ID:
+        priv->id = g_value_get_int (value);
+        break;
+      case PROP_TITLE:
+        g_free (priv->title);
+        priv->title = g_value_dup_string (value);
+        break;
+      case PROP_STATE:
+        priv->state = g_value_get_int (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+pp_job_finalize (GObject *object)
+{
+  PpJobPrivate *priv;
+
+  priv = pp_job_get_instance_private (PP_JOB (object));
+
+  g_free (priv->title);
+
+  G_OBJECT_CLASS (pp_job_parent_class)->finalize (object);
+}
+
+static void
+pp_job_class_init (PpJobClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+  object_class->get_property = pp_job_get_property;
+  object_class->set_property = pp_job_set_property;
+  object_class->finalize = pp_job_finalize;
+
+  properties[PROP_ID] = g_param_spec_int ("id",
+                                          "Id",
+                                          "Job id",
+                                          0,
+                                          G_MAXINT,
+                                          0,
+                                          G_PARAM_READWRITE);
+  properties[PROP_TITLE] = g_param_spec_string ("title",
+                                                "Title",
+                                                "Title of this print job",
+                                                NULL,
+                                                G_PARAM_READWRITE);
+  properties[PROP_STATE] = g_param_spec_int ("state",
+                                             "State",
+                                             "State of this print job (Paused, Completed, Cancelled,...)",
+                                             0,
+                                             G_MAXINT,
+                                             0,
+                                             G_PARAM_READWRITE);
+
+  g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
+}
diff --git a/panels/printers/pp-job.h b/panels/printers/pp-job.h
new file mode 100644
index 0000000..35e16e9
--- /dev/null
+++ b/panels/printers/pp-job.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2015  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_JOB_H__
+#define __PP_JOB_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#include "pp-utils.h"
+
+G_BEGIN_DECLS
+
+#define PP_TYPE_JOB (pp_job_get_type ())
+G_DECLARE_FINAL_TYPE (PpJob, pp_job, PP, JOB, GObject)
+
+struct _PpJob
+{
+  GObject parent_instance;
+};
+
+void pp_job_set_hold_until_async (PpJob        *job,
+                                  const gchar  *job_hold_until);
+
+void pp_job_cancel_purge_async (PpJob        *job,
+                                gboolean      job_purge);
+
+G_END_DECLS
+
+#endif
diff --git a/panels/printers/pp-jobs-dialog.c b/panels/printers/pp-jobs-dialog.c
index 2d8682e..1a1f31b 100644
--- a/panels/printers/pp-jobs-dialog.c
+++ b/panels/printers/pp-jobs-dialog.c
@@ -31,8 +31,11 @@
 
 #include <cups/cups.h>
 
+#include "shell/list-box-helper.h"
 #include "pp-jobs-dialog.h"
 #include "pp-utils.h"
+#include "pp-job.h"
+#include "pp-cups.h"
 
 #define EMPTY_TEXT "\xe2\x80\x94"
 
@@ -46,326 +49,166 @@ struct _PpJobsDialog {
   GtkWidget  *parent;
 
   GtkWidget  *dialog;
+  GListStore *store;
+  GtkListBox *listbox;
 
   UserResponseCallback user_callback;
   gpointer             user_data;
 
   gchar *printer_name;
 
-  cups_job_t *jobs;
-  gint num_jobs;
-  gint current_job_id;
-
   gint ref_count;
 };
 
-enum
+static void
+job_stop_cb (GtkButton *button,
+             PpJob     *job)
 {
-  JOB_ID_COLUMN,
-  JOB_TITLE_COLUMN,
-  JOB_STATE_COLUMN,
-  JOB_CREATION_TIME_COLUMN,
-  JOB_N_COLUMNS
-};
+  pp_job_cancel_purge_async (job, FALSE);
+}
 
 static void
-update_jobs_list_cb (cups_job_t *jobs,
-                     gint        num_of_jobs,
-                     gpointer    user_data)
+job_pause_cb (GtkButton *button,
+              PpJob     *job)
 {
-  GtkTreeSelection *selection;
-  PpJobsDialog     *dialog = (PpJobsDialog *) user_data;
-  GtkListStore     *store;
-  GtkTreeView      *treeview;
-  GtkTreeIter       select_iter;
-  GtkTreeIter       iter;
-  GSettings        *settings;
-  gboolean          select_iter_set = FALSE;
-  gint              i;
-  gint              select_index = 0;
-
-  treeview = (GtkTreeView*)
-    gtk_builder_get_object (dialog->builder, "job-treeview");
-
-  if (dialog->num_jobs > 0)
-    cupsFreeJobs (dialog->num_jobs, dialog->jobs);
-
-  dialog->num_jobs = num_of_jobs;
-  dialog->jobs = jobs;
-
-  store = gtk_list_store_new (JOB_N_COLUMNS,
-                              G_TYPE_INT,
-                              G_TYPE_STRING,
-                              G_TYPE_STRING,
-                              G_TYPE_STRING);
-
-  if (dialog->current_job_id >= 0)
-    {
-      for (i = 0; i < dialog->num_jobs; i++)
-        {
-          select_index = i;
-          if (dialog->jobs[i].id >= dialog->current_job_id)
-            break;
-        }
-    }
-
-  for (i = 0; i < dialog->num_jobs; i++)
-    {
-      GDesktopClockFormat  value;
-      GDateTime           *time;
-      struct tm *ts;
-      gchar     *time_string;
-      gchar     *state = NULL;
-
-      ts = localtime (&(dialog->jobs[i].creation_time));
-      time = g_date_time_new_local (ts->tm_year + 1900,
-                                    ts->tm_mon + 1,
-                                    ts->tm_mday,
-                                    ts->tm_hour,
-                                    ts->tm_min,
-                                    ts->tm_sec);
-
-      settings = g_settings_new (CLOCK_SCHEMA);
-      value = g_settings_get_enum (settings, CLOCK_FORMAT_KEY);
-
-      if (value == G_DESKTOP_CLOCK_FORMAT_24H)
-        time_string = g_date_time_format (time, "%k:%M");
-      else
-        time_string = g_date_time_format (time, "%l:%M %p");
-
-      g_date_time_unref (time);
-
-      switch (dialog->jobs[i].state)
-        {
-          case IPP_JOB_PENDING:
-            /* Translators: Job's state (job is waiting to be printed) */
-            state = g_strdup (C_("print job", "Pending"));
-            break;
-          case IPP_JOB_HELD:
-            /* Translators: Job's state (job is held for printing) */
-            state = g_strdup (C_("print job", "Held"));
-            break;
-          case IPP_JOB_PROCESSING:
-            /* Translators: Job's state (job is currently printing) */
-            state = g_strdup (C_("print job", "Processing"));
-            break;
-          case IPP_JOB_STOPPED:
-            /* Translators: Job's state (job has been stopped) */
-            state = g_strdup (C_("print job", "Stopped"));
-            break;
-          case IPP_JOB_CANCELED:
-            /* Translators: Job's state (job has been canceled) */
-            state = g_strdup (C_("print job", "Canceled"));
-            break;
-          case IPP_JOB_ABORTED:
-            /* Translators: Job's state (job has aborted due to error) */
-            state = g_strdup (C_("print job", "Aborted"));
-            break;
-          case IPP_JOB_COMPLETED:
-            /* Translators: Job's state (job has completed successfully) */
-            state = g_strdup (C_("print job", "Completed"));
-            break;
-        }
-
-      gtk_list_store_append (store, &iter);
-      gtk_list_store_set (store, &iter,
-                          JOB_ID_COLUMN, dialog->jobs[i].id,
-                          JOB_TITLE_COLUMN, dialog->jobs[i].title,
-                          JOB_STATE_COLUMN, state,
-                          JOB_CREATION_TIME_COLUMN, time_string,
-                          -1);
-
-      if (i == select_index)
-        {
-          select_iter = iter;
-          select_iter_set = TRUE;
-          dialog->current_job_id = dialog->jobs[i].id;
-        }
-
-      g_free (time_string);
-      g_free (state);
-    }
+  gint job_state;
 
-  gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
+  g_object_get (job, "state", &job_state, NULL);
 
-  if (select_iter_set &&
-      (selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview))))
-    {
-      gtk_tree_selection_select_iter (selection, &select_iter);
-    }
+  pp_job_set_hold_until_async (job, job_state == IPP_JOB_HELD ? "no-hold" : "indefinite");
 
-  g_object_unref (store);
-  dialog->ref_count--;
+  gtk_button_set_image (button,
+                        gtk_image_new_from_icon_name (job_state == IPP_JOB_HELD ?
+                                                      "media-playback-pause-symbolic" : 
"media-playback-start-symbolic",
+                                                      GTK_ICON_SIZE_SMALL_TOOLBAR));
 }
 
-static void
-update_jobs_list (PpJobsDialog *dialog)
+static GtkWidget *
+create_listbox_row (gpointer item,
+                    gpointer user_data)
 {
-  if (dialog->printer_name)
+  PpJob     *job = (PpJob *)item;
+  GtkWidget *box;
+  GtkWidget *widget;
+  gchar     *title;
+  gchar     *state_string;
+  gint       job_state;
+
+  g_object_get (job, "title", &title, "state", &job_state, NULL);
+
+  switch (job_state)
     {
-      dialog->ref_count++;
-      cups_get_jobs_async (dialog->printer_name,
-                           TRUE,
-                           CUPS_WHICHJOBS_ACTIVE,
-                           update_jobs_list_cb,
-                           dialog);
+      case IPP_JOB_PENDING:
+        /* Translators: Job's state (job is waiting to be printed) */
+        state_string = g_strdup (C_("print job", "Pending"));
+        break;
+      case IPP_JOB_HELD:
+        /* Translators: Job's state (job is held for printing) */
+        state_string = g_strdup (C_("print job", "Paused"));
+        break;
+      case IPP_JOB_PROCESSING:
+        /* Translators: Job's state (job is currently printing) */
+        state_string = g_strdup (C_("print job", "Processing"));
+        break;
+      case IPP_JOB_STOPPED:
+        /* Translators: Job's state (job has been stopped) */
+        state_string = g_strdup (C_("print job", "Stopped"));
+        break;
+      case IPP_JOB_CANCELED:
+        /* Translators: Job's state (job has been canceled) */
+        state_string = g_strdup (C_("print job", "Canceled"));
+        break;
+      case IPP_JOB_ABORTED:
+        /* Translators: Job's state (job has aborted due to error) */
+        state_string = g_strdup (C_("print job", "Aborted"));
+        break;
+      case IPP_JOB_COMPLETED:
+        /* Translators: Job's state (job has completed successfully) */
+        state_string = g_strdup (C_("print job", "Completed"));
+        break;
     }
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  g_object_set (box, "margin", 6, NULL);
+  gtk_container_set_border_width (GTK_CONTAINER (box), 2);
+
+  widget = gtk_label_new (title);
+  gtk_widget_set_halign (widget, GTK_ALIGN_START);
+  gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 10);
+
+  widget = gtk_label_new (state_string);
+  gtk_widget_set_halign (widget, GTK_ALIGN_END);
+  gtk_widget_set_margin_end (widget, 64);
+  gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 10);
+
+  widget = gtk_button_new_from_icon_name (job_state == IPP_JOB_HELD ? "media-playback-start-symbolic" : 
"media-playback-pause-symbolic",
+                                          GTK_ICON_SIZE_SMALL_TOOLBAR);
+  g_signal_connect (widget, "clicked", G_CALLBACK (job_pause_cb), item);
+  gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 4);
+
+  widget = gtk_button_new_from_icon_name ("edit-delete-symbolic",
+                                          GTK_ICON_SIZE_SMALL_TOOLBAR);
+  g_signal_connect (widget, "clicked", G_CALLBACK (job_stop_cb), item);
+  gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 4);
+
+  gtk_widget_show_all (box);
+
+  return box;
 }
 
 static void
-job_selection_changed_cb (GtkTreeSelection *selection,
-                          gpointer          user_data)
+update_jobs_list_cb (cups_job_t *jobs,
+                     gint        num_of_jobs,
+                     gpointer    user_data)
 {
-  PpJobsDialog *dialog = (PpJobsDialog *) user_data;
-  GtkTreeModel *model;
-  GtkTreeIter   iter;
-  GtkWidget    *widget;
-  gboolean      release_button_sensitive = FALSE;
-  gboolean      hold_button_sensitive = FALSE;
-  gboolean      cancel_button_sensitive = FALSE;
-  gint          id = -1;
-  gint          i;
-
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+  PpJobsDialog *dialog = user_data;
+  GtkWidget    *clear_all_button;
+  GtkStack     *stack;
+  guint         i;
+
+  g_list_store_remove_all (dialog->store);
+
+  stack = GTK_STACK (gtk_builder_get_object (GTK_BUILDER (dialog->builder), "stack"));
+  clear_all_button = GTK_WIDGET (gtk_builder_get_object (GTK_BUILDER (dialog->builder), 
"jobs-clear-all-button"));
+
+  if (num_of_jobs > 0)
     {
-      gtk_tree_model_get (model, &iter,
-                          JOB_ID_COLUMN, &id,
-                          -1);
+      gtk_widget_set_sensitive (clear_all_button, TRUE);
+      gtk_stack_set_visible_child_name (stack, "list-jobs-page");
     }
   else
     {
-      id = -1;
+      gtk_widget_set_sensitive (clear_all_button, FALSE);
+      gtk_stack_set_visible_child_name (stack, "no-jobs-page");
     }
 
-  dialog->current_job_id = id;
-
-  if (dialog->current_job_id >= 0 &&
-      dialog->jobs != NULL)
+  for (i = 0; i < num_of_jobs; i++)
     {
-      for (i = 0; i < dialog->num_jobs; i++)
-        {
-          if (dialog->jobs[i].id == dialog->current_job_id)
-            {
-              ipp_jstate_t job_state = dialog->jobs[i].state;
-
-              release_button_sensitive = job_state == IPP_JOB_HELD;
-              hold_button_sensitive = job_state == IPP_JOB_PENDING;
-              cancel_button_sensitive = job_state < IPP_JOB_CANCELED;
-
-              break;
-            }
-        }
+      PpJob *job;
+
+      job = g_object_new (pp_job_get_type (),
+                          "id", jobs[i].id,
+                          "title", jobs[i].title,
+                          "state", jobs[i].state,
+                          NULL);
+      g_list_store_append (dialog->store, job);
     }
 
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-release-button");
-  gtk_widget_set_sensitive (widget, release_button_sensitive);
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-hold-button");
-  gtk_widget_set_sensitive (widget, hold_button_sensitive);
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-cancel-button");
-  gtk_widget_set_sensitive (widget, cancel_button_sensitive);
-}
-
-static void
-populate_jobs_list (PpJobsDialog *dialog)
-{
-  GtkTreeViewColumn *column;
-  GtkCellRenderer   *renderer;
-  GtkCellRenderer   *title_renderer;
-  GtkTreeView       *treeview;
-
-  treeview = (GtkTreeView*)
-    gtk_builder_get_object (dialog->builder, "job-treeview");
-
-  renderer = gtk_cell_renderer_text_new ();
-  title_renderer = gtk_cell_renderer_text_new ();
-
-  /* Translators: Name of column showing titles of print jobs */
-  column = gtk_tree_view_column_new_with_attributes (_("Job Title"), title_renderer,
-                                                     "text", JOB_TITLE_COLUMN, NULL);
-  g_object_set (G_OBJECT (title_renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
-  gtk_tree_view_column_set_fixed_width (column, 180);
-  gtk_tree_view_column_set_min_width (column, 180);
-  gtk_tree_view_column_set_max_width (column, 180);
-  gtk_tree_view_append_column (treeview, column);
-
-  /* Translators: Name of column showing statuses of print jobs */
-  column = gtk_tree_view_column_new_with_attributes (_("Job State"), renderer,
-                                                     "text", JOB_STATE_COLUMN, NULL);
-  gtk_tree_view_column_set_expand (column, TRUE);
-  gtk_tree_view_append_column (treeview, column);
-
-  /* Translators: Name of column showing times of creation of print jobs */
-  column = gtk_tree_view_column_new_with_attributes (_("Time"), renderer,
-                                                     "text", JOB_CREATION_TIME_COLUMN, NULL);
-  gtk_tree_view_column_set_expand (column, TRUE);
-  gtk_tree_view_append_column (treeview, column);
-
-  g_signal_connect (gtk_tree_view_get_selection (treeview),
-                    "changed", G_CALLBACK (job_selection_changed_cb), dialog);
-
-  update_jobs_list (dialog);
-}
-
-static void
-job_process_cb_cb (gpointer user_data)
-{
+  dialog->ref_count--;
 }
 
 static void
-job_process_cb (GtkButton *button,
-                gpointer   user_data)
+update_jobs_list (PpJobsDialog *dialog)
 {
-  PpJobsDialog *dialog = (PpJobsDialog *) user_data;
-  GtkWidget    *widget;
-
-  if (dialog->current_job_id >= 0)
+  if (dialog->printer_name != NULL)
     {
-      if ((GtkButton*) gtk_builder_get_object (dialog->builder,
-                                               "job-cancel-button") ==
-          button)
-        {
-          job_cancel_purge_async (dialog->current_job_id,
-                                  FALSE,
-                                  NULL,
-                                  job_process_cb_cb,
-                                  dialog);
-        }
-      else if ((GtkButton*) gtk_builder_get_object (dialog->builder,
-                                                    "job-hold-button") ==
-               button)
-        {
-          job_set_hold_until_async (dialog->current_job_id,
-                                    "indefinite",
-                                    NULL,
-                                    job_process_cb_cb,
-                                    dialog);
-        }
-      else
-        {
-          job_set_hold_until_async (dialog->current_job_id,
-                                    "no-hold",
-                                    NULL,
-                                    job_process_cb_cb,
-                                    dialog);
-        }
-  }
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-release-button");
-  gtk_widget_set_sensitive (widget, FALSE);
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-hold-button");
-  gtk_widget_set_sensitive (widget, FALSE);
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-cancel-button");
-  gtk_widget_set_sensitive (widget, FALSE);
+      dialog->ref_count++;
+      cups_get_jobs_async (dialog->printer_name,
+                           TRUE,
+                           CUPS_WHICHJOBS_ACTIVE,
+                           update_jobs_list_cb,
+                           dialog);
+    }
 }
 
 static void
@@ -382,15 +225,32 @@ jobs_dialog_response_cb (GtkDialog *dialog,
                               jobs_dialog->user_data);
 }
 
+static void
+on_clear_all_button_clicked (GtkButton *button,
+                             gpointer   user_data)
+{
+  PpJobsDialog *dialog = user_data;
+  guint num_items;
+  guint i;
+
+  num_items = g_list_model_get_n_items (G_LIST_MODEL (dialog->store));
+
+  for (i = 0; i < num_items; i++)
+    {
+      PpJob *job = PP_JOB (g_list_model_get_item (G_LIST_MODEL (dialog->store), i));
+
+      pp_job_cancel_purge_async (job, FALSE);
+    }
+}
+
 PpJobsDialog *
 pp_jobs_dialog_new (GtkWindow            *parent,
                     UserResponseCallback  user_callback,
                     gpointer              user_data,
                     gchar                *printer_name)
 {
-  GtkStyleContext *context;
   PpJobsDialog    *dialog;
-  GtkWidget       *widget;
+  GtkButton       *clear_all_button;
   GError          *error = NULL;
   gchar           *objects[] = { "jobs-dialog", NULL };
   guint            builder_result;
@@ -416,43 +276,28 @@ pp_jobs_dialog_new (GtkWindow            *parent,
   dialog->user_callback = user_callback;
   dialog->user_data = user_data;
   dialog->printer_name = g_strdup (printer_name);
-  dialog->current_job_id = -1;
   dialog->ref_count = 0;
 
   /* connect signals */
   g_signal_connect (dialog->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
   g_signal_connect (dialog->dialog, "response", G_CALLBACK (jobs_dialog_response_cb), dialog);
 
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-cancel-button");
-  g_signal_connect (widget, "clicked", G_CALLBACK (job_process_cb), dialog);
+  clear_all_button = GTK_BUTTON (gtk_builder_get_object (dialog->builder, "jobs-clear-all-button"));
+  g_signal_connect (clear_all_button, "clicked", G_CALLBACK (on_clear_all_button_clicked), dialog);
 
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-hold-button");
-  g_signal_connect (widget, "clicked", G_CALLBACK (job_process_cb), dialog);
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-release-button");
-  g_signal_connect (widget, "clicked", G_CALLBACK (job_process_cb), dialog);
-
-
-  /* Set junctions */
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "queue-scrolledwindow");
-  context = gtk_widget_get_style_context (widget);
-  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
-
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "queue-toolbar");
-  context = gtk_widget_get_style_context (widget);
-  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
-
-
-  title = g_strdup_printf (_("%s Active Jobs"), printer_name);
+  /* Translators: This is the printer name for which we are showing the active jobs */
+  title = g_strdup_printf (C_("Printer jobs dialog title", "%s - Active Jobs"), printer_name);
   gtk_window_set_title (GTK_WINDOW (dialog->dialog), title);
   g_free (title);
 
-  populate_jobs_list (dialog);
+  dialog->listbox = GTK_LIST_BOX (gtk_builder_get_object (dialog->builder, "jobs-listbox"));
+  gtk_list_box_set_header_func (dialog->listbox,
+                                cc_list_box_update_header_func, NULL, NULL);
+  dialog->store = g_list_store_new (pp_job_get_type ());
+  gtk_list_box_bind_model (dialog->listbox, G_LIST_MODEL (dialog->store),
+                           create_listbox_row, NULL, NULL);
+
+  update_jobs_list (dialog);
 
   gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), GTK_WINDOW (parent));
   gtk_window_present (GTK_WINDOW (dialog->dialog));
@@ -480,9 +325,6 @@ pp_jobs_dialog_free_idle (gpointer user_data)
       g_object_unref (dialog->builder);
       dialog->builder = NULL;
 
-      if (dialog->num_jobs > 0)
-        cupsFreeJobs (dialog->num_jobs, dialog->jobs);
-
       g_free (dialog->printer_name);
 
       g_free (dialog);
diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c
index 899bcf3..151d337 100644
--- a/panels/printers/pp-utils.c
+++ b/panels/printers/pp-utils.c
@@ -3824,168 +3824,6 @@ cups_get_jobs_async (const gchar *printer_name,
     }
 }
 
-typedef struct
-{
-  GCancellable *cancellable;
-  JCPCallback   callback;
-  gpointer      user_data;
-} JCPData;
-
-static void
-job_cancel_purge_async_dbus_cb (GObject      *source_object,
-                                GAsyncResult *res,
-                                gpointer      user_data)
-{
-  GVariant *output;
-  JCPData  *data = (JCPData *) user_data;
-  GError   *error = NULL;
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  g_object_unref (source_object);
-
-  if (output)
-    {
-      g_variant_unref (output);
-    }
-  else
-    {
-      if (!g_cancellable_is_cancelled (data->cancellable))
-        g_warning ("%s", error->message);
-      g_error_free (error);
-    }
-
-  data->callback (data->user_data);
-
-  if (data->cancellable)
-    g_object_unref (data->cancellable);
-  g_free (data);
-}
-
-void
-job_cancel_purge_async (gint          job_id,
-                        gboolean      job_purge,
-                        GCancellable *cancellable,
-                        JCPCallback   callback,
-                        gpointer      user_data)
-{
-  GDBusConnection *bus;
-  JCPData         *data;
-  GError          *error = NULL;
-
-  bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
-  if (!bus)
-    {
-      g_warning ("Failed to get session bus: %s", error->message);
-      g_error_free (error);
-      callback (user_data);
-      return;
-    }
-
-  data = g_new0 (JCPData, 1);
-  if (cancellable)
-    data->cancellable = g_object_ref (cancellable);
-  data->callback = callback;
-  data->user_data = user_data;
-
-  g_dbus_connection_call (bus,
-                          MECHANISM_BUS,
-                          "/",
-                          MECHANISM_BUS,
-                          "JobCancelPurge",
-                          g_variant_new ("(ib)",
-                                         job_id,
-                                         job_purge),
-                          G_VARIANT_TYPE ("(s)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          -1,
-                          NULL,
-                          job_cancel_purge_async_dbus_cb,
-                          data);
-}
-
-typedef struct
-{
-  GCancellable *cancellable;
-  JSHUCallback  callback;
-  gpointer      user_data;
-} JSHUData;
-
-static void
-job_set_hold_until_async_dbus_cb (GObject      *source_object,
-                                  GAsyncResult *res,
-                                  gpointer      user_data)
-{
-  GVariant *output;
-  JSHUData *data = (JSHUData *) user_data;
-  GError   *error = NULL;
-
-  output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
-                                          res,
-                                          &error);
-  g_object_unref (source_object);
-
-  if (output)
-    {
-      g_variant_unref (output);
-    }
-  else
-    {
-      if (!g_cancellable_is_cancelled (data->cancellable))
-        g_warning ("%s", error->message);
-      g_error_free (error);
-    }
-
-  data->callback (data->user_data);
-
-  if (data->cancellable)
-    g_object_unref (data->cancellable);
-  g_free (data);
-}
-
-void
-job_set_hold_until_async (gint          job_id,
-                          const gchar  *job_hold_until,
-                          GCancellable *cancellable,
-                          JSHUCallback  callback,
-                          gpointer      user_data)
-{
-  GDBusConnection *bus;
-  JSHUData        *data;
-  GError          *error = NULL;
-
-  bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
-  if (!bus)
-    {
-      g_warning ("Failed to get session bus: %s", error->message);
-      g_error_free (error);
-      callback (user_data);
-      return;
-    }
-
-  data = g_new0 (JSHUData, 1);
-  if (cancellable)
-    data->cancellable = g_object_ref (cancellable);
-  data->callback = callback;
-  data->user_data = user_data;
-
-  g_dbus_connection_call (bus,
-                          MECHANISM_BUS,
-                          "/",
-                          MECHANISM_BUS,
-                          "JobSetHoldUntil",
-                          g_variant_new ("(is)",
-                                         job_id,
-                                         job_hold_until),
-                          G_VARIANT_TYPE ("(s)"),
-                          G_DBUS_CALL_FLAGS_NONE,
-                          -1,
-                          NULL,
-                          job_set_hold_until_async_dbus_cb,
-                          data);
-}
-
 gchar *
 guess_device_hostname (PpPrintDevice *device)
 {
diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h
index 9b39b6d..9783823 100644
--- a/panels/printers/pp-utils.h
+++ b/panels/printers/pp-utils.h
@@ -259,22 +259,6 @@ void        cups_get_jobs_async (const gchar *printer_name,
                                  CGJCallback  callback,
                                  gpointer     user_data);
 
-typedef void (*JCPCallback) (gpointer user_data);
-
-void job_cancel_purge_async (gint          job_id,
-                             gboolean      job_purge,
-                             GCancellable *cancellable,
-                             JCPCallback   callback,
-                             gpointer      user_data);
-
-typedef void (*JSHUCallback) (gpointer user_data);
-
-void job_set_hold_until_async (gint          job_id,
-                               const gchar  *job_hold_until,
-                               GCancellable *cancellable,
-                               JSHUCallback  callback,
-                               gpointer      user_data);
-
 void         pp_devices_list_free (PpDevicesList *result);
 
 const gchar *get_page_size_from_locale (void);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]