GCC Code Coverage Report


Directory: ./
File: panels/printers/pp-printer-entry.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 456 0.0%
Functions: 0 38 0.0%
Branches: 0 235 0.0%

Line Branch Exec Source
1 /*
2 * Copyright 2017 Red Hat, Inc
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Felipe Borges <felipeborges@gnome.org>
18 */
19
20 #include <config.h>
21
22 #include "pp-printer-entry.h"
23 #include <gtk/gtk.h>
24 #include <glib/gi18n-lib.h>
25 #include <glib/gstdio.h>
26
27 #include "pp-details-dialog.h"
28 #include "pp-maintenance-command.h"
29 #include "pp-options-dialog.h"
30 #include "pp-jobs-dialog.h"
31 #include "pp-printer.h"
32 #include "pp-utils.h"
33
34 #define SUPPLY_BAR_HEIGHT 8
35
36 typedef struct
37 {
38 gchar *marker_names;
39 gchar *marker_levels;
40 gchar *marker_colors;
41 gchar *marker_types;
42 } InkLevelData;
43
44 struct _PpPrinterEntry
45 {
46 GtkListBoxRow parent;
47
48 gchar *printer_name;
49 gboolean is_accepting_jobs;
50 gchar *printer_make_and_model;
51 gchar *printer_location;
52 gchar *printer_hostname;
53 gboolean is_authorized;
54 gint printer_state;
55 InkLevelData *inklevel;
56
57 /* Maintenance commands */
58 PpMaintenanceCommand *clean_command;
59 GCancellable *check_clean_heads_cancellable;
60
61 /* Widgets */
62 GtkOrientable *header_box;
63 GtkLabel *printer_status;
64 GtkLabel *printer_name_label;
65 GtkLabel *printer_model_label;
66 GtkLabel *printer_model;
67 GtkLabel *printer_location_label;
68 GtkLabel *printer_location_address_label;
69 GtkLabel *printer_inklevel_label;
70 GtkFrame *supply_frame;
71 GtkDrawingArea *supply_drawing_area;
72 GtkWidget *show_jobs_dialog_button;
73 GtkBox *printer_error;
74 GtkLabel *error_status;
75
76 gboolean is_default;
77 gboolean compact;
78
79 /* Dialogs */
80 PpJobsDialog *pp_jobs_dialog;
81
82 GCancellable *get_jobs_cancellable;
83 };
84
85 struct _PpPrinterEntryClass
86 {
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 enum {
97 IS_DEFAULT_PRINTER,
98 PRINTER_DELETE,
99 PRINTER_RENAMED,
100 LAST_SIGNAL,
101 };
102
103 static guint signals[LAST_SIGNAL] = { 0 };
104
105 enum {
106 PROP_0,
107 PROP_DEFAULT,
108 PROP_COMPACT,
109 };
110
111 static InkLevelData *
112 ink_level_data_new (void)
113 {
114 return g_slice_new0 (InkLevelData);
115 }
116
117 static void
118 ink_level_data_free (InkLevelData *data)
119 {
120 g_clear_pointer (&data->marker_names, g_free);
121 g_clear_pointer (&data->marker_levels, g_free);
122 g_clear_pointer (&data->marker_colors, g_free);
123 g_clear_pointer (&data->marker_types, g_free);
124 g_slice_free (InkLevelData, data);
125 }
126
127 static void
128 pp_printer_entry_init (PpPrinterEntry *self)
129 {
130 gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.clean-heads", FALSE);
131
132 gtk_widget_init_template (GTK_WIDGET (self));
133 self->inklevel = ink_level_data_new ();
134 }
135
136 typedef struct {
137 gchar *color;
138 gchar *type;
139 gchar *name;
140 gint level;
141 } MarkerItem;
142
143 static gint
144 markers_cmp (gconstpointer a,
145 gconstpointer b)
146 {
147 MarkerItem *x = (MarkerItem*) a;
148 MarkerItem *y = (MarkerItem*) b;
149
150 if (x->level < y->level)
151 return 1;
152 else if (x->level == y->level)
153 return 0;
154 else
155 return -1;
156 }
157
158 static gchar *
159 sanitize_printer_model (const gchar *printer_make_and_model)
160 {
161 gchar *breakpoint = NULL, *tmp2 = NULL;
162 g_autofree gchar *tmp = NULL;
163 gchar backup;
164 size_t length = 0;
165 gchar *forbidden[] = {
166 "foomatic",
167 ",",
168 "hpijs",
169 "hpcups",
170 "(recommended)",
171 "postscript (recommended)",
172 NULL };
173 int i;
174
175 tmp = g_ascii_strdown (printer_make_and_model, -1);
176
177 for (i = 0; i < g_strv_length (forbidden); i++)
178 {
179 tmp2 = g_strrstr (tmp, forbidden[i]);
180 if (breakpoint == NULL ||
181 (tmp2 != NULL && tmp2 < breakpoint))
182 breakpoint = tmp2;
183 }
184
185 if (breakpoint)
186 {
187 backup = *breakpoint;
188 *breakpoint = '\0';
189 length = strlen (tmp);
190 *breakpoint = backup;
191
192 if (length > 0)
193 return g_strndup (printer_make_and_model, length);
194 }
195 else
196 return g_strdup (printer_make_and_model);
197
198 return NULL;
199 }
200
201 static gboolean
202 supply_level_is_empty (PpPrinterEntry *self)
203 {
204 return !((self->inklevel->marker_levels != NULL) &&
205 (self->inklevel->marker_colors != NULL) &&
206 (self->inklevel->marker_names != NULL) &&
207 (self->inklevel->marker_types != NULL));
208 }
209
210 /* To tone down the colors in the supply level bar
211 * we shade them by darkening the hue.
212 *
213 * Obs.: we don't know whether the color is already
214 * shaded.
215 *
216 */
217 static void
218 tone_down_color (GdkRGBA *color,
219 gdouble hue_ratio,
220 gdouble saturation_ratio,
221 gdouble value_ratio)
222 {
223 gfloat h, s, v;
224
225 gtk_rgb_to_hsv (color->red, color->green, color->blue,
226 &h, &s, &v);
227 gtk_hsv_to_rgb (h * hue_ratio, s * saturation_ratio, v * value_ratio,
228 &color->red, &color->green, &color->blue);
229 }
230
231 static void
232 supply_levels_draw_cb (GtkDrawingArea *drawing_area,
233 cairo_t *cr,
234 int width,
235 int height,
236 gpointer user_data)
237 {
238 PpPrinterEntry *self = PP_PRINTER_ENTRY (user_data);
239 GtkStyleContext *context;
240 gboolean is_empty = TRUE;
241 g_autofree gchar *tooltip_text = NULL;
242 int i;
243
244 context = gtk_widget_get_style_context (GTK_WIDGET (self->supply_drawing_area));
245
246 gtk_render_background (context, cr, 0, 0, width, height);
247
248 if (!supply_level_is_empty (self))
249 {
250 GSList *markers = NULL;
251 GSList *tmp_list = NULL;
252 gchar **marker_levelsv = NULL;
253 gchar **marker_colorsv = NULL;
254 gchar **marker_namesv = NULL;
255 gchar **marker_typesv = NULL;
256
257 gtk_style_context_save (context);
258
259 marker_levelsv = g_strsplit (self->inklevel->marker_levels, ",", -1);
260 marker_colorsv = g_strsplit (self->inklevel->marker_colors, ",", -1);
261 marker_namesv = g_strsplit (self->inklevel->marker_names, ",", -1);
262 marker_typesv = g_strsplit (self->inklevel->marker_types, ",", -1);
263
264 if (g_strv_length (marker_levelsv) == g_strv_length (marker_colorsv) &&
265 g_strv_length (marker_colorsv) == g_strv_length (marker_namesv) &&
266 g_strv_length (marker_namesv) == g_strv_length (marker_typesv))
267 {
268 for (i = 0; i < g_strv_length (marker_levelsv); i++)
269 {
270 MarkerItem *marker;
271
272 if (g_strcmp0 (marker_typesv[i], "ink") == 0 ||
273 g_strcmp0 (marker_typesv[i], "toner") == 0 ||
274 g_strcmp0 (marker_typesv[i], "inkCartridge") == 0 ||
275 g_strcmp0 (marker_typesv[i], "tonerCartridge") == 0)
276 {
277 marker = g_new0 (MarkerItem, 1);
278 marker->type = g_strdup (marker_typesv[i]);
279 marker->name = g_strdup (marker_namesv[i]);
280 marker->color = g_strdup (marker_colorsv[i]);
281 marker->level = atoi (marker_levelsv[i]);
282
283 markers = g_slist_prepend (markers, marker);
284 }
285 }
286
287 markers = g_slist_sort (markers, markers_cmp);
288
289 for (tmp_list = markers; tmp_list; tmp_list = tmp_list->next)
290 {
291 GdkRGBA color = {0.0, 0.0, 0.0, 1.0};
292 double display_value;
293 int value;
294
295 value = ((MarkerItem*) tmp_list->data)->level;
296
297 gdk_rgba_parse (&color, ((MarkerItem*) tmp_list->data)->color);
298 tone_down_color (&color, 1.0, 0.6, 0.9);
299
300 if (value > 0)
301 {
302 display_value = value / 100.0 * (width - 3.0);
303 gdk_cairo_set_source_rgba (cr, &color);
304 cairo_rectangle (cr, 2.0, 2.0, display_value, SUPPLY_BAR_HEIGHT);
305 cairo_fill (cr);
306
307 tone_down_color (&color, 1.0, 1.0, 0.85);
308 gdk_cairo_set_source_rgba (cr, &color);
309 cairo_set_line_width (cr, 1.0);
310 cairo_rectangle (cr, 1.5, 1.5, display_value, SUPPLY_BAR_HEIGHT + 1);
311 cairo_stroke (cr);
312
313 is_empty = FALSE;
314 }
315
316 if (tooltip_text)
317 {
318 g_autofree gchar *old_tooltip_text = g_steal_pointer (&tooltip_text);
319 tooltip_text = g_strdup_printf ("%s\n%s",
320 old_tooltip_text,
321 ((MarkerItem*) tmp_list->data)->name);
322 }
323 else
324 tooltip_text = g_strdup_printf ("%s",
325 ((MarkerItem*) tmp_list->data)->name);
326 }
327
328 gtk_render_frame (context, cr, 1, 1, width - 1, SUPPLY_BAR_HEIGHT);
329
330 for (tmp_list = markers; tmp_list; tmp_list = tmp_list->next)
331 {
332 g_free (((MarkerItem*) tmp_list->data)->name);
333 g_free (((MarkerItem*) tmp_list->data)->type);
334 g_free (((MarkerItem*) tmp_list->data)->color);
335 }
336 g_slist_free_full (markers, g_free);
337 }
338
339 gtk_style_context_restore (context);
340
341 if (tooltip_text)
342 {
343 gtk_widget_set_tooltip_text (GTK_WIDGET (self->supply_drawing_area), tooltip_text);
344 }
345 else
346 {
347 gtk_widget_set_tooltip_text (GTK_WIDGET (self->supply_drawing_area), NULL);
348 gtk_widget_set_has_tooltip (GTK_WIDGET (self->supply_drawing_area), FALSE);
349 }
350 }
351
352 gtk_widget_set_visible (GTK_WIDGET (self->printer_inklevel_label), !is_empty);
353 gtk_widget_set_visible (GTK_WIDGET (self->supply_frame), !is_empty);
354 }
355
356 static void
357 on_printer_rename_cb (GObject *source_object,
358 GAsyncResult *result,
359 gpointer user_data)
360 {
361 PpPrinterEntry *self = user_data;
362
363 if (!pp_printer_rename_finish (PP_PRINTER (source_object), result, NULL))
364 return;
365
366 g_signal_emit_by_name (self, "printer-renamed", pp_printer_get_name (PP_PRINTER (source_object)));
367 }
368
369 static gboolean
370 show_printer_details_response_cb (PpPrinterEntry *self,
371 PpDetailsDialog *dialog)
372 {
373 const gchar *new_name;
374 const gchar *new_location;
375
376 new_location = pp_details_dialog_get_printer_location (dialog);
377 if (g_strcmp0 (self->printer_location, new_location) != 0)
378 printer_set_location (self->printer_name, new_location);
379
380 new_name = pp_details_dialog_get_printer_name (dialog);
381 if (g_strcmp0 (self->printer_name, new_name) != 0 && printer_name_is_valid (new_name))
382 {
383 g_autoptr(PpPrinter) printer = pp_printer_new (self->printer_name);
384
385 pp_printer_rename_async (printer,
386 new_name,
387 NULL,
388 on_printer_rename_cb,
389 self);
390 }
391
392 g_signal_emit_by_name (self, "printer-changed");
393
394 return GDK_EVENT_PROPAGATE;
395 }
396
397 static void
398 printer_details_cb (PpPrinterEntry *self)
399 {
400 PpDetailsDialog *dialog = pp_details_dialog_new (self->printer_name,
401 self->printer_location,
402 self->printer_hostname,
403 self->printer_make_and_model,
404 self->is_authorized);
405
406 gtk_window_set_transient_for (GTK_WINDOW (dialog),
407 GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (self))));
408
409 g_signal_connect_swapped (dialog, "close-request", G_CALLBACK (show_printer_details_response_cb), self);
410
411 gtk_window_present (GTK_WINDOW (dialog));
412 }
413
414 static void
415 printer_options_cb (PpPrinterEntry *self)
416 {
417 PpOptionsDialog *dialog;
418
419 dialog = pp_options_dialog_new (self->printer_name, self->is_authorized);
420
421 gtk_window_set_transient_for (GTK_WINDOW (dialog),
422 GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (self))));
423
424 gtk_window_present (GTK_WINDOW (dialog));
425 }
426
427 static void
428 set_as_default_printer (PpPrinterEntry *self)
429 {
430 if (self->is_default)
431 return;
432
433 printer_set_default (self->printer_name);
434
435 self->is_default = TRUE;
436
437 g_object_notify (G_OBJECT (self), "default");
438
439 g_signal_emit_by_name (self, "printer-changed");
440 }
441
442 static void
443 check_clean_heads_maintenance_command_cb (GObject *source_object,
444 GAsyncResult *res,
445 gpointer user_data)
446 {
447 PpPrinterEntry *self = user_data;
448 gboolean is_supported = FALSE;
449 g_autoptr(GError) error = NULL;
450
451 is_supported = pp_maintenance_command_is_supported_finish (PP_MAINTENANCE_COMMAND (source_object), res, &error);
452 if (error != NULL)
453 {
454 g_debug ("Could not check 'Clean' maintenance command: %s", error->message);
455 return;
456 }
457
458 if (is_supported)
459 {
460 gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.clean-heads", TRUE);
461 }
462 }
463
464 static void
465 check_clean_heads_maintenance_command (PpPrinterEntry *self)
466 {
467 if (self->clean_command == NULL)
468 return;
469
470 g_object_ref (self->clean_command);
471 self->check_clean_heads_cancellable = g_cancellable_new ();
472
473 pp_maintenance_command_is_supported_async (self->clean_command,
474 self->check_clean_heads_cancellable,
475 check_clean_heads_maintenance_command_cb,
476 self);
477 }
478
479 static void
480 clean_heads_maintenance_command_cb (GObject *source_object,
481 GAsyncResult *res,
482 gpointer user_data)
483 {
484 PpPrinterEntry *self = user_data;
485 g_autoptr(GError) error = NULL;
486
487 if (!pp_maintenance_command_execute_finish (PP_MAINTENANCE_COMMAND (source_object), res, &error))
488 g_warning ("Error cleaning print heads for %s: %s", self->printer_name, error->message);
489 }
490
491 static void
492 printer_clean_heads_cb (PpPrinterEntry *self)
493 {
494 if (self->clean_command == NULL)
495 return;
496
497 g_object_ref (self->clean_command);
498 pp_maintenance_command_execute_async (self->clean_command,
499 NULL,
500 clean_heads_maintenance_command_cb,
501 self);
502 }
503
504 static void
505 printer_remove_cb (PpPrinterEntry *self)
506 {
507 g_signal_emit_by_name (self, "printer-delete");
508 }
509
510 static void
511 get_jobs_cb (GObject *source_object,
512 GAsyncResult *result,
513 gpointer user_data)
514 {
515 PpPrinterEntry *self = user_data;
516 g_autoptr(GError) error = NULL;
517 g_autoptr(GPtrArray) jobs = NULL;
518 g_autofree gchar *button_label = NULL;
519
520 jobs = pp_printer_get_jobs_finish (PP_PRINTER (source_object), result, &error);
521
522 if (error != NULL)
523 {
524 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
525 {
526 g_warning ("Could not get jobs: %s", error->message);
527 }
528
529 return;
530 }
531
532 if (jobs->len == 0)
533 {
534 /* Translators: This is the label of the button that opens the Jobs Dialog. */
535 button_label = g_strdup (_("No Active Jobs"));
536 }
537 else
538 {
539 /* Translators: This is the label of the button that opens the Jobs Dialog. */
540 button_label = g_strdup_printf (ngettext ("%u Job", "%u Jobs", jobs->len), jobs->len);
541 }
542
543 gtk_button_set_label (GTK_BUTTON (self->show_jobs_dialog_button), button_label);
544 gtk_widget_set_sensitive (self->show_jobs_dialog_button, jobs->len > 0);
545
546 if (self->pp_jobs_dialog != NULL)
547 {
548 pp_jobs_dialog_update (self->pp_jobs_dialog);
549 }
550
551 g_clear_object (&self->get_jobs_cancellable);
552 }
553
554 void
555 pp_printer_entry_update_jobs_count (PpPrinterEntry *self)
556 {
557 g_autoptr(PpPrinter) printer = NULL;
558
559 g_cancellable_cancel (self->get_jobs_cancellable);
560 g_clear_object (&self->get_jobs_cancellable);
561
562 self->get_jobs_cancellable = g_cancellable_new ();
563
564 printer = pp_printer_new (self->printer_name);
565 pp_printer_get_jobs_async (printer,
566 TRUE,
567 CUPS_WHICHJOBS_ACTIVE,
568 self->get_jobs_cancellable,
569 get_jobs_cb,
570 self);
571 }
572
573 static gboolean
574 jobs_dialog_close_request_cb (PpPrinterEntry *self)
575 {
576 if (self->pp_jobs_dialog != NULL)
577 {
578 gtk_window_destroy (GTK_WINDOW (self->pp_jobs_dialog));
579 self->pp_jobs_dialog = NULL;
580 }
581
582 return GDK_EVENT_STOP;
583 }
584
585 void
586 pp_printer_entry_show_jobs_dialog (PpPrinterEntry *self)
587 {
588 if (self->pp_jobs_dialog == NULL)
589 {
590 self->pp_jobs_dialog = pp_jobs_dialog_new (self->printer_name);
591 g_signal_connect_object (self->pp_jobs_dialog, "close-request", G_CALLBACK (jobs_dialog_close_request_cb), self, G_CONNECT_SWAPPED);
592 gtk_window_set_transient_for (GTK_WINDOW (self->pp_jobs_dialog), GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (self))));
593 gtk_window_present (GTK_WINDOW (self->pp_jobs_dialog));
594 }
595 }
596
597 void
598 pp_printer_entry_authenticate_jobs (PpPrinterEntry *self)
599 {
600 pp_printer_entry_show_jobs_dialog (self);
601 pp_jobs_dialog_authenticate_jobs (self->pp_jobs_dialog);
602 }
603
604 static void
605 show_jobs_dialog (GtkButton *button,
606 gpointer user_data)
607 {
608 pp_printer_entry_show_jobs_dialog (PP_PRINTER_ENTRY (user_data));
609 }
610
611 enum
612 {
613 PRINTER_READY = 3,
614 PRINTER_PROCESSING,
615 PRINTER_STOPPED
616 };
617
618 static void
619 restart_printer (GtkButton *button,
620 PpPrinterEntry *self)
621 {
622 if (self->printer_state == PRINTER_STOPPED)
623 printer_set_enabled (self->printer_name, TRUE);
624
625 if (!self->is_accepting_jobs)
626 printer_set_accepting_jobs (self->printer_name, TRUE, NULL);
627
628 g_signal_emit_by_name (self, "printer-changed");
629 }
630
631 GSList *
632 pp_printer_entry_get_size_group_widgets (PpPrinterEntry *self)
633 {
634 GSList *widgets = NULL;
635
636 widgets = g_slist_prepend (widgets, self->printer_location_label);
637 widgets = g_slist_prepend (widgets, self->printer_model_label);
638 widgets = g_slist_prepend (widgets, self->printer_inklevel_label);
639
640 return widgets;
641 }
642
643 PpPrinterEntry *
644 pp_printer_entry_new (cups_dest_t printer,
645 gboolean is_authorized)
646 {
647 PpPrinterEntry *self;
648
649 self = g_object_new (PP_PRINTER_ENTRY_TYPE, NULL);
650
651 self->printer_name = g_strdup (printer.name);
652
653 self->clean_command = pp_maintenance_command_new (self->printer_name,
654 "Clean",
655 "all",
656 /* Translators: Name of job which makes printer to clean its heads */
657 _("Clean print heads"));
658 check_clean_heads_maintenance_command (self);
659
660 gtk_drawing_area_set_draw_func (self->supply_drawing_area,
661 supply_levels_draw_cb,
662 self,
663 NULL);
664
665 pp_printer_entry_update (self, printer, is_authorized);
666
667 return self;
668 }
669
670 const gchar *
671 pp_printer_entry_get_name (PpPrinterEntry *self)
672 {
673 g_return_val_if_fail (PP_IS_PRINTER_ENTRY (self), NULL);
674 return self->printer_name;
675 }
676
677 const gchar *
678 pp_printer_entry_get_location (PpPrinterEntry *self)
679 {
680 g_return_val_if_fail (PP_IS_PRINTER_ENTRY (self), NULL);
681 return self->printer_location;
682 }
683
684 void
685 pp_printer_entry_update (PpPrinterEntry *self,
686 cups_dest_t printer,
687 gboolean is_authorized)
688 {
689 cups_ptype_t printer_type = 0;
690 gboolean is_accepting_jobs = TRUE;
691 gboolean ink_supply_is_empty;
692 g_autofree gchar *instance = NULL;
693 const gchar *printer_uri = NULL;
694 const gchar *device_uri = NULL;
695 const gchar *location = NULL;
696 const gchar *printer_make_and_model = NULL;
697 const gchar *reason = NULL;
698 gchar **printer_reasons = NULL;
699 g_autofree gchar *status = NULL;
700 g_autofree gchar *printer_status = NULL;
701 int i, j;
702 static const char * const reasons[] =
703 {
704 "toner-low",
705 "toner-empty",
706 "developer-low",
707 "developer-empty",
708 "marker-supply-low",
709 "marker-supply-empty",
710 "cover-open",
711 "door-open",
712 "media-low",
713 "media-empty",
714 "offline",
715 "paused",
716 "marker-waste-almost-full",
717 "marker-waste-full",
718 "opc-near-eol",
719 "opc-life-over"
720 };
721 static const char * statuses[] =
722 {
723 /* Translators: The printer is low on toner */
724 N_("Low on toner"),
725 /* Translators: The printer has no toner left */
726 N_("Out of toner"),
727 /* Translators: "Developer" is a chemical for photo development,
728 * http://en.wikipedia.org/wiki/Photographic_developer */
729 N_("Low on developer"),
730 /* Translators: "Developer" is a chemical for photo development,
731 * http://en.wikipedia.org/wiki/Photographic_developer */
732 N_("Out of developer"),
733 /* Translators: "marker" is one color bin of the printer */
734 N_("Low on a marker supply"),
735 /* Translators: "marker" is one color bin of the printer */
736 N_("Out of a marker supply"),
737 /* Translators: One or more covers on the printer are open */
738 N_("Open cover"),
739 /* Translators: One or more doors on the printer are open */
740 N_("Open door"),
741 /* Translators: At least one input tray is low on media */
742 N_("Low on paper"),
743 /* Translators: At least one input tray is empty */
744 N_("Out of paper"),
745 /* Translators: The printer is offline */
746 NC_("printer state", "Offline"),
747 /* Translators: Someone has stopped the Printer */
748 NC_("printer state", "Stopped"),
749 /* Translators: The printer marker supply waste receptacle is almost full */
750 N_("Waste receptacle almost full"),
751 /* Translators: The printer marker supply waste receptacle is full */
752 N_("Waste receptacle full"),
753 /* Translators: Optical photo conductors are used in laser printers */
754 N_("The optical photo conductor is near end of life"),
755 /* Translators: Optical photo conductors are used in laser printers */
756 N_("The optical photo conductor is no longer functioning")
757 };
758
759 if (printer.instance)
760 {
761 instance = g_strdup_printf ("%s / %s", printer.name, printer.instance);
762 }
763 else
764 {
765 instance = g_strdup (printer.name);
766 }
767
768 self->printer_state = PRINTER_READY;
769
770 for (i = 0; i < printer.num_options; i++)
771 {
772 if (g_strcmp0 (printer.options[i].name, "device-uri") == 0)
773 device_uri = printer.options[i].value;
774 else if (g_strcmp0 (printer.options[i].name, "printer-uri-supported") == 0)
775 printer_uri = printer.options[i].value;
776 else if (g_strcmp0 (printer.options[i].name, "printer-type") == 0)
777 printer_type = atoi (printer.options[i].value);
778 else if (g_strcmp0 (printer.options[i].name, "printer-location") == 0)
779 location = printer.options[i].value;
780 else if (g_strcmp0 (printer.options[i].name, "printer-state-reasons") == 0)
781 reason = printer.options[i].value;
782 else if (g_strcmp0 (printer.options[i].name, "marker-names") == 0)
783 {
784 g_free (self->inklevel->marker_names);
785 self->inklevel->marker_names = g_strcompress (g_strdup (printer.options[i].value));
786 }
787 else if (g_strcmp0 (printer.options[i].name, "marker-levels") == 0)
788 {
789 g_free (self->inklevel->marker_levels);
790 self->inklevel->marker_levels = g_strdup (printer.options[i].value);
791 }
792 else if (g_strcmp0 (printer.options[i].name, "marker-colors") == 0)
793 {
794 g_free (self->inklevel->marker_colors);
795 self->inklevel->marker_colors = g_strdup (printer.options[i].value);
796 }
797 else if (g_strcmp0 (printer.options[i].name, "marker-types") == 0)
798 {
799 g_free (self->inklevel->marker_types);
800 self->inklevel->marker_types = g_strdup (printer.options[i].value);
801 }
802 else if (g_strcmp0 (printer.options[i].name, "printer-make-and-model") == 0)
803 printer_make_and_model = printer.options[i].value;
804 else if (g_strcmp0 (printer.options[i].name, "printer-state") == 0)
805 self->printer_state = atoi (printer.options[i].value);
806 else if (g_strcmp0 (printer.options[i].name, "printer-is-accepting-jobs") == 0)
807 {
808 if (g_strcmp0 (printer.options[i].value, "true") == 0)
809 is_accepting_jobs = TRUE;
810 else
811 is_accepting_jobs = FALSE;
812 }
813 }
814
815 /* Find the first of the most severe reasons
816 * and show it in the status field
817 */
818 if (reason && !g_str_equal (reason, "none"))
819 {
820 int errors = 0, warnings = 0, reports = 0;
821 int error_index = -1, warning_index = -1, report_index = -1;
822
823 printer_reasons = g_strsplit (reason, ",", -1);
824 for (i = 0; i < g_strv_length (printer_reasons); i++)
825 {
826 for (j = 0; j < G_N_ELEMENTS (reasons); j++)
827 if (strncmp (printer_reasons[i], reasons[j], strlen (reasons[j])) == 0)
828 {
829 if (g_str_has_suffix (printer_reasons[i], "-report"))
830 {
831 if (reports == 0)
832 report_index = j;
833 reports++;
834 }
835 else if (g_str_has_suffix (printer_reasons[i], "-warning"))
836 {
837 if (warnings == 0)
838 warning_index = j;
839 warnings++;
840 }
841 else
842 {
843 if (errors == 0)
844 error_index = j;
845 errors++;
846 }
847 }
848 }
849 g_strfreev (printer_reasons);
850
851 if (error_index >= 0)
852 status = g_strdup (_(statuses[error_index]));
853 else if (warning_index >= 0)
854 status = g_strdup (_(statuses[warning_index]));
855 else if (report_index >= 0)
856 status = g_strdup (_(statuses[report_index]));
857 }
858
859 if ((self->printer_state == PRINTER_STOPPED || !is_accepting_jobs) &&
860 status != NULL && status[0] != '\0')
861 {
862 gtk_label_set_label (self->error_status, status);
863 gtk_widget_set_visible (GTK_WIDGET (self->printer_error), TRUE);
864 }
865 else
866 {
867 gtk_label_set_label (self->error_status, "");
868 gtk_widget_set_visible (GTK_WIDGET (self->printer_error), FALSE);
869 }
870
871 switch (self->printer_state)
872 {
873 case PRINTER_READY:
874 if (is_accepting_jobs)
875 {
876 /* Translators: Printer's state (can start new job without waiting) */
877 printer_status = g_strdup ( C_("printer state", "Ready"));
878 }
879 else
880 {
881 /* Translators: Printer's state (printer is ready but doesn't accept new jobs) */
882 printer_status = g_strdup ( C_("printer state", "Does not accept jobs"));
883 }
884 break;
885 case PRINTER_PROCESSING:
886 /* Translators: Printer's state (jobs are processing) */
887 printer_status = g_strdup ( C_("printer state", "Processing"));
888 break;
889 case PRINTER_STOPPED:
890 /* Translators: Printer's state (no jobs can be processed) */
891 printer_status = g_strdup ( C_("printer state", "Stopped"));
892 break;
893 }
894
895 g_free (self->printer_location);
896 self->printer_location = g_strdup (location);
897
898 self->is_accepting_jobs = is_accepting_jobs;
899 self->is_authorized = is_authorized;
900
901 g_free (self->printer_hostname);
902 self->printer_hostname = printer_get_hostname (printer_type, device_uri, printer_uri);
903
904 gtk_label_set_text (self->printer_status, printer_status);
905 gtk_label_set_text (self->printer_name_label, instance);
906 self->is_default = printer.is_default;
907 g_object_notify (G_OBJECT (self), "default");
908
909 self->printer_make_and_model = sanitize_printer_model (printer_make_and_model);
910
911 if (self->printer_make_and_model == NULL || self->printer_make_and_model[0] == '\0')
912 {
913 gtk_widget_set_visible (GTK_WIDGET (self->printer_model_label), FALSE);
914 gtk_widget_set_visible (GTK_WIDGET (self->printer_model), FALSE);
915 }
916 else
917 {
918 gtk_label_set_text (self->printer_model, self->printer_make_and_model);
919 }
920
921 if (location != NULL && location[0] == '\0')
922 {
923 gtk_widget_set_visible (GTK_WIDGET (self->printer_location_label), FALSE);
924 gtk_widget_set_visible (GTK_WIDGET (self->printer_location_address_label), FALSE);
925 }
926 else
927 {
928 gtk_label_set_text (self->printer_location_address_label, location);
929 }
930
931 ink_supply_is_empty = supply_level_is_empty (self);
932 gtk_widget_set_visible (GTK_WIDGET (self->printer_inklevel_label), !ink_supply_is_empty);
933 gtk_widget_set_visible (GTK_WIDGET (self->supply_frame), !ink_supply_is_empty);
934
935 pp_printer_entry_update_jobs_count (self);
936
937 gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.default", self->is_authorized);
938 gtk_widget_action_set_enabled (GTK_WIDGET (self), "printer.remove", self->is_authorized);
939 }
940
941 static void
942 set_compact (PpPrinterEntry *self,
943 gboolean compact)
944 {
945 compact = !!compact;
946
947 if (self->compact == compact)
948 return;
949
950 self->compact = compact;
951
952 if (compact)
953 gtk_orientable_set_orientation (self->header_box, GTK_ORIENTATION_VERTICAL);
954 else
955 gtk_orientable_set_orientation (self->header_box, GTK_ORIENTATION_HORIZONTAL);
956 }
957
958 static void
959 pp_printer_entry_dispose (GObject *object)
960 {
961 PpPrinterEntry *self = PP_PRINTER_ENTRY (object);
962
963 g_cancellable_cancel (self->get_jobs_cancellable);
964 g_cancellable_cancel (self->check_clean_heads_cancellable);
965
966 g_clear_pointer (&self->printer_name, g_free);
967 g_clear_pointer (&self->printer_location, g_free);
968 g_clear_pointer (&self->printer_make_and_model, g_free);
969 g_clear_pointer (&self->printer_hostname, g_free);
970 g_clear_pointer (&self->inklevel, ink_level_data_free);
971 g_clear_object (&self->get_jobs_cancellable);
972 g_clear_object (&self->check_clean_heads_cancellable);
973 g_clear_object (&self->clean_command);
974
975 G_OBJECT_CLASS (pp_printer_entry_parent_class)->dispose (object);
976 }
977
978 static void
979 pp_printer_entry_get_property (GObject *object,
980 guint prop_id,
981 GValue *value,
982 GParamSpec *pspec)
983 {
984 PpPrinterEntry *self = PP_PRINTER_ENTRY (object);
985
986 switch (prop_id)
987 {
988 case PROP_DEFAULT:
989 g_value_set_boolean (value, self->is_default);
990 break;
991 case PROP_COMPACT:
992 g_value_set_boolean (value, self->compact);
993 break;
994 default:
995 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
996 }
997 }
998
999 static void
1000 pp_printer_entry_set_property (GObject *object,
1001 guint prop_id,
1002 const GValue *value,
1003 GParamSpec *pspec)
1004 {
1005 PpPrinterEntry *self = PP_PRINTER_ENTRY (object);
1006
1007 switch (prop_id)
1008 {
1009 case PROP_DEFAULT:
1010 set_as_default_printer (self);
1011 break;
1012 case PROP_COMPACT:
1013 set_compact (self, g_value_get_boolean (value));
1014 break;
1015 default:
1016 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1017 }
1018 }
1019
1020 static void
1021 pp_printer_entry_class_init (PpPrinterEntryClass *klass)
1022 {
1023 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1024 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1025
1026 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/printers/printer-entry.ui");
1027
1028 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, header_box);
1029 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_name_label);
1030 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_status);
1031 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_model_label);
1032 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_model);
1033 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_location_label);
1034 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_location_address_label);
1035 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_inklevel_label);
1036 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, supply_frame);
1037 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, supply_drawing_area);
1038 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, show_jobs_dialog_button);
1039 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, error_status);
1040 gtk_widget_class_bind_template_child (widget_class, PpPrinterEntry, printer_error);
1041
1042 gtk_widget_class_bind_template_callback (widget_class, show_jobs_dialog);
1043 gtk_widget_class_bind_template_callback (widget_class, restart_printer);
1044
1045 object_class->dispose = pp_printer_entry_dispose;
1046 object_class->get_property = pp_printer_entry_get_property;
1047 object_class->set_property = pp_printer_entry_set_property;
1048
1049 g_object_class_install_property (object_class,
1050 PROP_DEFAULT,
1051 g_param_spec_boolean ("default",
1052 "default",
1053 "default",
1054 FALSE,
1055 G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
1056
1057 g_object_class_install_property (object_class,
1058 PROP_COMPACT,
1059 g_param_spec_boolean ("compact",
1060 "compact",
1061 "compact",
1062 FALSE,
1063 G_PARAM_READWRITE));
1064
1065 signals[IS_DEFAULT_PRINTER] =
1066 g_signal_new ("printer-changed",
1067 G_TYPE_FROM_CLASS (klass),
1068 G_SIGNAL_RUN_LAST,
1069 0,
1070 NULL, NULL, NULL,
1071 G_TYPE_NONE, 0);
1072
1073 signals[PRINTER_DELETE] =
1074 g_signal_new ("printer-delete",
1075 G_TYPE_FROM_CLASS (klass),
1076 G_SIGNAL_RUN_LAST,
1077 0,
1078 NULL, NULL, NULL,
1079 G_TYPE_NONE, 0);
1080
1081 signals[PRINTER_RENAMED] =
1082 g_signal_new ("printer-renamed",
1083 G_TYPE_FROM_CLASS (klass),
1084 G_SIGNAL_RUN_LAST,
1085 0,
1086 NULL, NULL, NULL,
1087 G_TYPE_NONE, 1,
1088 G_TYPE_STRING);
1089
1090 gtk_widget_class_install_action (widget_class, "printer.options", NULL,
1091 (GtkWidgetActionActivateFunc) printer_options_cb);
1092 gtk_widget_class_install_action (widget_class, "printer.details", NULL,
1093 (GtkWidgetActionActivateFunc) printer_details_cb);
1094 gtk_widget_class_install_property_action (widget_class, "printer.default", "default");
1095 gtk_widget_class_install_action (widget_class, "printer.clean-heads", NULL,
1096 (GtkWidgetActionActivateFunc) printer_clean_heads_cb);
1097 gtk_widget_class_install_action (widget_class, "printer.remove", NULL,
1098 (GtkWidgetActionActivateFunc) printer_remove_cb);
1099 }
1100