pp-printer-entry.c 33.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * Copyright 2017 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 <felipeborges@gnome.org>
 */

#include <config.h>

#include "pp-printer-entry.h"
#include <gtk/gtk.h>
#include <glib/gi18n-lib.h>
#include <glib/gstdio.h>

27
#include "pp-details-dialog.h"
28
#include "pp-maintenance-command.h"
29 30
#include "pp-options-dialog.h"
#include "pp-jobs-dialog.h"
31
#include "pp-printer.h"
32 33
#include "pp-utils.h"

34
#define SUPPLY_BAR_HEIGHT 8
35

36 37 38 39 40 41 42 43
typedef struct
{
  gchar *marker_names;
  gchar *marker_levels;
  gchar *marker_colors;
  gchar *marker_types;
} InkLevelData;

44 45
struct _PpPrinterEntry
{
46
  GtkListBoxRow parent;
47 48 49 50 51 52 53 54

  gchar    *printer_name;
  gboolean  is_accepting_jobs;
  gchar    *printer_make_and_model;
  gchar    *printer_location;
  gchar    *printer_hostname;
  gboolean  is_authorized;
  gint      printer_state;
55
  InkLevelData *inklevel;
56

57 58 59 60
  /* Maintenance commands */
  PpMaintenanceCommand *clean_command;
  GCancellable *check_clean_heads_cancellable;

61 62 63 64 65 66 67 68
  /* Widgets */
  GtkImage       *printer_icon;
  GtkLabel       *printer_status;
  GtkLabel       *printer_name_label;
  GtkLabel       *printer_model_label;
  GtkLabel       *printer_model;
  GtkLabel       *printer_location_label;
  GtkLabel       *printer_location_address_label;
69 70
  GtkLabel       *printer_inklevel_label;
  GtkFrame       *supply_frame;
71 72
  GtkDrawingArea *supply_drawing_area;
  GtkWidget      *show_jobs_dialog_button;
73
  GtkWidget      *clean_heads_menuitem;
74 75 76 77 78 79 80
  GtkCheckButton *printer_default_checkbutton;
  GtkModelButton *remove_printer_menuitem;
  GtkBox         *printer_error;
  GtkLabel       *error_status;

  /* Dialogs */
  PpJobsDialog    *pp_jobs_dialog;
81 82

  GCancellable *get_jobs_cancellable;
83 84 85 86
};

struct _PpPrinterEntryClass
{
87
  GtkListBoxRowClass parent_class;
88 89

  void (*printer_changed) (PpPrinterEntry *printer_entry);
90
  void (*printer_delete)  (PpPrinterEntry *printer_entry);
91
  void (*printer_renamed) (PpPrinterEntry *printer_entry, const gchar *new_name);
92 93
};

94
G_DEFINE_TYPE (PpPrinterEntry, pp_printer_entry, GTK_TYPE_LIST_BOX_ROW)
95 96 97

enum {
  IS_DEFAULT_PRINTER,
98
  PRINTER_DELETE,
99
  PRINTER_RENAMED,
100 101 102 103 104
  LAST_SIGNAL,
};

static guint signals[LAST_SIGNAL] = { 0 };

Robert Ancell's avatar
Robert Ancell committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
static InkLevelData *
ink_level_data_new (void)
{
  return g_slice_new0 (InkLevelData);
}

static void
ink_level_data_free (InkLevelData *data)
{
  g_clear_pointer (&data->marker_names, g_free);
  g_clear_pointer (&data->marker_levels, g_free);
  g_clear_pointer (&data->marker_colors, g_free);
  g_clear_pointer (&data->marker_types, g_free);
  g_slice_free (InkLevelData, data);
}

121 122 123 124
static void
pp_printer_entry_init (PpPrinterEntry *self)
{
  gtk_widget_init_template (GTK_WIDGET (self));
Robert Ancell's avatar
Robert Ancell committed
125
  self->inklevel = ink_level_data_new ();
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
}

typedef struct {
  gchar *color;
  gchar *type;
  gchar *name;
  gint   level;
} MarkerItem;

static gint
markers_cmp (gconstpointer a,
             gconstpointer b)
{
  MarkerItem *x = (MarkerItem*) a;
  MarkerItem *y = (MarkerItem*) b;

  if (x->level < y->level)
    return 1;
  else if (x->level == y->level)
    return 0;
  else
    return -1;
}

