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



commit d8a864dd1c3d18979dd18fbc5aa84c0a88d1c840
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         |  133 +++++++++++
 panels/printers/pp-job.h         |   38 +++
 panels/printers/pp-jobs-dialog.c |  471 ++++++++++++++------------------------
 panels/printers/pp-utils.c       |   29 ++-
 panels/printers/pp-utils.h       |    8 +-
 7 files changed, 423 insertions(+), 403 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..c47381d 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">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">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..cdb8f07
--- /dev/null
+++ b/panels/printers/pp-job.c
@@ -0,0 +1,133 @@
+/* -*- 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_init (PpJob *obj)
+{
+}
+
+static void
+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
+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
+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 = job_get_property;
+  object_class->set_property = job_set_property;
+  object_class->finalize = job_finalize;
+
+  properties[PROP_ID] = g_param_spec_int ("id", "id", "id",
+                                          0, G_MAXINT, 0, G_PARAM_READWRITE);
+  properties[PROP_TITLE] = g_param_spec_string ("title", "title", "title",
+                                                NULL, G_PARAM_READWRITE);
+  properties[PROP_STATE] = g_param_spec_int ("state", "state", "state",
+                                             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..e42ccf8
--- /dev/null
+++ b/panels/printers/pp-job.h
@@ -0,0 +1,38 @@
+/* -*- 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 <glib-object.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;
+};
+
+G_END_DECLS
+
+#endif
diff --git a/panels/printers/pp-jobs-dialog.c b/panels/printers/pp-jobs-dialog.c
index 2d8682e..4c3e9b5 100644
--- a/panels/printers/pp-jobs-dialog.c
+++ b/panels/printers/pp-jobs-dialog.c
@@ -31,8 +31,10 @@
 
 #include <cups/cups.h>
 
+#include "shell/list-box-helper.h"
 #include "pp-jobs-dialog.h"
 #include "pp-utils.h"
+#include "pp-job.h"
 
 #define EMPTY_TEXT "\xe2\x80\x94"
 
@@ -46,6 +48,7 @@ struct _PpJobsDialog {
   GtkWidget  *parent;
 
   GtkWidget  *dialog;
+  GtkListBox *listbox;
 
   UserResponseCallback user_callback;
   gpointer             user_data;
@@ -54,318 +57,189 @@ struct _PpJobsDialog {
 
   cups_job_t *jobs;
   gint num_jobs;
-  gint current_job_id;
 
   gint ref_count;
 };
 
-enum
+static void
+job_stop_cb (GtkButton *button,
+             gint       job_id)
 {
-  JOB_ID_COLUMN,
-  JOB_TITLE_COLUMN,
-  JOB_STATE_COLUMN,
-  JOB_CREATION_TIME_COLUMN,
-  JOB_N_COLUMNS
-};
+  job_cancel_purge_async (job_id,
+                          FALSE,
+                          NULL,
+                          NULL);
+}
 
 static void
-update_jobs_list_cb (cups_job_t *jobs,
-                     gint        num_of_jobs,
-                     gpointer    user_data)
+job_pause_cb (GtkButton *button,
+              gpointer   user_data)
 {
-  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");
+  PpJob *job = (PpJob *)user_data;
+  gint   job_id;
+  gint   job_state;
+
+  g_object_get (job,
+                "id", &job_id,
+                "state", &job_state,
+                NULL);
+
+  job_set_hold_until_async (job_id,
+                            job_state == IPP_JOB_HELD ? "no-hold" : "indefinite",
+                            NULL,
+                            NULL);
+
+  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));
+}
 
-  if (dialog->num_jobs > 0)
-    cupsFreeJobs (dialog->num_jobs, dialog->jobs);
+static GtkWidget *
+create_listbox_row (gpointer item,
+                    gpointer user_data)
+{
+  PpJob     *job = (PpJob *)item;
+  GtkWidget *box;
+  GtkWidget *widget;
+  gchar     *state_string;
+  gint      *job_id;
+  gint       job_state = NULL;
+
+  g_object_get (job,
+                "id", &job_id,
+                "state", &job_state,
+                NULL);
+
+  switch (job_state)
+  {
+    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;
+  }
 
-  dialog->num_jobs = num_of_jobs;
-  dialog->jobs = jobs;
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  g_object_set (box, "margin", 6, NULL);
+  gtk_container_set_border_width (GTK_CONTAINER (box), 2);
 
-  store = gtk_list_store_new (JOB_N_COLUMNS,
-                              G_TYPE_INT,
-                              G_TYPE_STRING,
-                              G_TYPE_STRING,
-                              G_TYPE_STRING);
+  widget = gtk_label_new ("");
+  g_object_bind_property (job, "title", widget, "label", G_BINDING_SYNC_CREATE);
+  gtk_widget_set_halign (widget, GTK_ALIGN_START);
+  gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 10);
 
-  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;
-        }
-    }
+  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);
 
-  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);
-    }
+  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);
 
-  gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
+  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), job_id);
+  gtk_box_pack_start (GTK_BOX (box), widget, FALSE, FALSE, 4);
 
-  if (select_iter_set &&
-      (selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview))))
-    {
-      gtk_tree_selection_select_iter (selection, &select_iter);
-    }
+  gtk_widget_show_all (box);
 
-  g_object_unref (store);
-  dialog->ref_count--;
+  return box;
 }
 
 static void
-update_jobs_list (PpJobsDialog *dialog)
-{
-  if (dialog->printer_name)
-    {
-      dialog->ref_count++;
-      cups_get_jobs_async (dialog->printer_name,
-                           TRUE,
-                           CUPS_WHICHJOBS_ACTIVE,
-                           update_jobs_list_cb,
-                           dialog);
-    }
-}
-
-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;
+  PpJobsDialog *dialog = user_data;
+  GListStore   *store;
   gint          i;
+  GtkStack     *stack;
+  GtkWidget    *clear_all_button;
+
+  store = g_list_store_new (pp_job_get_type ());
 
-  if (gtk_tree_selection_get_selected (selection, &model, &iter))
+  stack = (GtkStack*) gtk_builder_get_object (GTK_BUILDER (dialog->builder), "stack");
+  clear_all_button = (GtkWidget*) 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 (GTK_STACK (stack), "list-jobs-page");
     }
   else
     {
-      id = -1;
+      gtk_widget_set_sensitive (clear_all_button, FALSE);
+      gtk_stack_set_visible_child_name (GTK_STACK (stack), "no-jobs-page");
     }
 
-  dialog->current_job_id = id;
+  if (dialog->num_jobs > 0)
+    cupsFreeJobs (dialog->num_jobs, dialog->jobs);
 
-  if (dialog->current_job_id >= 0 &&
-      dialog->jobs != NULL)
-    {
-      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;
-            }
-        }
-    }
+  dialog->num_jobs = num_of_jobs;
+  dialog->jobs = jobs;
 
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-release-button");
-  gtk_widget_set_sensitive (widget, release_button_sensitive);
+  for (i = 0; i < dialog->num_jobs; i++)
+    {
+      gchar *state = NULL;
+      PpJob *job;
 
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-hold-button");
-  gtk_widget_set_sensitive (widget, hold_button_sensitive);
+      job = g_object_new (pp_job_get_type (),
+                          "id", dialog->jobs[i].id,
+                          "title", dialog->jobs[i].title,
+                          "state", dialog->jobs[i].state,
+                          NULL);
+      g_list_store_append (store, job);
 
-  widget = (GtkWidget*)
-    gtk_builder_get_object (dialog->builder, "job-cancel-button");
-  gtk_widget_set_sensitive (widget, cancel_button_sensitive);
-}
+      g_object_unref (job);
+      g_free (state);
+    }
 
-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);
+  gtk_list_box_bind_model (dialog->listbox, G_LIST_MODEL (store),
+                           create_listbox_row, NULL, NULL);
 
-  update_jobs_list (dialog);
-}
+  gtk_container_add (GTK_CONTAINER (dialog->listbox), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
 
-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)
     {
-      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 +256,27 @@ 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;
+
+  job_cancel_purge_all (dialog->jobs,
+                        dialog->num_jobs,
+                        FALSE,
+                        NULL,
+                        NULL);
+}
+
 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 +302,26 @@ 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);
-
-  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);
-
+  clear_all_button = (GtkButton *) 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);
 
-  /* 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 to 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 = (GtkListBox*)
+        gtk_builder_get_object (dialog->builder, "jobs-listbox");
+  gtk_list_box_set_header_func (dialog->listbox,
+                                cc_list_box_update_header_func, 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));
diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c
index 899bcf3..f9314eb 100644
--- a/panels/printers/pp-utils.c
+++ b/panels/printers/pp-utils.c
@@ -3856,18 +3856,33 @@ job_cancel_purge_async_dbus_cb (GObject      *source_object,
       g_error_free (error);
     }
 
-  data->callback (data->user_data);
-
   if (data->cancellable)
     g_object_unref (data->cancellable);
   g_free (data);
 }
 
 void
+job_cancel_purge_all (cups_job_t   *jobs,
+                      gint          num_of_jobs,
+                      gboolean      job_purge,
+                      GCancellable *cancellable,
+                      gpointer      user_data)
+{
+  gint i;
+
+  for (i = 0; i < num_of_jobs; i++)
+   {
+     job_cancel_purge_async (jobs[i].id,
+                             job_purge,
+                             cancellable,
+                             user_data);
+   }
+}
+
+void
 job_cancel_purge_async (gint          job_id,
                         gboolean      job_purge,
                         GCancellable *cancellable,
-                        JCPCallback   callback,
                         gpointer      user_data)
 {
   GDBusConnection *bus;
@@ -3879,14 +3894,12 @@ job_cancel_purge_async (gint          job_id,
     {
       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,
@@ -3908,7 +3921,6 @@ job_cancel_purge_async (gint          job_id,
 typedef struct
 {
   GCancellable *cancellable;
-  JSHUCallback  callback;
   gpointer      user_data;
 } JSHUData;
 
@@ -3937,8 +3949,6 @@ job_set_hold_until_async_dbus_cb (GObject      *source_object,
       g_error_free (error);
     }
 
-  data->callback (data->user_data);
-
   if (data->cancellable)
     g_object_unref (data->cancellable);
   g_free (data);
@@ -3948,7 +3958,6 @@ void
 job_set_hold_until_async (gint          job_id,
                           const gchar  *job_hold_until,
                           GCancellable *cancellable,
-                          JSHUCallback  callback,
                           gpointer      user_data)
 {
   GDBusConnection *bus;
@@ -3960,14 +3969,12 @@ job_set_hold_until_async (gint          job_id,
     {
       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,
diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h
index 9b39b6d..0b0fdd5 100644
--- a/panels/printers/pp-utils.h
+++ b/panels/printers/pp-utils.h
@@ -264,15 +264,19 @@ typedef void (*JCPCallback) (gpointer user_data);
 void job_cancel_purge_async (gint          job_id,
                              gboolean      job_purge,
                              GCancellable *cancellable,
-                             JCPCallback   callback,
                              gpointer      user_data);
 
+void job_cancel_purge_all (cups_job_t   *jobs,
+                           gint          num_of_jobs,
+                           gboolean      job_purge,
+                           GCancellable *cancellable,
+                           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);



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