aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/indicator-printer-state-notifier.c333
-rw-r--r--src/indicator-printer-state-notifier.h58
-rw-r--r--src/indicator-printers-service.c7
6 files changed, 402 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 6b75694..1474d2b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
-SUBDIRS = src
+SUBDIRS = src test
diff --git a/configure.ac b/configure.ac
index 3ef37fd..ef57d5c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -14,6 +14,7 @@ AC_CONFIG_HEADER(config.h)
AC_CONFIG_FILES([
Makefile
src/Makefile
+ test/Makefile
])
PKG_CHECK_MODULES(APPLET, gtk+-3.0 >= 3.0
diff --git a/src/Makefile.am b/src/Makefile.am
index 7b66b1b..d355299 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,6 +30,8 @@ indicator_printers_service_SOURCES = \
indicator-printers-service.h \
indicator-printers-menu.c \
indicator-printers-menu.h \
+ indicator-printer-state-notifier.c \
+ indicator-printer-state-notifier.h \
$(cups_notifier_sources)
indicator_printers_service_CPPFLAGS = $(SERVICE_CFLAGS)
diff --git a/src/indicator-printer-state-notifier.c b/src/indicator-printer-state-notifier.c
new file mode 100644
index 0000000..751137a
--- /dev/null
+++ b/src/indicator-printer-state-notifier.c
@@ -0,0 +1,333 @@
+
+#include "indicator-printer-state-notifier.h"
+
+#include <gtk/gtk.h>
+#include <cups/cups.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "cups-notifier.h"
+
+
+#define RESPONSE_SHOW_SYSTEM_SETTINGS 1
+
+
+G_DEFINE_TYPE (IndicatorPrinterStateNotifier, indicator_printer_state_notifier, G_TYPE_OBJECT)
+
+
+struct _IndicatorPrinterStateNotifierPrivate
+{
+ CupsNotifier *cups_notifier;
+
+ /* printer states that were already notified about in this session */
+ GHashTable *notified_printer_states;
+
+ /* state-reason -> user visible string with a %s for printer name */
+ GHashTable *printer_alerts;
+};
+
+
+enum {
+ PROP_0,
+ PROP_CUPS_NOTIFIER,
+ NUM_PROPERTIES
+};
+
+GParamSpec *properties[NUM_PROPERTIES];
+
+
+static void
+g_hash_table_insert_many (GHashTable *hash_table,
+ gpointer key,
+ ...)
+{
+ va_list args;
+
+ va_start (args, key);
+ while (key) {
+ gpointer value = va_arg (args, gpointer);
+ g_hash_table_insert (hash_table, key, value);
+ key = va_arg (args, gpointer);
+ }
+ va_end (args);
+}
+
+
+static gboolean
+g_strv_contains (gchar **str_array,
+ gchar *needle)
+{
+ gchar **str;
+
+ if (!str_array)
+ return FALSE;
+
+ for (str = str_array; *str; str++) {
+ if (!strcmp (*str, needle))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* returns a list of strings that are in a but not in b; does not copy the
+ * strings */
+static GList *
+g_strv_diff (gchar **a,
+ gchar **b)
+{
+ GList *result = NULL;
+ gchar **p;
+
+ if (!a)
+ return NULL;
+
+ for (p = a; *p; p++) {
+ if (!g_strv_contains (b, *p))
+ result = g_list_prepend (result, *p);
+ }
+
+ return g_list_reverse (result);
+}
+
+
+void
+show_alert_box (const gchar *printer,
+ const gchar *reason,
+ int njobs)
+{
+ GtkWidget *dialog;
+ GtkWidget *image;
+ gchar *primary_text;
+ gchar *secondary_text;
+ const gchar *fmt;
+
+ image = gtk_image_new_from_icon_name ("printer", GTK_ICON_SIZE_DIALOG);
+ primary_text = g_strdup_printf (reason, printer);
+
+ if (njobs == 1)
+ fmt = "You have %d job queued to print on this printer.";
+ else
+ fmt = "You have %d jobs queued to print on this printer.";
+ secondary_text = g_strdup_printf (fmt, njobs);
+
+ dialog = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
+ "title", "Printing Problem",
+ "icon-name", "printer",
+ "image", image,
+ "text", primary_text,
+ "secondary-text", secondary_text,
+ "urgency-hint", TRUE,
+ "focus-on-map", FALSE,
+ "window-position", GTK_WIN_POS_CENTER,
+ "skip-taskbar-hint", FALSE,
+ "deletable", FALSE,
+ NULL);
+
+ g_free (primary_text);
+ g_free (secondary_text);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ "_Settings…", RESPONSE_SHOW_SYSTEM_SETTINGS,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ gtk_widget_show_all (dialog);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ gtk_widget_destroy (dialog);
+}
+
+
+static void
+on_printer_state_changed (CupsNotifier *object,
+ const gchar *text,
+ const gchar *printer_uri,
+ const gchar *printer,
+ guint printer_state,
+ const gchar *printer_state_reasons,
+ gboolean printer_is_accepting_jobs,
+ gpointer user_data)
+{
+ IndicatorPrinterStateNotifierPrivate *priv = INDICATOR_PRINTER_STATE_NOTIFIER (user_data)->priv;
+ int njobs;
+ cups_job_t *jobs;
+ gchar **state_reasons, **already_notified;
+ GList *new_state_reasons, *it;
+
+ njobs = cupsGetJobs (&jobs, printer, 1, CUPS_WHICHJOBS_ACTIVE);
+ cupsFreeJobs (njobs, jobs);
+
+ /* don't show any events if the current user does not have jobs queued on
+ * that printer */
+ if (njobs == 0)
+ return;
+
+ state_reasons = g_strsplit (printer_state_reasons, " ", 0);
+ already_notified = g_hash_table_lookup (priv->notified_printer_states,
+ printer);
+
+ new_state_reasons = g_strv_diff (state_reasons, already_notified);
+
+ for (it = new_state_reasons; it; it = g_list_next (new_state_reasons)) {
+ const gchar *reason_text = g_hash_table_lookup (priv->printer_alerts,
+ it->data);
+ if (reason_text)
+ show_alert_box (printer, reason_text, njobs);
+ }
+
+ g_list_free (new_state_reasons);
+
+ g_hash_table_replace (priv->notified_printer_states,
+ g_strdup (printer),
+ state_reasons);
+}
+
+
+static void
+get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IndicatorPrinterStateNotifier *self = INDICATOR_PRINTER_STATE_NOTIFIER (object);
+
+ switch (property_id)
+ {
+ case PROP_CUPS_NOTIFIER:
+ g_value_set_object (value,
+ indicator_printer_state_notifier_get_cups_notifier (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+
+static void
+set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IndicatorPrinterStateNotifier *self = INDICATOR_PRINTER_STATE_NOTIFIER (object);
+
+ switch (property_id)
+ {
+ case PROP_CUPS_NOTIFIER:
+ indicator_printer_state_notifier_set_cups_notifier (self,
+ g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+
+static void
+dispose (GObject *object)
+{
+ IndicatorPrinterStateNotifier *self = INDICATOR_PRINTER_STATE_NOTIFIER (object);
+
+ if (self->priv->notified_printer_states) {
+ g_hash_table_unref (self->priv->notified_printer_states);
+ self->priv->notified_printer_states = NULL;
+ }
+ if (self->priv->printer_alerts) {
+ g_hash_table_unref (self->priv->printer_alerts);
+ self->priv->printer_alerts = NULL;
+ }
+ g_clear_object (&self->priv->cups_notifier);
+
+ G_OBJECT_CLASS (indicator_printer_state_notifier_parent_class)->dispose (object);
+}
+
+
+static void
+finalize (GObject *object)
+{
+ G_OBJECT_CLASS (indicator_printer_state_notifier_parent_class)->finalize (object);
+}
+
+
+static void
+indicator_printer_state_notifier_class_init (IndicatorPrinterStateNotifierClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (IndicatorPrinterStateNotifierPrivate));
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+
+ properties[PROP_CUPS_NOTIFIER] = g_param_spec_object ("cups-notifier",
+ "Cups Notifier",
+ "A cups notifier object",
+ CUPS_TYPE_NOTIFIER,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
+}
+
+
+static void
+indicator_printer_state_notifier_init (IndicatorPrinterStateNotifier *self)
+{
+ IndicatorPrinterStateNotifierPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ INDICATOR_TYPE_PRINTER_STATE_NOTIFIER,
+ IndicatorPrinterStateNotifierPrivate);
+ self->priv = priv;
+
+ priv->notified_printer_states = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify)g_strfreev);
+
+ priv->printer_alerts = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert_many ( priv->printer_alerts,
+ "media-low", "The printer “%s” is low on paper.",
+ "media-empty", "The printer “%s” is out of paper.",
+ "toner-low", "The printer “%s” is low on toner.",
+ "toner-empty", "The printer “%s” is out of toner.",
+ "cover-open", "A cover is open on the printer “%s”.",
+ "door-open", "A door is open on the printer “%s”.",
+ "cups-missing-filter", "The printer “%s” can’t be used, because required software is missing.",
+ "offline", "The printer “%s” is currently off-line.",
+ NULL);
+}
+
+
+CupsNotifier *
+indicator_printer_state_notifier_get_cups_notifier (IndicatorPrinterStateNotifier *self)
+{
+ return self->priv->cups_notifier;
+}
+
+
+void
+indicator_printer_state_notifier_set_cups_notifier (IndicatorPrinterStateNotifier *self,
+ CupsNotifier *cups_notifier)
+{
+ if (self->priv->cups_notifier) {
+ g_signal_handlers_disconnect_by_func (self->priv->cups_notifier,
+ on_printer_state_changed,
+ self);
+ g_clear_object (&self->priv->cups_notifier);
+ }
+
+ if (cups_notifier) {
+ self->priv->cups_notifier = g_object_ref (cups_notifier);
+ g_signal_connect (cups_notifier, "printer-state-changed",
+ G_CALLBACK (on_printer_state_changed), self);
+ }
+}
+
diff --git a/src/indicator-printer-state-notifier.h b/src/indicator-printer-state-notifier.h
new file mode 100644
index 0000000..f7d53b9
--- /dev/null
+++ b/src/indicator-printer-state-notifier.h
@@ -0,0 +1,58 @@
+
+#ifndef INDICATOR_PRINTER_STATE_NOTIFIER_H
+#define INDICATOR_PRINTER_STATE_NOTIFIER_H
+
+#include <glib-object.h>
+#include "cups-notifier.h"
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_PRINTER_STATE_NOTIFIER indicator_printer_state_notifier_get_type()
+
+#define INDICATOR_PRINTER_STATE_NOTIFIER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ INDICATOR_TYPE_PRINTER_STATE_NOTIFIER, IndicatorPrinterStateNotifier))
+
+#define INDICATOR_PRINTER_STATE_NOTIFIER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), \
+ INDICATOR_TYPE_PRINTER_STATE_NOTIFIER, IndicatorPrinterStateNotifierClass))
+
+#define INDICATOR_IS_PRINTER_STATE_NOTIFIER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ INDICATOR_TYPE_PRINTER_STATE_NOTIFIER))
+
+#define INDICATOR_IS_PRINTER_STATE_NOTIFIER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+ INDICATOR_TYPE_PRINTER_STATE_NOTIFIER))
+
+#define INDICATOR_PRINTER_STATE_NOTIFIER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ INDICATOR_TYPE_PRINTER_STATE_NOTIFIER, IndicatorPrinterStateNotifierClass))
+
+typedef struct _IndicatorPrinterStateNotifier IndicatorPrinterStateNotifier;
+typedef struct _IndicatorPrinterStateNotifierClass IndicatorPrinterStateNotifierClass;
+typedef struct _IndicatorPrinterStateNotifierPrivate IndicatorPrinterStateNotifierPrivate;
+
+struct _IndicatorPrinterStateNotifier
+{
+ GObject parent;
+
+ IndicatorPrinterStateNotifierPrivate *priv;
+};
+
+struct _IndicatorPrinterStateNotifierClass
+{
+ GObjectClass parent_class;
+};
+
+GType indicator_printer_state_notifier_get_type (void) G_GNUC_CONST;
+
+CupsNotifier * indicator_printer_state_notifier_get_cups_notifier (IndicatorPrinterStateNotifier *self);
+void indicator_printer_state_notifier_set_cups_notifier (IndicatorPrinterStateNotifier *self,
+ CupsNotifier *cups_notifier);
+
+
+G_END_DECLS
+
+#endif
+
diff --git a/src/indicator-printers-service.c b/src/indicator-printers-service.c
index ce89dd0..58644ab 100644
--- a/src/indicator-printers-service.c
+++ b/src/indicator-printers-service.c
@@ -23,6 +23,7 @@
#include "cups-notifier.h"
#include "indicator-printers-menu.h"
+#include "indicator-printer-state-notifier.h"
static void
@@ -39,6 +40,7 @@ int main (int argc, char *argv[])
DbusmenuServer *menuserver;
CupsNotifier *cups_notifier;
IndicatorPrintersMenu *menu;
+ IndicatorPrinterStateNotifier *state_notifier;
GError *error = NULL;
gtk_init (&argc, &argv);
@@ -71,10 +73,15 @@ int main (int argc, char *argv[])
dbusmenu_server_set_root (menuserver,
indicator_printers_menu_get_root (menu));
+ state_notifier = g_object_new (INDICATOR_TYPE_PRINTER_STATE_NOTIFIER,
+ "cups-notifier", cups_notifier,
+ NULL);
+
gtk_main ();
g_object_unref (menu);
g_object_unref (menuserver);
+ g_object_unref (state_notifier);
g_object_unref (cups_notifier);
g_object_unref (service);
return 0;