static gchar *
151
sanitize_printer_model (const gchar *printer_make_and_model)
152
{
153 154 155 156
  gchar            *breakpoint = NULL, *tmp2 = NULL;
  g_autofree gchar *tmp = NULL;
  gchar             backup;
  size_t            length = 0;
Yuri Chornoivan's avatar
Yuri Chornoivan committed
157
  gchar            *forbidden[] = {
158 159 160 161 162 163 164 165 166 167 168
    "foomatic",
    ",",
    "hpijs",
    "hpcups",
    "(recommended)",
    "postscript (recommended)",
    NULL };
  int     i;

  tmp = g_ascii_strdown (printer_make_and_model, -1);

Yuri Chornoivan's avatar
Yuri Chornoivan committed
169
  for (i = 0; i < g_strv_length (forbidden); i++)
170
    {
Yuri Chornoivan's avatar
Yuri Chornoivan committed
171
      tmp2 = g_strrstr (tmp, forbidden[i]);
172 173 174 175 176 177 178 179 180 181 182 183 184
      if (breakpoint == NULL ||
         (tmp2 != NULL && tmp2 < breakpoint))
           breakpoint = tmp2;
    }

  if (breakpoint)
    {
      backup = *breakpoint;
      *breakpoint = '\0';
      length = strlen (tmp);
      *breakpoint = backup;

      if (length > 0)
185
        return g_strndup (printer_make_and_model, length);
186 187
    }
  else
188
    return g_strdup (printer_make_and_model);
189

190
  return NULL;
191 192
}

193 194 195 196 197 198 199 200 201
static gboolean
supply_level_is_empty (PpPrinterEntry *self)
{
    return !((self->inklevel->marker_levels != NULL) &&
             (self->inklevel->marker_colors != NULL) &&
             (self->inklevel->marker_names != NULL) &&
             (self->inklevel->marker_types != NULL));
}

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
/* To tone down the colors in the supply level bar
 * we shade them by darkening the hue.
 *
 * Obs.: we don't know whether the color is already
 * shaded.
 *
 */
static void
tone_down_color (GdkRGBA *color,
                 gdouble  hue_ratio,
                 gdouble  saturation_ratio,
                 gdouble  value_ratio)
{
  gdouble h, s, v;

  gtk_rgb_to_hsv (color->red, color->green, color->blue,
                  &h, &s, &v);
  gtk_hsv_to_rgb (h * hue_ratio, s * saturation_ratio, v * value_ratio,
                  &color->red, &color->green, &color->blue);
}

223
static gboolean
224 225
supply_levels_draw_cb (PpPrinterEntry *self,
                       cairo_t        *cr)
226 227
{
  GtkStyleContext        *context;
228
  gboolean                is_empty = TRUE;
229
  g_autofree gchar       *tooltip_text = NULL;
230 231 232 233
  gint                    width;
  gint                    height;
  int                     i;

234
  context = gtk_widget_get_style_context (GTK_WIDGET (self->supply_drawing_area));
235

236 237
  width = gtk_widget_get_allocated_width (GTK_WIDGET (self->supply_drawing_area));
  height = gtk_widget_get_allocated_height (GTK_WIDGET (self->supply_drawing_area));
238 239 240

  gtk_render_background (context, cr, 0, 0, width, height);

241
  if (!supply_level_is_empty (self))
242 243 244 245 246 247 248 249 250 251
    {
      GSList   *markers = NULL;
      GSList   *tmp_list = NULL;
      gchar   **marker_levelsv = NULL;
      gchar   **marker_colorsv = NULL;
      gchar   **marker_namesv = NULL;
      gchar   **marker_typesv = NULL;

      gtk_style_context_save (context);

252 253 254 255
      marker_levelsv = g_strsplit (self->inklevel->marker_levels, ",", -1);
      marker_colorsv = g_strsplit (self->inklevel->marker_colors, ",", -1);
      marker_namesv = g_strsplit (self->inklevel->marker_names, ",", -1);
      marker_typesv = g_strsplit (self->inklevel->marker_types, ",", -1);
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290

      if (g_strv_length (marker_levelsv) == g_strv_length (marker_colorsv) &&
          g_strv_length (marker_colorsv) == g_strv_length (marker_namesv) &&
          g_strv_length (marker_namesv) == g_strv_length (marker_typesv))
        {
          for (i = 0; i < g_strv_length (marker_levelsv); i++)
            {
              MarkerItem *marker;

              if (g_strcmp0 (marker_typesv[i], "ink") == 0 ||
                  g_strcmp0 (marker_typesv[i], "toner") == 0 ||
                  g_strcmp0 (marker_typesv[i], "inkCartridge") == 0 ||
                  g_strcmp0 (marker_typesv[i], "tonerCartridge") == 0)
                {
                  marker = g_new0 (MarkerItem, 1);
                  marker->type = g_strdup (marker_typesv[i]);
                  marker->name = g_strdup (marker_namesv[i]);
                  marker->color = g_strdup (marker_colorsv[i]);
                  marker->level = atoi (marker_levelsv[i]);

                  markers = g_slist_prepend (markers, marker);
                }
            }

            markers = g_slist_sort (markers, markers_cmp);

            for (tmp_list = markers; tmp_list; tmp_list = tmp_list->next)
              {
                GdkRGBA color = {0.0, 0.0, 0.0, 1.0};
                double  display_value;
                int     value;

                value = ((MarkerItem*) tmp_list->data)->level;

                gdk_rgba_parse (&color, ((MarkerItem*) tmp_list->data)->color);
291
                tone_down_color (&color, 1.0, 0.6, 0.9);
292 293 294 295 296

                if (value > 0)
                  {
                    display_value = value / 100.0 * (width - 3.0);
                    gdk_cairo_set_source_rgba (cr, &color);
297
                    cairo_rectangle (cr, 2.0, 2.0, display_value, SUPPLY_BAR_HEIGHT);
298
                    cairo_fill (cr);
299 300 301 302 303 304

                    tone_down_color (&color, 1.0, 1.0, 0.85);
                    gdk_cairo_set_source_rgba (cr, &color);
                    cairo_set_line_width (cr, 1.0);
                    cairo_rectangle (cr, 1.5, 1.5, display_value, SUPPLY_BAR_HEIGHT + 1);
                    cairo_stroke (cr);
305 306

                    is_empty = FALSE;
307 308 309 310
                  }

                if (tooltip_text)
                  {
311 312 313 314
                    g_autofree gchar *old_tooltip_text = g_steal_pointer (&tooltip_text);
                    tooltip_text = g_strdup_printf ("%s\n%s",
                                                    old_tooltip_text,
                                                    ((MarkerItem*) tmp_list->data)->name);
315 316 317 318 319 320
                  }
                else
                  tooltip_text = g_strdup_printf ("%s",
                                                  ((MarkerItem*) tmp_list->data)->name);
              }

321
            gtk_render_frame (context, cr, 1, 1, width - 1, SUPPLY_BAR_HEIGHT);
322 323 324 325 326 327 328 329 330 331 332 333 334 335

            for (tmp_list = markers; tmp_list; tmp_list = tmp_list->next)
              {
                g_free (((MarkerItem*) tmp_list->data)->name);
                g_free (((MarkerItem*) tmp_list->data)->type);
                g_free (((MarkerItem*) tmp_list->data)->color);
              }
            g_slist_free_full (markers, g_free);
          }

        gtk_style_context_restore (context);

    if (tooltip_text)
      {
336
        gtk_widget_set_tooltip_text (GTK_WIDGET (self->supply_drawing_area), tooltip_text);
337 338 339
      }
    else
      {
340 341
        gtk_widget_set_tooltip_text (GTK_WIDGET (self->supply_drawing_area), NULL);
        gtk_widget_set_has_tooltip (GTK_WIDGET (self->supply_drawing_area), FALSE);
342 343 344
      }
    }

345 346 347
  gtk_widget_set_visible (GTK_WIDGET (self->printer_inklevel_label), !is_empty);
  gtk_widget_set_visible (GTK_WIDGET (self->supply_frame), !is_empty);

348 349 350
  return TRUE;
}

351
static void
Robert Ancell's avatar
Robert Ancell committed
352 353 354
on_printer_rename_cb (GObject      *source_object,
                      GAsyncResult *result,
                      gpointer      user_data)
355
{
356
  PpPrinterEntry *self = user_data;
Robert Ancell's avatar
Robert Ancell committed
357 358 359

  if (!pp_printer_rename_finish (PP_PRINTER (source_object), result, NULL))
    return;
360

361
  g_signal_emit_by_name (self, "printer-renamed", pp_printer_get_name (PP_PRINTER (source_object)));
362 363 364 365 366 367
}

static void
on_show_printer_details_dialog (GtkButton      *button,
                                PpPrinterEntry *self)
{
Robert Ancell's avatar
Robert Ancell committed
368 369 370 371 372 373 374 375
  const gchar *new_name;
  const gchar *new_location;

  PpDetailsDialog *dialog = pp_details_dialog_new (self->printer_name,
                                                   self->printer_location,
                                                   self->printer_hostname,
                                                   self->printer_make_and_model,
                                                   self->is_authorized);
376

Robert Ancell's avatar
Robert Ancell committed
377
  gtk_window_set_transient_for (GTK_WINDOW (dialog),
378 379
                                GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));

Robert Ancell's avatar
Robert Ancell committed
380 381 382 383 384 385 386 387 388
  gtk_dialog_run (GTK_DIALOG (dialog));

  new_location = pp_details_dialog_get_printer_location (dialog);
  if (g_strcmp0 (self->printer_location, new_location) != 0)
    printer_set_location (self->printer_name, new_location);

  new_name = pp_details_dialog_get_printer_name (dialog);
  if (g_strcmp0 (self->printer_name, new_name) != 0)
    {
389
      g_autoptr(PpPrinter) printer = pp_printer_new (self->printer_name);
Robert Ancell's avatar
Robert Ancell committed
390 391 392 393 394 395 396 397 398 399 400

      pp_printer_rename_async (printer,
                               new_name,
                               NULL,
                               on_printer_rename_cb,
                               self);
    }

  g_signal_emit_by_name (self, "printer-changed");

  gtk_widget_destroy (GTK_WIDGET (dialog));
401 402
}

403
static void
404 405
on_show_printer_options_dialog (GtkButton      *button,
                                PpPrinterEntry *self)
406
{
407
  PpOptionsDialog *dialog;
408

409
  dialog = pp_options_dialog_new (self->printer_name, self->is_authorized);
410

411 412
  gtk_window_set_transient_for (GTK_WINDOW (dialog),
                                GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
413

414 415 416
  gtk_dialog_run (GTK_DIALOG (dialog));

  gtk_widget_destroy (GTK_WIDGET (dialog));
417 418 419 420 421 422 423 424 425 426 427
}

static void
set_as_default_printer (GtkToggleButton *button,
                        PpPrinterEntry  *self)
{
  printer_set_default (self->printer_name);

  g_signal_emit_by_name (self, "printer-changed");
}

428 429 430 431 432
static void
check_clean_heads_maintenance_command_cb (GObject      *source_object,
                                          GAsyncResult *res,
                                          gpointer      user_data)
{
433
  PpPrinterEntry       *self = user_data;
434
  gboolean              is_supported = FALSE;
435
  g_autoptr(GError)     error = NULL;
436

437
  is_supported = pp_maintenance_command_is_supported_finish (PP_MAINTENANCE_COMMAND (source_object), res, &error);
438 439 440
  if (error != NULL)
    {
      g_debug ("Could not check 'Clean' maintenance command: %s", error->message);
441
      return;
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469
    }

  if (is_supported)
    {
      gtk_widget_show (GTK_WIDGET (self->clean_heads_menuitem));
    }
}

static void
check_clean_heads_maintenance_command (PpPrinterEntry *self)
{
  if (self->clean_command == NULL)
    return;

  g_object_ref (self->clean_command);
  self->check_clean_heads_cancellable = g_cancellable_new ();

  pp_maintenance_command_is_supported_async (self->clean_command,
                                             self->check_clean_heads_cancellable,
                                             check_clean_heads_maintenance_command_cb,
                                             self);
}

static void
clean_heads_maintenance_command_cb (GObject      *source_object,
                                    GAsyncResult *res,
                                    gpointer      user_data)
{
470 471
  PpPrinterEntry *self = user_data;
  g_autoptr(GError) error = NULL;
472

473 474
  if (!pp_maintenance_command_execute_finish (PP_MAINTENANCE_COMMAND (source_object), res, &error))
    g_warning ("Error cleaning print heads for %s: %s", self->printer_name, error->message);
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
}

static void
clean_heads (GtkButton *button,
             PpPrinterEntry *self)
{
  if (self->clean_command == NULL)
    return;

  g_object_ref (self->clean_command);
  pp_maintenance_command_execute_async (self->clean_command,
                                        NULL,
                                        clean_heads_maintenance_command_cb,
                                        self);
}

491 492 493 494
static void
remove_printer (GtkButton      *button,
                PpPrinterEntry *self)
{
495
  g_signal_emit_by_name (self, "printer-delete");
496 497
}

498 499 500 501
static void
get_jobs_cb (GObject      *source_object,
             GAsyncResult *result,
             gpointer      user_data)
502
{
503 504 505 506
  PpPrinterEntry      *self = user_data;
  g_autoptr(GError)    error = NULL;
  g_autoptr(GPtrArray) jobs = NULL;
  g_autofree gchar    *button_label = NULL;
507

508
  jobs = pp_printer_get_jobs_finish (PP_PRINTER (source_object), result, &error);
509

510 511 512 513 514 515 516 517 518 519
  if (error != NULL)
    {
      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
        {
          g_warning ("Could not get jobs: %s", error->message);
        }

      return;
    }

520
  if (jobs->len == 0)
521 522 523 524 525 526 527
    {
      /* Translators: This is the label of the button that opens the Jobs Dialog. */
      button_label = g_strdup (_("No Active Jobs"));
    }
  else
    {
      /* Translators: This is the label of the button that opens the Jobs Dialog. */
528
      button_label = g_strdup_printf (ngettext ("%u Job", "%u Jobs", jobs->len), jobs->len);
529 530 531
    }

  gtk_button_set_label (GTK_BUTTON (self->show_jobs_dialog_button), button_label);
532
  gtk_widget_set_sensitive (self->show_jobs_dialog_button, jobs->len > 0);
533

534 535 536 537 538
  if (self->pp_jobs_dialog != NULL)
    {
      pp_jobs_dialog_update (self->pp_jobs_dialog);
    }

539 540 541 542 543 544
  g_clear_object (&self->get_jobs_cancellable);
}

void
pp_printer_entry_update_jobs_count (PpPrinterEntry *self)
{
545
  g_autoptr(PpPrinter) printer = NULL;
546

Robert Ancell's avatar
Robert Ancell committed
547 548
  g_cancellable_cancel (self->get_jobs_cancellable);
  g_clear_object (&self->get_jobs_cancellable);
549 550 551 552 553 554 555 556 557 558

  self->get_jobs_cancellable = g_cancellable_new ();

  printer = pp_printer_new (self->printer_name);
  pp_printer_get_jobs_async (printer,
                             TRUE,
                             CUPS_WHICHJOBS_ACTIVE,
                             self->get_jobs_cancellable,
                             get_jobs_cb,
                             self);
559 560 561 562 563 564 565 566 567 568 569
}

static void
jobs_dialog_response_cb (GtkDialog  *dialog,
                         gint        response_id,
                         gpointer    user_data)
{
  PpPrinterEntry *self = (PpPrinterEntry*) user_data;

  if (self->pp_jobs_dialog != NULL)
    {
570
      gtk_widget_destroy (GTK_WIDGET (self->pp_jobs_dialog));
571 572 573 574
      self->pp_jobs_dialog = NULL;
    }
}

575
void
576 577 578 579
pp_printer_entry_show_jobs_dialog (PpPrinterEntry *self)
{
  if (self->pp_jobs_dialog == NULL)
    {
580 581
      self->pp_jobs_dialog = pp_jobs_dialog_new (self->printer_name);
      g_signal_connect_object (self->pp_jobs_dialog, "response", G_CALLBACK (jobs_dialog_response_cb), self, 0);
582
      gtk_window_set_transient_for (GTK_WINDOW (self->pp_jobs_dialog), GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))));
583
      gtk_window_present (GTK_WINDOW (self->pp_jobs_dialog));
584 585 586 587 588 589 590 591 592 593
    }
}

void
pp_printer_entry_authenticate_jobs (PpPrinterEntry *self)
{
  pp_printer_entry_show_jobs_dialog (self);
  pp_jobs_dialog_authenticate_jobs (self->pp_jobs_dialog);
}

594 595 596 597
static void
show_jobs_dialog (GtkButton *button,
                  gpointer   user_data)
{
598
  pp_printer_entry_show_jobs_dialog (PP_PRINTER_ENTRY (user_data));
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
}

enum
{
  PRINTER_READY = 3,
  PRINTER_PROCESSING,
  PRINTER_STOPPED
};

static void
restart_printer (GtkButton      *button,
                 PpPrinterEntry *self)
{
  if (self->printer_state == PRINTER_STOPPED)
    printer_set_enabled (self->printer_name, TRUE);

  if (!self->is_accepting_jobs)
    printer_set_accepting_jobs (self->printer_name, TRUE, NULL);

  g_signal_emit_by_name (self, "printer-changed");
}

621 622 623 624 625 626 627 628 629 630 631 632 633
GSList *
pp_printer_entry_get_size_group_widgets (PpPrinterEntry *self)
{
  GSList *widgets = NULL;

  widgets = g_slist_prepend (widgets, self->printer_icon);
  widgets = g_slist_prepend (widgets, self->printer_location_label);
  widgets = g_slist_prepend (widgets, self->printer_model_label);
  widgets = g_slist_prepend (widgets, self->printer_inklevel_label);

  return widgets;
}

634 635 636 637
PpPrinterEntry *
pp_printer_entry_new (cups_dest_t  printer,
                      gboolean     is_authorized)
{
638 639
  PpPrinterEntry *self;

640 641 642
  self = g_object_new (PP_PRINTER_ENTRY_TYPE, NULL);

  self->printer_name = g_strdup (printer.name);
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657

  self->clean_command = pp_maintenance_command_new (self->printer_name,
                                                    "Clean",
                                                    "all",
                                                    /* Translators: Name of job which makes printer to clean its heads */
                                                    _("Clean print heads"));
  check_clean_heads_maintenance_command (self);

  g_signal_connect_object (self->supply_drawing_area, "draw", G_CALLBACK (supply_levels_draw_cb), self, G_CONNECT_SWAPPED);

  pp_printer_entry_update (self, printer, is_authorized);

  return self;
}

658 659 660 661 662 663 664 665 666 667 668 669 670 671
const gchar *
pp_printer_entry_get_name (PpPrinterEntry *self)
{
  g_return_val_if_fail (PP_IS_PRINTER_ENTRY (self), NULL);
  return self->printer_name;
}

const gchar *
pp_printer_entry_get_location (PpPrinterEntry *self)
{
  g_return_val_if_fail (PP_IS_PRINTER_ENTRY (self), NULL);
  return self->printer_location;
}

672 673 674 675 676
void
pp_printer_entry_update (PpPrinterEntry *self,
                         cups_dest_t     printer,
                         gboolean        is_authorized)
{
677 678 679 680 681
  cups_ptype_t      printer_type = 0;
  gboolean          is_accepting_jobs = TRUE;
  gboolean          ink_supply_is_empty;
  g_autofree gchar *instance = NULL;
  const gchar      *printer_uri = NULL;
682
  const gchar      *device_uri = NULL;
683 684 685 686 687 688 689 690
  const gchar      *location = NULL;
  g_autofree gchar *printer_icon_name = NULL;
  const gchar      *printer_make_and_model = NULL;
  const gchar      *reason = NULL;
  gchar           **printer_reasons = NULL;
  g_autofree gchar *status = NULL;
  g_autofree gchar *printer_status = NULL;
  int               i, j;
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
  static const char * const reasons[] =
    {
      "toner-low",
      "toner-empty",
      "developer-low",
      "developer-empty",
      "marker-supply-low",
      "marker-supply-empty",
      "cover-open",
      "door-open",
      "media-low",
      "media-empty",
      "offline",
      "paused",
      "marker-waste-almost-full",
      "marker-waste-full",
      "opc-near-eol",
      "opc-life-over"
    };
  static const char * statuses[] =
    {
      /* Translators: The printer is low on toner */
      N_("Low on toner"),
      /* Translators: The printer has no toner left */
      N_("Out of toner"),
      /* Translators: "Developer" is a chemical for photo development,
       * http://en.wikipedia.org/wiki/Photographic_developer */
      N_("Low on developer"),
      /* Translators: "Developer" is a chemical for photo development,
       * http://en.wikipedia.org/wiki/Photographic_developer */
      N_("Out of developer"),
      /* Translators: "marker" is one color bin of the printer */
      N_("Low on a marker supply"),
      /* Translators: "marker" is one color bin of the printer */
      N_("Out of a marker supply"),
      /* Translators: One or more covers on the printer are open */
      N_("Open cover"),
      /* Translators: One or more doors on the printer are open */
      N_("Open door"),
      /* Translators: At least one input tray is low on media */
      N_("Low on paper"),
      /* Translators: At least one input tray is empty */
      N_("Out of paper"),
      /* Translators: The printer is offline */
      NC_("printer state", "Offline"),
      /* Translators: Someone has stopped the Printer */
      NC_("printer state", "Stopped"),
      /* Translators: The printer marker supply waste receptacle is almost full */
      N_("Waste receptacle almost full"),
      /* Translators: The printer marker supply waste receptacle is full */
      N_("Waste receptacle full"),
      /* Translators: Optical photo conductors are used in laser printers */
      N_("The optical photo conductor is near end of life"),
      /* Translators: Optical photo conductors are used in laser printers */
      N_("The optical photo conductor is no longer functioning")
    };

  if (printer.instance)
    {
      instance = g_strdup_printf ("%s / %s", printer.name, printer.instance);
    }
  else
    {
      instance = g_strdup (printer.name);
    }

  self->printer_state = PRINTER_READY;

  for (i = 0; i < printer.num_options; i++)
    {
      if (g_strcmp0 (printer.options[i].name, "device-uri") == 0)
762
        device_uri = printer.options[i].value;
763 764 765 766 767 768 769 770 771
      else if (g_strcmp0 (printer.options[i].name, "printer-uri-supported") == 0)
        printer_uri = printer.options[i].value;
      else if (g_strcmp0 (printer.options[i].name, "printer-type") == 0)
        printer_type = atoi (printer.options[i].value);
      else if (g_strcmp0 (printer.options[i].name, "printer-location") == 0)
        location = printer.options[i].value;
      else if (g_strcmp0 (printer.options[i].name, "printer-state-reasons") == 0)
        reason = printer.options[i].value;
      else if (g_strcmp0 (printer.options[i].name, "marker-names") == 0)
772 773 774 775
        {
          g_free (self->inklevel->marker_names);
          self->inklevel->marker_names = g_strcompress (g_strdup (printer.options[i].value));
        }
776
      else if (g_strcmp0 (printer.options[i].name, "marker-levels") == 0)
777 778 779 780
        {
          g_free (self->inklevel->marker_levels);
          self->inklevel->marker_levels = g_strdup (printer.options[i].value);
        }
781
      else if (g_strcmp0 (printer.options[i].name, "marker-colors") == 0)
782 783 784 785
        {
          g_free (self->inklevel->marker_colors);
          self->inklevel->marker_colors = g_strdup (printer.options[i].value);
        }
786
      else if (g_strcmp0 (printer.options[i].name, "marker-types") == 0)
787 788 789 790
        {
          g_free (self->inklevel->marker_types);
          self->inklevel->marker_types = g_strdup (printer.options[i].value);
        }
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
      else if (g_strcmp0 (printer.options[i].name, "printer-make-and-model") == 0)
        printer_make_and_model = printer.options[i].value;
      else if (g_strcmp0 (printer.options[i].name, "printer-state") == 0)
        self->printer_state = atoi (printer.options[i].value);
      else if (g_strcmp0 (printer.options[i].name, "printer-is-accepting-jobs") == 0)
        {
          if (g_strcmp0 (printer.options[i].value, "true") == 0)
            is_accepting_jobs = TRUE;
          else
            is_accepting_jobs = FALSE;
        }
    }

  /* Find the first of the most severe reasons
   * and show it in the status field
   */
  if (reason && !g_str_equal (reason, "none"))
    {
      int errors = 0, warnings = 0, reports = 0;
      int error_index = -1, warning_index = -1, report_index = -1;

      printer_reasons = g_strsplit (reason, ",", -1);
      for (i = 0; i < g_strv_length (printer_reasons); i++)
        {
          for (j = 0; j < G_N_ELEMENTS (reasons); j++)
            if (strncmp (printer_reasons[i], reasons[j], strlen (reasons[j])) == 0)
                {
                  if (g_str_has_suffix (printer_reasons[i], "-report"))
                    {
                      if (reports == 0)
                        report_index = j;
                      reports++;
                    }
                  else if (g_str_has_suffix (printer_reasons[i], "-warning"))
                    {
                      if (warnings == 0)
                        warning_index = j;
                      warnings++;
                    }
                  else
                    {
                      if (errors == 0)
                        error_index = j;
                      errors++;
                    }
                }
        }
      g_strfreev (printer_reasons);

      if (error_index >= 0)
        status = g_strdup (_(statuses[error_index]));
      else if (warning_index >= 0)
        status = g_strdup (_(statuses[warning_index]));
      else if (report_index >= 0)
        status = g_strdup (_(statuses[report_index]));
    }

  if ((self->printer_state == PRINTER_STOPPED || !is_accepting_jobs) &&
      status != NULL && status[0] != '\0')
    {
      gtk_label_set_label (self->error_status, status);
      gtk_widget_set_visible (GTK_WIDGET (self->printer_error), TRUE);
    }
854 855 856 857 858
  else
    {
      gtk_label_set_label (self->error_status, "");
      gtk_widget_set_visible (GTK_WIDGET (self->printer_error), FALSE);
    }
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883

  switch (self->printer_state)
    {
      case PRINTER_READY:
        if (is_accepting_jobs)
          {
            /* Translators: Printer's state (can start new job without waiting) */
            printer_status = g_strdup ( C_("printer state", "Ready"));
          }
        else
          {
            /* Translators: Printer's state (printer is ready but doesn't accept new jobs) */
            printer_status = g_strdup ( C_("printer state", "Does not accept jobs"));
          }
        break;
      case PRINTER_PROCESSING:
        /* Translators: Printer's state (jobs are processing) */
        printer_status = g_strdup ( C_("printer state", "Processing"));
        break;
      case PRINTER_STOPPED:
        /* Translators: Printer's state (no jobs can be processed) */
        printer_status = g_strdup ( C_("printer state", "Stopped"));
        break;
    }

884
  if (printer_is_local (printer_type, device_uri))
885 886 887 888
    printer_icon_name = g_strdup ("printer");
  else
    printer_icon_name = g_strdup ("printer-network");

889 890
  g_free (self->printer_location);
  self->printer_location = g_strdup (location);
891

892 893 894
  self->is_accepting_jobs = is_accepting_jobs;
  self->is_authorized = is_authorized;

895 896
  g_free (self->printer_hostname);
  self->printer_hostname = printer_get_hostname (printer_type, device_uri, printer_uri);
897

898 899 900 901 902 903 904 905 906
  gtk_image_set_from_icon_name (self->printer_icon, printer_icon_name, GTK_ICON_SIZE_DIALOG);
  gtk_label_set_text (self->printer_status, printer_status);
  gtk_label_set_text (self->printer_name_label, instance);
  g_signal_handlers_block_by_func (self->printer_default_checkbutton, set_as_default_printer, self);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->printer_default_checkbutton), printer.is_default);
  g_signal_handlers_unblock_by_func (self->printer_default_checkbutton, set_as_default_printer, self);

  self->printer_make_and_model = sanitize_printer_model (printer_make_and_model);

907
  if (self->printer_make_and_model == NULL || self->printer_make_and_model[0] == '\0')
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926
    {
      gtk_widget_hide (GTK_WIDGET (self->printer_model_label));
      gtk_widget_hide (GTK_WIDGET (self->printer_model));
    }
  else
    {
      gtk_label_set_text (self->printer_model, self->printer_make_and_model);
    }

  if (location != NULL && location[0] == '\0')
    {
      gtk_widget_hide (GTK_WIDGET (self->printer_location_label));
      gtk_widget_hide (GTK_WIDGET (self->printer_location_address_label));
    }
  else
    {
      gtk_label_set_text (self->printer_location_address_label, location);
    }

927 928 929
  ink_supply_is_empty = supply_level_is_empty (self);
  gtk_widget_set_visible (GTK_WIDGET (self->printer_inklevel_label), !ink_supply_is_empty);
  gtk_widget_set_visible (GTK_WIDGET (self->supply_frame), !ink_supply_is_empty);
930

931
  pp_printer_entry_update_jobs_count (self);
932 933 934 935 936 937 938 939 940 941

  gtk_widget_set_sensitive (GTK_WIDGET (self->printer_default_checkbutton), self->is_authorized);
  gtk_widget_set_sensitive (GTK_WIDGET (self->remove_printer_menuitem), self->is_authorized);
}

static void
pp_printer_entry_dispose (GObject *object)
{
  PpPrinterEntry *self = PP_PRINTER_ENTRY (object);

942 943 944
  g_cancellable_cancel (self->get_jobs_cancellable);
  g_cancellable_cancel (self->check_clean_heads_cancellable);

945 946 947 948
  g_clear_pointer (&self->printer_name, g_free);
  g_clear_pointer (&self->printer_location, g_free);
  g_clear_pointer (&self->printer_make_and_model, g_free);
  g_clear_pointer (&self->printer_hostname, g_free);
Robert Ancell's avatar
Robert Ancell committed
949
  g_clear_pointer (&self->inklevel, ink_level_data_free);
Robert Ancell's avatar
Robert Ancell committed
950 951
  g_clear_object (&self->get_jobs_cancellable);
  g_clear_object (&self->check_clean_heads_cancellable);
952 953
  g_clear_object (&self->clean_command);

954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
  G_OBJECT_CLASS (pp_printer_entry_parent_class)->dispose (object);
}

static void
pp_printer_entry_class_init (PpPrinterEntryClass *klass)
{
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);

  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/printers/printer-entry.ui");

  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_icon);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_name_label);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_status);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_model_label);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_model);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_location_label);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_location_address_label);
972 973
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_inklevel_label);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, supply_frame);
974 975 976
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, supply_drawing_area);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_default_checkbutton);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, show_jobs_dialog_button);
977
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, clean_heads_menuitem);
978 979 980 981
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, remove_printer_menuitem);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, error_status);
  gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_error);

982
  gtk_widget_class_bind_template_callback (widget_class, on_show_printer_details_dialog);
983 984
  gtk_widget_class_bind_template_callback (widget_class, on_show_printer_options_dialog);
  gtk_widget_class_bind_template_callback (widget_class, set_as_default_printer);
985
  gtk_widget_class_bind_template_callback (widget_class, clean_heads);
986 987 988 989 990 991 992 993 994 995
  gtk_widget_class_bind_template_callback (widget_class, remove_printer);
  gtk_widget_class_bind_template_callback (widget_class, show_jobs_dialog);
  gtk_widget_class_bind_template_callback (widget_class, restart_printer);

  object_class->dispose = pp_printer_entry_dispose;

  signals[IS_DEFAULT_PRINTER] =
    g_signal_new ("printer-changed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
996
                  0,
997 998
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);
999 1000 1001 1002 1003

  signals[PRINTER_DELETE] =
    g_signal_new ("printer-delete",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
1004
                  0,
1005 1006
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 0);
1007 1008 1009 1010 1011

  signals[PRINTER_RENAMED] =
    g_signal_new ("printer-renamed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_LAST,
1012
                  0,
1013 1014 1015
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 1,
                  G_TYPE_STRING);
1016
}